diff -Nru libwebsockets-4.0.20/appveyor.yml libwebsockets-2.4.2/appveyor.yml --- libwebsockets-4.0.20/appveyor.yml 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/appveyor.yml 2018-03-08 10:28:37.000000000 +0000 @@ -1,11 +1,5 @@ environment: matrix: - - LWS_METHOD: jose - CMAKE_ARGS: -DLWS_WITH_JOSE=1 - - - LWS_METHOD: x64 - CMAKE_ARGS: -DCMAKE_GENERATOR_PLATFORM=x64 -DLWS_WITH_HTTP2=1 -DLWS_WITH_PLUGINS=1 -DLIBUV_INCLUDE_DIRS=C:\assets\libuv64\include -DLIBUV_LIBRARIES=C:\assets\libuv64\libuv.lib - - LWS_METHOD: lwsws CMAKE_ARGS: -DLWS_WITH_LWSWS=1 -DSQLITE3_INCLUDE_DIRS=C:\assets\sqlite3 -DSQLITE3_LIBRARIES=C:\assets\sqlite3\sqlite3.lib -DLIBUV_INCLUDE_DIRS=C:\assets\libuv\include -DLIBUV_LIBRARIES=C:\assets\libuv\libuv.lib @@ -22,21 +16,18 @@ - LWS_METHOD: nossl CMAKE_ARGS: -DLWS_WITH_SSL=OFF - install: - appveyor DownloadFile https://libwebsockets.org:444/win-libuv.zip - mkdir c:\assets - mkdir c:\assets\libuv - 7z x -oc:\assets\libuv win-libuv.zip - - appveyor DownloadFile https://libwebsockets.org:444/win-libuv64.zip - - mkdir c:\assets\libuv64 - - 7z x -oc:\assets\libuv64 win-libuv64.zip - appveyor DownloadFile https://libwebsockets.org:444/nsis-3.0rc1-setup.exe - cmd /c start /wait nsis-3.0rc1-setup.exe /S /D=C:\nsis - appveyor DownloadFile https://libwebsockets.org:444/sqlite-dll-win32-x86-3130000.zip - mkdir c:\assets\sqlite3 - 7z x -oc:\assets\sqlite3 sqlite-dll-win32-x86-3130000.zip - SET PATH=C:\Program Files\NSIS\;C:\Program Files (x86)\NSIS\;c:\nsis;%PATH% +build: build_script: - md build @@ -45,33 +36,12 @@ - cmake --build . --config Release after_build: - - cd %APPVEYOR_BUILD_FOLDER% - - mkdir staging - - mkdir staging\include - - cp -r %APPVEYOR_BUILD_FOLDER%\build\bin %APPVEYOR_BUILD_FOLDER%\build\lib staging - - if EXIST staging\bin\share mv staging\bin\share staging - - if NOT EXIST staging\share\libwebsockets-test-server mkdir staging\share\libwebsockets-test-server - - IF EXIST %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.pem cp %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.pem staging\share\libwebsockets-test-server - - IF EXIST %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.key.pem cp %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.key.pem staging\share\libwebsockets-test-server - - IF EXIST %APPVEYOR_BUILD_FOLDER%\build\lws_config.h cp %APPVEYOR_BUILD_FOLDER%\build\lws_config.h staging\include - - cp %APPVEYOR_BUILD_FOLDER%\include\libwebsockets.h staging\include - - cp -r %APPVEYOR_BUILD_FOLDER%\include\libwebsockets staging\include - - 7z a build\lws-%LWS_METHOD%-%APPVEYOR_BUILD_ID%.zip %APPVEYOR_BUILD_FOLDER%\staging\* + - 7z a lws.zip %APPVEYOR_BUILD_FOLDER%\build\lib\Release\websockets.lib %APPVEYOR_BUILD_FOLDER%\build\lib\Release\websockets.exp %APPVEYOR_BUILD_FOLDER%\build\bin\Release\websockets.dll %APPVEYOR_BUILD_FOLDER%\lib\libwebsockets.h %APPVEYOR_BUILD_FOLDER%\build\lws_config.h %APPVEYOR_BUILD_FOLDER%\build\bin\Release\*.exe artifacts: - - path: build\lws-%LWS_METHOD%-%APPVEYOR_BUILD_ID%.zip - -#deploy: -#- provider: BinTray -# username: lws-team -# api_key: -# secure: nDpZ7P/wrk98DwJPMC6KpCC23QrVP8f3RxvKzBaqOmb9LiVrg1IyO1cc5vcgShZC -# subject: lws-team -# repo: libwebsockets -# package: windows -# publish: true -# override: true -# explode: false + - path: lws.zip + name: lws.zip + type: Zip matrix: fast_finish: true diff -Nru libwebsockets-4.0.20/changelog libwebsockets-2.4.2/changelog --- libwebsockets-4.0.20/changelog 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/changelog 2018-03-08 10:28:37.000000000 +0000 @@ -1,402 +1,6 @@ Changelog --------- -v4.0.0 -====== - - - NEW: Lws is now under the MIT license, see ./LICENSE for details - - - NEW: GLIB native event loop support, lws + gtk example - - - NEW: native lws MQTT client... supports client stream binding like h2 when - multiple logical connections are going to the same endpoint over MQTT, they - transparently and independently share the one connection + tls tunnel - - - NEW: "Secure Streams"... if you are making a device with client connections - to the internet or cloud, this allows separation of the communications - policy (endpoints, tls cert validation, protocols, etc) from the code, with - the goal you can combine streams, change protocols and cloud provision, and - reflect that in the device's JSON policy document without having to change - any code. - - - NEW: lws_system: New lightweight and efficient Asynchronous DNS resolver - implementation for both A and AAAA records, supports recursive (without - recursion in code) lookups, caching, and getaddrinfo() compatible results - scheme (from cache directly without per-consumer allocation). Able to - perform DNS lookups without introducing latency in the event loop. - - - NEW: lws_system: ntpclient implementation with interface for setting system - time via lws_system ops - - - NEW: lws_system: dhcpclient implementation - - - NEW: Connection validity tracking, autoproduce PING/PONG for protocols that - support it if not informed that the connection has passed data in both - directions recently enough - - - NEW: lws_retry: standardized exponential backoff and retry timing based - around backoff table and lws_sul - - - NEW: there are official public helpers for unaligned de/serialization of all - common types, see eh, lws_ser_wu16be() in include/libwebsockets/lws-misc.h - - - NEW: lws_tls_client_vhost_extra_cert_mem() api allows attaching extra certs - to a client vhost from DER in memory - - - NEW: lws_system: generic blobs support passing auth tokens, per-connection - client certs etc from platform into lws - - - NEW: public helpers to consume and produce ipv4/6 addresses in a clean way, - along with lws_sockaddr46 type now public. See eg, lws_sockaddr46-based - lws_sa46_parse_numeric_address(), lws_write_numeric_address() - in include/libwebsockets/lws-network-helper.h - - - Improved client redirect handling, h2 compatibility - - - NEW: lwsac: additional features for constant folding support (strings that - already are in the lwsac can be pointed to without copying again), backfill - (look for gaps in previous chunks that could take a new use size), and - lwsac_extend() so last use() can attempt to use more unallocated chunk space - - - NEW: lws_humanize: apis for reporting scalar quanties like 1234 as "1.234KB" - with the scaled symbol strings passed in by caller - - - NEW: freertos: support lws_cancel_service() by using UDP pair bound to lo, - since it doesn't have logical pipes - - - NEW: "esp32" plat, which implemented freertos plat compatibility on esp32, is - renamed to "freertos" plat, targeting esp32 and other freertos platforms - - - NEW: base64 has an additional api supporting stateful decode, where the input - is not all in the same place at the same time and can be processed - incrementally - - - NEW: lws ws proxy: support RFC8441 - - - NEW: lws_spawn_piped apis: generic support for vforking a process with child - wsis attached to its stdin, stdout and stderr via pipes. When processes are - reaped, a specified callback is triggered. Currently Linux + OSX. - - - NEW: lws_fsmount apis: Linux-only overlayfs mount and unmount management for - aggregating read-only layers with disposable, changeable upper layer fs - - - Improvements for RTOS / small build case bring the footprint of lws v4 below - that of v3.1 on ARM - - - lws_tokenize: flag specifying # should mark rest of line as comment - - - NEW: minimal example for integrating libasound / alsa via raw file - - - lws_struct: sqlite and json / lejp translation now usable - - -v3.2.0 -====== - - - This is the last planned release under LGPLv2+SLE. It's not planned to be - maintained like previous releases, please switch to master for the latest - stuff or continue to use v3.1-stable until the next release under the - new MIT license. - - - NEW: completely refactored scheduler with a unified, sorted us-resolution - linked-list implementation. All polled checks like timeout are migrated - to use the new timers, which also work on the event lib implementations. - Faster operation, us-resolution timeouts and generic scheduled callbacks - from the event loop. - - - NEW: lws_dsh specialized buffer memory allocator that can borrow space - from other cooperating buffers on the same list. - - - NEW: lws_sequencer allows managing multi-connection processes and - retries - - - NEW: memory buffer cert support - - - NEW: LWS_WITH_NETWORK in CMake... can be configured without any network- - related code at all - - - NEW: builds on QNX 6.5 and SmartOS - - - NEW: JOSE / JWK / JWS / JWE support, for all common ciphers and algs, - works on OpenSSL and mbedtls backends - - - NEW: gencrypto now has genaes and genec in addition to genrsa, works - on OpenSSL and mbedtls backends - - - NEW: raw_proxy role - - - NEW: Basic Auth works on ws connections - - - CHANGE: REMOVED: LWS_WITH_GENRSA, LWS_WITH_GENHASH, LWS_WITH_GENEC, - LWS_WITH_GENAES have all been removed and combined into LWS_WITH_GENCRYPTO - - - CHANGE: REMOVED: LWS_WITH_JWS, LWS_WITH_JWE have been removed and combined - into LWS_WITH_JOSE - -v3.1.0 -====== - - - CHANGE: REMOVED: lws_client_connect() and lws_client_connect_extended() - compatibility apis for lws_client_connect_via_info() have been marked as - deprecated for several versions and are now removed. Use - lws_client_connect_via_info() directly instead. - - - CHANGE: CMAKE: - - LWS_WITH_HTTP2: now defaults ON - - - CHANGE: Minimal examples updated to use Content Security Policy best - practices, using - `LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE` vhost - option flag and disabling of inline style and scripts. A side-effect of - this is that buffers used to marshal headers have to be prepared to take - more content than previously... LWS_RECOMMENDED_MIN_HEADER_SPACE (2048 - currently) is available for user (and internal) use to logically tie the - buffer size to this usecase (and follow future increases). - - - NEW: CMAKE - - LWS_FOR_GITOHASHI: sets various cmake options suitable for gitohashi - - LWS_WITH_ASAN: for Linux, enable build with ASAN - - Don't forget LWS_WITH_DISTRO_RECOMMENDED, which enables a wide range of lws - options suitable for a distro build of the library. - - - NEW: lws threadpool - lightweight pool of pthreads integrated to lws wsi, with - all synchronization to event loop handled internally, queue for excess tasks - [threadpool docs](https://libwebsockets.org/git/libwebsockets/tree/lib/misc/threadpool) - [threadpool minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/ws-server/minimal-ws-server-threadpool) - Cmake config: `-DLWS_WITH_THREADPOOL=1` - - - NEW: libdbus support integrated on lws event loop - [lws dbus docs](https://libwebsockets.org/git/libwebsockets/tree/lib/roles/dbus) - [lws dbus client minimal examples](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/dbus-client) - [lws dbus server minimal examples](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/dbus-server) - Cmake config: `-DLWS_ROLE_DBUS=1` - - - NEW: lws allocated chunks (lwsac) - helpers for optimized mass allocation of small - objects inside a few larger malloc chunks... if you need to allocate a lot of - inter-related structs for a limited time, this removes per-struct allocation - library overhead completely and removes the need for any destruction handling - [lwsac docs](https://libwebsockets.org/git/libwebsockets/tree/lib/misc/lwsac) - [lwsac minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-lwsac) - Cmake Config: `-DLWS_WITH_LWSAC=1` - - - NEW: lws tokenizer - helper api for robustly tokenizing your own strings without - allocating or adding complexity. Configurable by flags for common delimiter - sets and comma-separated-lists in the tokenizer. Detects and reports syntax - errors. - [lws_tokenize docs](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-tokenize.h) - [lws_tokenize minimal example / api test](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-lws_tokenize) - - - NEW: lws full-text search - optimized trie generation, serialization, - autocomplete suggestion generation and instant global search support extensible - to huge corpuses of UTF-8 text while remaining super lightweight on resources. - [full-text search docs](https://libwebsockets.org/git/libwebsockets/tree/lib/misc/fts) - [full-text search minimal example / api test](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-fts) - [demo](https://libwebsockets.org/ftsdemo/) - [demo sources](https://libwebsockets.org/git/libwebsockets/tree/plugins/protocol_fulltext_demo.c) - Cmake config: `-DLWS_WITH_FTS=1 -DLWS_WITH_LWSAC=1` - - - NEW: gzip + brotli http server-side compression - h1 and h2 automatic advertising - of server compression and application to files with mimetypes "text/*", - "application/javascript" and "image/svg.xml". - Cmake config: `-DLWS_WITH_HTTP_STREAM_COMPRESSION=1`, `-DLWS_WITH_HTTP_BROTLI=1` - - - NEW: managed disk cache - API for managing a directory containing cached files - with hashed names, and automatic deletion of LRU files once the cache is - above a given limit. - [lws diskcache docs](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-diskcache.h) - Cmake config: `-DLWS_WITH_DISKCACHE=1` - - - NEW: http reverse proxy - lws mounts support proxying h1 or h2 requests to - a local or remote IP, or unix domain socket over h1. This allows microservice - type architectures where parts of the common URL space are actually handled - by external processes which may be remote or on the same machine. - [lws gitohashi serving](https://libwebsockets.org/git/) is handled this way. - CMake config: `-DLWS_WITH_HTTP_PROXY=1` - - - NEW: lws_buflist - internally several types of ad-hoc malloc'd buffer have - been replaced by a new, exported api `struct lws_buflist`. This allows - multiple buffers to be chained and drawn down in strict FIFO order. - - - NEW: In the case of h1 upgrade, the connection header is checked to contain - "upgrade". The vhost flag LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK - also causes the Host: header to be confirmed to match the vhost name and - listen port. - - - NEW: If no 404 redirect for `lws_return_http_status()` is specified for the vhost, - the status page produced will try to bring in a stylesheet `/error.css`. This allows - you to produce styled 404 or other error pages with logos, graphics etc. See - https://libwebsockets.org/git/badrepo for an example of what you can do with it. - -v3.0.0 -====== - - - CHANGE: Clients used to call LWS_CALLBACK_CLOSED same as servers... - LWS_CALLBACK_CLIENT_CLOSED has been introduced and is called for clients - now. - - - CHANGE: LWS_CALLBACK_CLIENT_CONNECTION_ERROR used to only be directed at - protocols[0]. However in many cases, the protocol to bind to was provided - at client connection info time and the wsi bound accordingly. In those - cases, CONNECTION_ERROR is directed at the bound protocol, not protcols[0] - any more. - - - CHANGE: CMAKE: the following cmake defaults have changed with this version: - - - LWS_WITH_ZIP_FOPS: now defaults OFF - - LWS_WITH_RANGES: now defaults OFF - - LWS_WITH_ZLIB: now defaults OFF - - LWS_WITHOUT_EXTENSIONS: now defaults ON - - - CHANGE: REMOVED: lws_alloc_vfs_file() (read a file to malloc buffer) - - - CHANGE: REMOVED: lws_read() (no longer useful outside of lws internals) - - - CHANGE: REMOVED: ESP8266... ESP32 is now within the same price range and much - more performant - - - CHANGE: soname bump... don't forget to `ldconfig` - - - NEW: all event libraries support "foreign" loop integration where lws itself - if just a temporary user of the loop unrelated to the actual loop lifecycle. - - See `minimal-http-server-eventlib-foreign` for example code demonstrating - this for all the event libraries. - - Internal loop in lws is also supported and demonstrated by - `minimal-http-server-eventlib`. - - - NEW: ws-over-h2 support. This is a new RFC-on-the-way supported by Chrome - and shortly firefox that allows ws connections to be multiplexed back to the - server on the same tcp + tls wrapper h2 connection that the html and scripts - came in on. This is hugely faster that discrete connections. - - - NEW: UDP socket adoption and related event callbacks - - - NEW: Multi-client connection binding, queuing and pipelining support. - - Lws detects multiple client connections to the same server and port, and - optimizes how it handles them according to the server type and provided - flags. For http/1.0, all occur with individual parallel connections. For - http/1.1, you can enable keepalive pipelining, so the connections occur - sequentially on a single network connection. For http/2, they all occur - as parallel streams within a single h2 network connection. - - See minimal-http-client-multi for example code. - - - NEW: High resolution timer API for wsi, get a callback on your wsi with - LWS_CALLBACK_TIMER, set and reset the timer with lws_set_timer_usecs(wsi, us) - Actual resolution depends on event backend. Works with all backends, poll, - libuv, libevent, and libev. - - - NEW: Protocols can arrange vhost-protocol instance specific callbacks with - second resolution using `lws_timed_callback_vh_protocol()` - - - NEW: ACME client plugin for self-service TLS certificates - - - NEW: RFC7517 JSON Web Keys RFC7638 JWK thumbprint, and RFC7515 JSON Web - signatures support - - - NEW: lws_cancel_service() now provides a generic way to synchronize events - from other threads, which appear as a LWS_CALLBACK_EVENT_WAIT_CANCELLED - callback on all protocols. This is compatible with all the event libraries. - - - NEW: support BSD poll() where changes to the poll wait while waiting are - undone. - - - NEW: Introduce generic hash, hmac and RSA apis that operate the same - regardless of OpenSSL or mbedTLS tls backend - - - NEW: Introduce X509 element query api that works the same regardless of - OpenSSL or mbedTLS tls backend - - - NEW: Introduce over 30 "minimal examples" in ./minimal-examples... these - replace most of the old test servers - - - test-echo -> minimal-ws-server-echo and minimal-ws-client-echo - - - test-server-libuv / -libevent / -libev -> - minimal-https-server-eventlib / -eventlib-foreign / -eventlib-demos - - - test-server-v2.0 -> folded into all the minimal servers - - - test-server direct http serving -> minimal-http-server-dynamic - - The minimal examples allow individual standalone build using their own - small CMakeLists.txt. - - - NEW: lws now detects any back-to-back writes that did not go through the - event loop inbetween and reports them. This will flag any possibility of - failure rather than wait until the problem happens. - - - NEW: CMake has LWS_WITH_DISTRO_RECOMMENDED to select features that are - appropriate for distros - - - NEW: Optional vhost URL `error_document_404` if given causes a redirect there - instead of serve the default 404 page. - - - NEW: lws_strncpy() wrapper guarantees NUL in copied string even if it was - truncated to fit. - - - NEW: for client connections, local protocol binding name can be separated - from the ws subprotocol name if needed, using .local_protocol_name - - - NEW: Automatic detection of time discontiguities - - - NEW: Applies TCP_USER_TIMEOUT for Linux tcp keepalive where available - - - QA: 1600 tests run on each commit in Travis CI, including almost all - Autobahn in client and server mode, various h2load tests, h2spec, attack.sh - the minimal example selftests and others. - - - QA: fix small warnings introduced on gcc8.x (eg, Fedora 28) - - - QA: Add most of -Wextra on gcc (-Wsign-compare, -Wignored-qualifiers, - -Wtype-limits, -Wuninitialized) - - - QA: clean out warnings on windows - - - QA: pass all 146 h2spec tests now on strict - - - QA: introduce 35 selftests that operate different minimal examples against - each other and confirm the results. - - - QA: LWS_WITH_MINIMAL_EXAMPLES allows mass build of all relevant minimal- - examples with the LWS build, for CI and to make all the example binaries - available from the lws build dir ./bin - - - REFACTOR: the lws source directory layout in ./lib has been radically - improved, and there are now README.md files in selected subdirs with extra - documentation of interest to people working on lws itself. - - - REFACTOR: pipelined transactions return to the event loop before starting the - next part. - - - REFACTOR: TLS: replace all TLS library constants with generic LWS ones and - adapt all the TLS library code to translate to these common ones. - - Isolated all the tls-related private stuff in `./lib/tls/private.h`, and all - the mbedTLS stuff in `./lib/tls/mbedtls` + openSSL stuff in - `./lib/tls/openssl` - - - REFACTOR: the various kinds of wsi possible with lws have been extracted - from the main code and isolated into "roles" in `./lib/roles` which - communicate with the core code via an ops struct. Everything related to - ah is migrated to the http role. - - wsi modes are eliminated and replaced by the ops pointer for the role the - wsi is performing. Generic states for wsi are available to control the - lifecycle using core code. - - Adding new "roles" is now much easier with the changes and ops struct to - plug into. - - - REFACTOR: reduce four different kinds of buffer management in lws into a - generic scatter-gather struct lws_buflist. - - - REFACTOR: close notifications go through event loop - - v2.4.0 ====== @@ -534,4 +138,627 @@ See README.coding.md +v2.1.0 +====== + +Major new features + + - Support POST arguments, including multipart and file attachment + + - Move most of lwsws into lws, make the stub CC0 + + - Add loopback test plugin to confirm client ws / http coexistence + + - Integrate lwsws testing on Appveyor (ie, windows) + + - Introduce helpers for sql, urlencode and urldecode sanitation + + - Introduce LWS_CALLBACK_HTTP_BIND_PROTOCOL / DROP_PROTOCOL that + are compatible with http:/1.1 pipelining and different plugins + owning different parts of the URL space + + - lwsgs - Generic Sessions plugin supports serverside sessions, + cookies, hashed logins, forgot password etc + + - Added APIs for sending email to SMTP servers + + - Messageboard example plugin for lwsgs + + - Automatic PING sending at fixed intervals and close if no response + + - Change default header limit in ah to 4096 (from 1024) + + - Add SNI matching for wildcards if no specific wildcard vhost name match + + - Convert docs to Doxygen + + - ESP8266 support ^^ + +Fixes +----- + +See git log v2.0.0.. + + + +v2.0.0 +====== + +Summary +------- + + - There are only api additions, the api is compatible with v1.7.x. But + there is necessarily an soname bump to 8. + + - If you are using lws client, you mainly need to be aware the option + LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT is needed at context-creation time + if you will use SSL. + + - If you are using lws for serving, the above is also true but there are + many new features to simplify your code (and life). There is a + summany online here + + https://libwebsockets.org/lws-2.0-new-features.html + + but basically the keywords are vhosts, mounts and plugins. You can now + do the web serving part from lws without any user callback code at all. + See ./test-server/test-server-v2.0.c for an example, it has no user + code for ws either since it uses the protocol plugins... that one C file + is all that is needed to do the whole test server function. + + You now have the option to use a small generic ws-capable webserver + "lwsws" and write your ws part as a plugin. That eliminates even + cut-and-pasting the test server code and offers more configurable + features like control over http cacheability in JSON. + + +Fixes +----- + +These are already in 1.7.x series + +1) MAJOR (Windows-only) fix assert firing + +2) MAJOR http:/1.1 connections handled by lws_return_http_status() did not +get sent a content-length resulting in the link hanging until the peer closed +it. attack.sh updated to add a test for this. + +3) MINOR An error about hdr struct in _lws_ws_related is corrected, it's not +known to affect anything until after it was fixed + +4) MINOR During the close shutdown wait state introduced at v1.7, if something +requests callback on writeable for the socket it will busywait until the +socket closes + +5) MAJOR Although the test server has done it for a few versions already, it +is now required for the user code to explicitly call + + if (lws_http_transaction_completed(wsi)) + return -1; + +when it finishes replying to a transaction in http. Previously the library +did it for you, but that disallowed large, long transfers with multiple +trips around the event loop (and cgi...). + +6) MAJOR connections on ah waiting list that closed did not get removed from +the waiting list... + +7) MAJOR since we added the ability to hold an ah across http keepalive +transactions where more headers had already arrived, we broke the ability +to tell if more headers had arrived. Result was if the browser didn't +close the keepalive, we retained ah for the lifetime of the keepalive, +using up the pool. + +8) MAJOR windows-only-POLLHUP was not coming + +9) Client should not send ext hdr if no exts + +Changes +------- + +1) MINOR test-server gained some new switches + + -C use external SSL cert file + -K use external SSL key file + -A use external SSL CA cert file + + -u set effective uid + -g set effective gid + +together you can use them like this to have the test-server work with the +usual purchased SSL certs from an official CA. + + --ssl -C your.crt -K your.key -A your.cer -u 99 -g 99 + +2) MINOR the OpenSSL magic to setup ECDH cipher usage is implemented in the +library, and the ciphers restricted to use ECDH only. +Using this, the lws test server can score an A at SSLLABS test + +3) MINOR STS (SSL always) header is added to the test server if you use --ssl. With +that, we score A+ at SSLLABS test + +4) MINOR daemonize function (disabled at cmake by default) is updated to work +with systemd + +5) MINOR example systemd .service file now provided for test server +(not installed by default) + +6) test server html is updated with tabs and a new live server monitoring +feature. Input sanitization added to the js. + +7) client connections attempted when no ah is free no longer fail, they are +just deferred until an ah becomes available. + +8) The test client pays attention to if you give it an http:/ or https:// +protocol string to its argument in URL format. If so, it stays in http[s] +client mode and doesn't upgrade to ws[s], allowing you to do generic http client +operations. Receiving transfer-encoding: chunked is supported. + +9) If you enable -DLWS_WITH_HTTP_PROXY=1 at cmake, the test server has a +new URI path http://localhost:7681/proxytest If you visit here, a client +connection to http://example.com:80 is spawned, and the results piped on +to your original connection. + +10) Also with LWS_WITH_HTTP_PROXY enabled at cmake, lws wants to link to an +additional library, "libhubbub". This allows lws to do html rewriting on the +fly, adjusting proxied urls in a lightweight and fast way. + +11) There's a new context creation flag LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT, +this is included automatically if you give any other SSL-related option flag. +If you give no SSL-related option flag, nor this one directly, then even +though SSL support may be compiled in, it is never initialized nor used for the +whole lifetime of the lws context. + +Conversely in order to prepare the context to use SSL, even though, eg, you +are not listening on SSL but will use SSL client connections later, you must +give this flag explicitly to make sure SSL is initialized. + + +User API additions +------------------ + +1) MINOR APIBREAK There's a new member in struct lws_context_creation_info, ecdh_curve, +which lets you set the name of the ECDH curve OpenSSL should use. By +default (if you leave ecdh_curve NULL) it will use "prime256v1" + +2) MINOR NEWAPI It was already possible to adopt a foreign socket that had not +been read from using lws_adopt_socket() since v1.7. Now you can adopt a +partially-used socket if you don't need SSL, by passing it what you read +so it can drain that before reading from the socket. + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, + const char *readbuf, size_t len); + +3) MINOR NEWAPI CGI type "network io" subprocess execution is now possible from +a simple api. + +LWS_VISIBLE LWS_EXTERN int +lws_cgi(struct lws *wsi, char * const *exec_array, int script_uri_path_len, + int timeout_secs); + +LWS_VISIBLE LWS_EXTERN int +lws_cgi_kill(struct lws *wsi); + +To use it, you must first set the cmake option + +$ cmake .. -DLWS_WITH_CGI=1 + +See test-server-http.c and test server path + +http://localhost:7681/cgitest + +stdin gets http body, you can test it with wget + +$ echo hello > hello.txt +$ wget http://localhost:7681/cgitest --post-file=hello.txt -O- --quiet +lwstest script +read="hello" + +The test script returns text/html table showing /proc/meminfo. But the cgi +support is complete enough to run cgit cgi. + +4) There is a helper api for forming logging timestamps + +LWS_VISIBLE int +lwsl_timestamp(int level, char *p, int len) + +this generates this kind of timestamp for use as logging preamble + +lwsts[13116]: [2016/01/25 14:52:52:8386] NOTICE: Initial logging level 7 + +5) struct lws_client_connect_info has a new member + + const char *method + +If it's NULL, then everything happens as before, lws_client_connect_via_info() +makes a ws or wss connection to the address given. + +If you set method to a valid http method like "GET", though, then this method +is used and the connection remains in http[s], it's not upgraded to ws[s]. + +So with this, you can perform http[s] client operations as well as ws[s] ones. + +There are 4 new related callbacks + + LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44, + LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45, + LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46, + LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47, + +6) struct lws_client_connect_info has a new member + + const char *parent_wsi + +if non-NULL, the client wsi is set to be a child of parent_wsi. This ensures +if parent_wsi closes, then the client child is closed just before. + +7) If you're using SSL, there's a new context creation-time option flag +LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS. If you give this, non-ssl +connections to the server listen port are accepted and receive a 301 +redirect to / on the same host and port using https:// + +8) User code may set per-connection extension options now, using a new api +"lws_set_extension_option()". + +This should be called from the ESTABLISHED callback like this + + lws_set_extension_option(wsi, "permessage-deflate", + "rx_buf_size", "12"); /* 1 << 12 */ + +If the extension is not active (missing or not negotiated for the +connection, or extensions are disabled on the library) the call is +just returns -1. Otherwise the connection's extension has its +named option changed. + +The extension may decide to alter or disallow the change, in the +example above permessage-deflate restricts the size of his rx +output buffer also considering the protocol's rx_buf_size member. + + +New application lwsws +--------------------- + +A libwebsockets-based general webserver is built by default now, lwsws. + +It's configured by JSON, by default in + + /etc/lwsws/conf + +which contains global lws context settings like this + +{ + "global": { + "uid": "99", + "gid": "99", + "interface": "eth0", + "count-threads": "1" + } +} + + /etc/lwsws/conf.d/* + +which contains zero or more files describing vhosts, like this + +{ + "vhosts": [ + { "name": "warmcat.com", + "port": "443", + "host-ssl-key": "/etc/pki/tls/private/warmcat.com.key", + "host-ssl-cert": "/etc/pki/tls/certs/warmcat.com.crt", + "host-ssl-ca": "/etc/pki/tls/certs/warmcat.com.cer", + "mounts": [ + { "/": [ + { "home": "file:///var/www/warmcat.com" }, + { "default": "index.html" } + ] + } + ] + } + ] +} + + + +v1.7.0 +====== + +Extension Changes +----------------- + +1) There is now a "permessage-deflate" / RFC7692 implementation. It's very +similar to "deflate-frame" we have offered for a long while; deflate-frame is +now provided as an alias of permessage-deflate. + +The main differences are that the new permessage-deflate implementation: + + - properly performs streaming respecting input and output buffer limits. The + old deflate-frame implementation could only work on complete deflate input + and produce complete inflate output for each frame. The new implementation + only mallocs buffers at initialization. + + - goes around the event loop after each input package is processed allowing + interleaved output processing. The RX flow control api can be used to + force compressed input processing to match the rate of compressed output + processing (test--echo shows an example of how to do this). + + - when being "deflate-frame" for compatibility he uses the same default zlib + settings as the old "deflate-frame", but instead of exponentially increasing + malloc allocations until the whole output will fit, he observes the default + input and output chunking buffer sizes of "permessage-deflate", that's + 1024 in and 1024 out at a time. + +2) deflate-stream has been disabled for many versions (for over a year) and is +now removed. Browsers are now standardizing on "permessage-deflate" / RFC7692 + +3) struct lws_extension is simplified, and lws extensions now have a public +api (their callback) for use in user code to compose extensions and options +the user code wants. lws_get_internal_exts() is deprecated but kept around +as a NOP. The changes allow one extension implementation to go by different +names and allows the user client code to control option offers per-ext. + +The test client and server are updated to use the new way. If you use +the old way it should still work, but extensions will be disabled until you +update your code. + +Extensions are now responsible for allocating and per-instance private struct +at instance construction time and freeing it when the instance is destroyed. +Not needing to know the size means the extension's struct can be opaque +to user code. + + +User api additions +------------------ + +1) The info struct gained three new members + + - max_http_header_data: 0 for default (1024) or set the maximum amount of known + http header payload that lws can deal with. Payload in unknown http + headers is dropped silently. If for some reason you need to send huge + cookies or other HTTP-level headers, you can now increase this at context- + creation time. + + - max_http_header_pool: 0 for default (16) or set the maximum amount of http + headers that can be tracked by lws in this context. For the server, if + the header pool is completely in use then accepts on the listen socket + are disabled until one becomes free. For the client, if you simultaneously + have pending connects for more than this number of client connections, + additional connects will fail until some of the pending connections timeout + or complete. + + - timeout_secs: 0 for default (currently 20s), or set the library's + network activity timeout to the given number of seconds + +HTTP header processing in lws only exists until just after the first main +callback after the HTTP handshake... for ws connections that is ESTABLISHED and +for HTTP connections the HTTP callback. + +So these settings are not related to the maximum number of simultaneous +connections, but the number of HTTP handshakes that may be expected or ongoing, +or have just completed, at one time. The reason it's useful is it changes the +memory allocation for header processing to be one-time at context creation +instead of every time there is a new connection, and gives you control over +the peak allocation. + +Setting max_http_header_pool to 1 is fine it will just queue incoming +connections before the accept as necessary, you can still have as many +simultaneous post-header connections as you like. Since the http header +processing is completed and the allocation released after ESTABLISHED or the +HTTP callback, even with a pool of 1 many connections can be handled rapidly. + +2) There is a new callback that allows the user code to get acccess to the +optional close code + aux data that may have been sent by the peer. + +LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: + The peer has sent an unsolicited Close WS packet. @in and + @len are the optional close code (first 2 bytes, network + order) and the optional additional information which is not + defined in the standard, and may be a string or non-human- + readble data. + If you return 0 lws will echo the close and then close the + connection. If you return nonzero lws will just close the + connection. + +As usual not handling it does the right thing, if you're not interested in it +just ignore it. + +The test server has "open and close" testing buttons at the bottom, if you +open and close that connection, on close it will send a close code 3000 decimal +and the string "Bye!" as the aux data. + +The test server dumb-increment callback handles this callback reason and prints + +lwsts[15714]: LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: len 6 +lwsts[15714]: 0: 0x0B +lwsts[15714]: 1: 0xB8 +lwsts[15714]: 2: 0x42 +lwsts[15714]: 3: 0x79 +lwsts[15714]: 4: 0x65 +lwsts[15714]: 5: 0x21 + +3) There is a new API to allow the user code to control the content of the +close frame sent when about to return nonzero from the user callback to +indicate the connection should close. + +/** + * lws_close_reason - Set reason and aux data to send with Close packet + * If you are going to return nonzero from the callback + * requesting the connection to close, you can optionally + * call this to set the reason the peer will be told if + * possible. + * + * @wsi: The websocket connection to set the close reason on + * @status: A valid close status from websocket standard + * @buf: NULL or buffer containing up to 124 bytes of auxiliary data + * @len: Length of data in @buf to send + */ +LWS_VISIBLE LWS_EXTERN void +lws_close_reason(struct lws *wsi, enum lws_close_status status, + unsigned char *buf, size_t len); + +An extra button is added to the "open and close" test server page that requests +that the test server close the connection from his end. + +The test server code will do so by + + lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY, + (unsigned char *)"seeya", 5); + return -1; + +The browser shows the close code and reason he received + +websocket connection CLOSED, code: 1001, reason: seeya + +4) There's a new context creation time option flag + +LWS_SERVER_OPTION_VALIDATE_UTF8 + +if you set it in info->options, then TEXT and CLOSE frames will get checked to +confirm that they contain valid UTF-8. If they don't, the connection will get +closed by lws. + +5) ECDH Certs are now supported. Enable the CMake option + +cmake .. -DLWS_SSL_SERVER_WITH_ECDH_CERT=1 + +**and** the info->options flag + +LWS_SERVER_OPTION_SSL_ECDH + +to build in support and select it at runtime. + +6) There's a new api lws_parse_uri() that simplifies chopping up +https://xxx:yyy/zzz uris into parts nicely. The test client now uses this +to allow proper uris as well as the old address style. + +7) SMP support is integrated into LWS without any internal threading. It's +very simple to use, libwebsockets-test-server-pthread shows how to do it, +use -j argument there to control the number of service threads up to 32. + +Two new members are added to the info struct + + unsigned int count_threads; + unsigned int fd_limit_per_thread; + +leave them at the default 0 to get the normal singlethreaded service loop. + +Set count_threads to n to tell lws you will have n simultaneous service threads +operating on the context. + +There is still a single listen socket on one port, no matter how many +service threads. + +When a connection is made, it is accepted by the service thread with the least +connections active to perform load balancing. + +The user code is responsible for spawning n threads running the service loop +associated to a specific tsi (Thread Service Index, 0 .. n - 1). See +the libwebsockets-test-server-pthread for how to do. + +If you leave fd_limit_per_thread at 0, then the process limit of fds is shared +between the service threads; if you process was allowed 1024 fds overall then +each thread is limited to 1024 / n. + +You can set fd_limit_per_thread to a nonzero number to control this manually, eg +the overall supported fd limit is less than the process allowance. + +You can control the context basic data allocation for multithreading from Cmake +using -DLWS_MAX_SMP=, if not given it's set to 32. The serv_buf allocation +for the threads (currently 4096) is made at runtime only for active threads. + +Because lws will limit the requested number of actual threads supported +according to LWS_MAX_SMP, there is an api lws_get_count_threads(context) to +discover how many threads were actually allowed when the context was created. + +It's required to implement locking in the user code in the same way that +libwebsockets-test-server-pthread does it, for the FD locking callbacks. + +If LWS_MAX_SMP=1, then there is no code related to pthreads compiled in the +library. If more than 1, a small amount of pthread mutex code is built into +the library. + +8) New API + +LWS_VISIBLE struct lws * +lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) + +allows foreign sockets accepted by non-lws code to be adopted by lws as if they +had just been accepted by lws' own listen socket. + +9) X-Real-IP: header has been added as WSI_TOKEN_HTTP_X_REAL_IP + +10) Libuv support is added, there are new related user apis + +typedef void (lws_uv_signal_cb_t)(uv_loop_t *l, uv_signal_t *w, int revents); + +LWS_VISIBLE LWS_EXTERN int +lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint, + lws_uv_signal_cb_t *cb); + +LWS_VISIBLE LWS_EXTERN int +lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi); + +LWS_VISIBLE void +lws_uv_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents); + +and CMAKE option + +LWS_WITH_LIBUV + + +User api changes +---------------- + +1) LWS_SEND_BUFFER_POST_PADDING is now 0 and deprecated. You can remove it; if +you still use it, obviously it does nothing. Old binary code with nonzero +LWS_SEND_BUFFER_POST_PADDING is perfectly compatible, the old code just +allocated a buffer bigger than the library is going to use. + +The example apps no longer use LWS_SEND_BUFFER_POST_PADDING. + +The only path who made use of it was sending with LWS_WRITE_CLOSE ---> + +2) Because of lws_close_reason() formalizing handling close frames, +LWS_WRITE_CLOSE is removed from libwebsockets.h. It was only of use to send +close frames...close frame content should be managed using lws_close_reason() +now. + +3) We check for invalid CLOSE codes and complain about protocol violation in +our close code. But it changes little since we were in the middle of closing +anyway. + +4) zero-length RX frames and zero length TX frames are now allowed. + +5) Pings and close used to be limited to 124 bytes, the correct limit is 125 +so that is now also allowed. + +6) LWS_PRE is provided as a synonym for LWS_SEND_BUFFER_PRE_PADDING, either is +valid to use now. + +7) There's generic support for RFC7462 style extension options built into the +library now. As a consequence, a field "options" is added to lws_extension. +It can be NULL if there are no options on the extension. Extension internal +info is part of the public abi because extensions may be implemented outside +the library. + +8) WSI_TOKEN_PROXY enum was accidentally defined to collide with another token +of value 73. That's now corrected and WSI_TOKEN_PROXY moved to his own place at +77. + +9) With the addition of libuv support, libev is not the only event loop +library in town and his api names must be elaborated with _ev_ + + Callback typedef: lws_signal_cb ---> lws_ev_signal_cb_t + lws_sigint_cfg --> lws_ev_sigint_cfg + lws_initloop --> lws_ev_initloop + lws_sigint_cb --> lws_ev_sigint_cb + +10) Libev support is made compatible with multithreaded service, +lws_ev_initloop (was lws_initloop) gets an extra argument for the +thread service index (use 0 if you will just have 1 service thread). + +LWS_VISIBLE LWS_EXTERN int +lws_ev_initloop(struct lws_context *context, ev_loop_t *loop, int tsi); + + (for earlier changelogs, see the tagged releases) diff -Nru libwebsockets-4.0.20/cmake/FindMiniz.cmake libwebsockets-2.4.2/cmake/FindMiniz.cmake --- libwebsockets-4.0.20/cmake/FindMiniz.cmake 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/cmake/FindMiniz.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -# This module tries to find miniz library and include files -# -# MINIZ_INCLUDE_DIR, path where to find miniz.h -# MINIZ_LIBRARY_DIR, path where to find libminiz.so -# MINIZ_LIBRARIES, the library to link against -# MINIZ_FOUND, If false, do not try to use miniz -# -# This currently works probably only for Linux - -FIND_PATH ( MINIZ_INCLUDE_DIR miniz.h - /usr/local/include - /usr/include -) - -FIND_LIBRARY ( MINIZ_LIBRARIES libminiz.so libminiz.a libminiz.so.2 libminiz.so.0.1 - /usr/local/lib - /usr/local/lib64 - /usr/lib - /usr/lib64 -) - -GET_FILENAME_COMPONENT( MINIZ_LIBRARY_DIR ${MINIZ_LIBRARIES} PATH ) - -SET ( MINIZ_FOUND "NO" ) -IF ( MINIZ_INCLUDE_DIR ) - IF ( MINIZ_LIBRARIES ) - SET ( MINIZ_FOUND "YES" ) - ENDIF ( MINIZ_LIBRARIES ) -ENDIF ( MINIZ_INCLUDE_DIR ) - -MARK_AS_ADVANCED( - MINIZ_LIBRARY_DIR - MINIZ_INCLUDE_DIR - MINIZ_LIBRARIES -) diff -Nru libwebsockets-4.0.20/cmake/lws_config.h.in libwebsockets-2.4.2/cmake/lws_config.h.in --- libwebsockets-4.0.20/cmake/lws_config.h.in 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/cmake/lws_config.h.in 2018-03-08 10:28:37.000000000 +0000 @@ -7,171 +7,150 @@ #endif #define LWS_INSTALL_DATADIR "${CMAKE_INSTALL_PREFIX}/share" + +/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL. + * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */ +#cmakedefine USE_WOLFSSL + +/* Also define to 1 (in addition to USE_WOLFSSL) when using the + (older) CyaSSL library */ +#cmakedefine USE_OLD_CYASSL +#cmakedefine LWS_WITH_BORINGSSL + +#cmakedefine LWS_WITH_MBEDTLS +#cmakedefine LWS_WITH_POLARSSL +#cmakedefine LWS_WITH_ESP8266 +#cmakedefine LWS_WITH_ESP32 + +#cmakedefine LWS_WITH_PLUGINS +#cmakedefine LWS_WITH_NO_LOGS + +/* The Libwebsocket version */ +#cmakedefine LWS_LIBRARY_VERSION "${LWS_LIBRARY_VERSION}" + #define LWS_LIBRARY_VERSION_MAJOR ${LWS_LIBRARY_VERSION_MAJOR} #define LWS_LIBRARY_VERSION_MINOR ${LWS_LIBRARY_VERSION_MINOR} #define LWS_LIBRARY_VERSION_PATCH ${LWS_LIBRARY_VERSION_PATCH} /* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */ -#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR * 1000000) + \ - (LWS_LIBRARY_VERSION_MINOR * 1000) + \ - LWS_LIBRARY_VERSION_PATCH -#define LWS_MAX_SMP ${LWS_MAX_SMP} +#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH -#cmakedefine LWS_LIBRARY_VERSION_NUMBER - -#cmakedefine LWS_AVOID_SIGPIPE_IGN +/* The current git commit hash that we're building from */ #cmakedefine LWS_BUILD_HASH "${LWS_BUILD_HASH}" -#cmakedefine LWS_BUILTIN_GETIFADDRS -#cmakedefine LWS_CLIENT_HTTP_PROXYING -#cmakedefine LWS_DETECTED_PLAT_IOS -#cmakedefine LWS_FALLBACK_GETHOSTBYNAME -#cmakedefine LWS_HAS_INTPTR_T -#cmakedefine LWS_HAS_GETOPT_LONG -#cmakedefine LWS_HAVE__ATOI64 -#cmakedefine LWS_HAVE_ATOLL -#cmakedefine LWS_HAVE_BN_bn2binpad -#cmakedefine LWS_HAVE_CLOCK_GETTIME -#cmakedefine LWS_HAVE_EC_POINT_get_affine_coordinates -#cmakedefine LWS_HAVE_ECDSA_SIG_set0 -#cmakedefine LWS_HAVE_EVP_MD_CTX_free -#cmakedefine LWS_HAVE_EVP_aes_128_wrap -#cmakedefine LWS_HAVE_EVP_aes_128_cfb8 -#cmakedefine LWS_HAVE_EVP_aes_128_cfb128 -#cmakedefine LWS_HAVE_EVP_aes_192_cfb8 -#cmakedefine LWS_HAVE_EVP_aes_192_cfb128 -#cmakedefine LWS_HAVE_EVP_aes_256_cfb8 -#cmakedefine LWS_HAVE_EVP_aes_256_cfb128 -#cmakedefine LWS_HAVE_EVP_aes_128_xts -#cmakedefine LWS_HAVE_EXECVPE -#cmakedefine LWS_HAVE_LIBCAP -#cmakedefine LWS_HAVE_HMAC_CTX_new -#cmakedefine LWS_HAVE_MALLOC_H -#cmakedefine LWS_HAVE_MALLOC_TRIM -#cmakedefine LWS_HAVE_MALLOC_USABLE_SIZE -#cmakedefine LWS_HAVE_mbedtls_net_init -#cmakedefine LWS_HAVE_mbedtls_ssl_conf_alpn_protocols -#cmakedefine LWS_HAVE_mbedtls_ssl_get_alpn_protocol -#cmakedefine LWS_HAVE_mbedtls_ssl_conf_sni -#cmakedefine LWS_HAVE_mbedtls_ssl_set_hs_ca_chain -#cmakedefine LWS_HAVE_mbedtls_ssl_set_hs_own_cert -#cmakedefine LWS_HAVE_mbedtls_ssl_set_hs_authmode -#cmakedefine LWS_HAVE_MBEDTLS_NET_SOCKETS -#cmakedefine LWS_HAVE_NEW_UV_VERSION_H -#cmakedefine LWS_HAVE_OPENSSL_ECDH_H -#cmakedefine LWS_HAVE_PIPE2 -#cmakedefine LWS_HAVE_EVENTFD -#cmakedefine LWS_HAVE_PTHREAD_H -#cmakedefine LWS_HAVE_RSA_SET0_KEY -#cmakedefine LWS_HAVE_RSA_verify_pss_mgf1 -#cmakedefine LWS_HAVE_SSL_CTX_get0_certificate -#cmakedefine LWS_HAVE_SSL_CTX_set1_param -#cmakedefine LWS_HAVE_SSL_CTX_set_ciphersuites -#cmakedefine LWS_HAVE_SSL_EXTRA_CHAIN_CERTS -#cmakedefine LWS_HAVE_SSL_get0_alpn_selected -#cmakedefine LWS_HAVE_SSL_CTX_EVP_PKEY_new_raw_private_key -#cmakedefine LWS_HAVE_SSL_set_alpn_protos -#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK -#cmakedefine LWS_HAVE__STAT32I64 -#cmakedefine LWS_HAVE_STDINT_H -#cmakedefine LWS_HAVE_SYS_CAPABILITY_H -#cmakedefine LWS_HAVE_TLS_CLIENT_METHOD -#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD -#cmakedefine LWS_HAVE_UV_VERSION_H -#cmakedefine LWS_HAVE_VFORK -#cmakedefine LWS_HAVE_X509_get_key_usage -#cmakedefine LWS_HAVE_X509_VERIFY_PARAM_set1_host -#cmakedefine LWS_LIBRARY_VERSION "${LWS_LIBRARY_VERSION}" -#define LWS_LOGGING_BITFIELD_CLEAR ${LWS_LOGGING_BITFIELD_CLEAR} -#define LWS_LOGGING_BITFIELD_SET ${LWS_LOGGING_BITFIELD_SET} -#cmakedefine LWS_MINGW_SUPPORT -#cmakedefine LWS_NO_CLIENT -#cmakedefine LWS_NO_DAEMONIZE -#cmakedefine LWS_OPENSSL_CLIENT_CERTS "${LWS_OPENSSL_CLIENT_CERTS}" + +/* Build with OpenSSL support */ #cmakedefine LWS_OPENSSL_SUPPORT -#cmakedefine LWS_PLAT_OPTEE -#cmakedefine LWS_PLAT_UNIX -#cmakedefine LWS_PLAT_FREERTOS -#cmakedefine LWS_ROLE_CGI -#cmakedefine LWS_ROLE_DBUS -#cmakedefine LWS_ROLE_H1 -#cmakedefine LWS_ROLE_H2 -#cmakedefine LWS_ROLE_RAW -#cmakedefine LWS_ROLE_RAW_FILE -#cmakedefine LWS_ROLE_RAW_PROXY -#cmakedefine LWS_ROLE_WS -#cmakedefine LWS_ROLE_MQTT -#cmakedefine LWS_SHA1_USE_OPENSSL_NAME + +/* The client should load and trust CA root certs it finds in the OS */ #cmakedefine LWS_SSL_CLIENT_USE_OS_CA_CERTS + +/* Sets the path where the client certs should be installed. */ +#cmakedefine LWS_OPENSSL_CLIENT_CERTS "${LWS_OPENSSL_CLIENT_CERTS}" + +/* Turn off websocket extensions */ +#cmakedefine LWS_NO_EXTENSIONS + +/* Enable libev io loop */ +#cmakedefine LWS_WITH_LIBEV + +/* Enable libuv io loop */ +#cmakedefine LWS_WITH_LIBUV + +/* Enable libevent io loop */ +#cmakedefine LWS_WITH_LIBEVENT + +/* Build with support for ipv6 */ +#cmakedefine LWS_WITH_IPV6 + +/* Build with support for UNIX domain socket */ +#cmakedefine LWS_WITH_UNIX_SOCK + +/* Build with support for HTTP2 */ +#cmakedefine LWS_WITH_HTTP2 + +/* Turn on latency measuring code */ +#cmakedefine LWS_LATENCY + +/* Don't build the daemonizeation api */ +#cmakedefine LWS_NO_DAEMONIZE + +/* Build without server support */ +#cmakedefine LWS_NO_SERVER + +/* Build without client support */ +#cmakedefine LWS_NO_CLIENT + +/* If we should compile with MinGW support */ +#cmakedefine LWS_MINGW_SUPPORT + +/* Use the BSD getifaddrs that comes with libwebsocket, for uclibc support */ +#cmakedefine LWS_BUILTIN_GETIFADDRS + +/* use SHA1() not internal libwebsockets_SHA1 */ +#cmakedefine LWS_SHA1_USE_OPENSSL_NAME + +/* SSL server using ECDH certificate */ #cmakedefine LWS_SSL_SERVER_WITH_ECDH_CERT -#cmakedefine LWS_WITH_ABSTRACT -#cmakedefine LWS_WITH_ACCESS_LOG -#cmakedefine LWS_WITH_ACME -#cmakedefine LWS_WITH_ALSA -#cmakedefine LWS_WITH_SYS_ASYNC_DNS -#cmakedefine LWS_WITH_BORINGSSL +#cmakedefine LWS_HAVE_SSL_CTX_set1_param +#cmakedefine LWS_HAVE_X509_VERIFY_PARAM_set1_host +#cmakedefine LWS_HAVE_RSA_SET0_KEY + +#cmakedefine LWS_HAVE_UV_VERSION_H + +/* CGI apis */ #cmakedefine LWS_WITH_CGI -#cmakedefine LWS_WITH_CUSTOM_HEADERS -#cmakedefine LWS_WITH_DEPRECATED_LWS_DLL -#cmakedefine LWS_WITH_DETAILED_LATENCY -#cmakedefine LWS_WITH_DIR -#cmakedefine LWS_WITH_ESP32 -#cmakedefine LWS_HAVE_EVBACKEND_LINUXAIO -#cmakedefine LWS_HAVE_EVBACKEND_IOURING -#cmakedefine LWS_WITH_EXTERNAL_POLL -#cmakedefine LWS_WITH_FILE_OPS -#cmakedefine LWS_WITH_FSMOUNT -#cmakedefine LWS_WITH_FTS -#cmakedefine LWS_WITH_GENCRYPTO -#cmakedefine LWS_WITH_GENERIC_SESSIONS -#cmakedefine LWS_WITH_GLIB -#cmakedefine LWS_WITH_GTK -#cmakedefine LWS_WITH_HTTP2 -#cmakedefine LWS_WITH_HTTP_BASIC_AUTH -#cmakedefine LWS_WITH_HTTP_BROTLI + +/* whether the Openssl is recent enough, and / or built with, ecdh */ +#cmakedefine LWS_HAVE_OPENSSL_ECDH_H + +/* HTTP Proxy support */ #cmakedefine LWS_WITH_HTTP_PROXY -#cmakedefine LWS_WITH_HTTP_STREAM_COMPRESSION -#cmakedefine LWS_WITH_HTTP_UNCOMMON_HEADERS -#cmakedefine LWS_WITH_IPV6 -#cmakedefine LWS_WITH_JOSE -#cmakedefine LWS_WITH_LEJP -#cmakedefine LWS_WITH_LIBEV -#cmakedefine LWS_WITH_LIBEVENT -#cmakedefine LWS_WITH_LIBUV -#cmakedefine LWS_WITH_LWSAC -#cmakedefine LWS_LOGS_TIMESTAMP -#cmakedefine LWS_WITH_MBEDTLS -#cmakedefine LWS_WITH_MINIZ -#cmakedefine LWS_WITH_NETWORK -#cmakedefine LWS_WITH_NO_LOGS -#cmakedefine LWS_WITH_CLIENT -#cmakedefine LWS_WITHOUT_EXTENSIONS -#cmakedefine LWS_WITH_SERVER -#cmakedefine LWS_WITH_SPAWN -#cmakedefine LWS_WITH_PEER_LIMITS -#cmakedefine LWS_WITH_PLUGINS -#cmakedefine LWS_WITH_POLARSSL -#cmakedefine LWS_WITH_POLL + +/* HTTP Ranges support */ #cmakedefine LWS_WITH_RANGES -#cmakedefine LWS_WITH_SECURE_STREAMS -#cmakedefine LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM -#cmakedefine LWS_WITH_SECURE_STREAMS_PROXY_API -#cmakedefine LWS_WITH_SELFTESTS -#cmakedefine LWS_WITH_SEQUENCER + +/* Http access log support */ +#cmakedefine LWS_WITH_ACCESS_LOG #cmakedefine LWS_WITH_SERVER_STATUS -#cmakedefine LWS_WITH_SMTP -#cmakedefine LWS_WITH_SOCKS5 + #cmakedefine LWS_WITH_STATEFUL_URLDECODE -#cmakedefine LWS_WITH_STATS -#cmakedefine LWS_WITH_STRUCT_SQLITE3 -#cmakedefine LWS_WITH_STRUCT_JSON -#cmakedefine LWS_WITH_SQLITE3 -#cmakedefine LWS_WITH_SYS_NTPCLIENT -#cmakedefine LWS_WITH_SYS_DHCP_CLIENT -#cmakedefine LWS_WITH_THREADPOOL -#cmakedefine LWS_WITH_TLS -#cmakedefine LWS_WITH_UDP -#cmakedefine LWS_WITH_UNIX_SOCK +#cmakedefine LWS_WITH_PEER_LIMITS + +/* Maximum supported service threads */ +#define LWS_MAX_SMP ${LWS_MAX_SMP} + +/* Lightweight JSON Parser */ +#cmakedefine LWS_WITH_LEJP + +/* SMTP */ +#cmakedefine LWS_WITH_SMTP + +/* OPTEE */ +#cmakedefine LWS_PLAT_OPTEE + +/* ZIP FOPS */ #cmakedefine LWS_WITH_ZIP_FOPS -#cmakedefine USE_OLD_CYASSL -#cmakedefine USE_WOLFSSL +#cmakedefine LWS_HAVE_STDINT_H + +#cmakedefine LWS_AVOID_SIGPIPE_IGN + +#cmakedefine LWS_FALLBACK_GETHOSTBYNAME + +#cmakedefine LWS_WITH_STATS +#cmakedefine LWS_WITH_SOCKS5 + +#cmakedefine LWS_HAVE_SYS_CAPABILITY_H +#cmakedefine LWS_HAVE_LIBCAP + +#cmakedefine LWS_HAVE_ATOLL +#cmakedefine LWS_HAVE__ATOI64 +#cmakedefine LWS_HAVE__STAT32I64 + +/* OpenSSL various APIs */ + +#cmakedefine LWS_HAVE_TLS_CLIENT_METHOD +#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD +#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK + +#cmakedefine LWS_HAS_INTPTR_T ${LWS_SIZEOFPTR_CODE} diff -Nru libwebsockets-4.0.20/cmake/lws_config_private.h.in libwebsockets-2.4.2/cmake/lws_config_private.h.in --- libwebsockets-4.0.20/cmake/lws_config_private.h.in 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/cmake/lws_config_private.h.in 2018-03-08 10:28:37.000000000 +0000 @@ -10,6 +10,9 @@ * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */ #cmakedefine USE_CYASSL +/* Define to 1 if you have the `bzero' function. */ +#cmakedefine LWS_HAVE_BZERO + /* Define to 1 if you have the header file. */ #cmakedefine LWS_HAVE_DLFCN_H @@ -19,12 +22,18 @@ /* Define to 1 if you have the `fork' function. */ #cmakedefine LWS_HAVE_FORK -/* Define to 1 if you have the `getenv' function. */ +/* Define to 1 if you have the `getenv’ function. */ #cmakedefine LWS_HAVE_GETENV /* Define to 1 if you have the header file. */ #cmakedefine LWS_HAVE_IN6ADDR_H +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_INTTYPES_H + +/* Define to 1 if you have the `ssl' library (-lssl). */ +//#cmakedefine LWS_HAVE_LIBSSL + /* Define to 1 if your system has a GNU libc compatible `malloc' function, and to 0 otherwise. */ #cmakedefine LWS_HAVE_MALLOC @@ -78,8 +87,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine LWS_HAVE_UNISTD_H -#cmakedefine LWS_HAVE_TCP_USER_TIMEOUT - /* Define to 1 if you have the `vfork' function. */ #cmakedefine LWS_HAVE_VFORK @@ -116,8 +123,4 @@ /* Define if the inline keyword doesn't exist. */ #cmakedefine inline ${inline} -#cmakedefine LWS_WITH_ZLIB -#cmakedefine LWS_HAS_PTHREAD_SETNAME_NP -/* Defined if you have the header file. */ -#cmakedefine LWS_HAVE_INTTYPES_H diff -Nru libwebsockets-4.0.20/cmake/UseRPMTools.cmake libwebsockets-2.4.2/cmake/UseRPMTools.cmake --- libwebsockets-4.0.20/cmake/UseRPMTools.cmake 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/cmake/UseRPMTools.cmake 2018-03-08 10:28:37.000000000 +0000 @@ -111,7 +111,7 @@ mkdir build_tree cd build_tree cmake -DCMAKE_INSTALL_PREFIX=%{rpmprefix} ../%{srcdirname} -make %{?_smp_mflags} +make %install cd ../build_tree diff -Nru libwebsockets-4.0.20/CMakeLists.txt libwebsockets-2.4.2/CMakeLists.txt --- libwebsockets-4.0.20/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/CMakeLists.txt 2018-03-08 10:28:37.000000000 +0000 @@ -1,340 +1,25 @@ cmake_minimum_required(VERSION 2.8.9) -if(POLICY CMP0048) - cmake_policy(SET CMP0048 NEW) -endif() - # General Advice # # For selecting between DEBUG / RELEASE, use -DCMAKE_BUILD_TYPE=DEBUG or =RELEASE # debug builds include source level debug info and extra logging -set(LWS_WITH_BUNDLED_ZLIB_DEFAULT OFF) -if(WIN32) - set(LWS_WITH_BUNDLED_ZLIB_DEFAULT ON) -endif() - -set(LWS_ROLE_RAW 1) -set(LWS_WITH_POLL 1) - -# it's at this point any toolchain file is brought in -project(libwebsockets C) - -# -# Select features recommended for PC distro packaging -# -option(LWS_WITH_DISTRO_RECOMMENDED "Enable features recommended for distro packaging" OFF) -option(LWS_FOR_GITOHASHI "Enable features recommended for use with gitohashi" OFF) - -# -# Major individual features -# -option(LWS_WITH_NETWORK "Compile with network-related code" ON) -option(LWS_ROLE_H1 "Compile with support for http/1 (needed for ws)" ON) -option(LWS_ROLE_WS "Compile with support for websockets" ON) -option(LWS_ROLE_MQTT "Build with support for MQTT client" OFF) -option(LWS_ROLE_DBUS "Compile with support for DBUS" OFF) -option(LWS_ROLE_RAW_PROXY "Raw packet proxy" OFF) -option(LWS_ROLE_RAW_FILE "Compile with support for raw files" ON) -option(LWS_WITH_HTTP2 "Compile with server support for HTTP/2" ON) -option(LWS_WITH_LWSWS "Libwebsockets Webserver" OFF) -option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out/err) APIs" OFF) -option(LWS_IPV6 "Compile with support for ipv6" OFF) -option(LWS_UNIX_SOCK "Compile with support for UNIX domain socket" OFF) -option(LWS_WITH_PLUGINS "Support plugins for protocols and extensions" OFF) -option(LWS_WITH_HTTP_PROXY "Support for active HTTP proxying" OFF) -option(LWS_WITH_ZIP_FOPS "Support serving pre-zipped files" OFF) -option(LWS_WITH_SOCKS5 "Allow use of SOCKS5 proxy on client connections" OFF) -option(LWS_WITH_GENERIC_SESSIONS "With the Generic Sessions plugin" OFF) -option(LWS_WITH_PEER_LIMITS "Track peers and restrict resources a single peer can allocate" OFF) -option(LWS_WITH_ACCESS_LOG "Support generating Apache-compatible access logs" OFF) -option(LWS_WITH_RANGES "Support http ranges (RFC7233)" OFF) -option(LWS_WITH_SERVER_STATUS "Support json + jscript server monitoring" OFF) -option(LWS_WITH_THREADPOOL "Managed worker thread pool support (relies on pthreads)" OFF) -option(LWS_WITH_HTTP_STREAM_COMPRESSION "Support HTTP stream compression" OFF) -option(LWS_WITH_HTTP_BROTLI "Also offer brotli http stream compression (requires LWS_WITH_HTTP_STREAM_COMPRESSION)" OFF) -option(LWS_WITH_ACME "Enable support for ACME automatic cert acquisition + maintenance (letsencrypt etc)" OFF) -option(LWS_WITH_HUBBUB "Enable libhubbub rewriting support" OFF) -option(LWS_WITH_ALSA "Enable alsa audio example" OFF) -option(LWS_WITH_GTK "Enable gtk example" OFF) -option(LWS_WITH_FTS "Full Text Search support" OFF) -option(LWS_WITH_SYS_ASYNC_DNS "Nonblocking internal IPv4 + IPv6 DNS resolver" OFF) -option(LWS_WITH_SYS_NTPCLIENT "Build in tiny ntpclient good for tls date validation and run via lws_system" OFF) -option(LWS_WITH_SYS_DHCP_CLIENT "Build in tiny DHCP client" OFF) -option(LWS_WITH_HTTP_BASIC_AUTH "Support Basic Auth" ON) -option(LWS_WITH_HTTP_UNCOMMON_HEADERS "Include less common http header support" ON) - -# -# Secure Streams -# -option(LWS_WITH_SECURE_STREAMS "Secure Streams protocol-agnostic API" OFF) -option(LWS_WITH_SECURE_STREAMS_PROXY_API "Secure Streams support to work across processes" OFF) -option(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM "Auth support for api.amazon.com" OFF) - -# -# TLS library options... all except mbedTLS are basically OpenSSL variants. -# -option(LWS_WITH_SSL "Include SSL support (defaults to OpenSSL or similar, mbedTLS if LWS_WITH_MBEDTLS is set)" ON) -option(LWS_WITH_MBEDTLS "Use mbedTLS (>=2.0) replacement for OpenSSL. When setting this, you also may need to specify LWS_MBEDTLS_LIBRARIES and LWS_MBEDTLS_INCLUDE_DIRS" OFF) -option(LWS_WITH_BORINGSSL "Use BoringSSL replacement for OpenSSL" OFF) -option(LWS_WITH_CYASSL "Use CyaSSL replacement for OpenSSL. When setting this, you also need to specify LWS_CYASSL_LIBRARIES and LWS_CYASSL_INCLUDE_DIRS" OFF) -option(LWS_WITH_WOLFSSL "Use wolfSSL replacement for OpenSSL. When setting this, you also need to specify LWS_WOLFSSL_LIBRARIES and LWS_WOLFSSL_INCLUDE_DIRS" OFF) -option(LWS_SSL_CLIENT_USE_OS_CA_CERTS "SSL support should make use of the OS-installed CA root certs" ON) -# -# Event library options (may select multiple, or none for default poll() -# -option(LWS_WITH_LIBEV "Compile with support for libev" OFF) -option(LWS_WITH_LIBUV "Compile with support for libuv" OFF) -option(LWS_WITH_LIBEVENT "Compile with support for libevent" OFF) -option(LWS_WITH_GLIB "Compile with support for glib event loop" OFF) - -# -# Static / Dynamic build options -# -option(LWS_WITH_STATIC "Build the static version of the library" ON) -option(LWS_WITH_SHARED "Build the shared version of the library" ON) -option(LWS_LINK_TESTAPPS_DYNAMIC "Link the test apps to the shared version of the library. Default is to link statically" OFF) -option(LWS_STATIC_PIC "Build the static version of the library with position-independent code" OFF) -# -# Specific platforms -# -option(LWS_WITH_ESP32 "Build for ESP32" OFF) -option(LWS_WITH_ESP32_HELPER "Build ESP32 helper" OFF) -option(LWS_PLAT_OPTEE "Build for OPTEE" OFF) -option(LWS_PLAT_FREERTOS "Build for FreeRTOS" OFF) -option(LWS_PLAT_ANDROID "Android flavour of unix platform" OFF) - -# -# Client / Server / Test Apps build control -# -option(LWS_WITHOUT_CLIENT "Don't build the client part of the library" OFF) -option(LWS_WITHOUT_SERVER "Don't build the server part of the library" OFF) -option(LWS_WITHOUT_TESTAPPS "Don't build the libwebsocket-test-apps" OFF) -option(LWS_WITHOUT_TEST_SERVER "Don't build the test server" OFF) -option(LWS_WITHOUT_TEST_SERVER_EXTPOLL "Don't build the test server version that uses external poll" OFF) -option(LWS_WITHOUT_TEST_PING "Don't build the ping test application" OFF) -option(LWS_WITHOUT_TEST_CLIENT "Don't build the client test application" OFF) -# -# Extensions (permessage-deflate) -# -option(LWS_WITHOUT_EXTENSIONS "Don't compile with extensions" ON) -# -# Helpers + misc -# -option(LWS_WITHOUT_BUILTIN_GETIFADDRS "Don't use the BSD getifaddrs implementation from libwebsockets if it is missing (this will result in a compilation error) ... The default is to assume that your libc provides it. On some systems such as uclibc it doesn't exist." OFF) -option(LWS_FALLBACK_GETHOSTBYNAME "Also try to do dns resolution using gethostbyname if getaddrinfo fails" OFF) -option(LWS_WITHOUT_BUILTIN_SHA1 "Don't build the lws sha-1 (eg, because openssl will provide it" OFF) -option(LWS_WITHOUT_DAEMONIZE "Don't build the daemonization api" ON) -option(LWS_SSL_SERVER_WITH_ECDH_CERT "Include SSL server use ECDH certificate" OFF) -option(LWS_WITH_LEJP "With the Lightweight JSON Parser" ON) -option(LWS_WITH_SQLITE3 "Require SQLITE3 support" OFF) -option(LWS_WITH_STRUCT_JSON "Generic struct serialization to and from JSON" OFF) -option(LWS_WITH_STRUCT_SQLITE3 "Generic struct serialization to and from SQLITE3" OFF) -# broken atm -#option(LWS_WITH_SMTP "Provide SMTP support" OFF) -if (WIN32 OR LWS_WITH_ESP32) -option(LWS_WITH_DIR "Directory scanning api support" OFF) -option(LWS_WITH_LEJP_CONF "With LEJP configuration parser as used by lwsws" OFF) -else() -option(LWS_WITH_DIR "Directory scanning api support" ON) -option(LWS_WITH_LEJP_CONF "With LEJP configuration parser as used by lwsws" ON) -endif() -option(LWS_WITH_NO_LOGS "Disable all logging other than _err and _user from being compiled in" OFF) -set(LWS_LOGGING_BITFIELD_SET 0 CACHE STRING "Bitfield describing which log levels to force included into the build") -set(LWS_LOGGING_BITFIELD_CLEAR 0 CACHE STRING "Bitfield describing which log levels to force removed from the build") -option(LWS_LOGS_TIMESTAMP "Timestamp at start of logs" ON) -option(LWS_AVOID_SIGPIPE_IGN "Android 7+ reportedly needs this" OFF) -option(LWS_WITH_STATS "Keep statistics of lws internal operations" OFF) -option(LWS_WITH_JOSE "JSON Web Signature / Encryption / Keys (RFC7515/6/) API" OFF) -option(LWS_WITH_GENCRYPTO "Enable support for Generic Crypto apis independent of TLS backend" OFF) -option(LWS_WITH_SELFTESTS "Selftests run at context creation" OFF) -option(LWS_WITH_GCOV "Build with gcc gcov coverage instrumentation" OFF) -option(LWS_WITH_EXPORT_LWSTARGETS "Export libwebsockets CMake targets. Disable if they conflict with an outer cmake project." ON) -option(LWS_REPRODUCIBLE "Build libwebsockets reproducible. It removes the build user and hostname from the build" ON) -option(LWS_WITH_MINIMAL_EXAMPLES "Also build the normally standalone minimal examples, for QA" OFF) -option(LWS_WITH_LWSAC "lwsac Chunk Allocation api" ON) -option(LWS_WITH_CUSTOM_HEADERS "Store and allow querying custom HTTP headers (H1 only)" ON) -option(LWS_WITH_DISKCACHE "Hashed cache directory with lazy LRU deletion to size limit" OFF) -option(LWS_WITH_ASAN "Build with gcc runtime sanitizer options enabled (needs libasan)" OFF) -option(LWS_WITH_DIR "Directory scanning api support" OFF) -option(LWS_WITH_LEJP_CONF "With LEJP configuration parser as used by lwsws" OFF) -option(LWS_WITH_ZLIB "Include zlib support (required for extensions)" OFF) -option(LWS_WITH_BUNDLED_ZLIB "Use bundled zlib version (Windows only)" ${LWS_WITH_BUNDLED_ZLIB_DEFAULT}) -option(LWS_WITH_MINIZ "Use miniz instead of zlib" OFF) -option(LWS_WITH_DEPRECATED_LWS_DLL "Migrate to lws_dll2 instead ASAP" OFF) -option(LWS_WITH_SEQUENCER "lws_seq_t support" ON) -option(LWS_WITH_EXTERNAL_POLL "Support external POLL integration using callback messages (not recommended)" OFF) -option(LWS_WITH_LWS_DSH "Support lws_dsh_t Disordered Shared Heap" OFF) -option(LWS_CLIENT_HTTP_PROXYING "Support external http proxies for client connections" ON) -option(LWS_WITH_FILE_OPS "Support file operations vfs" ON) -option(LWS_WITH_DETAILED_LATENCY "Record detailed latency stats for each read and write" OFF) -option(LWS_WITH_UDP "Platform supports UDP" ON) -option(LWS_WITH_SPAWN "Spawn subprocesses with piped stdin/out/stderr" OFF) -option(LWS_WITH_FSMOUNT "Overlayfs and fallback mounting apis" OFF) - - -# -# to use miniz, enable both LWS_WITH_ZLIB and LWS_WITH_MINIZ -# -# End of user settings -# - -if(IOS) - set(LWS_DETECTED_PLAT_IOS 1) -endif() - -# Workaround for ESP-IDF -# Detect ESP_PLATFORM environment flag, if exist, set LWS_WITH_ESP32. -# Otherwise the user may not be able to run configuration ESP-IDF in the first time. -if(ESP_PLATFORM) - message(STATUS "ESP-IDF enabled") - set(LWS_WITH_ESP32 ON) -else() - set(LWS_WITH_ESP32_HELPER OFF) -endif() - -if (LWS_WITH_ESP32) - set(LWS_PLAT_FREERTOS 1) -endif() - -if (LWS_PLAT_FREERTOS OR LWS_PLAT_OPTEE) - set(LWS_WITH_UDP 0) -endif() - -if (WIN32 OR LWS_PLAT_FREERTOS) - message(STATUS "No LWS_WITH_DIR or LWS_WITH_LEJP_CONF") - set(LWS_WITH_DIR OFF) - set(LWS_WITH_LEJP_CONF OFF) - message("LWS_WITH_DIR ${LWS_WITH_DIR}") -else() - message(STATUS "Compiled with LWS_WITH_DIR and LWS_WITH_LEJP_CONF") - set(LWS_WITH_DIR ON) - set(LWS_WITH_LEJP_CONF ON) -endif() - -if (LWS_FOR_GITOHASHI) - set(LWS_WITH_THREADPOOL 1) - set(LWS_WITH_HTTP2 1) - set(LWS_UNIX_SOCK 1) - set(LWS_WITH_HTTP_PROXY 1) - set(LWS_WITH_FTS 1) - set(LWS_WITH_DISKCACHE 1) - set(LWS_WITH_LWSAC 1) - set(LWS_WITH_LEJP_CONF 1) - set(LWS_WITH_SPAWN 1) - set(LWS_WITH_FSMOUNT 1) - set(LWS_WITH_STRUCT_JSON 1) - set(LWS_WITH_STRUCT_SQLITE3 1) -endif() - -if(LWS_WITH_DISTRO_RECOMMENDED) - set(LWS_WITH_HTTP2 1) - set(LWS_WITH_LWSWS 1) - set(LWS_WITH_CGI 1) - set(LWS_IPV6 1) - set(LWS_WITH_ZIP_FOPS 1) - set(LWS_WITH_SOCKS5 1) - set(LWS_WITH_RANGES 1) - set(LWS_WITH_ACME 1) - set(LWS_WITH_SERVER_STATUS 1) - set(LWS_WITH_GLIB 1) - set(LWS_WITH_LIBUV 1) - set(LWS_WITH_LIBEV 1) - # libev + libevent cannot coexist at build-time - set(LWS_WITH_LIBEVENT 0) - set(LWS_WITHOUT_EXTENSIONS 0) - set(LWS_ROLE_DBUS 1) - set(LWS_WITH_FTS 1) - set(LWS_WITH_THREADPOOL 1) - set(LWS_UNIX_SOCK 1) - set(LWS_WITH_HTTP_PROXY 1) - set(LWS_WITH_DISKCACHE 1) - set(LWS_WITH_LWSAC 1) - set(LWS_WITH_LEJP_CONF 1) - set(LWS_WITH_PLUGINS 1) - set(LWS_ROLE_RAW_PROXY 1) - set(LWS_WITH_GENCRYPTO 1) - set(LWS_WITH_JOSE 1) - set(LWS_WITH_STRUCT_JSON 1) - set(LWS_WITH_STRUCT_SQLITE3 1) - set(LWS_WITH_SPAWN 1) - set(LWS_WITH_FSMOUNT 1) - set(LWS_ROLE_MQTT 1) -endif() - -if (LWS_WITH_SECURE_STREAMS_PROXY_API) - set(LWS_WITH_LWS_DSH 1) - set(LWS_WITH_UNIX_SOCK 1) -endif() - -if (NOT LWS_WITH_NETWORK) - set(LWS_ROLE_MQTT 0) - set(LWS_ROLE_H1 0) - set(LWS_ROLE_WS 0) - set(LWS_ROLE_RAW 0) - set(LWS_WITHOUT_EXTENSIONS 1) - set(LWS_WITHOUT_SERVER 1) - set(LWS_WITHOUT_CLIENT 1) - set(LWS_WITH_HTTP2 0) - set(LWS_WITH_SOCKS5 0) - set(LWS_UNIX_SOCK 0) - set(LWS_WITH_HTTP_PROXY 0) - set(LWS_WITH_PLUGINS 0) - set(LWS_WITH_LWSWS 0) - set(LWS_WITH_CGI 0) - set(LWS_ROLE_RAW_PROXY 0) - set(LWS_WITH_PEER_LIMITS 0) - set(LWS_WITH_GENERIC_SESSIONS 0) - set(LWS_WITH_HTTP_STREAM_COMPRESSION 0) - set(LWS_WITH_HTTP_BROTLI 0) - set(LWS_WITH_POLL 0) - set(LWS_WITH_SEQUENCER 0) - set(LWS_ROLE_DBUS 0) - set(LWS_WITH_LWS_DSH 0) - set(LWS_WITH_THREADPOOL 0) -endif() - -if (LWS_WITH_CGI) - set(LWS_WITH_SPAWN 1) -endif() - -if (LWS_WITH_STRUCT_SQLITE3) - set(LWS_WITH_SQLITE3 1) -endif() - -# do you care about this? Then send me a patch where it disables it on travis -# but allows it on APPLE -if (APPLE) - set(LWS_ROLE_DBUS 0) -endif() - if(NOT DEFINED CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type") endif() -# microsoft... that's why you can't have nice things - -if (WIN32 OR LWS_PLAT_FREERTOS) - set(LWS_UNIX_SOCK 0) -endif() - -if (LWS_PLAT_FREERTOS) - set(LWS_WITH_LWSAC 0) - set(LWS_WITH_FTS 0) -endif() +project(libwebsockets C) set(PACKAGE "libwebsockets") set(CPACK_PACKAGE_NAME "${PACKAGE}") -set(CPACK_PACKAGE_VERSION_MAJOR "4") -set(CPACK_PACKAGE_VERSION_MINOR "0") -set(CPACK_PACKAGE_VERSION_PATCH "20") -set(CPACK_PACKAGE_RELEASE 1) -set(CPACK_GENERATOR "RPM") +set(CPACK_PACKAGE_VERSION_MAJOR "2") +set(CPACK_PACKAGE_VERSION_MINOR "4") +set(CPACK_PACKAGE_VERSION_PATCH "2") set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") set(CPACK_PACKAGE_VENDOR "andy@warmcat.com") -set(CPACK_PACKAGE_CONTACT "andy@warmcat.com") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PACKAGE} ${PACKAGE_VERSION}") -set(SOVERSION "16") +set(SOVERSION "12") if(NOT CPACK_GENERATOR) if(UNIX) set(CPACK_GENERATOR "TGZ") @@ -353,86 +38,101 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") - message(STATUS "CMAKE_TOOLCHAIN_FILE='${CMAKE_TOOLCHAIN_FILE}'") -if(WIN32) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/win32port/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/win32port/version.rc @ONLY) - set(RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/win32port/version.rc) -endif() - # Try to find the current Git hash. find_package(Git) if(GIT_EXECUTABLE) execute_process( - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND "${GIT_EXECUTABLE}" describe --tags --always - OUTPUT_VARIABLE GIT_HASH - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - set(LWS_BUILD_HASH ${GIT_HASH}) - - # append the build user and hostname - if(NOT LWS_REPRODUCIBLE) - execute_process( - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND "whoami" - OUTPUT_VARIABLE GIT_USER - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - execute_process( - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND "hostname" - OUTPUT_VARIABLE GIT_HOST - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - string(REGEX REPLACE "([^\\])[\\]([^\\])" "\\1\\\\\\\\\\2" GIT_USER ${GIT_USER}) - set(LWS_BUILD_HASH ${GIT_USER}@${GIT_HOST}-${GIT_HASH}) - endif() - - message("Git commit hash: ${LWS_BUILD_HASH}") -endif() - -# translate old functionality enables to set up ROLE enables so nothing changes -if (LWS_WITH_HTTP2 AND LWS_WITHOUT_SERVER) - set(LWS_WITH_HTTP2 0) - message("HTTP2 disabled due to LWS_WITHOUT_SERVER") -endif() - -if (LWS_WITH_HTTP2) - set(LWS_ROLE_H2 1) -endif() -if (LWS_WITH_CGI) - set(LWS_ROLE_CGI 1) -endif() - -if (NOT LWS_ROLE_WS) - set(LWS_WITHOUT_EXTENSIONS 1) + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND "${GIT_EXECUTABLE}" describe + OUTPUT_VARIABLE GIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + execute_process( + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND "whoami" + OUTPUT_VARIABLE GIT_USER + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + execute_process( + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND "hostname" + OUTPUT_VARIABLE GIT_HOST + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REGEX REPLACE "([^\\])[\\]([^\\])" "\\1\\\\\\\\\\2" GIT_USER ${GIT_USER}) + set(LWS_BUILD_HASH ${GIT_USER}@${GIT_HOST}-${GIT_HASH}) + message("Git commit hash: ${LWS_BUILD_HASH}") endif() -if (LWS_WITH_MBEDTLS) - include_directories(lib/tls/mbedtls/wrapper/include) +set(LWS_WITH_BUNDLED_ZLIB_DEFAULT OFF) +if(WIN32) + set(LWS_WITH_BUNDLED_ZLIB_DEFAULT ON) endif() -include_directories(include plugins lib/core lib/core-net lib/event-libs include/abstract lib/tls lib/roles lib/event-libs/libuv lib/event-libs/poll lib/event-libs/libevent lib/event-libs/glib lib/event-libs/libev lib/jose/jwe lib/jose/jws lib/jose lib/misc lib/roles/http lib/roles/http/compression lib/roles/h1 lib/roles/h2 lib/roles/ws lib/roles/cgi lib/roles/dbus lib/roles/raw-proxy lib/abstract lib/system/async-dns lib/roles/mqtt) - -if (LWS_WITH_SECURE_STREAMS) - set(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM 1) -endif() +option(LWS_WITH_STATIC "Build the static version of the library" ON) +option(LWS_WITH_SHARED "Build the shared version of the library" ON) +option(LWS_WITH_SSL "Include SSL support (default OpenSSL, wolfSSL if LWS_WITH_WOLFSSL is set)" ON) +option(LWS_WITH_BORINGSSL "Use BoringSSL replacement for OpenSSL" OFF) +option(LWS_WITH_CYASSL "Use CyaSSL replacement for OpenSSL. When setting this, you also need to specify LWS_CYASSL_LIBRARIES and LWS_CYASSL_INCLUDE_DIRS" OFF) +option(LWS_WITH_WOLFSSL "Use wolfSSL replacement for OpenSSL. When setting this, you also need to specify LWS_WOLFSSL_LIBRARIES and LWS_WOLFSSL_INCLUDE_DIRS" OFF) +option(LWS_WITH_MBEDTLS "Use mbedTLS (>=2.0) replacement for OpenSSL. When setting this, you also need to specify LWS_MBEDTLS_LIBRARIES and LWS_MBEDTLS_INCLUDE_DIRS" OFF) +option(LWS_WITH_ZLIB "Include zlib support (required for extensions)" ON) +option(LWS_WITH_LIBEV "Compile with support for libev" OFF) +option(LWS_WITH_LIBUV "Compile with support for libuv" OFF) +option(LWS_WITH_LIBEVENT "Compile with support for libevent" OFF) +option(LWS_WITH_BUNDLED_ZLIB "Use bundled zlib version (Windows only)" ${LWS_WITH_BUNDLED_ZLIB_DEFAULT}) +option(LWS_SSL_CLIENT_USE_OS_CA_CERTS "SSL support should make use of the OS-installed CA root certs" ON) +option(LWS_WITHOUT_BUILTIN_GETIFADDRS "Don't use the BSD getifaddrs implementation from libwebsockets if it is missing (this will result in a compilation error) ... The default is to assume that your libc provides it. On some systems such as uclibc it doesn't exist." OFF) +option(LWS_WITHOUT_BUILTIN_SHA1 "Don't build the lws sha-1 (eg, because openssl will provide it" OFF) +option(LWS_WITHOUT_CLIENT "Don't build the client part of the library" OFF) +option(LWS_WITHOUT_SERVER "Don't build the server part of the library" OFF) +option(LWS_LINK_TESTAPPS_DYNAMIC "Link the test apps to the shared version of the library. Default is to link statically" OFF) +option(LWS_WITHOUT_TESTAPPS "Don't build the libwebsocket-test-apps" OFF) +option(LWS_WITHOUT_TEST_SERVER "Don't build the test server" OFF) +option(LWS_WITHOUT_TEST_SERVER_EXTPOLL "Don't build the test server version that uses external poll" OFF) +option(LWS_WITHOUT_TEST_PING "Don't build the ping test application" OFF) +option(LWS_WITHOUT_TEST_ECHO "Don't build the echo test application" OFF) +option(LWS_WITHOUT_TEST_CLIENT "Don't build the client test application" OFF) +option(LWS_WITHOUT_TEST_FRAGGLE "Don't build the ping test application" OFF) +option(LWS_WITHOUT_EXTENSIONS "Don't compile with extensions" OFF) +option(LWS_WITH_LATENCY "Build latency measuring code into the library" OFF) +option(LWS_WITHOUT_DAEMONIZE "Don't build the daemonization api" ON) +option(LWS_IPV6 "Compile with support for ipv6" OFF) +option(LWS_UNIX_SOCK "Compile with support for UNIX domain socket" OFF) +option(LWS_WITH_HTTP2 "Compile with server support for HTTP/2" OFF) +option(LWS_SSL_SERVER_WITH_ECDH_CERT "Include SSL server use ECDH certificate" OFF) +option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out/err) APIs" OFF) +option(LWS_WITH_HTTP_PROXY "Support for rewriting HTTP proxying (requires libhubbub)" OFF) +option(LWS_WITH_LWSWS "Libwebsockets Webserver" OFF) +option(LWS_WITH_PLUGINS "Support plugins for protocols and extensions" OFF) +option(LWS_WITH_ACCESS_LOG "Support generating Apache-compatible access logs" OFF) +option(LWS_WITH_SERVER_STATUS "Support json + jscript server monitoring" OFF) +option(LWS_WITH_LEJP "With the Lightweight JSON Parser" OFF) +option(LWS_WITH_LEJP_CONF "With LEJP configuration parser as used by lwsws" OFF) +option(LWS_WITH_GENERIC_SESSIONS "With the Generic Sessions plugin" OFF) +option(LWS_WITH_SQLITE3 "Require SQLITE3 support" OFF) +option(LWS_WITH_SMTP "Provide SMTP support" OFF) +option(LWS_WITH_ESP8266 "Build for ESP8266" OFF) +option(LWS_WITH_ESP32 "Build for ESP32" OFF) +option(LWS_PLAT_OPTEE "Build for OPTEE" OFF) +option(LWS_WITH_NO_LOGS "Disable all logging from being compiled in" OFF) +option(LWS_STATIC_PIC "Build the static version of the library with position-independent code" OFF) +option(LWS_WITH_RANGES "Support http ranges (RFC7233)" ON) +option(LWS_FALLBACK_GETHOSTBYNAME "Also try to do dns resolution using gethostbyname if getaddrinfo fails" OFF) +option(LWS_WITH_ZIP_FOPS "Support serving pre-zipped files" ON) +option(LWS_AVOID_SIGPIPE_IGN "Android 7+ seems to need this" OFF) +option(LWS_WITH_STATS "Keep statistics of lws internal operations" OFF) +option(LWS_WITH_SOCKS5 "Allow use of SOCKS5 proxy on client connections" OFF) +option(LWS_WITH_PEER_LIMITS "Track peers and restrict resources a single peer can allocate" OFF) -if (LWS_PLAT_FREERTOS) - include_directories(lib/plat/freertos lib/plat/freertos/esp32) -else() - if (WIN32) - include_directories(lib/plat/windows) - else() - if (LWS_WITH_OPTEE) - include_directories(lib/plat/optee) - else() - include_directories(lib/plat/unix) - endif() +macro(confirm_command CMD NOCMD) + find_program (HAVE_CMD_${CMD} ${CMD} ) + if (NOT HAVE_CMD_${CMD}) + message(FATAL_ERROR "Missing command ${CMD} required for build: ${NOCMD}" ) endif() -endif() +endmacro() if (LWS_WITH_LWSWS) @@ -444,47 +144,50 @@ set(LWS_WITH_LEJP 1) set(LWS_WITH_LEJP_CONF 1) set(LWS_WITH_PEER_LIMITS 1) - set(LWS_ROLE_RAW_PROXY 1) -endif() - -# sshd plugin -if (LWS_WITH_PLUGINS) - set(LWS_WITH_GENCRYPTO 1) endif() -if (LWS_ROLE_RAW_PROXY) - set (LWS_WITH_CLIENT 1) - set (LWS_WITH_SERVER 1) +if (LWS_WITH_PLUGINS AND NOT LWS_WITH_LIBUV) +message(STATUS "LWS_WITH_PLUGINS --> Enabling LWS_WITH_LIBUV") + set(LWS_WITH_LIBUV 1) endif() -if (LWS_WITH_ACME) - set (LWS_WITH_CLIENT 1) - set (LWS_WITH_SERVER 1) - set (LWS_WITH_JOSE 1) +if (LWS_WITH_SMTP AND NOT LWS_WITH_LIBUV) +message(STATUS "LWS_WITH_SMTP --> Enabling LWS_WITH_LIBUV") + set(LWS_WITH_LIBUV 1) endif() -if (LWS_WITH_JOSE) - set(LWS_WITH_LEJP 1) - set(LWS_WITH_GENCRYPTO 1) +if (LWS_WITH_GENERIC_SESSIONS) + set(LWS_WITH_SQLITE3 1) + set(LWS_WITH_SMTP 1) endif() -if (LWS_WITH_PLUGINS AND NOT LWS_WITH_LIBUV) -message(STATUS "LWS_WITH_PLUGINS --> Enabling LWS_WITH_LIBUV") +if (LWS_WITH_SMTP AND NOT LWS_WITH_LIBUV) +message(STATUS "LWS_WITH_SMTP --> Enabling LWS_WITH_LIBUV") set(LWS_WITH_LIBUV 1) endif() -if (LWS_WITH_PLUGINS OR LWS_WITH_CGI) - # sshd plugin - set(LWS_WITH_GENCRYPTO 1) +if (LWS_WITH_ESP8266) + set(LWS_WITH_SHARED OFF) + set(LWS_WITH_SSL OFF) + set(LWS_WITH_ZLIB OFF) + set(LWS_WITHOUT_CLIENT ON) + set(LWS_WITHOUT_TESTAPPS ON) + set(LWS_WITHOUT_EXTENSIONS ON) + set(LWS_WITH_PLUGINS OFF) + set(LWS_WITH_RANGES OFF) + # this implies no pthreads in the lib + set(LWS_MAX_SMP 1) + set(LWS_HAVE_MALLOC 1) + set(LWS_HAVE_REALLOC 1) + set(LWS_HAVE_GETIFADDRS 1) + set(LWS_WITH_ZIP_FOPS 0) endif() -if (LWS_WITH_GENERIC_SESSIONS) - set(LWS_WITH_SQLITE3 1) - # set(LWS_WITH_SMTP 1) - set(LWS_WITH_STRUCT_SQLITE3 1) -endif() +if (LWS_WITH_ESP32) + + confirm_command(xxd "usually found in vim package") + confirm_command(genromfs "install genromfs package") -if (LWS_PLAT_FREERTOS) set(LWS_WITH_SHARED OFF) set(LWS_WITH_MBEDTLS ON) # set(LWS_WITHOUT_CLIENT ON) @@ -497,62 +200,22 @@ set(LWS_HAVE_MALLOC 1) set(LWS_HAVE_REALLOC 1) set(LWS_HAVE_GETIFADDRS 1) - set(LWS_WITH_CUSTOM_HEADERS 0) -endif() - -if (LWS_WITH_ESP32) set(LWS_WITH_ZIP_FOPS 1) endif() + if (WIN32) +# this implies no pthreads in the lib set(LWS_MAX_SMP 1) -set(LWS_WITH_THREADPOOL 0) endif() + if (LWS_WITHOUT_SERVER) set(LWS_WITH_LWSWS OFF) endif() -if (LWS_WITH_LEJP_CONF) - set(LWS_WITH_DIR 1) -endif() - -# confirm H1 relationships - -if (NOT LWS_ROLE_H1 AND LWS_ROLE_H2) - message(FATAL_ERROR "H2 requires LWS_ROLE_H1") -endif() - -if (NOT LWS_ROLE_H1 AND LWS_ROLE_WS) - message(FATAL_ERROR "WS requires LWS_ROLE_H1") -endif() - -if (NOT LWS_ROLE_H1 AND LWS_ROLE_CGI) - message(FATAL_ERROR "CGI requires LWS_ROLE_H1") -endif() - -# confirm HTTP relationships - -if (NOT LWS_ROLE_H1 AND NOT LWS_ROLE_H2 AND LWS_WITH_HTTP_PROXY) - message(FATAL_ERROR "LWS_WITH_LWSWS requires LWS_ROLE_H1") -endif() - -if (NOT LWS_ROLE_H1 AND NOT LWS_ROLE_H2 AND LWS_WITH_HTTP_PROXY) - message(FATAL_ERROR "LWS_WITH_HTTP_PROXY requires LWS_ROLE_H1") -endif() - -if (NOT LWS_ROLE_H1 AND NOT LWS_ROLE_H2 AND LWS_WITH_RANGES) - message(FATAL_ERROR "LWS_WITH_RANGES requires LWS_ROLE_H1") -endif() - -if (NOT LWS_ROLE_H1 AND NOT LWS_ROLE_H2 AND LWS_WITH_ACCESS_LOG) - message(FATAL_ERROR "LWS_WITH_ACCESS_LOG requires LWS_ROLE_H1") -endif() - - if (LWS_WITH_HTTP_PROXY AND (LWS_WITHOUT_CLIENT OR LWS_WITHOUT_SERVER)) - message("You have to enable both client and server for http proxy") - set(LWS_WITH_HTTP_PROXY 0) + message(FATAL_ERROR "You have to enable both client and server for http proxy") endif() # Allow the user to override installation directories. @@ -572,19 +235,22 @@ set(LWS_WOLFSSL_INCLUDE_DIRS ${LWS_CYASSL_INCLUDE_DIRS} CACHE PATH "Path to wolfSSL/CyaSSL header files" FORCE) endif() +if (LWS_WITHOUT_CLIENT AND LWS_WITHOUT_SERVER) + message(FATAL_ERROR "Makes no sense to compile with neither client nor server.") +endif() + if (NOT (LWS_WITH_STATIC OR LWS_WITH_SHARED)) message(FATAL_ERROR "Makes no sense to compile with neither static nor shared libraries.") endif() -if (NOT LWS_WITHOUT_EXTENSIONS OR LWS_WITH_ZIP_FOPS) - set(LWS_WITH_ZLIB 1) +if (NOT LWS_WITHOUT_EXTENSIONS) + if (NOT LWS_WITH_ZLIB) + message(FATAL_ERROR "zlib is required for extensions.") + endif() endif() -# if you gave LWS_WITH_MINIZ, point to MINIZ here if not found -# automatically - -set(LWS_ZLIB_LIBRARIES CACHE PATH "Path to the zlib/miniz library") -set(LWS_ZLIB_INCLUDE_DIRS CACHE PATH "Path to the zlib/miniz include directory") +set(LWS_ZLIB_LIBRARIES CACHE PATH "Path to the zlib library") +set(LWS_ZLIB_INCLUDE_DIRS CACHE PATH "Path to the zlib include directory") set(LWS_OPENSSL_LIBRARIES CACHE PATH "Path to the OpenSSL library") set(LWS_OPENSSL_INCLUDE_DIRS CACHE PATH "Path to the OpenSSL include directory") set(LWS_WOLFSSL_LIBRARIES CACHE PATH "Path to the wolfSSL library") @@ -597,25 +263,16 @@ set(LWS_SQLITE3_INCLUDE_DIRS CACHE PATH "Path to the sqlite3 include directory") set(LWS_LIBEVENT_INCLUDE_DIRS CACHE PATH "Path to the libevent include directory") set(LWS_LIBEVENT_LIBRARIES CACHE PATH "Path to the libevent library") -set(LWS_GLIB_INCLUDE_DIRS CACHE PATH "Path to the glib include directory") -set(LWS_GLIB_LIBRARIES CACHE PATH "Path to the glib library") -set(LWS_LIBMOUNT_INCLUDE_DIRS CACHE PATH "Path to the libmount include directory") -set(LWS_LIBMOUNT_LIBRARIES CACHE PATH "Path to the libmount library") if (NOT LWS_WITH_SSL) set(LWS_WITHOUT_BUILTIN_SHA1 OFF) endif() -if (LWS_WITH_BORINGSSL) - # boringssl deprecated EVP_PKEY - set (LWS_WITH_GENHASH OFF) -endif() - if (LWS_WITH_SSL AND NOT LWS_WITH_WOLFSSL AND NOT LWS_WITH_MBEDTLS) if ("${LWS_OPENSSL_LIBRARIES}" STREQUAL "" OR "${LWS_OPENSSL_INCLUDE_DIRS}" STREQUAL "") else() - if (NOT LWS_PLAT_FREERTOS) + if (NOT LWS_WITH_ESP32) set(OPENSSL_LIBRARIES ${LWS_OPENSSL_LIBRARIES}) endif() set(OPENSSL_INCLUDE_DIRS ${LWS_OPENSSL_INCLUDE_DIRS}) @@ -638,14 +295,13 @@ set(WOLFSSL_FOUND 1) endif() set(USE_WOLFSSL 1) - set(LWS_WITH_TLS 1) if (LWS_WITH_CYASSL) set(USE_OLD_CYASSL 1) endif() endif() if (LWS_WITH_SSL AND LWS_WITH_MBEDTLS) - if ("${LWS_MBEDTLS_LIBRARIES}" STREQUAL "" OR "${LWS_MBEDTLS_INCLUDE_DIRS}" STREQUAL "" AND NOT LWS_PLAT_FREERTOS) + if ("${LWS_MBEDTLS_LIBRARIES}" STREQUAL "" OR "${LWS_MBEDTLS_INCLUDE_DIRS}" STREQUAL "" AND NOT LWS_WITH_ESP32) find_path(LWS_MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h) @@ -671,27 +327,6 @@ set(USE_MBEDTLS 1) endif() -if (LWS_WITH_HTTP_STREAM_COMPRESSION) - set(LWS_WITH_ZLIB 1) -endif() - -if (LWS_WITH_FSMOUNT AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - if ("${LWS_LIBMOUNT_LIBRARIES}" STREQUAL "" OR "${LWS_LIBMOUNT_INCLUDE_DIRS}" STREQUAL "") - - # libmount paths (this is only on Linux) - # - find_path( LIBMOUNT_INC_PATH NAMES "libmount/libmount.h") - find_library(LIBMOUNT_LIB_PATH NAMES "mount") - else() - set(LIBMOUNT_INC_PATH ${LWS_LIBMOUNT_INCLUDE_DIRS}) - set(LIBMOUNT_LIB_PATH ${LWS_LIBMOUNT_LIBRARIES}) - endif() - - if (NOT LIBMOUNT_INC_PATH OR NOT LIBMOUNT_LIB_PATH) - message(FATAL_ERROR " Unable to find libmount") - endif() -endif() - if (LWS_WITH_ZLIB AND NOT LWS_WITH_BUNDLED_ZLIB) if ("${LWS_ZLIB_LIBRARIES}" STREQUAL "" OR "${LWS_ZLIB_INCLUDE_DIRS}" STREQUAL "") else() @@ -728,15 +363,6 @@ endif() endif() -if (LWS_WITH_GLIB) - if ("${LWS_GLIB_LIBRARIES}" STREQUAL "" OR "${LWS_GLIB_INCLUDE_DIRS}" STREQUAL "") - else() - set(LIBGLIB_LIBRARIES ${LWS_GLIB_LIBRARIES}) - set(LIBGLIB_INCLUDE_DIRS ${LWS_GLIB_INCLUDE_DIRS}) - set(LIBGLIB_FOUND 1) - endif() -endif() - if (LWS_WITH_SQLITE3) if ("${LWS_SQLITE3_LIBRARIES}" STREQUAL "" OR "${LWS_SQLITE3_INCLUDE_DIRS}" STREQUAL "") else() @@ -747,10 +373,6 @@ endif() -if (LWS_WITH_LIBEV AND LWS_WITH_LIBEVENT) - message(FATAL_ERROR "Sorry libev and libevent conflict with each others' namespace, you can only have one or the other") -endif() - # The base dir where the test-apps look for the SSL certs. set(LWS_OPENSSL_CLIENT_CERTS ../share CACHE PATH "Server SSL certificate directory") if (WIN32) @@ -764,20 +386,34 @@ set(LWS_OPENSSL_CLIENT_CERTS /etc/pki/tls/certs/ CACHE PATH "Client SSL certificate directory") endif() -# LWS_OPENSSL_SUPPORT deprecated... use LWS_WITH_TLS -if (LWS_WITH_SSL OR LWS_WITH_MBEDTLS) +if (LWS_WITHOUT_EXTENSIONS) + set(LWS_NO_EXTENSIONS 1) +endif() + +if (LWS_WITH_SSL) set(LWS_OPENSSL_SUPPORT 1) - set(LWS_WITH_TLS 1) endif() if (LWS_SSL_CLIENT_USE_OS_CA_CERTS) set(LWS_SSL_CLIENT_USE_OS_CA_CERTS 1) endif() +if (LWS_WITH_LATENCY) + set(LWS_LATENCY 1) +endif() + if (LWS_WITHOUT_DAEMONIZE OR WIN32) set(LWS_NO_DAEMONIZE 1) endif() +if (LWS_WITHOUT_SERVER) + set(LWS_NO_SERVER 1) +endif() + +if (LWS_WITHOUT_CLIENT) + set(LWS_NO_CLIENT 1) +endif() + if (LWS_WITH_LIBEV) set(LWS_WITH_LIBEV 1) endif() @@ -806,27 +442,14 @@ set(LWS_MAX_SMP 1) endif() -set(LWS_WITH_CLIENT 1) -if (LWS_WITHOUT_CLIENT) - set(LWS_WITH_CLIENT) -endif() -set(LWS_WITH_SERVER 1) -if (LWS_WITHOUT_SERVER) - set(LWS_WITH_SERVER) -endif() - -# using any abstract protocol enables LWS_WITH_ABSTRACT - -#if (LWS_WITH_SMTP) -# set(LWS_WITH_ABSTRACT 1) -#endif() - +if (LWS_WITH_ESP8266) +set(CMAKE_C_FLAGS "-nostdlib ${CMAKE_C_FLAGS}") +endif() if (MINGW) set(LWS_MINGW_SUPPORT 1) set(CMAKE_C_FLAGS "-D__USE_MINGW_ANSI_STDIO ${CMAKE_C_FLAGS}") - add_definitions(-DWINVER=0x0601 -D_WIN32_WINNT=0x0601) endif() if (LWS_SSL_SERVER_WITH_ECDH_CERT) @@ -874,7 +497,6 @@ include(CheckIncludeFiles) include(CheckLibraryExists) include(CheckTypeSize) -include(CheckCSourceCompiles) if (LWS_WITHOUT_BUILTIN_SHA1) set(LWS_SHA1_USE_OPENSSL_NAME 1) @@ -884,15 +506,7 @@ set(CMAKE_REQUIRED_LIBRARIES network) endif() -CHECK_C_SOURCE_COMPILES( - "#include - int main(int argc, char **argv) { return malloc_trim(0); } - " LWS_HAVE_MALLOC_TRIM) -CHECK_C_SOURCE_COMPILES( - "#include - int main(int argc, char **argv) { return (int)malloc_usable_size((void *)0); } - " LWS_HAVE_MALLOC_USABLE_SIZE) - +CHECK_FUNCTION_EXISTS(bzero LWS_HAVE_BZERO) CHECK_FUNCTION_EXISTS(fork LWS_HAVE_FORK) CHECK_FUNCTION_EXISTS(getenv LWS_HAVE_GETENV) CHECK_FUNCTION_EXISTS(malloc LWS_HAVE_MALLOC) @@ -910,8 +524,6 @@ CHECK_FUNCTION_EXISTS(atoll LWS_HAVE_ATOLL) CHECK_FUNCTION_EXISTS(_atoi64 LWS_HAVE__ATOI64) CHECK_FUNCTION_EXISTS(_stat32i64 LWS_HAVE__STAT32I64) -CHECK_FUNCTION_EXISTS(clock_gettime LWS_HAVE_CLOCK_GETTIME) -CHECK_FUNCTION_EXISTS(eventfd LWS_HAVE_EVENTFD) if (NOT LWS_HAVE_GETIFADDRS) if (LWS_WITHOUT_BUILTIN_GETIFADDRS) @@ -923,6 +535,7 @@ CHECK_INCLUDE_FILE(dlfcn.h LWS_HAVE_DLFCN_H) CHECK_INCLUDE_FILE(fcntl.h LWS_HAVE_FCNTL_H) CHECK_INCLUDE_FILE(in6addr.h LWS_HAVE_IN6ADDR_H) +CHECK_INCLUDE_FILE(inttypes.h LWS_HAVE_INTTYPES_H) CHECK_INCLUDE_FILE(memory.h LWS_HAVE_MEMORY_H) CHECK_INCLUDE_FILE(netinet/in.h LWS_HAVE_NETINET_IN_H) CHECK_INCLUDE_FILE(stdint.h LWS_HAVE_STDINT_H) @@ -937,77 +550,16 @@ CHECK_INCLUDE_FILE(unistd.h LWS_HAVE_UNISTD_H) CHECK_INCLUDE_FILE(vfork.h LWS_HAVE_VFORK_H) CHECK_INCLUDE_FILE(sys/capability.h LWS_HAVE_SYS_CAPABILITY_H) -CHECK_INCLUDE_FILE(malloc.h LWS_HAVE_MALLOC_H) -CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) -CHECK_INCLUDE_FILE(inttypes.h LWS_HAVE_INTTYPES_H) - -CHECK_LIBRARY_EXISTS(cap cap_set_flag "" LWS_HAVE_LIBCAP) - -if (LWS_ROLE_DBUS) - - if (NOT LWS_DBUS_LIB) - set(LWS_DBUS_LIB "dbus-1") - endif() - - CHECK_LIBRARY_EXISTS(${LWS_DBUS_LIB} dbus_connection_set_watch_functions "" LWS_HAVE_LIBDBUS) - if (NOT LWS_HAVE_LIBDBUS) - message(FATAL_ERROR "Install dbus-devel, or libdbus-1-dev etc") - endif() - - if (NOT LWS_DBUS_INCLUDE1) - # look in fedora and debian / ubuntu place - if (EXISTS "/usr/include/dbus-1.0") - set(LWS_DBUS_INCLUDE1 "/usr/include/dbus-1.0") - else() - message(FATAL_ERROR "Set LWS_DBUS_INCLUDE1 to /usr/include/dbus-1.0 or wherever the main dbus includes are") - endif() - endif() - - if (NOT LWS_DBUS_INCLUDE2) - # look in fedora... debian / ubuntu has the ARCH in the path... - if (EXISTS "/usr/lib64/dbus-1.0/include") - set(LWS_DBUS_INCLUDE2 "/usr/lib64/dbus-1.0/include") - else() - message(FATAL_ERROR "Set LWS_DBUS_INCLUDE2 to /usr/lib/ARCH-linux-gnu/dbus-1.0/include or wherever dbus-arch-deps.h is on your system") - endif() - endif() - set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES};${LWS_DBUS_INCLUDE1};${LWS_DBUS_INCLUDE2}) - - CHECK_C_SOURCE_COMPILES("#include - int main(void) { - return 0; - }" LWS_DBUS_CHECK_OK) -endif() +CHECK_LIBRARY_EXISTS(cap cap_set_flag "" LWS_HAVE_LIBCAP) if (LWS_WITH_LIBUV) CHECK_INCLUDE_FILE(uv-version.h LWS_HAVE_UV_VERSION_H) - # libuv changed the location in 1.21.0. Retain both - # checks temporarily to ensure a smooth transition. - if (NOT LWS_HAVE_UV_VERSION_H) - CHECK_INCLUDE_FILE(uv/version.h LWS_HAVE_NEW_UV_VERSION_H) - endif() -endif() - -if (LWS_WITH_LIBEV) - CHECK_C_SOURCE_COMPILES( - "#include - int main(int argc, char **argv) { return EVBACKEND_LINUXAIO; } - " LWS_HAVE_EVBACKEND_LINUXAIO) - CHECK_C_SOURCE_COMPILES( - "#include - int main(int argc, char **argv) { return EVBACKEND_IOURING; } - " LWS_HAVE_EVBACKEND_IOURING) - endif() if (LWS_WITH_ZLIB AND NOT LWS_WITH_BUNDLED_ZLIB) - if (LWS_WITH_MINIZ) - CHECK_INCLUDE_FILE(miniz.h LWS_HAVE_ZLIB_H) - else() - CHECK_INCLUDE_FILE(zlib.h LWS_HAVE_ZLIB_H) - endif() + CHECK_INCLUDE_FILE(zlib.h LWS_HAVE_ZLIB_H) endif() # TODO: These can also be tested to see whether they actually work... @@ -1022,29 +574,10 @@ return 0; }" LWS_HAS_INTPTR_T) -if (LWS_HAVE_PTHREAD_H) - if ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) - set(CMAKE_REQUIRED_FLAGS "-pthread -Wno-error=unused-command-line-argument") - else() - set(CMAKE_REQUIRED_FLAGS "-pthread") - endif() - - CHECK_C_SOURCE_COMPILES("#define _GNU_SOURCE - #include - int main(void) { - pthread_t th = 0; - pthread_setname_np(th, NULL); - return 0; - }" LWS_HAS_PTHREAD_SETNAME_NP) -endif() - -CHECK_C_SOURCE_COMPILES("#include - #include - int main(void) { - void *p = (void *)getopt_long; - return p != NULL; - }" LWS_HAS_GETOPT_LONG) - +# These don't work Cross... +#CHECK_TYPE_SIZE(pid_t PID_T_SIZE) +#CHECK_TYPE_SIZE(size_t SIZE_T_SIZE) +#CHECK_TYPE_SIZE("void *" LWS_SIZEOFPTR LANGUAGE C) if (NOT PID_T_SIZE) set(pid_t int) @@ -1062,17 +595,6 @@ set(realloc rpl_realloc) endif() -if (UNIX) - execute_process( COMMAND grep -c illumos /lib/ld.so.1 - OUTPUT_VARIABLE ILLUMOS ERROR_QUIET ) - # Chomp the \n at end of output. - string(REGEX REPLACE "[\n]+" "" ILLUMOS "${ILLUMOS}") - - if(NOT ${ILLUMOS} MATCHES "0") - add_definitions( "-D__illumos__" ) - set(ILLUMOS 1) - endif() -endif() if (MSVC) # Turn off stupid microsoft security warnings. @@ -1084,413 +606,103 @@ # Group headers and sources. # Some IDEs use this for nicer file structure. set(HDR_PRIVATE - lib/core/private-lib-core.h) + lib/private-libwebsockets.h) set(HDR_PUBLIC - "${PROJECT_SOURCE_DIR}/include/libwebsockets.h" + "${PROJECT_SOURCE_DIR}/lib/libwebsockets.h" "${PROJECT_BINARY_DIR}/lws_config.h" "${PROJECT_SOURCE_DIR}/plugins/ssh-base/include/lws-plugin-ssh.h" ) set(SOURCES - lib/core/alloc.c - lib/core/buflist.c - lib/core/context.c - lib/core/lws_dll2.c - lib/core/libwebsockets.c - lib/core/logs.c - lib/system/system.c lib/misc/base64-decode.c - lib/misc/lws-ring.c -) - -if (LWS_WITH_SPAWN) - list(APPEND SOURCES lib/misc/spawn.c) -endif() - -if (LWS_WITH_FSMOUNT AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - list(APPEND SOURCES lib/misc/fsmount.c) -endif() - - - -if (LWS_WITH_FILE_OPS) - list(APPEND SOURCES lib/core/vfs.c) -endif() - -if (LWS_WITH_DEPRECATED_LWS_DLL) - list(APPEND SOURCES - lib/core/lws_dll.c) -endif() - -if (LWS_WITH_NETWORK) - list(APPEND SOURCES - lib/core-net/dummy-callback.c - lib/core-net/output.c - lib/core-net/close.c - lib/core-net/network.c - lib/core-net/vhost.c - lib/core-net/pollfd.c - lib/core-net/service.c - lib/core-net/sorted-usec-list.c - lib/core-net/state.c - lib/core-net/stats.c - lib/core-net/wsi.c - lib/core-net/wsi-timeout.c - lib/core-net/adopt.c - lib/roles/pipe/ops-pipe.c - ) - - if (LWS_WITH_SYS_ASYNC_DNS) - list(APPEND SOURCES - lib/system/async-dns/async-dns.c - lib/system/async-dns/async-dns-parse.c) - endif() - - if (LWS_WITH_SYS_NTPCLIENT) - list(APPEND SOURCES - lib/system/ntpclient/ntpclient.c) - endif() - - if (LWS_WITH_SYS_DHCP_CLIENT) - list(APPEND SOURCES - lib/system/dhcpclient/dhcpclient.c) - endif() - + lib/handshake.c + lib/libwebsockets.c + lib/service.c + lib/pollfd.c + lib/output.c + lib/server/parsers.c + lib/context.c + lib/alloc.c + lib/header.c + lib/misc/lws-ring.c) - if (LWS_WITH_DETAILED_LATENCY) - list(APPEND SOURCES - lib/core-net/detailed-latency.c) - endif() - - if (LWS_WITH_LWS_DSH) - list(APPEND SOURCES - lib/core-net/lws-dsh.c) - endif() - - if (LWS_WITH_SEQUENCER) - list(APPEND SOURCES - lib/core-net/sequencer.c) - endif() - - if (LWS_WITH_ABSTRACT) - list(APPEND SOURCES - lib/abstract/abstract.c - ) - if (LWS_WITH_SEQUENCER) - list(APPEND SOURCES - lib/abstract/test-sequencer.c) - endif() - endif() - - if (LWS_WITH_SECURE_STREAMS) - list(APPEND SOURCES - lib/secure-streams/secure-streams.c - lib/secure-streams/policy.c - lib/secure-streams/system/fetch-policy/fetch-policy.c - ) - if (LWS_ROLE_H1) - list(APPEND SOURCES - lib/secure-streams/protocols/ss-h1.c - ) - endif() - if (LWS_ROLE_H2) - list(APPEND SOURCES - lib/secure-streams/protocols/ss-h2.c - ) - endif() - if (LWS_ROLE_WS) - list(APPEND SOURCES - lib/secure-streams/protocols/ss-ws.c - ) - endif() - if (LWS_ROLE_MQTT) - list(APPEND SOURCES - lib/secure-streams/protocols/ss-mqtt.c - ) - endif() - - if (LWS_WITH_SECURE_STREAMS_PROXY_API) - list(APPEND SOURCES - lib/secure-streams/secure-streams-serialize.c - lib/secure-streams/secure-streams-client.c - ) - endif() - - if (LWS_WITH_SECURE_STREAMS_PROXY_API) - list(APPEND SOURCES - lib/secure-streams/secure-streams-process.c - ) - endif() - - if (LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM) - list(APPEND SOURCES - lib/secure-streams/system/auth-api.amazon.com/auth.c - ) - endif() - endif() - - if (LWS_WITH_STATS) - list(APPEND SOURCES - lib/core-net/stats.c - ) - endif() -endif() - -if (LWS_WITH_DIR) - list(APPEND SOURCES lib/misc/dir.c) -endif() - -if (LWS_ROLE_MQTT AND LWS_WITH_CLIENT) - list(APPEND SOURCES - lib/roles/mqtt/mqtt.c - lib/roles/mqtt/ops-mqtt.c - lib/roles/mqtt/primitives.c - lib/roles/mqtt/client/client-mqtt.c - lib/roles/mqtt/client/client-mqtt-handshake.c - ) -endif() - -if (LWS_WITH_THREADPOOL AND UNIX AND LWS_HAVE_PTHREAD_H) - list(APPEND SOURCES lib/misc/threadpool/threadpool.c) -endif() - -if (LWS_ROLE_H1 OR LWS_ROLE_H2) - list(APPEND SOURCES - lib/roles/http/header.c - lib/roles/http/parsers.c) - if (LWS_WITH_HTTP_STREAM_COMPRESSION) - list(APPEND SOURCES - lib/roles/http/compression/stream.c - lib/roles/http/compression/deflate/deflate.c) - if (LWS_WITH_HTTP_BROTLI) - list(APPEND SOURCES - lib/roles/http/compression/brotli/brotli.c) - endif() - endif() -endif() - -if (LWS_ROLE_H1) - list(APPEND SOURCES - lib/roles/h1/ops-h1.c) -endif() - -if (LWS_ROLE_WS) - list(APPEND SOURCES - lib/roles/ws/ops-ws.c) - if (NOT LWS_WITHOUT_CLIENT) - list(APPEND SOURCES - lib/roles/ws/client-ws.c - lib/roles/ws/client-parser-ws.c) - endif() - if (NOT LWS_WITHOUT_SERVER) - list(APPEND SOURCES - lib/roles/ws/server-ws.c) - endif() -endif() - -if (LWS_ROLE_RAW) - list(APPEND SOURCES - lib/roles/raw-skt/ops-raw-skt.c) - if (LWS_ROLE_RAW_FILE) - list(APPEND SOURCES lib/roles/raw-file/ops-raw-file.c) - endif() - - if (LWS_WITH_ABSTRACT) - list(APPEND SOURCES - lib/abstract/transports/raw-skt.c) - endif() -endif() - -if (LWS_ROLE_RAW_PROXY) - list(APPEND SOURCES - lib/roles/raw-proxy/ops-raw-proxy.c) -endif() - -if (LWS_ROLE_CGI) - list(APPEND SOURCES - lib/roles/cgi/cgi-server.c - lib/roles/cgi/ops-cgi.c) -endif() - -if (LWS_ROLE_DBUS) +if (LWS_WITH_CGI) list(APPEND SOURCES - lib/roles/dbus/dbus.c) + lib/server/cgi.c) endif() if (LWS_WITH_ACCESS_LOG) list(APPEND SOURCES - lib/roles/http/server/access-log.c) + lib/server/access-log.c) endif() if (LWS_WITH_PEER_LIMITS) list(APPEND SOURCES - lib/misc/peer-limits.c) + lib/server/peer-limits.c) endif() -if (LWS_WITH_LWSAC) - list(APPEND SOURCES - lib/misc/lwsac/lwsac.c - lib/misc/lwsac/cached-file.c) -endif() - -if (LWS_WITH_FTS) +if (NOT LWS_WITHOUT_CLIENT) list(APPEND SOURCES - lib/misc/fts/trie.c - lib/misc/fts/trie-fd.c) + lib/client/client.c + lib/client/client-handshake.c + lib/client/client-parser.c) endif() -if (LWS_WITH_DISKCACHE) - list(APPEND SOURCES - lib/misc/diskcache.c) -endif() +if (LWS_WITH_MBEDTLS) + set(LWS_WITH_SSL ON) -if (LWS_WITH_STRUCT_JSON) - list(APPEND SOURCES - lib/misc/lws-struct-lejp.c) -endif() + list(APPEND HDR_PRIVATE + lib/mbedtls_wrapper/include/internal/ssl3.h + lib/mbedtls_wrapper/include/internal/ssl_cert.h + lib/mbedtls_wrapper/include/internal/ssl_code.h + lib/mbedtls_wrapper/include/internal/ssl_dbg.h + lib/mbedtls_wrapper/include/internal/ssl_lib.h + lib/mbedtls_wrapper/include/internal/ssl_methods.h + lib/mbedtls_wrapper/include/internal/ssl_pkey.h + lib/mbedtls_wrapper/include/internal/ssl_stack.h + lib/mbedtls_wrapper/include/internal/ssl_types.h + lib/mbedtls_wrapper/include/internal/ssl_x509.h + lib/mbedtls_wrapper/include/internal/tls1.h + lib/mbedtls_wrapper/include/internal/x509_vfy.h) -if (LWS_WITH_STRUCT_SQLITE3) - list(APPEND SOURCES - lib/misc/lws-struct-sqlite.c) -endif() + list(APPEND HDR_PRIVATE + lib/mbedtls_wrapper/include/openssl/ssl.h) -if (NOT LWS_WITHOUT_CLIENT) - list(APPEND SOURCES - lib/core-net/connect.c - lib/core-net/client.c - lib/roles/http/client/client-http.c - lib/roles/http/client/client-handshake.c) -endif() + list(APPEND HDR_PRIVATE + lib/mbedtls_wrapper/include/platform/ssl_pm.h + lib/mbedtls_wrapper/include/platform/ssl_port.h) -if (NOT LWS_WITHOUT_SERVER) - list(APPEND SOURCES - lib/core-net/server.c) -endif() + include_directories(lib/mbedtls_wrapper/include) + include_directories(lib/mbedtls_wrapper/include/platform) + include_directories(lib/mbedtls_wrapper/include/internal) + include_directories(lib/mbedtls_wrapper/include/openssl) -if (NOT LWS_WITHOUT_SERVER OR LWS_WITH_SECURE_STREAMS_PROCESS_API) list(APPEND SOURCES - lib/roles/listen/ops-listen.c) -endif() + lib/mbedtls_wrapper/library/ssl_cert.c + lib/mbedtls_wrapper/library/ssl_lib.c + lib/mbedtls_wrapper/library/ssl_methods.c + lib/mbedtls_wrapper/library/ssl_pkey.c + lib/mbedtls_wrapper/library/ssl_stack.c + lib/mbedtls_wrapper/library/ssl_x509.c) -if (LWS_WITH_MBEDTLS) - set(LWS_WITH_SSL ON) - - include_directories(lib/tls/mbedtls/wrapper/include) - include_directories(lib/tls/mbedtls/wrapper/include/platform) - include_directories(lib/tls/mbedtls/wrapper/include/internal) - include_directories(lib/tls/mbedtls/wrapper/include/openssl) - - if (LWS_WITH_NETWORK) - list(APPEND HDR_PRIVATE - lib/tls/mbedtls/wrapper/include/internal/ssl3.h - lib/tls/mbedtls/wrapper/include/internal/ssl_cert.h - lib/tls/mbedtls/wrapper/include/internal/ssl_code.h - lib/tls/mbedtls/wrapper/include/internal/ssl_dbg.h - lib/tls/mbedtls/wrapper/include/internal/ssl_lib.h - lib/tls/mbedtls/wrapper/include/internal/ssl_methods.h - lib/tls/mbedtls/wrapper/include/internal/ssl_pkey.h - lib/tls/mbedtls/wrapper/include/internal/ssl_stack.h - lib/tls/mbedtls/wrapper/include/internal/ssl_types.h - lib/tls/mbedtls/wrapper/include/internal/ssl_x509.h - lib/tls/mbedtls/wrapper/include/internal/tls1.h - lib/tls/mbedtls/wrapper/include/internal/x509_vfy.h) - - list(APPEND HDR_PRIVATE - lib/tls/mbedtls/wrapper/include/openssl/ssl.h) - - list(APPEND HDR_PRIVATE - lib/tls/mbedtls/wrapper/include/platform/ssl_pm.h - lib/tls/mbedtls/wrapper/include/platform/ssl_port.h) - - list(APPEND SOURCES - lib/tls/mbedtls/wrapper/library/ssl_cert.c - lib/tls/mbedtls/wrapper/library/ssl_lib.c - lib/tls/mbedtls/wrapper/library/ssl_methods.c - lib/tls/mbedtls/wrapper/library/ssl_pkey.c - lib/tls/mbedtls/wrapper/library/ssl_stack.c - lib/tls/mbedtls/wrapper/library/ssl_x509.c) - - list(APPEND SOURCES - lib/tls/mbedtls/wrapper/platform/ssl_pm.c - lib/tls/mbedtls/wrapper/platform/ssl_port.c) - endif() + list(APPEND SOURCES + lib/mbedtls_wrapper/platform/ssl_pm.c + lib/mbedtls_wrapper/platform/ssl_port.c) endif() if (LWS_WITH_SSL) list(APPEND SOURCES - lib/tls/tls.c - ) - if (LWS_WITH_NETWORK) - list(APPEND SOURCES - lib/tls/tls-network.c - ) - endif() - - if (LWS_WITH_MBEDTLS) - list(APPEND SOURCES - lib/tls/mbedtls/mbedtls-tls.c - lib/tls/mbedtls/mbedtls-x509.c - ) - if (LWS_WITH_NETWORK) - list(APPEND SOURCES - lib/tls/mbedtls/mbedtls-ssl.c - ) - endif() - if (LWS_WITH_GENCRYPTO) - list(APPEND SOURCES - lib/tls/mbedtls/lws-genhash.c - lib/tls/mbedtls/lws-genrsa.c - lib/tls/mbedtls/lws-genaes.c - lib/tls/lws-genec-common.c - lib/tls/mbedtls/lws-genec.c - lib/tls/mbedtls/lws-gencrypto.c - ) - endif() - else() - list(APPEND SOURCES - lib/tls/openssl/openssl-tls.c - lib/tls/openssl/openssl-x509.c - ) - if (LWS_WITH_NETWORK) - list(APPEND SOURCES - lib/tls/openssl/openssl-ssl.c - ) - endif() - if (LWS_WITH_GENCRYPTO) - list(APPEND SOURCES - lib/tls/openssl/lws-genhash.c - lib/tls/openssl/lws-genrsa.c - lib/tls/openssl/lws-genaes.c - lib/tls/lws-genec-common.c - lib/tls/openssl/lws-genec.c - lib/tls/openssl/lws-gencrypto.c - ) - endif() - endif() + lib/ssl.c + lib/misc/lws-genhash.c) if (NOT LWS_WITHOUT_SERVER) list(APPEND SOURCES - lib/tls/tls-server.c) - if (LWS_WITH_MBEDTLS) - list(APPEND SOURCES - lib/tls/mbedtls/mbedtls-server.c) - else() - list(APPEND SOURCES - lib/tls/openssl/openssl-server.c) - endif() + lib/server/ssl-server.c) endif() if (NOT LWS_WITHOUT_CLIENT) list(APPEND SOURCES - lib/tls/tls-client.c) - if (LWS_WITH_MBEDTLS) - list(APPEND SOURCES - lib/tls/mbedtls/mbedtls-client.c) - else() - list(APPEND SOURCES - lib/tls/openssl/openssl-client.c) - endif() - + lib/client/ssl-client.c) endif() endif() @@ -1499,199 +711,105 @@ lib/misc/sha-1.c) endif() -if (LWS_WITH_HTTP2 AND NOT LWS_WITHOUT_SERVER) +if (LWS_WITH_HTTP2) list(APPEND SOURCES - lib/roles/h2/http2.c - lib/roles/h2/hpack.c - lib/roles/h2/ops-h2.c) + lib/http2/http2.c + lib/http2/hpack.c + lib/http2/ssl-http2.c) endif() # select the active platform files if (WIN32) list(APPEND SOURCES - lib/plat/windows/windows-fds.c - lib/plat/windows/windows-file.c - lib/plat/windows/windows-init.c - lib/plat/windows/windows-misc.c - lib/plat/windows/windows-pipe.c - lib/plat/windows/windows-plugins.c - lib/plat/windows/windows-service.c - lib/plat/windows/windows-sockets.c - ) - if (LWS_WITH_SYS_ASYNC_DNS) - list(APPEND SOURCES lib/plat/windows/windows-resolv.c) - endif() + lib/plat/lws-plat-win.c) else() - if (LWS_PLAT_OPTEE) + if (LWS_WITH_ESP8266) list(APPEND SOURCES - lib/plat/optee/lws-plat-optee.c - ) - if (LWS_WITH_NETWORK) - list(APPEND SOURCES - lib/plat/optee/network.c - ) - endif() + lib/plat/lws-plat-esp8266.c) else() - if (LWS_PLAT_FREERTOS) + if (LWS_PLAT_OPTEE) list(APPEND SOURCES - lib/plat/freertos/freertos-fds.c - lib/plat/freertos/freertos-init.c - lib/plat/freertos/freertos-misc.c - lib/plat/freertos/freertos-pipe.c - lib/plat/freertos/freertos-service.c - lib/plat/freertos/freertos-sockets.c - lib/misc/romfs.c) - if (LWS_WITH_ESP32_HELPER) - list(APPEND SOURCES lib/plat/freertos/esp32/esp32-helpers.c) - endif() - if (LWS_WITH_FILE_OPS) - list(APPEND SOURCES lib/plat/freertos/freertos-file.c) - endif() - if (LWS_WITH_SYS_ASYNC_DNS) - list(APPEND SOURCES lib/plat/freertos/freertos-resolv.c) - endif() + lib/plat/lws-plat-optee.c) else() - set(LWS_PLAT_UNIX 1) - list(APPEND SOURCES - lib/plat/unix/unix-caps.c - lib/plat/unix/unix-misc.c - lib/plat/unix/unix-init.c - ) - if (LWS_WITH_FILE_OPS) - list(APPEND SOURCES lib/plat/unix/unix-file.c) - endif() - if (LWS_WITH_NETWORK) + if (LWS_WITH_ESP32) list(APPEND SOURCES - lib/plat/unix/unix-pipe.c - lib/plat/unix/unix-service.c - lib/plat/unix/unix-sockets.c - lib/plat/unix/unix-fds.c - ) - if (LWS_WITH_SYS_ASYNC_DNS) - if (LWS_PLAT_ANDROID) - list(APPEND SOURCES lib/plat/unix/android/android-resolv.c) - else() - list(APPEND SOURCES lib/plat/unix/unix-resolv.c) - endif() - endif() - endif() - - if (LWS_WITH_PLUGINS AND LWS_WITH_LIBUV) - list(APPEND SOURCES lib/plat/unix/unix-plugins.c) + lib/plat/lws-plat-esp32.c + lib/misc/romfs.c) + else() + list(APPEND SOURCES + lib/plat/lws-plat-unix.c) endif() endif() endif() endif() -if (LWS_WITH_SOCKS5 AND NOT LWS_WITHOUT_CLIENT) - list(APPEND SOURCES - lib/core-net/socks5-client.c) -endif() - -if ((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_SERVER) +if (NOT LWS_WITHOUT_SERVER) list(APPEND SOURCES - lib/roles/http/server/server.c - lib/roles/http/server/lws-spa.c) + lib/server/server.c + lib/server/lws-spa.c + lib/server/server-handshake.c) endif() -if (LWS_ROLE_WS AND NOT LWS_WITHOUT_EXTENSIONS) +if (NOT LWS_WITHOUT_EXTENSIONS) list(APPEND HDR_PRIVATE - lib/roles/ws/ext/extension-permessage-deflate.h) + lib/ext/extension-permessage-deflate.h) list(APPEND SOURCES - lib/roles/ws/ext/extension.c - lib/roles/ws/ext/extension-permessage-deflate.c) + lib/ext/extension.c + lib/ext/extension-permessage-deflate.c) endif() if (LWS_WITH_HTTP_PROXY) list(APPEND SOURCES - lib/roles/http/server/rewrite.c) -endif() - -if (LWS_WITH_POLL AND LWS_WITH_NETWORK) - list(APPEND SOURCES - lib/event-libs/poll/poll.c) -endif() - -if (LWS_WITH_LIBUV AND LWS_WITH_NETWORK) - list(APPEND SOURCES - lib/event-libs/libuv/libuv.c) + lib/server/rewrite.c) endif() -if (LWS_WITH_LIBEVENT AND LWS_WITH_NETWORK) +if (LWS_WITH_LIBEV) list(APPEND SOURCES - lib/event-libs/libevent/libevent.c) + lib/event-libs/libev.c) endif() -if (LWS_WITH_GLIB AND LWS_WITH_NETWORK) +if (LWS_WITH_LIBUV) list(APPEND SOURCES - lib/event-libs/glib/glib.c) + lib/event-libs/libuv.c) endif() - -if (LWS_WITH_LIBEV AND LWS_WITH_NETWORK) +if (LWS_WITH_LIBEVENT) list(APPEND SOURCES - lib/event-libs/libev/libev.c) - # libev generates a big mess of warnings with gcc, maintainer claims gcc to blame - set_source_files_properties( lib/event-libs/libev/libev.c PROPERTIES COMPILE_FLAGS "-Wno-error" ) + lib/event-libs/libevent.c) endif() if (LWS_WITH_LEJP) list(APPEND SOURCES lib/misc/lejp.c) + list(APPEND HDR_PUBLIC + lib/misc/lejp.h) endif() -if (LWS_WITH_LEJP_CONF AND LWS_WITH_NETWORK AND NOT LWS_PLAT_OPTEE) +if (LWS_WITH_LEJP_CONF) list(APPEND SOURCES - "lib/roles/http/server/lejp-conf.c" + "lib/server/lejp-conf.c" ) endif() -if (LWS_WITH_ABSTRACT) +if (LWS_WITH_SMTP) list(APPEND SOURCES - lib/abstract/transports/unit-test.c) + lib/misc/smtp.c) endif() -#if (LWS_WITH_SMTP) -# list(APPEND SOURCES -# lib/abstract/protocols/smtp/smtp.c -# lib/abstract/protocols/smtp/smtp-sequencer.c -# ) -#endif() - if (LWS_WITH_RANGES) list(APPEND SOURCES - lib/roles/http/server/ranges.c) + lib/server/ranges.c) endif() if (LWS_WITH_ZIP_FOPS) if (LWS_WITH_ZLIB) list(APPEND SOURCES - lib/roles/http/server/fops-zip.c) + lib/server/fops-zip.c) else() message(FATAL_ERROR "Pre-zipped file support (LWS_WITH_ZIP_FOPS) requires ZLIB (LWS_WITH_ZLIB)") endif() endif() -if (LWS_WITH_JOSE) - list(APPEND SOURCES - lib/jose/jwk/jwk.c - lib/jose/jws/jose.c - lib/jose/jws/jws.c - lib/jose/jwe/jwe.c - lib/jose/jwe/enc/aescbc.c - lib/jose/jwe/enc/aesgcm.c - lib/jose/jwe/enc/aeskw.c - lib/jose/jwe/jwe-rsa-aescbc.c - lib/jose/jwe/jwe-rsa-aesgcm.c - lib/jose/jwe/jwe-ecdh-es-aeskw.c - ) -endif() - -if (LWS_WITH_TLS AND (LWS_WITH_JOSE OR LWS_WITH_GENCRYPTO)) - list(APPEND SOURCES - lib/tls/lws-gencrypto-common.c) -endif() - # Add helper files for Windows. if (WIN32) set(WIN32_HELPERS_PATH win32port/win32helpers) @@ -1711,7 +829,7 @@ # Unix. if (NOT LWS_WITHOUT_DAEMONIZE) list(APPEND SOURCES - lib/misc/daemonize.c) + lib/server/daemonize.c) endif() endif() @@ -1722,71 +840,35 @@ endif() endif() -if ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) - set(COMPILER_IS_CLANG ON) -endif() - -if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG) +if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) include (CheckCCompilerFlag) CHECK_C_COMPILER_FLAG(-fvisibility=hidden LWS_HAVE_VISIBILITY) if (LWS_HAVE_VISIBILITY) set(VISIBILITY_FLAG -fvisibility=hidden) endif() - if (LWS_WITH_GCOV) - set (GCOV_FLAGS "-fprofile-arcs -ftest-coverage ") - endif() - - if (LWS_WITH_ASAN) - set (ASAN_FLAGS "-fsanitize=address -fsanitize=undefined -fsanitize-address-use-after-scope -fsanitize-undefined-trap-on-error") - if (NOT COMPILER_IS_CLANG) - set (ASAN_FLAGS "${ASAN_FLAGS} -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize=leak") - endif() - message("Enabling ASAN") - endif() - - check_c_compiler_flag("-Wignored-qualifiers" LWS_GCC_HAS_IGNORED_QUALIFIERS) - check_c_compiler_flag("-Wtype-limits" LWS_GCC_HAS_TYPE_LIMITS) - - if (LWS_GCC_HAS_IGNORED_QUALIFIERS) - set(CMAKE_C_FLAGS "-Wignored-qualifiers ${CMAKE_C_FLAGS}" ) - endif() - - if (LWS_GCC_HAS_TYPE_LIMITS) - set(CMAKE_C_FLAGS "-Wtype-limits ${CMAKE_C_FLAGS}" ) - endif() - - if (UNIX AND NOT LWS_PLAT_FREERTOS) - set(CMAKE_C_FLAGS "-Wall -Wsign-compare -Wstrict-aliasing -Wuninitialized -Werror ${VISIBILITY_FLAG} -Wundef ${GCOV_FLAGS} ${CMAKE_C_FLAGS} ${ASAN_FLAGS}" ) + if ((UNIX OR LWS_WITH_ESP8266) AND NOT LWS_WITH_ESP32) + set(CMAKE_C_FLAGS "-Wall -Werror ${VISIBILITY_FLAG} -Wundef ${CMAKE_C_FLAGS}" ) else() - set(CMAKE_C_FLAGS "-Wall -Wsign-compare -Wuninitialized -Werror ${VISIBILITY_FLAG} ${GCOV_FLAGS} ${CMAKE_C_FLAGS}" ) + set(CMAKE_C_FLAGS "-Wall ${VISIBILITY_FLAG} ${CMAKE_C_FLAGS}" ) endif() endif () -if (LWS_PLAT_OPTEE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --sysroot ../../../../lib/libutils/isoc/include -I../../../../lib/libutils/isoc/include -I../../../../lib/libutils/ext/include" ) -endif() - if ((CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) AND NOT LWS_WITHOUT_TESTAPPS) - if (UNIX AND LWS_HAVE_PTHREAD_H) + if (UNIX AND LWS_MAX_SMP GREATER 1) # jeez clang understands -pthread but dies if he sees it at link time! # http://stackoverflow.com/questions/2391194/what-is-gs-pthread-equiv-in-clang set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread" ) endif() endif() -if (COMPILER_IS_CLANG) - +if ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) # otherwise osx blows a bunch of openssl deprecated api errors set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations" ) - if (UNIX AND LWS_HAVE_PTHREAD_H) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -Wno-error=unused-command-line-argument" ) - endif() endif() source_group("Headers Private" FILES ${HDR_PRIVATE}) source_group("Headers Public" FILES ${HDR_PUBLIC}) source_group("Sources" FILES ${SOURCES}) -source_group("Resources" FILES ${RESOURCES}) # # Create the lib. @@ -1812,16 +894,9 @@ endif() add_custom_command( TARGET websockets - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/include/libwebsockets.h + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/lib/libwebsockets.h ${CMAKE_CURRENT_BINARY_DIR}/include/libwebsockets.h ) - - add_custom_command( - TARGET websockets - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/include/libwebsockets/ - ${CMAKE_CURRENT_BINARY_DIR}/include/libwebsockets - ) - add_custom_command( TARGET websockets COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/lws_config.h @@ -1834,8 +909,7 @@ add_library(websockets_shared SHARED ${HDR_PRIVATE} ${HDR_PUBLIC} - ${SOURCES} - ${RESOURCES}) + ${SOURCES}) list(APPEND LWS_LIBRARIES websockets_shared) # We want the shared lib to be named "libwebsockets" @@ -1859,16 +933,9 @@ add_custom_command( TARGET websockets_shared - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/include/libwebsockets.h + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/lib/libwebsockets.h ${CMAKE_CURRENT_BINARY_DIR}/include/libwebsockets.h ) - - add_custom_command( - TARGET websockets - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/include/libwebsockets - ${CMAKE_CURRENT_BINARY_DIR}/include/libwebsockets - ) - add_custom_command( TARGET websockets_shared COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/lws_config.h @@ -1880,7 +947,7 @@ # Set the so version of the lib. # Equivalent to LDFLAGS=-version-info x:x:x -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG) +if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) foreach(lib ${LWS_LIBRARIES}) set_target_properties(${lib} PROPERTIES @@ -1895,7 +962,7 @@ # # -# ZLIB (needed for deflate extension and if LWS_WITH_HTTP_STREAM_COMPRESSION) +# ZLIB (Only needed for deflate extensions). # if (LWS_WITH_ZLIB) if (LWS_WITH_BUNDLED_ZLIB) @@ -1906,6 +973,8 @@ ${WIN32_ZLIB_PATH}/compress.c ${WIN32_ZLIB_PATH}/crc32.c ${WIN32_ZLIB_PATH}/deflate.c + ${WIN32_ZLIB_PATH}/gzclose.c + ${WIN32_ZLIB_PATH}/gzio.c ${WIN32_ZLIB_PATH}/gzlib.c ${WIN32_ZLIB_PATH}/gzread.c ${WIN32_ZLIB_PATH}/gzwrite.c @@ -1928,23 +997,12 @@ message(FATAL_ERROR "Don't have bundled zlib for that platform") endif() elseif (NOT ZLIB_FOUND) - if (LWS_WITH_MINIZ) - find_package(Miniz REQUIRED) - set(ZLIB_INCLUDE_DIRS ${MINIZ_INCLUDE_DIRS}) - set(ZLIB_LIBRARIES ${MINIZ_LIBRARIES}) - else() - find_package(ZLIB REQUIRED) - endif() + find_package(ZLIB REQUIRED) endif() - message("zlib/miniz include dirs: ${ZLIB_INCLUDE_DIRS}") - message("zlib/miniz libraries: ${ZLIB_LIBRARIES}") + message("zlib include dirs: ${ZLIB_INCLUDE_DIRS}") + message("zlib libraries: ${ZLIB_LIBRARIES}") include_directories(${ZLIB_INCLUDE_DIRS}) - # done later at end of link list - # list(APPEND LIB_LIST ${ZLIB_LIBRARIES}) -endif() - -if (LWS_WITH_HTTP_BROTLI) - list(APPEND LIB_LIST brotlienc brotlidec brotlidec) + list(APPEND LIB_LIST ${ZLIB_LIBRARIES}) endif() # @@ -1965,17 +1023,13 @@ if (LWS_WITH_CYASSL) foreach(inc ${WOLFSSL_INCLUDE_DIRS}) - set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIRS} ${inc} ${inc}/cyassl) include_directories("${inc}" "${inc}/cyassl") endforeach() else() foreach(inc ${WOLFSSL_INCLUDE_DIRS}) - set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIRS} ${inc} ${inc}/wolfssl) include_directories("${inc}" "${inc}/wolfssl") endforeach() endif() - set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) - set(VARIA wolfSSL_) list(APPEND LIB_LIST "${WOLFSSL_LIBRARIES}") set(chose_ssl 1) @@ -1996,22 +1050,19 @@ if (NOT chose_ssl) if (NOT OPENSSL_FOUND AND NOT LWS_WITH_BORINGSSL) # TODO: Add support for STATIC also. - if (NOT LWS_PLAT_FREERTOS) - find_package(PkgConfig QUIET) - pkg_check_modules(PC_OPENSSL openssl QUIET) + if (NOT LWS_WITH_ESP32) find_package(OpenSSL REQUIRED) - list(APPEND OPENSSL_LIBRARIES ${PC_OPENSSL_LIBRARIES}) endif() set(OPENSSL_INCLUDE_DIRS "${OPENSSL_INCLUDE_DIR}") endif() message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIRS}") - if (NOT LWS_PLAT_FREERTOS) + if (NOT LWS_WITH_ESP32) message("OpenSSL libraries: ${OPENSSL_LIBRARIES}") endif() include_directories("${OPENSSL_INCLUDE_DIRS}") - if (NOT LWS_PLAT_FREERTOS) + if (NOT LWS_WITH_ESP32) list(APPEND LIB_LIST ${OPENSSL_LIBRARIES}) endif() @@ -2072,37 +1123,6 @@ list(APPEND LIB_LIST ${LIBEVENT_LIBRARIES}) endif(LWS_WITH_LIBEVENT) -if (LWS_WITH_GLIB) - include (FindPkgConfig) - if (NOT GLIB_FOUND) - find_path(GLIB_INCLUDE_DIRS NAMES glib-2.0/glib.h) - find_library(GLIB_LIBRARIES NAMES glib-2.0) - if(GLIB_INCLUDE_DIRS AND GLIB_LIBRARIES) - set(GLIB_FOUND 1) - endif() - if (GLIB_INCLUDE_DIRS) - set(GLIB_INCLUDE_DIRS "${GLIB_INCLUDE_DIRS}/glib-2.0") - endif() - endif() - PKG_SEARCH_MODULE(LWS_GLIB2 glib-2.0) - if (LWS_GLIB2_FOUND) - list(APPEND GLIB_INCLUDE_DIRS "${LWS_GLIB2_INCLUDE_DIRS}") - endif() - message("glib include dir: ${GLIB_INCLUDE_DIRS}") - message("glib libraries: ${GLIB_LIBRARIES}") - include_directories("${GLIB_INCLUDE_DIRS}") - list(APPEND LIB_LIST ${GLIB_LIBRARIES}) -endif(LWS_WITH_GLIB) - -if (LWS_WITH_FSMOUNT AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - message("libmount include dir: ${LIBMOUNT_INC_PATH}") - message("libmount libraries: ${LIBMOUNT_LIB_PATH}") - - include_directories("${LIBMOUNT_INC_PATH}") - list(APPEND LIB_LIST ${LIBMOUNT_LIB_PATH}) -endif() - - if (LWS_WITH_SQLITE3) if (NOT SQLITE3_FOUND) find_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h) @@ -2118,18 +1138,11 @@ endif() -if (LWS_WITH_HUBBUB) +if (LWS_WITH_HTTP_PROXY) find_library(LIBHUBBUB_LIBRARIES NAMES hubbub) list(APPEND LIB_LIST ${LIBHUBBUB_LIBRARIES} ) endif() -if (LWS_ROLE_DBUS) - message("dbus include dir 1: ${LWS_DBUS_INCLUDE1}") - message("dbus include dir 2: ${LWS_DBUS_INCLUDE2}") - include_directories("${LWS_DBUS_INCLUDE1}") - include_directories("${LWS_DBUS_INCLUDE2}") - list(APPEND LIB_LIST ${LWS_DBUS_LIB}) -endif() # # Platform specific libs. @@ -2140,22 +1153,10 @@ list(APPEND LIB_LIST ws2_32.lib userenv.lib psapi.lib iphlpapi.lib) endif() -if (${CMAKE_SYSTEM_NAME} MATCHES "QNX") - list(APPEND LIB_LIST socket) -endif() - -# -# add libs here that need to be at the end of the link order -# - if (UNIX) list(APPEND LIB_LIST m) endif() -if(ILLUMOS) - list(APPEND LIB_LIST socket) -endif() - if (HAIKU) list(APPEND LIB_LIST network) endif() @@ -2164,13 +1165,7 @@ list(APPEND LIB_LIST cap ) endif() -if (UNIX) - list(APPEND LIB_LIST dl) -endif() -if (LWS_WITH_ZLIB AND NOT LWS_WITH_BUNDLED_ZLIB) - list(APPEND LIB_LIST "${ZLIB_LIBRARIES}") -endif() # Setup the linking for all libs. foreach (lib ${LWS_LIBRARIES}) @@ -2189,69 +1184,21 @@ endif() endif() -CHECK_FUNCTION_EXISTS(${VARIA}SSL_CTX_set1_param LWS_HAVE_SSL_CTX_set1_param) -CHECK_FUNCTION_EXISTS(${VARIA}SSL_set_info_callback LWS_HAVE_SSL_SET_INFO_CALLBACK) -CHECK_FUNCTION_EXISTS(${VARIA}X509_VERIFY_PARAM_set1_host LWS_HAVE_X509_VERIFY_PARAM_set1_host) -CHECK_FUNCTION_EXISTS(${VARIA}RSA_set0_key LWS_HAVE_RSA_SET0_KEY) -CHECK_FUNCTION_EXISTS(${VARIA}X509_get_key_usage LWS_HAVE_X509_get_key_usage) -CHECK_FUNCTION_EXISTS(${VARIA}EVP_PKEY_new_raw_private_key LWS_HAVE_SSL_CTX_EVP_PKEY_new_raw_private_key) -CHECK_FUNCTION_EXISTS(${VARIA}SSL_CTX_get0_certificate LWS_HAVE_SSL_CTX_get0_certificate) -CHECK_FUNCTION_EXISTS(${VARIA}SSL_get0_alpn_selected LWS_HAVE_SSL_get0_alpn_selected) -CHECK_FUNCTION_EXISTS(${VARIA}SSL_set_alpn_protos LWS_HAVE_SSL_set_alpn_protos) -CHECK_FUNCTION_EXISTS(${VARIA}EVP_aes_128_cfb8 LWS_HAVE_EVP_aes_128_cfb8) -CHECK_FUNCTION_EXISTS(${VARIA}EVP_aes_128_cfb128 LWS_HAVE_EVP_aes_128_cfb128) -CHECK_FUNCTION_EXISTS(${VARIA}EVP_aes_192_cfb8 LWS_HAVE_EVP_aes_192_cfb8) -CHECK_FUNCTION_EXISTS(${VARIA}EVP_aes_192_cfb128 LWS_HAVE_EVP_aes_192_cfb128) -CHECK_FUNCTION_EXISTS(${VARIA}EVP_aes_256_cfb8 LWS_HAVE_EVP_aes_256_cfb8) -CHECK_FUNCTION_EXISTS(${VARIA}EVP_aes_256_cfb128 LWS_HAVE_EVP_aes_256_cfb128) -CHECK_FUNCTION_EXISTS(${VARIA}EVP_aes_128_xts LWS_HAVE_EVP_aes_128_xts) -CHECK_FUNCTION_EXISTS(${VARIA}RSA_verify_pss_mgf1 LWS_HAVE_RSA_verify_pss_mgf1) -CHECK_FUNCTION_EXISTS(${VARIA}HMAC_CTX_new LWS_HAVE_HMAC_CTX_new) -CHECK_FUNCTION_EXISTS(${VARIA}SSL_CTX_set_ciphersuites LWS_HAVE_SSL_CTX_set_ciphersuites) -if (LWS_WITH_SSL AND NOT LWS_WITH_MBEDTLS) - if (UNIX) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} dl) - endif() -CHECK_C_SOURCE_COMPILES("#include \nint main(void) { STACK_OF(X509) *c = NULL; SSL_CTX *ctx = NULL; return (int)SSL_CTX_get_extra_chain_certs_only(ctx, &c); }\n" LWS_HAVE_SSL_EXTRA_CHAIN_CERTS) -CHECK_C_SOURCE_COMPILES("#include \nint main(void) { EVP_MD_CTX *md_ctx = NULL; EVP_MD_CTX_free(md_ctx); return 0; }\n" LWS_HAVE_EVP_MD_CTX_free) -CHECK_FUNCTION_EXISTS(${VARIA}ECDSA_SIG_set0 LWS_HAVE_ECDSA_SIG_set0) -CHECK_FUNCTION_EXISTS(${VARIA}BN_bn2binpad LWS_HAVE_BN_bn2binpad) -CHECK_FUNCTION_EXISTS(${VARIA}EVP_aes_128_wrap LWS_HAVE_EVP_aes_128_wrap) -CHECK_FUNCTION_EXISTS(${VARIA}EC_POINT_get_affine_coordinates LWS_HAVE_EC_POINT_get_affine_coordinates) -endif() +CHECK_FUNCTION_EXISTS(SSL_CTX_set1_param LWS_HAVE_SSL_CTX_set1_param) +CHECK_FUNCTION_EXISTS(SSL_set_info_callback LWS_HAVE_SSL_SET_INFO_CALLBACK) +CHECK_FUNCTION_EXISTS(X509_VERIFY_PARAM_set1_host LWS_HAVE_X509_VERIFY_PARAM_set1_host) +CHECK_FUNCTION_EXISTS(RSA_set0_key LWS_HAVE_RSA_SET0_KEY) + if (LWS_WITH_MBEDTLS) set(LWS_HAVE_TLS_CLIENT_METHOD 1) - if (NOT LWS_PLAT_FREERTOS) + if (NOT LWS_WITH_ESP32) # not supported in esp-idf openssl wrapper yet, but is in our version set(LWS_HAVE_X509_VERIFY_PARAM_set1_host 1) endif() - - CHECK_FUNCTION_EXISTS(mbedtls_ssl_conf_alpn_protocols LWS_HAVE_mbedtls_ssl_conf_alpn_protocols) - CHECK_FUNCTION_EXISTS(mbedtls_ssl_get_alpn_protocol LWS_HAVE_mbedtls_ssl_get_alpn_protocol) - CHECK_FUNCTION_EXISTS(mbedtls_ssl_conf_sni LWS_HAVE_mbedtls_ssl_conf_sni) - CHECK_FUNCTION_EXISTS(mbedtls_ssl_set_hs_ca_chain LWS_HAVE_mbedtls_ssl_set_hs_ca_chain) - CHECK_FUNCTION_EXISTS(mbedtls_ssl_set_hs_own_cert LWS_HAVE_mbedtls_ssl_set_hs_own_cert) - CHECK_FUNCTION_EXISTS(mbedtls_ssl_set_hs_authmode LWS_HAVE_mbedtls_ssl_set_hs_authmode) - CHECK_FUNCTION_EXISTS(mbedtls_net_init LWS_HAVE_mbedtls_net_init) - else() -CHECK_FUNCTION_EXISTS(${VARIA}TLS_client_method LWS_HAVE_TLS_CLIENT_METHOD) -CHECK_FUNCTION_EXISTS(${VARIA}TLSv1_2_client_method LWS_HAVE_TLSV1_2_CLIENT_METHOD) +CHECK_FUNCTION_EXISTS(TLS_client_method LWS_HAVE_TLS_CLIENT_METHOD) +CHECK_FUNCTION_EXISTS(TLSv1_2_client_method LWS_HAVE_TLSV1_2_CLIENT_METHOD) endif() - -# ideally we want to use pipe2() - -CHECK_C_SOURCE_COMPILES("#define _GNU_SOURCE\n#include \nint main(void) {int fd[2];\n return pipe2(fd, 0);\n}\n" LWS_HAVE_PIPE2) - -# old mbedtls has everything in mbedtls/net.h - -CHECK_C_SOURCE_COMPILES("#include \nint main(void) { return 0;}\n" LWS_HAVE_MBEDTLS_NET_SOCKETS) - -# tcp keepalive needs this on linux to work practically... but it only exists -# after kernel 2.6.37 - -CHECK_C_SOURCE_COMPILES("#include \nint main(void) { return TCP_USER_TIMEOUT; }\n" LWS_HAVE_TCP_USER_TIMEOUT) - set(CMAKE_REQUIRED_LIBRARIES ${temp}) # Generate the lws_config.h that includes all the public compilation settings. configure_file( @@ -2280,10 +1227,10 @@ set(GENCERTS 0) -if (LWS_WITH_SSL AND OPENSSL_EXECUTABLE AND NOT LWS_WITHOUT_TEST_SERVER AND NOT LWS_WITHOUT_SERVER AND NOT LWS_WITHOUT_TESTAPPS) +if (LWS_WITH_SSL AND OPENSSL_EXECUTABLE AND NOT LWS_WITHOUT_TEST_SERVER) set(GENCERTS 1) endif() -if (LWS_PLAT_FREERTOS) +if (LWS_WITH_ESP32) set(GENCERTS 1) endif() message(" GENCERTS = ${GENCERTS}") @@ -2357,7 +1304,7 @@ # Test applications # set(TEST_APP_LIST) -if ((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_TESTAPPS) +if (NOT LWS_WITHOUT_TESTAPPS) # # Helper function for adding a test app. # @@ -2414,13 +1361,6 @@ endif() target_link_libraries(${TEST_NAME} websockets) add_dependencies(${TEST_NAME} websockets) - if (UNIX AND LWS_WITH_SSL AND NOT LWS_WITH_MBEDTLS) - target_link_libraries(${TEST_NAME} dl) - endif() - endif() - - if (LWS_WITH_HTTP_STREAM_COMPRESSION) - target_link_libraries(${TEST_NAME} z) endif() # Set test app specific defines. @@ -2440,24 +1380,27 @@ if (UNIX AND LWS_WITH_PLUGINS) set(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}") - if(NOT((${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") OR (${CMAKE_SYSTEM_NAME} MATCHES "QNX"))) + if(NOT(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")) target_link_libraries(websockets dl) endif() endif() + + + if (NOT LWS_WITHOUT_SERVER) # # test-server # if (NOT LWS_WITHOUT_TEST_SERVER) create_test_app(test-server "test-apps/test-server.c" - "" - "" + "test-apps/test-server-http.c" + "test-apps/test-server-dumb-increment.c" "" "" "") - if (LWS_WITH_CGI AND LWS_WITH_TLS) + if (LWS_WITH_CGI) create_test_app(test-sshd "test-apps/test-sshd.c" "" "" @@ -2468,16 +1411,64 @@ endif() + if (UNIX) + create_test_app(test-fuzxy "test-apps/fuzxy.c" + "" + "" + "" + "" + "") + endif() + if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) AND LWS_MAX_SMP GREATER 1) + create_test_app(test-server-pthreads + "test-apps/test-server-pthreads.c" + "test-apps/test-server-http.c" + "test-apps/test-server-dumb-increment.c" + "" + "" + "") + endif() + if (NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + AND LWS_WITH_LIBEV) + create_test_app(test-server-libev + "test-apps/test-server-libev.c" + "test-apps/test-server-http.c" + "test-apps/test-server-dumb-increment.c" + "" + "" + "") + # libev generates a big mess of warnings with gcc, maintainers blame gcc + set_source_files_properties( test-apps/test-server-libev.c PROPERTIES COMPILE_FLAGS "-Wno-error" ) + endif() + if (NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + AND LWS_WITH_LIBUV) + create_test_app(test-server-libuv + "test-apps/test-server-libuv.c" + "test-apps/test-server-http.c" + "" + "" + "" + "") + endif() + if (NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + AND LWS_WITH_LIBEVENT) + create_test_app(test-server-libevent + "test-apps/test-server-libevent.c" + "test-apps/test-server-http.c" + "test-apps/test-server-dumb-increment.c" + "" + "" + "") + endif() endif() # # test-server-extpoll # - if (NOT LWS_WITHOUT_TEST_SERVER_EXTPOLL AND NOT WIN32) - create_test_app(test-server-extpoll - "test-apps/test-server.c" - "" - "" + if (NOT LWS_WITHOUT_TEST_SERVER_EXTPOLL) + create_test_app(test-server-extpoll "test-apps/test-server.c" + "test-apps/test-server-http.c" + "test-apps/test-server-dumb-increment.c" "" "" "") @@ -2495,10 +1486,13 @@ endif(WIN32) endif() - if (LWS_WITH_LEJP) + # + # test-server-v2.0 + # + if (LWS_WITH_PLUGINS) create_test_app( - test-lejp - "test-apps/test-lejp.c" + test-server-v2.0 + "test-apps/test-server-v2.0.c" "" "" "" @@ -2507,17 +1501,14 @@ endif() # Data files for running the test server. - list(APPEND TEST_SERVER_DATA + set(TEST_SERVER_DATA "${PROJECT_SOURCE_DIR}/test-apps/favicon.ico" "${PROJECT_SOURCE_DIR}/test-apps/leaf.jpg" "${PROJECT_SOURCE_DIR}/test-apps/candide.zip" - "${PROJECT_SOURCE_DIR}/test-apps/libwebsockets.org-logo.svg" + "${PROJECT_SOURCE_DIR}/test-apps/libwebsockets.org-logo.png" "${PROJECT_SOURCE_DIR}/test-apps/http2.png" - "${PROJECT_SOURCE_DIR}/test-apps/wss-over-h2.png" "${PROJECT_SOURCE_DIR}/test-apps/lws-common.js" - "${PROJECT_SOURCE_DIR}/test-apps/test.html" - "${PROJECT_SOURCE_DIR}/test-apps/test.css" - "${PROJECT_SOURCE_DIR}/test-apps/test.js") + "${PROJECT_SOURCE_DIR}/test-apps/test.html") add_custom_command(TARGET test-server POST_BUILD @@ -2542,6 +1533,26 @@ create_test_app(test-client "test-apps/test-client.c" "" "" "" "" "") endif() + # + # test-fraggle + # + if (NOT LWS_WITHOUT_TEST_FRAGGLE) + create_test_app(test-fraggle "test-apps/test-fraggle.c" "" "" "" "" "") + endif() + + # + # test-ping + # + if (NOT LWS_WITHOUT_TEST_PING) + create_test_app(test-ping "test-apps/test-ping.c" "" "" "" "" "") + endif() + # + # test-echo + # + if (NOT LWS_WITHOUT_TEST_ECHO) + create_test_app(test-echo "test-apps/test-echo.c" "" "" "" "" "") + endif() + endif(NOT LWS_WITHOUT_CLIENT) @@ -2586,9 +1597,6 @@ INSTALL_DATADIR="${CMAKE_INSTALL_PREFIX}/plugins" ) - SET_TARGET_PROPERTIES(${PLUGIN_NAME} - PROPERTIES COMPILE_FLAGS ${CMAKE_C_FLAGS}) - # set_target_properties(${PLUGIN_NAME} # PROPERTIES # OUTPUT_NAME ${PLUGIN_NAME}) @@ -2597,51 +1605,19 @@ endmacro() -if (LWS_ROLE_WS) + + create_plugin(protocol_lws_meta "" + "plugins/protocol_lws_meta.c" "" "") create_plugin(protocol_dumb_increment "" "plugins/protocol_dumb_increment.c" "" "") create_plugin(protocol_lws_mirror "" "plugins/protocol_lws_mirror.c" "" "") create_plugin(protocol_lws_status "" "plugins/protocol_lws_status.c" "" "") - create_plugin(protocol_lws_table_dirlisting "" - "plugins/generic-table/protocol_table_dirlisting.c" "" "") - if (NOT WIN32) - create_plugin(protocol_lws_raw_test "" - "plugins/protocol_lws_raw_test.c" "" "") - - if (UNIX AND LWS_HAVE_PTHREAD_H) - create_plugin(protocol_deaddrop "" - "plugins/deaddrop/protocol_lws_deaddrop.c" "" "") - endif() - endif() - - if (LWS_WITH_SERVER_STATUS) - create_plugin(protocol_lws_server_status "" - "plugins/protocol_lws_server_status.c" "" "") - endif() - - if (NOT LWS_WITHOUT_CLIENT) - create_plugin(protocol_client_loopback_test "" - "plugins/protocol_client_loopback_test.c" "" "") - endif() - -endif(LWS_ROLE_WS) - create_plugin(protocol_post_demo "" "plugins/protocol_post_demo.c" "" "") - -if (LWS_ROLE_RAW_PROXY) - create_plugin(protocol_lws_raw_proxy "" - "plugins/raw-proxy/protocol_lws_raw_proxy.c" "" "") -endif() - -if (LWS_WITH_FTS) - create_plugin(protocol_fulltext_demo "" - "plugins/protocol_fulltext_demo.c" "" "") -endif() - - + create_plugin(protocol_lws_table_dirlisting "" + "plugins/generic-table/protocol_table_dirlisting.c" "" "") if (LWS_WITH_SSL) create_plugin(protocol_lws_ssh_base "plugins/ssh-base/include" "plugins/ssh-base/sshd.c;plugins/ssh-base/telnet.c;plugins/ssh-base/kex-25519.c" "plugins/ssh-base/crypto/chacha.c;plugins/ssh-base/crypto/ed25519.c;plugins/ssh-base/crypto/fe25519.c;plugins/ssh-base/crypto/ge25519.c;plugins/ssh-base/crypto/poly1305.c;plugins/ssh-base/crypto/sc25519.c;plugins/ssh-base/crypto/smult_curve25519_ref.c" "") @@ -2650,14 +1626,22 @@ include_directories("${PROJECT_SOURCE_DIR}/plugins/ssh-base/include") endif() + if (NOT WIN32) + create_plugin(protocol_lws_raw_test "" + "plugins/protocol_lws_raw_test.c" "" "") + endif() - -if (LWS_WITH_ACME) - create_plugin(protocol_lws_acme_client "" - "plugins/acme-client/protocol_lws_acme_client.c" "" "") +if (LWS_WITH_SERVER_STATUS) + create_plugin(protocol_lws_server_status "" + "plugins/protocol_lws_server_status.c" "" "") endif() -if (LWS_WITH_GENERIC_SESSIONS AND LWS_ROLE_WS AND LWS_WITH_TLS) +if (NOT LWS_WITHOUT_CLIENT) + create_plugin(protocol_client_loopback_test "" + "plugins/protocol_client_loopback_test.c" "" "") +endif(NOT LWS_WITHOUT_CLIENT) + +if (LWS_WITH_GENERIC_SESSIONS) create_plugin(protocol_generic_sessions "" "plugins/generic-sessions/protocol_generic_sessions.c" "plugins/generic-sessions/utils.c" @@ -2677,7 +1661,7 @@ target_link_libraries(protocol_lws_messageboard sqlite3 ) endif(WIN32) -endif(LWS_WITH_GENERIC_SESSIONS AND LWS_ROLE_WS AND LWS_WITH_TLS) +endif(LWS_WITH_GENERIC_SESSIONS) endif(LWS_WITH_PLUGINS AND LWS_WITH_SHARED) @@ -2699,20 +1683,10 @@ add_custom_command(TARGET ${TARGET_BIN} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy "${SSLEAY_BIN}" "$" VERBATIM) - - # - # Win32: if we are using libuv, also need to copy it in the output dir - # - if (WIN32 AND LWS_WITH_LIBUV) - STRING(REPLACE ".lib" ".dll" LIBUV_BIN ${LIBUV_LIBRARIES}) - add_custom_command(TARGET ${TARGET_BIN} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy "${LIBUV_BIN}" "$" VERBATIM) - endif() endforeach() endif() endif() -endif((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_TESTAPPS) +endif(NOT LWS_WITHOUT_TESTAPPS) if (LWS_WITH_LWSWS) list(APPEND LWSWS_SRCS @@ -2746,59 +1720,6 @@ ) endif (LWS_WITH_LWSWS) -# secure streams plugins - -if (LWS_WITH_SECURE_STREAMS) - # - # Helper function for adding a secure stream plugin - # - macro(create_ss_plugin NAME S2 S3 S4 S5 S6) - - set(SSP_SRCS) - set(SSP_PUBLIC_HDR) - set(SSP_HDR) - - if ("${S2}" STREQUAL "") - else() - list(APPEND SSP_SRCS - lib/secure-streams/plugins/${NAME}/${S2}) - endif() - if ("${S3}" STREQUAL "") - else() - list(APPEND SSP_SRCS - lib/secure-streams/plugins/${NAME}/${S3}) - endif() - if ("${S4}" STREQUAL "") - else() - list(APPEND SSP_SRCS - lib/secure-streams/plugins/${NAME}/${S4}) - endif() - if ("${S5}" STREQUAL "") - else() - list(APPEND SSP_SRCS - lib/secure-streams/plugins/${NAME}/${S5}) - endif() - if ("${S6}" STREQUAL "") - else() - list(APPEND SSP_SRCS - lib/secure-streams/plugins/${NAME}/${S6}) - endif() - - source_group("Headers Private" FILES ${SSP_HDR}) - source_group("Sources" FILES ${SSP_SRCS}) - - add_library( ${NAME} STATIC - ${SSP_HDR} ${SSP_PUBLIC_HDR} ${SSP_SRCS} ) - - add_dependencies(${NAME} websockets_shared) - list(APPEND SS_PLUGINS_LIST ${NAME}) - endmacro() - - include_directories(lib/secure-streams) - - create_ss_plugin(ssp-h1url "h1url.c" "" "" "" "") -endif() - if (UNIX) # Generate and install pkgconfig. @@ -2854,11 +1775,8 @@ set(LWS_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") # Export targets (This is used for other CMake projects to easily find the libraries and include files). -if (LWS_WITH_EXPORT_LWSTARGETS) - export(TARGETS ${LWS_LIBRARIES} - FILE "${PROJECT_BINARY_DIR}/LibwebsocketsTargets.cmake") -endif() - +export(TARGETS ${LWS_LIBRARIES} + FILE "${PROJECT_BINARY_DIR}/LibwebsocketsTargets.cmake") export(PACKAGE libwebsockets) # Generate the config file for the build-tree. @@ -2900,9 +1818,6 @@ # Installation. # -install(DIRECTORY include/libwebsockets - DESTINATION "${LWS_INSTALL_INCLUDE_DIR}" COMPONENT dev_headers) - # Install libs and headers. install(TARGETS ${LWS_LIBRARIES} EXPORT LibwebsocketsTargets @@ -2910,7 +1825,6 @@ ARCHIVE DESTINATION "${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}" COMPONENT libraries RUNTIME DESTINATION "${LWS_INSTALL_BIN_DIR}" COMPONENT libraries # Windows DLLs PUBLIC_HEADER DESTINATION "${LWS_INSTALL_INCLUDE_DIR}" COMPONENT dev) - set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries") set(CPACK_COMPONENT_DEV_DISPLAY_NAME "Development files") @@ -2946,12 +1860,9 @@ endif() endif() - -if (NOT LWS_WITHOUT_TEST_SERVER AND NOT LWS_WITHOUT_SERVER AND NOT LWS_WITHOUT_TESTAPPS) - install(FILES test-apps/lws-ssh-test-keys;test-apps/lws-ssh-test-keys.pub - DESTINATION share/libwebsockets-test-server - COMPONENT examples) -endif() +install(FILES test-apps/lws-ssh-test-keys;test-apps/lws-ssh-test-keys.pub + DESTINATION share/libwebsockets-test-server + COMPONENT examples) # plugins @@ -2960,16 +1871,8 @@ PERMISSIONS OWNER_WRITE OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE OWNER_READ GROUP_READ WORLD_READ DESTINATION share/libwebsockets-test-server/plugins COMPONENT plugins) - - if (NOT WIN32) - install(FILES plugins/deaddrop/assets/index.html;plugins/deaddrop/assets/deaddrop.js;plugins/deaddrop/assets/deaddrop.css;plugins/deaddrop/assets/drop.svg - DESTINATION share/libwebsockets-test-server/deaddrop - COMPONENT plugins) - endif() - - if (LWS_WITH_SERVER_STATUS) - install(FILES plugins/server-status.html;plugins/server-status.js;plugins/server-status.css;plugins/lwsws-logo.png + install(FILES plugins/server-status.html;plugins/lwsws-logo.png DESTINATION share/libwebsockets-test-server/server-status COMPONENT examples) endif() @@ -2979,7 +1882,6 @@ plugins/generic-sessions/assets/seats.jpg plugins/generic-sessions/assets/failed-login.html plugins/generic-sessions/assets/lwsgs.js - plugins/generic-sessions/assets/lwsgs.css plugins/generic-sessions/assets/post-register-fail.html plugins/generic-sessions/assets/post-register-ok.html plugins/generic-sessions/assets/post-verify-ok.html @@ -3014,13 +1916,11 @@ DESTINATION "${LWS_INSTALL_CMAKE_DIR}" COMPONENT dev) # Install exports for the install-tree. -if (LWS_WITH_EXPORT_LWSTARGETS) - install(EXPORT LibwebsocketsTargets - DESTINATION "${LWS_INSTALL_CMAKE_DIR}" COMPONENT dev) -endif() +install(EXPORT LibwebsocketsTargets + DESTINATION "${LWS_INSTALL_CMAKE_DIR}" COMPONENT dev) # build subdir is not part of sources -set(CPACK_SOURCE_IGNORE_FILES $(CPACK_SOURCE_IGNORE_FILES) "/.git/" "/build/" "\\\\.tgz$" "\\\\.tar\\\\.gz$") +set(CPACK_SOURCE_IGNORE_FILES $(CPACK_SOURCE_IGNORE_FILES) ".git" "build" "tgz" "tar.gz") # Most people are more used to "make dist" compared to "make package_source" add_custom_target(dist COMMAND "${CMAKE_MAKE_PROGRAM}" package_source) @@ -3052,20 +1952,20 @@ message(" LWS_WITHOUT_TEST_SERVER = ${LWS_WITHOUT_TEST_SERVER}") message(" LWS_WITHOUT_TEST_SERVER_EXTPOLL = ${LWS_WITHOUT_TEST_SERVER_EXTPOLL}") message(" LWS_WITHOUT_TEST_PING = ${LWS_WITHOUT_TEST_PING}") +message(" LWS_WITHOUT_TEST_ECHO = ${LWS_WITHOUT_TEST_ECHO}") message(" LWS_WITHOUT_TEST_CLIENT = ${LWS_WITHOUT_TEST_CLIENT}") +message(" LWS_WITHOUT_TEST_FRAGGLE = ${LWS_WITHOUT_TEST_FRAGGLE}") message(" LWS_WITHOUT_EXTENSIONS = ${LWS_WITHOUT_EXTENSIONS}") +message(" LWS_WITH_LATENCY = ${LWS_WITH_LATENCY}") message(" LWS_WITHOUT_DAEMONIZE = ${LWS_WITHOUT_DAEMONIZE}") message(" LWS_WITH_LIBEV = ${LWS_WITH_LIBEV}") message(" LWS_WITH_LIBUV = ${LWS_WITH_LIBUV}") message(" LWS_WITH_LIBEVENT = ${LWS_WITH_LIBEVENT}") -message(" LWS_WITH_GLIB = ${LWS_WITH_GLIB}") message(" LWS_IPV6 = ${LWS_IPV6}") message(" LWS_UNIX_SOCK = ${LWS_UNIX_SOCK}") message(" LWS_WITH_HTTP2 = ${LWS_WITH_HTTP2}") -message(" LWS_ROLE_MQTT = ${LWS_ROLE_MQTT}") message(" LWS_SSL_SERVER_WITH_ECDH_CERT = ${LWS_SSL_SERVER_WITH_ECDH_CERT}") message(" LWS_MAX_SMP = ${LWS_MAX_SMP}") -message(" LWS_HAVE_PTHREAD_H = ${LWS_HAVE_PTHREAD_H}") message(" LWS_WITH_CGI = ${LWS_WITH_CGI}") message(" LWS_HAVE_OPENSSL_ECDH_H = ${LWS_HAVE_OPENSSL_ECDH_H}") message(" LWS_HAVE_SSL_CTX_set1_param = ${LWS_HAVE_SSL_CTX_set1_param}") @@ -3077,13 +1977,12 @@ message(" LWS_WITH_SERVER_STATUS = ${LWS_WITH_SERVER_STATUS}") message(" LWS_WITH_LEJP = ${LWS_WITH_LEJP}") message(" LWS_WITH_LEJP_CONF = ${LWS_WITH_LEJP_CONF}") -# this is broken atm -#message(" LWS_WITH_SMTP = ${LWS_WITH_SMTP}") +message(" LWS_WITH_SMTP = ${LWS_WITH_SMTP}") message(" LWS_WITH_GENERIC_SESSIONS = ${LWS_WITH_GENERIC_SESSIONS}") message(" LWS_STATIC_PIC = ${LWS_STATIC_PIC}") message(" LWS_WITH_RANGES = ${LWS_WITH_RANGES}") message(" LWS_PLAT_OPTEE = ${LWS_PLAT_OPTEE}") -message(" LWS_PLAT_FREERTOS = ${LWS_PLAT_FREERTOS}") +message(" LWS_WITH_ESP32 = ${LWS_WITH_ESP32}") message(" LWS_WITH_ZIP_FOPS = ${LWS_WITH_ZIP_FOPS}") message(" LWS_AVOID_SIGPIPE_IGN = ${LWS_AVOID_SIGPIPE_IGN}") message(" LWS_WITH_STATS = ${LWS_WITH_STATS}") @@ -3095,8 +1994,6 @@ message(" LWS_HAVE__ATOI64 = ${LWS_HAVE__ATOI64}") message(" LWS_HAVE_STAT32I64 = ${LWS_HAVE_STAT32I64}") message(" LWS_HAS_INTPTR_T = ${LWS_HAS_INTPTR_T}") -message(" LWS_WITH_EXPORT_LWSTARGETS = ${LWS_WITH_EXPORT_LWSTARGETS}") -message(" LWS_WITH_ABSTRACT = ${LWS_WITH_ABSTRACT}") message("---------------------------------------------------------------------") @@ -3109,32 +2006,5 @@ set(LIBWEBSOCKETS_LIBRARIES_SHARED websockets_shared CACHE STRING "Libwebsocket shared library") endif() -if (LWS_WITH_MINIMAL_EXAMPLES) - MACRO(SUBDIRLIST result curdir) - FILE(GLOB children RELATIVE ${curdir} ${curdir}/*) - SET(dirlist "") - - FOREACH(child ${children}) - IF(IS_DIRECTORY ${curdir}/${child}) - LIST(APPEND dirlist ${child}) - ENDIF() - ENDFOREACH() - - SET(${result} ${dirlist}) - ENDMACRO() - - SUBDIRLIST(SUBDIRS "${PROJECT_SOURCE_DIR}/minimal-examples") - FOREACH(subdir ${SUBDIRS}) - - SUBDIRLIST(SUBDIRS2 "${PROJECT_SOURCE_DIR}/minimal-examples/${subdir}") - FOREACH(subdir2 ${SUBDIRS2}) - if (EXISTS "${PROJECT_SOURCE_DIR}/minimal-examples/${subdir}/${subdir2}/CMakeLists.txt") - message("Processing ${PROJECT_SOURCE_DIR}/minimal-examples/${subdir}/${subdir2}") - add_subdirectory("${PROJECT_SOURCE_DIR}/minimal-examples/${subdir}/${subdir2}") - endif() - ENDFOREACH() - ENDFOREACH() -ENDIF() - # This must always be last! include(CPack) diff -Nru libwebsockets-4.0.20/component.mk libwebsockets-2.4.2/component.mk --- libwebsockets-4.0.20/component.mk 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/component.mk 2018-03-08 10:28:37.000000000 +0000 @@ -1,41 +1,36 @@ -COMPONENT_DEPENDS := mbedtls openssl -#COMPONENT_ADD_INCLUDEDIRS := ../../../../../../../../../../../../../../../../../../../../$(COMPONENT_BUILD_DIR)/include +COMPONENT_DEPENDS:=mbedtls openssl +COMPONENT_ADD_INCLUDEDIRS := ../../../../../../../../../../../../../../../../../../../../$(COMPONENT_BUILD_DIR)/include -COMPONENT_OWNBUILDTARGET := 1 +COMPONENT_OWNBUILDTARGET:= 1 -CROSS_PATH1 := $(shell which xtensa-esp32-elf-gcc ) -CROSS_PATH := $(shell dirname $(CROSS_PATH1) )/.. +CROSS_PATH1:=$(shell which xtensa-esp32-elf-gcc ) +CROSS_PATH:= $(shell dirname $(CROSS_PATH1) )/.. -# detect MSYS2 environment and set generator flag if found -# also set executable extension to .exe so that tools can be properly found -# and disable bundled zlib -MSYS_VERSION = $(if $(findstring Msys, $(shell uname -o)),$(word 1, $(subst ., ,$(shell uname -r))),0) -ifneq ($(MSYS_VERSION),0) - MSYS_FLAGS = -DLWS_WITH_BUNDLED_ZLIB=0 -DEXECUTABLE_EXT=.exe -G'MSYS Makefiles' -endif +#-DLWS_WITH_BORINGSSL=1 \ +# -DOPENSSL_ROOT_DIR="${PWD}/../../boringssl" \ +# -DOPENSSL_LIBRARIES="${PWD}/../../boringssl/build/ssl/libssl.a;${PWD}/../../boringssl/build/crypto/libcrypto.a" \ +# -DOPENSSL_INCLUDE_DIRS="${PWD}/../../boringssl/include" \ + +# -DNDEBUG=1 after cflags +# -DOPENSSL_LIBRARIES=x \ +# -DCOMPONENT_PATH=$(COMPONENT_PATH) \ -# -DNDEBUG=1 after cflags stops debug etc being built .PHONY: build build: cd $(COMPONENT_BUILD_DIR) ; \ echo "doing lws cmake" ; \ - cmake $(COMPONENT_PATH) -DLWS_C_FLAGS="$(CFLAGS) -DNDEBUG=1" \ + cmake $(COMPONENT_PATH) -DLWS_C_FLAGS="$(CFLAGS) " \ -DIDF_PATH=$(IDF_PATH) \ -DCROSS_PATH=$(CROSS_PATH) \ -DBUILD_DIR_BASE=$(BUILD_DIR_BASE) \ -DCMAKE_TOOLCHAIN_FILE=$(COMPONENT_PATH)/contrib/cross-esp32.cmake \ -DCMAKE_BUILD_TYPE=RELEASE \ - -DLWS_MBEDTLS_INCLUDE_DIRS="${IDF_PATH}/components/openssl/include;${IDF_PATH}/components/mbedtls/mbedtls/include;${IDF_PATH}/components/mbedtls/port/include" \ + -DLWS_MBEDTLS_INCLUDE_DIRS="${IDF_PATH}/components/openssl/include;${IDF_PATH}/components/mbedtls/include;${IDF_PATH}/components/mbedtls/port/include" \ -DLWS_WITH_STATS=0 \ -DLWS_WITH_HTTP2=1 \ - -DLWS_WITH_RANGES=1 \ - -DLWS_WITH_ACME=1 \ - -DLWS_WITH_ZLIB=1 \ - -DLWS_WITH_ZIP_FOPS=1 \ -DZLIB_LIBRARY=$(BUILD_DIR_BASE)/zlib/libzlib.a \ -DZLIB_INCLUDE_DIR=$(COMPONENT_PATH)/../zlib \ - -DLWS_WITH_ESP32=1 \ - $(MSYS_FLAGS) ; \ + -DLWS_WITH_ESP32=1 ;\ make && \ cp ${COMPONENT_BUILD_DIR}/lib/libwebsockets.a ${COMPONENT_BUILD_DIR}/liblibwebsockets.a @@ -43,3 +38,6 @@ myclean: rm -rf ./build + +INCLUDES := $(INCLUDES) -I build/ + diff -Nru libwebsockets-4.0.20/contrib/abi/libwebsockets.json libwebsockets-2.4.2/contrib/abi/libwebsockets.json --- libwebsockets-4.0.20/contrib/abi/libwebsockets.json 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/abi/libwebsockets.json 2018-03-08 10:28:37.000000000 +0000 @@ -15,156 +15,6 @@ "ABIDiff": "Off" }, { - "Number": "3.0.0", - "Installed": "installed/libwebsockets/3.0.0", - "Source": "src/libwebsockets/3.0.0/libwebsockets-3.0.0.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "2.4.2", - "Installed": "installed/libwebsockets/2.4.2", - "Source": "src/libwebsockets/2.4.2/libwebsockets-2.4.2.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "2.4.1", - "Installed": "installed/libwebsockets/2.4.1", - "Source": "src/libwebsockets/2.4.1/libwebsockets-2.4.1.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "2.4.0", - "Installed": "installed/libwebsockets/2.4.0", - "Source": "src/libwebsockets/2.4.0/libwebsockets-2.4.0.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "2.3.0", - "Installed": "installed/libwebsockets/2.3.0", - "Source": "src/libwebsockets/2.3.0/libwebsockets-2.3.0.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "2.2.2", - "Installed": "installed/libwebsockets/2.2.2", - "Source": "src/libwebsockets/2.2.2/libwebsockets-2.2.2.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "2.2.1", - "Installed": "installed/libwebsockets/2.2.1", - "Source": "src/libwebsockets/2.2.1/libwebsockets-2.2.1.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "2.2.0", - "Installed": "installed/libwebsockets/2.2.0", - "Source": "src/libwebsockets/2.2.0/libwebsockets-2.2.0.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "2.1.1", - "Installed": "installed/libwebsockets/2.1.1", - "Source": "src/libwebsockets/2.1.1/libwebsockets-2.1.1.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "2.1.0", - "Installed": "installed/libwebsockets/2.1.0", - "Source": "src/libwebsockets/2.1.0/libwebsockets-2.1.0.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "1.7.9", - "Installed": "installed/libwebsockets/1.7.9", - "Source": "src/libwebsockets/1.7.9/libwebsockets-1.7.9.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "1.7.8", - "Installed": "installed/libwebsockets/1.7.8", - "Source": "src/libwebsockets/1.7.8/libwebsockets-1.7.8.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "1.7.7", - "Installed": "installed/libwebsockets/1.7.7", - "Source": "src/libwebsockets/1.7.7/libwebsockets-1.7.7.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "1.7.6", - "Installed": "installed/libwebsockets/1.7.6", - "Source": "src/libwebsockets/1.7.6/libwebsockets-1.7.6.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { - "Number": "1.7.5", - "Installed": "installed/libwebsockets/1.7.5", - "Source": "src/libwebsockets/1.7.5/libwebsockets-1.7.5.tar.gz", - "Changelog": "changelog", - "HeadersDiff": "On", - "PkgDiff": "Off", - "ABIView": "Off", - "ABIDiff": "Off" - }, - { "Number": "1.7.4", "Installed": "installed/libwebsockets/1.7.4", "Source": "src/libwebsockets/1.7.4/libwebsockets-1.7.4.tar.gz", diff -Nru libwebsockets-4.0.20/contrib/abi/README.md libwebsockets-2.4.2/contrib/abi/README.md --- libwebsockets-4.0.20/contrib/abi/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/abi/README.md 2018-03-08 10:28:37.000000000 +0000 @@ -20,13 +20,7 @@ Installation ------------ -The abi monitoring stuff is now packaged in, eg, fedora, which is a lot -easier than using the helper script. - -``` -# dnf install abi-tracker vtable-dumper - -Otherwise, the author provides an "easy way" to install the various tools he provides: +The author provides an easy way to install the various tools he provides: git clone https://github.com/lvc/installer cd installer diff -Nru libwebsockets-4.0.20/contrib/android-make-script.sh libwebsockets-2.4.2/contrib/android-make-script.sh --- libwebsockets-4.0.20/contrib/android-make-script.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/android-make-script.sh 2018-03-08 10:28:37.000000000 +0000 @@ -3,40 +3,52 @@ # # Build libwebsockets static library for Android # +# requires debian package xutils-dev for makedepend (openssl make depend) +# + +# This is based on http://stackoverflow.com/questions/11929773/compiling-the-latest-openssl-for-android/ +# via https://github.com/warmcat/libwebsockets/pull/502 # path to NDK -export NDK=/opt/ndk_r17/android-ndk-r17-beta2-linux-x86_64/android-ndk-r17-beta2 -export ANDROID_NDK=${NDK} -export TOOLCHAIN=${NDK}/toolchain -export CORSS_SYSROOT=${NDK}/sysroot -export SYSROOT=${NDK}/platforms/android-22/arch-arm +export NDK=/opt/Android/SDK/ndk-bundle + set -e -# Download packages libz, libuv, mbedtls and libwebsockets -#zlib-1.2.8 -#libuv-1.x -#mbedtls-2.11.0 -#libwebsockets-3.0.0 +# Download packages libz, openssl and libwebsockets +[ ! -f zlib-1.2.8.tar.gz ] && { +wget http://prdownloads.sourceforge.net/libpng/zlib-1.2.8.tar.gz +} -# create a local android toolchain -API=${3:-24} +[ ! -f openssl-1.0.2g.tar.gz ] && { +wget https://openssl.org/source/openssl-1.0.2g.tar.gz +} +[ ! -f libwebsockets.tar.gz ] && { +git clone https://github.com/warmcat/libwebsockets.git +tar caf libwebsockets.tar.gz libwebsockets +} + +# Clean then Unzip + +[ -d zlib-1.2.8 ] && rm -fr zlib-1.2.8 +[ -d openssl-1.0.2g ] && rm -fr openssl-1.0.2g +[ -d libwebsockets ] && rm -fr libwebsockets +[ -d android-toolchain-arm ] && rm -fr android-toolchain-arm +tar xf zlib-1.2.8.tar.gz +tar xf openssl-1.0.2g.tar.gz +tar xf libwebsockets.tar.gz + +# create a local android toolchain $NDK/build/tools/make-standalone-toolchain.sh \ + --platform=android-9 \ --toolchain=arm-linux-androideabi-4.9 \ - --arch=arm \ - --install-dir=`pwd`/android-toolchain-arm \ - --platform=android-$API \ - --stl=libc++ \ - --force \ - --verbose + --install-dir=`pwd`/android-toolchain-arm # setup environment to use the gcc/ld from the android toolchain -export INSTALL_PATH=/opt/libwebsockets_android/android-toolchain-arm -export TOOLCHAIN_PATH=`pwd`/android-toolchain-arm +export TOOLCHAIN_PATH=`pwd`/android-toolchain-arm/bin export TOOL=arm-linux-androideabi -export NDK_TOOLCHAIN_BASENAME=${TOOLCHAIN_PATH}/bin/${TOOL} -export PATH=`pwd`/android-toolchain-arm/bin:$PATH +export NDK_TOOLCHAIN_BASENAME=${TOOLCHAIN_PATH}/${TOOL} export CC=$NDK_TOOLCHAIN_BASENAME-gcc export CXX=$NDK_TOOLCHAIN_BASENAME-g++ export LINK=${CXX} @@ -44,73 +56,59 @@ export AR=$NDK_TOOLCHAIN_BASENAME-ar export RANLIB=$NDK_TOOLCHAIN_BASENAME-ranlib export STRIP=$NDK_TOOLCHAIN_BASENAME-strip -export PLATFORM=android -export CFLAGS="D__ANDROID_API__=$API" -# configure and build libuv -[ ! -f ./android-toolchain-arm/lib/libuv.so ] && { -cd libuv -echo "=============================================>> build libuv" - -PATH=$TOOLCHAIN_PATH:$PATH make clean -PATH=$TOOLCHAIN_PATH:$PATH make -PATH=$TOOLCHAIN_PATH:$PATH make install -echo "<<============================================= build libuv" -cd .. -} +# setup buildflags +export ARCH_FLAGS="-mthumb" +export ARCH_LINK= +export CPPFLAGS=" ${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64 " +export CXXFLAGS=" ${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64 -frtti -fexceptions " +export CFLAGS=" ${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64 " +export LDFLAGS=" ${ARCH_LINK} " # configure and build zlib -[ ! -f ./android-toolchain-arm/lib/libz.so ] && { +[ ! -f ./android-toolchain-arm/lib/libz.a ] && { cd zlib-1.2.8 -echo "=============================================>> build libz" - -PATH=$TOOLCHAIN_PATH:$PATH make clean +PATH=$TOOLCHAIN_PATH:$PATH ./configure --static --prefix=$TOOLCHAIN_PATH/.. PATH=$TOOLCHAIN_PATH:$PATH make PATH=$TOOLCHAIN_PATH:$PATH make install -echo "<<============================================= build libz" cd .. } -# configure and build mbedtls -[ ! -f ./android-toolchain-arm/lib/libmbedtls.so ] && { -echo "=============================================>> build mbedtls" -PREFIX=$TOOLCHAIN_PATH -cd mbedtls-2.11.0 -[ ! -d build ] && mkdir build -cd build -export CFLAGS="$CFLAGS -fomit-frame-pointer" - -PATH=$TOOLCHAIN_PATH:$PATH cmake .. -DCMAKE_TOOLCHAIN_FILE=`pwd`/../cross-arm-android-gnueabi.cmake \ - -DCMAKE_INSTALL_PREFIX:PATH=${INSTALL_PATH} \ - -DCMAKE_BUILD_TYPE=RELEASE -DUSE_SHARED_MBEDTLS_LIBRARY=On - -PATH=$TOOLCHAIN_PATH:$PATH make clean -PATH=$TOOLCHAIN_PATH:$PATH make SHARED=1 -PATH=$TOOLCHAIN_PATH:$PATH make install -echo "<<============================================= build mbedtls" -cd ../.. +# configure and build openssl +[ ! -f ./android-toolchain-arm/lib/libssl.a ] && { +PREFIX=$TOOLCHAIN_PATH/.. +cd openssl-1.0.2g +./Configure android --prefix=${PREFIX} no-shared no-idea no-mdc2 no-rc5 no-zlib no-zlib-dynamic enable-tlsext no-ssl2 no-ssl3 enable-ec enable-ecdh enable-ecp +PATH=$TOOLCHAIN_PATH:$PATH make depend +PATH=$TOOLCHAIN_PATH:$PATH make +PATH=$TOOLCHAIN_PATH:$PATH make install_sw +cd .. } # configure and build libwebsockets -[ ! -f ./android-toolchain-arm/lib/libwebsockets.so ] && { +[ ! -f ./android-toolchain-arm/lib/libwebsockets.a ] && { cd libwebsockets [ ! -d build ] && mkdir build cd build -echo "=============================================>> build libwebsockets" - -PATH=$TOOLCHAIN_PATH:$PATH cmake .. -DCMAKE_TOOLCHAIN_FILE=`pwd`/../cross-arm-android-gnueabi.cmake \ - -DCMAKE_INSTALL_PREFIX:PATH=${INSTALL_PATH} \ - -DLWS_WITH_LWSWS=1 \ - -DLWS_WITH_MBEDTLS=1 \ - -DLWS_WITHOUT_TESTAPPS=1 \ - -DLWS_MBEDTLS_LIBRARIES="${INSTALL_PATH}/lib/libmbedcrypto.a;${INSTALL_PATH}/lib/libmbedtls.a;${INSTALL_PATH}/lib/libmbedx509.a" \ - -DLWS_MBEDTLS_INCLUDE_DIRS=${INSTALL_PATH}/include \ - -DLWS_LIBUV_LIBRARIES=${INSTALL_PATH}/lib/libuv.so \ - -DLWS_LIBUV_INCLUDE_DIRS=${INSTALL_PATH}/include \ - -DLWS_ZLIB_LIBRARIES=${INSTALL_PATH}/lib/libz.so \ - -DLWS_ZLIB_INCLUDE_DIRS=${INSTALL_PATH}/include +PATH=$TOOLCHAIN_PATH:$PATH cmake \ + -DCMAKE_C_COMPILER=$CC \ + -DCMAKE_AR=$AR \ + -DCMAKE_RANLIB=$RANLIB \ + -DCMAKE_C_FLAGS="$CFLAGS" \ + -DCMAKE_INSTALL_PREFIX=$TOOLCHAIN_PATH/.. \ + -DLWS_WITH_SHARED=OFF \ + -DLWS_WITH_STATIC=ON \ + -DLWS_WITHOUT_DAEMONIZE=ON \ + -DLWS_WITHOUT_TESTAPPS=ON \ + -DLWS_IPV6=OFF \ + -DLWS_WITH_BUNDLED_ZLIB=OFF \ + -DLWS_WITH_SSL=ON \ + -DLWS_WITH_HTTP2=ON \ + -DLWS_OPENSSL_LIBRARIES="$TOOLCHAIN_PATH/../lib/libssl.a;$TOOLCHAIN_PATH/../lib/libcrypto.a" \ + -DLWS_OPENSSL_INCLUDE_DIRS=$TOOLCHAIN_PATH/../include \ + -DCMAKE_BUILD_TYPE=Debug \ + .. PATH=$TOOLCHAIN_PATH:$PATH make PATH=$TOOLCHAIN_PATH:$PATH make install -echo "<<============================================= build libwebsockets" cd ../.. } diff -Nru libwebsockets-4.0.20/contrib/cross-aarch64-android.cmake libwebsockets-2.4.2/contrib/cross-aarch64-android.cmake --- libwebsockets-4.0.20/contrib/cross-aarch64-android.cmake 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/cross-aarch64-android.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -# -# CMake Toolchain file for crosscompiling Android / aarch64 -# -# This can be used when running cmake in the following way: -# cd build/ -# cmake .. -DCMAKE_TOOLCHAIN_FILE=../cross-aarch64-android.cmake -# - -# Target operating system name. -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR aarch64) - -# Name of C compiler. -set(CMAKE_C_COMPILER "aarch64-linux-android-gcc") -set(CMAKE_CXX_COMPILER "aarch64-linux-android-g++") - -# -# Different build system distros set release optimization level to different -# things according to their local policy, eg, Fedora is -O2 and Ubuntu is -O3 -# here. Actually the build system's local policy is completely unrelated to -# our desire for cross-build release optimization policy for code built to run -# on a completely different target than the build system itself. -# -# Since this goes last on the compiler commandline we have to override it to a -# sane value for cross-build here. Notice some gcc versions enable broken -# optimizations with -O3. -# -if (CMAKE_BUILD_TYPE MATCHES RELEASE OR CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES release) - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") -endif() - -#-nostdlib -SET(CMAKE_C_FLAGS "-DGCC_VER=\"\\\"$(GCC_VER)\\\"\" -DARM64=1 -D__LP64__=1 -Os -g3 -fpie -mstrict-align -fPIC -ffunction-sections -fdata-sections " CACHE STRING "" FORCE) - - -# Where to look for the target environment. (More paths can be added here) -#set(CMAKE_FIND_ROOT_PATH "") - -# Adjust the default behavior of the FIND_XXX() commands: -# search programs in the host environment only. -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - -# Search headers and libraries in the target environment only. -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - - diff -Nru libwebsockets-4.0.20/contrib/cross-aarch64.cmake libwebsockets-2.4.2/contrib/cross-aarch64.cmake --- libwebsockets-4.0.20/contrib/cross-aarch64.cmake 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/cross-aarch64.cmake 2018-03-08 10:28:37.000000000 +0000 @@ -14,24 +14,8 @@ set(CMAKE_C_COMPILER "aarch64-linux-gnu-gcc") set(CMAKE_CXX_COMPILER "aarch64-linux-gnu-g++") -# -# Different build system distros set release optimization level to different -# things according to their local policy, eg, Fedora is -O2 and Ubuntu is -O3 -# here. Actually the build system's local policy is completely unrelated to -# our desire for cross-build release optimization policy for code built to run -# on a completely different target than the build system itself. -# -# Since this goes last on the compiler commandline we have to override it to a -# sane value for cross-build here. Notice some gcc versions enable broken -# optimizations with -O3. -# -if (CMAKE_BUILD_TYPE MATCHES RELEASE OR CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES release) - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") -endif() - #-nostdlib -SET(CMAKE_C_FLAGS "-DGCC_VER=\"\\\"$(GCC_VER)\\\"\" -DARM64=1 -D__LP64__=1 -Os -g3 -fpie -mstrict-align -DOPTEE_DEV_KIT=../../../../out/arm-plat-hikey/export-ta_arm64/include -I../../../../lib/libutee/include -fPIC -ffunction-sections -fdata-sections -I../../../../core/include" CACHE STRING "" FORCE) +SET(CMAKE_C_FLAGS "-DGCC_VER=\"\\\"$(GCC_VER)\\\"\" -DARM64=1 -D__LP64__=1 -Os -g3 -fpie -mstrict-align -DOPTEE_DEV_KIT=../../../optee_os/out/arm-plat-hikey/export-ta_arm64/include -fPIC -ffunction-sections -fdata-sections" CACHE STRING "" FORCE) # Where to look for the target environment. (More paths can be added here) diff -Nru libwebsockets-4.0.20/contrib/cross-arm-android-gnueabi.cmake libwebsockets-2.4.2/contrib/cross-arm-android-gnueabi.cmake --- libwebsockets-4.0.20/contrib/cross-arm-android-gnueabi.cmake 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/cross-arm-android-gnueabi.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -# -# CMake Toolchain file for crosscompiling on ARM. -# -# This can be used when running cmake in the following way: -# cd build/ -# cmake .. -DCMAKE_TOOLCHAIN_FILE=../cross-arm-linux-gnueabihf.cmake -# - -set(CROSS_PATH /opt/libwebsockets_android/android-toolchain-arm) - -# Target operating system name. -set(CMAKE_SYSTEM_NAME Android) - -# Target build dynamic libs. -set(BUILD_SHARED_LIBS ON) - -# Name of C compiler. -set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/arm-linux-androideabi-gcc") -set(CMAKE_CXX_COMPILER "${CROSS_PATH}/bin/arm-linux-androideabi-g++") - -# -# Different build system distros set release optimization level to different -# things according to their local policy, eg, Fedora is -O2 and Ubuntu is -O3 -# here. Actually the build system's local policy is completely unrelated to -# our desire for cross-build release optimization policy for code built to run -# on a completely different target than the build system itself. -# -# Since this goes last on the compiler commandline we have to override it to a -# sane value for cross-build here. Notice some gcc versions enable broken -# optimizations with -O3. -# -if (CMAKE_BUILD_TYPE MATCHES RELEASE OR CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES release) - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") -endif() - -# Where to look for the target environment. (More paths can be added here) -set(CMAKE_FIND_ROOT_PATH "${CROSS_PATH}") - -# Adjust the default behavior of the FIND_XXX() commands: -# search programs in the host environment only. -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - -# Search headers and libraries in the target environment only. -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff -Nru libwebsockets-4.0.20/contrib/cross-arm-linux-gnueabihf.cmake libwebsockets-2.4.2/contrib/cross-arm-linux-gnueabihf.cmake --- libwebsockets-4.0.20/contrib/cross-arm-linux-gnueabihf.cmake 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/cross-arm-linux-gnueabihf.cmake 2018-03-08 10:28:37.000000000 +0000 @@ -15,22 +15,6 @@ set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/arm-linux-gnueabihf-gcc") set(CMAKE_CXX_COMPILER "${CROSS_PATH}/bin/arm-linux-gnueabihf-g++") -# -# Different build system distros set release optimization level to different -# things according to their local policy, eg, Fedora is -O2 and Ubuntu is -O3 -# here. Actually the build system's local policy is completely unrelated to -# our desire for cross-build release optimization policy for code built to run -# on a completely different target than the build system itself. -# -# Since this goes last on the compiler commandline we have to override it to a -# sane value for cross-build here. Notice some gcc versions enable broken -# optimizations with -O3. -# -if (CMAKE_BUILD_TYPE MATCHES RELEASE OR CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES release) - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") -endif() - # Where to look for the target environment. (More paths can be added here) set(CMAKE_FIND_ROOT_PATH "${CROSS_PATH}") diff -Nru libwebsockets-4.0.20/contrib/cross-esp32.cmake libwebsockets-2.4.2/contrib/cross-esp32.cmake --- libwebsockets-4.0.20/contrib/cross-esp32.cmake 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/cross-esp32.cmake 2018-03-08 10:28:37.000000000 +0000 @@ -10,32 +10,14 @@ set(CMAKE_SYSTEM_NAME Linux) # Name of C compiler. -set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/xtensa-esp32-elf-gcc${EXECUTABLE_EXT}") -set(CMAKE_AR "${CROSS_PATH}/bin/xtensa-esp32-elf-ar${EXECUTABLE_EXT}") -set(CMAKE_RANLIB "${CROSS_PATH}/bin/xtensa-esp32-elf-ranlib${EXECUTABLE_EXT}") -set(CMAKE_LINKER "${CROSS_PATH}/bin/xtensa-esp32-elf-ld${EXECUTABLE_EXT}") - -# -# Different build system distros set release optimization level to different -# things according to their local policy, eg, Fedora is -O2 and Ubuntu is -O3 -# here. Actually the build system's local policy is completely unrelated to -# our desire for cross-build release optimization policy for code built to run -# on a completely different target than the build system itself. -# -# Since this goes last on the compiler commandline we have to override it to a -# sane value for cross-build here. Notice some gcc versions enable broken -# optimizations with -O3. -# -if (CMAKE_BUILD_TYPE MATCHES RELEASE OR CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES release) - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") -endif() +set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/xtensa-esp32-elf-gcc") +set(CMAKE_AR "${CROSS_PATH}/bin/xtensa-esp32-elf-ar") +set(CMAKE_RANLIB "${CROSS_PATH}/bin/xtensa-esp32-elf-ranlib") +set(CMAKE_LINKER "${CROSS_PATH}/bin/xtensa-esp32-elf-ld") SET(CMAKE_C_FLAGS "-nostdlib -Wall -Werror \ -I${BUILD_DIR_BASE}/include \ - -I${IDF_PATH}/components/newlib/platform_include \ -I${IDF_PATH}/components/mdns/include \ - -I${IDF_PATH}/components/heap/include \ -I${IDF_PATH}/components/driver/include \ -I${IDF_PATH}/components/spi_flash/include \ -I${IDF_PATH}/components/nvs_flash/include \ @@ -47,8 +29,6 @@ -I${IDF_PATH}/components/bootloader_support/include/ \ -I${IDF_PATH}/components/app_update/include/ \ -I$(IDF_PATH)/components/soc/esp32/include/ \ - -I$(IDF_PATH)/components/soc/include/ \ - -I$(IDF_PATH)/components/vfs/include/ \ ${LWS_C_FLAGS} -Os \ -I${IDF_PATH}/components/nvs_flash/test_nvs_host \ -I${IDF_PATH}/components/freertos/include" CACHE STRING "" FORCE) diff -Nru libwebsockets-4.0.20/contrib/cross-linkit.cmake libwebsockets-2.4.2/contrib/cross-linkit.cmake --- libwebsockets-4.0.20/contrib/cross-linkit.cmake 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/cross-linkit.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -# -# CMake Toolchain file for crosscompiling on Mediatek Linkit 7967 -# -# This can be used like this (with Linkit sdk unpacked to /projects/linkit/sdk) -# -# cd build/ -# cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/projects/linkit/cross-root \ -# -DCMAKE_TOOLCHAIN_FILE=../contrib/cross-linkit.cmake \ -# -DLWS_PLAT_FREERTOS=1 \ -# -DLWS_WITH_ZLIB=0 \ -# -DLWS_WITHOUT_EXTENSIONS=1 \ -# -DLWS_WITH_ZIP_FOPS=0 \ -# -DLWS_WITH_HTTP_STREAM_COMPRESSION=0 \ -# -DLWS_WITH_MBEDTLS=1 \ -# -DLWS_WITH_FILE_OPS=0 -# - -# if your sdk lives somewhere else, this is the only place that should need changing -set(CROSS_BASE /projects/linkit/sdk) -set(CROSS_PATH ${CROSS_BASE}/tools/gcc/gcc-arm-none-eabi) - -# -# Target operating system name. -set(CMAKE_SYSTEM_NAME Generic) - -# Name of C compiler. -set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/arm-none-eabi-gcc") -set(CMAKE_CXX_COMPILER "${CROSS_PATH}/bin/arm-none-eabi-g++") - -# -# cmake believes we should link a NOP test program OK, but since we're -# baremetal, that's not true in our case. It tries to build this test -# with the cross compiler, but with no args on it, and it fails. -# So disable this test for this toolchain (we'll find out soon enough -# if we actually can't compile anything) - -set(CMAKE_C_COMPILER_WORKS 1) -set(CMAKE_CXX_COMPILER_WORKS 1) - -# -# Different build system distros set release optimization level to different -# things according to their local policy, eg, Fedora is -O2 and Ubuntu is -O3 -# here. Actually the build system's local policy is completely unrelated to -# our desire for cross-build release optimization policy for code built to run -# on a completely different target than the build system itself. -# -# Since this goes last on the compiler commandline we have to override it to a -# sane value for cross-build here. Notice some gcc versions enable broken -# optimizations with -O3. -# -if (CMAKE_BUILD_TYPE MATCHES RELEASE OR CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES release) - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") -endif() - -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostartfiles -I${CROSS_BASE}/middleware/third_party/lwip/src/include/lwip -I${CROSS_BASE}/middleware/third_party/lwip/src/include -I${CROSS_BASE}/project/mt7687_hdk/apps/httpd/inc/ -I${CROSS_BASE}/kernel/service/inc/ -I${CROSS_BASE}/driver/chip/inc -I${CROSS_BASE}/driver/chip/mt7687/inc/ -I${CROSS_BASE}/driver/CMSIS/Device/MTK/mt7687/Include/ -I${CROSS_BASE}/driver/CMSIS/Include -I${CROSS_BASE}/middleware/third_party/lwip/ports/include/ -I${CROSS_BASE}/middleware/third_party/lwip/src/include/posix/ -I${CROSS_BASE}/kernel/rtos/FreeRTOS/Source/include/ -I${CROSS_BASE}/middleware/third_party/mbedtls/include/ -I${CROSS_BASE}/kernel/rtos/FreeRTOS/Source/portable/GCC/ARM_CM4F/ -I${CROSS_BASE}/middleware/third_party/sntp/inc/ -DLWS_AMAZON_RTOS=1" CACHE STRING "" FORCE) - -# Where to look for the target environment. (More paths can be added here) -set(CMAKE_FIND_ROOT_PATH "${CROSS_PATH}") - -# Adjust the default behavior of the FIND_XXX() commands: -# search programs in the host environment only. -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - -# Search headers and libraries in the target environment only. -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - diff -Nru libwebsockets-4.0.20/contrib/cross-ming.cmake libwebsockets-2.4.2/contrib/cross-ming.cmake --- libwebsockets-4.0.20/contrib/cross-ming.cmake 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/cross-ming.cmake 2018-03-08 10:28:37.000000000 +0000 @@ -18,22 +18,6 @@ set(CMAKE_RC_COMPILER "${CROSS_PATH}/x86_64-w64-mingw32-windres") set(CMAKE_C_FLAGS "-Wno-error") -# -# Different build system distros set release optimization level to different -# things according to their local policy, eg, Fedora is -O2 and Ubuntu is -O3 -# here. Actually the build system's local policy is completely unrelated to -# our desire for cross-build release optimization policy for code built to run -# on a completely different target than the build system itself. -# -# Since this goes last on the compiler commandline we have to override it to a -# sane value for cross-build here. Notice some gcc versions enable broken -# optimizations with -O3. -# -if (CMAKE_BUILD_TYPE MATCHES RELEASE OR CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES release) - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") -endif() - # Where to look for the target environment. (More paths can be added here) set(CMAKE_FIND_ROOT_PATH "${CROSS_PATH}") diff -Nru libwebsockets-4.0.20/contrib/cross-w32.cmake libwebsockets-2.4.2/contrib/cross-w32.cmake --- libwebsockets-4.0.20/contrib/cross-w32.cmake 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/cross-w32.cmake 2018-03-08 10:28:37.000000000 +0000 @@ -17,22 +17,6 @@ set(CMAKE_RC_COMPILER "${CROSS_PATH}/bin/i686-w64-mingw32-windres") set(CMAKE_C_FLAGS "-Wno-error") -# -# Different build system distros set release optimization level to different -# things according to their local policy, eg, Fedora is -O2 and Ubuntu is -O3 -# here. Actually the build system's local policy is completely unrelated to -# our desire for cross-build release optimization policy for code built to run -# on a completely different target than the build system itself. -# -# Since this goes last on the compiler commandline we have to override it to a -# sane value for cross-build here. Notice some gcc versions enable broken -# optimizations with -O3. -# -if (CMAKE_BUILD_TYPE MATCHES RELEASE OR CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES release) - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") -endif() - # Where to look for the target environment. (More paths can be added here) set(CMAKE_FIND_ROOT_PATH "${CROSS_PATH}") diff -Nru libwebsockets-4.0.20/contrib/cross-w64.cmake libwebsockets-2.4.2/contrib/cross-w64.cmake --- libwebsockets-4.0.20/contrib/cross-w64.cmake 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/contrib/cross-w64.cmake 2018-03-08 10:28:37.000000000 +0000 @@ -17,22 +17,6 @@ set(CMAKE_RC_COMPILER "${CROSS_PATH}/bin/x86_64-w64-mingw32-windres") set(CMAKE_C_FLAGS "-Wno-error") -# -# Different build system distros set release optimization level to different -# things according to their local policy, eg, Fedora is -O2 and Ubuntu is -O3 -# here. Actually the build system's local policy is completely unrelated to -# our desire for cross-build release optimization policy for code built to run -# on a completely different target than the build system itself. -# -# Since this goes last on the compiler commandline we have to override it to a -# sane value for cross-build here. Notice some gcc versions enable broken -# optimizations with -O3. -# -if (CMAKE_BUILD_TYPE MATCHES RELEASE OR CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES release) - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") -endif() - # Where to look for the target environment. (More paths can be added here) set(CMAKE_FIND_ROOT_PATH "${CROSS_PATH}") diff -Nru libwebsockets-4.0.20/debian/changelog libwebsockets-2.4.2/debian/changelog --- libwebsockets-4.0.20/debian/changelog 2020-07-14 15:27:49.000000000 +0000 +++ libwebsockets-2.4.2/debian/changelog 2020-12-12 15:31:44.000000000 +0000 @@ -1,286 +1,5 @@ -libwebsockets (4.0.20-1) unstable; urgency=medium +libwebsockets (2.4.2-0~mosquitto1~groovy1) groovy; urgency=medium - * New upstream release. + * PPA upload - -- Laszlo Boszormenyi (GCS) Tue, 14 Jul 2020 17:27:49 +0200 - -libwebsockets (4.0.19-1) unstable; urgency=medium - - * New upstream release. - * Update library symbols for this release. - - [ Matti Hamalainen ] - * Re-enable extensions support (closes: #962821). - - -- Laszlo Boszormenyi (GCS) Sat, 27 Jun 2020 17:33:41 +0200 - -libwebsockets (4.0.16-1) unstable; urgency=medium - - * New upstream release. - - -- Laszlo Boszormenyi (GCS) Sun, 14 Jun 2020 14:09:19 +0200 - -libwebsockets (4.0.15-2) unstable; urgency=medium - - * Upload to Sid. - - -- Laszlo Boszormenyi (GCS) Thu, 11 Jun 2020 07:55:41 +0200 - -libwebsockets (4.0.15-1) experimental; urgency=medium - - * New upstream release. - - [ Pino Toscano ] - * Enable to compile again on !Linux archs (closes: #955281). - - -- Laszlo Boszormenyi (GCS) Sat, 06 Jun 2020 07:49:47 +0200 - -libwebsockets (4.0.13-1) experimental; urgency=medium - - * New upstream release. - - -- Laszlo Boszormenyi (GCS) Mon, 01 Jun 2020 07:07:42 +0200 - -libwebsockets (4.0.1-1) experimental; urgency=medium - - * New upstream release. - - -- Laszlo Boszormenyi (GCS) Sat, 28 Mar 2020 08:29:58 +0000 - -libwebsockets (4.0.0-1) experimental; urgency=medium - - * New upstream release. - * Library transition from libwebsockets15 to libwebsockets16 . - * Update library symbols for this release. - * Update copyright file. - * Update Standards-Version to 4.5.0 . - - -- Laszlo Boszormenyi (GCS) Mon, 09 Mar 2020 18:04:03 +0000 - -libwebsockets (3.2.1-3) unstable; urgency=medium - - * Build with IPv6 support. - * Disable outdated server and client self-tests for now. - - -- Laszlo Boszormenyi (GCS) Sat, 28 Dec 2019 10:13:56 +0000 - -libwebsockets (3.2.1-2) unstable; urgency=medium - - * Upload to Sid. - - -- Laszlo Boszormenyi (GCS) Thu, 26 Dec 2019 21:27:29 +0000 - -libwebsockets (3.2.1-1) experimental; urgency=medium - - * New upstream release. - - -- Laszlo Boszormenyi (GCS) Sat, 21 Dec 2019 22:27:32 +0000 - -libwebsockets (3.2.0-1) experimental; urgency=medium - - * New upstream release (closes: #860197). - * Library transition from libwebsockets12 to libwebsockets15 . - * Update library symbols for this release. - * Update copyright file. - * Update watch file. - * Disable Vcs-* fields for now. - * Update Standards-Version to 4.4.1 . - - -- Laszlo Boszormenyi (GCS) Sun, 13 Oct 2019 14:23:13 +0000 - -libwebsockets (2.4.2-1) experimental; urgency=medium - - * Declare compliance with Debian Policy 4.1.3 with no changes. - * Add 2017 and 2018 to the debian/* copyright years. - * Add "Rules-Requires-Root: no" to the source control stanza. - * Let dpkg-dev handle Large File Support. - * Bump the debhelper compatibility level to 11 with no changes. - * Update the watch file to version 4 of the format and use the new - substitution variables. - * New upstream release: - - point the Vcs-* URLs to the debian-v2.4-stable branch - - drop the typos, soname-8, and openssl-1.1 patches, applied upstream - - update the library SONAME from 8 to 12 and update the symbols file - - do not try to install the API reference documentation - - install all the files from the READMEs/ directory - - add libcap-dev as a build dependency - - update the upstream copyright information - - place the missing source of md5.min.js into debian/missing-sources/ - and also symlink it to lwsgs.js, since that's the part that is - missing from the latter file, too - * Library transition from libwebsockets8 to libwebsockets12. - * Add a trivial autopkgtest that builds a program. - * Add two patches for the "echo" test client. - * Add a "send and receive ten pings" autopkgtest. - - -- Peter Pentchev Fri, 02 Mar 2018 20:39:44 +0200 - -libwebsockets (2.0.3-3) unstable; urgency=medium - - * Build with Unix domain socket support (closes: #860884). - * Be the primary maintainer, thanks to Peter for all the fish. - * Update Standards-Version to 4.0.0 . - - -- Laszlo Boszormenyi (GCS) Sun, 17 Sep 2017 16:47:12 +0000 - -libwebsockets (2.0.3-2.1) unstable; urgency=medium - - * Non-maintainer upload. - * stop-gap: set -Wno-error={format-truncation,format-overflow - (Closes: #853515) - - -- Michael Stapelberg Wed, 23 Aug 2017 21:53:14 +0200 - -libwebsockets (2.0.3-2) unstable; urgency=medium - - * Upload to unstable. - - -- Peter Pentchev Thu, 03 Nov 2016 19:04:11 +0200 - -libwebsockets (2.0.3-1) experimental; urgency=medium - - * Add the typos patch to correct some typographical errors. - * Fix the upstream Git URLs in the watch file and the upstream - metadata file to reflect another URL change on libwebsockets.org. - * Use the HTTPS scheme for the copyright format specification URL. - * Use a pregenerated test server SSL key to make the build reproducible. - Closes: #831569; thanks, Chris Lamb! - * Fix the build with OpenSSL 1.1. Closes: #828416; thanks, Kurt Roeckx! - * New upstream release: - - add the new lws_snprintf function to the symbols file - - add the soname-8 patch to avoid a SONAME bump merely for - an added function - * Point the Vcs-* URLs to the debian-v2.0-stable branch. - * Bump the debhelper compatibility level to 10: - - drop --parallel from the dh invocation, it is the default now - - -- Peter Pentchev Sun, 02 Oct 2016 02:27:22 +0300 - -libwebsockets (2.0.2-1) experimental; urgency=low - - * New upstream release. - * Library transition from libwebsockets7 to libwebsockets8 . - * Update symbols file. - * Update Standards-Version to 3.9.8 . - - -- Laszlo Boszormenyi (GCS) Sun, 19 Jun 2016 11:46:05 +0000 - -libwebsockets (1.7.3-1) unstable; urgency=medium - - * New upstream release. - - -- Peter Pentchev Mon, 29 Feb 2016 13:24:01 +0200 - -libwebsockets (1.7.1-1) unstable; urgency=medium - - * Fix the upstream website URLs after the author migrated to HTTPS - and moved some of the contents around. - * New upstream release: - - remove the typos patch, integrated upstream - - update the Vcs-* fields for the debian-v1.7-stable branch. - - -- Peter Pentchev Sun, 21 Feb 2016 15:15:12 +0200 - -libwebsockets (1.7.0-2) unstable; urgency=medium - - * Brown paper bag release: add libuv1-dev to the runtime - dependencies of libwebsockets-dev. Closes: #815147 - - -- Peter Pentchev Fri, 19 Feb 2016 17:36:54 +0200 - -libwebsockets (1.7.0-1) unstable; urgency=medium - - * Declare compliance with Debian Policy 3.9.7 with no changes. - * Use https:// for the Git-Vcs URL. - * New upstream version: - - drop all the patches (0003-libev-context-destroy, - 0004-libev-context-init, 0005-wsi-context, 02-kfreebsd, and - 03-sort-doc-sources) - integrated (or taken from) upstream - - add -fno-strict-aliasing because of the new test-server-libev.c - - bump the soname to libwebsockets7 and update the symbols file - - update the upstream copyright years - - add the typos patch to fix some typos - - build with support for libuv, too - * Bump the debian/* copyright years. - * Switch to *-dbgsym packages for the library debug symbols. - - -- Peter Pentchev Tue, 16 Feb 2016 15:44:39 +0200 - -libwebsockets (1.6.0-5) unstable; urgency=medium - - * Fix the relationship between the libwebsockets-test-server-common - and the libwebsockets-test-server packages. Closes: #810517 - Suggested by: Gianfranco Costamagna - * Add the 03-sort-doc-sources patch to sort the source *.c files - before passing them to the script that generates the API reference. - Closes: #808877 - * Build with Large File Support. - - -- Peter Pentchev Sun, 17 Jan 2016 01:58:53 +0200 - -libwebsockets (1.6.0-4) unstable; urgency=low - - * Remove unneeded libwebsockets3{,-dbg} packages (closes: #809061). - - -- Laszlo Boszormenyi (GCS) Sat, 09 Jan 2016 08:41:42 +0100 - -libwebsockets (1.6.0-3) unstable; urgency=medium - - * Remove the Conflicts and Replaces fields for the libwebsockets6 and - libwebsockets6-dbg binary packages. Closes: #808878 - * Add the 0003-libev-context-destroy, 0004-libev-context-init, and - 0005-wsi-context upstream patches. - * Refresh the line numbers of the 02-kfreebsd patch. - * Mark the 02-kfreebsd patch as forwarded upstream. - * Always regenerate the HTML API documentation, too, so that - the plain-text documentation is also regenerated. Closes: #808877 - - -- Peter Pentchev Fri, 25 Dec 2015 14:32:06 +0200 - -libwebsockets (1.6.0-2) unstable; urgency=medium - - * Upload to unstable (closes: #794476). - * Add some -dev package runtime dependencies to the libwebsockets-dev - package so that programs that use libwebsockets may actually compile. - Reported by: Gianfranco Costamagna - * Aggregate some of the Files sections in the debian/copyright file: - - almost all of the libwebsockets code is Andy Green's work - - the whole of the win32port/zlib/ directory is joint work by - Jean-loup Gailly and Mark Adler - * Add the 02-kfreebsd patch to fix the FTBFS on GNU/kFreeBSD. - * Point to /usr/share/common-licenses/LGPL-2 in the copyright file. - - -- Peter Pentchev Mon, 21 Dec 2015 18:04:52 +0200 - -libwebsockets (1.6.0-1) experimental; urgency=medium - - * Bump Standards-Version to 3.9.6 with no changes. - * New upstream release: - - drop the 0001-getnameinfo, 0002-keepalive, and 01-visibility - patches, integrated upstream - - switch the build to CMake since autoconf is no longer supported - - bump the soname to libwebsockets6 and update the symbols file - - reflect the renaming of several README.* files to *.md - - update the copyright file - - turn the lws_daemonize build back on for compatibility with - previous Debian releases of libwebsockets - * Bump the debian/* copyright years. - * Move the static data for the test servers to an arch:all package. - * Drop the no longer needed misc:Pre-Depends, see #783898. - * Add an upstream metadata file. - * Remove the get-orig-source script and rules target, we don't repack - anything, so we actually get our tarballs from the watch file's - locations; suggested by - Gianfranco Costamagna - * Turn on the libwebsockets support for libev. - * Change the short name of the license on the base64-decode.c file - from MIT to Expat to be more precise. - * Switch the Vcs-Browser URL to cgit. - - -- Peter Pentchev Fri, 18 Dec 2015 14:12:36 +0200 - -libwebsockets (1.2.2-1) unstable; urgency=low - - * Initial release (closes: #697671). - - -- Peter Pentchev Thu, 28 Mar 2013 19:10:28 +0200 + -- Roger A. Light Sat, 12 Dec 2020 15:31:44 +0000 diff -Nru libwebsockets-4.0.20/debian/control libwebsockets-2.4.2/debian/control --- libwebsockets-4.0.20/debian/control 2020-06-06 05:49:47.000000000 +0000 +++ libwebsockets-2.4.2/debian/control 2020-12-12 15:31:44.000000000 +0000 @@ -9,7 +9,7 @@ Homepage: https://libwebsockets.org/ Rules-Requires-Root: no -Package: libwebsockets16 +Package: libwebsockets12 Architecture: any Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} @@ -24,7 +24,7 @@ Section: libdevel Architecture: any Multi-Arch: same -Depends: libwebsockets16 (= ${binary:Version}), libev-dev, libssl-dev, +Depends: libwebsockets12 (= ${binary:Version}), libev-dev, libssl-dev, libuv1-dev, zlib1g-dev, libcap-dev [linux-any], ${misc:Depends} Description: lightweight C websockets library - development files Libwebsockets is a lightweight pure C library for both websockets @@ -38,7 +38,7 @@ Section: utils Architecture: any Multi-Arch: foreign -Depends: libwebsockets16 (= ${binary:Version}), +Depends: libwebsockets12 (= ${binary:Version}), libwebsockets-test-server-common (= ${source:Version}), ${shlibs:Depends}, ${misc:Depends} Description: lightweight C websockets library - test servers diff -Nru libwebsockets-4.0.20/debian/libwebsockets12.install libwebsockets-2.4.2/debian/libwebsockets12.install --- libwebsockets-4.0.20/debian/libwebsockets12.install 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/debian/libwebsockets12.install 2020-12-12 15:31:44.000000000 +0000 @@ -0,0 +1 @@ +usr/lib/*/lib*.so.* diff -Nru libwebsockets-4.0.20/debian/libwebsockets16.install libwebsockets-2.4.2/debian/libwebsockets16.install --- libwebsockets-4.0.20/debian/libwebsockets16.install 2018-03-02 18:39:44.000000000 +0000 +++ libwebsockets-2.4.2/debian/libwebsockets16.install 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -usr/lib/*/lib*.so.* diff -Nru libwebsockets-4.0.20/debian/libwebsockets16.symbols libwebsockets-2.4.2/debian/libwebsockets16.symbols --- libwebsockets-4.0.20/debian/libwebsockets16.symbols 2020-06-27 15:33:41.000000000 +0000 +++ libwebsockets-2.4.2/debian/libwebsockets16.symbols 1970-01-01 00:00:00.000000000 +0000 @@ -1,330 +0,0 @@ -libwebsockets.so.16 libwebsockets16 #MINVER# - __lws_sul_insert@Base 4.0.0 - __lws_sul_service_ripe@Base 4.0.0 - __lws_system_attach@Base 4.0.0 - _lws_log@Base 1.2 - _lws_logv@Base 1.4 - _lws_plat_file_close@Base 2.4.1 - _lws_plat_file_open@Base 2.4.1 - _lws_plat_file_read@Base 2.4.1 - _lws_plat_file_seek_cur@Base 2.4.1 - _lws_plat_file_write@Base 2.4.1 - _lws_plat_service_tsi@Base 2.4.1 - humanize_schema_si@Base 3.2.0 - humanize_schema_si_bytes@Base 3.2.0 - humanize_schema_us@Base 3.2.0 - lejp_change_callback@Base 3.2.0 - lejp_check_path_match@Base 3.2.0 - lejp_construct@Base 3.2.0 - lejp_destruct@Base 3.2.0 - lejp_error_to_string@Base 3.2.0 - lejp_get_wildcard@Base 3.2.0 - lejp_parse@Base 3.2.0 - lejp_parser_pop@Base 3.2.0 - lejp_parser_push@Base 3.2.0 - lws_SHA1@Base 1.6.0 - lws_add_http_common_headers@Base 3.2.0 - lws_add_http_header_by_name@Base 1.4 - lws_add_http_header_by_token@Base 1.4 - lws_add_http_header_content_length@Base 1.4 - lws_add_http_header_status@Base 1.4 - lws_adjust_protocol_psds@Base 2.4.1 - lws_adopt_descriptor_vhost@Base 2.4.1 - lws_adopt_descriptor_vhost_via_info@Base 4.0.0 - lws_adopt_socket@Base 1.7.0 - lws_adopt_socket_readbuf@Base 2.0.2 - lws_adopt_socket_vhost@Base 2.4.1 - lws_adopt_socket_vhost_readbuf@Base 2.4.1 - lws_b64_decode_state_init@Base 4.0.0 - lws_b64_decode_stateful@Base 4.0.0 - lws_b64_decode_string@Base 1.4 - lws_b64_decode_string_len@Base 3.2.0 - lws_b64_encode_string@Base 1.2 - lws_b64_encode_string_url@Base 3.2.0 - lws_buflist_append_segment@Base 3.2.0 - lws_buflist_describe@Base 4.0.0 - lws_buflist_destroy_all_segments@Base 3.2.0 - lws_buflist_linear_copy@Base 4.0.0 - lws_buflist_next_segment_len@Base 3.2.0 - lws_buflist_total_len@Base 4.0.0 - lws_buflist_use_segment@Base 3.2.0 - lws_callback_all_protocol@Base 1.6.0 - lws_callback_all_protocol_vhost@Base 2.0.2 - lws_callback_all_protocol_vhost_args@Base 2.4.1 - lws_callback_http_dummy@Base 2.4.1 - lws_callback_on_writable@Base 1.6.0 - lws_callback_on_writable_all_protocol@Base 1.6.0 - lws_callback_on_writable_all_protocol_vhost@Base 2.0.2 - lws_callback_vhost_protocols@Base 2.4.1 - lws_callback_vhost_protocols_vhost@Base 3.2.0 - lws_cancel_service@Base 1.6.0 - lws_cancel_service_pt@Base 1.7.0 - lws_canonical_hostname@Base 1.6.0 - lws_chunked_html_process@Base 2.4.1 - lws_clear_child_pending_on_writable@Base 2.4.1 - lws_client_connect_via_info@Base 1.7.0 - lws_client_http_body_pending@Base 2.4.1 - lws_client_http_multipart@Base 4.0.0 - lws_client_reset@Base 1.7.0 - lws_close_reason@Base 1.7.0 - lws_cmdline_option@Base 3.2.0 - lws_cmdline_option_handle_builtin@Base 4.0.0 - lws_context_deprecate@Base 2.4.1 - lws_context_destroy@Base 1.6.0 - lws_context_init_extensions@Base 4.0.19 - lws_context_is_deprecated@Base 2.4.1 - lws_context_user@Base 1.6.0 - lws_create_adopt_udp@Base 3.2.0 - lws_create_context@Base 1.6.0 - lws_create_vhost@Base 2.0.2 - lws_daemonize@Base 1.2 - lws_dir@Base 3.2.0 - lws_dll2_add_before@Base 3.2.0 - lws_dll2_add_head@Base 3.2.0 - lws_dll2_add_sorted@Base 3.2.0 - lws_dll2_add_tail@Base 3.2.0 - lws_dll2_clear@Base 3.2.0 - lws_dll2_foreach_safe@Base 3.2.0 - lws_dll2_owner_clear@Base 3.2.0 - lws_dll2_remove@Base 3.2.0 - lws_explicit_bzero@Base 3.2.0 - lws_ext_parse_options@Base 4.0.19 - lws_extension_callback_pm_deflate@Base 4.0.19 - lws_filename_purify_inplace@Base 3.2.0 - lws_finalize_http_header@Base 1.4 - lws_finalize_startup@Base 2.0.2 - lws_finalize_write_http_header@Base 3.2.0 - lws_frame_is_binary@Base 1.2 - lws_get_child@Base 2.0.2 - lws_get_child_pending_on_writable@Base 2.4.1 - lws_get_close_length@Base 2.4.1 - lws_get_close_payload@Base 2.4.1 - lws_get_context@Base 1.6.0 - lws_get_count_threads@Base 1.7.0 - lws_get_effective_uid_gid@Base 3.2.0 - lws_get_fops@Base 1.6.0 - lws_get_library_version@Base 1.2 - lws_get_mimetype@Base 2.4.1 - lws_get_network_wsi@Base 2.4.1 - lws_get_opaque_parent_data@Base 2.4.1 - lws_get_opaque_user_data@Base 3.2.0 - lws_get_parent@Base 2.0.2 - lws_get_peer_addresses@Base 1.6.0 - lws_get_peer_simple@Base 2.4.1 - lws_get_peer_simple_fd@Base 4.0.0 - lws_get_peer_write_allowance@Base 1.4 - lws_get_protocol@Base 1.6.0 - lws_get_random@Base 1.6.0 - lws_get_reserved_bits@Base 1.6.0 - lws_get_socket_fd@Base 1.6.0 - lws_get_ssl@Base 2.4.1 - lws_get_tsi@Base 4.0.0 - lws_get_udp@Base 3.2.0 - lws_get_urlarg_by_name@Base 2.4.1 - lws_get_vhost@Base 2.4.1 - lws_get_vhost_by_name@Base 3.2.0 - lws_get_vhost_iface@Base 3.2.0 - lws_get_vhost_listen_port@Base 3.2.0 - lws_get_vhost_name@Base 3.2.0 - lws_get_vhost_port@Base 3.2.0 - lws_get_vhost_user@Base 3.2.0 - lws_h2_client_stream_long_poll_rxonly@Base 4.0.0 - lws_h2_get_peer_txcredit_estimate@Base 4.0.0 - lws_h2_update_peer_txcredit@Base 4.0.0 - lws_handle_POLLOUT_event@Base 2.4.1 - lws_hdr_copy@Base 1.2 - lws_hdr_copy_fragment@Base 1.6.0 - lws_hdr_custom_copy@Base 3.2.0 - lws_hdr_custom_length@Base 3.2.0 - lws_hdr_fragment_length@Base 1.7.0 - lws_hdr_total_length@Base 1.2 - lws_hex_to_byte_array@Base 3.2.0 - lws_http_basic_auth_gen@Base 4.0.0 - lws_http_client_http_response@Base 2.4.1 - lws_http_client_read@Base 2.0.2 - lws_http_compression_apply@Base 3.2.0 - lws_http_get_uri_and_method@Base 3.2.0 - lws_http_headers_detach@Base 3.2.0 - lws_http_is_redirected_to_get@Base 4.0.0 - lws_http_mark_sse@Base 3.2.0 - lws_http_redirect@Base 2.0.2 - lws_http_transaction_completed@Base 1.4 - lws_humanize@Base 3.2.0 - lws_init_vhost_client_ssl@Base 2.4.1 - lws_interface_to_sa@Base 1.7.0 - lws_is_cgi@Base 2.0.2 - lws_is_final_fragment@Base 1.6.0 - lws_is_first_fragment@Base 2.4.1 - lws_is_ssl@Base 1.4 - lws_json_purify@Base 2.4.1 - lws_json_purify_len@Base 4.0.0 - lws_libuv_static_refcount_add@Base 3.2.0 - lws_libuv_static_refcount_del@Base 3.2.0 - lws_list_ptr_insert@Base 3.2.0 - lws_now_secs@Base 2.4.1 - lws_now_usecs@Base 3.2.0 - lws_open@Base 3.2.0 - lws_parse_numeric_address@Base 4.0.0 - lws_parse_uri@Base 1.7.0 - lws_partial_buffered@Base 1.4 - lws_plat_read_file@Base 3.2.0 - lws_plat_recommended_rsa_bits@Base 3.2.0 - lws_plat_write_cert@Base 3.2.0 - lws_plat_write_file@Base 3.2.0 - lws_protocol_get@Base 2.0.2 - lws_protocol_init@Base 2.4.1 - lws_protocol_vh_priv_get@Base 2.0.2 - lws_protocol_vh_priv_zalloc@Base 2.0.2 - lws_pvo_get_str@Base 3.2.0 - lws_pvo_search@Base 3.2.0 - lws_raw_transaction_completed@Base 3.2.0 - lws_remaining_packet_payload@Base 1.6.0 - lws_retry_get_delay_ms@Base 3.2.0 - lws_retry_sul_schedule@Base 4.0.0 - lws_retry_sul_schedule_retry_wsi@Base 4.0.0 - lws_return_http_status@Base 1.6.0 - lws_ring_bump_head@Base 2.4.1 - lws_ring_consume@Base 2.4.1 - lws_ring_create@Base 2.4.1 - lws_ring_destroy@Base 2.4.1 - lws_ring_dump@Base 3.2.0 - lws_ring_get_count_free_elements@Base 2.4.1 - lws_ring_get_count_waiting_elements@Base 2.4.1 - lws_ring_get_element@Base 2.4.1 - lws_ring_get_oldest_tail@Base 2.4.1 - lws_ring_insert@Base 2.4.1 - lws_ring_next_linear_insert_range@Base 2.4.1 - lws_ring_update_oldest_tail@Base 2.4.1 - lws_rx_flow_allow_all_protocol@Base 1.6.0 - lws_rx_flow_control@Base 1.6.0 - lws_sa46_compare_ads@Base 4.0.0 - lws_sa46_parse_numeric_address@Base 4.0.0 - lws_sa46_write_numeric_address@Base 4.0.0 - lws_send_pipe_choked@Base 1.2 - lws_seq_check_wsi@Base 3.2.0 - lws_seq_create@Base 3.2.0 - lws_seq_destroy@Base 3.2.0 - lws_seq_from_user@Base 3.2.0 - lws_seq_get_context@Base 3.2.0 - lws_seq_name@Base 3.2.0 - lws_seq_queue_event@Base 3.2.0 - lws_seq_timeout_us@Base 3.2.0 - lws_seq_us_since_creation@Base 3.2.0 - lws_ser_ru16be@Base 4.0.0 - lws_ser_ru32be@Base 4.0.0 - lws_ser_ru64be@Base 4.0.0 - lws_ser_wu16be@Base 4.0.0 - lws_ser_wu32be@Base 4.0.0 - lws_ser_wu64be@Base 4.0.0 - lws_serve_http_file@Base 1.6.0 - lws_serve_http_file_fragment@Base 1.6.0 - lws_service@Base 1.6.0 - lws_service_adjust_timeout@Base 2.4.1 - lws_service_fd@Base 1.6.0 - lws_service_fd_tsi@Base 1.7.0 - lws_service_tsi@Base 1.7.0 - lws_set_allocator@Base 1.4 - lws_set_extension_option@Base 2.0.2 - lws_set_fops@Base 2.4.1 - lws_set_log_level@Base 1.2 - lws_set_opaque_parent_data@Base 2.4.1 - lws_set_opaque_user_data@Base 3.2.0 - lws_set_proxy@Base 1.6.0 - lws_set_timeout@Base 1.6.0 - lws_set_timer_usecs@Base 3.2.0 - lws_set_wsi_user@Base 2.4.1 - lws_snprintf@Base 2.0.3 - lws_spa_create@Base 2.4.1 - lws_spa_create_via_info@Base 3.2.0 - lws_spa_destroy@Base 2.4.1 - lws_spa_finalize@Base 2.4.1 - lws_spa_get_length@Base 2.4.1 - lws_spa_get_string@Base 2.4.1 - lws_spa_process@Base 2.4.1 - lws_sql_purify@Base 2.4.1 - lws_ssl_remove_wsi_from_buffered_list@Base 1.4 - lws_state_reg_deregister@Base 4.0.0 - lws_state_reg_notifier@Base 4.0.0 - lws_state_reg_notifier_list@Base 4.0.0 - lws_state_transition@Base 4.0.0 - lws_state_transition_steps@Base 4.0.0 - lws_strexp_expand@Base 4.0.0 - lws_strexp_init@Base 4.0.0 - lws_strexp_reset_out@Base 4.0.0 - lws_strncpy@Base 3.2.0 - lws_sul_schedule@Base 3.2.0 - lws_system_blob_destroy@Base 4.0.0 - lws_system_blob_direct_set@Base 4.0.0 - lws_system_blob_get@Base 4.0.0 - lws_system_blob_get_single_ptr@Base 4.0.0 - lws_system_blob_get_size@Base 4.0.0 - lws_system_blob_heap_append@Base 4.0.0 - lws_system_blob_heap_empty@Base 4.0.0 - lws_system_context_from_system_mgr@Base 4.0.0 - lws_system_get_blob@Base 4.0.0 - lws_system_get_ops@Base 4.0.0 - lws_system_get_state_manager@Base 4.0.0 - lws_timed_callback_vh_protocol@Base 3.2.0 - lws_timed_callback_vh_protocol_us@Base 3.2.0 - lws_timingsafe_bcmp@Base 3.2.0 - lws_tls_cert_updated@Base 3.2.0 - lws_tls_client_vhost_extra_cert_mem@Base 4.0.0 - lws_tls_peer_cert_info@Base 3.2.0 - lws_tls_vhost_cert_info@Base 3.2.0 - lws_token_to_string@Base 1.4 - lws_tokenize@Base 3.2.0 - lws_tokenize_cstr@Base 3.2.0 - lws_tokenize_init@Base 3.2.0 - lws_urldecode@Base 2.4.1 - lws_urlencode@Base 2.0.2 - lws_uv_getloop@Base 1.7.0 - lws_validity_confirmed@Base 4.0.0 - lws_vbi_decode@Base 4.0.0 - lws_vbi_encode@Base 4.0.0 - lws_vfs_file_open@Base 2.4.1 - lws_vfs_file_seek_end@Base 2.4.1 - lws_vfs_file_seek_set@Base 2.4.1 - lws_vfs_get_length@Base 2.4.1 - lws_vfs_get_mod_time@Base 2.4.1 - lws_vfs_tell@Base 2.4.1 - lws_vhost_destroy@Base 2.4.1 - lws_vhost_name_to_protocol@Base 2.4.1 - lws_vhost_user@Base 2.4.1 - lws_write@Base 1.6.0 - lws_write_numeric_address@Base 4.0.0 - lws_wsi_tx_credit@Base 4.0.0 - lws_wsi_user@Base 1.6.0 - lws_x509_create@Base 3.2.0 - lws_x509_destroy@Base 3.2.0 - lws_x509_info@Base 3.2.0 - lws_x509_parse_from_pem@Base 3.2.0 - lws_x509_verify@Base 3.2.0 - lwsac_align@Base 3.2.0 - lwsac_cached_file@Base 3.2.0 - lwsac_detach@Base 3.2.0 - lwsac_extend@Base 4.0.0 - lwsac_free@Base 3.2.0 - lwsac_get_next@Base 3.2.0 - lwsac_get_tail_pos@Base 3.2.0 - lwsac_info@Base 3.2.0 - lwsac_reference@Base 3.2.0 - lwsac_scan_extant@Base 4.0.0 - lwsac_sizeof@Base 3.2.0 - lwsac_total_alloc@Base 3.2.0 - lwsac_total_overhead@Base 4.0.0 - lwsac_unreference@Base 3.2.0 - lwsac_use@Base 3.2.0 - lwsac_use_backfill@Base 4.0.0 - lwsac_use_cached_file_detach@Base 3.2.0 - lwsac_use_cached_file_end@Base 3.2.0 - lwsac_use_cached_file_start@Base 3.2.0 - lwsac_use_zero@Base 3.2.0 - lwsl_emit_stderr@Base 1.3 - lwsl_emit_stderr_notimestamp@Base 3.2.0 - lwsl_emit_syslog@Base 1.2 - lwsl_hexdump@Base 1.2 - lwsl_hexdump_level@Base 2.4.1 - lwsl_timestamp@Base 2.0.2 - lwsl_visible@Base 2.4.1 - lwsws_get_config_globals@Base 3.2.0 - lwsws_get_config_vhosts@Base 3.2.0 diff -Nru libwebsockets-4.0.20/doc-assets/abstract-overview.svg libwebsockets-2.4.2/doc-assets/abstract-overview.svg --- libwebsockets-4.0.20/doc-assets/abstract-overview.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/abstract-overview.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,348 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/doc-assets/accept-flow-1.svg libwebsockets-2.4.2/doc-assets/accept-flow-1.svg --- libwebsockets-4.0.20/doc-assets/accept-flow-1.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/accept-flow-1.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - httpprocessing - Connection - Fallbackrole +protocol - - - - - Invalid Methodin "http header" - - Fallbackset and enabledon vhost? - - - - - Error - Yes - Yes - - - LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG - plain http port - - - - - This flow happensbefore any Host: headers.Indeed there are no Host:headers if the connectionis not actually http.Therefore it occurs on thefirst vhost that listenson the connection port. - - - diff -Nru libwebsockets-4.0.20/doc-assets/accept-flow-2.svg libwebsockets-2.4.2/doc-assets/accept-flow-2.svg --- libwebsockets-4.0.20/doc-assets/accept-flow-2.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/accept-flow-2.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - Yes - - - - - - - - - httpprocessing - Connection - Issue httpredirect tohttps:// - - - - - TLS headerlooks bad? - - Redirect httpto https enabled? - - - - Yes - Yes - - - - - Allow httpon https enabled? - - - - - - - - - - Error - Yes - Fallbackset and enabledon vhost? - - - LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS - LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER - LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG - - - Fallbackrole +protocol - TLS port - - - - - This flow happensbefore any vhost selectionusing SNI or Host: headers.Therefore it occurs on thefirst vhost that listenson the connection port. - - - diff -Nru libwebsockets-4.0.20/doc-assets/accept-flow-3.svg libwebsockets-2.4.2/doc-assets/accept-flow-3.svg --- libwebsockets-4.0.20/doc-assets/accept-flow-3.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/accept-flow-3.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - Connection - Specifiedrole +protocol - - - - LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG - raw-only port - - - diff -Nru libwebsockets-4.0.20/doc-assets/http-proxy-overview.svg libwebsockets-2.4.2/doc-assets/http-proxy-overview.svg --- libwebsockets-4.0.20/doc-assets/http-proxy-overview.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/http-proxy-overview.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - LWSvhost - listensocket - - unixsocket - localserverprocess - - - - - tcpsocket - remoteserverprocess - - tcpsocket - - - - - - - - - - - - - - - - - - - - - - - - - - h1/h2 - h1 proxy - - - - h1 proxy - chosenby URL - chosenby URL - lws http proxying overview - Same physical server - - - diff -Nru libwebsockets-4.0.20/doc-assets/lwsac.svg libwebsockets-2.4.2/doc-assets/lwsac.svg --- libwebsockets-4.0.20/doc-assets/lwsac.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/lwsac.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,131 +0,0 @@ - - - - - - - - - - - - - - - - - struct lwsac - allocated area - - - struct lwsac *head - nextheadcurr - - ofsalloc_size - - - - - - - - - - ptr aligned - - struct lwsac - allocated area - - - struct lwsac *head - nextheadcurr - - ofsalloc_size - - - - - - - lwsac_use area - - - - ptr aligned - alignment padding - - - lwsac_use area - ptr aligned - alignment padding - - - - - ptr aligned - - struct lwsac - allocated area - - - - - struct lwsac *head - nextheadcurr - - ofsalloc_size - - - - - - - lwsac_use area - - - - alignment padding - - - lwsac_use area - ptr aligned - alignment padding - - - - struct lwsac - allocated area - - nextheadcurr - ofsalloc_size - - - - - - - - - - - lwsac_use area - ptr aligned - alignment padding - - - - - NULL - - NULL - - NULL - - - - empty, generic lwsac - lwsac with 2 "uses" - lwsac with 2 "uses", 3rd requires a new one - - - diff -Nru libwebsockets-4.0.20/doc-assets/lws-crypto-overview.svg libwebsockets-2.4.2/doc-assets/lws-crypto-overview.svg --- libwebsockets-4.0.20/doc-assets/lws-crypto-overview.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/lws-crypto-overview.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - OpenSSL - mbedTLS - and derivitives - - genhash, genrsa, genaes, genec - JOSE, JWS, JWK, JWE - - TLS-library-specificand cipher-specifickeys using EVPor bignum - TLS library-independentmetadata +binary key elements - JWK JSON keycreation and parsing - - - - - libwebsockets - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/doc-assets/lws-detailed-latency-example.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/doc-assets/lws-detailed-latency-example.png differ diff -Nru libwebsockets-4.0.20/doc-assets/lws_dll.svg libwebsockets-2.4.2/doc-assets/lws_dll.svg --- libwebsockets-4.0.20/doc-assets/lws_dll.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/lws_dll.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NULL - - NULL - - - - - NULL - - - - - prev - - next - lws_dll - - - - - prev - - next - lws_dll2 - - - - - head - lws_dll2_owner - - - count - - - - prev - - - - next - lws_dll2 - NULL - - - - - - owner - - owner - - - - - - - - tail - - - - - - - - prev - - next - lws_dll - - - - - - next - lws_dll - prev - - - - - - (head) - (tail) - - - - diff -Nru libwebsockets-4.0.20/doc-assets/lws-fts.svg libwebsockets-2.4.2/doc-assets/lws-fts.svg --- libwebsockets-4.0.20/doc-assets/lws-fts.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/lws-fts.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OriginalTextfile - - OriginalTextfile - - OriginalTextfile - - IndexFile - - - - - - - - - SearchAction - Keyword - - Auto-comp-lete - Result lwsac - - - - - - - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/doc-assets/lws-overview.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/doc-assets/lws-overview.png differ diff -Nru libwebsockets-4.0.20/doc-assets/lws-relpol-1.svg libwebsockets-2.4.2/doc-assets/lws-relpol-1.svg --- libwebsockets-4.0.20/doc-assets/lws-relpol-1.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/lws-relpol-1.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - All new work is done only on master - - master - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/doc-assets/lws-relpol-2.svg libwebsockets-2.4.2/doc-assets/lws-relpol-2.svg --- libwebsockets-4.0.20/doc-assets/lws-relpol-2.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/lws-relpol-2.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - master - - - - - - - - - - - - v3.0.0 - v3.0-stable - When a release happens, masteris copied into a release-specific-stable branch and tagged - - - - diff -Nru libwebsockets-4.0.20/doc-assets/lws-relpol-3.svg libwebsockets-2.4.2/doc-assets/lws-relpol-3.svg --- libwebsockets-4.0.20/doc-assets/lws-relpol-3.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/lws-relpol-3.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - master - - - - - - - - - - - - v3.0.0 - v3.0-stable - - Work continues on master, and if a fix is madethat is also relevant to the last release,it is also backported on to the -stable branch - - - - - - - - New featuresand APIchanges arenot allowed forbackport - - - diff -Nru libwebsockets-4.0.20/doc-assets/lws-relpol-4.svg libwebsockets-2.4.2/doc-assets/lws-relpol-4.svg --- libwebsockets-4.0.20/doc-assets/lws-relpol-4.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/lws-relpol-4.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - master - - - - - - - - - - - v3.0.0 - v3.0-stable - - - - - - - - - - - - - - - - - - - - - - - - - v3.0.1 - Periodically new point releases onthe -stable branch are tagged out,with backports that didn'tgenerate any problems on-stable. - - diff -Nru libwebsockets-4.0.20/doc-assets/lws-relpol-5.svg libwebsockets-2.4.2/doc-assets/lws-relpol-5.svg --- libwebsockets-4.0.20/doc-assets/lws-relpol-5.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/lws-relpol-5.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - master - - - - - - - - - - - - - - - - - - - - - - - - - - - - backport - - - - - - - - - - - - - - - - backport - v3.0.0 - backport - - - - - - - - - - - v3.1.0 - - - - - - - - - - - v3.0.1 - - - - - - - - - - - v3.1.1 - - v3.0-stable - v3.1-stable - critical - - - Occasionally fixes are added on master that fix old, maybecritical bugs that affect more than one release. Thesemay be backported to serveral -stable trees. - - diff -Nru libwebsockets-4.0.20/doc-assets/lws_sequencer.svg libwebsockets-2.4.2/doc-assets/lws_sequencer.svg --- libwebsockets-4.0.20/doc-assets/lws_sequencer.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/lws_sequencer.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,955 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/doc-assets/lws-smp-example.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/doc-assets/lws-smp-example.png differ Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/doc-assets/lws-smp-ov.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/doc-assets/lws-smp-ov.png differ diff -Nru libwebsockets-4.0.20/doc-assets/lws_struct-overview.svg libwebsockets-2.4.2/doc-assets/lws_struct-overview.svg --- libwebsockets-4.0.20/doc-assets/lws_struct-overview.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/lws_struct-overview.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sqlite - - C structs - - - - JSON - transit - storage - processing - - - - - - - - - - - - - - - - - - - - lws_struct - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/doc-assets/ss-explain.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/doc-assets/ss-explain.png differ diff -Nru libwebsockets-4.0.20/doc-assets/threadpool-states.svg libwebsockets-2.4.2/doc-assets/threadpool-states.svg --- libwebsockets-4.0.20/doc-assets/threadpool-states.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/threadpool-states.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,153 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - queued - - running - - finished - - stopped - - stopping - - - - - - - - - threadpoolworkerthreadtakestask - workerproducesa bufferof output - - - nothingmoreto do - buffersent onand moreto do - problemssending - - - sync - - - free task - - - - sync - worker thread context - lws service thread context - - - wait untilthe lwsservicethreadknows thetask is done - - - - newwsi onmount - - protocol_HTTPcallback - - - enqueue threadpool task - - - - protocolWRITEABLE - cancel service - - - - - protocolWRITEABLE - cancel service - - - - - lws_threadpool_task_sync - lws_threadpool_task_status_wsi - move to"done queue"idlingworker thread - - - - wsi hasunexpect-edly gone - write thebuffer onthe wsi - acknowledgethe task hasended - Threadpool - - - - - - - synchronization with the lws service thread(syncs to the correct service thread for the wsi) - - diff -Nru libwebsockets-4.0.20/doc-assets/threadpool.svg libwebsockets-2.4.2/doc-assets/threadpool.svg --- libwebsockets-4.0.20/doc-assets/threadpool.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/doc-assets/threadpool.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Worker threadsTask queueDone queueSYNCLWSServiceThreadreapAll communication with tasks happensin lws service thread context, via theWRITEABLE callbacktasktasktasktasktasktasktasktasktaskwsi (may be detached)task function pointercleanup function pointeruser private pointerThreadpoolenqueue Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/doc-assets/wss2.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/doc-assets/wss2.png differ diff -Nru libwebsockets-4.0.20/.gitignore libwebsockets-2.4.2/.gitignore --- libwebsockets-4.0.20/.gitignore 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/.gitignore 2018-03-08 10:28:37.000000000 +0000 @@ -1,12 +1,4 @@ #Ignore build files -CMakeCache.txt -CMakeFiles -build -cmake_install.cmake -lws-minimal* -Makefile -.cproject -.project config.h config.log config.status @@ -46,19 +38,3 @@ libwebsockets.pc build/ *.swp -doc -/build2/ -/build3/ -/cov-int/ -/.vs/ -/build-mtls/ -/build-mingw64/ -/n9/ -/bb/ -/openssl3/ -/bb-linkit/ -/bq/ -/cros/ -/q/ -/b1/ -/destdir/ diff -Nru libwebsockets-4.0.20/include/libwebsockets/abstract/abstract.h libwebsockets-2.4.2/include/libwebsockets/abstract/abstract.h --- libwebsockets-4.0.20/include/libwebsockets/abstract/abstract.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/abstract/abstract.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,138 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/* - * These are used to optionally pass an array of index = C string, binary array, - * or ulong tokens to the abstract transport or protocol. For example if it's - * raw socket transport, then the DNS address to connect to and the port are - * passed using these when the client created and bound to the transport. - */ - -typedef struct lws_token_map { - union { - const char *value; - uint8_t *bvalue; - unsigned long lvalue; - } u; - short name_index; /* 0 here indicates end of array */ - short length_or_zero; -} lws_token_map_t; - -/* - * The indvidual protocols and transports define their own name_index-es which - * are meaningful to them. Define index 0 globally as the end of an array of - * them, and provide bases so user protocol and transport ones don't overlap. - */ - -enum { - LTMI_END_OF_ARRAY, - - LTMI_PROTOCOL_BASE = 2048, - - LTMI_TRANSPORT_BASE = 4096 -}; - -struct lws_abs_transport; -struct lws_abs_protocol; -typedef struct lws_abs lws_abs_t; - -LWS_VISIBLE LWS_EXTERN const lws_token_map_t * -lws_abs_get_token(const lws_token_map_t *token_map, short name_index); - -/* - * the combination of a protocol, transport, and token maps for each - */ - -typedef void lws_abs_transport_inst_t; -typedef void lws_abs_protocol_inst_t; - -/** - * lws_abstract_alloc() - allocate and configure an lws_abs_t - * - * \param vhost: the struct lws_vhost to bind to - * \param user: opaque user pointer - * \param abstract_path: "protocol.transport" names - * \param ap_tokens: tokens for protocol options - * \param at_tokens: tokens for transport - * \param seq: optional sequencer we should bind to, or NULL - * \param opaque_user_data: data given in sequencer callback, if any - * - * Returns an allocated lws_abs_t pointer set up with the other arguments. - * - * Doesn't create a connection instance, just allocates the lws_abs_t and - * sets it up with the arguments. - * - * Returns NULL is there's any problem. - */ -LWS_VISIBLE LWS_EXTERN lws_abs_t * -lws_abstract_alloc(struct lws_vhost *vhost, void *user, - const char *abstract_path, const lws_token_map_t *ap_tokens, - const lws_token_map_t *at_tokens, struct lws_sequencer *seq, - void *opaque_user_data); - -/** - * lws_abstract_free() - free an allocated lws_abs_t - * - * \param pabs: pointer to the lws_abs_t * to free - * - * Frees and sets the pointer to NULL. - */ - -LWS_VISIBLE LWS_EXTERN void -lws_abstract_free(lws_abs_t **pabs); - -/** - * lws_abs_bind_and_create_instance - use an abstract protocol and transport - * - * \param abs: the lws_abs_t describing the combination desired - * - * This instantiates an abstract protocol and abstract transport bound together. - * A single heap allocation is made for the combination and the protocol and - * transport creation ops are called on it. The ap_tokens and at_tokens - * are consulted by the creation ops to decide the details of the protocol and - * transport for the instance. - */ -LWS_VISIBLE LWS_EXTERN lws_abs_t * -lws_abs_bind_and_create_instance(const lws_abs_t *ai); - -/** - * lws_abs_destroy_instance() - destroys an instance - * - * \param ai: pointer to the ai pointer to destroy - * - * This is for destroying an instance created by - * lws_abs_bind_and_create_instance() above. - * - * Calls the protocol and transport destroy operations on the instance, then - * frees the combined allocation in one step. The pointer ai is set to NULL. - */ -LWS_VISIBLE LWS_EXTERN void -lws_abs_destroy_instance(lws_abs_t **ai); - -/* - * bring in all the protocols and transports definitions - */ - -#include -#include diff -Nru libwebsockets-4.0.20/include/libwebsockets/abstract/protocols/smtp.h libwebsockets-2.4.2/include/libwebsockets/abstract/protocols/smtp.h --- libwebsockets-4.0.20/include/libwebsockets/abstract/protocols/smtp.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/abstract/protocols/smtp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,115 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup smtp SMTP related functions - * ##SMTP related functions - * \ingroup lwsapi - * - * These apis let you communicate with a local SMTP server to send email from - * lws. It handles all the SMTP sequencing and protocol actions. - * - * Your system should have postfix, sendmail or another MTA listening on port - * 25 and able to send email using the "mail" commandline app. Usually distro - * MTAs are configured for this by default. - * - * You can either use the abstract protocol layer directly, or instead use the - * provided smtp sequencer... this takes care of creating the protocol - * connections, and provides and email queue and retry management. - */ -//@{ - -#if defined(LWS_WITH_SMTP) - -enum { - LTMI_PSMTP_V_HELO = LTMI_PROTOCOL_BASE, /* u.value */ - - LTMI_PSMTP_V_LWS_SMTP_EMAIL_T, /* u.value */ -}; - -enum { - LWS_SMTP_DISPOSITION_SENT, - LWS_SMTP_DISPOSITION_FAILED, - LWS_SMTP_DISPOSITION_FAILED_DESTROY -}; - -typedef struct lws_smtp_sequencer_args { - const char helo[32]; - struct lws_vhost *vhost; - time_t retry_interval; - time_t delivery_timeout; - size_t email_queue_max; - size_t max_content_size; -} lws_smtp_sequencer_args_t; - -typedef struct lws_smtp_sequencer lws_smtp_sequencer_t; -typedef struct lws_smtp_email lws_smtp_email_t; - -LWS_VISIBLE LWS_EXTERN lws_smtp_sequencer_t * -lws_smtp_sequencer_create(const lws_smtp_sequencer_args_t *args); - -LWS_VISIBLE LWS_EXTERN void -lws_smtp_sequencer_destroy(lws_smtp_sequencer_t *s); - -typedef int (*lws_smtp_cb_t)(void *e, void *d, int disp, const void *b, size_t l); -typedef struct lws_smtp_email lws_smtp_email_t; - -/** - * lws_smtpc_add_email() - Allocates and queues an email object - * - * \param s: smtp sequencer to queue on - * \param payload: the email payload string, with headers and terminating . - * \param payload_len: size in bytes of the payload string - * \param sender: the sender name and email - * \param recipient: the recipient name and email - * \param data: opaque user data returned in the done callback - * \param done: callback called when the email send succeeded or failed - * - * Allocates an email object and copies the payload, sender and recipient into - * it and initializes it. Returns NULL if OOM, otherwise the allocated email - * object. - * - * Because it copies the arguments into an allocated buffer, the original - * arguments can be safely destroyed after calling this. - * - * The done() callback must free the email object. It doesn't have to free any - * individual members. - */ -LWS_VISIBLE LWS_EXTERN int -lws_smtpc_add_email(lws_smtp_sequencer_t *s, const char *payload, - size_t payload_len, const char *sender, - const char *recipient, void *data, lws_smtp_cb_t done); - -/** - * lws_smtpc_free_email() - Add email to the list of ones being sent - * - * \param e: email to queue for sending on \p c - * - * Adds an email to the linked-list of emails to send - */ -LWS_VISIBLE LWS_EXTERN int -lws_smtpc_free_email(lws_smtp_email_t *e); - - -#endif -//@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/abstract/protocols.h libwebsockets-2.4.2/include/libwebsockets/abstract/protocols.h --- libwebsockets-4.0.20/include/libwebsockets/abstract/protocols.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/abstract/protocols.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/* - * Information about how this protocol handles multiple use of connections. - * - * .flags of 0 indicates each connection must start with a fresh transport. - * - * Flags can be used to indicate the protocol itself supports different - * kinds of multiple use. However the actual use or not of these may depend on - * negotiation with the remote peer. - * - * LWS_AP_FLAG_PIPELINE_TRANSACTIONS: other instances can be queued on one - * with an existing connection and get a - * chance to "hot take over" the existing - * transport in turn, like h1 keepalive - * pipelining - * - * LWS_AP_FLAG_MUXABLE_STREAM: an existing connection can absorb more child - * connections and mux them as separate child - * streams ongoing, like h2 - */ - -enum { - LWS_AP_FLAG_PIPELINE_TRANSACTIONS = (1 << 0), - LWS_AP_FLAG_MUXABLE_STREAM = (1 << 1), -}; - -typedef struct lws_abs_protocol { - const char *name; - int alloc; - int flags; - - int (*create)(const struct lws_abs *ai); - void (*destroy)(lws_abs_protocol_inst_t **d); - int (*compare)(lws_abs_t *abs1, lws_abs_t *abs2); - - /* events the transport invokes (handled by abstract protocol) */ - - int (*accept)(lws_abs_protocol_inst_t *d); - int (*rx)(lws_abs_protocol_inst_t *d, const uint8_t *b, size_t l); - int (*writeable)(lws_abs_protocol_inst_t *d, size_t budget); - int (*closed)(lws_abs_protocol_inst_t *d); - int (*heartbeat)(lws_abs_protocol_inst_t *d); - - /* as parent, we get a notification a new child / queue entry - * bound to us... this is the parent lws_abs_t as arg */ - int (*child_bind)(lws_abs_t *abs); -} lws_abs_protocol_t; - -/** - * lws_abs_protocol_get_by_name() - returns a pointer to the named protocol ops - * - * \param name: the name of the abstract protocol - * - * Returns a pointer to the named protocol ops struct if available, otherwise - * NULL. - */ -LWS_VISIBLE LWS_EXTERN const lws_abs_protocol_t * -lws_abs_protocol_get_by_name(const char *name); - -/* - * bring in public api pieces from protocols - */ - -#include - diff -Nru libwebsockets-4.0.20/include/libwebsockets/abstract/transports/raw-skt.h libwebsockets-2.4.2/include/libwebsockets/abstract/transports/raw-skt.h --- libwebsockets-4.0.20/include/libwebsockets/abstract/transports/raw-skt.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/abstract/transports/raw-skt.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -enum { - LTMI_PEER_V_DNS_ADDRESS = LTMI_TRANSPORT_BASE, /* u.value */ - LTMI_PEER_LV_PORT, /* u.lvalue */ - LTMI_PEER_LV_TLS_FLAGS, /* u.lvalue */ -}; diff -Nru libwebsockets-4.0.20/include/libwebsockets/abstract/transports/unit-test.h libwebsockets-2.4.2/include/libwebsockets/abstract/transports/unit-test.h --- libwebsockets-4.0.20/include/libwebsockets/abstract/transports/unit-test.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/abstract/transports/unit-test.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 an abstract transport useful for unit testing abstract protocols. - * - * Instead of passing data anywhere, you give the transport a list of packets - * to deliver and packets you expect back from the abstract protocol it's - * bound to. - */ - -enum { - LWS_AUT_EXPECT_TEST_END = (1 << 0), - LWS_AUT_EXPECT_LOCAL_CLOSE = (1 << 1), - LWS_AUT_EXPECT_DO_REMOTE_CLOSE = (1 << 2), - LWS_AUT_EXPECT_TX /* expect this as tx from protocol */ = (1 << 3), - LWS_AUT_EXPECT_RX /* present this as rx to protocol */ = (1 << 4), - LWS_AUT_EXPECT_SHOULD_FAIL = (1 << 5), - LWS_AUT_EXPECT_SHOULD_TIMEOUT = (1 << 6), -}; - -typedef enum { - LPE_CONTINUE, - LPE_SUCCEEDED, - LPE_FAILED, - LPE_FAILED_UNEXPECTED_TIMEOUT, - LPE_FAILED_UNEXPECTED_PASS, - LPE_FAILED_UNEXPECTED_CLOSE, - LPE_SKIPPED, - LPE_CLOSING -} lws_unit_test_packet_disposition; - -typedef int (*lws_unit_test_packet_test_cb)(const void *cb_user, int disposition); -typedef int (*lws_unit_test_packet_cb)(lws_abs_t *instance); - -/* each step in the unit test */ - -typedef struct lws_unit_test_packet { - void *buffer; - lws_unit_test_packet_cb pre; - size_t len; - - uint32_t flags; -} lws_unit_test_packet_t; - -/* each unit test */ - -typedef struct lws_unit_test { - const char * name; /* NULL indicates end of test array */ - lws_unit_test_packet_t * expect_array; - int max_secs; -} lws_unit_test_t; - -enum { - LTMI_PEER_V_EXPECT_TEST = LTMI_TRANSPORT_BASE, /* u.value */ - LTMI_PEER_V_EXPECT_RESULT_CB, /* u.value */ - LTMI_PEER_V_EXPECT_RESULT_CB_ARG, /* u.value */ -}; - -LWS_VISIBLE LWS_EXTERN const char * -lws_unit_test_result_name(int in); - diff -Nru libwebsockets-4.0.20/include/libwebsockets/abstract/transports.h libwebsockets-2.4.2/include/libwebsockets/abstract/transports.h --- libwebsockets-4.0.20/include/libwebsockets/abstract/transports.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/abstract/transports.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/* - * Abstract transport ops - */ - -typedef struct lws_abs_transport { - const char *name; - int alloc; - - int (*create)(lws_abs_t *abs); - void (*destroy)(lws_abs_transport_inst_t **d); - - /* check if the transport settings for these connections are the same */ - int (*compare)(lws_abs_t *abs1, lws_abs_t *abs2); - - /* events the abstract protocol invokes (handled by transport) */ - - int (*tx)(lws_abs_transport_inst_t *d, uint8_t *buf, size_t len); - int (*client_conn)(const lws_abs_t *abs); - int (*close)(lws_abs_transport_inst_t *d); - int (*ask_for_writeable)(lws_abs_transport_inst_t *d); - int (*set_timeout)(lws_abs_transport_inst_t *d, int reason, int secs); - int (*state)(lws_abs_transport_inst_t *d); -} lws_abs_transport_t; - -/** - * lws_abs_protocol_get_by_name() - returns a pointer to the named protocol ops - * - * \param name: the name of the abstract protocol - * - * Returns a pointer to the named protocol ops struct if available, otherwise - * NULL. - */ -LWS_VISIBLE LWS_EXTERN const lws_abs_transport_t * -lws_abs_transport_get_by_name(const char *name); - -/* - * bring in public api pieces from transports - */ - -#include -#include diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-adopt.h libwebsockets-2.4.2/include/libwebsockets/lws-adopt.h --- libwebsockets-4.0.20/include/libwebsockets/lws-adopt.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-adopt.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,233 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup sock-adopt Socket adoption helpers - * ##Socket adoption helpers - * - * When integrating with an external app with its own event loop, these can - * be used to accept connections from someone else's listening socket. - * - * When using lws own event loop, these are not needed. - */ -///@{ - -/** - * lws_adopt_socket() - adopt foreign socket as if listen socket accepted it - * for the default vhost of context. - * - * \param context: lws context - * \param accept_fd: fd of already-accepted socket to adopt - * - * Either returns new wsi bound to accept_fd, or closes accept_fd and - * returns NULL, having cleaned up any new wsi pieces. - * - * LWS adopts the socket in http serving mode, it's ready to accept an upgrade - * to ws or just serve http. - */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd); -/** - * lws_adopt_socket_vhost() - adopt foreign socket as if listen socket accepted - * it for vhost - * - * \param vh: lws vhost - * \param accept_fd: fd of already-accepted socket to adopt - * - * Either returns new wsi bound to accept_fd, or closes accept_fd and - * returns NULL, having cleaned up any new wsi pieces. - * - * LWS adopts the socket in http serving mode, it's ready to accept an upgrade - * to ws or just serve http. - */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); - -typedef enum { - LWS_ADOPT_RAW_FILE_DESC = 0, /* convenience constant */ - LWS_ADOPT_HTTP = 1, /* flag: absent implies RAW */ - LWS_ADOPT_SOCKET = 2, /* flag: absent implies file descr */ - LWS_ADOPT_ALLOW_SSL = 4, /* flag: if set requires LWS_ADOPT_SOCKET */ - LWS_ADOPT_FLAG_UDP = 16, /* flag: socket is UDP */ - LWS_ADOPT_FLAG_RAW_PROXY = 32, /* flag: raw proxy */ - - LWS_ADOPT_RAW_SOCKET_UDP = LWS_ADOPT_SOCKET | LWS_ADOPT_FLAG_UDP, -} lws_adoption_type; - -typedef union { - lws_sockfd_type sockfd; - lws_filefd_type filefd; -} lws_sock_file_fd_type; - -#if defined(LWS_WITH_UDP) -struct lws_udp { - struct sockaddr sa; - socklen_t salen; - - struct sockaddr sa_pending; - socklen_t salen_pending; -}; -#endif - -/** -* lws_adopt_descriptor_vhost() - adopt foreign socket or file descriptor -* if socket descriptor, should already have been accepted from listen socket -* -* \param vh: lws vhost -* \param type: OR-ed combinations of lws_adoption_type flags -* \param fd: union with either .sockfd or .filefd set -* \param vh_prot_name: NULL or vh protocol name to bind raw connection to -* \param parent: NULL or struct lws to attach new_wsi to as a child -* -* Either returns new wsi bound to accept_fd, or closes accept_fd and -* returns NULL, having cleaned up any new wsi pieces. -* -* If LWS_ADOPT_SOCKET is set, LWS adopts the socket in http serving mode, it's -* ready to accept an upgrade to ws or just serve http. -* -* parent may be NULL, if given it should be an existing wsi that will become the -* parent of the new wsi created by this call. -*/ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, - lws_sock_file_fd_type fd, const char *vh_prot_name, - struct lws *parent); - -typedef struct lws_adopt_desc { - struct lws_vhost *vh; /**< vhost the wsi should belong to */ - lws_adoption_type type; /**< OR-ed combinations of lws_adoption_type flags */ - lws_sock_file_fd_type fd; /**< union with either .sockfd or .filefd set */ - const char *vh_prot_name; /**< NULL or vh protocol name to bind raw connection to */ - struct lws *parent; /**< NULL or struct lws to attach new_wsi to as a child */ - void *opaque; /**< opaque pointer to set on created wsi */ -} lws_adopt_desc_t; - -/** -* lws_adopt_descriptor_vhost_via_info() - adopt foreign socket or file descriptor -* if socket descriptor, should already have been accepted from listen socket -* -* \param info: the struct containing the parameters -* -* - vh: lws vhost -* - type: OR-ed combinations of lws_adoption_type flags -* - fd: union with either .sockfd or .filefd set -* - vh_prot_name: NULL or vh protocol name to bind raw connection to -* - parent: NULL or struct lws to attach new_wsi to as a child -* - opaque: opaque pointer to set on created wsi -* -* Either returns new wsi bound to accept_fd, or closes accept_fd and -* returns NULL, having cleaned up any new wsi pieces. -* -* If LWS_ADOPT_SOCKET is set, LWS adopts the socket in http serving mode, it's -* ready to accept an upgrade to ws or just serve http. -* -* parent may be NULL, if given it should be an existing wsi that will become the -* parent of the new wsi created by this call. -*/ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_adopt_descriptor_vhost_via_info(const lws_adopt_desc_t *info); - -/** - * lws_adopt_socket_readbuf() - adopt foreign socket and first rx as if listen socket accepted it - * for the default vhost of context. - * \param context: lws context - * \param accept_fd: fd of already-accepted socket to adopt - * \param readbuf: NULL or pointer to data that must be drained before reading from - * accept_fd - * \param len: The length of the data held at \p readbuf - * - * Either returns new wsi bound to accept_fd, or closes accept_fd and - * returns NULL, having cleaned up any new wsi pieces. - * - * LWS adopts the socket in http serving mode, it's ready to accept an upgrade - * to ws or just serve http. - * - * If your external code did not already read from the socket, you can use - * lws_adopt_socket() instead. - * - * This api is guaranteed to use the data at \p readbuf first, before reading from - * the socket. - * - * \p readbuf is limited to the size of the ah rx buf, currently 2048 bytes. - */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, - const char *readbuf, size_t len); -/** - * lws_adopt_socket_vhost_readbuf() - adopt foreign socket and first rx as if listen socket - * accepted it for vhost. - * \param vhost: lws vhost - * \param accept_fd: fd of already-accepted socket to adopt - * \param readbuf: NULL or pointer to data that must be drained before reading from accept_fd - * \param len: The length of the data held at \p readbuf - * - * Either returns new wsi bound to accept_fd, or closes accept_fd and - * returns NULL, having cleaned up any new wsi pieces. - * - * LWS adopts the socket in http serving mode, it's ready to accept an upgrade - * to ws or just serve http. - * - * If your external code did not already read from the socket, you can use - * lws_adopt_socket() instead. - * - * This api is guaranteed to use the data at \p readbuf first, before reading from - * the socket. - * - * \p readbuf is limited to the size of the ah rx buf, currently 2048 bytes. - */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, - lws_sockfd_type accept_fd, const char *readbuf, - size_t len); - -#define LWS_CAUDP_BIND (1 << 0) -#define LWS_CAUDP_BROADCAST (1 << 1) -#define LWS_CAUDP_PF_PACKET (1 << 2) - -#if defined(LWS_WITH_UDP) -/** - * lws_create_adopt_udp() - create, bind and adopt a UDP socket - * - * \param vhost: lws vhost - * \param ads: NULL or address to do dns lookup on - * \param port: UDP port to bind to, -1 means unbound - * \param flags: 0 or LWS_CAUDP_NO_BIND - * \param protocol_name: Name of protocol on vhost to bind wsi to - * \param ifname: NULL, for network interface name to bind socket to - * \param parent_wsi: NULL or parent wsi new wsi will be a child of - * \param opaque: set created wsi opaque ptr to this - * \param retry_policy: NULL for vhost default policy else wsi specific policy - * - * Either returns new wsi bound to accept_fd, or closes accept_fd and - * returns NULL, having cleaned up any new wsi pieces. - * */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port, - int flags, const char *protocol_name, const char *ifname, - struct lws *parent_wsi, void *opaque, - const lws_retry_bo_t *retry_policy); -#endif - - - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-async-dns.h libwebsockets-2.4.2/include/libwebsockets/lws-async-dns.h --- libwebsockets-4.0.20/include/libwebsockets/lws-async-dns.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-async-dns.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_UDP) - -typedef enum dns_query_type { - LWS_ADNS_RECORD_A = 0x01, - LWS_ADNS_RECORD_CNAME = 0x05, - LWS_ADNS_RECORD_MX = 0x0f, - LWS_ADNS_RECORD_AAAA = 0x1c, -} adns_query_type_t; - -typedef enum { - LADNS_RET_FAILED_WSI_CLOSED = -4, - LADNS_RET_NXDOMAIN = -3, - LADNS_RET_TIMEDOUT = -2, - LADNS_RET_FAILED = -1, - LADNS_RET_FOUND, - LADNS_RET_CONTINUING -} lws_async_dns_retcode_t; - -typedef struct lws * (*lws_async_dns_cb_t)(struct lws *wsi, const char *ads, - const struct addrinfo *result, int n, - void *opaque); - -/** - * lws_async_dns_query() - perform a dns lookup using async dns - * - * \param context: the lws_context - * \param tsi: thread service index (usually 0) - * \param name: DNS name to look up - * \param qtype: type of query (A, AAAA etc) - * \param cb: query completion callback - * \param wsi: wsi if the query is related to one - * - * Starts an asynchronous DNS lookup, on completion the \p cb callback will - * be called. - * - * The reference count on the cached object is incremented for every callback - * that was called with the cached addrinfo results. - * - * The cached object can't be evicted until the reference count reaches zero... - * use lws_async_dns_freeaddrinfo() to indicate you're finsihed with the - * results for each callback that happened with them. - */ -LWS_VISIBLE LWS_EXTERN lws_async_dns_retcode_t -lws_async_dns_query(struct lws_context *context, int tsi, const char *name, - adns_query_type_t qtype, lws_async_dns_cb_t cb, - struct lws *wsi, void *opaque); - -/** - * lws_async_dns_freeaddrinfo() - decrement refcount on cached addrinfo results - * - * \param pai: a pointert to a pointer to first addrinfo returned as result in the callback - * - * Decrements the cache object's reference count. When it reaches zero, the - * cached object may be reaped subject to LRU rules. - * - * The pointer to the first addrinfo give in the argument is set to NULL. - */ -LWS_VISIBLE LWS_EXTERN void -lws_async_dns_freeaddrinfo(const struct addrinfo **ai); - -#endif diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-callbacks.h libwebsockets-2.4.2/include/libwebsockets/lws-callbacks.h --- libwebsockets-4.0.20/include/libwebsockets/lws-callbacks.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-callbacks.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,888 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup usercb User Callback - * - * ##User protocol callback - * - * The protocol callback is the primary way lws interacts with - * user code. For one of a list of a few dozen reasons the callback gets - * called at some event to be handled. - * - * All of the events can be ignored, returning 0 is taken as "OK" and returning - * nonzero in most cases indicates that the connection should be closed. - */ -///@{ - -struct lws_ssl_info { - int where; - int ret; -}; - -enum lws_cert_update_state { - LWS_CUS_IDLE, - LWS_CUS_STARTING, - LWS_CUS_SUCCESS, - LWS_CUS_FAILED, - - LWS_CUS_CREATE_KEYS, - LWS_CUS_REG, - LWS_CUS_AUTH, - LWS_CUS_CHALLENGE, - LWS_CUS_CREATE_REQ, - LWS_CUS_REQ, - LWS_CUS_CONFIRM, - LWS_CUS_ISSUE, -}; - -enum { - LWS_TLS_REQ_ELEMENT_COUNTRY, - LWS_TLS_REQ_ELEMENT_STATE, - LWS_TLS_REQ_ELEMENT_LOCALITY, - LWS_TLS_REQ_ELEMENT_ORGANIZATION, - LWS_TLS_REQ_ELEMENT_COMMON_NAME, - LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME, - LWS_TLS_REQ_ELEMENT_EMAIL, - - LWS_TLS_REQ_ELEMENT_COUNT, - - LWS_TLS_SET_DIR_URL = LWS_TLS_REQ_ELEMENT_COUNT, - LWS_TLS_SET_AUTH_PATH, - LWS_TLS_SET_CERT_PATH, - LWS_TLS_SET_KEY_PATH, - - LWS_TLS_TOTAL_COUNT -}; - -struct lws_acme_cert_aging_args { - struct lws_vhost *vh; - const char *element_overrides[LWS_TLS_TOTAL_COUNT]; /* NULL = use pvo */ -}; - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -/** enum lws_callback_reasons - reason you're getting a protocol callback */ -enum lws_callback_reasons { - - /* --------------------------------------------------------------------- - * ----- Callbacks related to wsi and protocol binding lifecycle ----- - */ - - LWS_CALLBACK_PROTOCOL_INIT = 27, - /**< One-time call per protocol, per-vhost using it, so it can - * do initial setup / allocations etc */ - - LWS_CALLBACK_PROTOCOL_DESTROY = 28, - /**< One-time call per protocol, per-vhost using it, indicating - * this protocol won't get used at all after this callback, the - * vhost is getting destroyed. Take the opportunity to - * deallocate everything that was allocated by the protocol. */ - - LWS_CALLBACK_WSI_CREATE = 29, - /**< outermost (earliest) wsi create notification to protocols[0] */ - - LWS_CALLBACK_WSI_DESTROY = 30, - /**< outermost (latest) wsi destroy notification to protocols[0] */ - - LWS_CALLBACK_WSI_TX_CREDIT_GET = 103, - /**< manually-managed connection received TX credit (len is int32) */ - - - /* --------------------------------------------------------------------- - * ----- Callbacks related to Server TLS ----- - */ - - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS = 21, - /**< if configured for - * including OpenSSL support, this callback allows your user code - * to perform extra SSL_CTX_load_verify_locations() or similar - * calls to direct OpenSSL where to find certificates the client - * can use to confirm the remote server identity. user is the - * OpenSSL SSL_CTX* */ - - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22, - /**< if configured for - * including OpenSSL support, this callback allows your user code - * to load extra certificates into the server which allow it to - * verify the validity of certificates returned by clients. user - * is the server's OpenSSL SSL_CTX* and in is the lws_vhost */ - - LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23, - /**< if the libwebsockets vhost was created with the option - * LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT, then this - * callback is generated during OpenSSL verification of the cert - * sent from the client. It is sent to protocol[0] callback as - * no protocol has been negotiated on the connection yet. - * Notice that the libwebsockets context and wsi are both NULL - * during this callback. See - * http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html - * to understand more detail about the OpenSSL callback that - * generates this libwebsockets callback and the meanings of the - * arguments passed. In this callback, user is the x509_ctx, - * in is the ssl pointer and len is preverify_ok - * Notice that this callback maintains libwebsocket return - * conventions, return 0 to mean the cert is OK or 1 to fail it. - * This also means that if you don't handle this callback then - * the default callback action of returning 0 allows the client - * certificates. */ - - LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY = 37, - /**< if configured for including OpenSSL support but no private key - * file has been specified (ssl_private_key_filepath is NULL), this is - * called to allow the user to set the private key directly via - * libopenssl and perform further operations if required; this might be - * useful in situations where the private key is not directly accessible - * by the OS, for example if it is stored on a smartcard. - * user is the server's OpenSSL SSL_CTX* */ - - LWS_CALLBACK_SSL_INFO = 67, - /**< SSL connections only. An event you registered an - * interest in at the vhost has occurred on a connection - * using the vhost. in is a pointer to a - * struct lws_ssl_info containing information about the - * event*/ - - /* --------------------------------------------------------------------- - * ----- Callbacks related to Client TLS ----- - */ - - LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION = 58, - /**< Similar to LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION - * this callback is called during OpenSSL verification of the cert - * sent from the server to the client. It is sent to protocol[0] - * callback as no protocol has been negotiated on the connection yet. - * Notice that the wsi is set because lws_client_connect_via_info was - * successful. - * - * See http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html - * to understand more detail about the OpenSSL callback that - * generates this libwebsockets callback and the meanings of the - * arguments passed. In this callback, user is the x509_ctx, - * in is the ssl pointer and len is preverify_ok. - * - * THIS IS NOT RECOMMENDED BUT if a cert validation error shall be - * overruled and cert shall be accepted as ok, - * X509_STORE_CTX_set_error((X509_STORE_CTX*)user, X509_V_OK); must be - * called and return value must be 0 to mean the cert is OK; - * returning 1 will fail the cert in any case. - * - * This also means that if you don't handle this callback then - * the default callback action of returning 0 will not accept the - * certificate in case of a validation error decided by the SSL lib. - * - * This is expected and secure behaviour when validating certificates. - * - * Note: LCCSCF_ALLOW_SELFSIGNED and - * LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK still work without this - * callback being implemented. - */ - - /* --------------------------------------------------------------------- - * ----- Callbacks related to HTTP Server ----- - */ - - LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED = 19, - /**< A new client has been accepted by the ws server. This - * callback allows setting any relevant property to it. Because this - * happens immediately after the instantiation of a new client, - * there's no websocket protocol selected yet so this callback is - * issued only to protocol 0. Only wsi is defined, pointing to the - * new client, and the return value is ignored. */ - - LWS_CALLBACK_HTTP = 12, - /**< an http request has come from a client that is not - * asking to upgrade the connection to a websocket - * one. This is a chance to serve http content, - * for example, to send a script to the client - * which will then open the websockets connection. - * in points to the URI path requested and - * lws_serve_http_file() makes it very - * simple to send back a file to the client. - * Normally after sending the file you are done - * with the http connection, since the rest of the - * activity will come by websockets from the script - * that was delivered by http, so you will want to - * return 1; to close and free up the connection. */ - - LWS_CALLBACK_HTTP_BODY = 13, - /**< the next len bytes data from the http - * request body HTTP connection is now available in in. */ - - LWS_CALLBACK_HTTP_BODY_COMPLETION = 14, - /**< the expected amount of http request body has been delivered */ - - LWS_CALLBACK_HTTP_FILE_COMPLETION = 15, - /**< a file requested to be sent down http link has completed. */ - - LWS_CALLBACK_HTTP_WRITEABLE = 16, - /**< you can write more down the http protocol link now. */ - - LWS_CALLBACK_CLOSED_HTTP = 5, - /**< when a HTTP (non-websocket) session ends */ - - LWS_CALLBACK_FILTER_HTTP_CONNECTION = 18, - /**< called when the request has - * been received and parsed from the client, but the response is - * not sent yet. Return non-zero to disallow the connection. - * user is a pointer to the connection user space allocation, - * in is the URI, eg, "/" - * In your handler you can use the public APIs - * lws_hdr_total_length() / lws_hdr_copy() to access all of the - * headers using the header enums lws_token_indexes from - * libwebsockets.h to check for and read the supported header - * presence and content before deciding to allow the http - * connection to proceed or to kill the connection. */ - - LWS_CALLBACK_ADD_HEADERS = 53, - /**< This gives your user code a chance to add headers to a server - * transaction bound to your protocol. `in` points to a - * `struct lws_process_html_args` describing a buffer and length - * you can add headers into using the normal lws apis. - * - * (see LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to add headers to - * a client transaction) - * - * Only `args->p` and `args->len` are valid, and `args->p` should - * be moved on by the amount of bytes written, if any. Eg - * - * case LWS_CALLBACK_ADD_HEADERS: - * - * struct lws_process_html_args *args = - * (struct lws_process_html_args *)in; - * - * if (lws_add_http_header_by_name(wsi, - * (unsigned char *)"set-cookie:", - * (unsigned char *)cookie, cookie_len, - * (unsigned char **)&args->p, - * (unsigned char *)args->p + args->max_len)) - * return 1; - * - * break; - */ - - LWS_CALLBACK_VERIFY_BASIC_AUTHORIZATION = 102, - /**< This gives the user code a chance to accept or reject credentials - * provided HTTP to basic authorization. It will only be called if the - * http mount's authentication_mode is set to LWSAUTHM_BASIC_AUTH_CALLBACK - * `in` points to a credential string of the form `username:password` If - * the callback returns zero (the default if unhandled), then the - * transaction ends with HTTP_STATUS_UNAUTHORIZED, otherwise the request - * will be processed */ - - LWS_CALLBACK_CHECK_ACCESS_RIGHTS = 51, - /**< This gives the user code a chance to forbid an http access. - * `in` points to a `struct lws_process_html_args`, which - * describes the URL, and a bit mask describing the type of - * authentication required. If the callback returns nonzero, - * the transaction ends with HTTP_STATUS_UNAUTHORIZED. */ - - LWS_CALLBACK_PROCESS_HTML = 52, - /**< This gives your user code a chance to mangle outgoing - * HTML. `in` points to a `struct lws_process_html_args` - * which describes the buffer containing outgoing HTML. - * The buffer may grow up to `.max_len` (currently +128 - * bytes per buffer). - */ - - LWS_CALLBACK_HTTP_BIND_PROTOCOL = 49, - /**< By default, all HTTP handling is done in protocols[0]. - * However you can bind different protocols (by name) to - * different parts of the URL space using callback mounts. This - * callback occurs in the new protocol when a wsi is bound - * to that protocol. Any protocol allocation related to the - * http transaction processing should be created then. - * These specific callbacks are necessary because with HTTP/1.1, - * a single connection may perform at series of different - * transactions at different URLs, thus the lifetime of the - * protocol bind is just for one transaction, not connection. */ - - LWS_CALLBACK_HTTP_DROP_PROTOCOL = 50, - /**< This is called when a transaction is unbound from a protocol. - * It indicates the connection completed its transaction and may - * do something different now. Any protocol allocation related - * to the http transaction processing should be destroyed. */ - - LWS_CALLBACK_HTTP_CONFIRM_UPGRADE = 86, - /**< This is your chance to reject an HTTP upgrade action. The - * name of the protocol being upgraded to is in 'in', and the ah - * is still bound to the wsi, so you can look at the headers. - * - * The default of returning 0 (ie, also if not handled) means the - * upgrade may proceed. Return <0 to just hang up the connection, - * or >0 if you have rejected the connection by returning http headers - * and response code yourself. - * - * There is no need for you to call transaction_completed() as the - * caller will take care of it when it sees you returned >0. - */ - - /* --------------------------------------------------------------------- - * ----- Callbacks related to HTTP Client ----- - */ - - LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44, - /**< The HTTP client connection has succeeded, and is now - * connected to the server */ - - LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45, - /**< The HTTP client connection is closing */ - - LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48, - /**< This is generated by lws_http_client_read() used to drain - * incoming data. In the case the incoming data was chunked, it will - * be split into multiple smaller callbacks for each chunk block, - * removing the chunk headers. If not chunked, it will appear all in - * one callback. */ - - LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46, - /**< This indicates data was received on the HTTP client connection. It - * does NOT actually drain or provide the data, so if you are doing - * http client, you MUST handle this and call lws_http_client_read(). - * Failure to deal with it as in the minimal examples may cause spinning - * around the event loop as it's continuously signalled the same data - * is available for read. The related minimal examples show how to - * handle it. - * - * It's possible to defer calling lws_http_client_read() if you use - * rx flow control to stop further rx handling on the connection until - * you did deal with it. But normally you would call it in the handler. - * - * lws_http_client_read() strips any chunked framing and calls back - * with only payload data to LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ. The - * chunking is the reason this is not just all done in one callback for - * http. - */ - LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47, - /**< The client transaction completed... at the moment this - * is the same as closing since transaction pipelining on - * client side is not yet supported. */ - - LWS_CALLBACK_CLIENT_HTTP_WRITEABLE = 57, - /**< when doing an HTTP type client connection, you can call - * lws_client_http_body_pending(wsi, 1) from - * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to get these callbacks - * sending the HTTP headers. - * - * From this callback, when you have sent everything, you should let - * lws know by calling lws_client_http_body_pending(wsi, 0) - */ - - LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL = 85, - LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL = 76, - - /* --------------------------------------------------------------------- - * ----- Callbacks related to Websocket Server ----- - */ - - LWS_CALLBACK_ESTABLISHED = 0, - /**< (VH) after the server completes a handshake with an incoming - * client. If you built the library with ssl support, in is a - * pointer to the ssl struct associated with the connection or NULL. - * - * b0 of len is set if the connection was made using ws-over-h2 - */ - - LWS_CALLBACK_CLOSED = 4, - /**< when the websocket session ends */ - - LWS_CALLBACK_SERVER_WRITEABLE = 11, - /**< See LWS_CALLBACK_CLIENT_WRITEABLE */ - - LWS_CALLBACK_RECEIVE = 6, - /**< data has appeared for this server endpoint from a - * remote client, it can be found at *in and is - * len bytes long */ - - LWS_CALLBACK_RECEIVE_PONG = 7, - /**< servers receive PONG packets with this callback reason */ - - LWS_CALLBACK_WS_PEER_INITIATED_CLOSE = 38, - /**< The peer has sent an unsolicited Close WS packet. in and - * len are the optional close code (first 2 bytes, network - * order) and the optional additional information which is not - * defined in the standard, and may be a string or non human-readable - * data. - * If you return 0 lws will echo the close and then close the - * connection. If you return nonzero lws will just close the - * connection. */ - - LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION = 20, - /**< called when the handshake has - * been received and parsed from the client, but the response is - * not sent yet. Return non-zero to disallow the connection. - * user is a pointer to the connection user space allocation, - * in is the requested protocol name - * In your handler you can use the public APIs - * lws_hdr_total_length() / lws_hdr_copy() to access all of the - * headers using the header enums lws_token_indexes from - * libwebsockets.h to check for and read the supported header - * presence and content before deciding to allow the handshake - * to proceed or to kill the connection. */ - - LWS_CALLBACK_CONFIRM_EXTENSION_OKAY = 25, - /**< When the server handshake code - * sees that it does support a requested extension, before - * accepting the extension by additing to the list sent back to - * the client it gives this callback just to check that it's okay - * to use that extension. It calls back to the requested protocol - * and with in being the extension name, len is 0 and user is - * valid. Note though at this time the ESTABLISHED callback hasn't - * happened yet so if you initialize user content there, user - * content during this callback might not be useful for anything. */ - - LWS_CALLBACK_WS_SERVER_BIND_PROTOCOL = 77, - LWS_CALLBACK_WS_SERVER_DROP_PROTOCOL = 78, - - /* --------------------------------------------------------------------- - * ----- Callbacks related to Websocket Client ----- - */ - - LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 1, - /**< the request client connection has been unable to complete a - * handshake with the remote server. If in is non-NULL, you can - * find an error string of length len where it points to - * - * Diagnostic strings that may be returned include - * - * "getaddrinfo (ipv6) failed" - * "unknown address family" - * "getaddrinfo (ipv4) failed" - * "set socket opts failed" - * "insert wsi failed" - * "lws_ssl_client_connect1 failed" - * "lws_ssl_client_connect2 failed" - * "Peer hung up" - * "read failed" - * "HS: URI missing" - * "HS: Redirect code but no Location" - * "HS: URI did not parse" - * "HS: Redirect failed" - * "HS: Server did not return 200" - * "HS: OOM" - * "HS: disallowed by client filter" - * "HS: disallowed at ESTABLISHED" - * "HS: ACCEPT missing" - * "HS: ws upgrade response not 101" - * "HS: UPGRADE missing" - * "HS: Upgrade to something other than websocket" - * "HS: CONNECTION missing" - * "HS: UPGRADE malformed" - * "HS: PROTOCOL malformed" - * "HS: Cannot match protocol" - * "HS: EXT: list too big" - * "HS: EXT: failed setting defaults" - * "HS: EXT: failed parsing defaults" - * "HS: EXT: failed parsing options" - * "HS: EXT: Rejects server options" - * "HS: EXT: unknown ext" - * "HS: Accept hash wrong" - * "HS: Rejected by filter cb" - * "HS: OOM" - * "HS: SO_SNDBUF failed" - * "HS: Rejected at CLIENT_ESTABLISHED" - */ - - LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH = 2, - /**< this is the last chance for the client user code to examine the - * http headers and decide to reject the connection. If the - * content in the headers is interesting to the - * client (url, etc) it needs to copy it out at - * this point since it will be destroyed before - * the CLIENT_ESTABLISHED call */ - - LWS_CALLBACK_CLIENT_ESTABLISHED = 3, - /**< after your client connection completed the websocket upgrade - * handshake with the remote server */ - - LWS_CALLBACK_CLIENT_CLOSED = 75, - /**< when a client websocket session ends */ - - LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER = 24, - /**< this callback happens - * when a client handshake is being compiled. user is NULL, - * in is a char **, it's pointing to a char * which holds the - * next location in the header buffer where you can add - * headers, and len is the remaining space in the header buffer, - * which is typically some hundreds of bytes. So, to add a canned - * cookie, your handler code might look similar to: - * - * char **p = (char **)in, *end = (*p) + len; - * - * if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COOKIE, - * (unsigned char)"a=b", 3, p, end)) - * return -1; - * - * See LWS_CALLBACK_ADD_HEADERS for adding headers to server - * transactions. - */ - - LWS_CALLBACK_CLIENT_RECEIVE = 8, - /**< data has appeared from the server for the client connection, it - * can be found at *in and is len bytes long */ - - LWS_CALLBACK_CLIENT_RECEIVE_PONG = 9, - /**< clients receive PONG packets with this callback reason */ - - LWS_CALLBACK_CLIENT_WRITEABLE = 10, - /**< If you call lws_callback_on_writable() on a connection, you will - * get one of these callbacks coming when the connection socket - * is able to accept another write packet without blocking. - * If it already was able to take another packet without blocking, - * you'll get this callback at the next call to the service loop - * function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE - * and servers get LWS_CALLBACK_SERVER_WRITEABLE. */ - - LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED = 26, - /**< When a ws client - * connection is being prepared to start a handshake to a server, - * each supported extension is checked with protocols[0] callback - * with this reason, giving the user code a chance to suppress the - * claim to support that extension by returning non-zero. If - * unhandled, by default 0 will be returned and the extension - * support included in the header to the server. Notice this - * callback comes to protocols[0]. */ - - LWS_CALLBACK_WS_EXT_DEFAULTS = 39, - /**< Gives client connections an opportunity to adjust negotiated - * extension defaults. `user` is the extension name that was - * negotiated (eg, "permessage-deflate"). `in` points to a - * buffer and `len` is the buffer size. The user callback can - * set the buffer to a string describing options the extension - * should parse. Or just ignore for defaults. */ - - - LWS_CALLBACK_FILTER_NETWORK_CONNECTION = 17, - /**< called when a client connects to - * the server at network level; the connection is accepted but then - * passed to this callback to decide whether to hang up immediately - * or not, based on the client IP. in contains the connection - * socket's descriptor. Since the client connection information is - * not available yet, wsi still pointing to the main server socket. - * Return non-zero to terminate the connection before sending or - * receiving anything. Because this happens immediately after the - * network connection from the client, there's no websocket protocol - * selected yet so this callback is issued only to protocol 0. */ - - LWS_CALLBACK_WS_CLIENT_BIND_PROTOCOL = 79, - LWS_CALLBACK_WS_CLIENT_DROP_PROTOCOL = 80, - - /* --------------------------------------------------------------------- - * ----- Callbacks related to external poll loop integration ----- - */ - - LWS_CALLBACK_GET_THREAD_ID = 31, - /**< lws can accept callback when writable requests from other - * threads, if you implement this callback and return an opaque - * current thread ID integer. */ - - /* external poll() management support */ - LWS_CALLBACK_ADD_POLL_FD = 32, - /**< lws normally deals with its poll() or other event loop - * internally, but in the case you are integrating with another - * server you will need to have lws sockets share a - * polling array with the other server. This and the other - * POLL_FD related callbacks let you put your specialized - * poll array interface code in the callback for protocol 0, the - * first protocol you support, usually the HTTP protocol in the - * serving case. - * This callback happens when a socket needs to be - * added to the polling loop: in points to a struct - * lws_pollargs; the fd member of the struct is the file - * descriptor, and events contains the active events - * - * If you are using the internal lws polling / event loop - * you can just ignore these callbacks. */ - - LWS_CALLBACK_DEL_POLL_FD = 33, - /**< This callback happens when a socket descriptor - * needs to be removed from an external polling array. in is - * again the struct lws_pollargs containing the fd member - * to be removed. If you are using the internal polling - * loop, you can just ignore it. */ - - LWS_CALLBACK_CHANGE_MODE_POLL_FD = 34, - /**< This callback happens when lws wants to modify the events for - * a connection. - * in is the struct lws_pollargs with the fd to change. - * The new event mask is in events member and the old mask is in - * the prev_events member. - * If you are using the internal polling loop, you can just ignore - * it. */ - - LWS_CALLBACK_LOCK_POLL = 35, - /**< These allow the external poll changes driven - * by lws to participate in an external thread locking - * scheme around the changes, so the whole thing is threadsafe. - * These are called around three activities in the library, - * - inserting a new wsi in the wsi / fd table (len=1) - * - deleting a wsi from the wsi / fd table (len=1) - * - changing a wsi's POLLIN/OUT state (len=0) - * Locking and unlocking external synchronization objects when - * len == 1 allows external threads to be synchronized against - * wsi lifecycle changes if it acquires the same lock for the - * duration of wsi dereference from the other thread context. */ - - LWS_CALLBACK_UNLOCK_POLL = 36, - /**< See LWS_CALLBACK_LOCK_POLL, ignore if using lws internal poll */ - - /* --------------------------------------------------------------------- - * ----- Callbacks related to CGI serving ----- - */ - - LWS_CALLBACK_CGI = 40, - /**< CGI: CGI IO events on stdin / out / err are sent here on - * protocols[0]. The provided `lws_callback_http_dummy()` - * handles this and the callback should be directed there if - * you use CGI. */ - - LWS_CALLBACK_CGI_TERMINATED = 41, - /**< CGI: The related CGI process ended, this is called before - * the wsi is closed. Used to, eg, terminate chunking. - * The provided `lws_callback_http_dummy()` - * handles this and the callback should be directed there if - * you use CGI. The child PID that terminated is in len. */ - - LWS_CALLBACK_CGI_STDIN_DATA = 42, - /**< CGI: Data is, to be sent to the CGI process stdin, eg from - * a POST body. The provided `lws_callback_http_dummy()` - * handles this and the callback should be directed there if - * you use CGI. */ - - LWS_CALLBACK_CGI_STDIN_COMPLETED = 43, - /**< CGI: no more stdin is coming. The provided - * `lws_callback_http_dummy()` handles this and the callback - * should be directed there if you use CGI. */ - - LWS_CALLBACK_CGI_PROCESS_ATTACH = 70, - /**< CGI: Sent when the CGI process is spawned for the wsi. The - * len parameter is the PID of the child process */ - - /* --------------------------------------------------------------------- - * ----- Callbacks related to Generic Sessions ----- - */ - - LWS_CALLBACK_SESSION_INFO = 54, - /**< This is only generated by user code using generic sessions. - * It's used to get a `struct lws_session_info` filled in by - * generic sessions with information about the logged-in user. - * See the messageboard sample for an example of how to use. */ - - LWS_CALLBACK_GS_EVENT = 55, - /**< Indicates an event happened to the Generic Sessions session. - * `in` contains a `struct lws_gs_event_args` describing the event. */ - - LWS_CALLBACK_HTTP_PMO = 56, - /**< per-mount options for this connection, called before - * the normal LWS_CALLBACK_HTTP when the mount has per-mount - * options. - */ - - /* --------------------------------------------------------------------- - * ----- Callbacks related to RAW PROXY ----- - */ - - LWS_CALLBACK_RAW_PROXY_CLI_RX = 89, - /**< RAW mode client (outgoing) RX */ - - LWS_CALLBACK_RAW_PROXY_SRV_RX = 90, - /**< RAW mode server (listening) RX */ - - LWS_CALLBACK_RAW_PROXY_CLI_CLOSE = 91, - /**< RAW mode client (outgoing) is closing */ - - LWS_CALLBACK_RAW_PROXY_SRV_CLOSE = 92, - /**< RAW mode server (listening) is closing */ - - LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE = 93, - /**< RAW mode client (outgoing) may be written */ - - LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE = 94, - /**< RAW mode server (listening) may be written */ - - LWS_CALLBACK_RAW_PROXY_CLI_ADOPT = 95, - /**< RAW mode client (onward) accepted socket was adopted - * (equivalent to 'wsi created') */ - - LWS_CALLBACK_RAW_PROXY_SRV_ADOPT = 96, - /**< RAW mode server (listening) accepted socket was adopted - * (equivalent to 'wsi created') */ - - LWS_CALLBACK_RAW_PROXY_CLI_BIND_PROTOCOL = 97, - LWS_CALLBACK_RAW_PROXY_SRV_BIND_PROTOCOL = 98, - LWS_CALLBACK_RAW_PROXY_CLI_DROP_PROTOCOL = 99, - LWS_CALLBACK_RAW_PROXY_SRV_DROP_PROTOCOL = 100, - - - /* --------------------------------------------------------------------- - * ----- Callbacks related to RAW sockets ----- - */ - - LWS_CALLBACK_RAW_RX = 59, - /**< RAW mode connection RX */ - - LWS_CALLBACK_RAW_CLOSE = 60, - /**< RAW mode connection is closing */ - - LWS_CALLBACK_RAW_WRITEABLE = 61, - /**< RAW mode connection may be written */ - - LWS_CALLBACK_RAW_ADOPT = 62, - /**< RAW mode connection was adopted (equivalent to 'wsi created') */ - - LWS_CALLBACK_RAW_CONNECTED = 101, - /**< outgoing client RAW mode connection was connected */ - - LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL = 81, - LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL = 82, - - /* --------------------------------------------------------------------- - * ----- Callbacks related to RAW file handles ----- - */ - - LWS_CALLBACK_RAW_ADOPT_FILE = 63, - /**< RAW mode file was adopted (equivalent to 'wsi created') */ - - LWS_CALLBACK_RAW_RX_FILE = 64, - /**< This is the indication the RAW mode file has something to read. - * This doesn't actually do the read of the file and len is always - * 0... your code should do the read having been informed there is - * something to read now. */ - - LWS_CALLBACK_RAW_WRITEABLE_FILE = 65, - /**< RAW mode file is writeable */ - - LWS_CALLBACK_RAW_CLOSE_FILE = 66, - /**< RAW mode wsi that adopted a file is closing */ - - LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL = 83, - LWS_CALLBACK_RAW_FILE_DROP_PROTOCOL = 84, - - /* --------------------------------------------------------------------- - * ----- Callbacks related to generic wsi events ----- - */ - - LWS_CALLBACK_TIMER = 73, - /**< When the time elapsed after a call to - * lws_set_timer_usecs(wsi, usecs) is up, the wsi will get one of - * these callbacks. The deadline can be continuously extended into the - * future by later calls to lws_set_timer_usecs() before the deadline - * expires, or cancelled by lws_set_timer_usecs(wsi, -1); - */ - - LWS_CALLBACK_EVENT_WAIT_CANCELLED = 71, - /**< This is sent to every protocol of every vhost in response - * to lws_cancel_service() or lws_cancel_service_pt(). This - * callback is serialized in the lws event loop normally, even - * if the lws_cancel_service[_pt]() call was from a different - * thread. */ - - LWS_CALLBACK_CHILD_CLOSING = 69, - /**< Sent to parent to notify them a child is closing / being - * destroyed. in is the child wsi. - */ - - /* --------------------------------------------------------------------- - * ----- Callbacks related to TLS certificate management ----- - */ - - LWS_CALLBACK_VHOST_CERT_AGING = 72, - /**< When a vhost TLS cert has its expiry checked, this callback - * is broadcast to every protocol of every vhost in case the - * protocol wants to take some action with this information. - * \p in is a pointer to a struct lws_acme_cert_aging_args, - * and \p len is the number of days left before it expires, as - * a (ssize_t). In the struct lws_acme_cert_aging_args, vh - * points to the vhost the cert aging information applies to, - * and element_overrides[] is an optional way to update information - * from the pvos... NULL in an index means use the information from - * from the pvo for the cert renewal, non-NULL in the array index - * means use that pointer instead for the index. */ - - LWS_CALLBACK_VHOST_CERT_UPDATE = 74, - /**< When a vhost TLS cert is being updated, progress is - * reported to the vhost in question here, including completion - * and failure. in points to optional JSON, and len represents the - * connection state using enum lws_cert_update_state */ - - /* --------------------------------------------------------------------- - * ----- Callbacks related to MQTT Client ----- - */ - - LWS_CALLBACK_MQTT_NEW_CLIENT_INSTANTIATED = 200, - LWS_CALLBACK_MQTT_IDLE = 201, - LWS_CALLBACK_MQTT_CLIENT_ESTABLISHED = 202, - LWS_CALLBACK_MQTT_SUBSCRIBED = 203, - LWS_CALLBACK_MQTT_CLIENT_WRITEABLE = 204, - LWS_CALLBACK_MQTT_CLIENT_RX = 205, - LWS_CALLBACK_MQTT_UNSUBSCRIBED = 206, - LWS_CALLBACK_MQTT_DROP_PROTOCOL = 207, - LWS_CALLBACK_MQTT_CLIENT_CLOSED = 208, - LWS_CALLBACK_MQTT_ACK = 209, - /**< When a message is fully sent, if QoS0 this callback is generated - * to locally "acknowledge" it. For QoS1, this callback is only - * generated when the matching PUBACK is received. Return nonzero to - * close the wsi. - */ - LWS_CALLBACK_MQTT_RESEND = 210, - /**< In QoS1, this callback is generated instead of the _ACK one if - * we timed out waiting for a PUBACK and we must resend the message. - * Return nonzero to close the wsi. - */ - - /****** add new things just above ---^ ******/ - - LWS_CALLBACK_USER = 1000, - /**< user code can use any including above without fear of clashes */ -}; - - - -/** - * typedef lws_callback_function() - User server actions - * \param wsi: Opaque websocket instance pointer - * \param reason: The reason for the call - * \param user: Pointer to per-session user data allocated by library - * \param in: Pointer used for some callback reasons - * \param len: Length set for some callback reasons - * - * This callback is the way the user controls what is served. All the - * protocol detail is hidden and handled by the library. - * - * For each connection / session there is user data allocated that is - * pointed to by "user". You set the size of this user data area when - * the library is initialized with lws_create_server. - */ -typedef int -lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len); - -#define LWS_CB_REASON_AUX_BF__CGI 1 -#define LWS_CB_REASON_AUX_BF__PROXY 2 -#define LWS_CB_REASON_AUX_BF__CGI_CHUNK_END 4 -#define LWS_CB_REASON_AUX_BF__CGI_HEADERS 8 -#define LWS_CB_REASON_AUX_BF__PROXY_TRANS_END 16 -#define LWS_CB_REASON_AUX_BF__PROXY_HEADERS 32 -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-cgi.h libwebsockets-2.4.2/include/libwebsockets/lws-cgi.h --- libwebsockets-4.0.20/include/libwebsockets/lws-cgi.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-cgi.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup cgi cgi handling - * - * ##CGI handling - * - * These functions allow low-level control over stdin/out/err of the cgi. - * - * However for most cases, binding the cgi to http in and out, the default - * lws implementation already does the right thing. - */ - -enum lws_enum_stdinouterr { - LWS_STDIN = 0, - LWS_STDOUT = 1, - LWS_STDERR = 2, -}; - -enum lws_cgi_hdr_state { - LCHS_HEADER, - LCHS_CR1, - LCHS_LF1, - LCHS_CR2, - LCHS_LF2, - LHCS_RESPONSE, - LHCS_DUMP_HEADERS, - LHCS_PAYLOAD, - LCHS_SINGLE_0A, -}; - -struct lws_cgi_args { - struct lws **stdwsi; /**< get fd with lws_get_socket_fd() */ - enum lws_enum_stdinouterr ch; /**< channel index */ - unsigned char *data; /**< for messages with payload */ - enum lws_cgi_hdr_state hdr_state; /**< track where we are in cgi headers */ - int len; /**< length */ -}; - -#ifdef LWS_WITH_CGI -/** - * lws_cgi: spawn network-connected cgi process - * - * \param wsi: connection to own the process - * \param exec_array: array of "exec-name" "arg1" ... "argn" NULL - * \param script_uri_path_len: how many chars on the left of the uri are the - * path to the cgi, or -1 to spawn without URL-related env vars - * \param timeout_secs: seconds script should be allowed to run - * \param mp_cgienv: pvo list with per-vhost cgi options to put in env - */ -LWS_VISIBLE LWS_EXTERN int -lws_cgi(struct lws *wsi, const char * const *exec_array, - int script_uri_path_len, int timeout_secs, - const struct lws_protocol_vhost_options *mp_cgienv); - -/** - * lws_cgi_write_split_stdout_headers: write cgi output accounting for header part - * - * \param wsi: connection to own the process - */ -LWS_VISIBLE LWS_EXTERN int -lws_cgi_write_split_stdout_headers(struct lws *wsi); - -/** - * lws_cgi_kill: terminate cgi process associated with wsi - * - * \param wsi: connection to own the process - */ -LWS_VISIBLE LWS_EXTERN int -lws_cgi_kill(struct lws *wsi); - -/** - * lws_cgi_get_stdwsi: get wsi for stdin, stdout, or stderr - * - * \param wsi: parent wsi that has cgi - * \param ch: which of LWS_STDIN, LWS_STDOUT or LWS_STDERR - */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch); - -#endif -///@} - diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-client.h libwebsockets-2.4.2/include/libwebsockets/lws-client.h --- libwebsockets-4.0.20/include/libwebsockets/lws-client.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-client.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,323 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup client Client related functions - * ##Client releated functions - * \ingroup lwsapi - * - * */ -///@{ - -/** enum lws_client_connect_ssl_connection_flags - flags that may be used - * with struct lws_client_connect_info ssl_connection member to control if - * and how SSL checks apply to the client connection being created - */ - -enum lws_client_connect_ssl_connection_flags { - LCCSCF_USE_SSL = (1 << 0), - LCCSCF_ALLOW_SELFSIGNED = (1 << 1), - LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK = (1 << 2), - LCCSCF_ALLOW_EXPIRED = (1 << 3), - LCCSCF_ALLOW_INSECURE = (1 << 4), - LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM = (1 << 5), - LCCSCF_H2_QUIRK_OVERFLOWS_TXCR = (1 << 6), - LCCSCF_H2_AUTH_BEARER = (1 << 7), - LCCSCF_H2_HEXIFY_AUTH_TOKEN = (1 << 8), - LCCSCF_H2_MANUAL_RXFLOW = (1 << 9), - LCCSCF_HTTP_MULTIPART_MIME = (1 << 10), - LCCSCF_HTTP_X_WWW_FORM_URLENCODED = (1 << 11), - LCCSCF_HTTP_NO_FOLLOW_REDIRECT = (1 << 12), - - LCCSCF_PIPELINE = (1 << 16), - /**< Serialize / pipeline multiple client connections - * on a single connection where possible. - * - * HTTP/1.0: possible if Keep-Alive: yes sent by server - * HTTP/1.1: always possible... uses pipelining - * HTTP/2: always possible... uses parallel streams - */ - LCCSCF_MUXABLE_STREAM = (1 << 17), -}; - -/** struct lws_client_connect_info - parameters to connect with when using - * lws_client_connect_via_info() */ - -struct lws_client_connect_info { - struct lws_context *context; - /**< lws context to create connection in */ - const char *address; - /**< remote address to connect to */ - int port; - /**< remote port to connect to */ - int ssl_connection; - /**< 0, or a combination of LCCSCF_ flags */ - const char *path; - /**< uri path */ - const char *host; - /**< content of host header */ - const char *origin; - /**< content of origin header */ - const char *protocol; - /**< list of ws protocols we could accept */ - int ietf_version_or_minus_one; - /**< deprecated: currently leave at 0 or -1 */ - void *userdata; - /**< if non-NULL, use this as wsi user_data instead of malloc it */ - const void *client_exts; - /**< UNUSED... provide in info.extensions at context creation time */ - const char *method; - /**< if non-NULL, do this http method instead of ws[s] upgrade. - * use "GET" to be a simple http client connection. "RAW" gets - * you a connected socket that lws itself will leave alone once - * connected. */ - struct lws *parent_wsi; - /**< if another wsi is responsible for this connection, give it here. - * this is used to make sure if the parent closes so do any - * child connections first. */ - const char *uri_replace_from; - /**< if non-NULL, when this string is found in URIs in - * text/html content-encoding, it's replaced with uri_replace_to */ - const char *uri_replace_to; - /**< see uri_replace_from */ - struct lws_vhost *vhost; - /**< vhost to bind to (used to determine related SSL_CTX) */ - struct lws **pwsi; - /**< if not NULL, store the new wsi here early in the connection - * process. Although we return the new wsi, the call to create the - * client connection does progress the connection somewhat and may - * meet an error that will result in the connection being scrubbed and - * NULL returned. While the wsi exists though, he may process a - * callback like CLIENT_CONNECTION_ERROR with his wsi: this gives the - * user callback a way to identify which wsi it is that faced the error - * even before the new wsi is returned and even if ultimately no wsi - * is returned. - */ - const char *iface; - /**< NULL to allow routing on any interface, or interface name or IP - * to bind the socket to */ - const char *local_protocol_name; - /**< NULL: .protocol is used both to select the local protocol handler - * to bind to and as the list of remote ws protocols we could - * accept. - * non-NULL: this protocol name is used to bind the connection to - * the local protocol handler. .protocol is used for the - * list of remote ws protocols we could accept */ - const char *alpn; - /**< NULL: allow lws default ALPN list, from vhost if present or from - * list of roles built into lws - * non-NULL: require one from provided comma-separated list of alpn - * tokens - */ - - struct lws_sequencer *seq; - /**< NULL, or an lws_seq_t that wants to be given messages about - * this wsi's lifecycle as it connects, errors or closes. - */ - - void *opaque_user_data; - /**< This data has no meaning to lws but is applied to the client wsi - * and can be retrieved by user code with lws_get_opaque_user_data(). - * It's also provided with sequencer messages if the wsi is bound to - * an lws_seq_t. - */ - - const lws_retry_bo_t *retry_and_idle_policy; - /**< optional retry and idle policy to apply to this connection. - * Currently only the idle parts are applied to the connection. - */ - - int manual_initial_tx_credit; - /**< if LCCSCF_H2_MANUAL_REFLOW is set, this becomes the initial tx - * credit for the stream. - */ - - uint8_t sys_tls_client_cert; - /**< 0 means no client cert. 1+ means apply lws_system client cert 0+ - * to the client connection. - */ - -#if defined(LWS_ROLE_MQTT) - const lws_mqtt_client_connect_param_t *mqtt_cp; -#else - void *mqtt_cp; -#endif - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility - * - * The below is to ensure later library versions with new - * members added above will see 0 (default) even if the app - * was not built against the newer headers. - */ - - void *_unused[4]; /**< dummy */ -}; - -/** - * lws_client_connect_via_info() - Connect to another websocket server - * \param ccinfo: pointer to lws_client_connect_info struct - * - * This function creates a connection to a remote server using the - * information provided in ccinfo. - */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_client_connect_via_info(const struct lws_client_connect_info *ccinfo); - -/** - * lws_init_vhost_client_ssl() - also enable client SSL on an existing vhost - * - * \param info: client ssl related info - * \param vhost: which vhost to initialize client ssl operations on - * - * You only need to call this if you plan on using SSL client connections on - * the vhost. For non-SSL client connections, it's not necessary to call this. - * - * The following members of info are used during the call - * - * - options must have LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT set, - * otherwise the call does nothing - * - provided_client_ssl_ctx must be NULL to get a generated client - * ssl context, otherwise you can pass a prepared one in by setting it - * - ssl_cipher_list may be NULL or set to the client valid cipher list - * - ssl_ca_filepath may be NULL or client cert filepath - * - ssl_cert_filepath may be NULL or client cert filepath - * - ssl_private_key_filepath may be NULL or client cert private key - * - * You must create your vhost explicitly if you want to use this, so you have - * a pointer to the vhost. Create the context first with the option flag - * LWS_SERVER_OPTION_EXPLICIT_VHOSTS and then call lws_create_vhost() with - * the same info struct. - */ -LWS_VISIBLE LWS_EXTERN int -lws_init_vhost_client_ssl(const struct lws_context_creation_info *info, - struct lws_vhost *vhost); -/** - * lws_http_client_read() - consume waiting received http client data - * - * \param wsi: client connection - * \param buf: pointer to buffer pointer - fill with pointer to your buffer - * \param len: pointer to chunk length - fill with max length of buffer - * - * This is called when the user code is notified client http data has arrived. - * The user code may choose to delay calling it to consume the data, for example - * waiting until an onward connection is writeable. - * - * For non-chunked connections, up to len bytes of buf are filled with the - * received content. len is set to the actual amount filled before return. - * - * For chunked connections, the linear buffer content contains the chunking - * headers and it cannot be passed in one lump. Instead, this function will - * call back LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ with in pointing to the - * chunk start and len set to the chunk length. There will be as many calls - * as there are chunks or partial chunks in the buffer. - */ -LWS_VISIBLE LWS_EXTERN int -lws_http_client_read(struct lws *wsi, char **buf, int *len); - -/** - * lws_http_client_http_response() - get last HTTP response code - * - * \param wsi: client connection - * - * Returns the last server response code, eg, 200 for client http connections. - * - * You should capture this during the LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP - * callback, because after that the memory reserved for storing the related - * headers is freed and this value is lost. - */ -LWS_VISIBLE LWS_EXTERN unsigned int -lws_http_client_http_response(struct lws *wsi); - -/** - * lws_tls_client_vhost_extra_cert_mem() - add more certs to vh client tls ctx - * - * \param vh: the vhost to give more client certs to - * \param der: pointer to der format additional cert - * \param der_len: size in bytes of der - * - * After the vhost is created with one cert for client verification, you - * can add additional, eg, intermediate, certs to the client tls context - * of the vhost, for use with validating the incoming server cert(s). - */ -LWS_VISIBLE LWS_EXTERN int -lws_tls_client_vhost_extra_cert_mem(struct lws_vhost *vh, - const uint8_t *der, size_t der_len); - -/** - * lws_client_http_body_pending() - control if client connection needs to send body - * - * \param wsi: client connection - * \param something_left_to_send: nonzero if need to send more body, 0 (default) - * if nothing more to send - * - * If you will send payload data with your HTTP client connection, eg, for POST, - * when you set the related http headers in - * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER callback you should also call - * this API with something_left_to_send nonzero, and call - * lws_callback_on_writable(wsi); - * - * After sending the headers, lws will call your callback with - * LWS_CALLBACK_CLIENT_HTTP_WRITEABLE reason when writable. You can send the - * next part of the http body payload, calling lws_callback_on_writable(wsi); - * if there is more to come, or lws_client_http_body_pending(wsi, 0); to - * let lws know the last part is sent and the connection can move on. - */ -LWS_VISIBLE LWS_EXTERN void -lws_client_http_body_pending(struct lws *wsi, int something_left_to_send); - -/** - * lws_client_http_multipart() - issue appropriate multipart header or trailer - * - * \param wsi: client connection - * \param name: multipart header name field, or NULL if end of multipart - * \param filename: multipart header filename field, or NULL if none - * \param content_type: multipart header content-type part, or NULL if none - * \param p: pointer to position in buffer - * \param end: end of buffer - * - * This issues a multipart mime boundary, or terminator if name = NULL. - * - * Returns 0 if OK or nonzero if couldn't fit in buffer - */ -LWS_VISIBLE LWS_EXTERN int -lws_client_http_multipart(struct lws *wsi, const char *name, - const char *filename, const char *content_type, - char **p, char *end); - -/** - * lws_http_basic_auth_gen() - helper to encode client basic auth string - * - * \param user: user name - * \param pw: password - * \param buf: where to store base64 result - * \param len: max usable size of buf - * - * Encodes a username and password in Basic Auth format for use with the - * Authorization header. On return, buf is filled with something like - * "Basic QWxhZGRpbjpPcGVuU2VzYW1l". - */ -LWS_VISIBLE LWS_EXTERN int -lws_http_basic_auth_gen(const char *user, const char *pw, char *buf, size_t len); - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-context-vhost.h libwebsockets-2.4.2/include/libwebsockets/lws-context-vhost.h --- libwebsockets-4.0.20/include/libwebsockets/lws-context-vhost.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-context-vhost.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,1136 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup context-and-vhost context and vhost related functions - * ##Context and Vhost releated functions - * \ingroup lwsapi - * - * - * LWS requires that there is one context, in which you may define multiple - * vhosts. Each vhost is a virtual host, with either its own listen port - * or sharing an existing one. Each vhost has its own SSL context that can - * be set up individually or left disabled. - * - * If you don't care about multiple "site" support, you can ignore it and - * lws will create a single default vhost at context creation time. - */ -///@{ - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ - - -#define LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT ((1ll << 1) | \ - (1ll << 12)) - /**< (VH) Don't allow the connection unless the client has a - * client cert that we recognize; provides - * LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT */ -#define LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME (1ll << 2) - /**< (CTX) Don't try to get the server's hostname */ -#define LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT ((1ll << 3) | \ - (1ll << 12)) - /**< (VH) Allow non-SSL (plaintext) connections on the same - * port as SSL is listening. If combined with - * LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS it will try to - * force http connections on an https listener (eg, http://x.com:443) to - * redirect to an explicit https connection (eg, https://x.com) - */ -#define LWS_SERVER_OPTION_LIBEV (1ll << 4) - /**< (CTX) Use libev event loop */ -#define LWS_SERVER_OPTION_DISABLE_IPV6 (1ll << 5) - /**< (VH) Disable IPV6 support */ -#define LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS (1ll << 6) - /**< (VH) Don't load OS CA certs, you will need to load your - * own CA cert(s) */ -#define LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED (1ll << 7) - /**< (VH) Accept connections with no valid Cert (eg, selfsigned) */ -#define LWS_SERVER_OPTION_VALIDATE_UTF8 (1ll << 8) - /**< (VH) Check UT-8 correctness */ -#define LWS_SERVER_OPTION_SSL_ECDH ((1ll << 9) | \ - (1ll << 12)) - /**< (VH) initialize ECDH ciphers */ -#define LWS_SERVER_OPTION_LIBUV (1ll << 10) - /**< (CTX) Use libuv event loop */ -#define LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS ((1ll << 11) |\ - (1ll << 12)) - /**< (VH) Use an http redirect to force the client to ask for https. - * Notice if your http server issues the STS header and the client has - * ever seen that, the client will fail the http connection before it - * can actually do the redirect. - * - * Combine with LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS to handle, eg, - * http://x.com:443 -> https://x.com - * - * (deprecated: use mount redirection) */ -#define LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT (1ll << 12) - /**< (CTX) Initialize the SSL library at all */ -#define LWS_SERVER_OPTION_EXPLICIT_VHOSTS (1ll << 13) - /**< (CTX) Only create the context when calling context - * create api, implies user code will create its own vhosts */ -#define LWS_SERVER_OPTION_UNIX_SOCK (1ll << 14) - /**< (VH) Use Unix socket */ -#define LWS_SERVER_OPTION_STS (1ll << 15) - /**< (VH) Send Strict Transport Security header, making - * clients subsequently go to https even if user asked for http */ -#define LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY (1ll << 16) - /**< (VH) Enable LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE to take effect */ -#define LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE (1ll << 17) - /**< (VH) if set, only ipv6 allowed on the vhost */ -#define LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN (1ll << 18) - /**< (CTX) Libuv only: Do not spin on SIGSEGV / SIGFPE. A segfault - * normally makes the lib spin so you can attach a debugger to it - * even if it happened without a debugger in place. You can disable - * that by giving this option. - */ -#define LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN (1ll << 19) - /**< For backwards-compatibility reasons, by default - * lws prepends "http://" to the origin you give in the client - * connection info struct. If you give this flag when you create - * the context, only the string you give in the client connect - * info for .origin (if any) will be used directly. - */ -#define LWS_SERVER_OPTION_FALLBACK_TO_RAW /* use below name */ (1ll << 20) -#define LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG (1ll << 20) - /**< (VH) if invalid http is coming in the first line, then abandon - * trying to treat the connection as http, and belatedly apply the - * .listen_accept_role / .listen_accept_protocol info struct members to - * the connection. If they are NULL, for backwards-compatibility the - * connection is bound to "raw-skt" role, and in order of priority: - * 1) the vh protocol with a pvo named "raw", 2) the vh protocol with a - * pvo named "default", or 3) protocols[0]. - * - * Must be combined with LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT - * to work with a socket listening with tls. - */ - -#define LWS_SERVER_OPTION_LIBEVENT (1ll << 21) - /**< (CTX) Use libevent event loop */ - -#define LWS_SERVER_OPTION_ONLY_RAW /* Use below name instead */ (1ll << 22) -#define LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG (1ll << 22) - /**< (VH) All connections to this vhost / port are bound to the - * role and protocol given in .listen_accept_role / - * .listen_accept_protocol. - * - * If those explicit user-controlled names are NULL, for backwards- - * compatibility the connection is bound to "raw-skt" role, and in order - * of priority: 1) the vh protocol with a pvo named "raw", 2) the vh - * protocol with a pvo named "default", or 3) protocols[0]. - * - * It's much preferred to specify the role + protocol using the - * .listen_accept_role and .listen_accept_protocol in the info struct. - */ -#define LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE (1ll << 23) - /**< (VH) Set to allow multiple listen sockets on one interface + - * address + port. The default is to strictly allow only one - * listen socket at a time. This is automatically selected if you - * have multiple service threads. Linux only. - */ -#define LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX (1ll << 24) - /**< (VH) Force setting up the vhost SSL_CTX, even though the user - * code doesn't explicitly provide a cert in the info struct. It - * implies the user code is going to provide a cert at the - * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS callback, which - * provides the vhost SSL_CTX * in the user parameter. - */ -#define LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT (1ll << 25) - /**< (VH) You probably don't want this. It forces this vhost to not - * call LWS_CALLBACK_PROTOCOL_INIT on its protocols. It's used in the - * special case of a temporary vhost bound to a single protocol. - */ -#define LWS_SERVER_OPTION_IGNORE_MISSING_CERT (1ll << 26) - /**< (VH) Don't fail if the vhost TLS cert or key are missing, just - * continue. The vhost won't be able to serve anything, but if for - * example the ACME plugin was configured to fetch a cert, this lets - * you bootstrap your vhost from having no cert to start with. - */ -#define LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK (1ll << 27) - /**< (VH) On this vhost, if the connection is being upgraded, insist - * that there's a Host: header and that the contents match the vhost - * name + port (443 / 80 are assumed if no :port given based on if the - * connection is using TLS). - * - * By default, without this flag, on upgrade lws just checks that the - * Host: header was given without checking the contents... this is to - * allow lax hostname mappings like localhost / 127.0.0.1, and CNAME - * mappings like www.mysite.com / mysite.com - */ -#define LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE (1ll << 28) - /**< (VH) Send lws default HTTP headers recommended by Mozilla - * Observatory for security. This is a helper option that sends canned - * headers on each http response enabling a VERY strict Content Security - * Policy. The policy is so strict, for example it won't let the page - * run its own inline JS nor show images or take CSS from a different - * server. In many cases your JS only comes from your server as do the - * image sources and CSS, so that is what you want... attackers hoping - * to inject JS into your DOM are completely out of luck since even if - * they succeed, it will be rejected for execution by the browser - * according to the strict CSP. In other cases you have to deviate from - * the complete strictness, in which case don't use this flag: use the - * .headers member in the vhost init described in struct - * lws_context_creation_info instead to send the adapted headers - * yourself. - */ - -#define LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER (1ll << 29) - /**< (VH) If you really want to allow HTTP connections on a tls - * listener, you can do it with this combined with - * LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT. But this is allowing - * accidental loss of the security assurances provided by tls depending - * on the client using http when he meant https... it's not - * recommended. - */ -#define LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND (1ll << 30) - /**< (VH) When instantiating a new vhost and the specified port is - * already in use, a null value shall be return to signal the error. - */ - -#define LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW (1ll << 31) - /**< (VH) Indicates the connections using this vhost should ignore - * h2 WINDOW_UPDATE from broken peers and fix them up */ - -#define LWS_SERVER_OPTION_VH_H2_HALF_CLOSED_LONG_POLL (1ll << 32) - /**< (VH) Tell the vhost to treat half-closed remote clients as - * entered into an immortal (ie, not subject to normal timeouts) long - * poll mode. - */ - -#define LWS_SERVER_OPTION_GLIB (1ll << 33) - /**< (CTX) Use glib event loop */ - - /****** add new things just above ---^ ******/ - - -#define lws_check_opt(c, f) ((((uint64_t)c) & ((uint64_t)f)) == ((uint64_t)f)) - -struct lws_plat_file_ops; -struct lws_ss_policy; -struct lws_ss_plugin; - -typedef int (*lws_context_ready_cb_t)(struct lws_context *context); - -/** struct lws_context_creation_info - parameters to create context and /or vhost with - * - * This is also used to create vhosts.... if LWS_SERVER_OPTION_EXPLICIT_VHOSTS - * is not given, then for backwards compatibility one vhost is created at - * context-creation time using the info from this struct. - * - * If LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, then no vhosts are created - * at the same time as the context, they are expected to be created afterwards. - */ -struct lws_context_creation_info { - int port; - /**< VHOST: Port to listen on. Use CONTEXT_PORT_NO_LISTEN to suppress - * listening for a client. Use CONTEXT_PORT_NO_LISTEN_SERVER if you are - * writing a server but you are using \ref sock-adopt instead of the - * built-in listener. - * - * You can also set port to 0, in which case the kernel will pick - * a random port that is not already in use. You can find out what - * port the vhost is listening on using lws_get_vhost_listen_port() */ - const char *iface; - /**< VHOST: NULL to bind the listen socket to all interfaces, or the - * interface name, eg, "eth2" - * If options specifies LWS_SERVER_OPTION_UNIX_SOCK, this member is - * the pathname of a UNIX domain socket. you can use the UNIX domain - * sockets in abstract namespace, by prepending an at symbol to the - * socket name. */ - const struct lws_protocols *protocols; - /**< VHOST: Array of structures listing supported protocols and a - * protocol-specific callback for each one. The list is ended with an - * entry that has a NULL callback pointer. SEE ALSO .pprotocols below, - * which gives an alternative way to provide an array of pointers to - * protocol structs. */ - const struct lws_extension *extensions; - /**< VHOST: NULL or array of lws_extension structs listing the - * extensions this context supports. */ - const struct lws_token_limits *token_limits; - /**< CONTEXT: NULL or struct lws_token_limits pointer which is - * initialized with a token length limit for each possible WSI_TOKEN_ */ - const char *ssl_private_key_password; - /**< VHOST: NULL or the passphrase needed for the private key. (For - * backwards compatibility, this can also be used to pass the client - * cert passphrase when setting up a vhost client SSL context, but it is - * preferred to use .client_ssl_private_key_password for that.) */ - const char *ssl_cert_filepath; - /**< VHOST: If libwebsockets was compiled to use ssl, and you want - * to listen using SSL, set to the filepath to fetch the - * server cert from, otherwise NULL for unencrypted. (For backwards - * compatibility, this can also be used to pass the client certificate - * when setting up a vhost client SSL context, but it is preferred to - * use .client_ssl_cert_filepath for that.) - * - * Notice you can alternatively set a single DER or PEM from a memory - * buffer as the vhost tls cert using \p server_ssl_cert_mem and - * \p server_ssl_cert_mem_len. - */ - const char *ssl_private_key_filepath; - /**< VHOST: filepath to private key if wanting SSL mode; - * if this is set to NULL but ssl_cert_filepath is set, the - * OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY callback is called - * to allow setting of the private key directly via openSSL - * library calls. (For backwards compatibility, this can also be used - * to pass the client cert private key filepath when setting up a - * vhost client SSL context, but it is preferred to use - * .client_ssl_private_key_filepath for that.) - * - * Notice you can alternatively set a DER or PEM private key from a - * memory buffer as the vhost tls private key using - * \p server_ssl_private_key_mem and \p server_ssl_private_key_mem_len. - */ - const char *ssl_ca_filepath; - /**< VHOST: CA certificate filepath or NULL. (For backwards - * compatibility, this can also be used to pass the client CA - * filepath when setting up a vhost client SSL context, - * but it is preferred to use .client_ssl_ca_filepath for that.) - * - * Notice you can alternatively set a DER or PEM CA cert from a memory - * buffer using \p server_ssl_ca_mem and \p server_ssl_ca_mem_len. - */ - const char *ssl_cipher_list; - /**< VHOST: List of valid ciphers to use ON TLS1.2 AND LOWER ONLY (eg, - * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL" - * or you can leave it as NULL to get "DEFAULT" (For backwards - * compatibility, this can also be used to pass the client cipher - * list when setting up a vhost client SSL context, - * but it is preferred to use .client_ssl_cipher_list for that.) - * SEE .tls1_3_plus_cipher_list and .client_tls_1_3_plus_cipher_list - * for the equivalent for tls1.3. - */ - const char *http_proxy_address; - /**< VHOST: If non-NULL, attempts to proxy via the given address. - * If proxy auth is required, use format - * "username:password\@server:port" */ - unsigned int http_proxy_port; - /**< VHOST: If http_proxy_address was non-NULL, uses this port */ - int gid; - /**< CONTEXT: group id to change to after setting listen socket, - * or -1. See also .username below. */ - int uid; - /**< CONTEXT: user id to change to after setting listen socket, - * or -1. See also .groupname below. */ - uint64_t options; - /**< VHOST + CONTEXT: 0, or LWS_SERVER_OPTION_... bitfields */ - void *user; - /**< VHOST + CONTEXT: optional user pointer that will be associated - * with the context when creating the context (and can be retrieved by - * lws_context_user(context), or with the vhost when creating the vhost - * (and can be retrieved by lws_vhost_user(vhost)). You will need to - * use LWS_SERVER_OPTION_EXPLICIT_VHOSTS and create the vhost separately - * if you care about giving the context and vhost different user pointer - * values. - */ - int ka_time; - /**< CONTEXT: 0 for no TCP keepalive, otherwise apply this keepalive - * timeout to all libwebsocket sockets, client or server */ - int ka_probes; - /**< CONTEXT: if ka_time was nonzero, after the timeout expires how many - * times to try to get a response from the peer before giving up - * and killing the connection */ - int ka_interval; - /**< CONTEXT: if ka_time was nonzero, how long to wait before each ka_probes - * attempt */ -#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS) - SSL_CTX *provided_client_ssl_ctx; - /**< CONTEXT: If non-null, swap out libwebsockets ssl - * implementation for the one provided by provided_ssl_ctx. - * Libwebsockets no longer is responsible for freeing the context - * if this option is selected. */ -#else /* maintain structure layout either way */ - void *provided_client_ssl_ctx; /**< dummy if ssl disabled */ -#endif - - unsigned short max_http_header_data; - /**< CONTEXT: The max amount of header payload that can be handled - * in an http request (unrecognized header payload is dropped) */ - unsigned short max_http_header_pool; - /**< CONTEXT: The max number of connections with http headers that - * can be processed simultaneously (the corresponding memory is - * allocated and deallocated dynamically as needed). If the pool is - * fully busy new incoming connections must wait for accept until one - * becomes free. 0 = allow as many ah as number of availble fds for - * the process */ - - unsigned int count_threads; - /**< CONTEXT: how many contexts to create in an array, 0 = 1 */ - unsigned int fd_limit_per_thread; - /**< CONTEXT: nonzero means restrict each service thread to this - * many fds, 0 means the default which is divide the process fd - * limit by the number of threads. - * - * Note if this is nonzero, and fd_limit_per_thread multiplied by the - * number of service threads is less than the process ulimit, then lws - * restricts internal lookup table allocation to the smaller size, and - * switches to a less efficient lookup scheme. You should use this to - * trade off speed against memory usage if you know the lws context - * will only use a handful of fds. - * - * Bear in mind lws may use some fds internally, for example for the - * cancel pipe, so you may need to allow for some extras for normal - * operation. - */ - unsigned int timeout_secs; - /**< VHOST: various processes involving network roundtrips in the - * library are protected from hanging forever by timeouts. If - * nonzero, this member lets you set the timeout used in seconds. - * Otherwise a default timeout is used. */ - const char *ecdh_curve; - /**< VHOST: if NULL, defaults to initializing server with - * "prime256v1" */ - const char *vhost_name; - /**< VHOST: name of vhost, must match external DNS name used to - * access the site, like "warmcat.com" as it's used to match - * Host: header and / or SNI name for SSL. */ - const char * const *plugin_dirs; - /**< CONTEXT: NULL, or NULL-terminated array of directories to - * scan for lws protocol plugins at context creation time */ - const struct lws_protocol_vhost_options *pvo; - /**< VHOST: pointer to optional linked list of per-vhost - * options made accessible to protocols */ - int keepalive_timeout; - /**< VHOST: (default = 0 = 5s, 31s for http/2) seconds to allow remote - * client to hold on to an idle HTTP/1.1 connection. Timeout lifetime - * applied to idle h2 network connections */ - const char *log_filepath; - /**< VHOST: filepath to append logs to... this is opened before - * any dropping of initial privileges */ - const struct lws_http_mount *mounts; - /**< VHOST: optional linked list of mounts for this vhost */ - const char *server_string; - /**< CONTEXT: string used in HTTP headers to identify server - * software, if NULL, "libwebsockets". */ - unsigned int pt_serv_buf_size; - /**< CONTEXT: 0 = default of 4096. This buffer is used by - * various service related features including file serving, it - * defines the max chunk of file that can be sent at once. - * At the risk of lws having to buffer failed large sends, it - * can be increased to, eg, 128KiB to improve throughput. */ - unsigned int max_http_header_data2; - /**< CONTEXT: if max_http_header_data is 0 and this - * is nonzero, this will be used in place of the default. It's - * like this for compatibility with the original short version, - * this is unsigned int length. */ - long ssl_options_set; - /**< VHOST: Any bits set here will be set as server SSL options */ - long ssl_options_clear; - /**< VHOST: Any bits set here will be cleared as server SSL options */ - unsigned short ws_ping_pong_interval; - /**< CONTEXT: 0 for none, else interval in seconds between sending - * PINGs on idle websocket connections. When the PING is sent, - * the PONG must come within the normal timeout_secs timeout period - * or the connection will be dropped. - * Any RX or TX traffic on the connection restarts the interval timer, - * so a connection which always sends or receives something at intervals - * less than the interval given here will never send PINGs / expect - * PONGs. Conversely as soon as the ws connection is established, an - * idle connection will do the PING / PONG roundtrip as soon as - * ws_ping_pong_interval seconds has passed without traffic - */ - const struct lws_protocol_vhost_options *headers; - /**< VHOST: pointer to optional linked list of per-vhost - * canned headers that are added to server responses */ - - const struct lws_protocol_vhost_options *reject_service_keywords; - /**< CONTEXT: Optional list of keywords and rejection codes + text. - * - * The keywords are checked for existing in the user agent string. - * - * Eg, "badrobot" "404 Not Found" - */ - void *external_baggage_free_on_destroy; - /**< CONTEXT: NULL, or pointer to something externally malloc'd, that - * should be freed when the context is destroyed. This allows you to - * automatically sync the freeing action to the context destruction - * action, so there is no need for an external free() if the context - * succeeded to create. - */ - - const char *client_ssl_private_key_password; - /**< VHOST: Client SSL context init: NULL or the passphrase needed - * for the private key */ - const char *client_ssl_cert_filepath; - /**< VHOST: Client SSL context init: The certificate the client - * should present to the peer on connection */ - const void *client_ssl_cert_mem; - /**< VHOST: Client SSL context init: client certificate memory buffer or - * NULL... use this to load client cert from memory instead of file */ - unsigned int client_ssl_cert_mem_len; - /**< VHOST: Client SSL context init: length of client_ssl_cert_mem in - * bytes */ - const char *client_ssl_private_key_filepath; - /**< VHOST: Client SSL context init: filepath to client private key - * if this is set to NULL but client_ssl_cert_filepath is set, you - * can handle the LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS - * callback of protocols[0] to allow setting of the private key directly - * via tls library calls */ - const char *client_ssl_ca_filepath; - /**< VHOST: Client SSL context init: CA certificate filepath or NULL */ - const void *client_ssl_ca_mem; - /**< VHOST: Client SSL context init: CA certificate memory buffer or - * NULL... use this to load CA cert from memory instead of file */ - unsigned int client_ssl_ca_mem_len; - /**< VHOST: Client SSL context init: length of client_ssl_ca_mem in - * bytes */ - - const char *client_ssl_cipher_list; - /**< VHOST: Client SSL context init: List of valid ciphers to use (eg, - * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL" - * or you can leave it as NULL to get "DEFAULT" */ - - const struct lws_plat_file_ops *fops; - /**< CONTEXT: NULL, or pointer to an array of fops structs, terminated - * by a sentinel with NULL .open. - * - * If NULL, lws provides just the platform file operations struct for - * backwards compatibility. - */ - int simultaneous_ssl_restriction; - /**< CONTEXT: 0 (no limit) or limit of simultaneous SSL sessions - * possible.*/ - const char *socks_proxy_address; - /**< VHOST: If non-NULL, attempts to proxy via the given address. - * If proxy auth is required, use format - * "username:password\@server:port" */ - unsigned int socks_proxy_port; - /**< VHOST: If socks_proxy_address was non-NULL, uses this port - * if nonzero, otherwise requires "server:port" in .socks_proxy_address - */ -#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) - cap_value_t caps[4]; - /**< CONTEXT: array holding Linux capabilities you want to - * continue to be available to the server after it transitions - * to a noprivileged user. Usually none are needed but for, eg, - * .bind_iface, CAP_NET_RAW is required. This gives you a way - * to still have the capability but drop root. - */ - char count_caps; - /**< CONTEXT: count of Linux capabilities in .caps[]. 0 means - * no capabilities will be inherited from root (the default) */ -#endif - int bind_iface; - /**< VHOST: nonzero to strictly bind sockets to the interface name in - * .iface (eg, "eth2"), using SO_BIND_TO_DEVICE. - * - * Requires SO_BINDTODEVICE support from your OS and CAP_NET_RAW - * capability. - * - * Notice that common things like access network interface IP from - * your local machine use your lo / loopback interface and will be - * disallowed by this. - */ - int ssl_info_event_mask; - /**< VHOST: mask of ssl events to be reported on LWS_CALLBACK_SSL_INFO - * callback for connections on this vhost. The mask values are of - * the form SSL_CB_ALERT, defined in openssl/ssl.h. The default of - * 0 means no info events will be reported. - */ - unsigned int timeout_secs_ah_idle; - /**< VHOST: seconds to allow a client to hold an ah without using it. - * 0 defaults to 10s. */ - unsigned short ip_limit_ah; - /**< CONTEXT: max number of ah a single IP may use simultaneously - * 0 is no limit. This is a soft limit: if the limit is - * reached, connections from that IP will wait in the ah - * waiting list and not be able to acquire an ah until - * a connection belonging to the IP relinquishes one it - * already has. - */ - unsigned short ip_limit_wsi; - /**< CONTEXT: max number of wsi a single IP may use simultaneously. - * 0 is no limit. This is a hard limit, connections from - * the same IP will simply be dropped once it acquires the - * amount of simultaneous wsi / accepted connections - * given here. - */ - uint32_t http2_settings[7]; - /**< VHOST: if http2_settings[0] is nonzero, the values given in - * http2_settings[1]..[6] are used instead of the lws - * platform default values. - * Just leave all at 0 if you don't care. - */ - const char *error_document_404; - /**< VHOST: If non-NULL, when asked to serve a non-existent file, - * lws attempts to server this url path instead. Eg, - * "/404.html" */ - const char *alpn; - /**< CONTEXT: If non-NULL, default list of advertised alpn, comma- - * separated - * - * VHOST: If non-NULL, per-vhost list of advertised alpn, comma- - * separated - */ - void **foreign_loops; - /**< CONTEXT: This is ignored if the context is not being started with - * an event loop, ie, .options has a flag like - * LWS_SERVER_OPTION_LIBUV. - * - * NULL indicates lws should start its own even loop for - * each service thread, and deal with closing the loops - * when the context is destroyed. - * - * Non-NULL means it points to an array of external - * ("foreign") event loops that are to be used in turn for - * each service thread. In the default case of 1 service - * thread, it can just point to one foreign event loop. - */ - void (*signal_cb)(void *event_lib_handle, int signum); - /**< CONTEXT: NULL: default signal handling. Otherwise this receives - * the signal handler callback. event_lib_handle is the - * native event library signal handle, eg uv_signal_t * - * for libuv. - */ - struct lws_context **pcontext; - /**< CONTEXT: if non-NULL, at the end of context destroy processing, - * the pointer pointed to by pcontext is written with NULL. You can - * use this to let foreign event loops know that lws context destruction - * is fully completed. - */ - void (*finalize)(struct lws_vhost *vh, void *arg); - /**< VHOST: NULL, or pointer to function that will be called back - * when the vhost is just about to be freed. The arg parameter - * will be set to whatever finalize_arg is below. - */ - void *finalize_arg; - /**< VHOST: opaque pointer lws ignores but passes to the finalize - * callback. If you don't care, leave it NULL. - */ - unsigned int max_http_header_pool2; - /**< CONTEXT: if max_http_header_pool is 0 and this - * is nonzero, this will be used in place of the default. It's - * like this for compatibility with the original short version: - * this is unsigned int length. */ - - long ssl_client_options_set; - /**< VHOST: Any bits set here will be set as CLIENT SSL options */ - long ssl_client_options_clear; - /**< VHOST: Any bits set here will be cleared as CLIENT SSL options */ - - const char *tls1_3_plus_cipher_list; - /**< VHOST: List of valid ciphers to use for incoming server connections - * ON TLS1.3 AND ABOVE (eg, "TLS_CHACHA20_POLY1305_SHA256" on this vhost - * or you can leave it as NULL to get "DEFAULT". - * SEE .client_tls_1_3_plus_cipher_list to do the same on the vhost - * client SSL_CTX. - */ - const char *client_tls_1_3_plus_cipher_list; - /**< VHOST: List of valid ciphers to use for outgoing client connections - * ON TLS1.3 AND ABOVE on this vhost (eg, - * "TLS_CHACHA20_POLY1305_SHA256") or you can leave it as NULL to get - * "DEFAULT". - */ - const char *listen_accept_role; - /**< VHOST: NULL for default, or force accepted incoming connections to - * bind to this role. Uses the role names from their ops struct, eg, - * "raw-skt". - */ - const char *listen_accept_protocol; - /**< VHOST: NULL for default, or force accepted incoming connections to - * bind to this vhost protocol name. - */ - const struct lws_protocols **pprotocols; - /**< VHOST: NULL: use .protocols, otherwise ignore .protocols and use - * this array of pointers to protocols structs. The end of the array - * is marked by a NULL pointer. - * - * This is preferred over .protocols, because it allows the protocol - * struct to be opaquely defined elsewhere, with just a pointer to it - * needed to create the context with it. .protocols requires also - * the type of the user data to be known so its size can be given. - */ - - const void *server_ssl_cert_mem; - /**< VHOST: Alternative for \p ssl_cert_filepath that allows setting - * from memory instead of from a file. At most one of - * \p ssl_cert_filepath or \p server_ssl_cert_mem should be non-NULL. */ - unsigned int server_ssl_cert_mem_len; - /**< VHOST: Server SSL context init: length of server_ssl_cert_mem in - * bytes */ - const void *server_ssl_private_key_mem; - /**< VHOST: Alternative for \p ssl_private_key_filepath allowing - * init from a private key in memory instead of a file. At most one - * of \p ssl_private_key_filepath or \p server_ssl_private_key_mem - * should be non-NULL. */ - unsigned int server_ssl_private_key_mem_len; - /**< VHOST: length of \p server_ssl_private_key_mem in memory */ - const void *server_ssl_ca_mem; - /**< VHOST: Alternative for \p ssl_ca_filepath allowing - * init from a CA cert in memory instead of a file. At most one - * of \p ssl_ca_filepath or \p server_ssl_ca_mem should be non-NULL. */ - unsigned int server_ssl_ca_mem_len; - /**< VHOST: length of \p server_ssl_ca_mem in memory */ - const char *username; /**< CONTEXT: string username for post-init - * permissions. Like .uid but takes a string username. */ - const char *groupname; /**< CONTEXT: string groupname for post-init - * permissions. Like .gid but takes a string groupname. */ - const char *unix_socket_perms; /**< VHOST: if your vhost is listening - * on a unix socket, you can give a "username:groupname" string here - * to control the owner:group it's created with. It's always created - * with 0660 mode. */ - const lws_system_ops_t *system_ops; - /**< CONTEXT: hook up lws_system_ apis to system-specific - * implementations */ - det_lat_buf_cb_t detailed_latency_cb; - /**< CONTEXT: NULL, or callback to receive detailed latency information - * collected for each read and write */ - const char *detailed_latency_filepath; - /**< CONTEXT: NULL, or filepath to put latency data into */ - const lws_retry_bo_t *retry_and_idle_policy; - /**< VHOST: optional retry and idle policy to apply to this vhost. - * Currently only the idle parts are applied to the connections. - */ - lws_state_notify_link_t * const *register_notifier_list; - /**< CONTEXT: NULL, or pointer to an array of notifiers that should - * be registered during context creation, so they can see state change - * events from very early on. The array should end with a NULL. */ - uint8_t udp_loss_sim_tx_pc; - /**< CONTEXT: percentage of udp writes we could have performed - * to instead not do, in order to simulate and test udp retry flow */ - uint8_t udp_loss_sim_rx_pc; - /**< CONTEXT: percentage of udp reads we actually received - * to make disappear, in order to simulate and test udp retry flow */ -#if defined(LWS_WITH_SECURE_STREAMS) - const char *pss_policies_json; /**< CONTEXT: point to a string - * containing a JSON description of the secure streams policies. Set - * to NULL if not using Secure Streams. */ - const struct lws_ss_plugin **pss_plugins; /**< CONTEXT: point to an array - * of pointers to plugin structs here, terminated with a NULL ptr. - * Set to NULL if not using Secure Streams. */ - const char *ss_proxy_bind; /**< CONTEXT: NULL, or: ss_proxy_port == 0: - * point to a string giving the Unix Domain Socket address to use (start - * with @ for abstract namespace), ss_proxy_port nonzero: set the - * network interface address (not name, it's ambiguous for ipv4/6) to - * bind the tcp connection to the proxy to */ - const char *ss_proxy_address; /**< CONTEXT: NULL, or if ss_proxy_port - * nonzero: the tcp address of the ss proxy to connect to */ - uint16_t ss_proxy_port; /* 0 = if connecting to ss proxy, do it via a - * Unix Domain Socket, "+@proxy.ss.lws" if ss_proxy_bind is NULL else - * the socket path given in ss_proxy_bind (start it with a + or +@); - * nonzero means connect via a tcp socket to the tcp address in - * ss_proxy_bind and the given port */ -#endif - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility - * - * The below is to ensure later library versions with new - * members added above will see 0 (default) even if the app - * was not built against the newer headers. - */ - - void *_unused[2]; /**< dummy */ -}; - -/** - * lws_create_context() - Create the websocket handler - * \param info: pointer to struct with parameters - * - * This function creates the listening socket (if serving) and takes care - * of all initialization in one step. - * - * If option LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, no vhost is - * created; you're expected to create your own vhosts afterwards using - * lws_create_vhost(). Otherwise a vhost named "default" is also created - * using the information in the vhost-related members, for compatibility. - * - * After initialization, it returns a struct lws_context * that - * represents this server. After calling, user code needs to take care - * of calling lws_service() with the context pointer to get the - * server's sockets serviced. This must be done in the same process - * context as the initialization call. - * - * The protocol callback functions are called for a handful of events - * including http requests coming in, websocket connections becoming - * established, and data arriving; it's also called periodically to allow - * async transmission. - * - * HTTP requests are sent always to the FIRST protocol in protocol, since - * at that time websocket protocol has not been negotiated. Other - * protocols after the first one never see any HTTP callback activity. - * - * The server created is a simple http server by default; part of the - * websocket standard is upgrading this http connection to a websocket one. - * - * This allows the same server to provide files like scripts and favicon / - * images or whatever over http and dynamic data over websockets all in - * one place; they're all handled in the user callback. - */ -LWS_VISIBLE LWS_EXTERN struct lws_context * -lws_create_context(const struct lws_context_creation_info *info); - - -/** - * lws_context_destroy() - Destroy the websocket context - * \param context: Websocket context - * - * This function closes any active connections and then frees the - * context. After calling this, any further use of the context is - * undefined. - */ -LWS_VISIBLE LWS_EXTERN void -lws_context_destroy(struct lws_context *context); - -typedef int (*lws_reload_func)(void); - -/** - * lws_context_deprecate() - Deprecate the websocket context - * - * \param context: Websocket context - * \param cb: Callback notified when old context listen sockets are closed - * - * This function is used on an existing context before superceding it - * with a new context. - * - * It closes any listen sockets in the context, so new connections are - * not possible. - * - * And it marks the context to be deleted when the number of active - * connections into it falls to zero. - * - * This is aimed at allowing seamless configuration reloads. - * - * The callback cb will be called after the listen sockets are actually - * closed and may be reopened. In the callback the new context should be - * configured and created. (With libuv, socket close happens async after - * more loop events). - */ -LWS_VISIBLE LWS_EXTERN void -lws_context_deprecate(struct lws_context *context, lws_reload_func cb); - -LWS_VISIBLE LWS_EXTERN int -lws_context_is_deprecated(struct lws_context *context); - -/** - * lws_set_proxy() - Setups proxy to lws_context. - * \param vhost: pointer to struct lws_vhost you want set proxy for - * \param proxy: pointer to c string containing proxy in format address:port - * - * Returns 0 if proxy string was parsed and proxy was setup. - * Returns -1 if proxy is NULL or has incorrect format. - * - * This is only required if your OS does not provide the http_proxy - * environment variable (eg, OSX) - * - * IMPORTANT! You should call this function right after creation of the - * lws_context and before call to connect. If you call this - * function after connect behavior is undefined. - * This function will override proxy settings made on lws_context - * creation with genenv() call. - */ -LWS_VISIBLE LWS_EXTERN int -lws_set_proxy(struct lws_vhost *vhost, const char *proxy); - -/** - * lws_set_socks() - Setup socks to lws_context. - * \param vhost: pointer to struct lws_vhost you want set socks for - * \param socks: pointer to c string containing socks in format address:port - * - * Returns 0 if socks string was parsed and socks was setup. - * Returns -1 if socks is NULL or has incorrect format. - * - * This is only required if your OS does not provide the socks_proxy - * environment variable (eg, OSX) - * - * IMPORTANT! You should call this function right after creation of the - * lws_context and before call to connect. If you call this - * function after connect behavior is undefined. - * This function will override proxy settings made on lws_context - * creation with genenv() call. - */ -LWS_VISIBLE LWS_EXTERN int -lws_set_socks(struct lws_vhost *vhost, const char *socks); - -struct lws_vhost; - -/** - * lws_create_vhost() - Create a vhost (virtual server context) - * \param context: pointer to result of lws_create_context() - * \param info: pointer to struct with parameters - * - * This function creates a virtual server (vhost) using the vhost-related - * members of the info struct. You can create many vhosts inside one context - * if you created the context with the option LWS_SERVER_OPTION_EXPLICIT_VHOSTS - */ -LWS_VISIBLE LWS_EXTERN struct lws_vhost * -lws_create_vhost(struct lws_context *context, - const struct lws_context_creation_info *info); - -/** - * lws_vhost_destroy() - Destroy a vhost (virtual server context) - * - * \param vh: pointer to result of lws_create_vhost() - * - * This function destroys a vhost. Normally, if you just want to exit, - * then lws_destroy_context() will take care of everything. If you want - * to destroy an individual vhost and all connections and allocations, you - * can do it with this. - * - * If the vhost has a listen sockets shared by other vhosts, it will be given - * to one of the vhosts sharing it rather than closed. - * - * The vhost close is staged according to the needs of the event loop, and if - * there are multiple service threads. At the point the vhost itself if - * about to be freed, if you provided a finalize callback and optional arg at - * vhost creation time, it will be called just before the vhost is freed. - */ -LWS_VISIBLE LWS_EXTERN void -lws_vhost_destroy(struct lws_vhost *vh); - -/** - * lwsws_get_config_globals() - Parse a JSON server config file - * \param info: pointer to struct with parameters - * \param d: filepath of the config file - * \param config_strings: storage for the config strings extracted from JSON, - * the pointer is incremented as strings are stored - * \param len: pointer to the remaining length left in config_strings - * the value is decremented as strings are stored - * - * This function prepares a n lws_context_creation_info struct with global - * settings from a file d. - * - * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled - */ -LWS_VISIBLE LWS_EXTERN int -lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d, - char **config_strings, int *len); - -/** - * lwsws_get_config_vhosts() - Create vhosts from a JSON server config file - * \param context: pointer to result of lws_create_context() - * \param info: pointer to struct with parameters - * \param d: filepath of the config file - * \param config_strings: storage for the config strings extracted from JSON, - * the pointer is incremented as strings are stored - * \param len: pointer to the remaining length left in config_strings - * the value is decremented as strings are stored - * - * This function creates vhosts into a context according to the settings in - *JSON files found in directory d. - * - * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled - */ -LWS_VISIBLE LWS_EXTERN int -lwsws_get_config_vhosts(struct lws_context *context, - struct lws_context_creation_info *info, const char *d, - char **config_strings, int *len); - -/** - * lws_get_vhost() - return the vhost a wsi belongs to - * - * \param wsi: which connection - */ -LWS_VISIBLE LWS_EXTERN struct lws_vhost * -lws_get_vhost(struct lws *wsi); - -/** - * lws_get_vhost_name() - returns the name of a vhost - * - * \param vhost: which vhost - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_get_vhost_name(struct lws_vhost *vhost); - -/** - * lws_get_vhost_by_name() - returns the vhost with the requested name, or NULL - * - * \param context: the lws_context to look in - * \param name: vhost name we are looking for - * - * Returns NULL, or the vhost with the name \p name - */ -LWS_VISIBLE LWS_EXTERN struct lws_vhost * -lws_get_vhost_by_name(struct lws_context *context, const char *name); - -/** - * lws_get_vhost_port() - returns the port a vhost listens on, or -1 - * - * \param vhost: which vhost - */ -LWS_VISIBLE LWS_EXTERN int -lws_get_vhost_port(struct lws_vhost *vhost); - -/** - * lws_get_vhost_user() - returns the user pointer for the vhost - * - * \param vhost: which vhost - */ -LWS_VISIBLE LWS_EXTERN void * -lws_get_vhost_user(struct lws_vhost *vhost); - -/** - * lws_get_vhost_iface() - returns the binding for the vhost listen socket - * - * \param vhost: which vhost - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_get_vhost_iface(struct lws_vhost *vhost); - -/** - * lws_json_dump_vhost() - describe vhost state and stats in JSON - * - * \param vh: the vhost - * \param buf: buffer to fill with JSON - * \param len: max length of buf - */ -LWS_VISIBLE LWS_EXTERN int -lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len); - -/** - * lws_json_dump_context() - describe context state and stats in JSON - * - * \param context: the context - * \param buf: buffer to fill with JSON - * \param len: max length of buf - * \param hide_vhosts: nonzero to not provide per-vhost mount etc information - * - * Generates a JSON description of vhost state into buf - */ -LWS_VISIBLE LWS_EXTERN int -lws_json_dump_context(const struct lws_context *context, char *buf, int len, - int hide_vhosts); - -/** - * lws_vhost_user() - get the user data associated with the vhost - * \param vhost: Websocket vhost - * - * This returns the optional user pointer that can be attached to - * a vhost when it was created. Lws never dereferences this pointer, it only - * sets it when the vhost is created, and returns it using this api. - */ -LWS_VISIBLE LWS_EXTERN void * -lws_vhost_user(struct lws_vhost *vhost); - -/** - * lws_context_user() - get the user data associated with the context - * \param context: Websocket context - * - * This returns the optional user allocation that can be attached to - * the context the sockets live in at context_create time. It's a way - * to let all sockets serviced in the same context share data without - * using globals statics in the user code. - */ -LWS_VISIBLE LWS_EXTERN void * -lws_context_user(struct lws_context *context); - -/*! \defgroup vhost-mounts Vhost mounts and options - * \ingroup context-and-vhost-creation - * - * ##Vhost mounts and options - */ -///@{ -/** struct lws_protocol_vhost_options - linked list of per-vhost protocol - * name=value options - * - * This provides a general way to attach a linked-list of name=value pairs, - * which can also have an optional child link-list using the options member. - */ -struct lws_protocol_vhost_options { - const struct lws_protocol_vhost_options *next; /**< linked list */ - const struct lws_protocol_vhost_options *options; /**< child linked-list of more options for this node */ - const char *name; /**< name of name=value pair */ - const char *value; /**< value of name=value pair */ -}; - -/** enum lws_mount_protocols - * This specifies the mount protocol for a mountpoint, whether it is to be - * served from a filesystem, or it is a cgi etc. - */ -enum lws_mount_protocols { - LWSMPRO_HTTP = 0, /**< http reverse proxy */ - LWSMPRO_HTTPS = 1, /**< https reverse proxy */ - LWSMPRO_FILE = 2, /**< serve from filesystem directory */ - LWSMPRO_CGI = 3, /**< pass to CGI to handle */ - LWSMPRO_REDIR_HTTP = 4, /**< redirect to http:// url */ - LWSMPRO_REDIR_HTTPS = 5, /**< redirect to https:// url */ - LWSMPRO_CALLBACK = 6, /**< hand by named protocol's callback */ -}; - -/** enum lws_authentication_mode - * This specifies the authentication mode of the mount. The basic_auth_login_file mount parameter - * is ignored unless LWSAUTHM_DEFAULT is set. - */ -enum lws_authentication_mode { - LWSAUTHM_DEFAULT = 0, /**< default authenticate only if basic_auth_login_file is provided */ - LWSAUTHM_BASIC_AUTH_CALLBACK = 1 << 28 /**< Basic auth with a custom verifier */ -}; - -/** The authentication mode is stored in the top 4 bits of lws_http_mount.auth_mask */ -#define AUTH_MODE_MASK 0xF0000000 - -/** struct lws_http_mount - * - * arguments for mounting something in a vhost's url namespace - */ -struct lws_http_mount { - const struct lws_http_mount *mount_next; - /**< pointer to next struct lws_http_mount */ - const char *mountpoint; - /**< mountpoint in http pathspace, eg, "/" */ - const char *origin; - /**< path to be mounted, eg, "/var/www/warmcat.com" */ - const char *def; - /**< default target, eg, "index.html" */ - const char *protocol; - /**<"protocol-name" to handle mount */ - - const struct lws_protocol_vhost_options *cgienv; - /**< optional linked-list of cgi options. These are created - * as environment variables for the cgi process - */ - const struct lws_protocol_vhost_options *extra_mimetypes; - /**< optional linked-list of mimetype mappings */ - const struct lws_protocol_vhost_options *interpret; - /**< optional linked-list of files to be interpreted */ - - int cgi_timeout; - /**< seconds cgi is allowed to live, if cgi://mount type */ - int cache_max_age; - /**< max-age for reuse of client cache of files, seconds */ - unsigned int auth_mask; - /**< bits set here must be set for authorized client session */ - - unsigned int cache_reusable:1; /**< set if client cache may reuse this */ - unsigned int cache_revalidate:1; /**< set if client cache should revalidate on use */ - unsigned int cache_intermediaries:1; /**< set if intermediaries are allowed to cache */ - - unsigned char origin_protocol; /**< one of enum lws_mount_protocols */ - unsigned char mountpoint_len; /**< length of mountpoint string */ - - const char *basic_auth_login_file; - /** - * - * 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. - * - * must be included manually as - * - * #include - * - * if dbus apis needed - */ - -#if !defined(__LWS_DBUS_H__) -#define __LWS_DBUS_H__ - -#include - -/* helper type to simplify implementing methods as individual functions */ -typedef DBusHandlerResult (*lws_dbus_message_handler)(DBusConnection *conn, - DBusMessage *message, DBusMessage **reply, void *d); - -struct lws_dbus_ctx; -typedef void (*lws_dbus_closing_t)(struct lws_dbus_ctx *ctx); - -struct lws_dbus_ctx { - struct lws_dll2_owner owner; /* dbusserver ctx: HEAD of accepted list */ - struct lws_dll2 next; /* dbusserver ctx: HEAD of accepted list */ - struct lws_vhost *vh; /* the vhost we logically bind to in lws */ - int tsi; /* the lws thread service index (0 if only one service - thread as is the default */ - DBusConnection *conn; - DBusServer *dbs; - DBusWatch *w[4]; - DBusPendingCall *pc; - - char hup; - char timeouts; - - /* cb_closing callback will be called after the connection and this - * related ctx struct have effectively gone out of scope. - * - * The callback should close and clean up the connection and free the - * ctx. - */ - lws_dbus_closing_t cb_closing; -}; - -/** - * lws_dbus_connection_setup() - bind dbus connection object to lws event loop - * - * \param ctx: additional information about the connection - * \param conn: the DBusConnection object to bind - * - * This configures a DBusConnection object to use lws for watchers and timeout - * operations. - */ -LWS_VISIBLE LWS_EXTERN int -lws_dbus_connection_setup(struct lws_dbus_ctx *ctx, DBusConnection *conn, - lws_dbus_closing_t cb_closing); - -/** - * lws_dbus_server_listen() - bind dbus connection object to lws event loop - * - * \param ctx: additional information about the connection - * \param ads: the DBUS address to listen on, eg, "unix:abstract=mysocket" - * \param err: a DBusError object to take any extra error information - * \param new_conn: a callback function to prepare new accepted connections - * - * This creates a DBusServer and binds it to the lws event loop, and your - * callback to accept new connections. - */ -LWS_VISIBLE LWS_EXTERN DBusServer * -lws_dbus_server_listen(struct lws_dbus_ctx *ctx, const char *ads, - DBusError *err, DBusNewConnectionFunction new_conn); - -#endif diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-detailed-latency.h libwebsockets-2.4.2/include/libwebsockets/lws-detailed-latency.h --- libwebsockets-4.0.20/include/libwebsockets/lws-detailed-latency.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-detailed-latency.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,140 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * included from libwebsockets.h - */ - -enum { - - /* types of latency, all nonblocking except name resolution */ - - LDLT_READ, /* time taken to read LAT_DUR_PROXY_RX_TO_CLIENT_WRITE */ - LDLT_WRITE, - LDLT_NAME_RESOLUTION, /* BLOCKING: LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE */ - LDLT_CONNECTION, /* conn duration: LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE */ - LDLT_TLS_NEG_CLIENT, /* tls conn duration: LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE */ - LDLT_TLS_NEG_SERVER, /* tls conn duration: LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE */ - - LDLT_USER, - - /* interval / duration elements in latencies array */ - - LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE = 0, - /* us the client spent waiting to write to proxy */ - LAT_DUR_PROXY_CLIENT_WRITE_TO_PROXY_RX, - /* us the packet took to be received by proxy */ - LAT_DUR_PROXY_PROXY_REQ_TO_WRITE, - /* us the proxy has to wait before it could write */ - LAT_DUR_PROXY_RX_TO_ONWARD_TX, - /* us the proxy spent waiting to write to destination, or - * if nonproxied, then time between write request and write */ - - LAT_DUR_USERCB, /* us duration of user callback */ - - LAT_DUR_STEPS /* last */ -}; - -typedef struct lws_detlat { - lws_usec_t earliest_write_req; - lws_usec_t earliest_write_req_pre_write; - /**< use this for interval comparison */ - const char *aux; /* name for name resolution timing */ - int type; - uint32_t latencies[LAT_DUR_STEPS]; - size_t req_size; - size_t acc_size; -} lws_detlat_t; - -typedef int (*det_lat_buf_cb_t)(struct lws_context *context, - const lws_detlat_t *d); - -/** - * lws_det_lat_cb() - inject your own latency records - * - * \param context: the lws_context - * \param d: the lws_detlat_t you have prepared - * - * For proxying or similar cases where latency information is available from - * user code rather than lws itself, you can generate your own latency callback - * events with your own lws_detlat_t. - */ - -LWS_VISIBLE LWS_EXTERN int -lws_det_lat_cb(struct lws_context *context, lws_detlat_t *d); - -/* - * detailed_latency_plot_cb() - canned save to file in plottable format cb - * - * \p context: the lws_context - * \p d: the detailed latency event information - * - * This canned callback makes it easy to export the detailed latency information - * to a file. Just set the context creation members like this - * - * #if defined(LWS_WITH_DETAILED_LATENCY) - * info.detailed_latency_cb = lws_det_lat_plot_cb; - * info.detailed_latency_filepath = "/tmp/lws-latency-results"; - * #endif - * - * and you will get a file containing information like this - * - * 718823864615 N 10589 0 0 10589 0 0 0 - * 718823880837 C 16173 0 0 16173 0 0 0 - * 718823913063 T 32212 0 0 32212 0 0 0 - * 718823931835 r 0 0 0 0 232 30 256 - * 718823948757 r 0 0 0 0 40 30 256 - * 718823948799 r 0 0 0 0 83 30 256 - * 718823965602 r 0 0 0 0 27 30 256 - * 718823965617 r 0 0 0 0 43 30 256 - * 718823965998 r 0 0 0 0 12 28 256 - * 718823983887 r 0 0 0 0 74 3 4096 - * 718823986411 w 16 87 7 110 9 80 80 - * 718824006358 w 8 68 6 82 6 80 80 - * - * which is easy to grep and pass to gnuplot. - * - * The columns are - * - * - unix time in us - * - N = Name resolution, C = TCP Connection, T = TLS negotiation server, - * t = TLS negotiation client, r = Read, w = Write - * - us duration, for w time client spent waiting to write - * - us duration, for w time data spent in transit to proxy - * - us duration, for w time proxy waited to send data - * - as a convenience, sum of last 3 columns above - * - us duration, time spent in callback - * - last 2 are actual / requested size in bytes - */ -LWS_VISIBLE LWS_EXTERN int -lws_det_lat_plot_cb(struct lws_context *context, const lws_detlat_t *d); - -/** - * lws_det_lat_active() - indicates if latencies are being measured - * - * \context: lws_context - * - * Returns 0 if latency measurement has not been set up (the callback is NULL). - * Otherwise returns 1 - */ -LWS_VISIBLE LWS_EXTERN int -lws_det_lat_active(struct lws_context *context); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-diskcache.h libwebsockets-2.4.2/include/libwebsockets/lws-diskcache.h --- libwebsockets-4.0.20/include/libwebsockets/lws-diskcache.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-diskcache.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,187 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup diskcache LWS disk cache - * ## Disk cache API - * - * Lws provides helper apis useful if you need a disk cache containing hashed - * files and need to delete files from it on an LRU basis to keep it below some - * size limit. - * - * The API `lws_diskcache_prepare()` deals with creating the cache dir and - * 256 subdirs, which are used according to the first two chars of the hex - * hash of the cache file. - * - * `lws_diskcache_create()` and `lws_diskcache_destroy()` allocate and free - * an opaque struct that represents the disk cache. - * - * `lws_diskcache_trim()` should be called at eg, 1s intervals to perform the - * cache dir monitoring and LRU autodelete in the background lazily. It can - * be done in its own thread or on a timer... it monitors the directories in a - * stateful way that stats one or more file in the cache per call, and keeps - * a list of the oldest files as it goes. When it completes a scan, if the - * aggregate size is over the limit, it will delete oldest files first to try - * to keep it under the limit. - * - * The cache size monitoring is extremely efficient in time and memory even when - * the cache directory becomes huge. - * - * `lws_diskcache_query()` is used to determine if the file already exists in - * the cache, or if it must be created. If it must be created, then the file - * is opened using a temp name that must be converted to a findable name with - * `lws_diskcache_finalize_name()` when the generation of the file contents are - * complete. Aborted cached files that did not complete generation will be - * flushed by the LRU eventually. If the file already exists, it is 'touched' - * to make it new again and the fd returned. - * - */ -///@{ - -struct lws_diskcache_scan; - -/** - * lws_diskcache_create() - creates an opaque struct representing the disk cache - * - * \param cache_dir_base: The cache dir path, eg `/var/cache/mycache` - * \param cache_size_limit: maximum size on disk the cache is allowed to use - * - * This returns an opaque `struct lws_diskcache_scan *` which represents the - * disk cache, the trim scanning state and so on. You should use - * `lws_diskcache_destroy()` to free it to destroy it. - */ -LWS_VISIBLE LWS_EXTERN struct lws_diskcache_scan * -lws_diskcache_create(const char *cache_dir_base, uint64_t cache_size_limit); - -/** - * lws_diskcache_destroy() - destroys the pointer returned by ...create() - * - * \param lds: pointer to the pointer returned by lws_diskcache_create() - * - * Frees *lds and any allocations it did, and then sets *lds to NULL and - * returns. - */ -LWS_VISIBLE LWS_EXTERN void -lws_diskcache_destroy(struct lws_diskcache_scan **lds); - -/** - * lws_diskcache_prepare() - ensures the cache dir structure exists on disk - * - * \param cache_base_dir: The cache dir path, eg `/var/cache/mycache` - * \param mode: octal dir mode to enforce, like 0700 - * \param uid: uid the cache dir should belong to - * - * This should be called while your app is still privileged. It will create - * the cache directory structure on disk as necessary, enforce the given access - * mode on it and set the given uid as the owner. It won't make any trouble - * if the cache already exists. - * - * Typically the mode is 0700 and the owner is the user that your application - * will transition to use when it drops root privileges. - */ -LWS_VISIBLE LWS_EXTERN int -lws_diskcache_prepare(const char *cache_base_dir, int mode, int uid); - -#define LWS_DISKCACHE_QUERY_NO_CACHE 0 -#define LWS_DISKCACHE_QUERY_EXISTS 1 -#define LWS_DISKCACHE_QUERY_CREATING 2 -#define LWS_DISKCACHE_QUERY_ONGOING 3 /* something else is creating it */ - -/** - * lws_diskcache_query() - ensures the cache dir structure exists on disk - * - * \param lds: The opaque struct representing the disk cache - * \param is_bot: nonzero means the request is from a bot. Don't create new cache contents if so. - * \param hash_hex: hex string representation of the cache object hash - * \param _fd: pointer to the fd to be set - * \param cache: destination string to take the cache filepath - * \param cache_len: length of the buffer at `cache` - * \param extant_cache_len: pointer to a size_t to take any extant cached file size - * - * This function is called when you want to find if the hashed name already - * exists in the cache. The possibilities for the return value are - * - * - LWS_DISKCACHE_QUERY_NO_CACHE: It's not in the cache and you can't create - * it in the cache for whatever reason. - * - LWS_DISKCACHE_QUERY_EXISTS: It exists in the cache. It's open RDONLY and - * *_fd has been set to the file descriptor. *extant_cache_len has been set - * to the size of the cached file in bytes. cache has been set to the - * full filepath of the cached file. Closing _fd is your responsibility. - * - LWS_DISKCACHE_QUERY_CREATING: It didn't exist, but a temp file has been - * created in the cache and *_fd set to a file descriptor opened on it RDWR. - * You should create the contents, and call `lws_diskcache_finalize_name()` - * when it is done. Closing _fd is your responsibility. - * - LWS_DISKCACHE_QUERY_ONGOING: not returned by this api, but you may find it - * desirable to make a wrapper function which can handle another asynchronous - * process that is already creating the cached file. This can be used to - * indicate that situation externally... how to determine the same thing is - * already being generated is out of scope of this api. - */ -LWS_VISIBLE LWS_EXTERN int -lws_diskcache_query(struct lws_diskcache_scan *lds, int is_bot, - const char *hash_hex, int *_fd, char *cache, int cache_len, - size_t *extant_cache_len); - -/** - * lws_diskcache_query() - ensures the cache dir structure exists on disk - * - * \param cache: The cache file temp name returned with LWS_DISKCACHE_QUERY_CREATING - * - * This renames the cache file you are creating to its final name. It should - * be called on the temp name returned by `lws_diskcache_query()` if it gave a - * LWS_DISKCACHE_QUERY_CREATING return, after you have filled the cache file and - * closed it. - */ -LWS_VISIBLE LWS_EXTERN int -lws_diskcache_finalize_name(char *cache); - -/** - * lws_diskcache_trim() - performs one or more file checks in the cache for size management - * - * \param lds: The opaque object representing the cache - * - * This should be called periodically to statefully walk the cache on disk - * collecting the oldest files. When it has visited every file, if the cache - * is oversize it will delete the oldest files until it's back under size again. - * - * Each time it's called, it will look at one or more dir in the cache. If - * called when the cache is oversize, it increases the amount of work done each - * call until it is reduced again. Typically it will take 256 calls before it - * deletes anything, so if called once per second, it will delete files once - * every 4 minutes. Each call is very inexpensive both in memory and time. - */ -LWS_VISIBLE LWS_EXTERN int -lws_diskcache_trim(struct lws_diskcache_scan *lds); - - -/** - * lws_diskcache_secs_to_idle() - see how long to idle before calling trim - * - * \param lds: The opaque object representing the cache - * - * If the cache is undersize, there's no need to monitor it immediately. This - * suggests how long to "sleep" before calling `lws_diskcache_trim()` again. - */ -LWS_VISIBLE LWS_EXTERN int -lws_diskcache_secs_to_idle(struct lws_diskcache_scan *lds); -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-dll2.h libwebsockets-2.4.2/include/libwebsockets/lws-dll2.h --- libwebsockets-4.0.20/include/libwebsockets/lws-dll2.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-dll2.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,341 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup ll linked-lists -* ##Linked list apis -* -* simple single and doubly-linked lists -*/ -///@{ - -/** - * lws_start_foreach_ll(): linkedlist iterator helper start - * - * \param type: type of iteration, eg, struct xyz * - * \param it: iterator var name to create - * \param start: start of list - * - * This helper creates an iterator and starts a while (it) { - * loop. The iterator runs through the linked list starting at start and - * ends when it gets a NULL. - * The while loop should be terminated using lws_start_foreach_ll(). - */ -#define lws_start_foreach_ll(type, it, start)\ -{ \ - type it = start; \ - while (it) { - -/** - * lws_end_foreach_ll(): linkedlist iterator helper end - * - * \param it: same iterator var name given when starting - * \param nxt: member name in the iterator pointing to next list element - * - * This helper is the partner for lws_start_foreach_ll() that ends the - * while loop. - */ - -#define lws_end_foreach_ll(it, nxt) \ - it = it->nxt; \ - } \ -} - -/** - * lws_start_foreach_ll_safe(): linkedlist iterator helper start safe against delete - * - * \param type: type of iteration, eg, struct xyz * - * \param it: iterator var name to create - * \param start: start of list - * \param nxt: member name in the iterator pointing to next list element - * - * This helper creates an iterator and starts a while (it) { - * loop. The iterator runs through the linked list starting at start and - * ends when it gets a NULL. - * The while loop should be terminated using lws_end_foreach_ll_safe(). - * Performs storage of next increment for situations where iterator can become invalidated - * during iteration. - */ -#define lws_start_foreach_ll_safe(type, it, start, nxt)\ -{ \ - type it = start; \ - while (it) { \ - type next_##it = it->nxt; - -/** - * lws_end_foreach_ll_safe(): linkedlist iterator helper end (pre increment storage) - * - * \param it: same iterator var name given when starting - * - * This helper is the partner for lws_start_foreach_ll_safe() that ends the - * while loop. It uses the precreated next_ variable already stored during - * start. - */ - -#define lws_end_foreach_ll_safe(it) \ - it = next_##it; \ - } \ -} - -/** - * lws_start_foreach_llp(): linkedlist pointer iterator helper start - * - * \param type: type of iteration, eg, struct xyz ** - * \param it: iterator var name to create - * \param start: start of list - * - * This helper creates an iterator and starts a while (it) { - * loop. The iterator runs through the linked list starting at the - * address of start and ends when it gets a NULL. - * The while loop should be terminated using lws_start_foreach_llp(). - * - * This helper variant iterates using a pointer to the previous linked-list - * element. That allows you to easily delete list members by rewriting the - * previous pointer to the element's next pointer. - */ -#define lws_start_foreach_llp(type, it, start)\ -{ \ - type it = &(start); \ - while (*(it)) { - -#define lws_start_foreach_llp_safe(type, it, start, nxt)\ -{ \ - type it = &(start); \ - type next; \ - while (*(it)) { \ - next = &((*(it))->nxt); \ - -/** - * lws_end_foreach_llp(): linkedlist pointer iterator helper end - * - * \param it: same iterator var name given when starting - * \param nxt: member name in the iterator pointing to next list element - * - * This helper is the partner for lws_start_foreach_llp() that ends the - * while loop. - */ - -#define lws_end_foreach_llp(it, nxt) \ - it = &(*(it))->nxt; \ - } \ -} - -#define lws_end_foreach_llp_safe(it) \ - it = next; \ - } \ -} - -#define lws_ll_fwd_insert(\ - ___new_object, /* pointer to new object */ \ - ___m_list, /* member for next list object ptr */ \ - ___list_head /* list head */ \ - ) {\ - ___new_object->___m_list = ___list_head; \ - ___list_head = ___new_object; \ - } - -#define lws_ll_fwd_remove(\ - ___type, /* type of listed object */ \ - ___m_list, /* member for next list object ptr */ \ - ___target, /* object to remove from list */ \ - ___list_head /* list head */ \ - ) { \ - lws_start_foreach_llp(___type **, ___ppss, ___list_head) { \ - if (*___ppss == ___target) { \ - *___ppss = ___target->___m_list; \ - break; \ - } \ - } lws_end_foreach_llp(___ppss, ___m_list); \ - } - - -/* - * doubly linked-list - */ - -#if defined (LWS_WITH_DEPRECATED_LWS_DLL) - -/* - * This is going away in v4.1. You can set the cmake option above to keep it - * around temporarily. Migrate your stuff to the more capable and robust - * lws_dll2 below - */ - -struct lws_dll { - struct lws_dll *prev; - struct lws_dll *next; -}; - -/* - * these all point to the composed list objects... you have to use the - * lws_container_of() helper to recover the start of the containing struct - */ - -#define lws_dll_add_front lws_dll_add_head - -LWS_VISIBLE LWS_EXTERN void -lws_dll_add_head(struct lws_dll *d, struct lws_dll *phead); - -LWS_VISIBLE LWS_EXTERN void -lws_dll_add_tail(struct lws_dll *d, struct lws_dll *phead); - -LWS_VISIBLE LWS_EXTERN void -lws_dll_insert(struct lws_dll *d, struct lws_dll *target, - struct lws_dll *phead, int before); - -static LWS_INLINE struct lws_dll * -lws_dll_get_head(struct lws_dll *phead) { return phead->next; } - -static LWS_INLINE struct lws_dll * -lws_dll_get_tail(struct lws_dll *phead) { return phead->prev; } - -/* - * caution, this doesn't track the tail in the head struct. Use - * lws_dll_remove_track_tail() instead of this if you want tail tracking. Using - * this means you can't use lws_dll_add_tail() amd - */ -LWS_VISIBLE LWS_EXTERN void -lws_dll_remove(struct lws_dll *d) LWS_WARN_DEPRECATED; - -LWS_VISIBLE LWS_EXTERN void -lws_dll_remove_track_tail(struct lws_dll *d, struct lws_dll *phead); - -/* another way to do lws_start_foreach_dll_safe() on a list via a cb */ - -LWS_VISIBLE LWS_EXTERN int -lws_dll_foreach_safe(struct lws_dll *phead, void *user, - int (*cb)(struct lws_dll *d, void *user)); - -#define lws_dll_is_detached(___dll, __head) \ - (!(___dll)->prev && !(___dll)->next && (__head)->prev != (___dll)) - -#endif - -/* - * lws_dll2_owner / lws_dll2 : more capable version of lws_dll. Differences: - * - * - there's an explicit lws_dll2_owner struct which holds head, tail and - * count of members. - * - * - list members all hold a pointer to their owner. So user code does not - * have to track anything about exactly what lws_dll2_owner list the object - * is a member of. - * - * - you can use lws_dll unless you want the member count or the ability to - * not track exactly which list it's on. - * - * - layout is compatible with lws_dll (but lws_dll apis will not update the - * new stuff) - */ - - -struct lws_dll2; -struct lws_dll2_owner; - -typedef struct lws_dll2 { - struct lws_dll2 *prev; - struct lws_dll2 *next; - struct lws_dll2_owner *owner; -} lws_dll2_t; - -typedef struct lws_dll2_owner { - struct lws_dll2 *tail; - struct lws_dll2 *head; - - uint32_t count; -} lws_dll2_owner_t; - -static LWS_INLINE int -lws_dll2_is_detached(const struct lws_dll2 *d) { return !d->owner; } - -static LWS_INLINE const struct lws_dll2_owner * -lws_dll2_owner(const struct lws_dll2 *d) { return d->owner; } - -static LWS_INLINE struct lws_dll2 * -lws_dll2_get_head(struct lws_dll2_owner *owner) { return owner->head; } - -static LWS_INLINE struct lws_dll2 * -lws_dll2_get_tail(struct lws_dll2_owner *owner) { return owner->tail; } - -LWS_VISIBLE LWS_EXTERN void -lws_dll2_add_head(struct lws_dll2 *d, struct lws_dll2_owner *owner); - -LWS_VISIBLE LWS_EXTERN void -lws_dll2_add_tail(struct lws_dll2 *d, struct lws_dll2_owner *owner); - -LWS_VISIBLE LWS_EXTERN void -lws_dll2_remove(struct lws_dll2 *d); - -LWS_VISIBLE LWS_EXTERN int -lws_dll2_foreach_safe(struct lws_dll2_owner *owner, void *user, - int (*cb)(struct lws_dll2 *d, void *user)); - -LWS_VISIBLE LWS_EXTERN void -lws_dll2_clear(struct lws_dll2 *d); - -LWS_VISIBLE LWS_EXTERN void -lws_dll2_owner_clear(struct lws_dll2_owner *d); - -LWS_VISIBLE LWS_EXTERN void -lws_dll2_add_before(struct lws_dll2 *d, struct lws_dll2 *after); - -LWS_VISIBLE LWS_EXTERN void -lws_dll2_add_sorted(lws_dll2_t *d, lws_dll2_owner_t *own, - int (*compare)(const lws_dll2_t *d, const lws_dll2_t *i)); - -#if defined(_DEBUG) -void -lws_dll2_describe(struct lws_dll2_owner *owner, const char *desc); -#else -#define lws_dll2_describe(x, y) -#endif - -/* - * these are safe against the current container object getting deleted, - * since the hold his next in a temp and go to that next. ___tmp is - * the temp. - */ - -#define lws_start_foreach_dll_safe(___type, ___it, ___tmp, ___start) \ -{ \ - ___type ___it = ___start; \ - while (___it) { \ - ___type ___tmp = (___it)->next; - -#define lws_end_foreach_dll_safe(___it, ___tmp) \ - ___it = ___tmp; \ - } \ -} - -#define lws_start_foreach_dll(___type, ___it, ___start) \ -{ \ - ___type ___it = ___start; \ - while (___it) { - -#define lws_end_foreach_dll(___it) \ - ___it = (___it)->next; \ - } \ -} - -///@} - diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-dsh.h libwebsockets-2.4.2/include/libwebsockets/lws-dsh.h --- libwebsockets-4.0.20/include/libwebsockets/lws-dsh.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-dsh.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,145 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/* - * lws_dsh (Disordered Shared Heap) is an opaque abstraction supporting a single - * linear buffer (overallocated at end of the lws_dsh_t) which may contain - * multiple kinds of packets that are retired out of order, and tracked by kind. - * - * Each kind of packet has an lws_dll2 list of its kind of packets and acts as - * a FIFO; packets of a particular type are always retired in order. But there - * is no requirement about the order types are retired matching the original - * order they arrived. - * - * Gaps are tracked as just another kind of "packet" list. - * - * "allocations" (including gaps) are prepended by an lws_dsh_object_t. - * - * dsh may themselves be on an lws_dll2_owner list, and under memory pressure - * allocate into other buffers on the list. - * - * All management structures exist inside the allocated buffer. - */ - -/** - * lws_dsh_create() - Allocate a DSH buffer - * - * \param owner: the owning list this dsh belongs on, or NULL if standalone - * \param buffer_size: the allocation in bytes - * \param count_kinds: how many separately-tracked fifos use the buffer - * - * This makes a single heap allocation that includes internal tracking objects - * in the buffer. Sub-allocated objects are bound to a "kind" index and - * managed via a FIFO for each kind. - * - * Every "kind" of allocation shares the same buffer space. - * - * Multiple buffers may be bound together in an lws_dll2 list, and if an - * allocation cannot be satisfied by the local buffer, space can be borrowed - * from other dsh in the same list (the local dsh FIFO tracks these "foreign" - * allocations as if they were local). - * - * Returns an opaque pointer to the dsh, or NULL if allocation failed. - */ -LWS_VISIBLE LWS_EXTERN struct lws_dsh * -lws_dsh_create(lws_dll2_owner_t *owner, size_t buffer_size, int count_kinds); - -/** - * lws_dsh_destroy() - Destroy a DSH buffer - * - * \param pdsh: pointer to the dsh pointer - * - * Deallocates the DSH and sets *pdsh to NULL. - * - * Before destruction, any foreign buffer usage on the part of this dsh are - * individually freed. All dsh on the same list are walked and checked if they - * have their own foreign allocations on the dsh buffer being destroyed. If so, - * it attempts to migrate the allocation to a dsh that is not currently being - * destroyed. If all else fails (basically the buffer memory is being shrunk) - * unmigratable objects are cleanly destroyed. - */ -LWS_VISIBLE LWS_EXTERN void -lws_dsh_destroy(struct lws_dsh **pdsh); - -/** - * lws_dsh_alloc_tail() - make a suballocation inside a dsh - * - * \param dsh: the dsh tracking the allocation - * \param kind: the kind of allocation - * \param src1: the first source data to copy - * \param size1: the size of the first source data - * \param src2: the second source data to copy (after the first), or NULL - * \param size2: the size of the second source data - * - * Allocates size1 + size2 bytes in a dsh (it prefers the given dsh but will - * borrow space from other dsh on the same list if necessary) and copies size1 - * bytes into it from src1, followed by size2 bytes from src2 if src2 isn't - * NULL. The actual suballocation is a bit larger because of alignment and a - * prepended management header. - * - * The suballocation is added to the kind-specific FIFO at the tail. - */ -LWS_VISIBLE LWS_EXTERN int -lws_dsh_alloc_tail(struct lws_dsh *dsh, int kind, const void *src1, - size_t size1, const void *src2, size_t size2); - -/** - * lws_dsh_free() - free a suballocation from the dsh - * - * \param obj: a pointer to a void * that pointed to the allocated payload - * - * This returns the space used by \p obj in the dsh buffer to the free list - * of the dsh the allocation came from. - */ -LWS_VISIBLE LWS_EXTERN void -lws_dsh_free(void **obj); - -/** - * lws_dsh_get_head() - get the head allocation inside the dsh - * - * \param dsh: the dsh tracking the allocation - * \param kind: the kind of allocation - * \param obj: pointer to a void * to be set to the payload - * \param size: set to the size of the allocation - * - * This gets the "next" object in the kind FIFO for the dsh, and returns 0 if - * any. If none, returns nonzero. - * - * This is nondestructive of the fifo or the payload. Use lws_dsh_free on - * obj to remove the entry from the kind fifo and return the payload to the - * free list. - */ -LWS_VISIBLE LWS_EXTERN int -lws_dsh_get_head(struct lws_dsh *dsh, int kind, void **obj, size_t *size); - -/** - * lws_dsh_describe() - DEBUG BUILDS ONLY dump the dsh to the logs - * - * \param dsh: the dsh to dump - * \param desc: text that appears at the top of the dump - * - * Useful information for debugging lws_dsh - */ -LWS_VISIBLE LWS_EXTERN void -lws_dsh_describe(struct lws_dsh *dsh, const char *desc); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-esp32.h libwebsockets-2.4.2/include/libwebsockets/lws-esp32.h --- libwebsockets-4.0.20/include/libwebsockets/lws-esp32.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-esp32.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,153 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 only included from libwebsockets.h if LWS_WITH_ESP32 - */ - -/* ESP32 helper declarations */ - -#include -#include - -#define LWS_PLUGIN_STATIC -#define LWS_MAGIC_REBOOT_TYPE_ADS 0x50001ffc -#define LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY 0xb00bcafe -#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY 0xfaceb00b -#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON 0xf0cedfac -#define LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY_ERASE_OTA 0xfac0eeee - -/* user code provides these */ - -extern void -lws_esp32_identify_physical_device(void); - -/* lws-plat-esp32 provides these */ - -typedef void (*lws_cb_scan_done)(uint16_t count, wifi_ap_record_t *recs, void *arg); - -enum genled_state { - LWSESP32_GENLED__INIT, - LWSESP32_GENLED__LOST_NETWORK, - LWSESP32_GENLED__NO_NETWORK, - LWSESP32_GENLED__CONN_AP, - LWSESP32_GENLED__GOT_IP, - LWSESP32_GENLED__OK, -}; - -struct lws_group_member { - struct lws_group_member *next; - uint64_t last_seen; - char model[16]; - char role[16]; - char host[32]; - char mac[20]; - int width, height; - struct ip4_addr addr; - struct ip6_addr addrv6; - uint8_t flags; -}; - -#define LWS_SYSTEM_GROUP_MEMBER_ADD 1 -#define LWS_SYSTEM_GROUP_MEMBER_CHANGE 2 -#define LWS_SYSTEM_GROUP_MEMBER_REMOVE 3 - -#define LWS_GROUP_FLAG_SELF 1 - -struct lws_esp32 { - char sta_ip[16]; - char sta_mask[16]; - char sta_gw[16]; - char serial[16]; - char opts[16]; - char model[16]; - char group[16]; - char role[16]; - char ssid[4][64]; - char password[4][64]; - char active_ssid[64]; - char access_pw[16]; - char hostname[32]; - char mac[20]; - char le_dns[64]; - char le_email[64]; - char region; - char inet; - char conn_ap; - - enum genled_state genled; - uint64_t genled_t; - - lws_cb_scan_done scan_consumer; - void *scan_consumer_arg; - struct lws_group_member *first; - int extant_group_members; - - char acme; - char upload; - - volatile char button_is_down; -}; - -struct lws_esp32_image { - uint32_t romfs; - uint32_t romfs_len; - uint32_t json; - uint32_t json_len; -}; - -extern struct lws_esp32 lws_esp32; -struct lws_vhost; - -extern esp_err_t -lws_esp32_event_passthru(void *ctx, system_event_t *event); -extern void -lws_esp32_wlan_config(void); -extern void -lws_esp32_wlan_start_ap(void); -extern void -lws_esp32_wlan_start_station(void); -struct lws_context_creation_info; -extern void -lws_esp32_set_creation_defaults(struct lws_context_creation_info *info); -extern struct lws_context * -lws_esp32_init(struct lws_context_creation_info *, struct lws_vhost **pvh); -extern int -lws_esp32_wlan_nvs_get(int retry); -extern esp_err_t -lws_nvs_set_str(nvs_handle handle, const char* key, const char* value); -extern void -lws_esp32_restart_guided(uint32_t type); -extern const esp_partition_t * -lws_esp_ota_get_boot_partition(void); -extern int -lws_esp32_get_image_info(const esp_partition_t *part, struct lws_esp32_image *i, char *json, int json_len); -extern int -lws_esp32_leds_network_indication(void); - -extern uint32_t lws_esp32_get_reboot_type(void); -extern uint16_t lws_esp32_sine_interp(int n); - -/* required in external code by esp32 plat (may just return if no leds) */ -extern void lws_esp32_leds_timer_cb(TimerHandle_t th); - -#endif /* LWS_AMAZON_RTOS */ diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-freertos.h libwebsockets-2.4.2/include/libwebsockets/lws-freertos.h --- libwebsockets-4.0.20/include/libwebsockets/lws-freertos.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-freertos.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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 libwebsockets.h if LWS_PLAT_FREERTOS - */ - -typedef int lws_sockfd_type; -typedef int lws_filefd_type; - -#if defined(LWS_AMAZON_RTOS) -#include -#include -#include -#include "timers.h" -#include - -/* - * Later lwip (at least 2.1.12) already defines these in its own headers - * protected by the same test as used here... if POLLIN / POLLOUT already exist - * then assume no need to declare those and struct pollfd. - * - * Older lwip needs these declarations done here. - */ - -#if !defined(POLLIN) && !defined(POLLOUT) - -struct pollfd { - lws_sockfd_type fd; /**< fd related to */ - short events; /**< which POLL... events to respond to */ - short revents; /**< which POLL... events occurred */ -}; -#define POLLIN 0x0001 -#define POLLPRI 0x0002 -#define POLLOUT 0x0004 -#define POLLERR 0x0008 -#define POLLHUP 0x0010 -#define POLLNVAL 0x0020 - -#endif - -#else /* LWS_AMAZON_RTOS */ -#include -#include -#include -#include "esp_wifi.h" -#include "esp_system.h" -#include "esp_event.h" -#include "esp_event_loop.h" -#include "nvs.h" -#include "driver/gpio.h" -#include "esp_spi_flash.h" -#include "freertos/timers.h" -#endif /* LWS_AMAZON_RTOS */ - -#if !defined(CONFIG_FREERTOS_HZ) -#define CONFIG_FREERTOS_HZ 100 -#endif diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-fts.h libwebsockets-2.4.2/include/libwebsockets/lws-fts.h --- libwebsockets-4.0.20/include/libwebsockets/lws-fts.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-fts.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,215 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup search Search - * - * ##Full-text search - * - * Lws provides superfast indexing and fulltext searching from index files on - * storage. - */ -///@{ - -struct lws_fts; -struct lws_fts_file; - -/* - * Queries produce their results in an lwsac, using these public API types. - * The first thing in the lwsac is always a struct lws_fts_result (see below) - * containing heads for linked-lists of the other result types. - */ - -/* one filepath's results */ - -struct lws_fts_result_filepath { - struct lws_fts_result_filepath *next; - int matches; /* logical number of matches */ - int matches_length; /* bytes in length table (may be zero) */ - int lines_in_file; - int filepath_length; - - /* - uint32_t line table follows (first for alignment) */ - /* - filepath (of filepath_length) follows */ -}; - -/* autocomplete result */ - -struct lws_fts_result_autocomplete { - struct lws_fts_result_autocomplete *next; - int instances; - int agg_instances; - int ac_length; - char elided; /* children skipped in interest of antecedent children */ - char has_children; - - /* - autocomplete suggestion (of length ac_length) follows */ -}; - -/* - * The results lwsac always starts with this. If no results and / or no - * autocomplete the members may be NULL. This implies the symbol nor any - * suffix on it exists in the trie file. - */ -struct lws_fts_result { - struct lws_fts_result_filepath *filepath_head; - struct lws_fts_result_autocomplete *autocomplete_head; - int duration_ms; - int effective_flags; /* the search flags that were used */ -}; - -/* - * index creation functions - */ - -/** - * lws_fts_create() - Create a new index file - * - * \param fd: The fd opened for write - * - * Inits a new index file, returning a struct lws_fts to represent it - */ -LWS_VISIBLE LWS_EXTERN struct lws_fts * -lws_fts_create(int fd); - -/** - * lws_fts_destroy() - Finalize a new index file / destroy the trie lwsac - * - * \param trie: The previously opened index being finalized - * - * Finalizes an index file that was being created, and frees the memory involved - * *trie is set to NULL afterwards. - */ -LWS_VISIBLE LWS_EXTERN void -lws_fts_destroy(struct lws_fts **trie); - -/** - * lws_fts_file_index() - Create a new entry in the trie file for an input path - * - * \param t: The previously opened index being written - * \param filepath: The filepath (which may be virtual) associated with this file - * \param filepath_len: The number of chars in the filepath - * \param priority: not used yet - * - * Returns an ordinal that represents this new filepath in the index file. - */ -LWS_VISIBLE LWS_EXTERN int -lws_fts_file_index(struct lws_fts *t, const char *filepath, int filepath_len, - int priority); - -/** - * lws_fts_fill() - Process all or a bufferload of input file - * - * \param t: The previously opened index being written - * \param file_index: The ordinal representing this input filepath - * \param buf: A bufferload of data from the input file - * \param len: The number of bytes in buf - * - * Indexes a buffer of data from the input file. - */ -LWS_VISIBLE LWS_EXTERN int -lws_fts_fill(struct lws_fts *t, uint32_t file_index, const char *buf, - size_t len); - -/** - * lws_fts_serialize() - Store the in-memory trie into the index file - * - * \param t: The previously opened index being written - * - * The trie is held in memory where it can be added to... after all the input - * filepaths and data have been processed, this is called to serialize / - * write the trie data into the index file. - */ -LWS_VISIBLE LWS_EXTERN int -lws_fts_serialize(struct lws_fts *t); - -/* - * index search functions - */ - -/** - * lws_fts_open() - Open an existing index file to search it - * - * \param filepath: The filepath to the index file to open - * - * Opening the index file returns an opaque struct lws_fts_file * that is - * used to perform other operations on it, or NULL if it can't be opened. - */ -LWS_VISIBLE LWS_EXTERN struct lws_fts_file * -lws_fts_open(const char *filepath); - -#define LWSFTS_F_QUERY_AUTOCOMPLETE (1 << 0) -#define LWSFTS_F_QUERY_FILES (1 << 1) -#define LWSFTS_F_QUERY_FILE_LINES (1 << 2) -#define LWSFTS_F_QUERY_QUOTE_LINE (1 << 3) - -struct lws_fts_search_params { - /* the actual search term */ - const char *needle; - /* if non-NULL, FILE results for this filepath only */ - const char *only_filepath; - /* will be set to the results lwsac */ - struct lwsac *results_head; - /* combination of LWSFTS_F_QUERY_* flags */ - int flags; - /* maximum number of autocomplete suggestions to return */ - int max_autocomplete; - /* maximum number of filepaths to return */ - int max_files; - /* maximum number of line number results to return per filepath */ - int max_lines; -}; - -/** - * lws_fts_search() - Perform a search operation on an index - * - * \param jtf: The index file struct returned by lws_fts_open - * \param ftsp: The struct lws_fts_search_params filled in by the caller - * - * The caller should memset the ftsp struct to 0 to ensure members that may be - * introduced in later versions contain known values, then set the related - * members to describe the kind of search action required. - * - * ftsp->results_head is the results lwsac, or NULL. It should be freed with - * lwsac_free() when the results are finished with. - * - * Returns a pointer into the results lwsac that is a struct lws_fts_result - * containing the head pointers into linked-lists of results for autocomplete - * and filepath data, along with some sundry information. This does not need - * to be freed since freeing the lwsac will also remove this and everything it - * points to. - */ -LWS_VISIBLE LWS_EXTERN struct lws_fts_result * -lws_fts_search(struct lws_fts_file *jtf, struct lws_fts_search_params *ftsp); - -/** - * lws_fts_close() - Close a previously-opened index file - * - * \param jtf: The pointer returned from the open - * - * Closes the file handle on the index and frees any allocations - */ -LWS_VISIBLE LWS_EXTERN void -lws_fts_close(struct lws_fts_file *jtf); - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-genaes.h libwebsockets-2.4.2/include/libwebsockets/lws-genaes.h --- libwebsockets-4.0.20/include/libwebsockets/lws-genaes.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-genaes.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,170 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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. - */ - -/*! \defgroup generic AES - * ## Generic AES related functions - * - * Lws provides generic AES functions that abstract the ones - * provided by whatever tls library you are linking against. - * - * It lets you use the same code if you build against mbedtls or OpenSSL - * for example. - */ -///@{ - -#if defined(LWS_WITH_MBEDTLS) -#include -#include -#endif - -enum enum_aes_modes { - LWS_GAESM_CBC, - LWS_GAESM_CFB128, - LWS_GAESM_CFB8, - LWS_GAESM_CTR, - LWS_GAESM_ECB, - LWS_GAESM_OFB, - LWS_GAESM_XTS, /* care... requires double-length key */ - LWS_GAESM_GCM, - LWS_GAESM_KW, -}; - -enum enum_aes_operation { - LWS_GAESO_ENC, - LWS_GAESO_DEC -}; - -enum enum_aes_padding { - LWS_GAESP_NO_PADDING, - LWS_GAESP_WITH_PADDING -}; - -/* include/libwebsockets/lws-jwk.h must be included before this */ - -#define LWS_AES_BLOCKSIZE 128 -#define LWS_AES_CBC_BLOCKLEN 16 - -struct lws_genaes_ctx { -#if defined(LWS_WITH_MBEDTLS) - union { - mbedtls_aes_context ctx; -#if defined(MBEDTLS_CIPHER_MODE_XTS) - mbedtls_aes_xts_context ctx_xts; -#endif - mbedtls_gcm_context ctx_gcm; - } u; -#else - EVP_CIPHER_CTX *ctx; - const EVP_CIPHER *cipher; - ENGINE *engine; - char init; -#endif - unsigned char tag[16]; - struct lws_gencrypto_keyelem *k; - enum enum_aes_operation op; - enum enum_aes_modes mode; - enum enum_aes_padding padding; - int taglen; - char underway; -}; - -/** lws_genaes_create() - Create RSA public decrypt context - * - * \param ctx: your struct lws_genaes_ctx - * \param op: LWS_GAESO_ENC or LWS_GAESO_DEC - * \param mode: one of LWS_GAESM_ - * \param el: struct prepared with key element data - * \param padding: 0 = no padding, 1 = padding - * \param engine: if openssl engine used, pass the pointer here - * - * Creates an RSA context with a public key associated with it, formed from - * the key elements in \p el. - * - * Returns 0 for OK or nonzero for error. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op, - enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el, - enum enum_aes_padding padding, void *engine); - -/** lws_genaes_destroy() - Destroy genaes AES context - * - * \param ctx: your struct lws_genaes_ctx - * \param tag: NULL, or, GCM-only: buffer to receive tag - * \param tlen: 0, or, GCM-only: length of tag buffer - * - * Destroys any allocations related to \p ctx. - * - * For GCM only, up to tlen bytes of tag buffer will be set on exit. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen); - -/** lws_genaes_crypt() - Encrypt or decrypt - * - * \param ctx: your struct lws_genaes_ctx - * \param in: input plaintext or ciphertext - * \param len: length of input (which is always length of output) - * \param out: output plaintext or ciphertext - * \param iv_or_nonce_ctr_or_data_unit_16: NULL, iv, nonce_ctr16, or data_unit16 - * \param stream_block_16: pointer to 16-byte stream block for CTR mode only - * \param nc_or_iv_off: NULL or pointer to nc, or iv_off - * \param taglen: length of tag - * - * Encrypts or decrypts using the AES mode set when the ctx was created. - * The last three arguments have different meanings depending on the mode: - * - * KW CBC CFB128 CFB8 CTR ECB OFB XTS - * iv_or_nonce_ct.._unit_16 : iv iv iv iv nonce NULL iv dataunt - * stream_block_16 : NULL NULL NULL NULL stream NULL NULL NULL - * nc_or_iv_off : NULL NULL iv_off NULL nc_off NULL iv_off NULL - * - * For GCM: - * - * iv_or_nonce_ctr_or_data_unit_16 : iv - * stream_block_16 : pointer to tag - * nc_or_iv_off : set pointed-to size_t to iv length - * in : first call: additional data, subsequently - * : input data - * len : first call: add data length, subsequently - * : input / output length - * - * The length of the optional arg is always 16 if used, regardless of the mode. - * - * Returns 0 for OK or nonzero for error. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genaes_crypt(struct lws_genaes_ctx *ctx, const uint8_t *in, size_t len, - uint8_t *out, - uint8_t *iv_or_nonce_ctr_or_data_unit_16, - uint8_t *stream_block_16, - size_t *nc_or_iv_off, int taglen); - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-gencrypto.h libwebsockets-2.4.2/include/libwebsockets/lws-gencrypto.h --- libwebsockets-4.0.20/include/libwebsockets/lws-gencrypto.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-gencrypto.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,130 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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. - */ - -/* - * These are gencrypto-level constants... they are used by both JOSE and direct - * gencrypto code. However while JWK relies on these, using gencrypto apis has - * no dependency at all on any JOSE type. - */ - -enum lws_gencrypto_kty { - LWS_GENCRYPTO_KTY_UNKNOWN, - - LWS_GENCRYPTO_KTY_OCT, - LWS_GENCRYPTO_KTY_RSA, - LWS_GENCRYPTO_KTY_EC -}; - -/* - * Keytypes where the same element name is reused must all agree to put the - * same-named element at the same e[] index. It's because when used with jwk, - * we parse and store in incoming key data, but we may not be informed of the - * definitive keytype until the end. - */ - -enum lws_gencrypto_oct_tok { - LWS_GENCRYPTO_OCT_KEYEL_K, /* note... same offset as AES K */ - - LWS_GENCRYPTO_OCT_KEYEL_COUNT -}; - -enum lws_gencrypto_rsa_tok { - LWS_GENCRYPTO_RSA_KEYEL_E, - LWS_GENCRYPTO_RSA_KEYEL_N, - LWS_GENCRYPTO_RSA_KEYEL_D, /* note... same offset as EC D */ - LWS_GENCRYPTO_RSA_KEYEL_P, - LWS_GENCRYPTO_RSA_KEYEL_Q, - LWS_GENCRYPTO_RSA_KEYEL_DP, - LWS_GENCRYPTO_RSA_KEYEL_DQ, - LWS_GENCRYPTO_RSA_KEYEL_QI, - - LWS_GENCRYPTO_RSA_KEYEL_COUNT -}; - -enum lws_gencrypto_ec_tok { - LWS_GENCRYPTO_EC_KEYEL_CRV, - LWS_GENCRYPTO_EC_KEYEL_X, - /* note... same offset as RSA D */ - LWS_GENCRYPTO_EC_KEYEL_D = LWS_GENCRYPTO_RSA_KEYEL_D, - LWS_GENCRYPTO_EC_KEYEL_Y, - - LWS_GENCRYPTO_EC_KEYEL_COUNT -}; - -enum lws_gencrypto_aes_tok { - /* note... same offset as OCT K */ - LWS_GENCRYPTO_AES_KEYEL_K = LWS_GENCRYPTO_OCT_KEYEL_K, - - LWS_GENCRYPTO_AES_KEYEL_COUNT -}; - -/* largest number of key elements for any algorithm */ -#define LWS_GENCRYPTO_MAX_KEYEL_COUNT LWS_GENCRYPTO_RSA_KEYEL_COUNT - -/* this "stretchy" type holds individual key element data in binary form. - * It's typcially used in an array with the layout mapping the element index to - * the key element meaning defined by the enums above. An array of these of - * length LWS_GENCRYPTO_MAX_KEYEL_COUNT can define key elements for any key - * type. - */ - -struct lws_gencrypto_keyelem { - uint8_t *buf; - uint32_t len; -}; - - -/** - * lws_gencrypto_bits_to_bytes() - returns rounded up bytes needed for bits - * - * \param bits - * - * Returns the number of bytes needed to store the given number of bits. If - * a byte is partially used, the byte count is rounded up. - */ -LWS_VISIBLE LWS_EXTERN int -lws_gencrypto_bits_to_bytes(int bits); - -/** - * lws_base64_size() - returns estimated size of base64 encoding - * - * \param bytes - * - * Returns a slightly oversize estimate of the size of a base64 encoded version - * of the given amount of unencoded data. - */ -LWS_VISIBLE LWS_EXTERN int -lws_base64_size(int bytes); - -/** - * lws_gencrypto_padded_length() - returns PKCS#5/#7 padded length - * - * @param blocksize - blocksize to pad to - * @param len - Length of input to pad - * - * Returns the length of a buffer originally of size len after PKCS#5 or PKCS#7 - * padding has been applied to it. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_gencrypto_padded_length(size_t block_size, size_t len); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-genec.h libwebsockets-2.4.2/include/libwebsockets/lws-genec.h --- libwebsockets-4.0.20/include/libwebsockets/lws-genec.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-genec.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,211 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -enum enum_genec_alg { - LEGENEC_UNKNOWN, - - LEGENEC_ECDH, - LEGENEC_ECDSA -}; - -struct lws_genec_ctx { -#if defined(LWS_WITH_MBEDTLS) - union { - mbedtls_ecdh_context *ctx_ecdh; - mbedtls_ecdsa_context *ctx_ecdsa; - } u; -#else - EVP_PKEY_CTX *ctx[2]; -#endif - struct lws_context *context; - const struct lws_ec_curves *curve_table; - enum enum_genec_alg genec_alg; - - char has_private; -}; - -#if defined(LWS_WITH_MBEDTLS) -enum enum_lws_dh_side { - LDHS_OURS = MBEDTLS_ECDH_OURS, - LDHS_THEIRS = MBEDTLS_ECDH_THEIRS -}; -#else -enum enum_lws_dh_side { - LDHS_OURS, - LDHS_THEIRS -}; -#endif - -struct lws_ec_curves { - const char *name; - int tls_lib_nid; - uint16_t key_bytes; -}; - - -/* ECDH-specific apis */ - -/** lws_genecdh_create() - Create a genecdh - * - * \param ctx: your genec context - * \param context: your lws_context (for RNG access) - * \param curve_table: NULL, enabling P-256, P-384 and P-521, or a replacement - * struct lws_ec_curves array, terminated by an entry with - * .name = NULL, of curves you want to whitelist - * - * Initializes a genecdh - */ -LWS_VISIBLE int -lws_genecdh_create(struct lws_genec_ctx *ctx, struct lws_context *context, - const struct lws_ec_curves *curve_table); - -/** lws_genecdh_set_key() - Apply an EC key to our or theirs side - * - * \param ctx: your genecdh context - * \param el: your key elements - * \param side: LDHS_OURS or LDHS_THEIRS - * - * Applies an EC key to one side or the other of an ECDH ctx - */ -LWS_VISIBLE LWS_EXTERN int -lws_genecdh_set_key(struct lws_genec_ctx *ctx, struct lws_gencrypto_keyelem *el, - enum enum_lws_dh_side side); - -/** lws_genecdh_new_keypair() - Create a genec with a new public / private key - * - * \param ctx: your genec context - * \param side: LDHS_OURS or LDHS_THEIRS - * \param curve_name: an EC curve name, like "P-256" - * \param el: array pf LWS_GENCRYPTO_EC_KEYEL_COUNT key elems to take the new key - * - * Creates a genecdh with a newly minted EC public / private key - */ -LWS_VISIBLE LWS_EXTERN int -lws_genecdh_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side, - const char *curve_name, struct lws_gencrypto_keyelem *el); - -LWS_VISIBLE LWS_EXTERN int -lws_genecdh_compute_shared_secret(struct lws_genec_ctx *ctx, uint8_t *ss, - int *ss_len); - - -/* ECDSA-specific apis */ - -/** lws_genecdsa_create() - Create a genecdsa and - * - * \param ctx: your genec context - * \param context: your lws_context (for RNG access) - * \param curve_table: NULL, enabling P-256, P-384 and P-521, or a replacement - * struct lws_ec_curves array, terminated by an entry with - * .name = NULL, of curves you want to whitelist - * - * Initializes a genecdh - */ -LWS_VISIBLE int -lws_genecdsa_create(struct lws_genec_ctx *ctx, struct lws_context *context, - const struct lws_ec_curves *curve_table); - -/** lws_genecdsa_new_keypair() - Create a genecdsa with a new public / private key - * - * \param ctx: your genec context - * \param curve_name: an EC curve name, like "P-256" - * \param el: array pf LWS_GENCRYPTO_EC_KEYEL_COUNT key elements to take the new key - * - * Creates a genecdsa with a newly minted EC public / private key - */ -LWS_VISIBLE LWS_EXTERN int -lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name, - struct lws_gencrypto_keyelem *el); - -/** lws_genecdsa_set_key() - Apply an EC key to an ecdsa context - * - * \param ctx: your genecdsa context - * \param el: your key elements - * - * Applies an EC key to an ecdsa context - */ -LWS_VISIBLE LWS_EXTERN int -lws_genecdsa_set_key(struct lws_genec_ctx *ctx, - struct lws_gencrypto_keyelem *el); - -/** lws_genecdsa_hash_sig_verify_jws() - Verifies a JWS ECDSA signature on a given hash - * - * \param ctx: your struct lws_genrsa_ctx - * \param in: unencrypted payload (usually a recomputed hash) - * \param hash_type: one of LWS_GENHASH_TYPE_ - * \param keybits: number of bits in the crypto key - * \param sig: pointer to the signature we received with the payload - * \param sig_len: length of the signature we are checking in bytes - * - * This just looks at the signed hash... that's why there's no input length - * parameter, it's decided by the choice of hash. It's up to you to confirm - * separately the actual payload matches the hash that was confirmed by this to - * be validly signed. - * - * Returns <0 for error, or 0 if signature matches the hash + key.. - * - * The JWS ECDSA signature verification algorithm differs to generic ECDSA - * signatures and they're not interoperable. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, int keybits, - const uint8_t *sig, size_t sig_len); - -/** lws_genecdsa_hash_sign_jws() - Creates a JWS ECDSA signature for a hash you provide - * - * \param ctx: your struct lws_genrsa_ctx - * \param in: precomputed hash - * \param hash_type: one of LWS_GENHASH_TYPE_ - * \param keybits: number of bits in the crypto key - * \param sig: pointer to buffer to take signature - * \param sig_len: length of the buffer (must be >= length of key N) - * - * Returns <0 for error, or 0 for success. - * - * This creates a JWS ECDSA signature for a hash you already computed and provide. - * - * The JWS ECDSA signature generation algorithm differs to generic ECDSA - * signatures and they're not interoperable. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, int keybits, - uint8_t *sig, size_t sig_len); - - -/* Apis that apply to both ECDH and ECDSA */ - -LWS_VISIBLE LWS_EXTERN void -lws_genec_destroy(struct lws_genec_ctx *ctx); - -LWS_VISIBLE LWS_EXTERN void -lws_genec_destroy_elements(struct lws_gencrypto_keyelem *el); - -LWS_VISIBLE LWS_EXTERN int -lws_genec_dump(struct lws_gencrypto_keyelem *el); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-genhash.h libwebsockets-2.4.2/include/libwebsockets/lws-genhash.h --- libwebsockets-4.0.20/include/libwebsockets/lws-genhash.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-genhash.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,180 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup generichash Generic Hash - * ## Generic Hash related functions - * - * Lws provides generic hash / digest accessors that abstract the ones - * provided by whatever tls library you are linking against. - * - * It lets you use the same code if you build against mbedtls or OpenSSL - * for example. - */ -///@{ - -enum lws_genhash_types { - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHASH_TYPE_MD5, - LWS_GENHASH_TYPE_SHA1, - LWS_GENHASH_TYPE_SHA256, - LWS_GENHASH_TYPE_SHA384, - LWS_GENHASH_TYPE_SHA512, -}; - -enum lws_genhmac_types { - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_SHA256, - LWS_GENHMAC_TYPE_SHA384, - LWS_GENHMAC_TYPE_SHA512, -}; - -#define LWS_GENHASH_LARGEST 64 - -struct lws_genhash_ctx { - uint8_t type; -#if defined(LWS_WITH_MBEDTLS) - union { - mbedtls_md5_context md5; - mbedtls_sha1_context sha1; - mbedtls_sha256_context sha256; - mbedtls_sha512_context sha512; /* 384 also uses this */ - const mbedtls_md_info_t *hmac; - } u; -#else - const EVP_MD *evp_type; - EVP_MD_CTX *mdctx; -#endif -}; - -struct lws_genhmac_ctx { - uint8_t type; -#if defined(LWS_WITH_MBEDTLS) - const mbedtls_md_info_t *hmac; - mbedtls_md_context_t ctx; -#else - const EVP_MD *evp_type; -#if defined(LWS_HAVE_HMAC_CTX_new) - HMAC_CTX *ctx; -#else - HMAC_CTX ctx; -#endif -#endif -}; - -/** lws_genhash_size() - get hash size in bytes - * - * \param type: one of LWS_GENHASH_TYPE_... - * - * Returns number of bytes in this type of hash - */ -LWS_VISIBLE LWS_EXTERN size_t LWS_WARN_UNUSED_RESULT -lws_genhash_size(enum lws_genhash_types type); - -/** lws_genhmac_size() - get hash size in bytes - * - * \param type: one of LWS_GENHASH_TYPE_... - * - * Returns number of bytes in this type of hmac - */ -LWS_VISIBLE LWS_EXTERN size_t LWS_WARN_UNUSED_RESULT -lws_genhmac_size(enum lws_genhmac_types type); - -/** lws_genhash_init() - prepare your struct lws_genhash_ctx for use - * - * \param ctx: your struct lws_genhash_ctx - * \param type: one of LWS_GENHASH_TYPE_... - * - * Initializes the hash context for the type you requested - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type); - -/** lws_genhash_update() - digest len bytes of the buffer starting at in - * - * \param ctx: your struct lws_genhash_ctx - * \param in: start of the bytes to digest - * \param len: count of bytes to digest - * - * Updates the state of your hash context to reflect digesting len bytes from in - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len); - -/** lws_genhash_destroy() - copy out the result digest and destroy the ctx - * - * \param ctx: your struct lws_genhash_ctx - * \param result: NULL, or where to copy the result hash - * - * Finalizes the hash and copies out the digest. Destroys any allocations such - * that ctx can safely go out of scope after calling this. - * - * NULL result is supported so that you can destroy the ctx cleanly on error - * conditions, where there is no valid result. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result); - -/** lws_genhmac_init() - prepare your struct lws_genhmac_ctx for use - * - * \param ctx: your struct lws_genhmac_ctx - * \param type: one of LWS_GENHMAC_TYPE_... - * \param key: pointer to the start of the HMAC key - * \param key_len: length of the HMAC key - * - * Initializes the hash context for the type you requested - * - * If the return is nonzero, it failed and there is nothing needing to be - * destroyed. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type, - const uint8_t *key, size_t key_len); - -/** lws_genhmac_update() - digest len bytes of the buffer starting at in - * - * \param ctx: your struct lws_genhmac_ctx - * \param in: start of the bytes to digest - * \param len: count of bytes to digest - * - * Updates the state of your hash context to reflect digesting len bytes from in - * - * If the return is nonzero, it failed and needs destroying. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len); - -/** lws_genhmac_destroy() - copy out the result digest and destroy the ctx - * - * \param ctx: your struct lws_genhmac_ctx - * \param result: NULL, or where to copy the result hash - * - * Finalizes the hash and copies out the digest. Destroys any allocations such - * that ctx can safely go out of scope after calling this. - * - * NULL result is supported so that you can destroy the ctx cleanly on error - * conditions, where there is no valid result. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result); -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-genrsa.h libwebsockets-2.4.2/include/libwebsockets/lws-genrsa.h --- libwebsockets-4.0.20/include/libwebsockets/lws-genrsa.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-genrsa.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,254 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup genericRSA Generic RSA - * ## Generic RSA related functions - * - * Lws provides generic RSA functions that abstract the ones - * provided by whatever OpenSSL library you are linking against. - * - * It lets you use the same code if you build against mbedtls or OpenSSL - * for example. - */ -///@{ - -/* include/libwebsockets/lws-jwk.h must be included before this */ - -enum enum_genrsa_mode { - LGRSAM_PKCS1_1_5, - LGRSAM_PKCS1_OAEP_PSS, - - LGRSAM_COUNT -}; - -struct lws_genrsa_ctx { -#if defined(LWS_WITH_MBEDTLS) - mbedtls_rsa_context *ctx; -#else - BIGNUM *bn[LWS_GENCRYPTO_RSA_KEYEL_COUNT]; - EVP_PKEY_CTX *ctx; - RSA *rsa; -#endif - struct lws_context *context; - enum enum_genrsa_mode mode; -}; - -/** lws_genrsa_public_decrypt_create() - Create RSA public decrypt context - * - * \param ctx: your struct lws_genrsa_ctx - * \param el: struct prepared with key element data - * \param context: lws_context for RNG - * \param mode: RSA mode, one of LGRSAM_ constants - * \param oaep_hashid: the lws genhash id for the hash used in MFG1 hash - * used in OAEP mode - normally, SHA1 - * - * Creates an RSA context with a public key associated with it, formed from - * the key elements in \p el. - * - * Mode LGRSAM_PKCS1_1_5 is in widespread use but has weaknesses. It's - * recommended to use LGRSAM_PKCS1_OAEP_PSS for new implementations. - * - * Returns 0 for OK or nonzero for error. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el, - struct lws_context *context, enum enum_genrsa_mode mode, - enum lws_genhash_types oaep_hashid); - -/** lws_genrsa_destroy_elements() - Free allocations in genrsa_elements - * - * \param el: your struct lws_gencrypto_keyelem - * - * This is a helper for user code making use of struct lws_gencrypto_keyelem - * where the elements are allocated on the heap, it frees any non-NULL - * buf element and sets the buf to NULL. - * - * NB: lws_genrsa_public_... apis do not need this as they take care of the key - * creation and destruction themselves. - */ -LWS_VISIBLE LWS_EXTERN void -lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el); - -/** lws_genrsa_new_keypair() - Create new RSA keypair - * - * \param context: your struct lws_context (may be used for RNG) - * \param ctx: your struct lws_genrsa_ctx - * \param mode: RSA mode, one of LGRSAM_ constants - * \param el: struct to get the new key element data allocated into it - * \param bits: key size, eg, 4096 - * - * Creates a new RSA context and generates a new keypair into it, with \p bits - * bits. - * - * Returns 0 for OK or nonzero for error. - * - * Mode LGRSAM_PKCS1_1_5 is in widespread use but has weaknesses. It's - * recommended to use LGRSAM_PKCS1_OAEP_PSS for new implementations. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx, - enum enum_genrsa_mode mode, struct lws_gencrypto_keyelem *el, - int bits); - -/** lws_genrsa_public_encrypt() - Perform RSA public key encryption - * - * \param ctx: your struct lws_genrsa_ctx - * \param in: plaintext input - * \param in_len: length of plaintext input - * \param out: encrypted output - * - * Performs PKCS1 v1.5 Encryption - * - * Returns <0 for error, or length of decrypted data. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out); - -/** lws_genrsa_private_encrypt() - Perform RSA private key encryption - * - * \param ctx: your struct lws_genrsa_ctx - * \param in: plaintext input - * \param in_len: length of plaintext input - * \param out: encrypted output - * - * Performs PKCS1 v1.5 Encryption - * - * Returns <0 for error, or length of decrypted data. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out); - -/** lws_genrsa_public_decrypt() - Perform RSA public key decryption - * - * \param ctx: your struct lws_genrsa_ctx - * \param in: encrypted input - * \param in_len: length of encrypted input - * \param out: decrypted output - * \param out_max: size of output buffer - * - * Performs PKCS1 v1.5 Decryption - * - * Returns <0 for error, or length of decrypted data. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out, size_t out_max); - -/** lws_genrsa_private_decrypt() - Perform RSA private key decryption - * - * \param ctx: your struct lws_genrsa_ctx - * \param in: encrypted input - * \param in_len: length of encrypted input - * \param out: decrypted output - * \param out_max: size of output buffer - * - * Performs PKCS1 v1.5 Decryption - * - * Returns <0 for error, or length of decrypted data. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genrsa_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out, size_t out_max); - -/** lws_genrsa_hash_sig_verify() - Verifies RSA signature on a given hash - * - * \param ctx: your struct lws_genrsa_ctx - * \param in: input to be hashed - * \param hash_type: one of LWS_GENHASH_TYPE_ - * \param sig: pointer to the signature we received with the payload - * \param sig_len: length of the signature we are checking in bytes - * - * Returns <0 for error, or 0 if signature matches the payload + key. - * - * This just looks at a hash... that's why there's no input length - * parameter, it's decided by the choice of hash. It's up to you to confirm - * separately the actual payload matches the hash that was confirmed by this to - * be validly signed. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, - const uint8_t *sig, size_t sig_len); - -/** lws_genrsa_hash_sign() - Creates an ECDSA signature for a hash you provide - * - * \param ctx: your struct lws_genrsa_ctx - * \param in: input to be hashed and signed - * \param hash_type: one of LWS_GENHASH_TYPE_ - * \param sig: pointer to buffer to take signature - * \param sig_len: length of the buffer (must be >= length of key N) - * - * Returns <0 for error, or 0 for success. - * - * This creates an RSA signature for a hash you already computed and provide. - * You should have created the hash before calling this by iterating over the - * actual payload you need to confirm. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genrsa_hash_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, - uint8_t *sig, size_t sig_len); - -/** lws_genrsa_public_decrypt_destroy() - Destroy RSA public decrypt context - * - * \param ctx: your struct lws_genrsa_ctx - * - * Destroys any allocations related to \p ctx. - * - * This and related APIs operate identically with OpenSSL or mbedTLS backends. - */ -LWS_VISIBLE LWS_EXTERN void -lws_genrsa_destroy(struct lws_genrsa_ctx *ctx); - -/** lws_genrsa_render_pkey_asn1() - Exports public or private key to ASN1/DER - * - * \param ctx: your struct lws_genrsa_ctx - * \param _private: 0 = public part only, 1 = all parts of the key - * \param pkey_asn1: pointer to buffer to take the ASN1 - * \param pkey_asn1_len: max size of the pkey_asn1_len - * - * Returns length of pkey_asn1 written, or -1 for error. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private, - uint8_t *pkey_asn1, size_t pkey_asn1_len); -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-http.h libwebsockets-2.4.2/include/libwebsockets/lws-http.h --- libwebsockets-4.0.20/include/libwebsockets/lws-http.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-http.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,898 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/* minimal space for typical headers and CSP stuff */ - -#define LWS_RECOMMENDED_MIN_HEADER_SPACE 2048 - -/*! \defgroup http HTTP - - Modules related to handling HTTP -*/ -//@{ - -/*! \defgroup httpft HTTP File transfer - * \ingroup http - - APIs for sending local files in response to HTTP requests -*/ -//@{ - -/** - * lws_get_mimetype() - Determine mimetype to use from filename - * - * \param file: filename - * \param m: NULL, or mount context - * - * This uses a canned list of known filetypes first, if no match and m is - * non-NULL, then tries a list of per-mount file suffix to mimtype mappings. - * - * Returns either NULL or a pointer to the mimetype matching the file. - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_get_mimetype(const char *file, const struct lws_http_mount *m); - -/** - * lws_serve_http_file() - Send a file back to the client using http - * \param wsi: Websocket instance (available from user callback) - * \param file: The file to issue over http - * \param content_type: The http content type, eg, text/html - * \param other_headers: NULL or pointer to header string - * \param other_headers_len: length of the other headers if non-NULL - * - * This function is intended to be called from the callback in response - * to http requests from the client. It allows the callback to issue - * local files down the http link in a single step. - * - * Returning <0 indicates error and the wsi should be closed. Returning - * >0 indicates the file was completely sent and - * lws_http_transaction_completed() called on the wsi (and close if != 0) - * ==0 indicates the file transfer is started and needs more service later, - * the wsi should be left alone. - */ -LWS_VISIBLE LWS_EXTERN int -lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, - const char *other_headers, int other_headers_len); - -LWS_VISIBLE LWS_EXTERN int -lws_serve_http_file_fragment(struct lws *wsi); -//@} - - -enum http_status { - HTTP_STATUS_CONTINUE = 100, - - HTTP_STATUS_OK = 200, - HTTP_STATUS_NO_CONTENT = 204, - HTTP_STATUS_PARTIAL_CONTENT = 206, - - HTTP_STATUS_MOVED_PERMANENTLY = 301, - HTTP_STATUS_FOUND = 302, - HTTP_STATUS_SEE_OTHER = 303, - HTTP_STATUS_NOT_MODIFIED = 304, - - HTTP_STATUS_BAD_REQUEST = 400, - HTTP_STATUS_UNAUTHORIZED, - HTTP_STATUS_PAYMENT_REQUIRED, - HTTP_STATUS_FORBIDDEN, - HTTP_STATUS_NOT_FOUND, - HTTP_STATUS_METHOD_NOT_ALLOWED, - HTTP_STATUS_NOT_ACCEPTABLE, - HTTP_STATUS_PROXY_AUTH_REQUIRED, - HTTP_STATUS_REQUEST_TIMEOUT, - HTTP_STATUS_CONFLICT, - HTTP_STATUS_GONE, - HTTP_STATUS_LENGTH_REQUIRED, - HTTP_STATUS_PRECONDITION_FAILED, - HTTP_STATUS_REQ_ENTITY_TOO_LARGE, - HTTP_STATUS_REQ_URI_TOO_LONG, - HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, - HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, - HTTP_STATUS_EXPECTATION_FAILED, - - HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, - HTTP_STATUS_NOT_IMPLEMENTED, - HTTP_STATUS_BAD_GATEWAY, - HTTP_STATUS_SERVICE_UNAVAILABLE, - HTTP_STATUS_GATEWAY_TIMEOUT, - HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED, -}; -/*! \defgroup html-chunked-substitution HTML Chunked Substitution - * \ingroup http - * - * ##HTML chunked Substitution - * - * APIs for receiving chunks of text, replacing a set of variable names via - * a callback, and then prepending and appending HTML chunked encoding - * headers. - */ -//@{ - -struct lws_process_html_args { - char *p; /**< pointer to the buffer containing the data */ - int len; /**< length of the original data at p */ - int max_len; /**< maximum length we can grow the data to */ - int final; /**< set if this is the last chunk of the file */ - int chunked; /**< 0 == unchunked, 1 == produce chunk headers - (incompatible with HTTP/2) */ -}; - -typedef const char *(*lws_process_html_state_cb)(void *data, int index); - -struct lws_process_html_state { - char *start; /**< pointer to start of match */ - char swallow[16]; /**< matched character buffer */ - int pos; /**< position in match */ - void *data; /**< opaque pointer */ - const char * const *vars; /**< list of variable names */ - int count_vars; /**< count of variable names */ - - lws_process_html_state_cb replace; - /**< called on match to perform substitution */ -}; - -/*! lws_chunked_html_process() - generic chunked substitution - * \param args: buffer to process using chunked encoding - * \param s: current processing state - */ -LWS_VISIBLE LWS_EXTERN int -lws_chunked_html_process(struct lws_process_html_args *args, - struct lws_process_html_state *s); -//@} - -/** \defgroup HTTP-headers-read HTTP headers: read - * \ingroup http - * - * ##HTTP header releated functions - * - * In lws the client http headers are temporarily stored in a pool, only for the - * duration of the http part of the handshake. It's because in most cases, - * the header content is ignored for the whole rest of the connection lifetime - * and would then just be taking up space needlessly. - * - * During LWS_CALLBACK_HTTP when the URI path is delivered is the last time - * the http headers are still allocated, you can use these apis then to - * look at and copy out interesting header content (cookies, etc) - * - * Notice that the header total length reported does not include a terminating - * '\0', however you must allocate for it when using the _copy apis. So the - * length reported for a header containing "123" is 3, but you must provide - * a buffer of length 4 so that "123\0" may be copied into it, or the copy - * will fail with a nonzero return code. - * - * In the special case of URL arguments, like ?x=1&y=2, the arguments are - * stored in a token named for the method, eg, WSI_TOKEN_GET_URI if it - * was a GET or WSI_TOKEN_POST_URI if POST. You can check the total - * length to confirm the method. - * - * For URL arguments, each argument is stored urldecoded in a "fragment", so - * you can use the fragment-aware api lws_hdr_copy_fragment() to access each - * argument in turn: the fragments contain urldecoded strings like x=1 or y=2. - * - * As a convenience, lws has an api that will find the fragment with a - * given name= part, lws_get_urlarg_by_name(). - */ -///@{ - -/** struct lws_tokens - * you need these to look at headers that have been parsed if using the - * LWS_CALLBACK_FILTER_CONNECTION callback. If a header from the enum - * list below is absent, .token = NULL and len = 0. Otherwise .token - * points to .len chars containing that header content. - */ -struct lws_tokens { - unsigned char *token; /**< pointer to start of the token */ - int len; /**< length of the token's value */ -}; - -/* enum lws_token_indexes - * these have to be kept in sync with lextable.h / minilex.c - * - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -enum lws_token_indexes { - WSI_TOKEN_GET_URI, /* 0 */ - WSI_TOKEN_POST_URI, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - WSI_TOKEN_OPTIONS_URI, -#endif - WSI_TOKEN_HOST, - WSI_TOKEN_CONNECTION, - WSI_TOKEN_UPGRADE, /* 5 */ - WSI_TOKEN_ORIGIN, -#if defined(LWS_ROLE_WS) - WSI_TOKEN_DRAFT, -#endif - WSI_TOKEN_CHALLENGE, -#if defined(LWS_ROLE_WS) - WSI_TOKEN_EXTENSIONS, - WSI_TOKEN_KEY1, /* 10 */ - WSI_TOKEN_KEY2, - WSI_TOKEN_PROTOCOL, - WSI_TOKEN_ACCEPT, - WSI_TOKEN_NONCE, -#endif - WSI_TOKEN_HTTP, -#if defined(LWS_ROLE_H2) - WSI_TOKEN_HTTP2_SETTINGS, /* 16 */ -#endif - WSI_TOKEN_HTTP_ACCEPT, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - WSI_TOKEN_HTTP_AC_REQUEST_HEADERS, -#endif - WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, - WSI_TOKEN_HTTP_IF_NONE_MATCH, /* 20 */ - WSI_TOKEN_HTTP_ACCEPT_ENCODING, - WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, - WSI_TOKEN_HTTP_PRAGMA, - WSI_TOKEN_HTTP_CACHE_CONTROL, - WSI_TOKEN_HTTP_AUTHORIZATION, - WSI_TOKEN_HTTP_COOKIE, - WSI_TOKEN_HTTP_CONTENT_LENGTH, /* 27 */ - WSI_TOKEN_HTTP_CONTENT_TYPE, - WSI_TOKEN_HTTP_DATE, - WSI_TOKEN_HTTP_RANGE, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - WSI_TOKEN_HTTP_REFERER, -#endif -#if defined(LWS_ROLE_WS) - WSI_TOKEN_KEY, - WSI_TOKEN_VERSION, - WSI_TOKEN_SWORIGIN, -#endif -#if defined(LWS_ROLE_H2) - WSI_TOKEN_HTTP_COLON_AUTHORITY, - WSI_TOKEN_HTTP_COLON_METHOD, - WSI_TOKEN_HTTP_COLON_PATH, - WSI_TOKEN_HTTP_COLON_SCHEME, - WSI_TOKEN_HTTP_COLON_STATUS, -#endif - -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - WSI_TOKEN_HTTP_ACCEPT_CHARSET, -#endif - WSI_TOKEN_HTTP_ACCEPT_RANGES, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN, -#endif - WSI_TOKEN_HTTP_AGE, - WSI_TOKEN_HTTP_ALLOW, - WSI_TOKEN_HTTP_CONTENT_DISPOSITION, - WSI_TOKEN_HTTP_CONTENT_ENCODING, - WSI_TOKEN_HTTP_CONTENT_LANGUAGE, - WSI_TOKEN_HTTP_CONTENT_LOCATION, - WSI_TOKEN_HTTP_CONTENT_RANGE, - WSI_TOKEN_HTTP_ETAG, - WSI_TOKEN_HTTP_EXPECT, - WSI_TOKEN_HTTP_EXPIRES, - WSI_TOKEN_HTTP_FROM, - WSI_TOKEN_HTTP_IF_MATCH, - WSI_TOKEN_HTTP_IF_RANGE, - WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE, - WSI_TOKEN_HTTP_LAST_MODIFIED, - WSI_TOKEN_HTTP_LINK, - WSI_TOKEN_HTTP_LOCATION, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - WSI_TOKEN_HTTP_MAX_FORWARDS, - WSI_TOKEN_HTTP_PROXY_AUTHENTICATE, - WSI_TOKEN_HTTP_PROXY_AUTHORIZATION, -#endif - WSI_TOKEN_HTTP_REFRESH, - WSI_TOKEN_HTTP_RETRY_AFTER, - WSI_TOKEN_HTTP_SERVER, - WSI_TOKEN_HTTP_SET_COOKIE, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY, -#endif - WSI_TOKEN_HTTP_TRANSFER_ENCODING, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - WSI_TOKEN_HTTP_USER_AGENT, - WSI_TOKEN_HTTP_VARY, - WSI_TOKEN_HTTP_VIA, - WSI_TOKEN_HTTP_WWW_AUTHENTICATE, -#endif -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - WSI_TOKEN_PATCH_URI, - WSI_TOKEN_PUT_URI, - WSI_TOKEN_DELETE_URI, -#endif - - WSI_TOKEN_HTTP_URI_ARGS, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - WSI_TOKEN_PROXY, - WSI_TOKEN_HTTP_X_REAL_IP, -#endif - WSI_TOKEN_HTTP1_0, - WSI_TOKEN_X_FORWARDED_FOR, - WSI_TOKEN_CONNECT, - WSI_TOKEN_HEAD_URI, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - WSI_TOKEN_TE, - WSI_TOKEN_REPLAY_NONCE, /* ACME */ -#endif -#if defined(LWS_ROLE_H2) - WSI_TOKEN_COLON_PROTOCOL, -#endif - WSI_TOKEN_X_AUTH_TOKEN, - - /****** add new things just above ---^ ******/ - - /* use token storage to stash these internally, not for - * user use */ - - _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - _WSI_TOKEN_CLIENT_PEER_ADDRESS, - _WSI_TOKEN_CLIENT_URI, - _WSI_TOKEN_CLIENT_HOST, - _WSI_TOKEN_CLIENT_ORIGIN, - _WSI_TOKEN_CLIENT_METHOD, - _WSI_TOKEN_CLIENT_IFACE, - _WSI_TOKEN_CLIENT_ALPN, - - /* always last real token index*/ - WSI_TOKEN_COUNT, - - /* parser state additions, no storage associated */ - WSI_TOKEN_NAME_PART, -#if defined(LWS_WITH_CUSTOM_HEADERS) - WSI_TOKEN_UNKNOWN_VALUE_PART, -#endif - WSI_TOKEN_SKIPPING, - WSI_TOKEN_SKIPPING_SAW_CR, - WSI_PARSING_COMPLETE, - WSI_INIT_TOKEN_MUXURL, -}; - -struct lws_token_limits { - unsigned short token_limit[WSI_TOKEN_COUNT]; /**< max chars for this token */ -}; - -enum lws_h2_settings { - H2SET_HEADER_TABLE_SIZE = 1, - H2SET_ENABLE_PUSH, - H2SET_MAX_CONCURRENT_STREAMS, - H2SET_INITIAL_WINDOW_SIZE, - H2SET_MAX_FRAME_SIZE, - H2SET_MAX_HEADER_LIST_SIZE, - H2SET_RESERVED7, - H2SET_ENABLE_CONNECT_PROTOCOL, /* defined in mcmanus-httpbis-h2-ws-02 */ - - H2SET_COUNT /* always last */ -}; - -/** - * lws_token_to_string() - returns a textual representation of a hdr token index - * - * \param token: token index - */ -LWS_VISIBLE LWS_EXTERN const unsigned char * -lws_token_to_string(enum lws_token_indexes token); - -/** - * lws_hdr_total_length: report length of all fragments of a header totalled up - * The returned length does not include the space for a - * terminating '\0' - * - * \param wsi: websocket connection - * \param h: which header index we are interested in - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h); - -/** - * lws_hdr_fragment_length: report length of a single fragment of a header - * The returned length does not include the space for a - * terminating '\0' - * - * \param wsi: websocket connection - * \param h: which header index we are interested in - * \param frag_idx: which fragment of h we want to get the length of - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, - int frag_idx); - -/** - * lws_hdr_copy() - copy all fragments of the given header to a buffer - * The buffer length len must include space for an additional - * terminating '\0', or it will fail returning -1. - * - * \param wsi: websocket connection - * \param dest: destination buffer - * \param len: length of destination buffer - * \param h: which header index we are interested in - * - * copies the whole, aggregated header, even if it was delivered in - * several actual headers piece by piece. Returns -1 or length of the whole - * header. - */ -LWS_VISIBLE LWS_EXTERN int -lws_hdr_copy(struct lws *wsi, char *dest, int len, enum lws_token_indexes h); - -/** - * lws_hdr_copy_fragment() - copy a single fragment of the given header to a buffer - * The buffer length len must include space for an additional - * terminating '\0', or it will fail returning -1. - * If the requested fragment index is not present, it fails - * returning -1. - * - * \param wsi: websocket connection - * \param dest: destination buffer - * \param len: length of destination buffer - * \param h: which header index we are interested in - * \param frag_idx: which fragment of h we want to copy - * - * Normally this is only useful - * to parse URI arguments like ?x=1&y=2, token index WSI_TOKEN_HTTP_URI_ARGS - * fragment 0 will contain "x=1" and fragment 1 "y=2" - */ -LWS_VISIBLE LWS_EXTERN int -lws_hdr_copy_fragment(struct lws *wsi, char *dest, int len, - enum lws_token_indexes h, int frag_idx); - -/** - * lws_hdr_custom_length() - return length of a custom header - * - * \param wsi: websocket connection - * \param name: header string (including terminating :) - * \param nlen: length of name - * - * Lws knows about 100 common http headers, and parses them into indexes when - * it recognizes them. When it meets a header that it doesn't know, it stores - * the name and value directly, and you can look them up using - * lws_hdr_custom_length() and lws_hdr_custom_copy(). - * - * This api returns -1, or the length of the value part of the header if it - * exists. Lws must be built with LWS_WITH_CUSTOM_HEADERS (on by default) to - * use this api. - */ -LWS_VISIBLE LWS_EXTERN int -lws_hdr_custom_length(struct lws *wsi, const char *name, int nlen); - -/** - * lws_hdr_custom_copy() - copy value part of a custom header - * - * \param wsi: websocket connection - * \param dst: pointer to buffer to receive the copy - * \param len: number of bytes available at dst - * \param name: header string (including terminating :) - * \param nlen: length of name - * - * Lws knows about 100 common http headers, and parses them into indexes when - * it recognizes them. When it meets a header that it doesn't know, it stores - * the name and value directly, and you can look them up using - * lws_hdr_custom_length() and lws_hdr_custom_copy(). - * - * This api returns -1, or the length of the string it copied into dst if it - * was big enough to contain both the string and an extra terminating NUL. Lws - * must be built with LWS_WITH_CUSTOM_HEADERS (on by default) to use this api. - */ -LWS_VISIBLE LWS_EXTERN int -lws_hdr_custom_copy(struct lws *wsi, char *dst, int len, const char *name, - int nlen); - -/** - * lws_get_urlarg_by_name() - return pointer to arg value if present - * \param wsi: the connection to check - * \param name: the arg name, like "token=" - * \param buf: the buffer to receive the urlarg (including the name= part) - * \param len: the length of the buffer to receive the urlarg - * - * Returns NULL if not found or a pointer inside buf to just after the - * name= part. - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len); -///@} - -/*! \defgroup HTTP-headers-create HTTP headers: create - * - * ## HTTP headers: Create - * - * These apis allow you to create HTTP response headers in a way compatible with - * both HTTP/1.x and HTTP/2. - * - * They each append to a buffer taking care about the buffer end, which is - * passed in as a pointer. When data is written to the buffer, the current - * position p is updated accordingly. - * - * All of these apis are LWS_WARN_UNUSED_RESULT as they can run out of space - * and fail with nonzero return. - */ -///@{ - -#define LWSAHH_CODE_MASK ((1 << 16) - 1) -#define LWSAHH_FLAG_NO_SERVER_NAME (1 << 30) - -/** - * lws_add_http_header_status() - add the HTTP response status code - * - * \param wsi: the connection to check - * \param code: an HTTP code like 200, 404 etc (see enum http_status) - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Adds the initial response code, so should be called first. - * - * Code may additionally take OR'd flags: - * - * LWSAHH_FLAG_NO_SERVER_NAME: don't apply server name header this time - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_add_http_header_status(struct lws *wsi, - unsigned int code, unsigned char **p, - unsigned char *end); -/** - * lws_add_http_header_by_name() - append named header and value - * - * \param wsi: the connection to check - * \param name: the hdr name, like "my-header" - * \param value: the value after the = for this header - * \param length: the length of the value - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Appends name: value to the headers - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -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); -/** - * lws_add_http_header_by_token() - append given header and value - * - * \param wsi: the connection to check - * \param token: the token index for the hdr - * \param value: the value after the = for this header - * \param length: the length of the value - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Appends name=value to the headers, but is able to take advantage of better - * HTTP/2 coding mechanisms where possible. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -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); -/** - * lws_add_http_header_content_length() - append content-length helper - * - * \param wsi: the connection to check - * \param content_length: the content length to use - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Appends content-length: content_length to the headers - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_add_http_header_content_length(struct lws *wsi, - lws_filepos_t content_length, - unsigned char **p, unsigned char *end); -/** - * lws_finalize_http_header() - terminate header block - * - * \param wsi: the connection to check - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Indicates no more headers will be added - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_finalize_http_header(struct lws *wsi, unsigned char **p, - unsigned char *end); - -/** - * lws_finalize_write_http_header() - Helper finializing and writing http headers - * - * \param wsi: the connection to check - * \param start: pointer to the start of headers in the buffer, eg &buf[LWS_PRE] - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Terminates the headers correctly accoring to the protocol in use (h1 / h2) - * and writes the headers. Returns nonzero for error. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_finalize_write_http_header(struct lws *wsi, unsigned char *start, - unsigned char **p, unsigned char *end); - -#define LWS_ILLEGAL_HTTP_CONTENT_LEN ((lws_filepos_t)-1ll) - -/** - * lws_add_http_common_headers() - Helper preparing common http headers - * - * \param wsi: the connection to check - * \param code: an HTTP code like 200, 404 etc (see enum http_status) - * \param content_type: the content type, like "text/html" - * \param content_len: the content length, in bytes - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Adds the initial response code, so should be called first. - * - * Code may additionally take OR'd flags: - * - * LWSAHH_FLAG_NO_SERVER_NAME: don't apply server name header this time - * - * This helper just calls public apis to simplify adding headers that are - * commonly needed. If it doesn't fit your case, or you want to add additional - * headers just call the public apis directly yourself for what you want. - * - * You can miss out the content length header by providing the constant - * LWS_ILLEGAL_HTTP_CONTENT_LEN for the content_len. - * - * It does not call lws_finalize_http_header(), to allow you to add further - * headers after calling this. You will need to call that yourself at the end. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -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); - -enum { - LWSHUMETH_GET, - LWSHUMETH_POST, - LWSHUMETH_OPTIONS, - LWSHUMETH_PUT, - LWSHUMETH_PATCH, - LWSHUMETH_DELETE, - LWSHUMETH_CONNECT, - LWSHUMETH_HEAD, - LWSHUMETH_COLON_PATH, -}; - -/** - * lws_http_get_uri_and_method() - Get information on method and url - * - * \param wsi: the connection to get information on - * \param puri_ptr: points to pointer to set to url - * \param puri_len: points to int to set to uri length - * - * Returns -1 or method index as one of the LWSHUMETH_ constants - * - * If returns method, *puri_ptr is set to the method's URI string and *puri_len - * to its length - */ - -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len); - -///@} - -/*! \defgroup urlendec Urlencode and Urldecode - * \ingroup http - * - * ##HTML chunked Substitution - * - * APIs for receiving chunks of text, replacing a set of variable names via - * a callback, and then prepending and appending HTML chunked encoding - * headers. - */ -//@{ - -/** - * lws_urlencode() - like strncpy but with urlencoding - * - * \param escaped: output buffer - * \param string: input buffer ('/0' terminated) - * \param len: output buffer max length - * - * Because urlencoding expands the output string, it's not - * possible to do it in-place, ie, with escaped == string - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_urlencode(char *escaped, const char *string, int len); - -/* - * URLDECODE 1 / 2 - * - * This simple urldecode only operates until the first '\0' and requires the - * data to exist all at once - */ -/** - * lws_urldecode() - like strncpy but with urldecoding - * - * \param string: output buffer - * \param escaped: input buffer ('\0' terminated) - * \param len: output buffer max length - * - * This is only useful for '\0' terminated strings - * - * Since urldecoding only shrinks the output string, it is possible to - * do it in-place, ie, string == escaped - * - * Returns 0 if completed OK or nonzero for urldecode violation (non-hex chars - * where hex required, etc) - */ -LWS_VISIBLE LWS_EXTERN int -lws_urldecode(char *string, const char *escaped, int len); -///@} - -/** - * lws_return_http_status() - Return simple http status - * \param wsi: Websocket instance (available from user callback) - * \param code: Status index, eg, 404 - * \param html_body: User-readable HTML description < 1KB, or NULL - * - * Helper to report HTTP errors back to the client cleanly and - * consistently - */ -LWS_VISIBLE LWS_EXTERN int -lws_return_http_status(struct lws *wsi, unsigned int code, - const char *html_body); - -/** - * lws_http_redirect() - write http redirect out on wsi - * - * \param wsi: websocket connection - * \param code: HTTP response code (eg, 301) - * \param loc: where to redirect to - * \param len: length of loc - * \param p: pointer current position in buffer (updated as we write) - * \param end: pointer to end of buffer - * - * Returns amount written, or < 0 indicating fatal write failure. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len, - unsigned char **p, unsigned char *end); - -/** - * lws_http_transaction_completed() - wait for new http transaction or close - * \param wsi: websocket connection - * - * Returns 1 if the HTTP connection must close now - * Returns 0 and resets connection to wait for new HTTP header / - * transaction if possible - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_http_transaction_completed(struct lws *wsi); - -/** - * lws_http_headers_detach() - drop the associated headers storage and allow - * it to be reused by another connection - * \param wsi: http connection - * - * If the wsi has an ah headers struct attached, detach it. - */ -LWS_VISIBLE LWS_EXTERN int -lws_http_headers_detach(struct lws *wsi); - -/** - * lws_http_mark_sse() - called to indicate this http stream is now doing SSE - * - * \param wsi: http connection - * - * Cancel any timeout on the wsi, and for h2, mark the network connection as - * containing an immortal stream for the duration the SSE stream is open. - */ -LWS_VISIBLE LWS_EXTERN int -lws_http_mark_sse(struct lws *wsi); - -/** - * lws_h2_client_stream_long_poll_rxonly() - h2 stream to immortal read-only - * - * \param wsi: h2 stream client wsi - * - * Send END_STREAM-flagged zero-length DATA frame to set client stream wsi into - * half-closed (local) and remote into half-closed (remote). Set the client - * stream wsi to be immortal (not subject to timeouts). - * - * Used if the remote server supports immortal long poll to put the stream into - * a read-only state where it can wait as long as needed for rx. - * - * Returns 0 if the process (which happens asynchronously) started or non-zero - * if it wasn't an h2 stream. - */ -LWS_VISIBLE LWS_EXTERN int -lws_h2_client_stream_long_poll_rxonly(struct lws *wsi); - -/** - * lws_http_compression_apply() - apply an http compression transform - * - * \param wsi: the wsi to apply the compression transform to - * \param name: NULL, or the name of the compression transform, eg, "deflate" - * \param p: pointer to pointer to headers buffer - * \param end: pointer to end of headers buffer - * \param decomp: 0 = add compressor to wsi, 1 = add decompressor - * - * This allows transparent compression of dynamically generated HTTP. The - * requested compression (eg, "deflate") is only applied if the client headers - * indicated it was supported (and it has support in lws), otherwise it's a NOP. - * - * If the requested compression method is NULL, then the supported compression - * formats are tried, and for non-decompression (server) mode the first that's - * found on the client's accept-encoding header is chosen. - * - * NOTE: the compression transform, same as h2 support, relies on the user - * code using LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part - * written. The internal lws fileserving code already does this. - * - * If the library was built without the cmake option - * LWS_WITH_HTTP_STREAM_COMPRESSION set, then a NOP is provided for this api, - * allowing user code to build either way and use compression if available. - */ -LWS_VISIBLE LWS_EXTERN int -lws_http_compression_apply(struct lws *wsi, const char *name, - unsigned char **p, unsigned char *end, char decomp); - -/** - * lws_http_is_redirected_to_get() - true if redirected to GET - * - * \param wsi: the wsi to check - * - * Check if the wsi is currently in GET mode, after, eg, doing a POST and - * receiving a 303. - */ -LWS_VISIBLE LWS_EXTERN int -lws_http_is_redirected_to_get(struct lws *wsi); - -/** - * lws_h2_update_peer_txcredit() - manually update stream peer tx credit - * - * \param wsi: the h2 child stream whose peer credit to change - * \param sid: the stream ID, or LWS_H2_STREAM_SID for the wsi stream ID - * \param bump: signed change to confer upon peer tx credit for sid - * - * In conjunction with LCCSCF_H2_MANUAL_RXFLOW flag, allows the user code to - * selectively starve the remote peer of the ability to send us data on a client - * connection. - * - * Normally lws sends an initial window size for the peer to send to it of 0, - * but during the header phase it sends a WINDOW_UPDATE to increase the amount - * available. LCCSCF_H2_MANUAL_RXFLOW restricts this initial increase in tx - * credit for the stream, before it has been asked to send us anything, to the - * amount specified in the client info .manual_initial_tx_credit member, and - * this api can be called to send the other side permission to send us up to - * \p bump additional bytes. - * - * The nwsi tx credit is updated automatically for exactly what was sent to us - * on a stream with LCCSCF_H2_MANUAL_RXFLOW flag, but the stream's own tx credit - * must be handled manually by user code via this api. - * - * Returns 0 for success or nonzero for failure. - */ -#define LWS_H2_STREAM_SID -1 -LWS_VISIBLE LWS_EXTERN int -lws_h2_update_peer_txcredit(struct lws *wsi, int sid, int bump); - - -/** - * lws_h2_get_peer_txcredit_estimate() - return peer tx credit estimate - * - * \param wsi: the h2 child stream whose peer credit estimate to return - * - * Returns the estimated amount of tx credit at the peer, in other words the - * number of bytes the peer is authorized to send to us. - * - * It's an 'estimate' because we don't know how much is already in flight - * towards us and actually already used. - */ -LWS_VISIBLE LWS_EXTERN int -lws_h2_get_peer_txcredit_estimate(struct lws *wsi); - -///@} - diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-jose.h libwebsockets-2.4.2/include/libwebsockets/lws-jose.h --- libwebsockets-4.0.20/include/libwebsockets/lws-jose.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-jose.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,212 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -enum lws_jws_jose_hdr_indexes { - LJJHI_ALG, /* REQUIRED */ - LJJHI_JKU, /* Optional: string */ - LJJHI_JWK, /* Optional: jwk JSON object: public key: */ - LJJHI_KID, /* Optional: string */ - LJJHI_X5U, /* Optional: string: url of public key cert / chain */ - LJJHI_X5C, /* Optional: base64 (NOT -url): actual cert */ - LJJHI_X5T, /* Optional: base64url: SHA-1 of actual cert */ - LJJHI_X5T_S256, /* Optional: base64url: SHA-256 of actual cert */ - LJJHI_TYP, /* Optional: string: media type */ - LJJHI_CTY, /* Optional: string: content media type */ - LJJHI_CRIT, /* Optional for send, REQUIRED: array of strings: - * mustn't contain standardized strings or null set */ - - LJJHI_RECIPS_HDR, - LJJHI_RECIPS_HDR_ALG, - LJJHI_RECIPS_HDR_KID, - LJJHI_RECIPS_EKEY, - - LJJHI_ENC, /* JWE only: Optional: string */ - LJJHI_ZIP, /* JWE only: Optional: string ("DEF" = deflate) */ - - LJJHI_EPK, /* Additional arg for JWE ECDH: ephemeral public key */ - LJJHI_APU, /* Additional arg for JWE ECDH: base64url */ - LJJHI_APV, /* Additional arg for JWE ECDH: base64url */ - LJJHI_IV, /* Additional arg for JWE AES: base64url */ - LJJHI_TAG, /* Additional arg for JWE AES: base64url */ - LJJHI_P2S, /* Additional arg for JWE PBES2: base64url: salt */ - LJJHI_P2C, /* Additional arg for JWE PBES2: integer: count */ - - LWS_COUNT_JOSE_HDR_ELEMENTS -}; - -enum lws_jose_algtype { - LWS_JOSE_ENCTYPE_NONE, - - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5, - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP, - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS, - - LWS_JOSE_ENCTYPE_ECDSA, - LWS_JOSE_ENCTYPE_ECDHES, - - LWS_JOSE_ENCTYPE_AES_CBC, - LWS_JOSE_ENCTYPE_AES_CFB128, - LWS_JOSE_ENCTYPE_AES_CFB8, - LWS_JOSE_ENCTYPE_AES_CTR, - LWS_JOSE_ENCTYPE_AES_ECB, - LWS_JOSE_ENCTYPE_AES_OFB, - LWS_JOSE_ENCTYPE_AES_XTS, /* care: requires double-length key */ - LWS_JOSE_ENCTYPE_AES_GCM, -}; - -/* there's a table of these defined in lws-gencrypto-common.c */ - -struct lws_jose_jwe_alg { - enum lws_genhash_types hash_type; - enum lws_genhmac_types hmac_type; - enum lws_jose_algtype algtype_signing; /* the signing cipher */ - enum lws_jose_algtype algtype_crypto; /* the encryption cipher */ - const char *alg; /* the JWA enc alg name, eg "ES512" */ - const char *curve_name; /* NULL, or, eg, "P-256" */ - unsigned short keybits_min, keybits_fixed; - unsigned short ivbits; -}; - -/* - * For JWS, "JOSE header" is defined to be the union of... - * - * o JWS Protected Header - * o JWS Unprotected Header - * - * For JWE, the "JOSE header" is the union of... - * - * o JWE Protected Header - * o JWE Shared Unprotected Header - * o JWE Per-Recipient Unprotected Header - */ - -#define LWS_JWS_MAX_RECIPIENTS 3 - -struct lws_jws_recpient { - /* - * JOSE per-recipient unprotected header... for JWS this contains - * protected / header / signature - */ - struct lws_gencrypto_keyelem unprot[LWS_COUNT_JOSE_HDR_ELEMENTS]; - struct lws_jwk jwk_ephemeral; /* recipient ephemeral key if any */ - struct lws_jwk jwk; /* recipient "jwk" key if any */ -}; - -struct lws_jose { - /* JOSE protected and unprotected header elements */ - struct lws_gencrypto_keyelem e[LWS_COUNT_JOSE_HDR_ELEMENTS]; - - struct lws_jws_recpient recipient[LWS_JWS_MAX_RECIPIENTS]; - - char typ[32]; - - /* information from the protected header part */ - const struct lws_jose_jwe_alg *alg; - const struct lws_jose_jwe_alg *enc_alg; - - int recipients; /* count of used recipient[] entries */ -}; - -/** - * lws_jose_init() - prepare a struct lws_jose for use - * - * \param jose: the jose header struct to prepare - */ -LWS_VISIBLE LWS_EXTERN void -lws_jose_init(struct lws_jose *jose); - -/** - * lws_jose_destroy() - retire a struct lws_jose from use - * - * \param jose: the jose header struct to destroy - */ -LWS_VISIBLE LWS_EXTERN void -lws_jose_destroy(struct lws_jose *jose); - -/** - * lws_gencrypto_jws_alg_to_definition() - look up a jws alg name - * - * \param alg: the jws alg name - * \param jose: pointer to the pointer to the info struct to set on success - * - * Returns 0 if *jose set, else nonzero for failure - */ -LWS_VISIBLE LWS_EXTERN int -lws_gencrypto_jws_alg_to_definition(const char *alg, - const struct lws_jose_jwe_alg **jose); - -/** - * lws_gencrypto_jwe_alg_to_definition() - look up a jwe alg name - * - * \param alg: the jwe alg name - * \param jose: pointer to the pointer to the info struct to set on success - * - * Returns 0 if *jose set, else nonzero for failure - */ -LWS_VISIBLE LWS_EXTERN int -lws_gencrypto_jwe_alg_to_definition(const char *alg, - const struct lws_jose_jwe_alg **jose); - -/** - * lws_gencrypto_jwe_enc_to_definition() - look up a jwe enc name - * - * \param alg: the jwe enc name - * \param jose: pointer to the pointer to the info struct to set on success - * - * Returns 0 if *jose set, else nonzero for failure - */ -LWS_VISIBLE LWS_EXTERN int -lws_gencrypto_jwe_enc_to_definition(const char *enc, - const struct lws_jose_jwe_alg **jose); - -/** - * lws_jws_parse_jose() - parse a JWS JOSE header - * - * \param jose: the jose struct to set to parsing results - * \param buf: the raw JOSE header - * \param len: the length of the raw JOSE header - * \param temp: parent-owned buffer to "allocate" elements into - * \param temp_len: amount of space available in temp - * - * returns the amount of temp used, or -1 for error - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_parse_jose(struct lws_jose *jose, - const char *buf, int len, char *temp, int *temp_len); - -/** - * lws_jwe_parse_jose() - parse a JWE JOSE header - * - * \param jose: the jose struct to set to parsing results - * \param buf: the raw JOSE header - * \param len: the length of the raw JOSE header - * \param temp: parent-owned buffer to "allocate" elements into - * \param temp_len: amount of space available in temp - * - * returns the amount of temp used, or -1 for error - */ -LWS_VISIBLE LWS_EXTERN int -lws_jwe_parse_jose(struct lws_jose *jose, - const char *buf, int len, char *temp, int *temp_len); - diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-jwe.h libwebsockets-2.4.2/include/libwebsockets/lws-jwe.h --- libwebsockets-4.0.20/include/libwebsockets/lws-jwe.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-jwe.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,164 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * JWE Compact Serialization consists of - * - * BASE64URL(UTF8(JWE Protected Header)) || '.' || - * BASE64URL(JWE Encrypted Key) || '.' || - * BASE64URL(JWE Initialization Vector) || '.' || - * BASE64URL(JWE Ciphertext) || '.' || - * BASE64URL(JWE Authentication Tag) - */ - -#define LWS_JWE_RFC3394_OVERHEAD_BYTES 8 -#define LWS_JWE_AES_IV_BYTES 16 - -#define LWS_JWE_LIMIT_RSA_KEY_BITS 4096 -#define LWS_JWE_LIMIT_AES_KEY_BITS (512 + 64) /* RFC3394 Key Wrap adds 64b */ -#define LWS_JWE_LIMIT_EC_KEY_BITS 528 /* 521 rounded to byte boundary */ -#define LWS_JWE_LIMIT_HASH_BITS (LWS_GENHASH_LARGEST * 8) - -/* the largest key element for any cipher */ -#define LWS_JWE_LIMIT_KEY_ELEMENT_BYTES (LWS_JWE_LIMIT_RSA_KEY_BITS / 8) - - -struct lws_jwe { - struct lws_jose jose; - struct lws_jws jws; - struct lws_jwk jwk; - - /* - * We have to keep a copy of the CEK so we can reuse it with later - * key encryptions for the multiple recipient case. - */ - uint8_t cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES]; - unsigned int cek_valid:1; - - int recip; -}; - -LWS_VISIBLE LWS_EXTERN void -lws_jwe_init(struct lws_jwe *jwe, struct lws_context *context); - -LWS_VISIBLE LWS_EXTERN void -lws_jwe_destroy(struct lws_jwe *jwe); - -LWS_VISIBLE LWS_EXTERN void -lws_jwe_be64(uint64_t c, uint8_t *p8); - -/* - * JWE Compact Serialization consists of - * - * BASE64URL(UTF8(JWE Protected Header)) || '.' || - * BASE64URL(JWE Encrypted Key) || '.' || - * BASE64URL(JWE Initialization Vector) || '.' || - * BASE64URL(JWE Ciphertext) || '.' || - * BASE64URL(JWE Authentication Tag) - */ - -LWS_VISIBLE LWS_EXTERN int -lws_jwe_render_compact(struct lws_jwe *jwe, char *out, size_t out_len); - -LWS_VISIBLE int -lws_jwe_render_flattened(struct lws_jwe *jwe, char *out, size_t out_len); - -LWS_VISIBLE LWS_EXTERN int -lws_jwe_json_parse(struct lws_jwe *jwe, const uint8_t *buf, int len, - char *temp, int *temp_len); - -/** - * lws_jwe_auth_and_decrypt() - confirm and decrypt JWE - * - * \param jose: jose context - * \param jws: jws / jwe context... .map and .map_b64 must be filled already - * - * This is a high level JWE decrypt api that takes a jws with the maps - * already processed, and if the authentication passes, returns the decrypted - * plaintext in jws.map.buf[LJWE_CTXT] and its length in jws.map.len[LJWE_CTXT]. - * - * In the jws, the following fields must have been set by the caller - * - * .context - * .jwk (the key encryption key) - * .map - * .map_b64 - * - * Having the b64 and decoded maps filled externally makes it flexible where - * the data was picked from, eg, from a Complete JWE JSON serialization, a - * flattened one, or a Compact Serialization. - * - * Returns decrypt length, or -1 for failure. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jwe_auth_and_decrypt(struct lws_jwe *jwe, char *temp, int *temp_len); - -/** - * lws_jwe_encrypt() - perform JWE encryption - * - * \param jose: the JOSE header information (encryption types, etc) - * \param jws: the JWE elements, pointer to jwk etc - * \param temp: parent-owned buffer to "allocate" elements into - * \param temp_len: amount of space available in temp - * - * May be called up to LWS_JWS_MAX_RECIPIENTS times to encrypt the same CEK - * multiple ways on the same JWE payload. - * - * returns the amount of temp used, or -1 for error. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jwe_encrypt(struct lws_jwe *jwe, char *temp, int *temp_len); - -/** - * lws_jwe_create_packet() - add b64 sig to b64 hdr + payload - * - * \param jwe: the struct lws_jwe we are trying to render - * \param payload: unencoded payload JSON - * \param len: length of unencoded payload JSON - * \param nonce: Nonse string to include in protected header - * \param out: buffer to take signed packet - * \param out_len: size of \p out buffer - * \param conext: lws_context to get random from - * - * This creates a "flattened" JWS packet from the jwk and the plaintext - * payload, and signs it. The packet is written into \p out. - * - * This does the whole packet assembly and signing, calling through to - * lws_jws_sign_from_b64() as part of the process. - * - * Returns the length written to \p out, or -1. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jwe_create_packet(struct lws_jwe *jwe, - const char *payload, size_t len, const char *nonce, - char *out, size_t out_len, struct lws_context *context); - - -/* only exposed because we have test vectors that need it */ -LWS_VISIBLE LWS_EXTERN int -lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *enc_cek, - uint8_t *aad, int aad_len); - -/* only exposed because we have test vectors that need it */ -LWS_VISIBLE LWS_EXTERN int -lws_jwa_concat_kdf(struct lws_jwe *jwe, int direct, - uint8_t *out, const uint8_t *shared_secret, int sslen); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-jwk.h libwebsockets-2.4.2/include/libwebsockets/lws-jwk.h --- libwebsockets-4.0.20/include/libwebsockets/lws-jwk.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-jwk.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,218 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup jwk JSON Web Keys - * ## JSON Web Keys API - * - * Lws provides an API to parse JSON Web Keys into a struct lws_gencrypto_keyelem. - * - * "oct" and "RSA" type keys are supported. For "oct" keys, they are held in - * the "e" member of the struct lws_gencrypto_keyelem. - * - * Keys elements are allocated on the heap. You must destroy the allocations - * in the struct lws_gencrypto_keyelem by calling - * lws_genrsa_destroy_elements() when you are finished with it. - */ -///@{ - -enum enum_jwk_meta_tok { - JWK_META_KTY, - JWK_META_KID, - JWK_META_USE, - JWK_META_KEY_OPS, - JWK_META_X5C, - JWK_META_ALG, - - LWS_COUNT_JWK_ELEMENTS -}; - -struct lws_jwk { - /* key data elements */ - struct lws_gencrypto_keyelem e[LWS_GENCRYPTO_MAX_KEYEL_COUNT]; - /* generic meta key elements, like KID */ - struct lws_gencrypto_keyelem meta[LWS_COUNT_JWK_ELEMENTS]; - int kty; /**< one of LWS_JWK_ */ - char private_key; /* nonzero = has private key elements */ -}; - -typedef int (*lws_jwk_key_import_callback)(struct lws_jwk *s, void *user); - -struct lws_jwk_parse_state { - struct lws_jwk *jwk; - char b64[(((8192 / 8) * 4) / 3) + 1]; /* enough for 8Kb key */ - lws_jwk_key_import_callback per_key_cb; - void *user; - int pos; - unsigned short possible; -}; - -/** lws_jwk_import() - Create a JSON Web key from the textual representation - * - * \param jwk: the JWK object to create - * \param cb: callback for each jwk-processed key, or NULL if importing a single - * key with no parent "keys" JSON - * \param user: pointer to be passed to the callback, otherwise ignored by lws. - * NULL if importing a single key with no parent "keys" JSON - * \param in: a single JWK JSON stanza in utf-8 - * \param len: the length of the JWK JSON stanza in bytes - * - * Creates an lws_jwk struct filled with data from the JSON representation. - * - * There are two ways to use this... with some protocols a single jwk is - * delivered with no parent "keys": [] array. If you call this with cb and - * user as NULL, then the input will be interpreted like that and the results - * placed in s. - * - * The second case is that you are dealing with a "keys":[] array with one or - * more keys in it. In this case, the function iterates through the keys using - * s as a temporary jwk, and calls the user-provided callback for each key in - * turn while it return 0 (nonzero return from the callback terminates the - * iteration through any further keys). - */ -LWS_VISIBLE LWS_EXTERN int -lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user, - const char *in, size_t len); - -/** lws_jwk_destroy() - Destroy a JSON Web key - * - * \param jwk: the JWK object to destroy - * - * All allocations in the lws_jwk are destroyed - */ -LWS_VISIBLE LWS_EXTERN void -lws_jwk_destroy(struct lws_jwk *jwk); - -/** lws_jwk_dup_oct() - Set a jwk to a dup'd binary OCT key - * - * \param jwk: the JWK object to set - * \param key: the JWK object to destroy - * \param len: the JWK object to destroy - * - * Sets the kty to OCT, allocates len bytes for K and copies len bytes of key - * into the allocation. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jwk_dup_oct(struct lws_jwk *jwk, const void *key, int len); - -#define LWSJWKF_EXPORT_PRIVATE (1 << 0) -#define LWSJWKF_EXPORT_NOCRLF (1 << 1) - -/** lws_jwk_export() - Export a JSON Web key to a textual representation - * - * \param jwk: the JWK object to export - * \param flags: control export options - * \param p: the buffer to write the exported JWK to - * \param len: the length of the buffer \p p in bytes... reduced by used amount - * - * Returns length of the used part of the buffer if OK, or -1 for error. - * - * \p flags can be OR-ed together - * - * LWSJWKF_EXPORT_PRIVATE: default is only public part, set this to also export - * the private part - * - * LWSJWKF_EXPORT_NOCRLF: normally adds a CRLF at the end of the export, if - * you need to suppress it, set this flag - * - * Serializes the content of the JWK into a char buffer. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jwk_export(struct lws_jwk *jwk, int flags, char *p, int *len); - -/** lws_jwk_load() - Import a JSON Web key from a file - * - * \param jwk: the JWK object to load into - * \param filename: filename to load from - * \param cb: optional callback for each key - * \param user: opaque user pointer passed to cb if given - * - * Returns 0 for OK or -1 for failure - * - * There are two ways to use this... with some protocols a single jwk is - * delivered with no parent "keys": [] array. If you call this with cb and - * user as NULL, then the input will be interpreted like that and the results - * placed in s. - * - * The second case is that you are dealing with a "keys":[] array with one or - * more keys in it. In this case, the function iterates through the keys using - * s as a temporary jwk, and calls the user-provided callback for each key in - * turn while it return 0 (nonzero return from the callback terminates the - * iteration through any further keys, leaving the last one in s). - */ -LWS_VISIBLE LWS_EXTERN int -lws_jwk_load(struct lws_jwk *jwk, const char *filename, - lws_jwk_key_import_callback cb, void *user); - -/** lws_jwk_save() - Export a JSON Web key to a file - * - * \param jwk: the JWK object to save from - * \param filename: filename to save to - * - * Returns 0 for OK or -1 for failure - */ -LWS_VISIBLE LWS_EXTERN int -lws_jwk_save(struct lws_jwk *jwk, const char *filename); - -/** lws_jwk_rfc7638_fingerprint() - jwk to RFC7638 compliant fingerprint - * - * \param jwk: the JWK object to fingerprint - * \param digest32: buffer to take 32-byte digest - * - * Returns 0 for OK or -1 for failure - */ -LWS_VISIBLE LWS_EXTERN int -lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32); - -/** lws_jwk_strdup_meta() - allocate a duplicated string meta element - * - * \param jwk: the JWK object to fingerprint - * \param idx: JWK_META_ element index - * \param in: string to copy - * \param len: length of string to copy - * - * Returns 0 for OK or -1 for failure - */ -LWS_VISIBLE LWS_EXTERN int -lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx, - const char *in, int len); - - -LWS_VISIBLE LWS_EXTERN int -lws_jwk_dump(struct lws_jwk *jwk); - -/** lws_jwk_generate() - create a new key of given type and characteristics - * - * \param context: the struct lws_context used for RNG - * \param jwk: the JWK object to fingerprint - * \param kty: One of the LWS_GENCRYPTO_KTY_ key types - * \param bits: for OCT and RSA keys, the number of bits - * \param curve: for EC keys, the name of the curve - * - * Returns 0 for OK or -1 for failure - */ -LWS_VISIBLE int -lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk, - enum lws_gencrypto_kty kty, int bits, const char *curve); - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-jws.h libwebsockets-2.4.2/include/libwebsockets/lws-jws.h --- libwebsockets-4.0.20/include/libwebsockets/lws-jws.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-jws.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,405 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup jws JSON Web Signature - * ## JSON Web Signature API - * - * Lws provides an API to check and create RFC7515 JSON Web Signatures - * - * SHA256/384/512 HMAC, and RSA 256/384/512 are supported. - * - * The API uses your TLS library crypto, but works exactly the same no matter - * what your TLS backend is. - */ -///@{ - -/* - * The maps are built to work with both JWS (LJWS_) and JWE (LJWE_), and are - * sized to the slightly larger JWE case. - */ - -enum enum_jws_sig_elements { - - /* JWS block namespace */ - LJWS_JOSE, - LJWS_PYLD, - LJWS_SIG, - LJWS_UHDR, - - /* JWE block namespace */ - LJWE_JOSE = 0, - LJWE_EKEY, - LJWE_IV, - LJWE_CTXT, - LJWE_ATAG, - LJWE_AAD, - - LWS_JWS_MAX_COMPACT_BLOCKS -}; - -struct lws_jws_map { - const char *buf[LWS_JWS_MAX_COMPACT_BLOCKS]; - uint32_t len[LWS_JWS_MAX_COMPACT_BLOCKS]; -}; - -#define LWS_JWS_MAX_SIGS 3 - -struct lws_jws { - struct lws_jwk *jwk; /* the struct lws_jwk containing the signing key */ - struct lws_context *context; /* the lws context (used to get random) */ - struct lws_jws_map map, map_b64; -}; - -/* jws EC signatures do not have ASN.1 in them, meaning they're incompatible - * with generic signatures. - */ - -/** - * lws_jws_init() - initialize a jws for use - * - * \param jws: pointer to the jws to initialize - * \param jwk: the jwk to use with this jws - * \param context: the lws_context to use - */ -LWS_VISIBLE LWS_EXTERN void -lws_jws_init(struct lws_jws *jws, struct lws_jwk *jwk, - struct lws_context *context); - -/** - * lws_jws_destroy() - scrub a jws - * - * \param jws: pointer to the jws to destroy - * - * Call before the jws goes out of scope. - * - * Elements defined in the jws are zeroed. - */ -LWS_VISIBLE LWS_EXTERN void -lws_jws_destroy(struct lws_jws *jws); - -/** - * lws_jws_sig_confirm_compact() - check signature - * - * \param map: pointers and lengths for each of the unencoded JWS elements - * \param jwk: public key - * \param context: lws_context - * \param temp: scratchpad - * \param temp_len: length of scratchpad - * - * Confirms the signature on a JWS. Use if you have non-b64 plain JWS elements - * in a map... it'll make a temp b64 version needed for comparison. See below - * for other variants. - * - * Returns 0 on match. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_sig_confirm_compact(struct lws_jws_map *map, struct lws_jwk *jwk, - struct lws_context *context, - char *temp, int *temp_len); - -LWS_VISIBLE LWS_EXTERN int -lws_jws_sig_confirm_compact_b64_map(struct lws_jws_map *map_b64, - struct lws_jwk *jwk, - struct lws_context *context, - char *temp, int *temp_len); - -/** - * lws_jws_sig_confirm_compact_b64() - check signature on b64 compact JWS - * - * \param in: pointer to b64 jose.payload[.hdr].sig - * \param len: bytes available at \p in - * \param map: map to take decoded non-b64 content - * \param jwk: public key - * \param context: lws_context - * \param temp: scratchpad - * \param temp_len: size of scratchpad - * - * Confirms the signature on a JWS. Use if you have you have b64 compact layout - * (jose.payload.hdr.sig) as an aggregated string... it'll make a temp plain - * version needed for comparison. - * - * Returns 0 on match. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_sig_confirm_compact_b64(const char *in, size_t len, - struct lws_jws_map *map, - struct lws_jwk *jwk, - struct lws_context *context, - char *temp, int *temp_len); - -/** - * lws_jws_sig_confirm() - check signature on plain + b64 JWS elements - * - * \param map_b64: pointers and lengths for each of the b64-encoded JWS elements - * \param map: pointers and lengths for each of the unencoded JWS elements - * \param jwk: public key - * \param context: lws_context - * - * Confirms the signature on a JWS. Use if you have you already have both b64 - * compact layout (jose.payload.hdr.sig) and decoded JWS elements in maps. - * - * If you had the b64 string and called lws_jws_compact_decode() on it, you - * will end up with both maps, and can use this api version, saving needlessly - * regenerating any temp map. - * - * Returns 0 on match. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_sig_confirm(struct lws_jws_map *map_b64, /* b64-encoded */ - struct lws_jws_map *map, /* non-b64 */ - struct lws_jwk *jwk, struct lws_context *context); - -/** - * lws_jws_sign_from_b64() - add b64 sig to b64 hdr + payload - * - * \param jose: jose header information - * \param jws: information to include in the signature - * \param b64_sig: output buffer for b64 signature - * \param sig_len: size of \p b64_sig output buffer - * - * This adds a b64-coded JWS signature of the b64-encoded protected header - * and b64-encoded payload, at \p b64_sig. The signature will be as large - * as the N element of the RSA key when the RSA key is used, eg, 512 bytes for - * a 4096-bit key, and then b64-encoding on top. - * - * In some special cases, there is only payload to sign and no header, in that - * case \p b64_hdr may be NULL, and only the payload will be hashed before - * signing. - * - * Returns the length of the encoded signature written to \p b64_sig, or -1. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_sign_from_b64(struct lws_jose *jose, struct lws_jws *jws, char *b64_sig, - size_t sig_len); - -/** - * lws_jws_compact_decode() - converts and maps compact serialization b64 sections - * - * \param in: the incoming compact serialized b64 - * \param len: the length of the incoming compact serialized b64 - * \param map: pointer to the results structure - * \param map_b64: NULL, or pointer to a second results structure taking block - * information about the undecoded b64 - * \param out: buffer to hold decoded results - * \param out_len: size of out in bytes - * - * Returns number of sections (2 if "none", else 3), or -1 if illegal. - * - * map is set to point to the start and hold the length of each decoded block. - * If map_b64 is non-NULL, then it's set with information about the input b64 - * blocks. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_compact_decode(const char *in, int len, struct lws_jws_map *map, - struct lws_jws_map *map_b64, char *out, int *out_len); - -LWS_VISIBLE LWS_EXTERN int -lws_jws_compact_encode(struct lws_jws_map *map_b64, /* b64-encoded */ - const struct lws_jws_map *map, /* non-b64 */ - char *buf, int *out_len); - -LWS_VISIBLE LWS_EXTERN int -lws_jws_sig_confirm_json(const char *in, size_t len, - struct lws_jws *jws, struct lws_jwk *jwk, - struct lws_context *context, - char *temp, int *temp_len); - -/** - * lws_jws_write_flattened_json() - create flattened JSON sig - * - * \param jws: information to include in the signature - * \param flattened: output buffer for JSON - * \param len: size of \p flattened output buffer - * - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_write_flattened_json(struct lws_jws *jws, char *flattened, size_t len); - -/** - * lws_jws_write_compact() - create flattened JSON sig - * - * \param jws: information to include in the signature - * \param compact: output buffer for compact format - * \param len: size of \p flattened output buffer - * - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_write_compact(struct lws_jws *jws, char *compact, size_t len); - - - -/* - * below apis are not normally needed if dealing with whole JWS... they're - * useful for creating from scratch - */ - - -/** - * lws_jws_dup_element() - allocate space for an element and copy data into it - * - * \param map: map to create the element in - * \param idx: index of element in the map to create - * \param temp: space to allocate in - * \param temp_len: available space at temp - * \param in: data to duplicate into element - * \param in_len: length of data to duplicate - * \param actual_alloc: 0 for same as in_len, else actual allocation size - * - * Copies in_len from in to temp, if temp_len is sufficient. - * - * Returns 0 or -1 if not enough space in temp / temp_len. - * - * Over-allocation can be acheived by setting actual_alloc to the real - * allocation desired... in_len will be copied into it. - * - * *temp_len is reduced by actual_alloc if successful. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_dup_element(struct lws_jws_map *map, int idx, - char *temp, int *temp_len, const void *in, size_t in_len, - size_t actual_alloc); - -/** - * lws_jws_randomize_element() - create an element and fill with random - * - * \param context: lws_context used for random - * \param map: map to create the element in - * \param idx: index of element in the map to create - * \param temp: space to allocate in - * \param temp_len: available space at temp - * \param random_len: length of data to fill with random - * \param actual_alloc: 0 for same as random_len, else actual allocation size - * - * Randomize random_len bytes at temp, if temp_len is sufficient. - * - * Returns 0 or -1 if not enough space in temp / temp_len. - * - * Over-allocation can be acheived by setting actual_alloc to the real - * allocation desired... the first random_len will be filled with random. - * - * *temp_len is reduced by actual_alloc if successful. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_randomize_element(struct lws_context *context, - struct lws_jws_map *map, - int idx, char *temp, int *temp_len, size_t random_len, - size_t actual_alloc); - -/** - * lws_jws_alloc_element() - create an element and reserve space for content - * - * \param map: map to create the element in - * \param idx: index of element in the map to create - * \param temp: space to allocate in - * \param temp_len: available space at temp - * \param len: logical length of element - * \param actual_alloc: 0 for same as len, else actual allocation size - * - * Allocate len bytes at temp, if temp_len is sufficient. - * - * Returns 0 or -1 if not enough space in temp / temp_len. - * - * Over-allocation can be acheived by setting actual_alloc to the real - * allocation desired... the element logical length will be set to len. - * - * *temp_len is reduced by actual_alloc if successful. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_alloc_element(struct lws_jws_map *map, int idx, char *temp, - int *temp_len, size_t len, size_t actual_alloc); - -/** - * lws_jws_encode_b64_element() - create an b64-encoded element - * - * \param map: map to create the element in - * \param idx: index of element in the map to create - * \param temp: space to allocate in - * \param temp_len: available space at temp - * \param in: pointer to unencoded input - * \param in_len: length of unencoded input - * - * Allocate len bytes at temp, if temp_len is sufficient. - * - * Returns 0 or -1 if not enough space in temp / temp_len. - * - * Over-allocation can be acheived by setting actual_alloc to the real - * allocation desired... the element logical length will be set to len. - * - * *temp_len is reduced by actual_alloc if successful. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_encode_b64_element(struct lws_jws_map *map, int idx, - char *temp, int *temp_len, const void *in, - size_t in_len); - - -/** - * lws_jws_b64_compact_map() - find block starts and lengths in compact b64 - * - * \param in: pointer to b64 jose.payload[.hdr].sig - * \param len: bytes available at \p in - * \param map: output struct with pointers and lengths for each JWS element - * - * Scans a jose.payload[.hdr].sig b64 string and notes where the blocks start - * and their length into \p map. - * - * Returns number of blocks if OK. May return <0 if malformed. - * May not fill all map entries. - */ - -LWS_VISIBLE LWS_EXTERN int -lws_jws_b64_compact_map(const char *in, int len, struct lws_jws_map *map); - - -/** - * lws_jws_base64_enc() - encode input data into b64url data - * - * \param in: the incoming plaintext - * \param in_len: the length of the incoming plaintext in bytes - * \param out: the buffer to store the b64url encoded data to - * \param out_max: the length of \p out in bytes - * - * Returns either -1 if problems, or the number of bytes written to \p out. - */ -LWS_VISIBLE LWS_EXTERN int -lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max); - -/** - * lws_jws_encode_section() - encode input data into b64url data, - * prepending . if not first - * - * \param in: the incoming plaintext - * \param in_len: the length of the incoming plaintext in bytes - * \param first: nonzero if the first section - * \param p: the buffer to store the b64url encoded data to - * \param end: just past the end of p - * - * Returns either -1 if problems, or the number of bytes written to \p out. - * If the section is not the first one, '.' is prepended. - */ - -LWS_VISIBLE LWS_EXTERN int -lws_jws_encode_section(const char *in, size_t in_len, int first, char **p, - char *end); -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-lejp.h libwebsockets-2.4.2/include/libwebsockets/lws-lejp.h --- libwebsockets-4.0.20/include/libwebsockets/lws-lejp.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-lejp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,299 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup lejp JSON parser - * ##JSON parsing related functions - * \ingroup lwsapi - * - * LEJP is an extremely lightweight JSON stream parser included in lws. - */ -//@{ -struct lejp_ctx; - -#if !defined(LWS_ARRAY_SIZE) -#define LWS_ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0])) -#endif -#define LEJP_FLAG_WS_KEEP 64 -#define LEJP_FLAG_WS_COMMENTLINE 32 - -enum lejp_states { - LEJP_IDLE = 0, - LEJP_MEMBERS = 1, - LEJP_M_P = 2, - LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3, - LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4, - LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5, - LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6, - LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7, - LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8, - LEJP_MP_DELIM = 9, - LEJP_MP_VALUE = 10, - LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11, - LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12, - LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13, - LEJP_MP_COMMA_OR_END = 14, - LEJP_MP_ARRAY_END = 15, -}; - -enum lejp_reasons { - LEJP_CONTINUE = -1, - LEJP_REJECT_IDLE_NO_BRACE = -2, - LEJP_REJECT_MEMBERS_NO_CLOSE = -3, - LEJP_REJECT_MP_NO_OPEN_QUOTE = -4, - LEJP_REJECT_MP_STRING_UNDERRUN = -5, - LEJP_REJECT_MP_ILLEGAL_CTRL = -6, - LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7, - LEJP_REJECT_ILLEGAL_HEX = -8, - LEJP_REJECT_MP_DELIM_MISSING_COLON = -9, - LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10, - LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11, - LEJP_REJECT_MP_VAL_NUM_FORMAT = -12, - LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13, - LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14, - LEJP_REJECT_MP_C_OR_E_UNDERF = -15, - LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16, - LEJP_REJECT_MP_ARRAY_END_MISSING = -17, - LEJP_REJECT_STACK_OVERFLOW = -18, - LEJP_REJECT_MP_DELIM_ISTACK = -19, - LEJP_REJECT_NUM_TOO_LONG = -20, - LEJP_REJECT_MP_C_OR_E_NEITHER = -21, - LEJP_REJECT_UNKNOWN = -22, - LEJP_REJECT_CALLBACK = -23 -}; - -#define LEJP_FLAG_CB_IS_VALUE 64 - -enum lejp_callbacks { - LEJPCB_CONSTRUCTED = 0, - LEJPCB_DESTRUCTED = 1, - - LEJPCB_START = 2, - LEJPCB_COMPLETE = 3, - LEJPCB_FAILED = 4, - - LEJPCB_PAIR_NAME = 5, - - LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6, - LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7, - LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8, - LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9, - LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10, - LEJPCB_VAL_STR_START = 11, /* notice handle separately */ - LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12, - LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13, - - LEJPCB_ARRAY_START = 14, - LEJPCB_ARRAY_END = 15, - - LEJPCB_OBJECT_START = 16, - LEJPCB_OBJECT_END = 17, -}; - -/** - * _lejp_callback() - User parser actions - * \param ctx: LEJP context - * \param reason: Callback reason - * - * Your user callback is associated with the context at construction time, - * and receives calls as the parsing progresses. - * - * All of the callbacks may be ignored and just return 0. - * - * The reasons it might get called, found in @reason, are: - * - * LEJPCB_CONSTRUCTED: The context was just constructed... you might want to - * perform one-time allocation for the life of the context. - * - * LEJPCB_DESTRUCTED: The context is being destructed... if you made any - * allocations at construction-time, you can free them now - * - * LEJPCB_START: Parsing is beginning at the first byte of input - * - * LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or - * positive return code from lejp_parse indicating the - * amount of unused bytes left in the input buffer - * - * LEJPCB_FAILED: Parsing failed. You'll get a negative error code - * returned from lejp_parse - * - * LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed, - * this callback occurs. You can find the new name at - * the end of ctx->path[] - * - * LEJPCB_VAL_TRUE: The "true" value appeared - * - * LEJPCB_VAL_FALSE: The "false" value appeared - * - * LEJPCB_VAL_NULL: The "null" value appeared - * - * LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf - * - * LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf - * - * LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet - * - * LEJPCB_VAL_STR_CHUNK: We parsed LEJP_STRING_CHUNK -1 bytes of string data in - * ctx->buf, which is as much as we can buffer, so we are - * spilling it. If all your strings are less than - * LEJP_STRING_CHUNK - 1 bytes, you will never see this - * callback. - * - * LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the - * string is in ctx->buf. - * - * LEJPCB_ARRAY_START: An array started - * - * LEJPCB_ARRAY_END: An array ended - * - * LEJPCB_OBJECT_START: An object started - * - * LEJPCB_OBJECT_END: An object ended - */ -LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason); - -typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason); - -#ifndef LEJP_MAX_PARSING_STACK_DEPTH -#define LEJP_MAX_PARSING_STACK_DEPTH 5 -#endif -#ifndef LEJP_MAX_DEPTH -#define LEJP_MAX_DEPTH 12 -#endif -#ifndef LEJP_MAX_INDEX_DEPTH -#define LEJP_MAX_INDEX_DEPTH 5 -#endif -#ifndef LEJP_MAX_PATH -#define LEJP_MAX_PATH 128 -#endif -#ifndef LEJP_STRING_CHUNK -/* must be >= 30 to assemble floats */ -#define LEJP_STRING_CHUNK 254 -#endif - -enum num_flags { - LEJP_SEEN_MINUS = (1 << 0), - LEJP_SEEN_POINT = (1 << 1), - LEJP_SEEN_POST_POINT = (1 << 2), - LEJP_SEEN_EXP = (1 << 3) -}; - -struct _lejp_stack { - char s; /* lejp_state stack*/ - char p; /* path length */ - char i; /* index array length */ - char b; /* user bitfield */ -}; - -struct _lejp_parsing_stack { - void *user; /* private to the stack level */ - signed char (*callback)(struct lejp_ctx *ctx, char reason); - const char * const *paths; - uint8_t count_paths; - uint8_t ppos; - uint8_t path_match; -}; - -struct lejp_ctx { - - /* sorted by type for most compact alignment - * - * pointers - */ - void *user; - - /* arrays */ - - struct _lejp_parsing_stack pst[LEJP_MAX_PARSING_STACK_DEPTH]; - struct _lejp_stack st[LEJP_MAX_DEPTH]; - uint16_t i[LEJP_MAX_INDEX_DEPTH]; /* index array */ - uint16_t wild[LEJP_MAX_INDEX_DEPTH]; /* index array */ - char path[LEJP_MAX_PATH]; - char buf[LEJP_STRING_CHUNK + 1]; - - /* size_t */ - - size_t path_stride; /* 0 means default ptr size, else stride */ - - /* int */ - - uint32_t line; - - /* short */ - - uint16_t uni; - - /* char */ - - uint8_t npos; - uint8_t dcount; - uint8_t f; - uint8_t sp; /* stack head */ - uint8_t ipos; /* index stack depth */ - uint8_t count_paths; - uint8_t path_match; - uint8_t path_match_len; - uint8_t wildcount; - uint8_t pst_sp; /* parsing stack head */ -}; - -LWS_VISIBLE LWS_EXTERN void -lejp_construct(struct lejp_ctx *ctx, - signed char (*callback)(struct lejp_ctx *ctx, char reason), - void *user, const char * const *paths, unsigned char paths_count); - -LWS_VISIBLE LWS_EXTERN void -lejp_destruct(struct lejp_ctx *ctx); - -LWS_VISIBLE LWS_EXTERN int -lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len); - -LWS_VISIBLE LWS_EXTERN void -lejp_change_callback(struct lejp_ctx *ctx, - signed char (*callback)(struct lejp_ctx *ctx, char reason)); - -/* - * push the current paths / paths_count and lejp_cb to a stack in the ctx, and - * start using the new ones - */ -LWS_VISIBLE LWS_EXTERN int -lejp_parser_push(struct lejp_ctx *ctx, void *user, const char * const *paths, - unsigned char paths_count, lejp_callback lejp_cb); - -/* - * pop the previously used paths / paths_count and lejp_cb, and continue - * parsing using those as before - */ -LWS_VISIBLE LWS_EXTERN int -lejp_parser_pop(struct lejp_ctx *ctx); - -/* exported for use when reevaluating a path for use with a subcontext */ -LWS_VISIBLE LWS_EXTERN void -lejp_check_path_match(struct lejp_ctx *ctx); - -LWS_VISIBLE LWS_EXTERN int -lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len); - -LWS_VISIBLE LWS_EXTERN const char * -lejp_error_to_string(int e); -//@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-logs.h libwebsockets-2.4.2/include/libwebsockets/lws-logs.h --- libwebsockets-4.0.20/include/libwebsockets/lws-logs.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-logs.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,302 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup log Logging - * - * ##Logging - * - * Lws provides flexible and filterable logging facilities, which can be - * used inside lws and in user code. - * - * Log categories may be individually filtered bitwise, and directed to built-in - * sinks for syslog-compatible logging, or a user-defined function. - */ -///@{ - -#define LLL_ERR (1 << 0) -#define LLL_WARN (1 << 1) -#define LLL_NOTICE (1 << 2) -#define LLL_INFO (1 << 3) -#define LLL_DEBUG (1 << 4) -#define LLL_PARSER (1 << 5) -#define LLL_HEADER (1 << 6) -#define LLL_EXT (1 << 7) -#define LLL_CLIENT (1 << 8) -#define LLL_LATENCY (1 << 9) -#define LLL_USER (1 << 10) -#define LLL_THREAD (1 << 11) - -#define LLL_COUNT (12) /* set to count of valid flags */ - -/** - * lwsl_timestamp: generate logging timestamp string - * - * \param level: logging level - * \param p: char * buffer to take timestamp - * \param len: length of p - * - * returns length written in p - */ -LWS_VISIBLE LWS_EXTERN int -lwsl_timestamp(int level, char *p, int len); - -#if defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK) -#define _lws_log(aaa, ...) SMSG(__VA_ARGS__) -#else -LWS_VISIBLE LWS_EXTERN void _lws_log(int filter, const char *format, ...) LWS_FORMAT(2); -LWS_VISIBLE LWS_EXTERN void _lws_logv(int filter, const char *format, va_list vl); -#endif - -/* - * Figure out which logs to build in or not - */ - -#if defined(_DEBUG) - /* - * In DEBUG build, select all logs unless NO_LOGS - */ - #if defined(LWS_WITH_NO_LOGS) - #define _LWS_LINIT (LLL_ERR | LLL_USER) - #else - #define _LWS_LINIT ((1 << LLL_COUNT) - 1) - #endif -#else /* not _DEBUG */ - #define _LWS_LINIT (LLL_ERR | LLL_USER | LLL_WARN | LLL_NOTICE) -#endif /* _DEBUG */ - -/* - * Create either empty overrides or the ones forced at build-time. - * These overrides have the final say... any bits set in - * LWS_LOGGING_BITFIELD_SET force the build of those logs, any bits - * set in LWS_LOGGING_BITFIELD_CLEAR disable the build of those logs. - * - * If not defined lws decides based on CMAKE_BUILD_TYPE=DEBUG or not - */ - -#if defined(LWS_LOGGING_BITFIELD_SET) - #define _LWS_LBS (LWS_LOGGING_BITFIELD_SET) -#else - #define _LWS_LBS 0 -#endif - -#if defined(LWS_LOGGING_BITFIELD_CLEAR) - #define _LWS_LBC (LWS_LOGGING_BITFIELD_CLEAR) -#else - #define _LWS_LBC 0 -#endif - -/* - * Compute the final active logging bitfield for build - */ -#define _LWS_ENABLED_LOGS (((_LWS_LINIT) | (_LWS_LBS)) & ~(_LWS_LBC)) - -/* - * Individually enable or disable log levels for build - * depending on what was computed - */ - -#if (_LWS_ENABLED_LOGS & LLL_ERR) -#define lwsl_err(...) _lws_log(LLL_ERR, __VA_ARGS__) -#else -#define lwsl_err(...) do {} while(0) -#endif - -#if (_LWS_ENABLED_LOGS & LLL_WARN) -#define lwsl_warn(...) _lws_log(LLL_WARN, __VA_ARGS__) -#else -#define lwsl_warn(...) do {} while(0) -#endif - -#if (_LWS_ENABLED_LOGS & LLL_NOTICE) -#define lwsl_notice(...) _lws_log(LLL_NOTICE, __VA_ARGS__) -#else -#define lwsl_notice(...) do {} while(0) -#endif - -#if (_LWS_ENABLED_LOGS & LLL_INFO) -#define lwsl_info(...) _lws_log(LLL_INFO, __VA_ARGS__) -#else -#define lwsl_info(...) do {} while(0) -#endif - -#if (_LWS_ENABLED_LOGS & LLL_DEBUG) -#define lwsl_debug(...) _lws_log(LLL_DEBUG, __VA_ARGS__) -#else -#define lwsl_debug(...) do {} while(0) -#endif - -#if (_LWS_ENABLED_LOGS & LLL_PARSER) -#define lwsl_parser(...) _lws_log(LLL_PARSER, __VA_ARGS__) -#else -#define lwsl_parser(...) do {} while(0) -#endif - -#if (_LWS_ENABLED_LOGS & LLL_HEADER) -#define lwsl_header(...) _lws_log(LLL_HEADER, __VA_ARGS__) -#else -#define lwsl_header(...) do {} while(0) -#endif - -#if (_LWS_ENABLED_LOGS & LLL_EXT) -#define lwsl_ext(...) _lws_log(LLL_EXT, __VA_ARGS__) -#else -#define lwsl_ext(...) do {} while(0) -#endif - -#if (_LWS_ENABLED_LOGS & LLL_CLIENT) -#define lwsl_client(...) _lws_log(LLL_CLIENT, __VA_ARGS__) -#else -#define lwsl_client(...) do {} while(0) -#endif - -#if (_LWS_ENABLED_LOGS & LLL_LATENCY) -#define lwsl_latency(...) _lws_log(LLL_LATENCY, __VA_ARGS__) -#else -#define lwsl_latency(...) do {} while(0) -#endif - -#if (_LWS_ENABLED_LOGS & LLL_THREAD) -#define lwsl_thread(...) _lws_log(LLL_THREAD, __VA_ARGS__) -#else -#define lwsl_thread(...) do {} while(0) -#endif - -#if (_LWS_ENABLED_LOGS & LLL_USER) -#define lwsl_user(...) _lws_log(LLL_USER, __VA_ARGS__) -#else -#define lwsl_user(...) do {} while(0) -#endif - - -#define lwsl_hexdump_err(...) lwsl_hexdump_level(LLL_ERR, __VA_ARGS__) -#define lwsl_hexdump_warn(...) lwsl_hexdump_level(LLL_WARN, __VA_ARGS__) -#define lwsl_hexdump_notice(...) lwsl_hexdump_level(LLL_NOTICE, __VA_ARGS__) -#define lwsl_hexdump_info(...) lwsl_hexdump_level(LLL_INFO, __VA_ARGS__) -#define lwsl_hexdump_debug(...) lwsl_hexdump_level(LLL_DEBUG, __VA_ARGS__) - -/** - * lwsl_hexdump_level() - helper to hexdump a buffer at a selected debug level - * - * \param level: one of LLL_ constants - * \param vbuf: buffer start to dump - * \param len: length of buffer to dump - * - * If \p level is visible, does a nice hexdump -C style dump of \p vbuf for - * \p len bytes. This can be extremely convenient while debugging. - */ -LWS_VISIBLE LWS_EXTERN void -lwsl_hexdump_level(int level, const void *vbuf, size_t len); - -/** - * lwsl_hexdump() - helper to hexdump a buffer (DEBUG builds only) - * - * \param buf: buffer start to dump - * \param len: length of buffer to dump - * - * Calls through to lwsl_hexdump_level(LLL_DEBUG, ... for compatability. - * It's better to use lwsl_hexdump_level(level, ... directly so you can control - * the visibility. - */ -LWS_VISIBLE LWS_EXTERN void -lwsl_hexdump(const void *buf, size_t len); - -/** - * lws_is_be() - returns nonzero if the platform is Big Endian - */ -static LWS_INLINE int lws_is_be(void) { - const int probe = ~0xff; - - return *(const char *)&probe; -} - -/** - * lws_set_log_level() - Set the logging bitfield - * \param level: OR together the LLL_ debug contexts you want output from - * \param log_emit_function: NULL to leave it as it is, or a user-supplied - * function to perform log string emission instead of - * the default stderr one. - * - * log level defaults to "err", "warn" and "notice" contexts enabled and - * emission on stderr. If stderr is a tty (according to isatty()) then - * the output is coloured according to the log level using ANSI escapes. - */ -LWS_VISIBLE LWS_EXTERN void -lws_set_log_level(int level, - void (*log_emit_function)(int level, const char *line)); - -/** - * lwsl_emit_syslog() - helper log emit function writes to system log - * - * \param level: one of LLL_ log level indexes - * \param line: log string - * - * You use this by passing the function pointer to lws_set_log_level(), to set - * it as the log emit function, it is not called directly. - */ -LWS_VISIBLE LWS_EXTERN void -lwsl_emit_syslog(int level, const char *line); - -/** - * lwsl_emit_stderr() - helper log emit function writes to stderr - * - * \param level: one of LLL_ log level indexes - * \param line: log string - * - * You use this by passing the function pointer to lws_set_log_level(), to set - * it as the log emit function, it is not called directly. - * - * It prepends a system timestamp like [2018/11/13 07:41:57:3989] - * - * If stderr is a tty, then ansi colour codes are added. - */ -LWS_VISIBLE LWS_EXTERN void -lwsl_emit_stderr(int level, const char *line); - -/** - * lwsl_emit_stderr_notimestamp() - helper log emit function writes to stderr - * - * \param level: one of LLL_ log level indexes - * \param line: log string - * - * You use this by passing the function pointer to lws_set_log_level(), to set - * it as the log emit function, it is not called directly. - * - * If stderr is a tty, then ansi colour codes are added. - */ -LWS_VISIBLE LWS_EXTERN void -lwsl_emit_stderr_notimestamp(int level, const char *line); - -/** - * lwsl_visible() - returns true if the log level should be printed - * - * \param level: one of LLL_ log level indexes - * - * This is useful if you have to do work to generate the log content, you - * can skip the work if the log level used to print it is not actually - * enabled at runtime. - */ -LWS_VISIBLE LWS_EXTERN int -lwsl_visible(int level); - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-lwsac.h libwebsockets-2.4.2/include/libwebsockets/lws-lwsac.h --- libwebsockets-4.0.20/include/libwebsockets/lws-lwsac.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-lwsac.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,290 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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. - */ - -/** \defgroup lwsac lwsac - * - * ##Allocated Chunks - * - * If you know you will be allocating a large, unknown number of same or - * differently sized objects, it's certainly possible to do it with libc - * malloc. However the allocation cost in time and memory overhead can - * add up, and deallocation means walking the structure of every object and - * freeing them in turn. - * - * lwsac (LWS Allocated Chunks) allocates chunks intended to be larger - * than your objects (4000 bytes by default) which you linearly allocate from - * using lwsac_use(). - * - * If your next request won't fit in the current chunk, a new chunk is added - * to the chain of chunks and the allocaton done from there. If the request - * is larger than the chunk size, an oversize chunk is created to satisfy it. - * - * When you are finished with the allocations, you call lwsac_free() and - * free all the *chunks*. So you may have thousands of objects in the chunks, - * but they are all destroyed with the chunks without having to deallocate them - * one by one pointlessly. - */ -///@{ - -struct lwsac; -typedef unsigned char * lwsac_cached_file_t; - - -#define lws_list_ptr_container(P,T,M) ((T *)((char *)(P) - offsetof(T, M))) - -/* - * linked-list helper that's commonly useful to manage lists of things - * allocated using lwsac. - * - * These lists point to their corresponding "next" member in the target, NOT - * the original containing struct. To get the containing struct, you must use - * lws_list_ptr_container() to convert. - * - * It's like that because it means we no longer have to have the next pointer - * at the start of the struct, and we can have the same struct on multiple - * linked-lists with everything held in the struct itself. - */ -typedef void * lws_list_ptr; - -/* - * optional sorting callback called by lws_list_ptr_insert() to sort the right - * things inside the opqaue struct being sorted / inserted on the list. - */ -typedef int (*lws_list_ptr_sort_func_t)(lws_list_ptr a, lws_list_ptr b); - -#define lws_list_ptr_advance(_lp) _lp = *((void **)_lp) - -/* sort may be NULL if you don't care about order */ -LWS_VISIBLE LWS_EXTERN void -lws_list_ptr_insert(lws_list_ptr *phead, lws_list_ptr *add, - lws_list_ptr_sort_func_t sort); - - -/** - * lwsac_use - allocate / use some memory from a lwsac - * - * \param head: pointer to the lwsac list object - * \param ensure: the number of bytes we want to use - * \param chunk_size: 0, or the size of the chunk to (over)allocate if - * what we want won't fit in the current tail chunk. If - * 0, the default value of 4000 is used. If ensure is - * larger, it is used instead. - * - * This also serves to init the lwsac if *head is NULL. Basically it does - * whatever is necessary to return you a pointer to ensure bytes of memory - * reserved for the caller. - * - * This always allocates in the current chunk or a new chunk... see the - * lwsac_use_backfill() variant to try first to find space in earlier chunks. - * - * Returns NULL if OOM. - */ -LWS_VISIBLE LWS_EXTERN void * -lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size); - -/** - * lwsac_use_backfill - allocate / use some memory from a lwsac - * - * \param head: pointer to the lwsac list object - * \param ensure: the number of bytes we want to use - * \param chunk_size: 0, or the size of the chunk to (over)allocate if - * what we want won't fit in the current tail chunk. If - * 0, the default value of 4000 is used. If ensure is - * larger, it is used instead. - * - * This also serves to init the lwsac if *head is NULL. Basically it does - * whatever is necessary to return you a pointer to ensure bytes of memory - * reserved for the caller. - * - * Also checks if earlier blocks have enough remaining space to take the - * allocation before making a new allocation. - * - * Returns NULL if OOM. - */ -LWS_VISIBLE LWS_EXTERN void * -lwsac_use_backfill(struct lwsac **head, size_t ensure, size_t chunk_size); - -/** - * lwsac_use - allocate / use some memory from a lwsac - * - * \param head: pointer to the lwsac list object - * \param ensure: the number of bytes we want to use, which must be zeroed - * \param chunk_size: 0, or the size of the chunk to (over)allocate if - * what we want won't fit in the current tail chunk. If - * 0, the default value of 4000 is used. If ensure is - * larger, it is used instead. - * - * Same as lwsac_use(), but \p ensure bytes of memory at the return address - * are zero'd before returning. - * - * Returns NULL if OOM. - */ -LWS_VISIBLE LWS_EXTERN void * -lwsac_use_zero(struct lwsac **head, size_t ensure, size_t chunk_size); - -#define lwsac_use_zeroed lwsac_use_zero - -/** - * lwsac_free - deallocate all chunks in the lwsac and set head NULL - * - * \param head: pointer to the lwsac list object - * - * This deallocates all chunks in the lwsac, then sets *head to NULL. All - * lwsac_use() pointers are invalidated in one hit without individual frees. - */ -LWS_VISIBLE LWS_EXTERN void -lwsac_free(struct lwsac **head); - -/* - * Optional helpers useful for where consumers may need to defer destruction - * until all consumers are finished with the lwsac - */ - -/** - * lwsac_detach() - destroy an lwsac unless somebody else is referencing it - * - * \param head: pointer to the lwsac list object - * - * The creator of the lwsac can all this instead of lwsac_free() when it itself - * has finished with the lwsac, but other code may be consuming it. - * - * If there are no other references, the lwsac is destroyed, *head is set to - * NULL and that's the end; however if something else has called - * lwsac_reference() on the lwsac, it simply returns. When lws_unreference() - * is called and no references are left, it will be destroyed then. - */ -LWS_VISIBLE LWS_EXTERN void -lwsac_detach(struct lwsac **head); - -/** - * lwsac_reference() - increase the lwsac reference count - * - * \param head: pointer to the lwsac list object - * - * Increment the reference count on the lwsac to defer destruction. - */ -LWS_VISIBLE LWS_EXTERN void -lwsac_reference(struct lwsac *head); - -/** - * lwsac_reference() - increase the lwsac reference count - * - * \param head: pointer to the lwsac list object - * - * Decrement the reference count on the lwsac... if it reached 0 on a detached - * lwsac then the lwsac is immediately destroyed and *head set to NULL. - */ -LWS_VISIBLE LWS_EXTERN void -lwsac_unreference(struct lwsac **head); - -/** - * lwsac_extend() - try to increase the size of the last block - * - * \param head: pointer to the lwsac list object - * \param amount: amount to try to increase usage for - * - * This will either increase the usage reservation of the last allocated block - * by amount and return 0, or fail and return 1. - * - * This is very cheap to call and is designed to optimize usage after a static - * struct for vari-sized additional content which may flow into an additional - * block in a new chunk if necessary, but wants to make the most of the space - * in front of it first to try to avoid gaps and the new chunk if it can. - * - * The additional area if the call succeeds will have been memset to 0. - * - * To use it, the following must be true: - * - * - only the last lwsac use can be extended - * - * - if another use happens inbetween the use and extend, it will break - * - * - the use cannot have been using backfill - * - * - a user object must be tracking the current allocated size of the last use - * (lwsac doesn't know it) and increment by amount if the extend call succeeds - * - * Despite these restrictions this can be an important optimization for some - * cases - */ -LWS_VISIBLE LWS_EXTERN int -lwsac_extend(struct lwsac *head, int amount); - -/* helpers to keep a file cached in memory */ - -LWS_VISIBLE LWS_EXTERN void -lwsac_use_cached_file_start(lwsac_cached_file_t cache); - -LWS_VISIBLE LWS_EXTERN void -lwsac_use_cached_file_end(lwsac_cached_file_t *cache); - -LWS_VISIBLE LWS_EXTERN void -lwsac_use_cached_file_detach(lwsac_cached_file_t *cache); - -LWS_VISIBLE LWS_EXTERN int -lwsac_cached_file(const char *filepath, lwsac_cached_file_t *cache, - size_t *len); - -/* more advanced helpers */ - -/* offset from lac to start of payload, first = 1 = first lac in chain */ -LWS_VISIBLE LWS_EXTERN size_t -lwsac_sizeof(int first); - -LWS_VISIBLE LWS_EXTERN size_t -lwsac_get_tail_pos(struct lwsac *lac); - -LWS_VISIBLE LWS_EXTERN struct lwsac * -lwsac_get_next(struct lwsac *lac); - -LWS_VISIBLE LWS_EXTERN size_t -lwsac_align(size_t length); - -LWS_VISIBLE LWS_EXTERN void -lwsac_info(struct lwsac *head); - -LWS_VISIBLE LWS_EXTERN uint64_t -lwsac_total_alloc(struct lwsac *head); - -LWS_VISIBLE LWS_EXTERN uint64_t -lwsac_total_overhead(struct lwsac *head); - -/** - * lwsac_scan_extant() - returns existing copy of blob, or NULL - * - * \param head: the lwsac to scan - * \param find: the blob to look for - * \param len: the length of the blob to look for - * \param nul: nonzero if the next byte must be NUL - * - * Helper that looks through a whole lwsac for a given binary blob already - * present. Used in the case that lwsac contents are const once written, and - * strings or blobs may be repeated in the input: this allows the earlier - * copy to be pointed to by subsequent references without repeating the string - * or blob redundantly. - */ -LWS_VISIBLE LWS_EXTERN uint8_t * -lwsac_scan_extant(struct lwsac *head, uint8_t *find, size_t len, int nul); - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-misc.h libwebsockets-2.4.2/include/libwebsockets/lws-misc.h --- libwebsockets-4.0.20/include/libwebsockets/lws-misc.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-misc.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,899 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_SPAWN) - -#if defined(WIN32) || defined(_WIN32) -#else -#include -#include -#endif -#endif - -/** \defgroup misc Miscellaneous APIs -* ##Miscellaneous APIs -* -* Various APIs outside of other categories -*/ -///@{ - -struct lws_buflist; - -/** - * lws_buflist_append_segment(): add buffer to buflist at head - * - * \param head: list head - * \param buf: buffer to stash - * \param len: length of buffer to stash - * - * Returns -1 on OOM, 1 if this was the first segment on the list, and 0 if - * it was a subsequent segment. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf, - size_t len); -/** - * lws_buflist_next_segment_len(): number of bytes left in current segment - * - * \param head: list head - * \param buf: if non-NULL, *buf is written with the address of the start of - * the remaining data in the segment - * - * Returns the number of bytes left in the current segment. 0 indicates - * that the buflist is empty (there are no segments on the buflist). - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf); - -/** - * lws_buflist_use_segment(): remove len bytes from the current segment - * - * \param head: list head - * \param len: number of bytes to mark as used - * - * If len is less than the remaining length of the current segment, the position - * in the current segment is simply advanced and it returns. - * - * If len uses up the remaining length of the current segment, then the segment - * is deleted and the list head moves to the next segment if any. - * - * Returns the number of bytes left in the current segment. 0 indicates - * that the buflist is empty (there are no segments on the buflist). - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_buflist_use_segment(struct lws_buflist **head, size_t len); - -/** - * lws_buflist_total_len(): Get the total size of the buflist - * - * \param head: list head - * - * Returns the total number of bytes held on all segments of the buflist - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_buflist_total_len(struct lws_buflist **head); - -/** - * lws_buflist_linear_copy(): copy everything out as one without consuming - * - * \param head: list head - * \param ofs: start offset into buflist in bytes - * \param buf: buffer to copy linearly into - * \param len: length of buffer available - * - * Returns -1 if len is too small, or bytes copied. Happy to do partial - * copies, returns 0 when there are no more bytes to copy. - */ -LWS_VISIBLE LWS_EXTERN int -lws_buflist_linear_copy(struct lws_buflist **head, size_t ofs, uint8_t *buf, - size_t len); - -/** - * lws_buflist_destroy_all_segments(): free all segments on the list - * - * \param head: list head - * - * This frees everything on the list unconditionally. *head is always - * NULL after this. - */ -LWS_VISIBLE LWS_EXTERN void -lws_buflist_destroy_all_segments(struct lws_buflist **head); - -/** - * lws_buflist_describe(): debug helper logging buflist status - * - * \param head: list head - * \param id: pointer shown in debug list - * \param reason: reason string show in debug list - * - * Iterates through the buflist segments showing position and size. - * This only exists when lws was built in debug mode - */ -LWS_VISIBLE LWS_EXTERN void -lws_buflist_describe(struct lws_buflist **head, void *id, const char *reason); - -/** - * lws_ptr_diff(): helper to report distance between pointers as an int - * - * \param head: the pointer with the larger address - * \param tail: the pointer with the smaller address - * - * This helper gives you an int representing the number of bytes further - * forward the first pointer is compared to the second pointer. - */ -#define lws_ptr_diff(head, tail) \ - ((int)((char *)(head) - (char *)(tail))) - -/** - * lws_snprintf(): snprintf that truncates the returned length too - * - * \param str: destination buffer - * \param size: bytes left in destination buffer - * \param format: format string - * \param ...: args for format - * - * This lets you correctly truncate buffers by concatenating lengths, if you - * reach the limit the reported length doesn't exceed the limit. - */ -LWS_VISIBLE LWS_EXTERN int -lws_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3); - -/** - * lws_strncpy(): strncpy that guarantees NUL on truncated copy - * - * \param dest: destination buffer - * \param src: source buffer - * \param size: bytes left in destination buffer - * - * This lets you correctly truncate buffers by concatenating lengths, if you - * reach the limit the reported length doesn't exceed the limit. - */ -LWS_VISIBLE LWS_EXTERN char * -lws_strncpy(char *dest, const char *src, size_t size); - -/* - * Variation where we want to use the smaller of two lengths, useful when the - * source string is not NUL terminated - */ -#define lws_strnncpy(dest, src, size1, destsize) \ - lws_strncpy(dest, src, (size_t)(size1 + 1) < (size_t)(destsize) ? \ - (size_t)(size1 + 1) : (size_t)(destsize)) - -/** - * lws_hex_to_byte_array(): convert hex string like 0123456789ab into byte data - * - * \param h: incoming NUL-terminated hex string - * \param dest: array to fill with binary decodes of hex pairs from h - * \param max: maximum number of bytes dest can hold, must be at least half - * the size of strlen(h) - * - * This converts hex strings into an array of 8-bit representations, ie the - * input "abcd" produces two bytes of value 0xab and 0xcd. - * - * Returns number of bytes produced into \p dest, or -1 on error. - * - * Errors include non-hex chars and an odd count of hex chars in the input - * string. - */ -LWS_VISIBLE LWS_EXTERN int -lws_hex_to_byte_array(const char *h, uint8_t *dest, int max); - -/* - * lws_timingsafe_bcmp(): constant time memcmp - * - * \param a: first buffer - * \param b: second buffer - * \param len: count of bytes to compare - * - * Return 0 if the two buffers are the same, else nonzero. - * - * Always compares all of the buffer before returning, so it can't be used as - * a timing oracle. - */ - -LWS_VISIBLE LWS_EXTERN int -lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len); - -/** - * lws_get_random(): fill a buffer with platform random data - * - * \param context: the lws context - * \param buf: buffer to fill - * \param len: how much to fill - * - * Fills buf with len bytes of random. Returns the number of bytes set, if - * not equal to len, then getting the random failed. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_get_random(struct lws_context *context, void *buf, size_t len); -/** - * lws_daemonize(): make current process run in the background - * - * \param _lock_path: the filepath to write the lock file - * - * Spawn lws as a background process, taking care of various things - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_daemonize(const char *_lock_path); -/** - * lws_get_library_version(): return string describing the version of lws - * - * On unix, also includes the git describe - */ -LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT -lws_get_library_version(void); - -/** - * lws_wsi_user() - get the user data associated with the connection - * \param wsi: lws connection - * - * Not normally needed since it's passed into the callback - */ -LWS_VISIBLE LWS_EXTERN void * -lws_wsi_user(struct lws *wsi); - -/** - * lws_set_wsi_user() - set the user data associated with the client connection - * \param wsi: lws connection - * \param user: user data - * - * By default lws allocates this and it's not legal to externally set it - * yourself. However client connections may have it set externally when the - * connection is created... if so, this api can be used to modify it at - * runtime additionally. - */ -LWS_VISIBLE LWS_EXTERN void -lws_set_wsi_user(struct lws *wsi, void *user); - -/** - * lws_parse_uri: cut up prot:/ads:port/path into pieces - * Notice it does so by dropping '\0' into input string - * and the leading / on the path is consequently lost - * - * \param p: incoming uri string.. will get written to - * \param prot: result pointer for protocol part (https://) - * \param ads: result pointer for address part - * \param port: result pointer for port part - * \param path: result pointer for path part - * - * You may also refer to unix socket addresses, using a '+' at the start of - * the address. In this case, the address should end with ':', which is - * treated as the separator between the address and path (the normal separator - * '/' is a valid part of the socket path). Eg, - * - * http://+/var/run/mysocket:/my/path - * - * If the first character after the + is '@', it's interpreted by lws client - * processing as meaning to use linux abstract namespace sockets, the @ is - * replaced with a '\0' before use. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_parse_uri(char *p, const char **prot, const char **ads, int *port, - const char **path); -/** - * lws_cmdline_option(): simple commandline parser - * - * \param argc: count of argument strings - * \param argv: argument strings - * \param val: string to find - * - * Returns NULL if the string \p val is not found in the arguments. - * - * If it is found, then it returns a pointer to the next character after \p val. - * So if \p val is "-d", then for the commandlines "myapp -d15" and - * "myapp -d 15", in both cases the return will point to the "15". - * - * In the case there is no argument, like "myapp -d", the return will - * either point to the '\\0' at the end of -d, or to the start of the - * next argument, ie, will be non-NULL. - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_cmdline_option(int argc, const char **argv, const char *val); - -/** - * lws_cmdline_option_handle_builtin(): apply standard cmdline options - * - * \param argc: count of argument strings - * \param argv: argument strings - * \param info: context creation info - * - * Applies standard options to the context creation info to save them having - * to be (unevenly) copied into the minimal examples. - * - * Applies default log levels that can be overriden by -d - */ -LWS_VISIBLE LWS_EXTERN void -lws_cmdline_option_handle_builtin(int argc, const char **argv, - struct lws_context_creation_info *info); - -/** - * lws_now_secs(): return seconds since 1970-1-1 - */ -LWS_VISIBLE LWS_EXTERN unsigned long -lws_now_secs(void); - -/** - * lws_now_usecs(): return useconds since 1970-1-1 - */ -LWS_VISIBLE LWS_EXTERN lws_usec_t -lws_now_usecs(void); - -/** - * lws_get_context - Allow getting lws_context from a Websocket connection - * instance - * - * With this function, users can access context in the callback function. - * Otherwise users may have to declare context as a global variable. - * - * \param wsi: Websocket connection instance - */ -LWS_VISIBLE LWS_EXTERN struct lws_context * LWS_WARN_UNUSED_RESULT -lws_get_context(const struct lws *wsi); - -/** - * lws_get_vhost_listen_port - Find out the port number a vhost is listening on - * - * In the case you passed 0 for the port number at context creation time, you - * can discover the port number that was actually chosen for the vhost using - * this api. - * - * \param vhost: Vhost to get listen port from - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_get_vhost_listen_port(struct lws_vhost *vhost); - -/** - * lws_get_count_threads(): how many service threads the context uses - * - * \param context: the lws context - * - * By default this is always 1, if you asked for more than lws can handle it - * will clip the number of threads. So you can use this to find out how many - * threads are actually in use. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_get_count_threads(struct lws_context *context); - -/** - * lws_get_parent() - get parent wsi or NULL - * \param wsi: lws connection - * - * Specialized wsi like cgi stdin/out/err are associated to a parent wsi, - * this allows you to get their parent. - */ -LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT -lws_get_parent(const struct lws *wsi); - -/** - * lws_get_child() - get child wsi or NULL - * \param wsi: lws connection - * - * Allows you to find a related wsi from the parent wsi. - */ -LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT -lws_get_child(const struct lws *wsi); - -/** - * lws_get_effective_uid_gid() - find out eventual uid and gid while still root - * - * \param context: lws context - * \param uid: pointer to uid result - * \param gid: pointer to gid result - * - * This helper allows you to find out what the uid and gid for the process will - * be set to after the privileges are dropped, beforehand. So while still root, - * eg in LWS_CALLBACK_PROTOCOL_INIT, you can arrange things like cache dir - * and subdir creation / permissions down /var/cache dynamically. - */ -LWS_VISIBLE LWS_EXTERN void -lws_get_effective_uid_gid(struct lws_context *context, int *uid, int *gid); - -/** - * lws_get_udp() - get wsi's udp struct - * - * \param wsi: lws connection - * - * Returns NULL or pointer to the wsi's UDP-specific information - */ -LWS_VISIBLE LWS_EXTERN const struct lws_udp * LWS_WARN_UNUSED_RESULT -lws_get_udp(const struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN void * -lws_get_opaque_parent_data(const struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN void -lws_set_opaque_parent_data(struct lws *wsi, void *data); - -LWS_VISIBLE LWS_EXTERN void * -lws_get_opaque_user_data(const struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN void -lws_set_opaque_user_data(struct lws *wsi, void *data); - -LWS_VISIBLE LWS_EXTERN int -lws_get_child_pending_on_writable(const struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN void -lws_clear_child_pending_on_writable(struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN int -lws_get_close_length(struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN unsigned char * -lws_get_close_payload(struct lws *wsi); - -/** - * lws_get_network_wsi() - Returns wsi that has the tcp connection for this wsi - * - * \param wsi: wsi you have - * - * Returns wsi that has the tcp connection (which may be the incoming wsi) - * - * HTTP/1 connections will always return the incoming wsi - * HTTP/2 connections may return a different wsi that has the tcp connection - */ -LWS_VISIBLE LWS_EXTERN -struct lws *lws_get_network_wsi(struct lws *wsi); - -/** - * lws_set_allocator() - custom allocator support - * - * \param realloc - * - * Allows you to replace the allocator (and deallocator) used by lws - */ -LWS_VISIBLE LWS_EXTERN void -lws_set_allocator(void *(*realloc)(void *ptr, size_t size, const char *reason)); - -enum { - /* - * Flags for enable and disable rxflow with reason bitmap and with - * backwards-compatible single bool - */ - LWS_RXFLOW_REASON_USER_BOOL = (1 << 0), - LWS_RXFLOW_REASON_HTTP_RXBUFFER = (1 << 6), - LWS_RXFLOW_REASON_H2_PPS_PENDING = (1 << 7), - - LWS_RXFLOW_REASON_APPLIES = (1 << 14), - LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT = (1 << 13), - LWS_RXFLOW_REASON_APPLIES_ENABLE = LWS_RXFLOW_REASON_APPLIES | - LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT, - LWS_RXFLOW_REASON_APPLIES_DISABLE = LWS_RXFLOW_REASON_APPLIES, - LWS_RXFLOW_REASON_FLAG_PROCESS_NOW = (1 << 12), - -}; - -/** - * lws_rx_flow_control() - Enable and disable socket servicing for - * received packets. - * - * If the output side of a server process becomes choked, this allows flow - * control for the input side. - * - * \param wsi: Websocket connection instance to get callback for - * \param enable: 0 = disable read servicing for this connection, 1 = enable - * - * If you need more than one additive reason for rxflow control, you can give - * iLWS_RXFLOW_REASON_APPLIES_ENABLE or _DISABLE together with one or more of - * b5..b0 set to idicate which bits to enable or disable. If any bits are - * enabled, rx on the connection is suppressed. - * - * LWS_RXFLOW_REASON_FLAG_PROCESS_NOW flag may also be given to force any change - * in rxflowbstatus to benapplied immediately, this should be used when you are - * changing a wsi flow control state from outside a callback on that wsi. - */ -LWS_VISIBLE LWS_EXTERN int -lws_rx_flow_control(struct lws *wsi, int enable); - -/** - * lws_rx_flow_allow_all_protocol() - Allow all connections with this protocol to receive - * - * When the user server code realizes it can accept more input, it can - * call this to have the RX flow restriction removed from all connections using - * the given protocol. - * \param context: lws_context - * \param protocol: all connections using this protocol will be allowed to receive - */ -LWS_VISIBLE LWS_EXTERN void -lws_rx_flow_allow_all_protocol(const struct lws_context *context, - const struct lws_protocols *protocol); - -/** - * lws_remaining_packet_payload() - Bytes to come before "overall" - * rx fragment is complete - * \param wsi: Websocket instance (available from user callback) - * - * This tracks how many bytes are left in the current ws fragment, according - * to the ws length given in the fragment header. - * - * If the message was in a single fragment, and there is no compression, this - * is the same as "how much data is left to read for this message". - * - * However, if the message is being sent in multiple fragments, this will - * reflect the unread amount of the current **fragment**, not the message. With - * ws, it is legal to not know the length of the message before it completes. - * - * Additionally if the message is sent via the negotiated permessage-deflate - * extension, this number only tells the amount of **compressed** data left to - * be read, since that is the only information available at the ws layer. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_remaining_packet_payload(struct lws *wsi); - -#if defined(LWS_WITH_DIR) - -typedef enum { - LDOT_UNKNOWN, - LDOT_FILE, - LDOT_DIR, - LDOT_LINK, - LDOT_FIFO, - LDOTT_SOCKET, - LDOT_CHAR, - LDOT_BLOCK -} lws_dir_obj_type_t; - -struct lws_dir_entry { - const char *name; - lws_dir_obj_type_t type; -}; - -typedef int -lws_dir_callback_function(const char *dirpath, void *user, - struct lws_dir_entry *lde); - -/** - * lws_dir() - get a callback for everything in a directory - * - * \param dirpath: the directory to scan - * \param user: pointer to give to callback - * \param cb: callback to receive information on each file or dir - * - * Calls \p cb (with \p user) for every object in dirpath. - * - * This wraps whether it's using POSIX apis, or libuv (as needed for windows, - * since it refuses to support POSIX apis for this). - */ -LWS_VISIBLE LWS_EXTERN int -lws_dir(const char *dirpath, void *user, lws_dir_callback_function cb); -#endif - -/** - * lws_get_allocated_heap() - if the platform supports it, returns amount of - * heap allocated by lws itself - * - * On glibc currently, this reports the total amount of current logical heap - * allocation, found by tracking the amount allocated by lws_malloc() and - * friends and accounting for freed allocations via lws_free(). - * - * This is useful for confirming where processwide heap allocations actually - * come from... this number represents all lws internal allocations, for - * fd tables, wsi allocations, ah, etc combined. It doesn't include allocations - * from user code, since lws_malloc() etc are not exported from the library. - * - * On other platforms, it always returns 0. - */ -size_t lws_get_allocated_heap(void); - -/** - * lws_get_tsi() - Get thread service index wsi belong to - * \param wsi: websocket connection to check - * - * Returns more than zero (or zero if only one service thread as is the default). - */ -LWS_VISIBLE LWS_EXTERN int -lws_get_tsi(struct lws *wsi); - -/** - * lws_is_ssl() - Find out if connection is using SSL - * \param wsi: websocket connection to check - * - * Returns nonzero if the wsi is inside a tls tunnel, else zero. - */ -LWS_VISIBLE LWS_EXTERN int -lws_is_ssl(struct lws *wsi); -/** - * lws_is_cgi() - find out if this wsi is running a cgi process - * - * \param wsi: lws connection - */ -LWS_VISIBLE LWS_EXTERN int -lws_is_cgi(struct lws *wsi); - -/** - * lws_open() - platform-specific wrapper for open that prepares the fd - * - * \param __file: the filepath to open - * \param __oflag: option flags - * - * This is a wrapper around platform open() that sets options on the fd - * according to lws policy. Currently that is FD_CLOEXEC to stop the opened - * fd being available to any child process forked by user code. - */ -LWS_VISIBLE LWS_EXTERN int -lws_open(const char *__file, int __oflag, ...); - -struct lws_wifi_scan { /* generic wlan scan item */ - struct lws_wifi_scan *next; - char ssid[32]; - int32_t rssi; /* divide by .count to get db */ - uint8_t bssid[6]; - uint8_t count; - uint8_t channel; - uint8_t authmode; -}; - -#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS) -/** - * lws_get_ssl() - Return wsi's SSL context structure - * \param wsi: websocket connection - * - * Returns pointer to the SSL library's context structure - */ -LWS_VISIBLE LWS_EXTERN SSL* -lws_get_ssl(struct lws *wsi); -#endif - -LWS_VISIBLE LWS_EXTERN void -lws_explicit_bzero(void *p, size_t len); - -typedef struct lws_humanize_unit { - const char *name; /* array ends with NULL name */ - uint64_t factor; -} lws_humanize_unit_t; - -LWS_VISIBLE LWS_EXTERN const lws_humanize_unit_t humanize_schema_si[7]; -LWS_VISIBLE LWS_EXTERN const lws_humanize_unit_t humanize_schema_si_bytes[7]; -LWS_VISIBLE LWS_EXTERN const lws_humanize_unit_t humanize_schema_us[8]; - -/** - * lws_humanize() - Convert possibly large number to human-readable uints - * - * \param buf: result string buffer - * \param len: remaining length in \p buf - * \param value: the uint64_t value to represent - * \param schema: and array of scaling factors and units - * - * This produces a concise string representation of \p value, referencing the - * schema \p schema of scaling factors and units to find the smallest way to - * render it. - * - * Three schema are exported from lws for general use, humanize_schema_si, which - * represents as, eg, " 22.130Gi" or " 128 "; humanize_schema_si_bytes - * which is the same but shows, eg, " 22.130GiB", and humanize_schema_us, - * which represents a count of us as a human-readable time like " 14.350min", - * or " 1.500d". - * - * You can produce your own schema. - */ - -LWS_VISIBLE LWS_EXTERN int -lws_humanize(char *buf, int len, uint64_t value, - const lws_humanize_unit_t *schema); - -LWS_VISIBLE LWS_EXTERN void -lws_ser_wu16be(uint8_t *b, uint16_t u); - -LWS_VISIBLE LWS_EXTERN void -lws_ser_wu32be(uint8_t *b, uint32_t u32); - -LWS_VISIBLE LWS_EXTERN void -lws_ser_wu64be(uint8_t *b, uint64_t u64); - -LWS_VISIBLE LWS_EXTERN uint16_t -lws_ser_ru16be(const uint8_t *b); - -LWS_VISIBLE LWS_EXTERN uint32_t -lws_ser_ru32be(const uint8_t *b); - -LWS_VISIBLE LWS_EXTERN uint64_t -lws_ser_ru64be(const uint8_t *b); - -LWS_VISIBLE LWS_EXTERN int -lws_vbi_encode(uint64_t value, void *buf); - -LWS_VISIBLE LWS_EXTERN int -lws_vbi_decode(const void *buf, uint64_t *value, size_t len); - -///@} - -#if defined(LWS_WITH_SPAWN) - -/* opaque internal struct */ -struct lws_spawn_piped; - -typedef void (*lsp_cb_t)(void *opaque, lws_usec_t *accounting, siginfo_t *si, - int we_killed_him); - - -/** - * lws_spawn_piped_info - details given to create a spawned pipe - * - * \p owner: lws_dll2_owner_t that lists all active spawns, or NULL - * \p vh: vhost to bind stdwsi to... from opt_parent if given - * \p opt_parent: optional parent wsi for stdwsi - * \p exec_array: argv for process to spawn - * \p env_array: environment for spawned process, NULL ends env list - * \p protocol_name: NULL, or vhost protocol name to bind stdwsi to - * \p chroot_path: NULL, or chroot patch for child process - * \p wd: working directory to cd to after fork, NULL defaults to /tmp - * \p plsp: NULL, or pointer to the outer lsp pointer so it can be set NULL when destroyed - * \p opaque: pointer passed to the reap callback, if any - * \p timeout: optional us-resolution timeout, or zero - * \p reap_cb: callback when child process has been reaped and the lsp destroyed - * \p tsi: tsi to bind stdwsi to... from opt_parent if given - */ -struct lws_spawn_piped_info { - struct lws_dll2_owner *owner; - struct lws_vhost *vh; - struct lws *opt_parent; - - const char * const *exec_array; - char **env_array; - const char *protocol_name; - const char *chroot_path; - const char *wd; - - struct lws_spawn_piped **plsp; - - void *opaque; - - lsp_cb_t reap_cb; - - lws_usec_t timeout_us; - int max_log_lines; - int tsi; - - const struct lws_role_ops *ops; /* NULL is raw file */ - - uint8_t disable_ctrlc; -}; - -/** - * lws_spawn_piped() - spawn a child process with stdxxx redirected - * - * \p lspi: info struct describing details of spawn to create - * - * This spawns a child process managed in the lsp object and with attributes - * set in the arguments. The stdin/out/err streams are redirected to pipes - * which are instantiated into wsi that become child wsi of \p parent if non- - * NULL. .opaque_user_data on the stdwsi created is set to point to the - * lsp object, so this can be recovered easily in the protocol handler. - * - * If \p owner is non-NULL, successful spawns join the given dll2 owner in the - * original process. - * - * If \p timeout is non-zero, successful spawns register a sul with the us- - * resolution timeout to callback \p timeout_cb, in the original process. - * - * Returns 0 if the spawn went OK or nonzero if it failed and was cleaned up. - * The spawned process continues asynchronously and this will return after - * starting it if all went well. - */ -LWS_VISIBLE LWS_EXTERN struct lws_spawn_piped * -lws_spawn_piped(const struct lws_spawn_piped_info *lspi); - -/* - * lws_spawn_piped_kill_child_process() - attempt to kill child process - * - * \p lsp: child object to kill - * - * Attempts to signal the child process in \p lsp to terminate. - */ -LWS_VISIBLE LWS_EXTERN int -lws_spawn_piped_kill_child_process(struct lws_spawn_piped *lsp); - -/** - * lws_spawn_stdwsi_closed() - inform the spawn one of its stdxxx pipes closed - * - * \p lsp: the spawn object - * - * When you notice one of the spawn stdxxx pipes closed, inform the spawn - * instance using this api. When it sees all three have closed, it will - * automatically try to reap the child process. - * - * This is the mechanism whereby the spawn object can understand its child - * has closed. - */ -LWS_VISIBLE LWS_EXTERN void -lws_spawn_stdwsi_closed(struct lws_spawn_piped *lsp); - -/** - * lws_spawn_get_stdfd() - return std channel index for stdwsi - * - * \p wsi: the wsi - * - * If you know wsi is a stdwsi from a spawn, you can determine its original - * channel index / fd before the pipes replaced the default fds. It will return - * one of 0 (STDIN), 1 (STDOUT) or 2 (STDERR). You can handle all three in the - * same protocol handler and then disambiguate them using this api. - */ -LWS_VISIBLE LWS_EXTERN int -lws_spawn_get_stdfd(struct lws *wsi); - -#endif - -struct lws_fsmount { - const char *layers_path; /* where layers live */ - const char *overlay_path; /* where overlay instantiations live */ - - char mp[256]; /* mountpoint path */ - char ovname[64]; /* unique name for mount instance */ - char distro[64]; /* unique name for layer source */ - -#if defined(__linux__) - const char *layers[4]; /* distro layers, like "base", "env" */ -#endif -}; - -/** - * lws_fsmount_mount() - Mounts an overlayfs stack of layers - * - * \p fsm: struct lws_fsmount specifying the mount layout - * - * This api is able to assemble up to 4 layer directories on to a mountpoint - * using overlayfs mount (Linux only). - * - * Set fsm.layers_path to the base dir where the layers themselves live, the - * entries in fsm.layers[] specifies the relative path to the layer, comprising - * fsm.layers_path/fsm.distro/fsm.layers[], with [0] being the deepest, earliest - * layer and the rest being progressively on top of [0]; NULL indicates the - * layer is unused. - * - * fsm.overlay_path is the base path of the overlayfs instantiations... empty - * dirs must exist at - * - * fsm.overlay_path/overlays/fsm.ovname/work - * fsm.overlay_path/overlays/fsm.ovname/session - * - * Set fsm.mp to the path of an already-existing empty dir that will be the - * mountpoint, this can be whereever you like. - * - * Overlayfs merges the union of all the contributing layers at the mountpoint, - * the mount is writeable but the layer themselves are immutable, all additions - * and changes are stored in - * - * fsm.overlay_path/overlays/fsm.ovname/session - * - * Returns 0 if mounted OK, nonzero if errors. - * - * Retain fsm for use with unmounting. - */ -LWS_VISIBLE LWS_EXTERN int -lws_fsmount_mount(struct lws_fsmount *fsm); - -/** - * lws_fsmount_unmount() - Unmounts an overlayfs dir - * - * \p fsm: struct lws_fsmount specifying the mount layout - * - * Unmounts the mountpoint in fsm.mp. - * - * Delete fsm.overlay_path/overlays/fsm.ovname/session to permanently eradicate - * all changes from the time the mountpoint was in use. - * - * Returns 0 if unmounted OK. - */ -LWS_VISIBLE LWS_EXTERN int -lws_fsmount_unmount(struct lws_fsmount *fsm); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-mqtt.h libwebsockets-2.4.2/include/libwebsockets/lws-mqtt.h --- libwebsockets-4.0.20/include/libwebsockets/lws-mqtt.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-mqtt.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,330 +0,0 @@ -/* - * libwebsockets - protocol - mqtt - * - * Copyright (C) 2010 - 2020 Andy Green - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * included from libwebsockets.h - */ - -#ifndef _LWS_MQTT_H -#define _LWS_MQTT_H 1 - -struct _lws_mqtt_related; -typedef struct _lws_mqtt_related lws_mqtt_related_t; -struct lws_mqtt_str_st; -typedef struct lws_mqtt_str_st lws_mqtt_str_t; - -#define MQTT_VER_3_1_1 4 - -#define LWS_MQTT_FINAL_PART 1 - -typedef enum { - QOS0, - QOS1, - QOS2, /* not supported */ - RESERVED_QOS_LEVEL, - FAILURE_QOS_LEVEL = 0x80 -} lws_mqtt_qos_levels_t; - -typedef union { - struct { - uint8_t retain:1; - uint8_t qos:2; - uint8_t dup:1; - uint8_t ctrl_pkt_type:4; - } flags; - uint8_t bits; -} lws_mqtt_fixed_hdr_t; - -/* - * MQTT connection parameters, passed into struct - * lws_client_connect_info to establish a connection using - * lws_client_connect_via_info(). -*/ -typedef struct lws_mqtt_client_connect_param_s { - const char *client_id; /* Client ID */ - uint16_t keep_alive; /* MQTT keep alive - interval in - seconds */ - uint8_t clean_start; /* MQTT clean - session */ - struct { - const char *topic; - const char *message; - lws_mqtt_qos_levels_t qos; - uint8_t retain; - } will_param; /* MQTT LWT - parameters */ - const char *username; - const char *password; -} lws_mqtt_client_connect_param_t; - -/* - * MQTT publish parameters -*/ -typedef struct lws_mqtt_publish_param_s { - char *topic; /* Topic Name */ - uint16_t topic_len; - const void *payload; /* Publish Payload */ - uint32_t payload_len; /* Size of the - complete payload */ - uint32_t payload_pos; /* where we are in payload */ - lws_mqtt_qos_levels_t qos; - - /*--v-Following will be used by LWS-v--*/ - uint16_t packet_id; /* Packet ID for QoS > - 0 */ - uint8_t dup:1; /* Retried PUBLISH, - for QoS > 0 */ -} lws_mqtt_publish_param_t; - -typedef struct topic_elem { - const char *name; /* Topic Name */ - lws_mqtt_qos_levels_t qos; /* Requested QoS */ - - /*--v-Following will be used by LWS-v--*/ - uint8_t acked; -} lws_mqtt_topic_elem_t; - -/* - * MQTT publish parameters -*/ -typedef struct lws_mqtt_subscribe_param_s { - uint32_t num_topics; /* Number of topics */ - lws_mqtt_topic_elem_t *topic; /* Array of topic elements */ - - /*--v-Following will be used by LWS-v--*/ - uint16_t packet_id; -} lws_mqtt_subscribe_param_t; - -typedef enum { - LMQCP_RESERVED, - LMQCP_CTOS_CONNECT, /* Connection request */ - LMQCP_STOC_CONNACK, /* Connection acknowledgment */ - LMQCP_PUBLISH, /* Publish Message */ - LMQCP_PUBACK, /* QoS 1: Publish acknowledgment */ - LMQCP_PUBREC, /* QoS 2.1: Publish received */ - LMQCP_PUBREL, /* QoS 2.2: Publish release */ - LMQCP_PUBCOMP, /* QoS 2.3: Publish complete */ - LMQCP_CTOS_SUBSCRIBE, /* Subscribe request */ - LMQCP_STOC_SUBACK, /* Subscribe acknowledgment */ - LMQCP_CTOS_UNSUBSCRIBE, /* Unsubscribe request */ - LMQCP_STOC_UNSUBACK, /* Unsubscribe acknowledgment */ - LMQCP_CTOS_PINGREQ, /* PING request */ - LMQCP_STOC_PINGRESP, /* PONG response */ - LMQCP_DISCONNECT, /* Disconnect notification */ - LMQCP_AUTH /* Authentication exchange */ -} lws_mqtt_control_packet_t; - -/* flags from byte 8 of C_TO_S CONNECT */ -typedef enum { - LMQCFT_USERNAME = (1 << 7), - LMQCFT_PASSWORD = (1 << 6), - LMQCFT_WILL_RETAIN = (1 << 5), - LMQCFT_WILL_QOS = (1 << 3), - LMQCFT_WILL_FLAG = (1 << 2), - LMQCFT_CLEAN_START = (1 << 1), - LMQCFT_RESERVED = (1 << 0), - - LMQCFT_WILL_QOS_MASK = (3 << 3), -} lws_mqtt_connect_flags_t; - -/* flags for S_TO_C CONNACK */ -typedef enum { - LMQCFT_SESSION_PRESENT = (1 << 0), -} lws_mqtt_connack_flags_t; - -typedef enum { - LMQCP_REASON_SUCCESS = 0x00, - LMQCP_REASON_NORMAL_DISCONNECTION = 0x00, - LMQCP_REASON_GRANTED_QOS0 = 0x00, - LMQCP_REASON_GRANTED_QOS1 = 0x01, - LMQCP_REASON_GRANTED_QOS2 = 0x02, - LMQCP_REASON_DISCONNECT_WILL = 0x04, - LMQCP_REASON_NO_MATCHING_SUBSCRIBER = 0x10, - LMQCP_REASON_NO_SUBSCRIPTION_EXISTED = 0x11, - LMQCP_REASON_CONTINUE_AUTHENTICATION = 0x18, - LMQCP_REASON_RE_AUTHENTICATE = 0x19, - - LMQCP_REASON_UNSPECIFIED_ERROR = 0x80, - LMQCP_REASON_MALFORMED_PACKET = 0x81, - LMQCP_REASON_PROTOCOL_ERROR = 0x82, - LMQCP_REASON_IMPLEMENTATION_SPECIFIC_ERROR = 0x83, - - /* Begin - Error codes for CONNACK */ - LMQCP_REASON_UNSUPPORTED_PROTOCOL = 0x84, - LMQCP_REASON_CLIENT_ID_INVALID = 0x85, - LMQCP_REASON_BAD_CREDENTIALS = 0x86, - LMQCP_REASON_NOT_AUTHORIZED = 0x87, - /* End - Error codes for CONNACK */ - - LMQCP_REASON_SERVER_UNAVAILABLE = 0x88, - LMQCP_REASON_SERVER_BUSY = 0x89, - LMQCP_REASON_BANNED = 0x8a, - LMQCP_REASON_SERVER_SHUTTING_DOWN = 0x8b, - LMQCP_REASON_BAD_AUTHENTICATION_METHOD = 0x8c, - LMQCP_REASON_KEEPALIVE_TIMEOUT = 0x8d, - LMQCP_REASON_SESSION_TAKEN_OVER = 0x8e, - LMQCP_REASON_TOPIC_FILTER_INVALID = 0x8f, - LMQCP_REASON_TOPIC_NAME_INVALID = 0x90, - LMQCP_REASON_PACKET_ID_IN_USE = 0x91, - LMQCP_REASON_PACKET_ID_NOT_FOUND = 0x92, - LMQCP_REASON_MAX_RX_EXCEEDED = 0x93, - LMQCP_REASON_TOPIC_ALIAS_INVALID = 0x94, - LMQCP_REASON_PACKET_TOO_LARGE = 0x95, - LMQCP_REASON_RATELIMIT = 0x96, - LMQCP_REASON_QUOTA_EXCEEDED = 0x97, - LMQCP_REASON_ADMINISTRATIVE_ACTION = 0x98, - LMQCP_REASON_PAYLOAD_FORMAT_INVALID = 0x99, - LMQCP_REASON_RETAIN_NOT_SUPPORTED = 0x9a, - LMQCP_REASON_QOS_NOT_SUPPORTED = 0x9b, - LMQCP_REASON_USE_ANOTHER_SERVER = 0x9c, - LMQCP_REASON_SERVER_MOVED = 0x9d, - LMQCP_REASON_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 0x9e, - LMQCP_REASON_CONNECTION_RATE_EXCEEDED = 0x9f, - LMQCP_REASON_MAXIMUM_CONNECT_TIME = 0xa0, - LMQCP_REASON_SUBSCRIPTION_IDS_NOT_SUPPORTED = 0xa1, - LMQCP_REASON_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 0xa2, -} lws_mqtt_reason_t; - -typedef enum { - LMQPROP_INVALID, - LMQPROP_PAYLOAD_FORMAT_INDICATOR = 0x01, - LMQPROP_MESSAGE_EXPIRY_INTERVAL = 0x02, - LMQPROP_CONTENT_TYPE = 0x03, - LMQPROP_RESPONSE_TOPIC = 0x08, - LMQPROP_CORRELATION_DATA = 0x09, - LMQPROP_SUBSCRIPTION_IDENTIFIER = 0x0b, - LMQPROP_SESSION_EXPIRY_INTERVAL = 0x11, - LMQPROP_ASSIGNED_CLIENT_IDENTIFIER = 0x12, - LMQPROP_SERVER_KEEP_ALIVE = 0x13, - LMQPROP_AUTHENTICATION_METHOD = 0x15, - LMQPROP_AUTHENTICATION_DATA = 0x16, - LMQPROP_REQUEST_PROBLEM_INFORMATION = 0x17, - LMQPROP_WILL_DELAY_INTERVAL = 0x18, - LMQPROP_REQUEST_RESPONSE_INFORMATION = 0x19, - LMQPROP_RESPONSE_INFORMATION = 0x1a, - LMQPROP_SERVER_REFERENCE = 0x1c, - LMQPROP_REASON_STRING = 0x1f, - LMQPROP_RECEIVE_MAXIMUM = 0x21, - LMQPROP_TOPIC_ALIAS_MAXIMUM = 0x22, - LMQPROP_TOPIC_ALIAS = 0x23, - LMQPROP_MAXIMUM_QOS = 0x24, - LMQPROP_RETAIN_AVAILABLE = 0x25, - LMQPROP_USER_PROPERTY = 0x26, - LMQPROP_MAXIMUM_PACKET_SIZE = 0x27, - LMQPROP_WILDCARD_SUBSCRIPTION_AVAIL = 0x28, - LMQPROP_SUBSCRIPTION_IDENTIFIER_AVAIL = 0x29, - LMQPROP_SHARED_SUBSCRIPTION_AVAIL = 0x2a -} lws_mqtt_property; - -int -lws_read_mqtt(struct lws *wsi, unsigned char *buf, lws_filepos_t len); - -/* returns 0 if bd1 and bd2 are "the same", that includes empty, else nonzero */ -LWS_VISIBLE LWS_EXTERN int -lws_mqtt_bindata_cmp(const lws_mqtt_str_t *bd1, const lws_mqtt_str_t *bd2); - -LWS_VISIBLE LWS_EXTERN void -lws_mqtt_str_init(lws_mqtt_str_t *s, uint8_t *buf, uint16_t lim, char nf); - -LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t * -lws_mqtt_str_create(uint16_t lim); - -LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t * -lws_mqtt_str_create_init(uint8_t *buf, uint16_t len, uint16_t lim); - -LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t * -lws_mqtt_str_create_cstr_dup(const char *buf, uint16_t lim); - -LWS_VISIBLE LWS_EXTERN uint8_t * -lws_mqtt_str_next(lws_mqtt_str_t *s, uint16_t *budget); - -LWS_VISIBLE LWS_EXTERN int -lws_mqtt_str_advance(lws_mqtt_str_t *s, int n); - -LWS_VISIBLE LWS_EXTERN void -lws_mqtt_str_free(lws_mqtt_str_t **s); - - -/** - * lws_mqtt_client_send_publish() - lws_write a publish packet - * - * \param wsi: the mqtt child wsi - * \param pub: additional information on what we're publishing - * \param buf: payload to send - * \param len: length of data in buf - * \param final: flag indicating this is the last part - * - * Issues part of, or the whole of, a PUBLISH frame. The first part of the - * frame contains the header, and uses the .qos and .payload_len parts of \p pub - * since MQTT requires the frame to specify the PUBLISH message length at the - * start. The \p len paramter may be less than \p pub.payload_len, in which - * case subsequent calls with more payload are needed to complete the frame. - * - * Although the connection is stuck waiting for the remainder, in that it can't - * issue any other frames until the current one is completed, lws returns to the - * event loop normally and can continue the calls with additional payload even - * for huge frames as the data becomes available, consistent with timeout needs - * and latency to start any new frame (even, eg, related to ping / pong). - * - * If you're sending large frames, the OS will typically not allow the data to - * be sent all at once to kernel side. So you should ideally cut the payload - * up into 1 or 2- mtu sized chunks and send that. - * - * Final should be set when you're calling with the last part of the payload. - */ -LWS_VISIBLE LWS_EXTERN int -lws_mqtt_client_send_publish(struct lws *wsi, lws_mqtt_publish_param_t *pub, - const void *buf, uint32_t len, int final); - -/** - * lws_mqtt_client_send_subcribe() - lws_write a subscribe packet - * - * \param wsi: the mqtt child wsi - * \param sub: which topic(s) we want to subscribe to - * - * For topics other child streams have not already subscribed to, send a packet - * to the server asking to subscribe to them. If all topics listed are already - * subscribed to be the shared network connection, just trigger the - * LWS_CALLBACK_MQTT_SUBSCRIBED callback as if a SUBACK had come. - * - * \p sub doesn't need to exist after the return from this function. - */ -LWS_VISIBLE LWS_EXTERN int -lws_mqtt_client_send_subcribe(struct lws *wsi, lws_mqtt_subscribe_param_t *sub); - -/** - * lws_mqtt_client_send_unsubcribe() - lws_write a unsubscribe packet - * - * \param wsi: the mqtt child wsi - * \param sub: which topic(s) we want to unsubscribe from - * - * For topics other child streams are not subscribed to, send a packet - * to the server asking to unsubscribe from them. If all topics - * listed are already subscribed by other child streams on the shared - * network connection, just trigger the LWS_CALLBACK_MQTT_UNSUBSCRIBED - * callback as if a UNSUBACK had come. - * - * \p unsub doesn't need to exist after the return from this function. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_mqtt_client_send_unsubcribe(struct lws *wsi, - const lws_mqtt_subscribe_param_t *unsub); - -#endif /* _LWS_MQTT_H */ diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-network-helper.h libwebsockets-2.4.2/include/libwebsockets/lws-network-helper.h --- libwebsockets-4.0.20/include/libwebsockets/lws-network-helper.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-network-helper.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,189 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup net Network related helper APIs - * ##Network related helper APIs - * - * These wrap miscellaneous useful network-related functions - */ -///@{ - -typedef union { -#if defined(LWS_WITH_IPV6) - struct sockaddr_in6 sa6; -#endif - struct sockaddr_in sa4; -} lws_sockaddr46; - -/** - * lws_canonical_hostname() - returns this host's hostname - * - * This is typically used by client code to fill in the host parameter - * when making a client connection. You can only call it after the context - * has been created. - * - * \param context: Websocket context - */ -LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT -lws_canonical_hostname(struct lws_context *context); - -/** - * lws_get_peer_addresses() - Get client address information - * \param wsi: Local struct lws associated with - * \param fd: Connection socket descriptor - * \param name: Buffer to take client address name - * \param name_len: Length of client address name buffer - * \param rip: Buffer to take client address IP dotted quad - * \param rip_len: Length of client address IP buffer - * - * This function fills in name and rip with the name and IP of - * the client connected with socket descriptor fd. Names may be - * truncated if there is not enough room. If either cannot be - * determined, they will be returned as valid zero-length strings. - */ -LWS_VISIBLE LWS_EXTERN void -lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, - int name_len, char *rip, int rip_len); - -/** - * lws_get_peer_simple() - Get client address information without RDNS - * - * \param wsi: Local struct lws associated with - * \param name: Buffer to take client address name - * \param namelen: Length of client address name buffer - * - * This provides a 123.123.123.123 type IP address in name from the - * peer that has connected to wsi - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_get_peer_simple(struct lws *wsi, char *name, size_t namelen); - -LWS_VISIBLE LWS_EXTERN const char * -lws_get_peer_simple_fd(lws_sockfd_type fd, char *name, size_t namelen); - -#define LWS_ITOSA_USABLE 0 -#define LWS_ITOSA_NOT_EXIST -1 -#define LWS_ITOSA_NOT_USABLE -2 -#define LWS_ITOSA_BUSY -3 /* only returned by lws_socket_bind() on - EADDRINUSE */ - -#if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) -/** - * lws_interface_to_sa() - Convert interface name or IP to sockaddr struct - * - * \param ipv6: Allow IPV6 addresses - * \param ifname: Interface name or IP - * \param addr: struct sockaddr_in * to be written - * \param addrlen: Length of addr - * - * This converts a textual network interface name to a sockaddr usable by - * other network functions. - * - * If the network interface doesn't exist, it will return LWS_ITOSA_NOT_EXIST. - * - * If the network interface is not usable, eg ethernet cable is removed, it - * may logically exist but not have any IP address. As such it will return - * LWS_ITOSA_NOT_USABLE. - * - * If the network interface exists and is usable, it will return - * LWS_ITOSA_USABLE. - */ -LWS_VISIBLE LWS_EXTERN int -lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, - size_t addrlen); -#endif - -/** - * lws_sa46_compare_ads() - checks if two sa46 have the same address - * - * \param sa46a: first - * \param sa46b: second - * - * Returns 0 if the address family and address are the same, otherwise nonzero. - */ -LWS_VISIBLE LWS_EXTERN int -lws_sa46_compare_ads(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46b); - -/* - * lws_parse_numeric_address() - converts numeric ipv4 or ipv6 to byte address - * - * \param ads: the numeric ipv4 or ipv6 address string - * \param result: result array - * \param max_len: max length of result array - * - * Converts a 1.2.3.4 or 2001:abcd:123:: or ::ffff:1.2.3.4 formatted numeric - * address into an array of network ordered byte address elements. - * - * Returns < 0 on error, else length of result set, either 4 or 16 for ipv4 / - * ipv6. - */ -LWS_VISIBLE LWS_EXTERN int -lws_parse_numeric_address(const char *ads, uint8_t *result, size_t max_len); - -/* - * lws_sa46_parse_numeric_address() - converts numeric ipv4 or ipv6 to sa46 - * - * \param ads: the numeric ipv4 or ipv6 address string - * \param sa46: pointer to sa46 to set - * - * Converts a 1.2.3.4 or 2001:abcd:123:: or ::ffff:1.2.3.4 formatted numeric - * address into an sa46, a union of sockaddr_in or sockaddr_in6 depending on - * what kind of address was found. sa46->sa4.sin_fmaily will be AF_INET if - * ipv4, or AF_INET6 if ipv6. - * - * Returns 0 if the sa46 was set, else < 0 on error. - */ -LWS_VISIBLE LWS_EXTERN int -lws_sa46_parse_numeric_address(const char *ads, lws_sockaddr46 *sa46); - -/** - * lws_write_numeric_address() - convert network byte order ads to text - * - * \param ads: network byte order address array - * \param size: number of bytes valid in ads - * \param buf: result buffer to take text format - * \param len: max size of text buffer - * - * Converts an array of network-ordered byte address elements to a textual - * representation of the numeric address, like "1.2.3.4" or "::1". Return 0 - * if OK else < 0. ipv6 only supported with LWS_IPV6=1 at cmake. - */ -LWS_VISIBLE LWS_EXTERN int -lws_write_numeric_address(const uint8_t *ads, int size, char *buf, size_t len); - -/** - * lws_sa46_write_numeric_address() - convert sa46 ads to textual numeric ads - * - * \param sa46: the sa46 whose address to show - * \param buf: result buffer to take text format - * \param len: max size of text buffer - * - * Converts the ipv4 or ipv6 address in an lws_sockaddr46 to a textual - * representation of the numeric address, like "1.2.3.4" or "::1". Return 0 - * if OK else < 0. ipv6 only supported with LWS_IPV6=1 at cmake. - */ -LWS_VISIBLE LWS_EXTERN int -lws_sa46_write_numeric_address(lws_sockaddr46 *sa46, char *buf, size_t len); - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-optee.h libwebsockets-2.4.2/include/libwebsockets/lws-optee.h --- libwebsockets-4.0.20/include/libwebsockets/lws-optee.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-optee.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 __LWS_OPTEE_H -#define __LWS_OPTEE_H - -/* 128-bit IP6 address */ -struct in6_addr { - union { - uint8_t u6_addr8[16]; - uint16_t u6_addr16[8]; - uint32_t u6_addr32[4]; - }; -}; - -#define _SS_MAXSIZE 128U -#define _SS_ALIGNSIZE (sizeof(int64_t)) -#define _SS_PAD1SIZE (_SS_ALIGNSIZE - \ - sizeof(sa_family_t)) -#define _SS_PAD2SIZE (_SS_MAXSIZE - \ - sizeof(sa_family_t) - _SS_PAD1SIZE - _SS_ALIGNSIZE) - -struct sockaddr_storage { - sa_family_t ss_family; /* address family */ - char __ss_pad1[_SS_PAD1SIZE]; - int64_t __ss_align; /* force desired struct alignment */ - char __ss_pad2[_SS_PAD2SIZE]; -}; - -#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ -struct sockaddr { - sa_family_t sa_family; /* address family */ - uint8_t sa_data[__SOCK_SIZE__ /* address value */ - - sizeof(sa_family_t)]; -}; - -/* 16 bytes */ -struct sockaddr_in { - sa_family_t sin_family; - in_port_t sin_port; - struct in_addr sin_addr; - uint8_t sin_zero[__SOCK_SIZE__ /* padding until 16 bytes */ - - sizeof(sa_family_t) - - sizeof(in_port_t) - - sizeof(struct in_addr)]; -}; - -struct sockaddr_in6 { - sa_family_t sin6_family; /* AF_INET6 */ - in_port_t sin6_port; /* Transport layer port # */ - uint32_t sin6_flowinfo; /* IP6 flow information */ - struct in6_addr sin6_addr; /* IP6 address */ - uint32_t sin6_scope_id; /* scope zone index */ -}; - -#endif /* __LWS_OPTEE_H */ diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-plugin-generic-sessions.h libwebsockets-2.4.2/include/libwebsockets/lws-plugin-generic-sessions.h --- libwebsockets-4.0.20/include/libwebsockets/lws-plugin-generic-sessions.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-plugin-generic-sessions.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup generic-sessions plugin: generic-sessions - * \ingroup Protocols-and-Plugins - * - * ##Plugin Generic-sessions related - * - * generic-sessions plugin provides a reusable, generic session and login / - * register / forgot password framework including email verification. - */ -///@{ - -#define LWSGS_EMAIL_CONTENT_SIZE 16384 -/**< Maximum size of email we might send */ - -/* SHA-1 binary and hexified versions */ -/** typedef struct lwsgw_hash_bin */ -typedef struct { unsigned char bin[32]; /**< binary representation of hash */} lwsgw_hash_bin; -/** typedef struct lwsgw_hash */ -typedef struct { char id[65]; /**< ascii hex representation of hash */ } lwsgw_hash; - -/** enum lwsgs_auth_bits */ -enum lwsgs_auth_bits { - LWSGS_AUTH_LOGGED_IN = 1, /**< user is logged in as somebody */ - LWSGS_AUTH_ADMIN = 2, /**< logged in as the admin user */ - LWSGS_AUTH_VERIFIED = 4, /**< user has verified his email */ - LWSGS_AUTH_FORGOT_FLOW = 8, /**< just completed "forgot password" */ -}; - -/** struct lws_session_info - information about user session status */ -struct lws_session_info { - char username[32]; /**< username logged in as, or empty string */ - char email[100]; /**< email address associated with login, or empty string */ - char ip[72]; /**< ip address session was started from */ - unsigned int mask; /**< access rights mask associated with session - * see enum lwsgs_auth_bits */ - char session[42]; /**< session id string, usable as opaque uid when not logged in */ -}; - -/** enum lws_gs_event */ -enum lws_gs_event { - LWSGSE_CREATED, /**< a new user was created */ - LWSGSE_DELETED /**< an existing user was deleted */ -}; - -/** struct lws_gs_event_args */ -struct lws_gs_event_args { - enum lws_gs_event event; /**< which event happened */ - const char *username; /**< which username the event happened to */ - const char *email; /**< the email address of that user */ -}; - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-protocols-plugins.h libwebsockets-2.4.2/include/libwebsockets/lws-protocols-plugins.h --- libwebsockets-4.0.20/include/libwebsockets/lws-protocols-plugins.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-protocols-plugins.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,229 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup Protocols-and-Plugins Protocols and Plugins - * \ingroup lwsapi - * - * ##Protocol and protocol plugin -related apis - * - * Protocols bind ws protocol names to a custom callback specific to that - * protocol implementaion. - * - * A list of protocols can be passed in at context creation time, but it is - * also legal to leave that NULL and add the protocols and their callback code - * using plugins. - * - * Plugins are much preferable compared to cut and pasting code into an - * application each time, since they can be used standalone. - */ -///@{ -/** struct lws_protocols - List of protocols and handlers client or server - * supports. */ - -struct lws_protocols { - const char *name; - /**< Protocol name that must match the one given in the client - * Javascript new WebSocket(url, 'protocol') name. */ - lws_callback_function *callback; - /**< The service callback used for this protocol. It allows the - * service action for an entire protocol to be encapsulated in - * the protocol-specific callback */ - size_t per_session_data_size; - /**< Each new connection using this protocol gets - * this much memory allocated on connection establishment and - * freed on connection takedown. A pointer to this per-connection - * allocation is passed into the callback in the 'user' parameter */ - size_t rx_buffer_size; - /**< lws allocates this much space for rx data and informs callback - * when something came. Due to rx flow control, the callback may not - * be able to consume it all without having to return to the event - * loop. That is supported in lws. - * - * If .tx_packet_size is 0, this also controls how much may be sent at - * once for backwards compatibility. - */ - unsigned int id; - /**< ignored by lws, but useful to contain user information bound - * to the selected protocol. For example if this protocol was - * called "myprotocol-v2", you might set id to 2, and the user - * code that acts differently according to the version can do so by - * switch (wsi->protocol->id), user code might use some bits as - * capability flags based on selected protocol version, etc. */ - void *user; /**< ignored by lws, but user code can pass a pointer - here it can later access from the protocol callback */ - size_t tx_packet_size; - /**< 0 indicates restrict send() size to .rx_buffer_size for backwards- - * compatibility. - * If greater than zero, a single send() is restricted to this amount - * and any remainder is buffered by lws and sent afterwards also in - * these size chunks. Since that is expensive, it's preferable - * to restrict one fragment you are trying to send to match this - * size. - */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility */ -}; - -/** - * lws_vhost_name_to_protocol() - get vhost's protocol object from its name - * - * \param vh: vhost to search - * \param name: protocol name - * - * Returns NULL or a pointer to the vhost's protocol of the requested name - */ -LWS_VISIBLE LWS_EXTERN const struct lws_protocols * -lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name); - -/** - * lws_get_protocol() - Returns a protocol pointer from a websocket - * connection. - * \param wsi: pointer to struct websocket you want to know the protocol of - * - * - * Some apis can act on all live connections of a given protocol, - * this is how you can get a pointer to the active protocol if needed. - */ -LWS_VISIBLE LWS_EXTERN const struct lws_protocols * -lws_get_protocol(struct lws *wsi); - -/** lws_protocol_get() - deprecated: use lws_get_protocol */ -LWS_VISIBLE LWS_EXTERN const struct lws_protocols * -lws_protocol_get(struct lws *wsi) LWS_WARN_DEPRECATED; - -/** - * lws_protocol_vh_priv_zalloc() - Allocate and zero down a protocol's per-vhost - * storage - * \param vhost: vhost the instance is related to - * \param prot: protocol the instance is related to - * \param size: bytes to allocate - * - * Protocols often find it useful to allocate a per-vhost struct, this is a - * helper to be called in the per-vhost init LWS_CALLBACK_PROTOCOL_INIT - */ -LWS_VISIBLE LWS_EXTERN void * -lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, - const struct lws_protocols *prot, int size); - -/** - * lws_protocol_vh_priv_get() - retreive a protocol's per-vhost storage - * - * \param vhost: vhost the instance is related to - * \param prot: protocol the instance is related to - * - * Recover a pointer to the allocated per-vhost storage for the protocol created - * by lws_protocol_vh_priv_zalloc() earlier - */ -LWS_VISIBLE LWS_EXTERN void * -lws_protocol_vh_priv_get(struct lws_vhost *vhost, - const struct lws_protocols *prot); - -/** - * lws_adjust_protocol_psds - change a vhost protocol's per session data size - * - * \param wsi: a connection with the protocol to change - * \param new_size: the new size of the per session data size for the protocol - * - * Returns user_space for the wsi, after allocating - * - * This should not be used except to initalize a vhost protocol's per session - * data size one time, before any connections are accepted. - * - * Sometimes the protocol wraps another protocol and needs to discover and set - * its per session data size at runtime. - */ -LWS_VISIBLE LWS_EXTERN void * -lws_adjust_protocol_psds(struct lws *wsi, size_t new_size); - -/** - * lws_finalize_startup() - drop initial process privileges - * - * \param context: lws context - * - * This is called after the end of the vhost protocol initializations, but - * you may choose to call it earlier - */ -LWS_VISIBLE LWS_EXTERN int -lws_finalize_startup(struct lws_context *context); - -/** - * lws_pvo_search() - helper to find a named pvo in a linked-list - * - * \param pvo: the first pvo in the linked-list - * \param name: the name of the pvo to return if found - * - * Returns NULL, or a pointer to the name pvo in the linked-list - */ -LWS_VISIBLE LWS_EXTERN const struct lws_protocol_vhost_options * -lws_pvo_search(const struct lws_protocol_vhost_options *pvo, const char *name); - -/** - * lws_pvo_get_str() - retreive a string pvo value - * - * \param in: the first pvo in the linked-list - * \param name: the name of the pvo to return if found - * \param result: pointer to a const char * to get the result if any - * - * Returns 0 if found and *result set, or nonzero if not found - */ -LWS_VISIBLE LWS_EXTERN int -lws_pvo_get_str(void *in, const char *name, const char **result); - -LWS_VISIBLE LWS_EXTERN int -lws_protocol_init(struct lws_context *context); - -#ifdef LWS_WITH_PLUGINS - -/* PLUGINS implies LIBUV */ - -#define LWS_PLUGIN_API_MAGIC 180 - -/** struct lws_plugin_capability - how a plugin introduces itself to lws */ -struct lws_plugin_capability { - unsigned int api_magic; /**< caller fills this in, plugin fills rest */ - const struct lws_protocols *protocols; /**< array of supported protocols provided by plugin */ - int count_protocols; /**< how many protocols */ - const struct lws_extension *extensions; /**< array of extensions provided by plugin */ - int count_extensions; /**< how many extensions */ -}; - -typedef int (*lws_plugin_init_func)(struct lws_context *, - struct lws_plugin_capability *); -typedef int (*lws_plugin_destroy_func)(struct lws_context *); - -/** struct lws_plugin */ -struct lws_plugin { - struct lws_plugin *list; /**< linked list */ -#if (UV_VERSION_MAJOR > 0) - uv_lib_t lib; /**< shared library pointer */ -#endif - void *l; /**< so we can compile on ancient libuv */ - char name[64]; /**< name of the plugin */ - struct lws_plugin_capability caps; /**< plugin capabilities */ -}; - -#endif - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-purify.h libwebsockets-2.4.2/include/libwebsockets/lws-purify.h --- libwebsockets-4.0.20/include/libwebsockets/lws-purify.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-purify.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,105 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup pur Sanitize / purify SQL and JSON helpers - * - * ##Sanitize / purify SQL and JSON helpers - * - * APIs for escaping untrusted JSON and SQL safely before use - */ -//@{ - -/** - * lws_sql_purify() - like strncpy but with escaping for sql quotes - * - * \param escaped: output buffer - * \param string: input buffer ('/0' terminated) - * \param len: output buffer max length - * - * Because escaping expands the output string, it's not - * possible to do it in-place, ie, with escaped == string - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_sql_purify(char *escaped, const char *string, int len); - -/** - * lws_sql_purify_len() - return length of purified version of input string - * - * \param string: input buffer ('/0' terminated) - * - * Calculates any character escaping without writing it anywhere and returns the - * calculated length of the purified string. - */ -int -lws_sql_purify_len(const char *p); - -/** - * lws_json_purify() - like strncpy but with escaping for json chars - * - * \param escaped: output buffer - * \param string: input buffer ('/0' terminated) - * \param len: output buffer max length - * \param in_used: number of bytes of string we could escape in len - * - * Because escaping expands the output string, it's not - * possible to do it in-place, ie, with escaped == string - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_json_purify(char *escaped, const char *string, int len, int *in_used); - -/** - * lws_json_purify_len() - find out the escaped length of a string - * - * \param string: input buffer ('/0' terminated) - * - * JSON may have to expand escapes by up to 6x the original depending on what - * it is. This doesn't actually do the escaping but goes through the motions - * and computes the length of the escaped string. - */ -LWS_VISIBLE LWS_EXTERN int -lws_json_purify_len(const char *string); - -/** - * lws_filename_purify_inplace() - replace scary filename chars with underscore - * - * \param filename: filename to be purified - * - * Replace scary characters in the filename (it should not be a path) - * with underscore, so it's safe to use. - */ -LWS_VISIBLE LWS_EXTERN void -lws_filename_purify_inplace(char *filename); - -LWS_VISIBLE LWS_EXTERN int -lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf, - int len); -LWS_VISIBLE LWS_EXTERN int -lws_plat_write_file(const char *filename, void *buf, int len); - -LWS_VISIBLE LWS_EXTERN int -lws_plat_read_file(const char *filename, void *buf, int len); - -LWS_VISIBLE LWS_EXTERN int -lws_plat_recommended_rsa_bits(void); -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-retry.h libwebsockets-2.4.2/include/libwebsockets/lws-retry.h --- libwebsockets-4.0.20/include/libwebsockets/lws-retry.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-retry.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 struct lws_retry_bo { - const uint32_t *retry_ms_table; /* base delay in ms */ - uint16_t retry_ms_table_count; /* entries in table */ - uint16_t conceal_count; /* max retries to conceal */ - uint16_t secs_since_valid_ping; /* idle before PING issued */ - uint16_t secs_since_valid_hangup; /* idle before hangup conn */ - uint8_t jitter_percent; /* % additional random jitter */ -} lws_retry_bo_t; - -#define LWS_RETRY_CONCEAL_ALWAYS (0xffff) - -/** - * lws_retry_get_delay_ms() - get next delay from backoff table - * - * \param lws_context: the lws context (used for getting random) - * \param retry: the retry backoff table we are using, or NULL for default - * \param ctry: pointer to the try counter - * \param conceal: pointer to flag set to nonzero if the try should be concealed - * in terms of creating an error - * - * Increments *\p try and retruns the number of ms that should elapse before the - * next connection retry, according to the backoff table \p retry. *\p conceal is - * set if the number of tries is less than the backoff table conceal_count, or - * is zero if it exceeded it. This lets you conceal a certain number of retries - * before alerting the caller there is a problem. - * - * If \p retry is NULL, a default of 3s + (0..300ms jitter) is used. If it's - * non-NULL but jitter_percent is 0, the default of 30% jitter is retained. - */ - -LWS_VISIBLE LWS_EXTERN unsigned int -lws_retry_get_delay_ms(struct lws_context *context, const lws_retry_bo_t *retry, - uint16_t *ctry, char *conceal); - -/** - * lws_retry_sul_schedule() - schedule a sul according to the backoff table - * - * \param lws_context: the lws context (used for getting random) - * \param sul: pointer to the sul to schedule - * \param retry: the retry backoff table we are using, or NULL for default - * \param cb: the callback for when the sul schedule time arrives - * \param ctry: pointer to the try counter - * - * Helper that combines interpreting the retry table with scheduling a sul to - * the computed delay. If conceal is not set, it will not schedule the sul - * and just return 1. Otherwise the sul is scheduled and it returns 0. - */ -LWS_VISIBLE LWS_EXTERN int -lws_retry_sul_schedule(struct lws_context *context, int tid, - lws_sorted_usec_list_t *sul, const lws_retry_bo_t *retry, - sul_cb_t cb, uint16_t *ctry); - -/** - * lws_retry_sul_schedule_retry_wsi() - retry sul schedule helper using wsi - * - * \param wsi: the wsi to set the hrtimer sul on to the next retry interval - * \param sul: pointer to the sul to schedule - * \param cb: the callback for when the sul schedule time arrives - * \param ctry: pointer to the try counter - * - * Helper that uses context, tid and retry policy from a wsi to call - * lws_retry_sul_schedule. - * - * Since a udp connection can have many writes in flight, the retry count and - * the sul used to track each thing that wants to be written have to be handled - * individually, not the wsi. But the retry policy and the other things can - * be filled in from the wsi conveniently. - */ -LWS_VISIBLE LWS_EXTERN int -lws_retry_sul_schedule_retry_wsi(struct lws *wsi, lws_sorted_usec_list_t *sul, - sul_cb_t cb, uint16_t *ctry); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-ring.h libwebsockets-2.4.2/include/libwebsockets/lws-ring.h --- libwebsockets-4.0.20/include/libwebsockets/lws-ring.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-ring.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,306 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup lws_ring LWS Ringbuffer APIs - * ##lws_ring: generic ringbuffer struct - * - * Provides an abstract ringbuffer api supporting one head and one or an - * unlimited number of tails. - * - * All of the members are opaque and manipulated by lws_ring_...() apis. - * - * The lws_ring and its buffer is allocated at runtime on the heap, using - * - * - lws_ring_create() - * - lws_ring_destroy() - * - * It may contain any type, the size of the "element" stored in the ring - * buffer and the number of elements is given at creation time. - * - * When you create the ringbuffer, you can optionally provide an element - * destroy callback that frees any allocations inside the element. This is then - * automatically called for elements with no tail behind them, ie, elements - * which don't have any pending consumer are auto-freed. - * - * Whole elements may be inserted into the ringbuffer and removed from it, using - * - * - lws_ring_insert() - * - lws_ring_consume() - * - * You can find out how many whole elements are free or waiting using - * - * - lws_ring_get_count_free_elements() - * - lws_ring_get_count_waiting_elements() - * - * In addition there are special purpose optional byte-centric apis - * - * - lws_ring_next_linear_insert_range() - * - lws_ring_bump_head() - * - * which let you, eg, read() directly into the ringbuffer without needing - * an intermediate bounce buffer. - * - * The accessors understand that the ring wraps, and optimizes insertion and - * consumption into one or two memcpy()s depending on if the head or tail - * wraps. - * - * lws_ring only supports a single head, but optionally multiple tails with - * an API to inform it when the "oldest" tail has moved on. You can give - * NULL where-ever an api asks for a tail pointer, and it will use an internal - * single tail pointer for convenience. - * - * The "oldest tail", which is the only tail if you give it NULL instead of - * some other tail, is used to track which elements in the ringbuffer are - * still unread by anyone. - * - * - lws_ring_update_oldest_tail() - */ -///@{ -struct lws_ring; - -/** - * lws_ring_create(): create a new ringbuffer - * - * \param element_len: the size in bytes of one element in the ringbuffer - * \param count: the number of elements the ringbuffer can contain - * \param destroy_element: NULL, or callback to be called for each element - * that is removed from the ringbuffer due to the - * oldest tail moving beyond it - * - * Creates the ringbuffer and allocates the storage. Returns the new - * lws_ring *, or NULL if the allocation failed. - * - * If non-NULL, destroy_element will get called back for every element that is - * retired from the ringbuffer after the oldest tail has gone past it, and for - * any element still left in the ringbuffer when it is destroyed. It replaces - * all other element destruction code in your user code. - */ -LWS_VISIBLE LWS_EXTERN struct lws_ring * -lws_ring_create(size_t element_len, size_t count, - void (*destroy_element)(void *element)); - -/** - * lws_ring_destroy(): destroy a previously created ringbuffer - * - * \param ring: the struct lws_ring to destroy - * - * Destroys the ringbuffer allocation and the struct lws_ring itself. - */ -LWS_VISIBLE LWS_EXTERN void -lws_ring_destroy(struct lws_ring *ring); - -/** - * lws_ring_get_count_free_elements(): return how many elements can fit - * in the free space - * - * \param ring: the struct lws_ring to report on - * - * Returns how much room is left in the ringbuffer for whole element insertion. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_ring_get_count_free_elements(struct lws_ring *ring); - -/** - * lws_ring_get_count_waiting_elements(): return how many elements can be consumed - * - * \param ring: the struct lws_ring to report on - * \param tail: a pointer to the tail struct to use, or NULL for single tail - * - * Returns how many elements are waiting to be consumed from the perspective - * of the tail pointer given. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail); - -/** - * lws_ring_insert(): attempt to insert up to max_count elements from src - * - * \param ring: the struct lws_ring to report on - * \param src: the array of elements to be inserted - * \param max_count: the number of available elements at src - * - * Attempts to insert as many of the elements at src as possible, up to the - * maximum max_count. Returns the number of elements actually inserted. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count); - -/** - * lws_ring_consume(): attempt to copy out and remove up to max_count elements - * to src - * - * \param ring: the struct lws_ring to report on - * \param tail: a pointer to the tail struct to use, or NULL for single tail - * \param dest: the array of elements to be inserted. or NULL for no copy - * \param max_count: the number of available elements at src - * - * Attempts to copy out as many waiting elements as possible into dest, from - * the perspective of the given tail, up to max_count. If dest is NULL, the - * copying out is not done but the elements are logically consumed as usual. - * NULL dest is useful in combination with lws_ring_get_element(), where you - * can use the element direct from the ringbuffer and then call this with NULL - * dest to logically consume it. - * - * Increments the tail position according to how many elements could be - * consumed. - * - * Returns the number of elements consumed. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest, - size_t max_count); - -/** - * lws_ring_get_element(): get a pointer to the next waiting element for tail - * - * \param ring: the struct lws_ring to report on - * \param tail: a pointer to the tail struct to use, or NULL for single tail - * - * Points to the next element that tail would consume, directly in the - * ringbuffer. This lets you write() or otherwise use the element without - * having to copy it out somewhere first. - * - * After calling this, you must call lws_ring_consume(ring, &tail, NULL, 1) - * which will logically consume the element you used up and increment your - * tail (tail may also be NULL there if you use a single tail). - * - * Returns NULL if no waiting element, or a const void * pointing to it. - */ -LWS_VISIBLE LWS_EXTERN const void * -lws_ring_get_element(struct lws_ring *ring, uint32_t *tail); - -/** - * lws_ring_update_oldest_tail(): free up elements older than tail for reuse - * - * \param ring: the struct lws_ring to report on - * \param tail: a pointer to the tail struct to use, or NULL for single tail - * - * If you are using multiple tails, you must use this API to inform the - * lws_ring when none of the tails still need elements in the fifo any more, - * by updating it when the "oldest" tail has moved on. - */ -LWS_VISIBLE LWS_EXTERN void -lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail); - -/** - * lws_ring_get_oldest_tail(): get current oldest available data index - * - * \param ring: the struct lws_ring to report on - * - * If you are initializing a new ringbuffer consumer, you can set its tail to - * this to start it from the oldest ringbuffer entry still available. - */ -LWS_VISIBLE LWS_EXTERN uint32_t -lws_ring_get_oldest_tail(struct lws_ring *ring); - -/** - * lws_ring_next_linear_insert_range(): used to write directly into the ring - * - * \param ring: the struct lws_ring to report on - * \param start: pointer to a void * set to the start of the next ringbuffer area - * \param bytes: pointer to a size_t set to the max length you may use from *start - * - * This provides a low-level, bytewise access directly into the ringbuffer - * allowing direct insertion of data without having to use a bounce buffer. - * - * The api reports the position and length of the next linear range that can - * be written in the ringbuffer, ie, up to the point it would wrap, and sets - * *start and *bytes accordingly. You can then, eg, directly read() into - * *start for up to *bytes, and use lws_ring_bump_head() to update the lws_ring - * with what you have done. - * - * Returns nonzero if no insertion is currently possible. - */ -LWS_VISIBLE LWS_EXTERN int -lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start, - size_t *bytes); - -/** - * lws_ring_bump_head(): used to write directly into the ring - * - * \param ring: the struct lws_ring to operate on - * \param bytes: the number of bytes you inserted at the current head - */ -LWS_VISIBLE LWS_EXTERN void -lws_ring_bump_head(struct lws_ring *ring, size_t bytes); - -LWS_VISIBLE LWS_EXTERN void -lws_ring_dump(struct lws_ring *ring, uint32_t *tail); - -/* - * This is a helper that combines the common pattern of needing to consume - * some ringbuffer elements, move the consumer tail on, and check if that - * has moved any ringbuffer elements out of scope, because it was the last - * consumer that had not already consumed them. - * - * Elements that go out of scope because the oldest tail is now after them - * get garbage-collected by calling the destroy_element callback on them - * defined when the ringbuffer was created. - */ - -#define lws_ring_consume_and_update_oldest_tail(\ - ___ring, /* the lws_ring object */ \ - ___type, /* type of objects with tails */ \ - ___ptail, /* ptr to tail of obj with tail doing consuming */ \ - ___count, /* count of payload objects being consumed */ \ - ___list_head, /* head of list of objects with tails */ \ - ___mtail, /* member name of tail in ___type */ \ - ___mlist /* member name of next list member ptr in ___type */ \ - ) { \ - int ___n, ___m; \ - \ - ___n = lws_ring_get_oldest_tail(___ring) == *(___ptail); \ - lws_ring_consume(___ring, ___ptail, NULL, ___count); \ - if (___n) { \ - uint32_t ___oldest; \ - ___n = 0; \ - ___oldest = *(___ptail); \ - lws_start_foreach_llp(___type **, ___ppss, ___list_head) { \ - ___m = (int)lws_ring_get_count_waiting_elements( \ - ___ring, &(*___ppss)->___mtail); \ - if (___m >= ___n) { \ - ___n = ___m; \ - ___oldest = (*___ppss)->___mtail; \ - } \ - } lws_end_foreach_llp(___ppss, ___mlist); \ - \ - lws_ring_update_oldest_tail(___ring, ___oldest); \ - } \ -} - -/* - * This does the same as the lws_ring_consume_and_update_oldest_tail() - * helper, but for the simpler case there is only one consumer, so one - * tail, and that tail is always the oldest tail. - */ - -#define lws_ring_consume_single_tail(\ - ___ring, /* the lws_ring object */ \ - ___ptail, /* ptr to tail of obj with tail doing consuming */ \ - ___count /* count of payload objects being consumed */ \ - ) { \ - lws_ring_consume(___ring, ___ptail, NULL, ___count); \ - lws_ring_update_oldest_tail(___ring, *(___ptail)); \ -} -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-secure-streams-client.h libwebsockets-2.4.2/include/libwebsockets/lws-secure-streams-client.h --- libwebsockets-4.0.20/include/libwebsockets/lws-secure-streams-client.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-secure-streams-client.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,172 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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 the headers for secure stream api variants that deal with clients in - * different threads or even different processes. - * - * lws_ss_ when client is directly using the event loop - * lws_sstc_ when client is in a different thread to the event loop - * lws_sspc_ when client is in a different process to the event loop - * - * The client api is almost the same except the slightly diffent names. - */ - -/* - * lws_sspc_ apis... different process - */ - -/* - * Helper translation so user code written to lws_ss_ can be built for - * lws_sspc_ in one step by #define LWS_SS_USE_SSPC before including - */ - -#if defined(LWS_SS_USE_SSPC) -#define lws_ss_handle lws_sspc_handle -#define lws_ss_create lws_sspc_create -#define lws_ss_destroy lws_sspc_destroy -#define lws_ss_request_tx lws_sspc_request_tx -#define lws_ss_client_connect lws_sspc_client_connect -#define lws_ss_get_sequencer lws_sspc_get_sequencer -#define lws_ss_proxy_create lws_sspc_proxy_create -#define lws_ss_get_context lws_sspc_get_context -#define lws_ss_rideshare lws_sspc_rideshare -#define lws_ss_set_metadata lws_sspc_set_metadata -#define lws_ss_add_peer_tx_credit lws_sspc_add_peer_tx_credit -#define lws_ss_get_est_peer_tx_credit lws_sspc_get_est_peer_tx_credit -#endif - - -struct lws_sspc_handle; - -LWS_VISIBLE LWS_EXTERN int -lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi, - void *opaque_user_data, struct lws_sspc_handle **ppss, - struct lws_sequencer *seq_owner, const char **ppayload_fmt); - -/** - * lws_sspc_destroy() - Destroy secure stream - * - * \param ppss: pointer to lws_ss_t pointer to be destroyed - * - * Destroys the lws_ss_t pointed to by *ppss, and sets *ppss to NULL. - */ -LWS_VISIBLE LWS_EXTERN void -lws_sspc_destroy(struct lws_sspc_handle **ppss); - -/** - * lws_sspc_request_tx() - Schedule stream for tx - * - * \param pss: pointer to lws_ss_t representing stream that wants to transmit - * - * Schedules a write on the stream represented by \p pss. When it's possible to - * write on this stream, the *tx callback will occur with an empty buffer for - * the stream owner to fill in. - */ -LWS_VISIBLE LWS_EXTERN void -lws_sspc_request_tx(struct lws_sspc_handle *pss); - -/** - * lws_sspc_client_connect() - Attempt the client connect - * - * \param h: secure streams handle - * - * Starts the connection process for the secure stream. Returns 0 if OK or - * nonzero if we have already failed. - */ -LWS_VISIBLE LWS_EXTERN int -lws_sspc_client_connect(struct lws_sspc_handle *h); - -/** - * lws_sspc_get_sequencer() - Return parent sequencer pointer if any - * - * \param h: secure streams handle - * - * Returns NULL if the secure stream is not associated with a sequencer. - * Otherwise returns a pointer to the owning sequencer. You can use this to - * identify which sequencer to direct messages to, from the secure stream - * callback. - */ -LWS_VISIBLE LWS_EXTERN struct lws_sequencer * -lws_sspc_get_sequencer(struct lws_sspc_handle *h); - -/** - * lws_sspc_proxy_create() - Start a unix domain socket proxy for Secure Streams - * - * \param context: lws_context - * - * Creates a vhost that listens on an abstract namespace unix domain socket at - * address "proxy.ss.lws". Client connections to this proxy to Secure Streams - */ -LWS_VISIBLE LWS_EXTERN int -lws_sspc_proxy_create(struct lws_context *context); - -/** - * lws_ss_get_context() - convenience helper to recover the lws context - * - * \h: secure streams handle - * - * Returns the lws context. Dispenses with the need to pass a copy of it into - * your secure streams handler. - */ - -LWS_VISIBLE LWS_EXTERN struct lws_context * -lws_sspc_get_context(struct lws_sspc_handle *h); - -LWS_VISIBLE LWS_EXTERN const struct lws_protocols lws_sspc_protocols[]; - -LWS_VISIBLE LWS_EXTERN const char * -lws_sspc_rideshare(struct lws_sspc_handle *h); - - -/** - * lws_sspc_set_metadata() - allow user to bind external data to defined ss metadata - * - * \h: secure streams handle - * \name: metadata name from the policy - * \value: pointer to user-managed data to bind to name - * \len: length of the user-managed data in value - * - * Binds user-managed data to the named metadata item from the ss policy. - * If present, the metadata item is handled in a protocol-specific way using - * the associated policy information. For example, in the policy - * - * "\"metadata\":" "[" - * "{\"uptag\":" "\"X-Upload-Tag:\"}," - * "{\"ctype\":" "\"Content-Type:\"}," - * "{\"xctype\":" "\"X-Content-Type:\"}" - * "]," - * - * when the policy is using h1 is interpreted to add h1 headers of the given - * name with the value of the metadata on the left. - * - * Return 0 if OK. - */ -LWS_VISIBLE LWS_EXTERN int -lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name, - void *value, size_t len); - -LWS_VISIBLE LWS_EXTERN int -lws_sspc_add_peer_tx_credit(struct lws_sspc_handle *h, int32_t add); - -LWS_VISIBLE LWS_EXTERN int -lws_sspc_get_est_peer_tx_credit(struct lws_sspc_handle *h); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-secure-streams.h libwebsockets-2.4.2/include/libwebsockets/lws-secure-streams.h --- libwebsockets-4.0.20/include/libwebsockets/lws-secure-streams.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-secure-streams.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,492 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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. - * - * included from libwebsockets.h - * - * - * Secure Streams is a *payload-only* client communication channel where all the - * details about the connection are held in a systemwide policy database and - * are keyed by the streamtype field... the user of the communication channel - * does not know or manage the choice of endpoint, tls CA, or even wire - * protocol. The advantage is he then does not have any dependency on any of - * those and they can be changed just by changing the policy database without - * touching the code using the stream. - * - * There are two ways secure streams interfaces to user code: - * - * 1) [Linux / RTOS] the natural, smallest interface is to call back to user - * code that only operates directly from the lws event loop thread context - * (direct callbacks from lws_ss_t) - * - * lws_thread( [user code] ---- lws ) - * - * 2) [Linux] where the user code is in a different process and communicates - * asynchronously via a proxy socket - * - * user_process{ [user code] | shim | socket-}------ lws_process{ lws } - * - * In the second, IPC, case, all packets are prepended by one or more bytes - * indicating the packet type and serializing any associated data, known as - * Serialized Secure Streams or SSS. - * - * Serialized Secure Streams - * ------------------------- - * - * On the transport, adjacent packets may be coalesced, that is, the original - * packet sizes are lost and two or more packets are combined. For that reason - * the serialization format always contains a 1-byte type and then a 2-byte - * frame length. - * - * Client to proxy - * - * - Proxied connection setup - * - * - 0: LWSSS_SER_TXPRE_STREAMTYPE - * - 1: 2-byte MSB-first rest-of-frame length - * - 3: 4 byte MSB-first initial tx credit - * - 7: the streamtype name with no NUL - * - * - Proxied tx - * - * - 0: LWSSS_SER_TXPRE_TX_PAYLOAD - * - 1: 2 byte MSB-first rest-of-frame length - * - 3: 4-byte MSB-first flags - * - 7: 4-byte MSB-first us between client requested write and wrote to proxy - * - 11: 8-byte MSB-first us resolution unix time client wrote to proxy - * - 17: payload - * - * - Proxied secure stream destroy - * - * - 0: LWSSS_SER_TXPRE_DESTROYING - * - 1: 00, 00 - * - * - Proxied metadata - sent when one metadata item set clientside - * - * - 0: LWSSS_SER_TXPRE_METADATA - * - 1: 2-byte MSB-first rest-of-frame length - * - 2: 1-byte metadata name length - * - 3: metadata name - * - ...: metadata value (for rest of packet) - * - * Proxy to client - * - * - Proxied connection setup result - * - * - 0: LWSSS_SER_RXPRE_CREATE_RESULT - * - 1: 2 byte MSB-first rest-of-frame length (usually 00, 03) - * - 3: 1 byte result, 0 = success. On failure, proxy will close connection. - * - 4: 2 byte MSB-first initial tx credit - * - 6: if present, comma-sep list of rideshare types from policy - * - * - Proxied rx - * - * - 0: LWSSS_SER_RXPRE_RX_PAYLOAD - * - 1: 2 byte MSB-first rest-of-frame length - * - 3: 4-byte MSB-first flags - * - 7: 4-byte MSB-first us between inbound read and wrote to client - * - 11: 8-byte MSB-first us resolution unix time proxy wrote to client - * - 17: (rideshare name len + rideshare name if flags & LWSSS_FLAG_RIDESHARE) - * payload - * - * - Proxied tx credit - * - * - 0: LWSSS_SER_RXPRE_TXCR_UPDATE - * - 1: 00, 04 - * - 3: 4-byte MSB-first addition tx credit bytes - * - * - Proxied state - * - * - 0: LWSSS_SER_RXPRE_CONNSTATE - * - 1: 00, 05 - * - 3: 1 byte state index - * - 7: 4-byte MSB-first ordinal - * - * - * Proxied tx may be read by the proxy but rejected due to lack of buffer space - * at the proxy. For that reason, tx must be held at the sender until it has - * been acknowledged or denied. - * - * Sinks - * ----- - * - * Sinks are logical "servers", you can register as a sink for a particular - * streamtype by using the lws_ss_create() api with ssi->register_sink set to 1. - * - * For directly fulfilled Secure Streams, new streams of that streamtype bind - * to the rx, tx and state handlers given when it was registered. - * - * - When new streams are created the registered sink handler for (*state) is - * called with event LWSSSCS_SINK_JOIN and the new client stream handle in - * the h_src parameter. - * - * - When the client stream sends something to the sink, it calls the sink's - * (*rx) with the client stream's - */ - -#define LWS_SS_MTU 1540 - -struct lws_ss_handle; -typedef uint32_t lws_ss_tx_ordinal_t; - -/* - * connection state events - */ -typedef enum { - LWSSSCS_CREATING, - LWSSSCS_DISCONNECTED, - LWSSSCS_UNREACHABLE, - LWSSSCS_AUTH_FAILED, - LWSSSCS_CONNECTED, - LWSSSCS_CONNECTING, - LWSSSCS_DESTROYING, - LWSSSCS_POLL, - LWSSSCS_ALL_RETRIES_FAILED, /* all retries in bo policy failed */ - LWSSSCS_QOS_ACK_REMOTE, /* remote peer received and acked tx */ - LWSSSCS_QOS_NACK_REMOTE, - LWSSSCS_QOS_ACK_LOCAL, /* local proxy accepted our tx */ - LWSSSCS_QOS_NACK_LOCAL, /* local proxy refused our tx */ - - LWSSSCS_SINK_JOIN, /* sinks get this when a new source - * stream joins the sink */ - LWSSSCS_SINK_PART, /* sinks get this when a new source - * stream leaves the sink */ -} lws_ss_constate_t; - -enum { - LWSSS_FLAG_SOM = (1 << 0), - /* payload contains the start of new message */ - LWSSS_FLAG_EOM = (1 << 1), - /* payload contains the end of message */ - LWSSS_FLAG_POLL = (1 << 2), - /* Not a real transmit... poll for rx if protocol needs it */ - LWSSS_FLAG_RELATED_START = (1 << 3), - /* Appears in a zero-length message indicating a message group of zero - * or more messages is now starting. */ - LWSSS_FLAG_RELATED_END = (1 << 4), - /* Appears in a zero-length message indicating a message group of zero - * or more messages has now finished. */ - LWSSS_FLAG_RIDESHARE = (1 << 5), - /* Serialized payload starts with non-default rideshare name length and - * name string without NUL, then payload */ - - /* - * In the case the secure stream is proxied across a process or thread - * boundary, eg by proxying through a socket for IPC, metadata must be - * carried in-band. A byte is prepended to each rx payload to - * differentiate what it is. - * - * Secure streams where the user is called back directly does not need - * any of this and only pure payloads are passed. - * - * rx (received by client) prepends for proxied connections - */ - - LWSSS_SER_RXPRE_RX_PAYLOAD = 0x55, - LWSSS_SER_RXPRE_CREATE_RESULT, - LWSSS_SER_RXPRE_CONNSTATE, - LWSSS_SER_RXPRE_TXCR_UPDATE, - LWSSS_SER_RXPRE_TLSNEG_ENCLAVE_SIGN, - - /* tx (send by client) prepends for proxied connections */ - - LWSSS_SER_TXPRE_STREAMTYPE = 0xaa, - LWSSS_SER_TXPRE_ONWARD_CONNECT, - LWSSS_SER_TXPRE_DESTROYING, - LWSSS_SER_TXPRE_TX_PAYLOAD, - LWSSS_SER_TXPRE_METADATA, - LWSSS_SER_TXPRE_TXCR_UPDATE, - LWSSS_SER_TXPRE_TLSNEG_ENCLAVE_SIGNED, -}; - -typedef enum { - LPCS_WAIT_INITIAL_TX = 1, /* after connect, must send streamtype */ - LPCS_REPORTING_FAIL, /* stream creation failed, wait to to tell */ - LPCS_REPORTING_OK, /* stream creation succeeded, wait to to tell */ - LPCS_OPERATIONAL, /* ready for payloads */ - LPCS_DESTROYED, - - LPCS_SENDING_INITIAL_TX = 1, /* after connect, must send streamtype */ - LPCS_WAITING_CREATE_RESULT, /* wait to hear if proxy ss create OK */ - LPCS_LOCAL_CONNECTED, /* we are in touch with the proxy */ - LPCS_ONWARD_CONNECT, /* request onward ss connection */ - -} lws_ss_conn_states_t; - -/** - * lws_ss_info_t: information about stream to be created - * - * Prepare this struct with information about what the stream type is and how - * the stream should interface with your code, and pass it to lws_ss_create() - * to create the requested stream. - */ - -typedef struct lws_ss_info { - const char *streamtype; /**< type of stream we want to create */ - size_t user_alloc; /**< size of user allocation */ - size_t handle_offset; /**< offset of handle stg in user_alloc type, - set to offsetof(mytype, my_handle_member) */ - size_t opaque_user_data_offset; - /**< offset of opaque user data ptr in user_alloc type, set to - offsetof(mytype, opaque_ud_member) */ - - int (*rx)(void *userobj, const uint8_t *buf, size_t len, - int flags); - /**< callback with rx payload for this stream */ - int (*tx)(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, - size_t *len, int *flags); - /**< callback to send payload on this stream... 0 = send as set in - * len and flags, 1 = do not send anything (ie, not even 0 len frame) */ - int (*state)(void *userobj, void *h_src /* ss handle type */, - lws_ss_constate_t state, lws_ss_tx_ordinal_t ack); - /**< advisory cb about state of stream and QoS status if applicable... - * h_src is only used with sinks and LWSSSCS_SINK_JOIN/_PART events. - * Return nonzero to indicate you want to destroy the stream. */ - int manual_initial_tx_credit; - /**< 0 = manage any tx credit automatically, nonzero explicitly sets the - * peer stream to have the given amount of tx credit, if the protocol - * can support it. */ - char register_sink; - /**< If set, we're not creating a specific stream, but registering - * ourselves as the "sink" for .streamtype. It's analogous to saying - * we want to be the many-to-one "server" for .streamtype; when other - * streams are created with that streamtype, they should be forwarded - * to this stream owner, where they join and part from the sink via - * (*state) LWSSSCS_SINK_JOIN / _PART events, the new client handle - * being provided in the h_src parameter. - */ -} lws_ss_info_t; - -/** - * lws_ss_create() - Create secure stream - * - * \param context: the lws context to create this inside - * \param tsi: service thread index to create on (normally 0) - * \param ssi: pointer to lws_ss_info_t filled in with info about desired stream - * \param opaque_user_data: opaque data to set in the stream's user object - * \param ppss: pointer to secure stream handle pointer set on exit - * \param ppayload_fmt: NULL or pointer to a string ptr to take payload format - * name from the policy - * - * Requests a new secure stream described by \p ssi be created. If successful, - * the stream is created, its state callback called with LWSSSCS_CREATING, *ppss - * is set to point to the handle, and it returns 0. If it failed, it returns - * nonzero. - * - * Along with the opaque stream object, streams overallocate - * - * 1) a user data struct whose size is set in ssi - * 2) nauth plugin instantiation data (size set in the plugin struct) - * 3) sauth plugin instantiation data (size set in the plugin struct) - * 4) space for a copy of the stream type name - * - * The user data struct is initialized to all zeros, then the .handle_offset and - * .opaque_user_data_offset fields of the ssi are used to prepare the user data - * struct with the ss handle that was created, and a copy of the - * opaque_user_data pointer given as an argument. - * - * If you want to set up the stream with specific information, point to it in - * opaque_user_data and use the copy of that pointer in your user data member - * for it starting from the LWSSSCS_CREATING state call. - * - * Since different endpoints chosen by the policy may require different payload - * formats, \p ppayload_fmt is set to point to the name of the needed payload - * format from the policy database if non-NULL. - */ -LWS_VISIBLE LWS_EXTERN int -lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi, - void *opaque_user_data, struct lws_ss_handle **ppss, - struct lws_sequencer *seq_owner, const char **ppayload_fmt); - -/** - * lws_ss_destroy() - Destroy secure stream - * - * \param ppss: pointer to lws_ss_t pointer to be destroyed - * - * Destroys the lws_ss_t pointed to by *ppss, and sets *ppss to NULL. - */ -LWS_VISIBLE LWS_EXTERN void -lws_ss_destroy(struct lws_ss_handle **ppss); - -/** - * lws_ss_request_tx() - Schedule stream for tx - * - * \param pss: pointer to lws_ss_t representing stream that wants to transmit - * - * Schedules a write on the stream represented by \p pss. When it's possible to - * write on this stream, the *tx callback will occur with an empty buffer for - * the stream owner to fill in. - */ -LWS_VISIBLE LWS_EXTERN void -lws_ss_request_tx(struct lws_ss_handle *pss); - -/** - * lws_ss_request_tx() - Schedule stream for tx - * - * \param pss: pointer to lws_ss_t representing stream that wants to transmit - * \param len: the length of the write in bytes - * - * Schedules a write on the stream represented by \p pss. When it's possible to - * write on this stream, the *tx callback will occur with an empty buffer for - * the stream owner to fill in. - * - * This api variant should be used when it's possible the payload will go out - * over h1 with x-web-form-urlencoded or similar Content-Type. - */ -LWS_VISIBLE LWS_EXTERN void -lws_ss_request_tx_len(struct lws_ss_handle *pss, unsigned long len); - - -/** - * lws_ss_client_connect() - Attempt the client connect - * - * \param h: secure streams handle - * - * Starts the connection process for the secure stream. Returns 0 if OK or - * nonzero if we have already failed. - */ -LWS_VISIBLE LWS_EXTERN int -lws_ss_client_connect(struct lws_ss_handle *h); - -/** - * lws_ss_get_sequencer() - Return parent sequencer pointer if any - * - * \param h: secure streams handle - * - * Returns NULL if the secure stream is not associated with a sequencer. - * Otherwise returns a pointer to the owning sequencer. You can use this to - * identify which sequencer to direct messages to, from the secure stream - * callback. - */ -LWS_VISIBLE LWS_EXTERN struct lws_sequencer * -lws_ss_get_sequencer(struct lws_ss_handle *h); - -/** - * lws_ss_proxy_create() - Start a unix domain socket proxy for Secure Streams - * - * \param context: lws_context - * \param bind: if port is 0, unix domain path with leading @ for abstract. - * if port nonzero, NULL, or network interface to bind listen to - * \param port: tcp port to listen on - * - * Creates a vhost that listens either on an abstract namespace unix domain - * socket (port = 0) or a tcp listen socket (port nonzero). If bind is NULL - * and port is 0, the abstract unix domain socket defaults to "proxy.ss.lws". - * - * Client connections to this proxy to Secure Streams are fulfilled using the - * policy local to the proxy and the data passed between the client and the - * proxy using serialized Secure Streams protocol. - */ -LWS_VISIBLE LWS_EXTERN int -lws_ss_proxy_create(struct lws_context *context, const char *bind, int port); - -/** - * lws_ss_state_name() - convenience helper to get a printable conn state name - * - * \param state: the connection state index - * - * Returns a printable name for the connection state index passed in. - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_ss_state_name(int state); - -/** - * lws_ss_get_context() - convenience helper to recover the lws context - * - * \param h: secure streams handle - * - * Returns the lws context. Dispenses with the need to pass a copy of it into - * your secure streams handler. - */ -LWS_VISIBLE LWS_EXTERN struct lws_context * -lws_ss_get_context(struct lws_ss_handle *h); - -/** - * lws_ss_rideshare() - find the current streamtype when types rideshare - * - * \param h: the stream handle - * - * Under some conditions, the payloads may be structured using protocol- - * specific formatting, eg, http multipart mime. It's possible to map the - * logical partitions in the payload to different stream types using - * the policy "rideshare" feature. - * - * This api lets the callback code find out which rideshare stream type the - * current payload chunk belongs to. - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_ss_rideshare(struct lws_ss_handle *h); - - -/** - * lws_ss_set_metadata() - allow user to bind external data to defined ss metadata - * - * \param h: secure streams handle - * \param name: metadata name from the policy - * \param value: pointer to user-managed data to bind to name - * \param len: length of the user-managed data in value - * - * Binds user-managed data to the named metadata item from the ss policy. - * If present, the metadata item is handled in a protocol-specific way using - * the associated policy information. For example, in the policy - * - * "\"metadata\":" "[" - * "{\"uptag\":" "\"X-Upload-Tag:\"}," - * "{\"ctype\":" "\"Content-Type:\"}," - * "{\"xctype\":" "\"\"}" - * "]," - * - * when the policy is using h1 is interpreted to add h1 headers of the given - * name with the value of the metadata on the left. - * - * Return 0 if OK or nonzero if, eg, metadata name does not exist on the - * streamtype. - */ -LWS_VISIBLE LWS_EXTERN int -lws_ss_set_metadata(struct lws_ss_handle *h, const char *name, - void *value, size_t len); - - -/** - * lws_ss_add_peer_tx_credit() - allow peer to transmit more to us - * - * \param h: secure streams handle - * \param add: additional tx credit (signed) - * - * Indicate to remote peer that we can accept \p add bytes more payload being - * sent to us. - */ -LWS_VISIBLE LWS_EXTERN int -lws_ss_add_peer_tx_credit(struct lws_ss_handle *h, int32_t add); - -/** - * lws_ss_get_est_peer_tx_credit() - get our current estimate of peer's tx credit - * - * \param h: secure streams handle - * - * Based on what credit we gave it, and what we have received, report our - * estimate of peer's tx credit usable to transmit to us. This may be outdated - * in that some or all of its credit may already have been expended by sending - * stuff to us that is in flight already. - */ -LWS_VISIBLE LWS_EXTERN int -lws_ss_get_est_peer_tx_credit(struct lws_ss_handle *h); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-secure-streams-policy.h libwebsockets-2.4.2/include/libwebsockets/lws-secure-streams-policy.h --- libwebsockets-4.0.20/include/libwebsockets/lws-secure-streams-policy.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-secure-streams-policy.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,246 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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. - * - * included from libwebsockets.h - */ - -typedef int (*plugin_auth_status_cb)(struct lws_ss_handle *ss, int status); - -/** - * lws_ss_plugin_auth_t - api for an auth plugin - * - * Auth plugins create and sequence authenticated connections that can carry one - * or more streams to an endpoint. That may involve other connections to other - * places to eg, gather authenticated tokens and then make the real connection - * using the tokens. - * - * The secure stream object contains members to record which auth plugin the - * stream is bound to and an over-allocation of the secure stream object to - * contain the plugin auth private data. - * - * The auth plugin controls the state of the stream connection via the status - * callback, and handles retries. - * - * Network connections may require one kind of auth sequencing, and streams - * inside those connections another kind of auth sequencing depending on their - * role. So the secure stream object allows defining plugins for both kinds. - * - * Streams may disappear at any time and require reauth to bring a new one up. - * The auth plugin sequencer will connect / reconnect either on demand, or from - * the start and after any connectivity loss if any stream using the connection - * has the LWSSSPOLF_NAILED_UP flag. - */ - -typedef struct lws_ss_plugin { - struct lws_ss_plugin *next; - const char *name; /**< auth plugin name */ - size_t alloc; /**< size of private allocation */ - - int (*create)(struct lws_ss_handle *ss, void *info, - plugin_auth_status_cb status); - /**< called when the auth plugin is instantiated - and bound to the secure stream. status is - called back with advisory information about - the authenticated stream state as it - proceeds */ - int (*destroy)(struct lws_ss_handle *ss); - /**< called when the related secure stream is - being destroyed, and anything the auth - plugin is doing should also be destroyed */ - int (*munge)(struct lws_ss_handle *ss, char *path, - size_t path_len); - /**< if the plugin needs to munge transactions - that have metadata outside the payload (eg, - add http headers) this callback will give - it the opportunity to do so */ -} lws_ss_plugin_t; - - -typedef struct lws_ss_x509 { - struct lws_ss_x509 *next; - const char *vhost_name; /**< vhost name using cert ctx */ - const uint8_t *ca_der; /**< DER x.509 cert */ - size_t ca_der_len; /**< length of DER cert */ -} lws_ss_x509_t; - -enum { - LWSSSPOLF_OPPORTUNISTIC = (1 << 0), - /**< the connection doesn't exist unless client asks to write */ - LWSSSPOLF_NAILED_UP = (1 << 1), - /**< the connection tries to be connected the whole life of the ss */ - LWSSSPOLF_URGENT_TX = (1 << 2), - /**< this connection carries critical tx data */ - LWSSSPOLF_URGENT_RX = (1 << 3), - /**< this connection carries critical rx data */ - LWSSSPOLF_TLS = (1 << 4), - /**< stream must be connected via a tls tunnel */ - LWSSSPOLF_LONG_POLL = (1 << 5), - /**< stream used to receive async rx at arbitrary intervals */ - LWSSSPOLF_AUTH_BEARER = (1 << 6), - /**< for http, use lws_system auth token 0 in authentication: bearer */ - LWSSSPOLF_HTTP_NO_CONTENT_LENGTH = (1 << 7), - /**< don't add any content length even if we have it */ - LWSSSPOLF_QUIRK_NGHTTP2_END_STREAM = (1 << 8), - /**< set the client flag LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM */ - LWSSSPOLF_H2_QUIRK_OVERFLOWS_TXCR = (1 << 9), - /**< set the client flag LCCSCF_H2_QUIRK_OVERFLOWS_TXCR */ - LWSSSPOLF_H2_QUIRK_UNCLEAN_HPACK_STATE = (1 << 10), - /**< HPACK decoder state does not end cleanly */ - LWSSSPOLF_HTTP_MULTIPART = (1 << 11), - /**< indicates stream goes out as specifically a multipart mime POST - * section... if the tx has LWSSS_FLAG_COALESCE_CONTINUES flag then more - * multipart sections are expected. Without it, the multipart wrapper - * is closed and the http transaction issue completed when this message - * finishes. */ - LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED = (1 << 12), - /**< set up lws_system client cert */ - LWSSSPOLF_LOCAL_SINK = (1 << 13), - /**< expected to bind to a local sink only */ -}; - -typedef struct lws_ss_trust_store { - struct lws_ss_trust_store *next; - const char *name; - - lws_ss_x509_t *ssx509[8]; - int count; -} lws_ss_trust_store_t; - -enum { - LWSSSP_H1, - LWSSSP_H2, - LWSSSP_WS, - - - LWSSS_HBI_AUTH = 0, - LWSSS_HBI_DSN, - LWSSS_HBI_FWV, - LWSSS_HBI_TYPE, - - _LWSSS_HBI_COUNT /* always last */ -}; - -typedef struct lws_ss_metadata { - struct lws_ss_metadata *next; - const char *name; - void *value; - size_t length; - - uint8_t value_on_lws_heap; /* proxy does this */ -} lws_ss_metadata_t; - - -/** - * lws_ss_policy_t: policy database entry for a stream type - * - * Decides the system policy for how to implement connections of name - * .streamtype. - * - * Streams may need one kind of auth sequencing for the network connection and - * another kind of auth sequencing for the streams that are carried inside it, - * this is the purpose of .nauth and .sauth. Both are optional and may be NULL. - * - * An array of these is set at context creation time, ending with one with a - * NULL streamtype. - */ -typedef struct lws_ss_policy { - struct lws_ss_policy *next; - const char *streamtype; /**< stream type lhs to match on */ - - const char *endpoint; /**< DNS address to connect to */ - const char *rideshare_streamtype; /**< optional transport - * on another, preexisting stream of this - * streamtype name */ - const char *payload_fmt; - const char *socks5_proxy; - lws_ss_metadata_t *metadata; /* linked-list of metadata */ - - /* protocol-specific connection policy details */ - - union { - - /* details for http-related protocols... */ - - struct { - - /* common to all http-related protocols */ - - const char *method; - const char *url; - - const char *multipart_name; - const char *multipart_filename; - const char *multipart_content_type; - - const char *blob_header[_LWSSS_HBI_COUNT]; - const char *auth_preamble; - - union { -// struct { /* LWSSSP_H1 */ -// } h1; -// struct { /* LWSSSP_H2 */ -// } h2; - struct { /* LWSSSP_WS */ - const char *subprotocol; - uint8_t binary; - /* false = TEXT, true = BINARY */ - } ws; - } u; - } http; - - struct { - const char *topic; /* stream sends on this topic */ - const char *subscribe; /* stream subscribes to this topic */ - - const char *will_topic; - const char *will_message; - - uint16_t keep_alive; - uint8_t qos; - uint8_t clean_start; - uint8_t will_qos; - uint8_t will_retain; - - } mqtt; - - /* details for non-http related protocols... */ - } u; - - const - struct lws_ss_plugin *plugins[2]; /**< NULL or auth plugin */ - const void *plugins_info[2]; /**< plugin-specific data */ - - const lws_ss_trust_store_t *trust_store; /**< CA certs needed for conn - validation, only set between policy parsing and vhost creation */ - - const lws_retry_bo_t *retry_bo; /**< retry policy to use */ - - uint32_t flags; /**< stream attribute flags */ - - uint16_t port; /**< endpoint port */ - - uint8_t metadata_count; /**< metadata count */ - uint8_t protocol; /**< protocol index */ - uint8_t client_cert; /**< which client cert to apply - 0 = none, 1+ = cc 0+ */ -} lws_ss_policy_t; diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-sequencer.h libwebsockets-2.4.2/include/libwebsockets/lws-sequencer.h --- libwebsockets-4.0.20/include/libwebsockets/lws-sequencer.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-sequencer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,241 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws_sequencer is intended to help implement sequences that: - * - * - outlive a single connection lifetime, - * - are not associated with a particular protocol, - * - are not associated with a particular vhost, - * - must receive and issue events inside the event loop - * - * lws_sequencer-s are bound to a pt (per-thread) which for the default case of - * one service thread is the same as binding to an lws_context. - */ -/* - * retry backoff table... retry n happens after .retry_ms_table[n] ms, with - * the last entry used if n is greater than the number of entries. - * - * The first .conceal_count retries are concealed, but after that the failures - * are reported. - */ - -typedef enum { - LWSSEQ_CREATED, /* sequencer created */ - LWSSEQ_DESTROYED, /* sequencer destroyed */ - LWSSEQ_TIMED_OUT, /* sequencer timeout */ - LWSSEQ_HEARTBEAT, /* 1Hz callback */ - - LWSSEQ_WSI_CONNECTED, /* wsi we bound to us has connected */ - LWSSEQ_WSI_CONN_FAIL, /* wsi we bound to us has failed to connect */ - LWSSEQ_WSI_CONN_CLOSE, /* wsi we bound to us has closed */ - - - LWSSEQ_SS_STATE_BASE, /* secure streams owned by a sequencer provide - * automatic messages about state changes on - * the sequencer, passing the oridinal in the - * event argument field. The message index is - * LWSSEQ_SS_STATE_BASE + the enum from - * lws_ss_constate_t */ - - LWSSEQ_USER_BASE = 100 /* define your events from here */ -} lws_seq_events_t; - -typedef enum lws_seq_cb_return { - LWSSEQ_RET_CONTINUE, - LWSSEQ_RET_DESTROY -} lws_seq_cb_return_t; - -/* - * handler for this sequencer. Return 0 if OK else nonzero to destroy the - * sequencer. LWSSEQ_DESTROYED will be called back to the handler so it can - * close / destroy any private assets associated with the sequence. - * - * The callback may return either LWSSEQ_RET_CONTINUE for the sequencer to - * resume or LWSSEQ_RET_DESTROY to indicate the sequence is finished. - * - * Event indexes consist of some generic ones but mainly user-defined ones - * starting from LWSSEQ_USER_BASE. - */ -typedef lws_seq_cb_return_t (*lws_seq_event_cb)(struct lws_sequencer *seq, - void *user, int event, void *data, void *aux); - -typedef struct lws_seq_info { - struct lws_context *context; /* lws_context for seq */ - int tsi; /* thread service idx */ - size_t user_size; /* size of user alloc */ - void **puser; /* place ptr to user */ - lws_seq_event_cb cb; /* seq callback */ - const char *name; /* seq name */ - const lws_retry_bo_t *retry; /* retry policy */ -} lws_seq_info_t; - -/** - * lws_seq_create() - create and bind sequencer to a pt - * - * \param info: information about sequencer to create - * - * This binds an abstract sequencer to a per-thread (by default, the single - * event loop of an lws_context). After the event loop starts, the sequencer - * will receive an LWSSEQ_CREATED event on its callback from the event loop - * context, where it can begin its sequence flow. - * - * Lws itself will only call the callback subsequently with LWSSEQ_DESTROYED - * when the sequencer is being destroyed. - * - * pt locking is used to protect the related data structures. - */ -LWS_VISIBLE LWS_EXTERN struct lws_sequencer * -lws_seq_create(lws_seq_info_t *info); - -/** - * lws_seq_destroy() - destroy the sequencer - * - * \param seq: pointer to the the opaque sequencer pointer returned by - * lws_seq_create() - * - * This proceeds to destroy the sequencer, calling LWSSEQ_DESTROYED and then - * freeing the sequencer object itself. The pointed-to seq pointer will be - * set to NULL. - */ -LWS_VISIBLE LWS_EXTERN void -lws_seq_destroy(struct lws_sequencer **seq); - -/** - * lws_seq_queue_event() - queue an event on the given sequencer - * - * \param seq: the opaque sequencer pointer returned by lws_seq_create() - * \param e: the event index to queue - * \param data: associated opaque (to lws) data to provide the callback - * \param aux: second opaque data to provide the callback - * - * This queues the event on a given sequencer. Queued events are delivered one - * per sequencer each subsequent time around the event loop, so the cb is called - * from the event loop thread context. - * - * Notice that because the events are delivered in order from the event loop, - * the scope of objects pointed to by \p data or \p aux may exceed the lifetime - * of the thing containing the pointed-to data. So it's usually better to pass - * values here. - */ -LWS_VISIBLE LWS_EXTERN int -lws_seq_queue_event(struct lws_sequencer *seq, lws_seq_events_t e, void *data, - void *aux); - -/** - * lws_seq_check_wsi() - check if wsi still extant - * - * \param seq: the sequencer interested in the wsi - * \param wsi: the wsi we want to confirm hasn't closed yet - * - * Check if wsi still extant, by peeking in the message queue for a - * LWSSEQ_WSI_CONN_CLOSE message about wsi. (Doesn't need to do the same for - * CONN_FAIL since that will never have produced any messages prior to that). - * - * Use this to avoid trying to perform operations on wsi that have already - * closed but we didn't get to that message yet. - * - * Returns 0 if not closed yet or 1 if it has closed but we didn't process the - * close message yet. - */ -LWS_VISIBLE LWS_EXTERN int -lws_seq_check_wsi(struct lws_sequencer *seq, struct lws *wsi); - -#define LWSSEQTO_NONE 0 - -/** - * lws_seq_timeout_us() - set a timeout by which the sequence must have - * completed by a different event or inform the - * sequencer - * - * \param seq: The sequencer to set the timeout on - * \param us: How many us in the future to fire the timeout - * LWS_SET_TIMER_USEC_CANCEL = cancel any existing timeout - * - * This api allows the sequencer to ask to be informed if it has not completed - * or disabled its timeout after secs seconds. Lws will send a LWSSEQ_TIMED_OUT - * event to the sequencer if the timeout expires. - * - * Typically the sequencer sets the timeout when starting a step, then waits to - * hear a queued event informing it the step completed or failed. The timeout - * provides a way to deal with the case the step neither completed nor failed - * within the timeout period. - * - * Lws wsi timeouts are not really suitable for this since they are focused on - * short-term protocol timeout protection and may be set and reset many times - * in one transaction. Wsi timeouts also enforce closure of the wsi when they - * trigger, sequencer timeouts have no side effect except to queue the - * LWSSEQ_TIMED_OUT message and leave it to the sequencer to decide how to - * react appropriately. - */ -LWS_VISIBLE LWS_EXTERN int -lws_seq_timeout_us(struct lws_sequencer *seq, lws_usec_t us); - -/** - * lws_seq_from_user(): get the lws_seq_t pointer from the user ptr - * - * \param u: the sequencer user allocation returned by lws_seq_create() or - * provided in the sequencer callback - * - * This gets the lws_seq_t * from the sequencer user allocation pointer. - * Actually these are allocated at the same time in one step, with the user - * allocation immediately after the lws_seq_t, so lws can compute where - * the lws_seq_t is from having the user allocation pointer. Since the - * size of the lws_seq_t is unknown to user code, this helper does it for - * you. - */ -LWS_VISIBLE LWS_EXTERN struct lws_sequencer * -lws_seq_from_user(void *u); - -/** - * lws_seq_us_since_creation(): elapsed seconds since sequencer created - * - * \param seq: pointer to the lws_seq_t - * - * Returns the number of us elapsed since the lws_seq_t was - * created. This is useful to calculate sequencer timeouts for the current - * step considering a global sequencer lifetime limit. - */ -LWS_VISIBLE LWS_EXTERN lws_usec_t -lws_seq_us_since_creation(struct lws_sequencer *seq); - -/** - * lws_seq_name(): get the name of this sequencer - * - * \param seq: pointer to the lws_seq_t - * - * Returns the name given when the sequencer was created. This is useful to - * annotate logging when then are multiple sequencers in play. - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_seq_name(struct lws_sequencer *seq); - -/** - * lws_seq_get_context(): get the lws_context sequencer was created on - * - * \param seq: pointer to the lws_seq_t - * - * Returns the lws_context. Saves you having to store it if you have a seq - * pointer handy. - */ -LWS_VISIBLE LWS_EXTERN struct lws_context * -lws_seq_get_context(struct lws_sequencer *seq); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-service.h libwebsockets-2.4.2/include/libwebsockets/lws-service.h --- libwebsockets-4.0.20/include/libwebsockets/lws-service.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-service.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,200 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup service Built-in service loop entry - * - * ##Built-in service loop entry - * - * If you're not using libev / libuv, these apis are needed to enter the poll() - * wait in lws and service any connections with pending events. - */ -///@{ - -/** - * lws_service() - Service any pending websocket activity - * \param context: Websocket context - * \param timeout_ms: Set to 0; ignored; for backward compatibility - * - * This function deals with any pending websocket traffic, for three - * kinds of event. It handles these events on both server and client - * types of connection the same. - * - * 1) Accept new connections to our context's server - * - * 2) Call the receive callback for incoming frame data received by - * server or client connections. - * - * Since v3.2 internally the timeout wait is ignored, the lws scheduler is - * smart enough to stay asleep until an event is queued. - */ -LWS_VISIBLE LWS_EXTERN int -lws_service(struct lws_context *context, int timeout_ms); - -/** - * lws_service_tsi() - Service any pending websocket activity - * - * \param context: Websocket context - * \param timeout_ms: Set to 0; ignored; for backwards compatibility - * \param tsi: Thread service index, starting at 0 - * - * Same as lws_service(), but for a specific thread service index. Only needed - * if you are spawning multiple service threads. - */ -LWS_VISIBLE LWS_EXTERN int -lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi); - -/** - * lws_cancel_service_pt() - Cancel servicing of pending socket activity - * on one thread - * \param wsi: Cancel service on the thread this wsi is serviced by - * - * Same as lws_cancel_service(), but targets a single service thread, the one - * the wsi belongs to. You probably want to use lws_cancel_service() instead. - */ -LWS_VISIBLE LWS_EXTERN void -lws_cancel_service_pt(struct lws *wsi); - -/** - * lws_cancel_service() - Cancel wait for new pending socket activity - * \param context: Websocket context - * - * This function creates an immediate "synchronous interrupt" to the lws poll() - * wait or event loop. As soon as possible in the serialzed service sequencing, - * a LWS_CALLBACK_EVENT_WAIT_CANCELLED callback is sent to every protocol on - * every vhost. - * - * lws_cancel_service() may be called from another thread while the context - * exists, and its effect will be immediately serialized. - */ -LWS_VISIBLE LWS_EXTERN void -lws_cancel_service(struct lws_context *context); - -/** - * lws_service_fd() - Service polled socket with something waiting - * \param context: Websocket context - * \param pollfd: The pollfd entry describing the socket fd and which events - * happened - * - * This function takes a pollfd that has POLLIN or POLLOUT activity and - * services it according to the state of the associated - * struct lws. - * - * The one call deals with all "service" that might happen on a socket - * including listen accepts, http files as well as websocket protocol. - * - * If a pollfd says it has something, you can just pass it to - * lws_service_fd() whether it is a socket handled by lws or not. - * If it sees it is a lws socket, the traffic will be handled and - * pollfd->revents will be zeroed now. - * - * If the socket is foreign to lws, it leaves revents alone. So you can - * see if you should service yourself by checking the pollfd revents - * after letting lws try to service it. - * - * lws before v3.2 allowed pollfd to be NULL, to indicate that background - * periodic processing should be done. Since v3.2, lws schedules any items - * that need handling in the future using lws_sul and NULL is no longer valid. - */ -LWS_VISIBLE LWS_EXTERN int -lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd); - -/** - * lws_service_fd_tsi() - Service polled socket in specific service thread - * \param context: Websocket context - * \param pollfd: The pollfd entry describing the socket fd and which events - * happened. - * \param tsi: thread service index - * - * Same as lws_service_fd() but used with multiple service threads - */ -LWS_VISIBLE LWS_EXTERN int -lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, - int tsi); - -/** - * lws_service_adjust_timeout() - Check for any connection needing forced service - * \param context: Websocket context - * \param timeout_ms: The original poll timeout value. You can just set this - * to 1 if you don't really have a poll timeout. - * \param tsi: thread service index - * - * Under some conditions connections may need service even though there is no - * pending network action on them, this is "forced service". For default - * poll() and libuv / libev, the library takes care of calling this and - * dealing with it for you. But for external poll() integration, you need - * access to the apis. - * - * If anybody needs "forced service", returned timeout is zero. In that case, - * you can call lws_service_tsi() with a timeout of -1 to only service - * guys who need forced service. - */ -LWS_VISIBLE LWS_EXTERN int -lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi); - -/* Backwards compatibility */ -#define lws_plat_service_tsi lws_service_tsi - -LWS_VISIBLE LWS_EXTERN int -lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); - -///@} - -/*! \defgroup uv libuv helpers - * - * ##libuv helpers - * - * APIs specific to libuv event loop itegration - */ -///@{ -#ifdef LWS_WITH_LIBUV -/* - * Any direct libuv allocations in lws protocol handlers must participate in the - * lws reference counting scheme. Two apis are provided: - * - * - lws_libuv_static_refcount_add(handle, context) to mark the handle with - * a pointer to the context and increment the global uv object counter - * - * - lws_libuv_static_refcount_del() which should be used as the close callback - * for your own libuv objects declared in the protocol scope. - * - * Using the apis allows lws to detach itself from a libuv loop completely - * cleanly and at the moment all of its libuv objects have completed close. - */ - -LWS_VISIBLE LWS_EXTERN uv_loop_t * -lws_uv_getloop(struct lws_context *context, int tsi); - -LWS_VISIBLE LWS_EXTERN void -lws_libuv_static_refcount_add(uv_handle_t *, struct lws_context *context); - -LWS_VISIBLE LWS_EXTERN void -lws_libuv_static_refcount_del(uv_handle_t *); - -#endif /* LWS_WITH_LIBUV */ - -#if defined(LWS_PLAT_FREERTOS) -#define lws_libuv_static_refcount_add(_a, _b) -#define lws_libuv_static_refcount_del NULL -#endif -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-sha1-base64.h libwebsockets-2.4.2/include/libwebsockets/lws-sha1-base64.h --- libwebsockets-4.0.20/include/libwebsockets/lws-sha1-base64.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-sha1-base64.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup sha SHA and B64 helpers - * ##SHA and B64 helpers - * - * These provide SHA-1 and B64 helper apis - */ -///@{ -#ifdef LWS_SHA1_USE_OPENSSL_NAME -#define lws_SHA1 SHA1 -#else -/** - * lws_SHA1(): make a SHA-1 digest of a buffer - * - * \param d: incoming buffer - * \param n: length of incoming buffer - * \param md: buffer for message digest (must be >= 20 bytes) - * - * Reduces any size buffer into a 20-byte SHA-1 hash. - */ -LWS_VISIBLE LWS_EXTERN unsigned char * -lws_SHA1(const unsigned char *d, size_t n, unsigned char *md); -#endif -/** - * lws_b64_encode_string(): encode a string into base 64 - * - * \param in: incoming buffer - * \param in_len: length of incoming buffer - * \param out: result buffer - * \param out_size: length of result buffer - * - * Encodes a string using b64 - */ -LWS_VISIBLE LWS_EXTERN int -lws_b64_encode_string(const char *in, int in_len, char *out, int out_size); -/** - * lws_b64_encode_string_url(): encode a string into base 64 - * - * \param in: incoming buffer - * \param in_len: length of incoming buffer - * \param out: result buffer - * \param out_size: length of result buffer - * - * Encodes a string using b64 with the "URL" variant (+ -> -, and / -> _) - */ -LWS_VISIBLE LWS_EXTERN int -lws_b64_encode_string_url(const char *in, int in_len, char *out, int out_size); -/** - * lws_b64_decode_string(): decode a string from base 64 - * - * \param in: incoming buffer - * \param out: result buffer - * \param out_size: length of result buffer - * - * Decodes a NUL-terminated string using b64 - */ -LWS_VISIBLE LWS_EXTERN int -lws_b64_decode_string(const char *in, char *out, int out_size); -/** - * lws_b64_decode_string_len(): decode a string from base 64 - * - * \param in: incoming buffer - * \param in_len: length of incoming buffer - * \param out: result buffer - * \param out_size: length of result buffer - * - * Decodes a range of chars using b64 - */ -LWS_VISIBLE LWS_EXTERN int -lws_b64_decode_string_len(const char *in, int in_len, char *out, int out_size); - -struct lws_b64state { - unsigned char quad[4]; - size_t done; - size_t len; - int i; - int c; -}; - -LWS_VISIBLE LWS_EXTERN void -lws_b64_decode_state_init(struct lws_b64state *state); - -LWS_VISIBLE LWS_EXTERN int -lws_b64_decode_stateful(struct lws_b64state *s, const char *in, size_t *in_len, - uint8_t *out, size_t *out_size, int final); -///@} - diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-spa.h libwebsockets-2.4.2/include/libwebsockets/lws-spa.h --- libwebsockets-4.0.20/include/libwebsockets/lws-spa.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-spa.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,176 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup form-parsing Form Parsing - * \ingroup http - * ##POSTed form parsing functions - * - * These lws_spa (stateful post arguments) apis let you parse and urldecode - * POSTed form arguments, both using simple urlencoded and multipart transfer - * encoding. - * - * It's capable of handling file uploads as well a named input parsing, - * and the apis are the same for both form upload styles. - * - * You feed it a list of parameter names and it creates pointers to the - * urldecoded arguments: file upload parameters pass the file data in chunks to - * a user-supplied callback as they come. - * - * Since it's stateful, it handles the incoming data needing more than one - * POST_BODY callback and has no limit on uploaded file size. - */ -///@{ - -/** enum lws_spa_fileupload_states */ -enum lws_spa_fileupload_states { - LWS_UFS_CONTENT, - /**< a chunk of file content has arrived */ - LWS_UFS_FINAL_CONTENT, - /**< the last chunk (possibly zero length) of file content has arrived */ - LWS_UFS_OPEN, - /**< a new file is starting to arrive */ - LWS_UFS_CLOSE - /**< the file decode stuff is being destroyed */ -}; - -/** - * lws_spa_fileupload_cb() - callback to receive file upload data - * - * \param data: opt_data pointer set in lws_spa_create - * \param name: name of the form field being uploaded - * \param filename: original filename from client - * \param buf: start of data to receive - * \param len: length of data to receive - * \param state: information about how this call relates to file - * - * Notice name and filename shouldn't be trusted, as they are passed from - * HTTP provided by the client. - */ -typedef int (*lws_spa_fileupload_cb)(void *data, const char *name, - const char *filename, char *buf, int len, - enum lws_spa_fileupload_states state); - -/** struct lws_spa - opaque urldecode parser capable of handling multipart - * and file uploads */ -struct lws_spa; - -/** - * lws_spa_create() - create urldecode parser - * - * \param wsi: lws connection (used to find Content Type) - * \param param_names: array of form parameter names, like "username" - * \param count_params: count of param_names - * \param max_storage: total amount of form parameter values we can store - * \param opt_cb: NULL, or callback to receive file upload data. - * \param opt_data: NULL, or user pointer provided to opt_cb. - * - * Creates a urldecode parser and initializes it. - * - * It's recommended to use the newer api, lws_spa_create_via_info() - * instead. - * - * opt_cb can be NULL if you just want normal name=value parsing, however - * if one or more entries in your form are bulk data (file transfer), you - * can provide this callback and filter on the name callback parameter to - * treat that urldecoded data separately. The callback should return -1 - * in case of fatal error, and 0 if OK. - */ -LWS_VISIBLE LWS_EXTERN 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); - -typedef struct lws_spa_create_info { - const char * const *param_names; /* array of form parameter names, like "username" */ - int count_params; /* count of param_names */ - int max_storage; /* total amount of form parameter values we can store */ - lws_spa_fileupload_cb opt_cb; /* NULL, or callback to receive file upload data. */ - void *opt_data; /* NULL, or user pointer provided to opt_cb. */ - size_t param_names_stride; /* 0 if param_names is an array of char *. - Else stride to next char * */ - struct lwsac **ac; /* NULL, or pointer to lwsac * to contain all - related heap allocations */ - size_t ac_chunk_size; /* 0 for default, or ac chunk size */ -} lws_spa_create_info_t; - -/** - * lws_spa_create_via_info() - create urldecode parser - * - * \param wsi: lws connection (used to find Content Type) - * \param info: pointer to struct defining the arguments - * - * Creates a urldecode parser and initializes it. - * - * opt_cb can be NULL if you just want normal name=value parsing, however - * if one or more entries in your form are bulk data (file transfer), you - * can provide this callback and filter on the name callback parameter to - * treat that urldecoded data separately. The callback should return -1 - * in case of fatal error, and 0 if OK. - */ -LWS_VISIBLE LWS_EXTERN struct lws_spa * -lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *info); - -/** - * lws_spa_process() - parses a chunk of input data - * - * \param spa: the parser object previously created - * \param in: incoming urlencoded data - * \param len: count of bytes valid at \p in - */ -LWS_VISIBLE LWS_EXTERN int -lws_spa_process(struct lws_spa *spa, const char *in, int len); - -/** - * lws_spa_finalize() - indicate incoming data completed - * - * \param spa: the parser object previously created - */ -LWS_VISIBLE LWS_EXTERN int -lws_spa_finalize(struct lws_spa *spa); - -/** - * lws_spa_get_length() - return length of parameter value - * - * \param spa: the parser object previously created - * \param n: parameter ordinal to return length of value for - */ -LWS_VISIBLE LWS_EXTERN int -lws_spa_get_length(struct lws_spa *spa, int n); - -/** - * lws_spa_get_string() - return pointer to parameter value - * \param spa: the parser object previously created - * \param n: parameter ordinal to return pointer to value for - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_spa_get_string(struct lws_spa *spa, int n); - -/** - * lws_spa_destroy() - destroy parser object - * - * \param spa: the parser object previously created - */ -LWS_VISIBLE LWS_EXTERN int -lws_spa_destroy(struct lws_spa *spa); -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-state.h libwebsockets-2.4.2/include/libwebsockets/lws-state.h --- libwebsockets-4.0.20/include/libwebsockets/lws-state.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-state.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -struct lws_state_notify_link; -struct lws_state_manager; - -typedef int (*lws_state_notify_t)(struct lws_state_manager *mgr, - struct lws_state_notify_link *link, - int current, int target); - -typedef struct lws_state_notify_link { - lws_dll2_t list; - lws_state_notify_t notify_cb; - const char *name; -} lws_state_notify_link_t; - -typedef struct lws_state_manager { - lws_dll2_owner_t notify_list; - void *parent; - /**< optional opaque pointer to owning object... useful to make such - * a pointer available to a notification callback. Ignored by lws */ - const char **state_names; /* may be NULL */ - const char *name; - int state; -} lws_state_manager_t; - -/** - * lws_state_reg_notifier() - add dep handler for state notifications - * - * \param context: the lws_context - * \param nl: the handler to add to the notifier linked-list - * - * Add \p notify_link to the context's list of notification handlers for system - * state changes. The handlers can defeat or take over responsibility for - * retrying the change after they have initiated some dependency. - */ - -LWS_EXTERN LWS_VISIBLE void -lws_state_reg_notifier(lws_state_manager_t *mgr, lws_state_notify_link_t *nl); - -/** - * lws_state_reg_deregister() - deregister a notifier - * - * \param nl: notification hardler to deregister - * - * Remove a notification handler from its state manager - */ - -LWS_EXTERN LWS_VISIBLE void -lws_state_reg_deregister(lws_state_notify_link_t *nl); - -/** - * lws_state_reg_notifier_list() - add dep handlers for state notifications - * - * \param context: the lws_context - * \param nl: list of notification handlers - * - * Add a NULL-terminated list of notification handler pointers to a notification - * manager object - */ - -LWS_EXTERN LWS_VISIBLE void -lws_state_reg_notifier_list(lws_state_manager_t *mgr, - lws_state_notify_link_t * const *nl); - -/** - * lws_state_transition_steps() - move to state via starting any deps - * - * \param mgr: the state manager object - * \param target: the state we wish to move to - * - * Advance state by state towards state \p target. At each state, notifiers - * may veto the change and be triggered to perform dependencies, stopping the - * advance towards the target state. - */ -LWS_EXTERN LWS_VISIBLE int -lws_state_transition_steps(lws_state_manager_t *mgr, int target); - -/** - * lws_state_transition() - move to state via starting any deps - * - * \param mgr: the state manager object - * \param target: the state we wish to move to - * - * Jump to state target atomically. Notifiers may veto it. - */ -LWS_EXTERN LWS_VISIBLE int -lws_state_transition(lws_state_manager_t *mgr, int target); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-stats.h libwebsockets-2.4.2/include/libwebsockets/lws-stats.h --- libwebsockets-4.0.20/include/libwebsockets/lws-stats.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-stats.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/* - * Stats are all uint64_t numbers that start at 0. - * Index names here have the convention - * - * _C_ counter - * _B_ byte count - * _MS_ millisecond count - */ - -enum { - LWSSTATS_C_CONNECTIONS, /**< count incoming connections */ - LWSSTATS_C_API_CLOSE, /**< count calls to close api */ - LWSSTATS_C_API_READ, /**< count calls to read from socket api */ - LWSSTATS_C_API_LWS_WRITE, /**< count calls to lws_write API */ - LWSSTATS_C_API_WRITE, /**< count calls to write API */ - LWSSTATS_C_WRITE_PARTIALS, /**< count of partial writes */ - LWSSTATS_C_WRITEABLE_CB_REQ, /**< count of writable callback requests */ - LWSSTATS_C_WRITEABLE_CB_EFF_REQ, /**< count of effective writable callback requests */ - LWSSTATS_C_WRITEABLE_CB, /**< count of writable callbacks */ - LWSSTATS_C_SSL_CONNECTIONS_FAILED, /**< count of failed SSL connections */ - LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, /**< count of accepted SSL connections */ - LWSSTATS_C_SSL_ACCEPT_SPIN, /**< count of SSL_accept() attempts */ - LWSSTATS_C_SSL_CONNS_HAD_RX, /**< count of accepted SSL conns that have had some RX */ - LWSSTATS_C_TIMEOUTS, /**< count of timed-out connections */ - LWSSTATS_C_SERVICE_ENTRY, /**< count of entries to lws service loop */ - LWSSTATS_B_READ, /**< aggregate bytes read */ - LWSSTATS_B_WRITE, /**< aggregate bytes written */ - LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, /**< aggreate of size of accepted write data from new partials */ - LWSSTATS_US_SSL_ACCEPT_LATENCY_AVG, /**< aggregate delay in accepting connection */ - LWSSTATS_US_WRITABLE_DELAY_AVG, /**< aggregate delay between asking for writable and getting cb */ - LWSSTATS_US_WORST_WRITABLE_DELAY, /**< single worst delay between asking for writable and getting cb */ - LWSSTATS_US_SSL_RX_DELAY_AVG, /**< aggregate delay between ssl accept complete and first RX */ - LWSSTATS_C_PEER_LIMIT_AH_DENIED, /**< number of times we would have given an ah but for the peer limit */ - LWSSTATS_C_PEER_LIMIT_WSI_DENIED, /**< number of times we would have given a wsi but for the peer limit */ - LWSSTATS_C_CONNS_CLIENT, /**< attempted client conns */ - LWSSTATS_C_CONNS_CLIENT_FAILED, /**< failed client conns */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility - * - * UPDATE stat_names in stats.c in sync with this! - */ - LWSSTATS_SIZE -}; - -#if defined(LWS_WITH_STATS) - -LWS_VISIBLE LWS_EXTERN uint64_t -lws_stats_get(struct lws_context *context, int index); -LWS_VISIBLE LWS_EXTERN void -lws_stats_log_dump(struct lws_context *context); -#else -static LWS_INLINE uint64_t -lws_stats_get(struct lws_context *context, int index) { (void)context; (void)index; return 0; } -static LWS_INLINE void -lws_stats_log_dump(struct lws_context *context) { (void)context; } -#endif diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-struct.h libwebsockets-2.4.2/include/libwebsockets/lws-struct.h --- libwebsockets-4.0.20/include/libwebsockets/lws-struct.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-struct.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,266 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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_STRUCT_SQLITE3) -#include -#endif - -typedef enum { - LSMT_SIGNED, - LSMT_UNSIGNED, - LSMT_BOOLEAN, - LSMT_STRING_CHAR_ARRAY, - LSMT_STRING_PTR, - LSMT_LIST, - LSMT_CHILD_PTR, - LSMT_SCHEMA, - -} lws_struct_map_type_eum; - -typedef struct lejp_collation { - struct lws_dll2 chunks; - int len; - char buf[LEJP_STRING_CHUNK + 1]; -} lejp_collation_t; - -typedef struct lws_struct_map { - const char *colname; - const struct lws_struct_map *child_map; - lejp_callback lejp_cb; - size_t ofs; /* child dll2; points to dll2_owner */ - size_t aux; - size_t ofs_clist; - size_t child_map_size; - lws_struct_map_type_eum type; -} lws_struct_map_t; - -typedef int (*lws_struct_args_cb)(void *obj, void *cb_arg); - -typedef struct lws_struct_args { - const lws_struct_map_t *map_st[LEJP_MAX_PARSING_STACK_DEPTH]; - lws_struct_args_cb cb; - struct lwsac *ac; - void *cb_arg; - void *dest; - - size_t dest_len; - size_t toplevel_dll2_ofs; - size_t map_entries_st[LEJP_MAX_PARSING_STACK_DEPTH]; - size_t ac_block_size; - int subtype; - - int top_schema_index; - - /* - * temp ac used to collate unknown possibly huge strings before final - * allocation and copy - */ - struct lwsac *ac_chunks; - struct lws_dll2_owner chunks_owner; - size_t chunks_length; -} lws_struct_args_t; - -#define LSM_SIGNED(type, name, qname) \ - { \ - qname, \ - NULL, \ - NULL, \ - offsetof(type, name), \ - sizeof ((type *)0)->name, \ - 0, \ - 0, \ - LSMT_SIGNED \ - } - -#define LSM_UNSIGNED(type, name, qname) \ - { \ - qname, \ - NULL, \ - NULL, \ - offsetof(type, name), \ - sizeof ((type *)0)->name, \ - 0, \ - 0, \ - LSMT_UNSIGNED \ - } - -#define LSM_BOOLEAN(type, name, qname) \ - { \ - qname, \ - NULL, \ - NULL, \ - offsetof(type, name), \ - sizeof ((type *)0)->name, \ - 0, \ - 0, \ - LSMT_BOOLEAN \ - } - -#define LSM_CARRAY(type, name, qname) \ - { \ - qname, \ - NULL, \ - NULL, \ - offsetof(type, name), \ - sizeof (((type *)0)->name), \ - 0, \ - 0, \ - LSMT_STRING_CHAR_ARRAY \ - } - -#define LSM_STRING_PTR(type, name, qname) \ - { \ - qname, \ - NULL, \ - NULL, \ - offsetof(type, name), \ - sizeof (((type *)0)->name), \ - 0, \ - 0, \ - LSMT_STRING_PTR \ - } - -#define LSM_LIST(ptype, pname, ctype, cname, lejp_cb, cmap, qname) \ - { \ - qname, \ - cmap, \ - lejp_cb, \ - offsetof(ptype, pname), \ - sizeof (ctype), \ - offsetof(ctype, cname), \ - LWS_ARRAY_SIZE(cmap), \ - LSMT_LIST \ - } - -#define LSM_CHILD_PTR(ptype, pname, ctype, lejp_cb, cmap, qname) \ - { \ - qname, \ - cmap, \ - lejp_cb, \ - offsetof(ptype, pname), \ - sizeof (ctype), \ - 0, \ - LWS_ARRAY_SIZE(cmap), \ - LSMT_CHILD_PTR \ - } - -#define LSM_SCHEMA(ctype, lejp_cb, map, schema_name) \ - { \ - schema_name, \ - map, \ - lejp_cb, \ - 0, \ - sizeof (ctype), \ - 0, \ - LWS_ARRAY_SIZE(map), \ - LSMT_SCHEMA \ - } - -#define LSM_SCHEMA_DLL2(ctype, cdll2mem, lejp_cb, map, schema_name) \ - { \ - schema_name, \ - map, \ - lejp_cb, \ - offsetof(ctype, cdll2mem), \ - sizeof (ctype), \ - 0, \ - LWS_ARRAY_SIZE(map), \ - LSMT_SCHEMA \ - } - -typedef struct lws_struct_serialize_st { - const struct lws_dll2 *dllpos; - const lws_struct_map_t *map; - const char *obj; - size_t map_entries; - size_t map_entry; - size_t size; - char subsequent; - char idt; -} lws_struct_serialize_st_t; - -enum { - LSSERJ_FLAG_PRETTY = 1 -}; - -typedef struct lws_struct_serialize { - lws_struct_serialize_st_t st[LEJP_MAX_PARSING_STACK_DEPTH]; - - size_t offset; - size_t remaining; - - int sp; - int flags; -} lws_struct_serialize_t; - -typedef enum { - LSJS_RESULT_CONTINUE, - LSJS_RESULT_FINISH, - LSJS_RESULT_ERROR -} lws_struct_json_serialize_result_t; - -LWS_VISIBLE LWS_EXTERN int -lws_struct_json_init_parse(struct lejp_ctx *ctx, lejp_callback cb, - void *user); - -LWS_VISIBLE LWS_EXTERN signed char -lws_struct_schema_only_lejp_cb(struct lejp_ctx *ctx, char reason); - -LWS_VISIBLE LWS_EXTERN signed char -lws_struct_default_lejp_cb(struct lejp_ctx *ctx, char reason); - -LWS_VISIBLE LWS_EXTERN lws_struct_serialize_t * -lws_struct_json_serialize_create(const lws_struct_map_t *map, - size_t map_entries, int flags, - const void *ptoplevel); - -LWS_VISIBLE LWS_EXTERN void -lws_struct_json_serialize_destroy(lws_struct_serialize_t **pjs); - -LWS_VISIBLE LWS_EXTERN lws_struct_json_serialize_result_t -lws_struct_json_serialize(lws_struct_serialize_t *js, uint8_t *buf, - size_t len, size_t *written); - -#if defined(LWS_WITH_STRUCT_SQLITE3) - -LWS_VISIBLE LWS_EXTERN int -lws_struct_sq3_serialize(sqlite3 *pdb, const lws_struct_map_t *schema, - lws_dll2_owner_t *owner, uint32_t manual_idx); - -LWS_VISIBLE LWS_EXTERN int -lws_struct_sq3_deserialize(sqlite3 *pdb, const char *filter, const char *order, - const lws_struct_map_t *schema, lws_dll2_owner_t *o, - struct lwsac **ac, int start, int limit); - -LWS_VISIBLE LWS_EXTERN int -lws_struct_sq3_create_table(sqlite3 *pdb, const lws_struct_map_t *schema); - -LWS_VISIBLE LWS_EXTERN int -lws_struct_sq3_open(struct lws_context *context, const char *sqlite3_path, - sqlite3 **pdb); - -LWS_VISIBLE LWS_EXTERN int -lws_struct_sq3_close(sqlite3 **pdb); - -#endif diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-system.h libwebsockets-2.4.2/include/libwebsockets/lws-system.h --- libwebsockets-4.0.20/include/libwebsockets/lws-system.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-system.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,259 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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 provides a clean way to interface lws user code to be able to - * work unchanged on different systems for fetching common system information, - * and performing common system operations like reboot. - */ - -/* - * Types of system blob that can be set and retreived - */ - -typedef enum { - LWS_SYSBLOB_TYPE_AUTH, - LWS_SYSBLOB_TYPE_CLIENT_CERT_DER = LWS_SYSBLOB_TYPE_AUTH + 2, - LWS_SYSBLOB_TYPE_CLIENT_KEY_DER, - LWS_SYSBLOB_TYPE_DEVICE_SERIAL, - LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION, - LWS_SYSBLOB_TYPE_DEVICE_TYPE, - LWS_SYSBLOB_TYPE_NTP_SERVER, - - LWS_SYSBLOB_TYPE_COUNT /* ... always last */ -} lws_system_blob_item_t; - -/* opaque generic blob whose content may be on-the-heap or pointed-to - * directly case by case. When it's on the heap, it can be produced by - * appending (it's a buflist underneath). Either way, it can be consumed by - * copying out a given length from a given offset. - */ - -typedef struct lws_system_blob lws_system_blob_t; - -LWS_EXTERN LWS_VISIBLE void -lws_system_blob_direct_set(lws_system_blob_t *b, const uint8_t *ptr, size_t len); - -LWS_EXTERN LWS_VISIBLE void -lws_system_blob_heap_empty(lws_system_blob_t *b); - -LWS_EXTERN LWS_VISIBLE int -lws_system_blob_heap_append(lws_system_blob_t *b, const uint8_t *ptr, size_t len); - -LWS_EXTERN LWS_VISIBLE size_t -lws_system_blob_get_size(lws_system_blob_t *b); - -/* return 0 and sets *ptr to point to blob data if possible, nonzero = fail */ -LWS_EXTERN LWS_VISIBLE int -lws_system_blob_get_single_ptr(lws_system_blob_t *b, const uint8_t **ptr); - -LWS_EXTERN LWS_VISIBLE int -lws_system_blob_get(lws_system_blob_t *b, uint8_t *ptr, size_t *len, size_t ofs); - -LWS_EXTERN LWS_VISIBLE void -lws_system_blob_destroy(lws_system_blob_t *b); - -/* - * Get the opaque blob for index idx of various system blobs. Returns 0 if - * *b was set otherwise nonzero means out of range - */ - -LWS_EXTERN LWS_VISIBLE lws_system_blob_t * -lws_system_get_blob(struct lws_context *context, lws_system_blob_item_t type, - int idx); - -/* - * Lws view of system state... normal operation from user code perspective is - * dependent on implicit (eg, knowing the date for cert validation) and - * explicit dependencies. - * - * Bit of lws and user code can register notification handlers that can enforce - * dependent operations before state transitions can complete. - */ - -typedef enum { /* keep system_state_names[] in sync in context.c */ - LWS_SYSTATE_UNKNOWN, - - LWS_SYSTATE_CONTEXT_CREATED, /* context was just created */ - LWS_SYSTATE_INITIALIZED, /* protocols initialized. Lws itself - * can operate normally */ - LWS_SYSTATE_IFACE_COLDPLUG, /* existing net ifaces iterated */ - LWS_SYSTATE_DHCP, /* at least one net iface configured */ - LWS_SYSTATE_TIME_VALID, /* ntpclient ran, or hw time valid... - * tls cannot work until we reach here - */ - LWS_SYSTATE_POLICY_VALID, /* user code knows how to operate... */ - LWS_SYSTATE_REGISTERED, /* device has an identity... */ - LWS_SYSTATE_AUTH1, /* identity used for main auth token */ - LWS_SYSTATE_AUTH2, /* identity used for optional auth */ - - LWS_SYSTATE_OPERATIONAL, /* user code can operate normally */ - - LWS_SYSTATE_POLICY_INVALID, /* user code is changing its policies - * drop everything done with old - * policy, switch to new then enter - * LWS_SYSTATE_POLICY_VALID */ -} lws_system_states_t; - - -typedef void (*lws_attach_cb_t)(struct lws_context *context, int tsi, void *opaque); -struct lws_attach_item; - -typedef struct lws_system_ops { - int (*reboot)(void); - int (*set_clock)(lws_usec_t us); - int (*attach)(struct lws_context *context, int tsi, lws_attach_cb_t cb, - lws_system_states_t state, void *opaque, - struct lws_attach_item **get); - /**< if \p get is NULL, add an attach callback request to the pt for - * \p cb with arg \p opaque, that should be called when we're at or past - * system state \p state. - * - * If \p get is non-NULL, look for the first listed item on the pt whose - * state situation is ready, and set *get to point to it. If no items, - * or none where the system state is right, set *get to NULL. - * - * It's done like this so (*attach) can perform system-specific - * locking outside of lws core, for both getting and adding items the - * same so it is thread-safe. A non-threadsafe helper - * __lws_system_attach() is provided to do the actual work inside the - * system-specific locking. - */ -} lws_system_ops_t; - -/** - * lws_system_get_state_manager() - return the state mgr object for system state - * - * \param context: the lws_context - * - * The returned pointer can be used with the lws_state_ apis - */ - -LWS_EXTERN LWS_VISIBLE lws_state_manager_t * -lws_system_get_state_manager(struct lws_context *context); - - - -/* wrappers handle NULL members or no ops struct set at all cleanly */ - -#define LWSSYSGAUTH_HEX (1 << 0) - -/** - * lws_system_get_ops() - get ahold of the system ops struct from the context - * - * \param context: the lws_context - * - * Returns the system ops struct. It may return NULL and if not, anything in - * there may be NULL. - */ -LWS_EXTERN LWS_VISIBLE const lws_system_ops_t * -lws_system_get_ops(struct lws_context *context); - -/** - * lws_system_context_from_system_mgr() - return context from system state mgr - * - * \param mgr: pointer to specifically the system state mgr - * - * Returns the context from the system state mgr. Helper since the lws_context - * is opaque. - */ -LWS_EXTERN LWS_VISIBLE struct lws_context * -lws_system_context_from_system_mgr(lws_state_manager_t *mgr); - - -/** - * __lws_system_attach() - get and set items on context attach list - * - * \param context: context to get or set attach items to - * \param tsi: thread service index (normally 0) - * \param cb: callback to call from context event loop thread - * \param state: the lws_system state we have to be in or have passed through - * \param opaque: optional pointer to user specific info given to callback - * \param get: NULL, or pointer to pointer to take detached tail item on exit - * - * This allows other threads to enqueue callback requests to happen from a pt's - * event loop thread safely. The callback gets the context pointer and a user - * opaque pointer that can be optionally given when the item is added to the - * attach list. - * - * This api is the no-locking core function for getting and setting items on the - * pt's attach list. The lws_system operation (*attach) is the actual - * api that user and internal code calls for this feature, it should perform - * system-specific locking, call this helper, release the locking and then - * return the result. This api is public only so it can be used in the locked - * implementation of (*attach). - * - * If get is NULL, then the call adds to the head of the pt attach list using - * cb, state, and opaque; if get is non-NULL, then *get is set to the first - * waiting attached item that meets the state criteria and that item is removed - * from the list. - * - * This is a non-threadsafe helper only designed to be called from - * implementations of struct lws_system's (*attach) operation where system- - * specific locking has been applied around it, making it threadsafe. - */ -LWS_EXTERN LWS_VISIBLE int -__lws_system_attach(struct lws_context *context, int tsi, lws_attach_cb_t cb, - lws_system_states_t state, void *opaque, - struct lws_attach_item **get); - - -typedef int (*dhcpc_cb_t)(void *opaque, int af, uint8_t *ip, int ip_len); - -/** - * lws_dhcpc_request() - add a network interface to dhcpc management - * - * \param c: the lws_context - * \param i: the interface name, like "eth0" - * \param af: address family - * \param cb: the change callback - * \param opaque: opaque pointer given to the callback - * - * Register a network interface as being managed by DHCP. lws will proceed to - * try to acquire an IP. Requires LWS_WITH_SYS_DHCP_CLIENT at cmake. - */ -int -lws_dhcpc_request(struct lws_context *c, const char *i, int af, dhcpc_cb_t cb, - void *opaque); - -/** - * lws_dhcpc_remove() - remove a network interface to dhcpc management - * - * \param context: the lws_context - * \param iface: the interface name, like "eth0" - * - * Remove handling of the network interface from dhcp. - */ -int -lws_dhcpc_remove(struct lws_context *context, const char *iface); - -/** - * lws_dhcpc_status() - has any interface reached BOUND state - * - * \param context: the lws_context - * \param sa46: set to a DNS server from a bound interface, or NULL - * - * Returns 1 if any network interface managed by dhcpc has reached the BOUND - * state (has acquired an IP, gateway and DNS server), otherwise 0. - */ -int -lws_dhcpc_status(struct lws_context *context, lws_sockaddr46 *sa46); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-test-sequencer.h libwebsockets-2.4.2/include/libwebsockets/lws-test-sequencer.h --- libwebsockets-4.0.20/include/libwebsockets/lws-test-sequencer.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-test-sequencer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws_test_sequencer manages running an array of unit tests. - */ - -typedef void (*lws_test_sequence_cb)(const void *cb_user); - -typedef struct lws_test_sequencer_args { - lws_abs_t *abs; /* abstract protocol + unit test txport */ - lws_unit_test_t *tests; /* array of lws_unit_test_t */ - int *results; /* takes result dispositions */ - int results_max; /* max space usable in results */ - int *count_tests; /* count of done tests */ - int *count_passes; /* count of passed tests */ - lws_test_sequence_cb cb; /* completion callback */ - void *cb_user; /* opaque user ptr given to cb */ -} lws_test_sequencer_args_t; - -/** - * lws_abs_unit_test_sequencer() - helper to sequence multiple unit tests - * - * \param args: lws_test_sequencer_args_t prepared with arguments for the tests - * - * This helper sequences one or more unit tests to run and collects the results. - * - * The incoming abs should be set up for the abstract protocol you want to test - * and the lws unit-test transport. - * - * Results are one of - * - * LPE_SUCCEEDED - * LPE_FAILED - * LPE_FAILED_UNEXPECTED_TIMEOUT - * LPE_FAILED_UNEXPECTED_PASS - * LPE_FAILED_UNEXPECTED_CLOSE - * - * The callback args->cb is called when the tests have been done. - */ -LWS_VISIBLE LWS_EXTERN int -lws_abs_unit_test_sequencer(const lws_test_sequencer_args_t *args); diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-threadpool.h libwebsockets-2.4.2/include/libwebsockets/lws-threadpool.h --- libwebsockets-4.0.20/include/libwebsockets/lws-threadpool.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-threadpool.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,232 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup threadpool Threadpool related functions - * ##Threadpool - * \ingroup lwsapi - * - * This allows you to create one or more pool of threads which can run tasks - * associated with a wsi. If the pool is busy, tasks wait on a queue. - * - * Tasks don't have to be atomic, if they will take more than a few tens of ms - * they should return back to the threadpool worker with a return of 0. This - * will allow them to abort cleanly. - */ -//@{ - -struct lws_threadpool; -struct lws_threadpool_task; - -enum lws_threadpool_task_status { - LWS_TP_STATUS_QUEUED, - LWS_TP_STATUS_RUNNING, - LWS_TP_STATUS_SYNCING, - LWS_TP_STATUS_STOPPING, - LWS_TP_STATUS_FINISHED, /* lws_threadpool_task_status() frees task */ - LWS_TP_STATUS_STOPPED, /* lws_threadpool_task_status() frees task */ -}; - -enum lws_threadpool_task_return { - /** Still work to do, just confirming not being stopped */ - LWS_TP_RETURN_CHECKING_IN, - /** Still work to do, enter cond_wait until service thread syncs. This - * is used if you have filled your buffer(s) of data to the service - * thread and are blocked until the service thread completes sending at - * least one. - */ - LWS_TP_RETURN_SYNC, - /** No more work to do... */ - LWS_TP_RETURN_FINISHED, - /** Responding to request to stop */ - LWS_TP_RETURN_STOPPED, - - /* OR on to indicate this task wishes to outlive its wsi */ - LWS_TP_RETURN_FLAG_OUTLIVE = 64 -}; - -struct lws_threadpool_create_args { - int threads; - int max_queue_depth; -}; - -struct lws_threadpool_task_args { - struct lws *wsi; /**< user must set to wsi task is bound to */ - void *user; /**< user may set (user-private pointer) */ - const char *name; /**< user may set to describe task */ - char async_task; /**< set to allow the task to shrug off the loss - of the associated wsi and continue to - completion */ - enum lws_threadpool_task_return (*task)(void *user, - enum lws_threadpool_task_status s); - /**< user must set to actual task function */ - void (*cleanup)(struct lws *wsi, void *user); - /**< socket lifecycle may end while task is not stoppable, so the task - * must be able to detach from any wsi and clean itself up when it does - * stop. If NULL, no cleanup necessary, otherwise point to a user- - * supplied function that destroys the stuff in \p user. - * - * wsi may be NULL on entry, indicating the task got detached due to the - * wsi closing before. - */ -}; - -/** - * lws_threadpool_create() - create a pool of worker threads - * - * \param context: the lws_context the threadpool will exist inside - * \param args: argument struct prepared by caller - * \param format: printf-type format for the task name - * \param ...: printf type args for the task name format - * - * Creates a pool of worker threads with \p threads and a queue of up to - * \p max_queue_depth waiting tasks if all the threads are busy. - * - * Returns NULL if OOM, or a struct lws_threadpool pointer that must be - * destroyed by lws_threadpool_destroy(). - */ -LWS_VISIBLE LWS_EXTERN struct lws_threadpool * -lws_threadpool_create(struct lws_context *context, - const struct lws_threadpool_create_args *args, - const char *format, ...) LWS_FORMAT(3); - -/** - * lws_threadpool_finish() - Stop all pending and running tasks - * - * \param tp: the threadpool object - * - * Marks the threadpool as under destruction. Removes everything from the - * pending queue and completes those tasks as LWS_TP_STATUS_STOPPED. - * - * Running tasks will also get LWS_TP_STATUS_STOPPED as soon as they - * "resurface". - * - * This doesn't reap tasks or free the threadpool, the reaping is done by the - * lws_threadpool_task_status() on the done task. - */ -LWS_VISIBLE LWS_EXTERN void -lws_threadpool_finish(struct lws_threadpool *tp); - -/** - * lws_threadpool_destroy() - Destroy a threadpool - * - * \param tp: the threadpool object - * - * Waits for all worker threads to stop, ends the threads and frees the tp. - */ -LWS_VISIBLE LWS_EXTERN void -lws_threadpool_destroy(struct lws_threadpool *tp); - -/** - * lws_threadpool_enqueue() - Queue the task and run it on a worker thread when possible - * - * \param tp: the threadpool to queue / run on - * \param args: information about what to run - * \param format: printf-type format for the task name - * \param ...: printf type args for the task name format - * - * This asks for a task to run ASAP on a worker thread in threadpool \p tp. - * - * The args defines the wsi, a user-private pointer, a timeout in secs and - * a pointer to the task function. - * - * Returns NULL or an opaque pointer to the queued (or running, or completed) - * task. - * - * Once a task is created and enqueued, it can only be destroyed by calling - * lws_threadpool_task_status() on it after it has reached the state - * LWS_TP_STATUS_FINISHED or LWS_TP_STATUS_STOPPED. - */ -LWS_VISIBLE LWS_EXTERN struct lws_threadpool_task * -lws_threadpool_enqueue(struct lws_threadpool *tp, - const struct lws_threadpool_task_args *args, - const char *format, ...) LWS_FORMAT(3); - -/** - * lws_threadpool_dequeue() - Dequeue or try to stop a running task - * - * \param wsi: the wsi whose current task we want to eliminate - * - * Returns 0 is the task was dequeued or already compeleted, or 1 if the task - * has been asked to stop asynchronously. - * - * This doesn't free the task. It only shortcuts it to state - * LWS_TP_STATUS_STOPPED. lws_threadpool_task_status() must be performed on - * the task separately once it is in LWS_TP_STATUS_STOPPED to free the task. - */ -LWS_VISIBLE LWS_EXTERN int -lws_threadpool_dequeue(struct lws *wsi); - -/** - * lws_threadpool_task_status() - Dequeue or try to stop a running task - * - * \param wsi: the wsi to query the current task of - * \param task: receives a pointer to the opaque task - * \param user: receives a void * pointer to the task user data - * - * This is the equivalent of posix waitpid()... it returns the status of the - * task, and if the task is in state LWS_TP_STATUS_FINISHED or - * LWS_TP_STATUS_STOPPED, frees \p task. If in another state, the task - * continues to exist. - * - * This is designed to be called from the service thread. - * - * Its use is to make sure the service thread has seen the state of the task - * before deleting it. - */ -LWS_VISIBLE LWS_EXTERN enum lws_threadpool_task_status -lws_threadpool_task_status_wsi(struct lws *wsi, - struct lws_threadpool_task **task, void **user); - -/** - * lws_threadpool_task_sync() - Indicate to a stalled task it may continue - * - * \param task: the task to unblock - * \param stop: 0 = run after unblock, 1 = when he unblocks, stop him - * - * Inform the task that the service thread has finished with the shared data - * and that the task, if blocked in LWS_TP_RETURN_SYNC, may continue. - * - * If the lws service context determined that the task must be aborted, it - * should still call this but with stop = 1, causing the task to finish. - */ -LWS_VISIBLE LWS_EXTERN void -lws_threadpool_task_sync(struct lws_threadpool_task *task, int stop); - -/** - * lws_threadpool_dump() - dump the state of a threadpool to the log - * - * \param tp: The threadpool to dump - * - * This locks the threadpool and then dumps the pending queue, the worker - * threads and the done queue, together with time information for how long - * the tasks have been in their current state, how long they have occupied a - * thread, etc. - * - * This only does anything on lws builds with CMAKE_BUILD_TYPE=DEBUG, otherwise - * while it still exists, it's a NOP. - */ - -LWS_VISIBLE LWS_EXTERN void -lws_threadpool_dump(struct lws_threadpool *tp); -//@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-timeout-timer.h libwebsockets-2.4.2/include/libwebsockets/lws-timeout-timer.h --- libwebsockets-4.0.20/include/libwebsockets/lws-timeout-timer.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-timeout-timer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,266 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup timeout Connection timeouts - - APIs related to setting connection timeouts -*/ -//@{ - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -enum pending_timeout { - NO_PENDING_TIMEOUT = 0, - PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE = 1, - PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE = 2, - PENDING_TIMEOUT_ESTABLISH_WITH_SERVER = 3, - PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE = 4, - PENDING_TIMEOUT_AWAITING_PING = 5, - PENDING_TIMEOUT_CLOSE_ACK = 6, - PENDING_TIMEOUT_UNUSED1 = 7, - PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE = 8, - PENDING_TIMEOUT_SSL_ACCEPT = 9, - PENDING_TIMEOUT_HTTP_CONTENT = 10, - PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND = 11, - PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE = 12, - PENDING_TIMEOUT_SHUTDOWN_FLUSH = 13, - PENDING_TIMEOUT_CGI = 14, - PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE = 15, - PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING = 16, - PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG = 17, - PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD = 18, - PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY = 19, - PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY = 20, - PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY = 21, - PENDING_TIMEOUT_KILLED_BY_SSL_INFO = 22, - PENDING_TIMEOUT_KILLED_BY_PARENT = 23, - PENDING_TIMEOUT_CLOSE_SEND = 24, - PENDING_TIMEOUT_HOLDING_AH = 25, - PENDING_TIMEOUT_UDP_IDLE = 26, - PENDING_TIMEOUT_CLIENT_CONN_IDLE = 27, - PENDING_TIMEOUT_LAGGING = 28, - PENDING_TIMEOUT_THREADPOOL = 29, - PENDING_TIMEOUT_THREADPOOL_TASK = 30, - PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE = 31, - PENDING_TIMEOUT_USER_OK = 32, - - /****** add new things just above ---^ ******/ - - PENDING_TIMEOUT_USER_REASON_BASE = 1000 -}; - -#define lws_time_in_microseconds lws_now_usecs - -#define LWS_TO_KILL_ASYNC -1 -/**< If LWS_TO_KILL_ASYNC is given as the timeout sec in a lws_set_timeout() - * call, then the connection is marked to be killed at the next timeout - * check. This is how you should force-close the wsi being serviced if - * you are doing it outside the callback (where you should close by nonzero - * return). - */ -#define LWS_TO_KILL_SYNC -2 -/**< If LWS_TO_KILL_SYNC is given as the timeout sec in a lws_set_timeout() - * call, then the connection is closed before returning (which may delete - * the wsi). This should only be used where the wsi being closed is not the - * wsi currently being serviced. - */ -/** - * lws_set_timeout() - marks the wsi as subject to a timeout some seconds hence - * - * \param wsi: Websocket connection instance - * \param reason: timeout reason - * \param secs: how many seconds. You may set to LWS_TO_KILL_ASYNC to - * force the connection to timeout at the next opportunity, or - * LWS_TO_KILL_SYNC to close it synchronously if you know the - * wsi is not the one currently being serviced. - */ -LWS_VISIBLE LWS_EXTERN void -lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs); - -/** - * lws_set_timeout_us() - marks the wsi as subject to a timeout some us hence - * - * \param wsi: Websocket connection instance - * \param reason: timeout reason - * \param us: 0 removes the timeout, otherwise number of us to wait - * - * Higher-resolution version of lws_set_timeout(). Actual resolution depends - * on platform and load, usually ms. - */ -void -lws_set_timeout_us(struct lws *wsi, enum pending_timeout reason, lws_usec_t us); - -#define LWS_SET_TIMER_USEC_CANCEL ((lws_usec_t)-1ll) -#define LWS_USEC_PER_SEC ((lws_usec_t)1000000) - -/** - * lws_set_timer_usecs() - schedules a callback on the wsi in the future - * - * \param wsi: Websocket connection instance - * \param usecs: LWS_SET_TIMER_USEC_CANCEL removes any existing scheduled - * callback, otherwise number of microseconds in the future - * the callback will occur at. - * - * NOTE: event loop support for this: - * - * default poll() loop: yes - * libuv event loop: yes - * libev: not implemented (patch welcome) - * libevent: not implemented (patch welcome) - * - * After the deadline expires, the wsi will get a callback of type - * LWS_CALLBACK_TIMER and the timer is exhausted. The deadline may be - * continuously deferred by further calls to lws_set_timer_usecs() with a later - * deadline, or cancelled by lws_set_timer_usecs(wsi, -1). - * - * If the timer should repeat, lws_set_timer_usecs() must be called again from - * LWS_CALLBACK_TIMER. - * - * Accuracy depends on the platform and the load on the event loop or system... - * all that's guaranteed is the callback will come after the requested wait - * period. - */ -LWS_VISIBLE LWS_EXTERN void -lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs); - -/* - * lws_timed_callback_vh_protocol() - calls back a protocol on a vhost after - * the specified delay in seconds - * - * \param vh: the vhost to call back - * \param protocol: the protocol to call back - * \param reason: callback reason - * \param secs: how many seconds in the future to do the callback. - * - * Callback the specified protocol with a fake wsi pointing to the specified - * vhost and protocol, with the specified reason, at the specified time in the - * future. - * - * Returns 0 if OK or 1 on OOM. - * - * In the multithreaded service case, the callback will occur in the same - * service thread context as the call to this api that requested it. If it is - * called from a non-service thread, tsi 0 will handle it. - */ -LWS_VISIBLE LWS_EXTERN int -lws_timed_callback_vh_protocol(struct lws_vhost *vh, - const struct lws_protocols *prot, - int reason, int secs); - -/* - * lws_timed_callback_vh_protocol_us() - calls back a protocol on a vhost after - * the specified delay in us - * - * \param vh: the vhost to call back - * \param protocol: the protocol to call back - * \param reason: callback reason - * \param us: how many us in the future to do the callback. - * - * Callback the specified protocol with a fake wsi pointing to the specified - * vhost and protocol, with the specified reason, at the specified time in the - * future. - * - * Returns 0 if OK or 1 on OOM. - * - * In the multithreaded service case, the callback will occur in the same - * service thread context as the call to this api that requested it. If it is - * called from a non-service thread, tsi 0 will handle it. - */ -LWS_VISIBLE LWS_EXTERN int -lws_timed_callback_vh_protocol_us(struct lws_vhost *vh, - const struct lws_protocols *prot, int reason, - lws_usec_t us); - -struct lws_sorted_usec_list; - -typedef void (*sul_cb_t)(struct lws_sorted_usec_list *sul); - -typedef struct lws_sorted_usec_list { - struct lws_dll2 list; /* simplify the code by keeping this at start */ - sul_cb_t cb; - lws_usec_t us; -} lws_sorted_usec_list_t; - - -/* - * lws_sul_schedule() - schedule a callback - * - * \param context: the lws_context - * \param tsi: the thread service index (usually 0) - * \param sul: pointer to the sul element - * \param cb: the scheduled callback - * \param us: the delay before the callback arrives, or - * LWS_SET_TIMER_USEC_CANCEL to cancel it. - * - * Generic callback-at-a-later time function. The callback happens on the - * event loop thread context. - * - * Although the api has us resultion, the actual resolution depends on the - * platform and is commonly 1ms. - * - * This doesn't allocate and doesn't fail. - * - * You can call it again with another us value to change the delay. - */ -LWS_VISIBLE LWS_EXTERN void -lws_sul_schedule(struct lws_context *context, int tsi, - lws_sorted_usec_list_t *sul, sul_cb_t cb, lws_usec_t us); - -/* - * lws_validity_confirmed() - reset the validity timer for a network connection - * - * \param wsi: the connection that saw traffic proving the connection valid - * - * Network connections are subject to intervals defined by the context, the - * vhost if server connections, or the client connect info if a client - * connection. If the connection goes longer than the specified time since - * last observing traffic that can only happen if traffic is passing in both - * directions, then lws will try to create a PING transaction on the network - * connection. - * - * If the connection reaches the specified `.secs_since_valid_hangup` time - * still without any proof of validity, the connection will be closed. - * - * If the PONG comes, or user code observes traffic that satisfies the proof - * that both directions are passing traffic to the peer and calls this api, - * the connection validity timer is reset and the scheme repeats. - */ -LWS_VISIBLE LWS_EXTERN void -lws_validity_confirmed(struct lws *wsi); - -/* - * These are not normally needed, they're exported for the case there's code - * using lws_sul for which lws is an optional link dependency. - */ - -LWS_VISIBLE LWS_EXTERN int -__lws_sul_insert(lws_dll2_owner_t *own, lws_sorted_usec_list_t *sul, - lws_usec_t us); - -LWS_VISIBLE LWS_EXTERN lws_usec_t -__lws_sul_service_ripe(lws_dll2_owner_t *own, lws_usec_t usnow); - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-tokenize.h libwebsockets-2.4.2/include/libwebsockets/lws-tokenize.h --- libwebsockets-4.0.20/include/libwebsockets/lws-tokenize.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-tokenize.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,243 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/* Do not treat - as a terminal character, so "my-token" is one token */ -#define LWS_TOKENIZE_F_MINUS_NONTERM (1 << 0) -/* Separately report aggregate colon-delimited tokens */ -#define LWS_TOKENIZE_F_AGG_COLON (1 << 1) -/* Enforce sequencing for a simple token , token , token ... list */ -#define LWS_TOKENIZE_F_COMMA_SEP_LIST (1 << 2) -/* Allow more characters in the tokens and less delimiters... default is - * only alphanumeric + underscore in tokens */ -#define LWS_TOKENIZE_F_RFC7230_DELIMS (1 << 3) -/* Do not treat . as a terminal character, so "warmcat.com" is one token */ -#define LWS_TOKENIZE_F_DOT_NONTERM (1 << 4) -/* If something starts looking like a float, like 1.2, force to be string token. - * This lets you receive dotted-quads like 192.168.0.1 as string tokens, and - * avoids illegal float format detection like 1.myserver.com */ -#define LWS_TOKENIZE_F_NO_FLOATS (1 << 5) -/* Instead of LWS_TOKZE_INTEGER, report integers as any other string token */ -#define LWS_TOKENIZE_F_NO_INTEGERS (1 << 6) -/* # makes the rest of the line a comment */ -#define LWS_TOKENIZE_F_HASH_COMMENT (1 << 7) -/* Do not treat / as a terminal character, so "multipart/related" is one token */ -#define LWS_TOKENIZE_F_SLASH_NONTERM (1 << 8) - -typedef enum { - - LWS_TOKZE_ERRS = 5, /* the number of errors defined */ - - LWS_TOKZE_ERR_BROKEN_UTF8 = -5, /* malformed or partial utf8 */ - LWS_TOKZE_ERR_UNTERM_STRING = -4, /* ended while we were in "" */ - LWS_TOKZE_ERR_MALFORMED_FLOAT = -3, /* like 0..1 or 0.1.1 */ - LWS_TOKZE_ERR_NUM_ON_LHS = -2, /* like 123= or 0.1= */ - LWS_TOKZE_ERR_COMMA_LIST = -1, /* like ",tok", or, "tok,," */ - - LWS_TOKZE_ENDED = 0, /* no more content */ - - /* Note: results have ordinal 1+, EOT is 0 and errors are < 0 */ - - LWS_TOKZE_DELIMITER, /* a delimiter appeared */ - LWS_TOKZE_TOKEN, /* a token appeared */ - LWS_TOKZE_INTEGER, /* an integer appeared */ - LWS_TOKZE_FLOAT, /* a float appeared */ - LWS_TOKZE_TOKEN_NAME_EQUALS, /* token [whitespace] = */ - LWS_TOKZE_TOKEN_NAME_COLON, /* token [whitespace] : (only with - LWS_TOKENIZE_F_AGG_COLON flag) */ - LWS_TOKZE_QUOTED_STRING, /* "*", where * may have any char */ - -} lws_tokenize_elem; - -/* - * helper enums to allow caller to enforce legal delimiter sequencing, eg - * disallow "token,,token", "token,", and ",token" - */ - -enum lws_tokenize_delimiter_tracking { - LWSTZ_DT_NEED_FIRST_CONTENT, - LWSTZ_DT_NEED_DELIM, - LWSTZ_DT_NEED_NEXT_CONTENT, -}; - -typedef struct lws_tokenize { - const char *start; /**< set to the start of the string to tokenize */ - const char *token; /**< the start of an identified token or delimiter */ - size_t len; /**< set to the length of the string to tokenize */ - size_t token_len; /**< the length of the identied token or delimiter */ - - uint16_t flags; /**< optional LWS_TOKENIZE_F_ flags, or 0 */ - uint8_t delim; - - int8_t e; /**< convenient for storing lws_tokenize return */ -} lws_tokenize_t; - -/** - * lws_tokenize() - breaks down a string into tokens and delimiters in-place - * - * \param ts: the lws_tokenize struct to init - * \param start: the string to tokenize - * \param flags: LWS_TOKENIZE_F_ option flags - * - * This initializes the tokenize struct to point to the given string, and - * sets the length to 2GiB - 1 (so there must be a terminating NUL)... you can - * override this requirement by setting ts.len yourself before using it. - * - * .delim is also initialized to LWSTZ_DT_NEED_FIRST_CONTENT. - */ - -LWS_VISIBLE LWS_EXTERN void -lws_tokenize_init(struct lws_tokenize *ts, const char *start, int flags); - -/** - * lws_tokenize() - breaks down a string into tokens and delimiters in-place - * - * \param ts: the lws_tokenize struct with information and state on what to do - * - * The \p ts struct should have its start, len and flags members initialized to - * reflect the string to be tokenized and any options. - * - * Then `lws_tokenize()` may be called repeatedly on the struct, returning one - * of `lws_tokenize_elem` each time, and with the struct's `token` and - * `token_len` members set to describe the content of the delimiter or token - * payload each time. - * - * There are no allocations during the process. - * - * returns lws_tokenize_elem that was identified (LWS_TOKZE_ENDED means reached - * the end of the string). - */ - -LWS_VISIBLE LWS_EXTERN lws_tokenize_elem -lws_tokenize(struct lws_tokenize *ts); - -/** - * lws_tokenize_cstr() - copy token string to NUL-terminated buffer - * - * \param ts: pointer to lws_tokenize struct to operate on - * \param str: destination buffer - * \pparam max: bytes in destination buffer - * - * returns 0 if OK or nonzero if the string + NUL won't fit. - */ - -LWS_VISIBLE LWS_EXTERN int -lws_tokenize_cstr(struct lws_tokenize *ts, char *str, size_t max); - - -/* - * lws_strexp: flexible string expansion helper api - * - * This stateful helper can handle multiple separate input chunks and multiple - * output buffer loads with arbitrary boundaries between literals and expanded - * symbols. This allows it to handle fragmented input as well as arbitrarily - * long symbol expansions that are bigger than the output buffer itself. - * - * A user callback is used to convert symbol names to the symbol value. - * - * A single byte buffer for input and another for output can process any - * length substitution then. The state object is around 64 bytes on a 64-bit - * system and it only uses 8 bytes stack. - */ - - -typedef int (*lws_strexp_expand_cb)(void *priv, const char *name, char *out, - size_t *pos, size_t olen, size_t *exp_ofs); - -typedef struct lws_strexp { - char name[32]; - lws_strexp_expand_cb cb; - void *priv; - char *out; - size_t olen; - size_t pos; - - size_t exp_ofs; - - uint8_t name_pos; - char state; -} lws_strexp_t; - -enum { - LSTRX_DONE, /* it completed OK */ - LSTRX_FILLED_OUT, /* out buf filled and needs resetting */ - LSTRX_FATAL_NAME_TOO_LONG = -1, /* fatal */ - LSTRX_FATAL_NAME_UNKNOWN = -2, -}; - - -/** - * lws_strexp_init() - initialize an lws_strexp_t for use - * - * \p exp: the exp object to init - * \p priv: the user's object pointer to pass to callback - * \p cb: the callback to expand named objects - * \p out: the start of the output buffer - * \p olen: the length of the output buffer in bytes - * - * Prepares an lws_strexp_t for use and sets the initial output buffer - */ -LWS_VISIBLE LWS_EXTERN void -lws_strexp_init(lws_strexp_t *exp, void *priv, lws_strexp_expand_cb cb, - char *out, size_t olen); - -/** - * lws_strexp_reset_out() - reset the output buffer on an existing strexp - * - * \p exp: the exp object to init - * \p out: the start of the output buffer - * \p olen: the length of the output buffer in bytes - * - * Provides a new output buffer for lws_strexp_expand() to continue to write - * into. It can be the same as the old one if it has been copied out or used. - * The position of the next write will be reset to the start of the given buf. - */ -LWS_VISIBLE LWS_EXTERN void -lws_strexp_reset_out(lws_strexp_t *exp, char *out, size_t olen); - -/** - * lws_strexp_expand() - copy / expand a string into the output buffer - * - * \p exp: the exp object for the copy / expansion - * \p in: the start of the next input data - * \p len: the length of the input data - * \p pused_in: pointer to write the amount of input used - * \p pused_out: pointer to write the amount of output used - * - * Copies in to the output buffer set in exp, expanding any ${name} tokens using - * the callback. \p *pused_in is set to the number of input chars used and - * \p *pused_out the number of output characters used - * - * May return LSTRX_FILLED_OUT early with *pused < len if the output buffer is - * filled. Handle the output buffer and reset it with lws_strexp_reset_out() - * before calling again with adjusted in / len to continue. - * - * In the case of large expansions, the expansion itself may fill the output - * buffer, in which case the expansion callback returns the LSTRX_FILLED_OUT - * and will be called again to continue with its *exp_ofs parameter set - * appropriately. - */ -LWS_VISIBLE LWS_EXTERN int -lws_strexp_expand(lws_strexp_t *exp, const char *in, size_t len, - size_t *pused_in, size_t *pused_out); - diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-vfs.h libwebsockets-2.4.2/include/libwebsockets/lws-vfs.h --- libwebsockets-4.0.20/include/libwebsockets/lws-vfs.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-vfs.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,273 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup fops file operation wrapping - * - * ##File operation wrapping - * - * Use these helper functions if you want to access a file from the perspective - * of a specific wsi, which is usually the case. If you just want contextless - * file access, use the fops callbacks directly with NULL wsi instead of these - * helpers. - * - * If so, then it calls the platform handler or user overrides where present - * (as defined in info->fops) - * - * The advantage from all this is user code can be portable for file operations - * without having to deal with differences between platforms. - */ -//@{ - -/** struct lws_plat_file_ops - Platform-specific file operations - * - * These provide platform-agnostic ways to deal with filesystem access in the - * library and in the user code. - */ - -#if defined(LWS_PLAT_FREERTOS) -/* sdk preprocessor defs? compiler issue? gets confused with member names */ -#define LWS_FOP_OPEN _open -#define LWS_FOP_CLOSE _close -#define LWS_FOP_SEEK_CUR _seek_cur -#define LWS_FOP_READ _read -#define LWS_FOP_WRITE _write -#else -#define LWS_FOP_OPEN open -#define LWS_FOP_CLOSE close -#define LWS_FOP_SEEK_CUR seek_cur -#define LWS_FOP_READ read -#define LWS_FOP_WRITE write -#endif - -#define LWS_FOP_FLAGS_MASK ((1 << 23) - 1) -#define LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP (1 << 24) -#define LWS_FOP_FLAG_COMPR_IS_GZIP (1 << 25) -#define LWS_FOP_FLAG_MOD_TIME_VALID (1 << 26) -#define LWS_FOP_FLAG_VIRTUAL (1 << 27) - -struct lws_plat_file_ops; - -struct lws_fop_fd { - lws_filefd_type fd; - /**< real file descriptor related to the file... */ - const struct lws_plat_file_ops *fops; - /**< fops that apply to this fop_fd */ - void *filesystem_priv; - /**< ignored by lws; owned by the fops handlers */ - lws_filepos_t pos; - /**< generic "position in file" */ - lws_filepos_t len; - /**< generic "length of file" */ - lws_fop_flags_t flags; - /**< copy of the returned flags */ - uint32_t mod_time; - /**< optional "modification time of file", only valid if .open() - * set the LWS_FOP_FLAG_MOD_TIME_VALID flag */ -}; -typedef struct lws_fop_fd *lws_fop_fd_t; - -struct lws_fops_index { - const char *sig; /* NULL or vfs signature, eg, ".zip/" */ - uint8_t len; /* length of above string */ -}; - -struct lws_plat_file_ops { - lws_fop_fd_t (*LWS_FOP_OPEN)(const struct lws_plat_file_ops *fops, - const char *filename, const char *vpath, - lws_fop_flags_t *flags); - /**< Open file (always binary access if plat supports it) - * vpath may be NULL, or if the fops understands it, the point at which - * the filename's virtual part starts. - * *flags & LWS_FOP_FLAGS_MASK should be set to O_RDONLY or O_RDWR. - * If the file may be gzip-compressed, - * LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP is set. If it actually is - * gzip-compressed, then the open handler should OR - * LWS_FOP_FLAG_COMPR_IS_GZIP on to *flags before returning. - */ - int (*LWS_FOP_CLOSE)(lws_fop_fd_t *fop_fd); - /**< close file AND set the pointer to NULL */ - lws_fileofs_t (*LWS_FOP_SEEK_CUR)(lws_fop_fd_t fop_fd, - lws_fileofs_t offset_from_cur_pos); - /**< seek from current position */ - int (*LWS_FOP_READ)(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len); - /**< Read from file, on exit *amount is set to amount actually read */ - int (*LWS_FOP_WRITE)(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len); - /**< Write to file, on exit *amount is set to amount actually written */ - - struct lws_fops_index fi[3]; - /**< vfs path signatures implying use of this fops */ - - const struct lws_plat_file_ops *next; - /**< NULL or next fops in list */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility */ -}; - -/** - * lws_get_fops() - get current file ops - * - * \param context: context - */ -LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops * LWS_WARN_UNUSED_RESULT -lws_get_fops(struct lws_context *context); -LWS_VISIBLE LWS_EXTERN void -lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops); -/** - * lws_vfs_tell() - get current file position - * - * \param fop_fd: fop_fd we are asking about - */ -LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT -lws_vfs_tell(lws_fop_fd_t fop_fd); -/** - * lws_vfs_get_length() - get current file total length in bytes - * - * \param fop_fd: fop_fd we are asking about - */ -LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT -lws_vfs_get_length(lws_fop_fd_t fop_fd); -/** - * lws_vfs_get_mod_time() - get time file last modified - * - * \param fop_fd: fop_fd we are asking about - */ -LWS_VISIBLE LWS_EXTERN uint32_t LWS_WARN_UNUSED_RESULT -lws_vfs_get_mod_time(lws_fop_fd_t fop_fd); -/** - * lws_vfs_file_seek_set() - seek relative to start of file - * - * \param fop_fd: fop_fd we are seeking in - * \param offset: offset from start of file - */ -LWS_VISIBLE LWS_EXTERN lws_fileofs_t -lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset); -/** - * lws_vfs_file_seek_end() - seek relative to end of file - * - * \param fop_fd: fop_fd we are seeking in - * \param offset: offset from start of file - */ -LWS_VISIBLE LWS_EXTERN lws_fileofs_t -lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset); - -extern struct lws_plat_file_ops fops_zip; - -/** - * lws_plat_file_open() - open vfs filepath - * - * \param fops: file ops struct that applies to this descriptor - * \param vfs_path: filename to open - * \param flags: pointer to open flags - * - * The vfs_path is scanned for known fops signatures, and the open directed - * to any matching fops open. - * - * User code should use this api to perform vfs opens. - * - * returns semi-opaque handle - */ -LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT -lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path, - lws_fop_flags_t *flags); - -/** - * lws_plat_file_close() - close file - * - * \param fop_fd: file handle to close - */ -static LWS_INLINE int -lws_vfs_file_close(lws_fop_fd_t *fop_fd) -{ - if (*fop_fd && (*fop_fd)->fops) - return (*fop_fd)->fops->LWS_FOP_CLOSE(fop_fd); - - return 0; -} - -/** - * lws_plat_file_seek_cur() - close file - * - * - * \param fop_fd: file handle - * \param offset: position to seek to - */ -static LWS_INLINE lws_fileofs_t -lws_vfs_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) -{ - return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset); -} -/** - * lws_plat_file_read() - read from file - * - * \param fop_fd: file handle - * \param amount: how much to read (rewritten by call) - * \param buf: buffer to write to - * \param len: max length - */ -static LWS_INLINE int LWS_WARN_UNUSED_RESULT -lws_vfs_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len) -{ - return fop_fd->fops->LWS_FOP_READ(fop_fd, amount, buf, len); -} -/** - * lws_plat_file_write() - write from file - * - * \param fop_fd: file handle - * \param amount: how much to write (rewritten by call) - * \param buf: buffer to read from - * \param len: max length - */ -static LWS_INLINE int LWS_WARN_UNUSED_RESULT -lws_vfs_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len) -{ - return fop_fd->fops->LWS_FOP_WRITE(fop_fd, amount, buf, len); -} - -/* these are the platform file operations implementations... they can - * be called directly and used in fops arrays - */ - -LWS_VISIBLE LWS_EXTERN lws_fop_fd_t -_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, - const char *vpath, lws_fop_flags_t *flags); -LWS_VISIBLE LWS_EXTERN int -_lws_plat_file_close(lws_fop_fd_t *fop_fd); -LWS_VISIBLE LWS_EXTERN lws_fileofs_t -_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset); -LWS_VISIBLE LWS_EXTERN int -_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len); -LWS_VISIBLE LWS_EXTERN int -_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len); - -LWS_VISIBLE LWS_EXTERN int -lws_alloc_vfs_file(struct lws_context *context, const char *filename, - uint8_t **buf, lws_filepos_t *amount); -//@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-writeable.h libwebsockets-2.4.2/include/libwebsockets/lws-writeable.h --- libwebsockets-4.0.20/include/libwebsockets/lws-writeable.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-writeable.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,246 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup callback-when-writeable Callback when writeable - * - * ##Callback When Writeable - * - * lws can only write data on a connection when it is able to accept more - * data without blocking. - * - * So a basic requirement is we should only use the lws_write() apis when the - * connection we want to write on says that he can accept more data. - * - * When lws cannot complete your send at the time, it will buffer the data - * and send it in the background, suppressing any further WRITEABLE callbacks - * on that connection until it completes. So it is important to write new - * things in a new writeable callback. - * - * These apis reflect the various ways we can indicate we would like to be - * called back when one or more connections is writeable. - */ -///@{ - -/** - * lws_callback_on_writable() - Request a callback when this socket - * becomes able to be written to without - * blocking - * - * \param wsi: Websocket connection instance to get callback for - * - * - Which: only this wsi - * - When: when the individual connection becomes writeable - * - What: LWS_CALLBACK_*_WRITEABLE - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_on_writable(struct lws *wsi); - -/** - * lws_callback_on_writable_all_protocol() - Request a callback for all - * connections using the given protocol when it - * becomes possible to write to each socket without - * blocking in turn. - * - * \param context: lws_context - * \param protocol: Protocol whose connections will get callbacks - * - * - Which: connections using this protocol on ANY VHOST - * - When: when the individual connection becomes writeable - * - What: LWS_CALLBACK_*_WRITEABLE - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_on_writable_all_protocol(const struct lws_context *context, - const struct lws_protocols *protocol); - -/** - * lws_callback_on_writable_all_protocol_vhost() - Request a callback for - * all connections on same vhost using the given protocol - * when it becomes possible to write to each socket without - * blocking in turn. - * - * \param vhost: Only consider connections on this lws_vhost - * \param protocol: Protocol whose connections will get callbacks - * - * - Which: connections using this protocol on GIVEN VHOST ONLY - * - When: when the individual connection becomes writeable - * - What: LWS_CALLBACK_*_WRITEABLE - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost, - const struct lws_protocols *protocol); - -/** - * lws_callback_all_protocol() - Callback all connections using - * the given protocol with the given reason - * - * \param context: lws_context - * \param protocol: Protocol whose connections will get callbacks - * \param reason: Callback reason index - * - * - Which: connections using this protocol on ALL VHOSTS - * - When: before returning - * - What: reason - * - * This isn't normally what you want... normally any update of connection- - * specific information can wait until a network-related callback like rx, - * writable, or close. - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_all_protocol(struct lws_context *context, - const struct lws_protocols *protocol, int reason); - -/** - * lws_callback_all_protocol_vhost() - Callback all connections using - * the given protocol with the given reason. This is - * deprecated since v2.4: use lws_callback_all_protocol_vhost_args - * - * \param vh: Vhost whose connections will get callbacks - * \param protocol: Which protocol to match. NULL means all. - * \param reason: Callback reason index - * - * - Which: connections using this protocol on GIVEN VHOST ONLY - * - When: now - * - What: reason - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_all_protocol_vhost(struct lws_vhost *vh, - const struct lws_protocols *protocol, - int reason) -LWS_WARN_DEPRECATED; - -/** - * lws_callback_all_protocol_vhost_args() - Callback all connections using - * the given protocol with the given reason and args - * - * \param vh: Vhost whose connections will get callbacks - * \param protocol: Which protocol to match. NULL means all. - * \param reason: Callback reason index - * \param argp: Callback "in" parameter - * \param len: Callback "len" parameter - * - * - Which: connections using this protocol on GIVEN VHOST ONLY - * - When: now - * - What: reason - */ -LWS_VISIBLE int -lws_callback_all_protocol_vhost_args(struct lws_vhost *vh, - const struct lws_protocols *protocol, - int reason, void *argp, size_t len); - -/** - * lws_callback_vhost_protocols() - Callback all protocols enabled on a vhost - * with the given reason - * - * \param wsi: wsi whose vhost will get callbacks - * \param reason: Callback reason index - * \param in: in argument to callback - * \param len: len argument to callback - * - * - Which: connections using this protocol on same VHOST as wsi ONLY - * - When: now - * - What: reason - * - * This is deprecated since v2.5, use lws_callback_vhost_protocols_vhost() - * which takes the pointer to the vhost directly without using or needing the - * wsi. - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len) -LWS_WARN_DEPRECATED; - -/** - * lws_callback_vhost_protocols_vhost() - Callback all protocols enabled on a vhost - * with the given reason - * - * \param vh: vhost that will get callbacks - * \param reason: Callback reason index - * \param in: in argument to callback - * \param len: len argument to callback - * - * - Which: connections using this protocol on same VHOST as wsi ONLY - * - When: now - * - What: reason - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_vhost_protocols_vhost(struct lws_vhost *vh, int reason, void *in, - size_t len); - -LWS_VISIBLE LWS_EXTERN int -lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len); - -/** - * lws_get_socket_fd() - returns the socket file descriptor - * - * This is needed to use sendto() on UDP raw sockets - * - * \param wsi: Websocket connection instance - */ -LWS_VISIBLE LWS_EXTERN lws_sockfd_type -lws_get_socket_fd(struct lws *wsi); - -/** - * lws_get_peer_write_allowance() - get the amount of data writeable to peer - * if known - * - * \param wsi: Websocket connection instance - * - * if the protocol does not have any guidance, returns -1. Currently only - * http2 connections get send window information from this API. But your code - * should use it so it can work properly with any protocol. - * - * If nonzero return is the amount of payload data the peer or intermediary has - * reported it has buffer space for. That has NO relationship with the amount - * of buffer space your OS can accept on this connection for a write action. - * - * This number represents the maximum you could send to the peer or intermediary - * on this connection right now without the protocol complaining. - * - * lws manages accounting for send window updates and payload writes - * automatically, so this number reflects the situation at the peer or - * intermediary dynamically. - */ -LWS_VISIBLE LWS_EXTERN lws_fileofs_t -lws_get_peer_write_allowance(struct lws *wsi); - -/** - * lws_wsi_tx_credit() - get / set generic tx credit if role supports it - * - * \param wsi: connection to set / get tx credit on - * \param peer_to_us: 0 = set / get us-to-peer direction, else peer-to-us - * \param add: amount of credit to add - * - * If the wsi does not support tx credit, returns 0. - * - * If add is zero, returns one of the wsi tx credit values for the wsi. - * If add is nonzero, \p add is added to the selected tx credit value - * for the wsi. - */ -#define LWSTXCR_US_TO_PEER 0 -#define LWSTXCR_PEER_TO_US 1 - -LWS_VISIBLE LWS_EXTERN int -lws_wsi_tx_credit(struct lws *wsi, char peer_to_us, int add); - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-write.h libwebsockets-2.4.2/include/libwebsockets/lws-write.h --- libwebsockets-4.0.20/include/libwebsockets/lws-write.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-write.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,264 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup sending-data Sending data - - APIs related to writing data on a connection -*/ -//@{ -#if !defined(LWS_SIZEOFPTR) -#define LWS_SIZEOFPTR ((int)sizeof (void *)) -#endif - -#if defined(__x86_64__) -#define _LWS_PAD_SIZE 16 /* Intel recommended for best performance */ -#else -#define _LWS_PAD_SIZE LWS_SIZEOFPTR /* Size of a pointer on the target arch */ -#endif -#define _LWS_PAD(n) (((n) % _LWS_PAD_SIZE) ? \ - ((n) + (_LWS_PAD_SIZE - ((n) % _LWS_PAD_SIZE))) : (n)) -/* last 2 is for lws-meta */ -#define LWS_PRE _LWS_PAD(4 + 10 + 2) -/* used prior to 1.7 and retained for backward compatibility */ -#define LWS_SEND_BUFFER_PRE_PADDING LWS_PRE -#define LWS_SEND_BUFFER_POST_PADDING 0 - -#define LWS_WRITE_RAW LWS_WRITE_HTTP - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -enum lws_write_protocol { - LWS_WRITE_TEXT = 0, - /**< Send a ws TEXT message,the pointer must have LWS_PRE valid - * memory behind it. - * - * The receiver expects only valid utf-8 in the payload */ - LWS_WRITE_BINARY = 1, - /**< Send a ws BINARY message, the pointer must have LWS_PRE valid - * memory behind it. - * - * Any sequence of bytes is valid */ - LWS_WRITE_CONTINUATION = 2, - /**< Continue a previous ws message, the pointer must have LWS_PRE valid - * memory behind it */ - LWS_WRITE_HTTP = 3, - /**< Send HTTP content */ - - /* LWS_WRITE_CLOSE is handled by lws_close_reason() */ - LWS_WRITE_PING = 5, - LWS_WRITE_PONG = 6, - - /* Same as write_http but we know this write ends the transaction */ - LWS_WRITE_HTTP_FINAL = 7, - - /* HTTP2 */ - - LWS_WRITE_HTTP_HEADERS = 8, - /**< Send http headers (http2 encodes this payload and LWS_WRITE_HTTP - * payload differently, http 1.x links also handle this correctly. so - * to be compatible with both in the future,header response part should - * be sent using this regardless of http version expected) - */ - LWS_WRITE_HTTP_HEADERS_CONTINUATION = 9, - /**< Continuation of http/2 headers - */ - - /****** add new things just above ---^ ******/ - - /* flags */ - - LWS_WRITE_BUFLIST = 0x20, - /**< Don't actually write it... stick it on the output buflist and - * write it as soon as possible. Useful if you learn you have to - * write something, have the data to write to hand but the timing is - * unrelated as to whether the connection is writable or not, and were - * otherwise going to have to allocate a temp buffer and write it - * later anyway */ - - LWS_WRITE_NO_FIN = 0x40, - /**< This part of the message is not the end of the message */ - - LWS_WRITE_H2_STREAM_END = 0x80, - /**< Flag indicates this packet should go out with STREAM_END if h2 - * STREAM_END is allowed on DATA or HEADERS. - */ - - LWS_WRITE_CLIENT_IGNORE_XOR_MASK = 0x80 - /**< client packet payload goes out on wire unmunged - * only useful for security tests since normal servers cannot - * decode the content if used */ -}; - -/* used with LWS_CALLBACK_CHILD_WRITE_VIA_PARENT */ - -struct lws_write_passthru { - struct lws *wsi; - unsigned char *buf; - size_t len; - enum lws_write_protocol wp; -}; - - -/** - * lws_write() - Apply protocol then write data to client - * - * \param wsi: Websocket instance (available from user callback) - * \param buf: The data to send. For data being sent on a websocket - * connection (ie, not default http), this buffer MUST have - * LWS_PRE bytes valid BEFORE the pointer. - * This is so the protocol header data can be added in-situ. - * \param len: Count of the data bytes in the payload starting from buf - * \param protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one - * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate - * data on a websockets connection. Remember to allow the extra - * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT - * are used. - * - * This function provides the way to issue data back to the client - * for both http and websocket protocols. - * - * IMPORTANT NOTICE! - * - * When sending with websocket protocol - * - * LWS_WRITE_TEXT, - * LWS_WRITE_BINARY, - * LWS_WRITE_CONTINUATION, - * LWS_WRITE_PING, - * LWS_WRITE_PONG, - * - * or sending on http/2, - * - * the send buffer has to have LWS_PRE bytes valid BEFORE the buffer pointer you - * pass to lws_write(). Since you'll probably want to use http/2 before too - * long, it's wise to just always do this with lws_write buffers... LWS_PRE is - * typically 16 bytes it's not going to hurt usually. - * - * start of alloc ptr passed to lws_write end of allocation - * | | | - * v <-- LWS_PRE bytes --> v v - * [---------------- allocated memory ---------------] - * (for lws use) [====== user buffer ======] - * - * This allows us to add protocol info before and after the data, and send as - * one packet on the network without payload copying, for maximum efficiency. - * - * So for example you need this kind of code to use lws_write with a - * 128-byte payload - * - * char buf[LWS_PRE + 128]; - * - * // fill your part of the buffer... for example here it's all zeros - * memset(&buf[LWS_PRE], 0, 128); - * - * lws_write(wsi, &buf[LWS_PRE], 128, LWS_WRITE_TEXT); - * - * LWS_PRE is at least the frame nonce + 2 header + 8 length - * LWS_SEND_BUFFER_POST_PADDING is deprecated, it's now 0 and can be left off. - * The example apps no longer use it. - * - * Pad LWS_PRE to the CPU word size, so that word references - * to the address immediately after the padding won't cause an unaligned access - * error. Sometimes for performance reasons the recommended padding is even - * larger than sizeof(void *). - * - * In the case of sending using websocket protocol, be sure to allocate - * valid storage before and after buf as explained above. This scheme - * allows maximum efficiency of sending data and protocol in a single - * packet while not burdening the user code with any protocol knowledge. - * - * Return may be -1 for a fatal error needing connection close, or the - * number of bytes sent. - * - * Truncated Writes - * ================ - * - * The OS may not accept everything you asked to write on the connection. - * - * Posix defines POLLOUT indication from poll() to show that the connection - * will accept more write data, but it doesn't specifiy how much. It may just - * accept one byte of whatever you wanted to send. - * - * LWS will buffer the remainder automatically, and send it out autonomously. - * - * During that time, WRITABLE callbacks will be suppressed. - * - * This is to handle corner cases where unexpectedly the OS refuses what we - * usually expect it to accept. You should try to send in chunks that are - * almost always accepted in order to avoid the inefficiency of the buffering. - */ -LWS_VISIBLE LWS_EXTERN int -lws_write(struct lws *wsi, unsigned char *buf, size_t len, - enum lws_write_protocol protocol); - -/* helper for case where buffer may be const */ -#define lws_write_http(wsi, buf, len) \ - lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP) - -/** - * lws_write_ws_flags() - Helper for multi-frame ws message flags - * - * \param initial: the lws_write flag to use for the start fragment, eg, - * LWS_WRITE_TEXT - * \param is_start: nonzero if this is the first fragment of the message - * \param is_end: nonzero if this is the last fragment of the message - * - * Returns the correct LWS_WRITE_ flag to use for each fragment of a message - * in turn. - */ -static LWS_INLINE int -lws_write_ws_flags(int initial, int is_start, int is_end) -{ - int r; - - if (is_start) - r = initial; - else - r = LWS_WRITE_CONTINUATION; - - if (!is_end) - r |= LWS_WRITE_NO_FIN; - - return r; -} - -/** - * lws_raw_transaction_completed() - Helper for flushing before close - * - * \param wsi: the struct lws to operate on - * - * Returns -1 if the wsi can close now. However if there is buffered, unsent - * data, the wsi is marked as to be closed when the output buffer data is - * drained, and it returns 0. - * - * For raw cases where the transaction completed without failure, - * `return lws_raw_transaction_completed(wsi)` should better be used than - * return -1. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_raw_transaction_completed(struct lws *wsi); - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-ws-close.h libwebsockets-2.4.2/include/libwebsockets/lws-ws-close.h --- libwebsockets-4.0.20/include/libwebsockets/lws-ws-close.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-ws-close.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,125 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup wsclose Websocket Close - * - * ##Websocket close frame control - * - * When we close a ws connection, we can send a reason code and a short - * UTF-8 description back with the close packet. - */ -///@{ - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -/** enum lws_close_status - RFC6455 close status codes */ -enum lws_close_status { - LWS_CLOSE_STATUS_NOSTATUS = 0, - LWS_CLOSE_STATUS_NORMAL = 1000, - /**< 1000 indicates a normal closure, meaning that the purpose for - which the connection was established has been fulfilled. */ - LWS_CLOSE_STATUS_GOINGAWAY = 1001, - /**< 1001 indicates that an endpoint is "going away", such as a server - going down or a browser having navigated away from a page. */ - LWS_CLOSE_STATUS_PROTOCOL_ERR = 1002, - /**< 1002 indicates that an endpoint is terminating the connection due - to a protocol error. */ - LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE = 1003, - /**< 1003 indicates that an endpoint is terminating the connection - because it has received a type of data it cannot accept (e.g., an - endpoint that understands only text data MAY send this if it - receives a binary message). */ - LWS_CLOSE_STATUS_RESERVED = 1004, - /**< Reserved. The specific meaning might be defined in the future. */ - LWS_CLOSE_STATUS_NO_STATUS = 1005, - /**< 1005 is a reserved value and MUST NOT be set as a status code in a - Close control frame by an endpoint. It is designated for use in - applications expecting a status code to indicate that no status - code was actually present. */ - LWS_CLOSE_STATUS_ABNORMAL_CLOSE = 1006, - /**< 1006 is a reserved value and MUST NOT be set as a status code in a - Close control frame by an endpoint. It is designated for use in - applications expecting a status code to indicate that the - connection was closed abnormally, e.g., without sending or - receiving a Close control frame. */ - LWS_CLOSE_STATUS_INVALID_PAYLOAD = 1007, - /**< 1007 indicates that an endpoint is terminating the connection - because it has received data within a message that was not - consistent with the type of the message (e.g., non-UTF-8 [RFC3629] - data within a text message). */ - LWS_CLOSE_STATUS_POLICY_VIOLATION = 1008, - /**< 1008 indicates that an endpoint is terminating the connection - because it has received a message that violates its policy. This - is a generic status code that can be returned when there is no - other more suitable status code (e.g., 1003 or 1009) or if there - is a need to hide specific details about the policy. */ - LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE = 1009, - /**< 1009 indicates that an endpoint is terminating the connection - because it has received a message that is too big for it to - process. */ - LWS_CLOSE_STATUS_EXTENSION_REQUIRED = 1010, - /**< 1010 indicates that an endpoint (client) is terminating the - connection because it has expected the server to negotiate one or - more extension, but the server didn't return them in the response - message of the WebSocket handshake. The list of extensions that - are needed SHOULD appear in the /reason/ part of the Close frame. - Note that this status code is not used by the server, because it - can fail the WebSocket handshake instead */ - LWS_CLOSE_STATUS_UNEXPECTED_CONDITION = 1011, - /**< 1011 indicates that a server is terminating the connection because - it encountered an unexpected condition that prevented it from - fulfilling the request. */ - LWS_CLOSE_STATUS_TLS_FAILURE = 1015, - /**< 1015 is a reserved value and MUST NOT be set as a status code in a - Close control frame by an endpoint. It is designated for use in - applications expecting a status code to indicate that the - connection was closed due to a failure to perform a TLS handshake - (e.g., the server certificate can't be verified). */ - - LWS_CLOSE_STATUS_CLIENT_TRANSACTION_DONE = 2000, - - /****** add new things just above ---^ ******/ - - LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY = 9999, -}; - -/** - * lws_close_reason - Set reason and aux data to send with Close packet - * If you are going to return nonzero from the callback - * requesting the connection to close, you can optionally - * call this to set the reason the peer will be told if - * possible. - * - * \param wsi: The websocket connection to set the close reason on - * \param status: A valid close status from websocket standard - * \param buf: NULL or buffer containing up to 124 bytes of auxiliary data - * \param len: Length of data in \p buf to send - */ -LWS_VISIBLE LWS_EXTERN void -lws_close_reason(struct lws *wsi, enum lws_close_status status, - unsigned char *buf, size_t len); - -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-ws-ext.h libwebsockets-2.4.2/include/libwebsockets/lws-ws-ext.h --- libwebsockets-4.0.20/include/libwebsockets/lws-ws-ext.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-ws-ext.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,198 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/*! \defgroup extensions Extension related functions - * ##Extension releated functions - * - * Ws defines optional extensions, lws provides the ability to implement these - * in user code if so desired. - * - * We provide one extensions permessage-deflate. - */ -///@{ - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -enum lws_extension_callback_reasons { - LWS_EXT_CB_CONSTRUCT = 4, - LWS_EXT_CB_CLIENT_CONSTRUCT = 5, - LWS_EXT_CB_DESTROY = 8, - LWS_EXT_CB_PACKET_TX_PRESEND = 12, - LWS_EXT_CB_PAYLOAD_TX = 21, - LWS_EXT_CB_PAYLOAD_RX = 22, - LWS_EXT_CB_OPTION_DEFAULT = 23, - LWS_EXT_CB_OPTION_SET = 24, - LWS_EXT_CB_OPTION_CONFIRM = 25, - LWS_EXT_CB_NAMED_OPTION_SET = 26, - - /****** add new things just above ---^ ******/ -}; - -/** enum lws_ext_options_types */ -enum lws_ext_options_types { - EXTARG_NONE, /**< does not take an argument */ - EXTARG_DEC, /**< requires a decimal argument */ - EXTARG_OPT_DEC /**< may have an optional decimal argument */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility */ -}; - -/** struct lws_ext_options - Option arguments to the extension. These are - * used in the negotiation at ws upgrade time. - * The helper function lws_ext_parse_options() - * uses these to generate callbacks */ -struct lws_ext_options { - const char *name; /**< Option name, eg, "server_no_context_takeover" */ - enum lws_ext_options_types type; /**< What kind of args the option can take */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility */ -}; - -/** struct lws_ext_option_arg */ -struct lws_ext_option_arg { - const char *option_name; /**< may be NULL, option_index used then */ - int option_index; /**< argument ordinal to use if option_name missing */ - const char *start; /**< value */ - int len; /**< length of value */ -}; - -/** - * typedef lws_extension_callback_function() - Hooks to allow extensions to operate - * \param context: Websockets context - * \param ext: This extension - * \param wsi: Opaque websocket instance pointer - * \param reason: The reason for the call - * \param user: Pointer to ptr to per-session user data allocated by library - * \param in: Pointer used for some callback reasons - * \param len: Length set for some callback reasons - * - * Each extension that is active on a particular connection receives - * callbacks during the connection lifetime to allow the extension to - * operate on websocket data and manage itself. - * - * Libwebsockets takes care of allocating and freeing "user" memory for - * each active extension on each connection. That is what is pointed to - * by the user parameter. - * - * LWS_EXT_CB_CONSTRUCT: called when the server has decided to - * select this extension from the list provided by the client, - * just before the server will send back the handshake accepting - * the connection with this extension active. This gives the - * extension a chance to initialize its connection context found - * in user. - * - * LWS_EXT_CB_CLIENT_CONSTRUCT: same as LWS_EXT_CB_CONSTRUCT - * but called when client is instantiating this extension. Some - * extensions will work the same on client and server side and then - * you can just merge handlers for both CONSTRUCTS. - * - * LWS_EXT_CB_DESTROY: called when the connection the extension was - * being used on is about to be closed and deallocated. It's the - * last chance for the extension to deallocate anything it has - * allocated in the user data (pointed to by user) before the - * user data is deleted. This same callback is used whether you - * are in client or server instantiation context. - * - * LWS_EXT_CB_PACKET_TX_PRESEND: this works the same way as - * LWS_EXT_CB_PACKET_RX_PREPARSE above, except it gives the - * extension a chance to change websocket data just before it will - * be sent out. Using the same lws_token pointer scheme in in, - * the extension can change the buffer and the length to be - * transmitted how it likes. Again if it wants to grow the - * buffer safely, it should copy the data into its own buffer and - * set the lws_tokens token pointer to it. - * - * LWS_EXT_CB_ARGS_VALIDATE: - */ -typedef int -lws_extension_callback_function(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_extension - An extension we support */ -struct lws_extension { - const char *name; /**< Formal extension name, eg, "permessage-deflate" */ - lws_extension_callback_function *callback; /**< Service callback */ - const char *client_offer; /**< String containing exts and options client offers */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility */ -}; - -/** - * lws_set_extension_option(): set extension option if possible - * - * \param wsi: websocket connection - * \param ext_name: name of ext, like "permessage-deflate" - * \param opt_name: name of option, like "rx_buf_size" - * \param opt_val: value to set option to - */ -LWS_VISIBLE LWS_EXTERN int -lws_set_extension_option(struct lws *wsi, const char *ext_name, - const char *opt_name, const char *opt_val); - -/** - * lws_ext_parse_options() - deal with parsing negotiated extension options - * - * \param ext: related extension struct - * \param wsi: websocket connection - * \param ext_user: per-connection extension private data - * \param opts: list of supported options - * \param o: option string to parse - * \param len: length - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi, - void *ext_user, const struct lws_ext_options *opts, - const char *o, int len); - -/** lws_extension_callback_pm_deflate() - extension for RFC7692 - * - * \param context: lws context - * \param ext: related lws_extension struct - * \param wsi: websocket connection - * \param reason: incoming callback reason - * \param user: per-connection extension private data - * \param in: pointer parameter - * \param len: length parameter - * - * Built-in callback implementing RFC7692 permessage-deflate - */ -LWS_EXTERN 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); - -/* - * The internal exts are part of the public abi - * If we add more extensions, publish the callback here ------v - */ -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-ws-state.h libwebsockets-2.4.2/include/libwebsockets/lws-ws-state.h --- libwebsockets-4.0.20/include/libwebsockets/lws-ws-state.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-ws-state.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** \defgroup wsstatus Websocket status APIs - * ##Websocket connection status APIs - * - * These provide information about ws connection or message status - */ -///@{ -/** - * lws_send_pipe_choked() - tests if socket is writable or not - * \param wsi: lws connection - * - * Allows you to check if you can write more on the socket - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_send_pipe_choked(struct lws *wsi); - -/** - * lws_is_final_fragment() - tests if last part of ws message - * - * \param wsi: lws connection - */ -LWS_VISIBLE LWS_EXTERN int -lws_is_final_fragment(struct lws *wsi); - -/** - * lws_is_first_fragment() - tests if first part of ws message - * - * \param wsi: lws connection - */ -LWS_VISIBLE LWS_EXTERN int -lws_is_first_fragment(struct lws *wsi); - -/** - * lws_get_reserved_bits() - access reserved bits of ws frame - * \param wsi: lws connection - */ -LWS_VISIBLE LWS_EXTERN unsigned char -lws_get_reserved_bits(struct lws *wsi); - -/** - * lws_partial_buffered() - find out if lws buffered the last write - * \param wsi: websocket connection to check - * - * Returns 1 if you cannot use lws_write because the last - * write on this connection is still buffered, and can't be cleared without - * returning to the service loop and waiting for the connection to be - * writeable again. - * - * If you will try to do >1 lws_write call inside a single - * WRITEABLE callback, you must check this after every write and bail if - * set, ask for a new writeable callback and continue writing from there. - * - * This is never set at the start of a writeable callback, but any write - * may set it. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_partial_buffered(struct lws *wsi); - -/** - * lws_frame_is_binary(): true if the current frame was sent in binary mode - * - * \param wsi: the connection we are inquiring about - * - * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if - * it's interested to see if the frame it's dealing with was sent in binary - * mode. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_frame_is_binary(struct lws *wsi); -///@} diff -Nru libwebsockets-4.0.20/include/libwebsockets/lws-x509.h libwebsockets-2.4.2/include/libwebsockets/lws-x509.h --- libwebsockets-4.0.20/include/libwebsockets/lws-x509.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets/lws-x509.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,279 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -enum lws_tls_cert_info { - LWS_TLS_CERT_INFO_VALIDITY_FROM, - /**< fills .time with the time_t the cert validity started from */ - LWS_TLS_CERT_INFO_VALIDITY_TO, - /**< fills .time with the time_t the cert validity ends at */ - LWS_TLS_CERT_INFO_COMMON_NAME, - /**< fills up to len bytes of .ns.name with the cert common name */ - LWS_TLS_CERT_INFO_ISSUER_NAME, - /**< fills up to len bytes of .ns.name with the cert issuer name */ - LWS_TLS_CERT_INFO_USAGE, - /**< fills verified with a bitfield asserting the valid uses */ - LWS_TLS_CERT_INFO_VERIFIED, - /**< fills .verified with a bool representing peer cert validity, - * call returns -1 if no cert */ - LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY, - /**< the certificate's public key, as an opaque bytestream. These - * opaque bytestreams can only be compared with each other using the - * same tls backend, ie, OpenSSL or mbedTLS. The different backends - * produce different, incompatible representations for the same cert. - */ -}; - -union lws_tls_cert_info_results { - unsigned int verified; - time_t time; - unsigned int usage; - struct { - int len; - /* KEEP LAST... notice the [64] is only there because - * name[] is not allowed in a union. The actual length of - * name[] is arbitrary and is passed into the api using the - * len parameter. Eg - * - * char big[1024]; - * union lws_tls_cert_info_results *buf = - * (union lws_tls_cert_info_results *)big; - * - * lws_tls_peer_cert_info(wsi, type, buf, sizeof(big) - - * sizeof(*buf) + sizeof(buf->ns.name)); - */ - char name[64]; - } ns; -}; - -struct lws_x509_cert; -struct lws_jwk; - -/** - * lws_x509_create() - Allocate an lws_x509_cert object - * - * \param x509: pointer to lws_x509_cert pointer to be set to allocated object - * - * Allocates an lws_x509_cert object and set *x509 to point to it. - */ -LWS_VISIBLE LWS_EXTERN int -lws_x509_create(struct lws_x509_cert **x509); - -/** - * lws_x509_parse_from_pem() - Read one or more x509 certs in PEM format from memory - * - * \param x509: pointer to lws_x509_cert object - * \param pem: pointer to PEM format content - * \param len: length of PEM format content - * - * Parses PEM certificates in memory into a native x509 representation for the - * TLS library. If there are multiple PEM certs concatenated, they are all - * read into the same object and exist as a "chain". - * - * IMPORTANT for compatibility with mbedtls, the last used byte of \p pem - * must be '\0' and the \p len must include it. - * - * Returns 0 if all went OK. - */ -LWS_VISIBLE LWS_EXTERN int -lws_x509_parse_from_pem(struct lws_x509_cert *x509, const void *pem, size_t len); - -/** - * lws_x509_verify() - Validate signing relationship between one or more certs - * and a trusted CA cert - * - * \param x509: pointer to lws_x509_cert object, may contain multiple - * \param trusted: a single, trusted cert object that we are checking for - * \param common_name: NULL, or required CN (Common Name) of \p x509 - * - * Returns 0 if the cert or certs in \p x509 represent a complete chain that is - * ultimately signed by the cert in \p trusted. Returns nonzero if that's not - * the case. - */ -LWS_VISIBLE LWS_EXTERN int -lws_x509_verify(struct lws_x509_cert *x509, struct lws_x509_cert *trusted, - const char *common_name); - -/** - * lws_x509_public_to_jwk() - Copy the public key out of a cert and into a JWK - * - * \param jwk: pointer to the jwk to initialize and set to the public key - * \param x509: pointer to lws_x509_cert object that has the public key - * \param curves: NULL to disallow EC, else a comma-separated list of valid - * curves using the JWA naming, eg, "P-256,P-384,P-521". - * \param rsabits: minimum number of RSA bits required in the cert if RSA - * - * Returns 0 if JWK was set to the certificate public key correctly and the - * curve / the RSA key size was acceptable. Automatically produces an RSA or - * EC JWK depending on what the cert had. - */ -LWS_VISIBLE LWS_EXTERN int -lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509, - const char *curves, int rsabits); - -/** - * lws_x509_jwk_privkey_pem() - Copy a private key PEM into a jwk that has the - * public part already - * - * \param jwk: pointer to the jwk to initialize and set to the public key - * \param pem: pointer to PEM private key in memory - * \param len: length of PEM private key in memory - * \param passphrase: NULL or passphrase needed to decrypt private key - * - * IMPORTANT for compatibility with mbedtls, the last used byte of \p pem - * must be '\0' and the \p len must include it. - * - * Returns 0 if the private key was successfully added to the JWK, else - * nonzero if failed. - * - * The PEM image in memory is zeroed down on both successful and failed exits. - * The caller should take care to zero down passphrase if used. - */ -LWS_VISIBLE LWS_EXTERN int -lws_x509_jwk_privkey_pem(struct lws_jwk *jwk, void *pem, size_t len, - const char *passphrase); - -/** - * lws_x509_destroy() - Destroy a previously allocated lws_x509_cert object - * - * \param x509: pointer to lws_x509_cert pointer - * - * Deallocates an lws_x509_cert object and sets its pointer to NULL. - */ -LWS_VISIBLE LWS_EXTERN void -lws_x509_destroy(struct lws_x509_cert **x509); - -LWS_VISIBLE LWS_EXTERN int -lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len); - -/** - * lws_tls_peer_cert_info() - get information from the peer's TLS cert - * - * \param wsi: the connection to query - * \param type: one of LWS_TLS_CERT_INFO_ - * \param buf: pointer to union to take result - * \param len: when result is a string, the true length of buf->ns.name[] - * - * lws_tls_peer_cert_info() lets you get hold of information from the peer - * certificate. - * - * Return 0 if there is a result in \p buf, or -1 indicating there was no cert - * or another problem. - * - * This function works the same no matter if the TLS backend is OpenSSL or - * mbedTLS. - */ -LWS_VISIBLE LWS_EXTERN int -lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len); - -/** - * lws_tls_vhost_cert_info() - get information from the vhost's own TLS cert - * - * \param vhost: the vhost to query - * \param type: one of LWS_TLS_CERT_INFO_ - * \param buf: pointer to union to take result - * \param len: when result is a string, the true length of buf->ns.name[] - * - * lws_tls_vhost_cert_info() lets you get hold of information from the vhost - * certificate. - * - * Return 0 if there is a result in \p buf, or -1 indicating there was no cert - * or another problem. - * - * This function works the same no matter if the TLS backend is OpenSSL or - * mbedTLS. - */ -LWS_VISIBLE LWS_EXTERN int -lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len); - -/** - * lws_tls_acme_sni_cert_create() - creates a temp selfsigned cert - * and attaches to a vhost - * - * \param vhost: the vhost to acquire the selfsigned cert - * \param san_a: SAN written into the certificate - * \param san_b: second SAN written into the certificate - * - * - * Returns 0 if created and attached to the vhost. Returns -1 if problems and - * frees all allocations before returning. - * - * On success, any allocations are destroyed at vhost destruction automatically. - */ -LWS_VISIBLE LWS_EXTERN int -lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, - const char *san_b); - -/** - * lws_tls_acme_sni_csr_create() - creates a CSR and related private key PEM - * - * \param context: lws_context used for random - * \param elements: array of LWS_TLS_REQ_ELEMENT_COUNT const char * - * \param csr: buffer that will get the b64URL(ASN-1 CSR) - * \param csr_len: max length of the csr buffer - * \param privkey_pem: pointer to pointer allocated to hold the privkey_pem - * \param privkey_len: pointer to size_t set to the length of the privkey_pem - * - * Creates a CSR according to the information in \p elements, and a private - * RSA key used to sign the CSR. - * - * The outputs are the b64URL(ASN-1 CSR) into csr, and the PEM private key into - * privkey_pem. - * - * Notice that \p elements points to an array of const char *s pointing to the - * information listed in the enum above. If an entry is NULL or an empty - * string, the element is set to "none" in the CSR. - * - * Returns 0 on success or nonzero for failure. - */ -LWS_VISIBLE LWS_EXTERN int -lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[], - uint8_t *csr, size_t csr_len, char **privkey_pem, - size_t *privkey_len); - -/** - * lws_tls_cert_updated() - update every vhost using the given cert path - * - * \param context: our lws_context - * \param certpath: the filepath to the certificate - * \param keypath: the filepath to the private key of the certificate - * \param mem_cert: copy of the cert in memory - * \param len_mem_cert: length of the copy of the cert in memory - * \param mem_privkey: copy of the private key in memory - * \param len_mem_privkey: length of the copy of the private key in memory - * - * Checks every vhost to see if it is the using certificate described by the - * the given filepaths. If so, it attempts to update the vhost ssl_ctx to use - * the new certificate. - * - * Returns 0 on success or nonzero for failure. - */ -LWS_VISIBLE LWS_EXTERN int -lws_tls_cert_updated(struct lws_context *context, const char *certpath, - const char *keypath, - const char *mem_cert, size_t len_mem_cert, - const char *mem_privkey, size_t len_mem_privkey); - diff -Nru libwebsockets-4.0.20/include/libwebsockets.h libwebsockets-2.4.2/include/libwebsockets.h --- libwebsockets-4.0.20/include/libwebsockets.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/include/libwebsockets.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,616 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -/** @file */ - -#ifndef LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C -#define LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C - -#ifdef __cplusplus -#include -#include - -extern "C" { -#else -#include -#endif - -#include -#include - -#include "lws_config.h" - -/* place for one-shot opaque forward references */ - -struct lws_sequencer; -struct lws_dsh; - -/* - * CARE: everything using cmake defines needs to be below here - */ - -#define LWS_US_PER_SEC ((lws_usec_t)1000000) -#define LWS_MS_PER_SEC ((lws_usec_t)1000) -#define LWS_US_PER_MS ((lws_usec_t)1000) -#define LWS_NS_PER_US ((lws_usec_t)1000) - -#define LWS_KI (1024) -#define LWS_MI (LWS_KI * 1024) -#define LWS_GI (LWS_MI * 1024) -#define LWS_TI ((uint64_t)LWS_GI * 1024) -#define LWS_PI ((uint64_t)LWS_TI * 1024) - -#define LWS_US_TO_MS(x) ((x + (LWS_US_PER_MS / 2)) / LWS_US_PER_MS) - -#if defined(LWS_HAS_INTPTR_T) -#include -#define lws_intptr_t intptr_t -#else -typedef unsigned long long lws_intptr_t; -#endif - -#if defined(WIN32) || defined(_WIN32) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#include -#include -#include -#include -#include -#ifndef _WIN32_WCE -#include -#else -#define _O_RDONLY 0x0000 -#define O_RDONLY _O_RDONLY -#endif - -#define LWS_INLINE __inline -#define LWS_VISIBLE -#define LWS_WARN_UNUSED_RESULT -#define LWS_WARN_DEPRECATED -#define LWS_FORMAT(string_index) - -#if !defined(LWS_EXTERN) -#ifdef LWS_DLL -#ifdef LWS_INTERNAL -#define LWS_EXTERN extern __declspec(dllexport) -#else -#define LWS_EXTERN extern __declspec(dllimport) -#endif -#endif -#endif - -#define LWS_INVALID_FILE INVALID_HANDLE_VALUE -#define LWS_SOCK_INVALID (INVALID_SOCKET) -#define LWS_O_RDONLY _O_RDONLY -#define LWS_O_WRONLY _O_WRONLY -#define LWS_O_CREAT _O_CREAT -#define LWS_O_TRUNC _O_TRUNC - -#ifndef __func__ -#define __func__ __FUNCTION__ -#endif - -#else /* NOT WIN32 */ -#include -#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) -#include -#endif - -#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__QNX__) || defined(__OpenBSD__) -#include -#include -#endif - -#define LWS_INLINE inline -#define LWS_O_RDONLY O_RDONLY -#define LWS_O_WRONLY O_WRONLY -#define LWS_O_CREAT O_CREAT -#define LWS_O_TRUNC O_TRUNC - -#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_TA) && !defined(LWS_PLAT_FREERTOS) -#include -#include -#define LWS_INVALID_FILE -1 -#define LWS_SOCK_INVALID (-1) -#else -#define getdtablesize() (30) -#if defined(LWS_PLAT_FREERTOS) -#define LWS_INVALID_FILE NULL -#define LWS_SOCK_INVALID (-1) -#else -#define LWS_INVALID_FILE NULL -#define LWS_SOCK_INVALID (-1) -#endif -#endif - -#if defined(__GNUC__) - -/* warn_unused_result attribute only supported by GCC 3.4 or later */ -#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -#define LWS_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#else -#define LWS_WARN_UNUSED_RESULT -#endif - -#define LWS_VISIBLE __attribute__((visibility("default"))) -#define LWS_WARN_DEPRECATED __attribute__ ((deprecated)) -#define LWS_FORMAT(string_index) __attribute__ ((format(printf, string_index, string_index+1))) -#else -#define LWS_VISIBLE -#define LWS_WARN_UNUSED_RESULT -#define LWS_WARN_DEPRECATED -#define LWS_FORMAT(string_index) -#endif - -#if defined(__ANDROID__) -#include -#include -#endif - -#endif - -#if defined(LWS_WITH_LIBEV) -#include -#endif /* LWS_WITH_LIBEV */ -#ifdef LWS_WITH_LIBUV -#include -#ifdef LWS_HAVE_UV_VERSION_H -#include -#endif -#ifdef LWS_HAVE_NEW_UV_VERSION_H -#include -#endif -#endif /* LWS_WITH_LIBUV */ -#if defined(LWS_WITH_LIBEVENT) -#include -#endif /* LWS_WITH_LIBEVENT */ - -#ifndef LWS_EXTERN -#define LWS_EXTERN extern -#endif - -#ifdef _WIN32 -#define random rand -#else -#if !defined(LWS_PLAT_OPTEE) -#include -#include -#endif -#endif - -#if defined(LWS_WITH_TLS) - -#ifdef USE_WOLFSSL -#ifdef USE_OLD_CYASSL -#ifdef _WIN32 -/* - * Include user-controlled settings for windows from - * /IDE/WIN/user_settings.h - */ -#include -#include -#else -#include -#endif -#include -#include - -#else -#ifdef _WIN32 -/* - * Include user-controlled settings for windows from - * /IDE/WIN/user_settings.h - */ -#include -#include -#else -#include -#endif -#include -#include -#endif /* not USE_OLD_CYASSL */ -#else -#if defined(LWS_WITH_MBEDTLS) -#if defined(LWS_PLAT_FREERTOS) -/* this filepath is passed to us but without quotes or <> */ -#if !defined(LWS_AMAZON_RTOS) -/* AMAZON RTOS has its own setting via MTK_MBEDTLS_CONFIG_FILE */ -#undef MBEDTLS_CONFIG_FILE -#define MBEDTLS_CONFIG_FILE -#endif -#endif -#include -#include -#include -#else -#include -#if !defined(LWS_WITH_MBEDTLS) -#include -#endif -#endif -#endif /* not USE_WOLFSSL */ -#endif - -/* - * Helpers for pthread mutex in user code... if lws is built for - * multiple service threads, these resolve to pthread mutex - * operations. In the case LWS_MAX_SMP is 1 (the default), they - * are all NOPs and no pthread type or api is referenced. - */ - -#if LWS_MAX_SMP > 1 - -#include - -#define lws_pthread_mutex(name) pthread_mutex_t name; - -static LWS_INLINE void -lws_pthread_mutex_init(pthread_mutex_t *lock) -{ - pthread_mutex_init(lock, NULL); -} - -static LWS_INLINE void -lws_pthread_mutex_destroy(pthread_mutex_t *lock) -{ - pthread_mutex_destroy(lock); -} - -static LWS_INLINE void -lws_pthread_mutex_lock(pthread_mutex_t *lock) -{ - pthread_mutex_lock(lock); -} - -static LWS_INLINE void -lws_pthread_mutex_unlock(pthread_mutex_t *lock) -{ - pthread_mutex_unlock(lock); -} - -#else -#define lws_pthread_mutex(name) -#define lws_pthread_mutex_init(_a) -#define lws_pthread_mutex_destroy(_a) -#define lws_pthread_mutex_lock(_a) -#define lws_pthread_mutex_unlock(_a) -#endif - - -#define CONTEXT_PORT_NO_LISTEN -1 -#define CONTEXT_PORT_NO_LISTEN_SERVER -2 - -#include - - -#include - -#ifndef lws_container_of -#define lws_container_of(P,T,M) ((T *)((char *)(P) - offsetof(T, M))) -#endif -#define LWS_ALIGN_TO(x, bou) x += ((bou) - ((x) % (bou))) % (bou) - -struct lws; - -/* api change list for user code to test against */ - -#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_ARG - -/* the struct lws_protocols has the id field present */ -#define LWS_FEATURE_PROTOCOLS_HAS_ID_FIELD - -/* you can call lws_get_peer_write_allowance */ -#define LWS_FEATURE_PROTOCOLS_HAS_PEER_WRITE_ALLOWANCE - -/* extra parameter introduced in 917f43ab821 */ -#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_LEN - -/* File operations stuff exists */ -#define LWS_FEATURE_FOPS - - -#if defined(_WIN32) -#if !defined(LWS_WIN32_HANDLE_TYPES) -typedef SOCKET lws_sockfd_type; -typedef HANDLE lws_filefd_type; -#endif - -struct lws_pollfd { - lws_sockfd_type fd; /**< file descriptor */ - SHORT events; /**< which events to respond to */ - SHORT revents; /**< which events happened */ -}; -#define LWS_POLLHUP (FD_CLOSE) -#define LWS_POLLIN (FD_READ | FD_ACCEPT) -#define LWS_POLLOUT (FD_WRITE) - -#if !defined(pid_t) -#define pid_t int -#endif - -#else - - -#if defined(LWS_PLAT_FREERTOS) -#include -#if defined(LWS_WITH_ESP32) -#include -#endif -#else -typedef int lws_sockfd_type; -typedef int lws_filefd_type; -#endif - -#if defined(LWS_PLAT_OPTEE) -#include -struct timeval { - time_t tv_sec; - unsigned int tv_usec; -}; -#if defined(LWS_WITH_NETWORK) -// #include -#define lws_pollfd pollfd - -struct timezone; - -int gettimeofday(struct timeval *tv, struct timezone *tz); - - /* Internet address. */ - struct in_addr { - uint32_t s_addr; /* address in network byte order */ - }; - -typedef unsigned short sa_family_t; -typedef unsigned short in_port_t; -typedef uint32_t socklen_t; - -#include - -#if !defined(TEE_SE_READER_NAME_MAX) - struct addrinfo { - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - socklen_t ai_addrlen; - struct sockaddr *ai_addr; - char *ai_canonname; - struct addrinfo *ai_next; - }; -#endif - -ssize_t recv(int sockfd, void *buf, size_t len, int flags); -ssize_t send(int sockfd, const void *buf, size_t len, int flags); -ssize_t read(int fd, void *buf, size_t count); -int getsockopt(int sockfd, int level, int optname, - void *optval, socklen_t *optlen); - int setsockopt(int sockfd, int level, int optname, - const void *optval, socklen_t optlen); -int connect(int sockfd, const struct sockaddr *addr, - socklen_t addrlen); - -extern int errno; - -uint16_t ntohs(uint16_t netshort); -uint16_t htons(uint16_t hostshort); - -int bind(int sockfd, const struct sockaddr *addr, - socklen_t addrlen); - - -#define MSG_NOSIGNAL 0x4000 -#define EAGAIN 11 -#define EINTR 4 -#define EWOULDBLOCK EAGAIN -#define EADDRINUSE 98 -#define INADDR_ANY 0 -#define AF_INET 2 -#define SHUT_WR 1 -#define AF_UNSPEC 0 -#define PF_UNSPEC 0 -#define SOCK_STREAM 1 -#define SOCK_DGRAM 2 -# define AI_PASSIVE 0x0001 -#define IPPROTO_UDP 17 -#define SOL_SOCKET 1 -#define SO_SNDBUF 7 -#define EISCONN 106 -#define EALREADY 114 -#define EINPROGRESS 115 -int shutdown(int sockfd, int how); -int close(int fd); -int atoi(const char *nptr); -long long atoll(const char *nptr); - -int socket(int domain, int type, int protocol); - int getaddrinfo(const char *node, const char *service, - const struct addrinfo *hints, - struct addrinfo **res); - - void freeaddrinfo(struct addrinfo *res); - -#if !defined(TEE_SE_READER_NAME_MAX) -struct lws_pollfd -{ - int fd; /* File descriptor to poll. */ - short int events; /* Types of events poller cares about. */ - short int revents; /* Types of events that actually occurred. */ -}; -#endif - -int poll(struct pollfd *fds, int nfds, int timeout); - -#define LWS_POLLHUP (0x18) -#define LWS_POLLIN (1) -#define LWS_POLLOUT (4) -#else -struct lws_pollfd; -struct sockaddr_in; -#endif -#else -#define lws_pollfd pollfd -#define LWS_POLLHUP (POLLHUP | POLLERR) -#define LWS_POLLIN (POLLIN) -#define LWS_POLLOUT (POLLOUT) -#endif -#endif - - -#if (defined(WIN32) || defined(_WIN32)) && !defined(__MINGW32__) -/* ... */ -#define ssize_t SSIZE_T -#endif - -#if defined(WIN32) && defined(LWS_HAVE__STAT32I64) -#include -#include -#endif - -#if defined(LWS_HAVE_STDINT_H) -#include -#else -#if defined(WIN32) || defined(_WIN32) -/* !!! >:-[ */ -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -typedef __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int8 uint8_t; -#else -typedef unsigned int uint32_t; -typedef unsigned short uint16_t; -typedef unsigned char uint8_t; -#endif -#endif - -typedef int64_t lws_usec_t; -typedef unsigned long long lws_filepos_t; -typedef long long lws_fileofs_t; -typedef uint32_t lws_fop_flags_t; - -#define lws_concat_temp(_t, _l) (_t + sizeof(_t) - _l) -#define lws_concat_used(_t, _l) (sizeof(_t) - _l) - -/** struct lws_pollargs - argument structure for all external poll related calls - * passed in via 'in' */ -struct lws_pollargs { - lws_sockfd_type fd; /**< applicable socket descriptor */ - int events; /**< the new event mask */ - int prev_events; /**< the previous event mask */ -}; - -struct lws_extension; /* needed even with ws exts disabled for create context */ -struct lws_token_limits; -struct lws_protocols; -struct lws_context; -struct lws_tokens; -struct lws_vhost; -struct lws; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(LWS_ROLE_MQTT) -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(LWS_WITH_FILE_OPS) -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined(LWS_PLAT_FREERTOS) -#include - -#include -#endif -#include - -#if defined(LWS_WITH_TLS) - -#if defined(LWS_WITH_MBEDTLS) -#include -#include -#include -#include -#endif - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/abstract/abstract.c libwebsockets-2.4.2/lib/abstract/abstract.c --- libwebsockets-4.0.20/lib/abstract/abstract.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/abstract/abstract.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,355 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 -#include - -extern const lws_abs_transport_t lws_abs_transport_cli_raw_skt, - lws_abs_transport_cli_unit_test; -#if defined(LWS_WITH_SMTP) -extern const lws_abs_protocol_t lws_abs_protocol_smtp; -#endif -#if defined(LWS_WITH_MQTT) -extern const lws_abs_protocol_t lws_abs_protocol_mqttc; -#endif - -static const lws_abs_transport_t * const available_abs_transports[] = { - &lws_abs_transport_cli_raw_skt, - &lws_abs_transport_cli_unit_test, -}; - -#if defined(LWS_WITH_ABSTRACT) -static const lws_abs_protocol_t * const available_abs_protocols[] = { -#if defined(LWS_WITH_SMTP) - &lws_abs_protocol_smtp, -#endif -#if defined(LWS_WITH_MQTT) - &lws_abs_protocol_mqttc, -#endif -}; -#endif - -const lws_abs_transport_t * -lws_abs_transport_get_by_name(const char *name) -{ - int n; - - for (n = 0; n < (int)LWS_ARRAY_SIZE(available_abs_transports); n++) - if (!strcmp(name, available_abs_transports[n]->name)) - return available_abs_transports[n]; - - lwsl_err("%s: cannot find '%s'\n", __func__, name); - - return NULL; -} - -const lws_abs_protocol_t * -lws_abs_protocol_get_by_name(const char *name) -{ -#if defined(LWS_WITH_ABSTRACT) - int n; - - for (n = 0; n < (int)LWS_ARRAY_SIZE(available_abs_protocols); n++) - if (!strcmp(name, available_abs_protocols[n]->name)) - return available_abs_protocols[n]; -#endif - lwsl_err("%s: cannot find '%s'\n", __func__, name); - - return NULL; -} - -const lws_token_map_t * -lws_abs_get_token(const lws_token_map_t *token_map, short name_index) -{ - if (!token_map) - return NULL; - - do { - if (token_map->name_index == name_index) - return token_map; - token_map++; - } while (token_map->name_index); - - return NULL; -} - -static int -lws_abstract_compare_connection(lws_abs_t *abs1, lws_abs_t *abs2) -{ - /* it has to be using the same protocol */ - if (abs1->ap != abs2->ap) - return 1; - - /* protocol has to allow some kind of binding */ - if (!abs1->ap->flags) - return 1; - - /* it has to be using the same transport */ - if (abs1->at != abs2->at) - return 1; - - /* - * The transport must feel the endpoint and conditions in use match the - * requested endpoint and conditions... and the transport type must be - * willing to allow it - */ - if (abs1->at->compare(abs1, abs2)) - return 1; - - /* - * The protocol must feel they are in compatible modes if any - * (and the protocol type must be willing to allow it) - */ - if (abs1->ap->compare(abs1, abs2)) - return 1; - - /* - * If no objection by now, we can say there's already a comparable - * connection and both the protocol and transport feel we can make - * use of it. - */ - - return 0; -} - -static int -find_compatible(struct lws_dll2 *d, void *user) -{ - lws_abs_t *ai1 = (lws_abs_t *)user, - *ai2 = lws_container_of(d, lws_abs_t, abstract_instances); - - if (!lws_abstract_compare_connection(ai1, ai2)) { - /* we can bind to it */ - lws_dll2_add_tail(&ai1->bound, &ai2->children_owner); - - return 1; - } - - return 0; -} - -lws_abs_t * -lws_abs_bind_and_create_instance(const lws_abs_t *abs) -{ - size_t size = sizeof(lws_abs_t) + abs->ap->alloc + abs->at->alloc; - lws_abs_t *ai; - int n; - - /* - * since we know we will allocate the lws_abs_t, the protocol's - * instance allocation, and the transport's instance allocation, - * we merge it into a single heap allocation - */ - ai = lws_malloc(size, "abs inst"); - if (!ai) - return NULL; - - *ai = *abs; - ai->ati = NULL; - - ai->api = (char *)ai + sizeof(lws_abs_t); - - if (!ai->ap->flags) /* protocol only understands single connections */ - goto fresh; - - lws_vhost_lock(ai->vh); /* ----------------------------------- vh { */ - - /* - * Let's have a look for any already-connected transport we can use - */ - - n = lws_dll2_foreach_safe(&ai->vh->abstract_instances_owner, ai, - find_compatible); - - lws_vhost_unlock(ai->vh); /* } vh --------------------------------- */ - - if (n) - goto vh_list_add; - - /* there's no existing connection doing what we want */ - -fresh: - - ai->ati = (char *)ai->api + abs->ap->alloc; - if (ai->at->create(ai)) { - ai->ati = NULL; - goto bail; - } - -vh_list_add: - /* add us to the vhost's dll2 of instances */ - - lws_dll2_clear(&ai->abstract_instances); - lws_dll2_add_head(&ai->abstract_instances, - &ai->vh->abstract_instances_owner); - - if (ai->ap->create(ai)) { - ai->api = NULL; - goto bail; - } - - if (ai->bound.owner) { /* we are a piggybacker */ - lws_abs_t *ai2 = lws_container_of(ai->bound.owner, lws_abs_t, - children_owner); - /* - * Provide an 'event' in the parent context to start handling - * the bind if it's otherwise idle. We give the parent abs - * because we don't know if we're "next" or whatever. Just that - * a child joined him and he should look into his child - * situation in case he was waiting for one to appear. - */ - if (ai2->ap->child_bind(ai2)) { - lwsl_info("%s: anticpated child bind fail\n", __func__); - lws_dll2_remove(&ai->bound); - - goto bail; - } - } - - return ai; - -bail: - lws_abs_destroy_instance(&ai); - - return NULL; -} - -/* - * We get called to clean up each child that was still bound to a parent - * at the time the parent is getting destroyed. - */ - -static void -__lws_abs_destroy_instance2(lws_abs_t **ai) -{ - lws_abs_t *a = *ai; - - if (a->api) - a->ap->destroy(&a->api); - if (a->ati) - a->at->destroy(&a->ati); - - lws_dll2_remove(&a->abstract_instances); - - *ai = NULL; - free(a); -} - -static int -__reap_children(struct lws_dll2 *d, void *user) -{ - lws_abs_t *ac = lws_container_of(d, lws_abs_t, bound); - - lws_dll2_foreach_safe(&ac->children_owner, NULL, __reap_children); - - /* then destroy ourselves */ - - __lws_abs_destroy_instance2(&ac); - - return 0; -} - -void -lws_abs_destroy_instance(lws_abs_t **ai) -{ - lws_abs_t *a = *ai; - - /* destroy child instances that are bound to us first... */ - - lws_vhost_lock(a->vh); /* ----------------------------------- vh { */ - - lws_dll2_foreach_safe(&a->children_owner, NULL, __reap_children); - - /* ...then destroy ourselves */ - - __lws_abs_destroy_instance2(ai); - - lws_vhost_unlock(a->vh); /* } vh --------------------------------- */ -} - -lws_abs_t * -lws_abstract_alloc(struct lws_vhost *vhost, void *user, - const char *abstract_path, const lws_token_map_t *ap_tokens, - const lws_token_map_t *at_tokens, struct lws_sequencer *seq, - void *opaque_user_data) -{ - lws_abs_t *abs = lws_zalloc(sizeof(*abs), __func__); - struct lws_tokenize ts; - lws_tokenize_elem e; - char tmp[30]; - - if (!abs) - return NULL; - - lws_tokenize_init(&ts, abstract_path, LWS_TOKENIZE_F_MINUS_NONTERM); - - e = lws_tokenize(&ts); - if (e != LWS_TOKZE_TOKEN) - goto abs_path_problem; - - if (lws_tokenize_cstr(&ts, tmp, sizeof(tmp))) - goto abs_path_problem; - - abs->ap = lws_abs_protocol_get_by_name(tmp); - if (!abs->ap) - goto abs_path_problem; - - e = lws_tokenize(&ts); - if (e != LWS_TOKZE_DELIMITER) - goto abs_path_problem; - - e = lws_tokenize(&ts); - if (e != LWS_TOKZE_TOKEN) - goto abs_path_problem; - - if (lws_tokenize_cstr(&ts, tmp, sizeof(tmp))) - goto abs_path_problem; - - abs->at = lws_abs_transport_get_by_name(tmp); - if (!abs->at) - goto abs_path_problem; - - abs->vh = vhost; - abs->ap_tokens = ap_tokens; - abs->at_tokens = at_tokens; - abs->seq = seq; - abs->opaque_user_data = opaque_user_data; - - lwsl_info("%s: allocated %s\n", __func__, abstract_path); - - return abs; - -abs_path_problem: - lwsl_err("%s: bad abs path '%s'\n", __func__, abstract_path); - lws_free_set_NULL(abs); - - return NULL; -} - -void -lws_abstract_free(lws_abs_t **pabs) -{ - if (*pabs) - lws_free_set_NULL(*pabs); -} diff -Nru libwebsockets-4.0.20/lib/abstract/private-lib-abstract.h libwebsockets-2.4.2/lib/abstract/private-lib-abstract.h --- libwebsockets-4.0.20/lib/abstract/private-lib-abstract.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/abstract/private-lib-abstract.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(__PRIVATE_LIB_ABSTRACT_H__) -#define __PRIVATE_LIB_ABSTRACT_H__ - -typedef struct lws_token_map lws_token_map_t; -typedef void lws_abs_transport_inst_t; -typedef void lws_abs_protocol_inst_t; - -typedef struct lws_abs { - void *user; - struct lws_vhost *vh; - - const struct lws_abs_protocol *ap; - const lws_token_map_t *ap_tokens; - const struct lws_abs_transport *at; - const lws_token_map_t *at_tokens; - - struct lws_sequencer *seq; - void *opaque_user_data; - - /* vh lock */ - struct lws_dll2_owner children_owner; /* our children / queue */ - /* vh lock */ - struct lws_dll2 bound; /* parent or encapsulator */ - /* vh lock */ - struct lws_dll2 abstract_instances; - lws_abs_transport_inst_t *ati; - lws_abs_protocol_inst_t *api; -} lws_abs_t; - -#endif - diff -Nru libwebsockets-4.0.20/lib/abstract/protocols/smtp/private-lib-abstract-protocols-smtp.h libwebsockets-2.4.2/lib/abstract/protocols/smtp/private-lib-abstract-protocols-smtp.h --- libwebsockets-4.0.20/lib/abstract/protocols/smtp/private-lib-abstract-protocols-smtp.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/abstract/protocols/smtp/private-lib-abstract-protocols-smtp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -#define LWS_SMTP_MAX_EMAIL_LEN 32 - - -/* - * These are allocated on to the heap with an over-allocation to hold the - * payload at the end - */ - -typedef struct lws_smtp_email { - struct lws_dll2 list; - void *data; - - char from[LWS_SMTP_MAX_EMAIL_LEN]; - char to[LWS_SMTP_MAX_EMAIL_LEN]; - - time_t added; - time_t last_try; - - lws_smtp_cb_t done; - - int tries; - - /* email payload follows */ -} lws_smtp_email_t; diff -Nru libwebsockets-4.0.20/lib/abstract/protocols/smtp/smtp.c libwebsockets-2.4.2/lib/abstract/protocols/smtp/smtp.c --- libwebsockets-4.0.20/lib/abstract/protocols/smtp/smtp.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/abstract/protocols/smtp/smtp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,382 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-abstract.h" - -/** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */ -typedef enum lwsgs_smtp_states { - LGSSMTP_IDLE, /**< awaiting new email */ - LGSSMTP_CONNECTING, /**< opening tcp connection to MTA */ - LGSSMTP_CONNECTED, /**< tcp connection to MTA is connected */ - /* (server sends greeting) */ - LGSSMTP_SENT_HELO, /**< sent the HELO */ - - LGSSMTP_SENT_FROM, /**< sent FROM */ - LGSSMTP_SENT_TO, /**< sent TO */ - LGSSMTP_SENT_DATA, /**< sent DATA request */ - LGSSMTP_SENT_BODY, /**< sent the email body */ - - /* - * (server sends, eg, "250 Ok: queued as 12345") - * at this point we can return to LGSSMTP_SENT_HELO and send a - * new email, or continue below to QUIT, or just wait - */ - - LGSSMTP_SENT_QUIT, /**< sent the session quit */ - - /* (server sends, eg, "221 Bye" and closes the connection) */ -} lwsgs_smtp_states_t; - -/** abstract protocol instance data */ - -typedef struct lws_smtp_client_protocol { - const struct lws_abs *abs; - lwsgs_smtp_states_t estate; - - lws_smtp_email_t *e; /* the email we are trying to send */ - const char *helo; - - unsigned char send_pending:1; -} lws_smtpcp_t; - -static const short retcodes[] = { - 0, /* idle */ - 0, /* connecting */ - 220, /* connected */ - 250, /* helo */ - 250, /* from */ - 250, /* to */ - 354, /* data */ - 250, /* body */ - 221, /* quit */ -}; - -static void -lws_smtpc_state_transition(lws_smtpcp_t *c, lwsgs_smtp_states_t s) -{ - lwsl_debug("%s: cli %p: state %d -> %d\n", __func__, c, c->estate, s); - c->estate = s; -} - -static lws_smtp_email_t * -lws_smtpc_get_email(lws_smtpcp_t *c) -{ - const lws_token_map_t *tm; - - /* ... the email we want to send */ - tm = lws_abs_get_token(c->abs->ap_tokens, LTMI_PSMTP_V_LWS_SMTP_EMAIL_T); - if (!tm) { - assert(0); - - return NULL; - } - - return (lws_smtp_email_t *)tm->u.value; -} - -/* - * Called when something happened so that we know now the final disposition of - * the email send attempt, for good or ill. - * - * Inform the owner via the done callback and set up the next queued one if any. - * - * Returns nonzero if we queued a new one - */ - -static int -lws_smtpc_email_disposition(lws_smtpcp_t *c, int disp, const void *buf, - size_t len) -{ - lws_smtpcp_t *ch; - lws_abs_t *ach; - lws_dll2_t *d; - - lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO); - - /* lifetime of the email object is handled by done callback */ - c->e->done(c->e, c->e->data, disp, buf, len); - c->e = NULL; - - /* this may not be the time to try to send anything else... */ - - if (disp == LWS_SMTP_DISPOSITION_FAILED_DESTROY) - return 0; - - /* ... otherwise... do we have another queued? */ - - d = lws_dll2_get_tail(&c->abs->children_owner); - if (!d) - return 0; - - ach = lws_container_of(d, lws_abs_t, bound); - ch = (lws_smtpcp_t *)ach->api; - - c->e = lws_smtpc_get_email(ch); - - /* since we took it on, remove it from the queue */ - lws_dll2_remove(d); - - return 1; -} - -/* - * we became connected - */ - -static int -lws_smtpc_abs_accept(lws_abs_protocol_inst_t *api) -{ - lws_smtpcp_t *c = (lws_smtpcp_t *)api; - - /* we have become connected in the tcp sense */ - - lws_smtpc_state_transition(c, LGSSMTP_CONNECTED); - - /* - * From the accept(), the next thing that should happen is the SMTP - * server sends its greeting like "220 smtp2.example.com ESMTP Postfix", - * we'll hear about it in the rx callback, or time out - */ - - c->abs->at->set_timeout(c->abs->ati, - PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, 3); - - return 0; -} - -static int -lws_smtpc_abs_rx(lws_abs_protocol_inst_t *api, const uint8_t *buf, size_t len) -{ - lws_smtpcp_t *c = (lws_smtpcp_t *)api; - char dotstar[96], at[5]; - int n; - - c->abs->at->set_timeout(c->abs->ati, NO_PENDING_TIMEOUT, 0); - - lws_strncpy(at, (const char *)buf, sizeof(at)); - n = atoi(at); - - switch (c->estate) { - case LGSSMTP_CONNECTED: - if (n != 220) { - /* - * The server did not properly greet us... we can't - * even get started, so fail the transport connection - * (and anything queued on it) - */ - - lws_strnncpy(dotstar, (const char *)buf, len, sizeof(dotstar)); - lwsl_err("%s: server: %s\n", __func__, dotstar); - - return 1; - } - break; - - case LGSSMTP_SENT_BODY: - /* - * We finished one way or another... let's prepare to send a - * new one... or wait until server hangs up on us - */ - if (!lws_smtpc_email_disposition(c, - n == 250 ? LWS_SMTP_DISPOSITION_SENT : - LWS_SMTP_DISPOSITION_FAILED, - "destroyed", 0)) - return 0; /* become idle */ - - break; /* ask to send */ - - case LGSSMTP_SENT_QUIT: - lwsl_debug("%s: done\n", __func__); - lws_smtpc_state_transition(c, LGSSMTP_IDLE); - - return 1; - - default: - if (n != retcodes[c->estate]) { - lws_strnncpy(dotstar, buf, len, sizeof(dotstar)); - lwsl_notice("%s: bad response: %d (state %d) %s\n", - __func__, n, c->estate, dotstar); - - lws_smtpc_email_disposition(c, - LWS_SMTP_DISPOSITION_FAILED, buf, len); - - return 0; - } - break; - } - - c->send_pending = 1; - c->abs->at->ask_for_writeable(c->abs->ati); - - return 0; -} - -static int -lws_smtpc_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget) -{ - char b[256 + LWS_PRE], *p = b + LWS_PRE; - lws_smtpcp_t *c = (lws_smtpcp_t *)api; - int n; - - if (!c->send_pending || !c->e) - return 0; - - c->send_pending = 0; - - lwsl_debug("%s: writing response for state %d\n", __func__, c->estate); - - switch (c->estate) { - case LGSSMTP_CONNECTED: - n = lws_snprintf(p, sizeof(b) - LWS_PRE, "HELO %s\n", c->helo); - lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO); - break; - - case LGSSMTP_SENT_HELO: - n = lws_snprintf(p, sizeof(b) - LWS_PRE, "MAIL FROM: <%s>\n", - c->e->from); - lws_smtpc_state_transition(c, LGSSMTP_SENT_FROM); - break; - - case LGSSMTP_SENT_FROM: - n = lws_snprintf(p, sizeof(b) - LWS_PRE, - "RCPT TO: <%s>\n", c->e->to); - lws_smtpc_state_transition(c, LGSSMTP_SENT_TO); - break; - - case LGSSMTP_SENT_TO: - n = lws_snprintf(p, sizeof(b) - LWS_PRE, "DATA\n"); - lws_smtpc_state_transition(c, LGSSMTP_SENT_DATA); - break; - - case LGSSMTP_SENT_DATA: - p = (char *)&c->e[1]; - n = strlen(p); - lws_smtpc_state_transition(c, LGSSMTP_SENT_BODY); - break; - - case LGSSMTP_SENT_BODY: - n = lws_snprintf(p, sizeof(b) - LWS_PRE, "quit\n"); - lws_smtpc_state_transition(c, LGSSMTP_SENT_QUIT); - break; - - case LGSSMTP_SENT_QUIT: - return 0; - - default: - return 0; - } - - //puts(p); - c->abs->at->tx(c->abs->ati, (uint8_t *)p, n); - - return 0; -} - -static int -lws_smtpc_abs_closed(lws_abs_protocol_inst_t *api) -{ - lws_smtpcp_t *c = (lws_smtpcp_t *)api; - - if (c) - lws_smtpc_state_transition(c, LGSSMTP_IDLE); - - return 0; -} - -/* - * Creating for initial transport and for piggybacking on another transport - * both get created here the same. But piggybackers have ai->bound attached. - */ - -static int -lws_smtpc_create(const lws_abs_t *ai) -{ - lws_smtpcp_t *c = (lws_smtpcp_t *)ai->api; - - memset(c, 0, sizeof(*c)); - - c->abs = ai; - c->e = lws_smtpc_get_email(c); - - lws_smtpc_state_transition(c, lws_dll2_is_detached(&ai->bound) ? - LGSSMTP_CONNECTING : LGSSMTP_IDLE); - - /* If we are initiating the transport, we will get an accept() next... - * - * If we are piggybacking, the parent will get a .child_bind() after - * this to give it a chance to act on us joining (eg, it was completely - * idle and we joined). - */ - - return 0; -} - -static void -lws_smtpc_destroy(lws_abs_protocol_inst_t **_c) -{ - lws_smtpcp_t *c = (lws_smtpcp_t *)*_c; - - if (!c) - return; - - /* so if we are still holding on to c->e, we have failed to send it */ - if (c->e) - lws_smtpc_email_disposition(c, - LWS_SMTP_DISPOSITION_FAILED_DESTROY, "destroyed", 0); - - *_c = NULL; -} - -static int -lws_smtpc_compare(lws_abs_t *abs1, lws_abs_t *abs2) -{ - return 0; -} - -static int -lws_smtpc_child_bind(lws_abs_t *abs) -{ - return 0; -} - -/* events the transport invokes (handled by abstract protocol) */ - -const lws_abs_protocol_t lws_abs_protocol_smtp = { - .name = "smtp", - .alloc = sizeof(lws_smtpcp_t), - .flags = LWSABSPR_FLAG_PIPELINE, - - .create = lws_smtpc_create, - .destroy = lws_smtpc_destroy, - .compare = lws_smtpc_compare, - - .accept = lws_smtpc_abs_accept, - .rx = lws_smtpc_abs_rx, - .writeable = lws_smtpc_abs_writeable, - .closed = lws_smtpc_abs_closed, - .heartbeat = NULL, - - .child_bind = lws_smtpc_child_bind, -}; diff -Nru libwebsockets-4.0.20/lib/abstract/protocols/smtp/smtp-sequencer.c libwebsockets-2.4.2/lib/abstract/protocols/smtp/smtp-sequencer.c --- libwebsockets-4.0.20/lib/abstract/protocols/smtp/smtp-sequencer.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/abstract/protocols/smtp/smtp-sequencer.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,320 +0,0 @@ -/* - * Abstract SMTP support for libwebsockets - SMTP sequencer - * - * Copyright (C) 2016-2019 Andy Green - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * This sequencer sits above the abstract protocol, and manages queueing, - * retrying mail transmission, and retry limits. - * - * Having the sequencer means that, eg, we can manage retries after complete - * connection failure. - * - * Connections to the smtp server are serialized - */ - -#include "private-lib-core.h" -#include "private-lib-abstract-protocols-smtp.h" - -typedef enum { - LSMTPSS_DISCONNECTED, - LSMTPSS_CONNECTING, - LSMTPSS_CONNECTED, - LSMTPSS_BUSY, -} smtpss_connstate_t; - -typedef struct lws_smtp_sequencer { - struct lws_dll2_owner emails_owner; /* email queue */ - - lws_abs_t *abs, *instance; - lws_smtp_sequencer_args_t args; - struct lws_sequencer *seq; - - smtpss_connstate_t connstate; - - time_t email_connect_started; - - /* holds the HELO for the smtp protocol to consume */ - lws_token_map_t apt[3]; -} lws_smtp_sequencer_t; - -/* sequencer messages specific to this sequencer */ - -enum { - SEQ_MSG_CLIENT_FAILED = LWSSEQ_USER_BASE, - SEQ_MSG_CLIENT_DONE, -}; - -/* - * We're going to bind to the raw-skt transport, so tell that what we want it - * to connect to - */ - -static const lws_token_map_t smtp_rs_transport_tokens[] = { - { - .u = { .value = "127.0.0.1" }, - .name_index = LTMI_PEER_V_DNS_ADDRESS, - }, { - .u = { .lvalue = 25 }, - .name_index = LTMI_PEER_LV_PORT, - }, { - } -}; - -static void -lws_smtpc_kick_internal(lws_smtp_sequencer_t *s) -{ - lws_smtp_email_t *e; - lws_dll2_t *d; - char buf[64]; - int n; - lws_dll2_t *pd2; - - pd2 = lws_dll2_get_head(&s->emails_owner); - if (!pd2) - return; - - e = lws_container_of(pd2, lws_smtp_email_t, list); - if (!e) - return; - - /* Is there something to do? If so, we need a connection... */ - - if (s->connstate == LSMTPSS_DISCONNECTED) { - - s->apt[0].u.value = s->args.helo; - s->apt[0].name_index = LTMI_PSMTP_V_HELO; - s->apt[1].u.value = (void *)e; - s->apt[1].name_index = LTMI_PSMTP_V_LWS_SMTP_EMAIL_T; - - /* - * create and connect the smtp protocol + transport - */ - - s->abs = lws_abstract_alloc(s->args.vhost, NULL, "smtp.raw_skt", - s->apt, smtp_rs_transport_tokens, - s->seq, NULL); - if (!s->abs) - return; - - s->instance = lws_abs_bind_and_create_instance(s->abs); - if (!s->instance) { - lws_abstract_free(&s->abs); - lwsl_err("%s: failed to create SMTP client\n", __func__); - - goto bail1; - } - - s->connstate = LSMTPSS_CONNECTING; - lws_seq_timeout_us(s->seq, 10 * LWS_USEC_PER_SEC); - return; - } - - /* ask the transport if we have a connection to the server ongoing */ - - if (s->abs->at->state(s->abs->ati)) { - /* - * there's a connection, it could be still trying to connect - * or established - */ - s->abs->at->ask_for_writeable(s->abs->ati); - - return; - } - - /* there's no existing connection */ - - lws_smtpc_state_transition(c, LGSSMTP_CONNECTING); - - if (s->abs->at->client_conn(s->abs)) { - lwsl_err("%s: failed to connect\n", __func__); - - return; - } - - e->tries++; - e->last_try = lws_now_secs(); -} - - -/* - * The callback we get from the smtp protocol... we use this to drive - * decisions about destroy email, retry and fail. - * - * Sequencer will handle it via the event loop. - */ - -static int -email_result(void *e, void *d, int disp, void *b, size_t l) -{ - lws_smtp_sequencer_t *s = (lws_smtp_sequencer_t *)d; - - lws_sequencer_event(s->seq, LWSSEQ_USER_BASE + disp, e); - - return 0; -} - -static int -cleanup(struct lws_dll2 *d, void *user) -{ - lws_smtp_email_t *e; - - e = lws_container_of(d, lws_smtp_email_t, list); - if (e->done) - e->done(e, "destroying", 10); - - lws_dll2_remove(d); - lws_free(e); - - return 0; -} - -static lws_seq_cb_return_t -smtp_sequencer_cb(struct lws_sequencer *seq, void *user, int event, void *data) -{ - struct lws_smtp_sequencer_t *s = (struct lws_smtp_sequencer_t *)user; - - switch ((int)event) { - case LWSSEQ_CREATED: /* our sequencer just got started */ - lwsl_notice("%s: %s: created\n", __func__, - lws_sequencer_name(seq)); - s->connstate = LSMTPSS_DISCONNECTED; - s->state = 0; /* first thing we'll do is the first url */ - goto step; - - case LWSSEQ_DESTROYED: - lws_dll2_foreach_safe(&s->pending_owner, NULL, cleanup); - break; - - case LWSSEQ_TIMED_OUT: - lwsl_notice("%s: LWSSEQ_TIMED_OUT\n", __func__); - break; - - case LWSSEQ_USER_BASE + LWS_SMTP_DISPOSITION_SENT: - lws_smtpc_free_email(data); - break; - - case LWSSEQ_WSI_CONNECTED: - s->connstate = LSMTPSS_CONNECTED; - lws_smtpc_kick_internal(s); - break; - - case LWSSEQ_WSI_CONN_FAIL: - case LWSSEQ_WSI_CONN_CLOSE: - s->connstate = LSMTPSS_DISCONNECTED; - lws_smtpc_kick_internal(s); - break; - - case SEQ_MSG_SENT: - break; - - default: - break; - } - - return LWSSEQ_RET_CONTINUE; -} - -/* - * Creates an lws_sequencer to manage the test sequence - */ - -lws_smtp_sequencer_t * -lws_smtp_sequencer_create(const lws_smtp_sequencer_args_t *args) -{ - lws_smtp_sequencer_t *s; - struct lws_sequencer *seq; - - /* - * Create a sequencer in the event loop to manage the SMTP queue - */ - - seq = lws_sequencer_create(args->vhost->context, 0, - sizeof(lws_smtp_sequencer_t), (void **)&s, - smtp_sequencer_cb, "smtp-seq"); - if (!seq) { - lwsl_err("%s: unable to create sequencer\n", __func__); - return NULL; - } - - s->abs = *args->abs; - s->args = *args; - s->seq = seq; - - /* set defaults in our copy of the args */ - - if (!s->args.helo[0]) - strcpy(s->args.helo, "default-helo"); - if (!s->args.email_queue_max) - s->args.email_queue_max = 8; - if (!s->args.retry_interval) - s->args.retry_interval = 15 * 60; - if (!s->args.delivery_timeout) - s->args.delivery_timeout = 12 * 60 * 60; - - return s; -} - -void -lws_smtp_sequencer_destroy(lws_smtp_sequencer_t *s) -{ - /* sequencer destruction destroys all assets */ - lws_sequencer_destroy(&s->seq); -} - -int -lws_smtpc_add_email(lws_smtp_sequencer_t *s, const char *payload, - size_t payload_len, const char *sender, - const char *recipient, void *data, lws_smtp_cb_t done) -{ - lws_smtp_email_t *e; - - if (s->emails_owner.count > s->args.email_queue_max) { - lwsl_err("%s: email queue at limit of %d\n", __func__, - (int)s->args.email_queue_max); - - return 1; - } - - if (!done) - return 1; - - e = malloc(sizeof(*e) + payload_len + 1); - if (!e) - return 1; - - memset(e, 0, sizeof(*e)); - - e->data = data; - e->done = done; - - lws_strncpy(e->from, sender, sizeof(e->from)); - lws_strncpy(e->to, recipient, sizeof(e->to)); - - memcpy((char *)&e[1], payload, payload_len + 1); - - e->added = lws_now_secs(); - e->last_try = 0; - e->tries = 0; - - lws_dll2_clear(&e->list); - lws_dll2_add_tail(&e->list, &s->emails_owner); - - lws_smtpc_kick_internal(s); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/abstract/README.md libwebsockets-2.4.2/lib/abstract/README.md --- libwebsockets-4.0.20/lib/abstract/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/abstract/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,170 +0,0 @@ -# Abstract protocols and transports - -## Overview - -Until now protocol implementations in lws have been done directly -to the network-related apis inside lws. - -In an effort to separate out completely network implementation -details from protocol specification, lws now supports -"abstract protocols" and "abstract transports". - -![lws_abstract overview](/doc-assets/abstract-overview.svg) - -The concept is that the implementation is split into two separate -chunks of code hidden behind "ops" structs... the "abstract protocol" -implementation is responsible for the logical protocol operation -and reads and writes only memory buffers. - -The "abstract transport" implementation is responsible for sending -and receiving buffers on some kind of transport, and again is hidden -behind a standardized ops struct. - -In the system, both the abstract protocols and transports are -found by their name. - -An actual "connection" is created by calling a generic api -`lws_abs_bind_and_create_instance()` to instantiate the -combination of a protocol and a transport. - -This makes it possible to confidently offer the same protocol on -completely different transports, eg, like serial, or to wire -up the protocol implementation to a test jig sending canned -test vectors and confirming the response at buffer level, without -any network. The abstract protocol itself has no relationship -to the transport at all and is completely unchanged by changes -to the transport. - -In addition, generic tokens to control settings in both the -protocol and the transport are passed in at instantiation-time, -eg, controlling the IP address targeted by the transport. - -lws SMTP client support has been rewritten to use the new scheme, -and lws provides a raw socket transport built-in. - -## Public API - -The public api for defining abstract protocols and transports is -found at - - - [abstract.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/abstract/abstract.h) - - [protocols.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/abstract/protocols.h) - - [transports.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/abstract/transports.h) - -### `lws_abs_t` - -The main structure that defines the abstraction is `lws_abs_t`, -this is a name and then pointers to the protocol and transport, -optional tokens to control both the protocol and transport, -and pointers to private allocations for both the -protocol and transport when instantiated. - -The transport is selected using - -``` -LWS_VISIBLE LWS_EXTERN const lws_abs_transport_t * -lws_abs_transport_get_by_name(const char *name); -``` - -and similarly the protocol by - -``` -LWS_VISIBLE LWS_EXTERN const lws_abs_protocol_t * -lws_abs_protocol_get_by_name(const char *name); -``` - -At the moment only "`raw-skt`" is defined as an lws built-in, athough -you can also create your own mock transport the same way for creating -test jigs. - -|transport op|meaning| -|---|---| -|`tx()`|transmit a buffer| -|`client_conn()`|start a connection to a peer| -|`close()`|request to close the connection to a peer| -|`ask_for_writeable()`|request a `writeable()` callback when tx can be used| -|`set_timeout()`|set a timeout that will close the connection if reached| -|`state()`|check if the connection is established and can carry traffic| - -These are called by the protocol to get things done and make queries -through the abstract transport. - -|protocol op|meaning| -|---|---| -|`accept()`|The peer has accepted the transport connection| -|`rx()`|The peer has sent us some payload| -|`writeable()`|The connection to the peer can take more tx| -|`closed()`|The connection to the peer has closed| -|`heartbeat()`|Called periodically even when no network events| - -These are called by the transport to inform the protocol of events -and traffic. - -### Instantiation - -The user fills an lws_abs_t and passes a pointer to it to -`lws_abs_bind_and_create_instance()` to create an instantiation -of the protocol + transport. - -### `lws_token_map_t` - -The abstract protocol has no idea about a network or network addresses -or ports or whatever... it may not even be hooked up to one. - -If the transport it is bound to wants things like that, they are passed -in using an array of `lws_token_map_t` at instantiation time. - -For example this is passed to the raw socket protocol in the smtp client -minimal example to control where it would connect to: - -``` -static const lws_token_map_t smtp_abs_tokens[] = { -{ - .u = { .value = "127.0.0.1" }, - .name_index = LTMI_PEER_DNS_ADDRESS, -}, { - .u = { .lvalue = 25l }, - .name_index = LTMI_PEER_PORT, -}}; -``` - -## Steps for adding new abstract protocols - - - add the public header in `./include/libwebsockets/abstract/protocols/` - - add a directory under `./lib/abstract/protocols/` - - add your protocol sources in the new directory - - in CMakeLists.txt: - - add an `LWS_WITH_xxx` for your protocol - - search for "using any abstract protocol" and add your `LWS_WITH_xxx` to - the if so it also sets `LWS_WITH_ABSTRACT` if any set - - add a clause to append your source to SOURCES if `LWS_WITH_xxx` enabled - - add your `lws_abs_protocol` to the list `available_abs_protocols` in - `./lib/abstract/abstract.c` - -## Steps for adding new abstract transports - - - add the public header in `./include/libwebsockets/abstract/transports/` - - add your transport sources under `./lib/abstract/transports/` - - in CMakeLists.txt append your transport sources to SOURCES if `LWS_WITH_ABSTRACT` - and any other cmake conditionals - - add an extern for your transport `lws_protocols` in `./lib/core-net/private.h` - - add your transport `lws_protocols` to `available_abstract_protocols` in - `./lib/core-net/vhost.c` - - add your `lws_abs_transport` to the list `available_abs_transports` in - `./lib/abstract/abstract.c` - -# Protocol testing - -## unit tests - -lws features an abstract transport designed to facilitate unit testing. This -contains an lws_sequencer that performs the steps of tests involving sending the -protocol test vector buffers and confirming the response of the protocol matches -the test vectors. - -## test-sequencer - -test-sequencer is a helper that sequences running an array of unit tests and -collects the statistics and gives a PASS / FAIL result. - -See the SMTP client api test for an example of how to use. diff -Nru libwebsockets-4.0.20/lib/abstract/test-sequencer.c libwebsockets-2.4.2/lib/abstract/test-sequencer.c --- libwebsockets-4.0.20/lib/abstract/test-sequencer.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/abstract/test-sequencer.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,274 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * A helper for running multiple unit tests against abstract protocols. - * - * An lws_seq_t is used to base its actions in the event loop and manage - * the sequencing of multiple tests. A new abstract connection is instantiated - * for each test using te - */ - -#include - -struct lws_seq_test_sequencer { - lws_abs_t original_abs; - - lws_test_sequencer_args_t args; - - struct lws_context *context; - struct lws_vhost *vhost; - struct lws_sequencer *unit_test_seq; - - /* holds the per-test token for the unit-test transport to consume */ - lws_token_map_t uttt[4]; - - lws_abs_t *instance; - - int state; -}; - -/* sequencer messages specific to this sequencer */ - -enum { - SEQ_MSG_PASS = LWSSEQ_USER_BASE, - SEQ_MSG_FAIL, - SEQ_MSG_FAIL_TIMEOUT, -}; - -/* - * We get called back when the unit test transport has decided if the test - * passed or failed. We get the priv, and report to the sequencer message queue - * what the result was. - */ - -static int -unit_test_result_cb(const void *cb_user, int disposition) -{ - const struct lws_seq_test_sequencer *s = - (const struct lws_seq_test_sequencer *)cb_user; - int r; - - lwsl_debug("%s: disp %d\n", __func__, disposition); - - switch (disposition) { - case LPE_FAILED_UNEXPECTED_PASS: - case LPE_FAILED_UNEXPECTED_CLOSE: - case LPE_FAILED: - r = SEQ_MSG_FAIL; - break; - - case LPE_FAILED_UNEXPECTED_TIMEOUT: - r = SEQ_MSG_FAIL_TIMEOUT; - break; - - case LPE_SUCCEEDED: - r = SEQ_MSG_PASS; - break; - - default: - assert(0); - return -1; - } - - lws_seq_queue_event(s->unit_test_seq, r, NULL, NULL); - - ((struct lws_seq_test_sequencer *)s)->instance = NULL; - - return 0; -} - -/* - * We receive the unit test result callback's messages via the message queue. - * - * We log the results and always move on to the next test until there are no - * more tests. - */ - -static lws_seq_cb_return_t -test_sequencer_cb(struct lws_sequencer *seq, void *user, int event, void *data, - void *aux) -{ - struct lws_seq_test_sequencer *s = - (struct lws_seq_test_sequencer *)user; - lws_unit_test_packet_t *exp = (lws_unit_test_packet_t *) - s->args.tests[s->state].expect_array; - lws_abs_t test_abs; - - switch ((int)event) { - case LWSSEQ_CREATED: /* our sequencer just got started */ - lwsl_notice("%s: %s: created\n", __func__, - lws_seq_name(seq)); - s->state = 0; /* first thing we'll do is the first url */ - goto step; - - case LWSSEQ_DESTROYED: - /* - * We are going down... if we have a child unit test sequencer - * still around inform and destroy it - */ - if (s->instance) { - s->instance->at->close(s->instance); - s->instance = NULL; - } - break; - - case SEQ_MSG_FAIL_TIMEOUT: /* current step timed out */ - if (exp->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT) { - lwsl_user("%s: test %d got expected timeout\n", - __func__, s->state); - - goto pass; - } - lwsl_user("%s: seq timed out at step %d\n", __func__, s->state); - - s->args.results[s->state] = LPE_FAILED_UNEXPECTED_TIMEOUT; - goto done; /* always move on to the next test */ - - case SEQ_MSG_FAIL: - if (exp->flags & LWS_AUT_EXPECT_SHOULD_FAIL) { - /* - * in this case, we expected to fail like this, it's OK - */ - lwsl_user("%s: test %d failed as expected\n", - __func__, s->state); - - goto pass; /* always move on to the next test */ - } - - lwsl_user("%s: seq failed at step %d\n", __func__, s->state); - - s->args.results[s->state] = LPE_FAILED; - goto done; /* always move on to the next test */ - - case SEQ_MSG_PASS: - if (exp->flags & (LWS_AUT_EXPECT_SHOULD_FAIL | - LWS_AUT_EXPECT_SHOULD_TIMEOUT)) { - /* - * In these specific cases, done would be a failure, - * we expected to timeout or fail - */ - lwsl_user("%s: seq failed at step %d\n", __func__, - s->state); - - s->args.results[s->state] = LPE_FAILED_UNEXPECTED_PASS; - - goto done; /* always move on to the next test */ - } - lwsl_info("%s: seq done test %d\n", __func__, s->state); -pass: - (*s->args.count_passes)++; - s->args.results[s->state] = LPE_SUCCEEDED; - -done: - lws_seq_timeout_us(lws_seq_from_user(s), LWSSEQTO_NONE); - s->state++; -step: - if (!s->args.tests[s->state].name) { - /* the sequence has completed */ - lwsl_user("%s: sequence completed OK\n", __func__); - - if (s->args.cb) - s->args.cb(s->args.cb_user); - - return LWSSEQ_RET_DESTROY; - } - lwsl_info("%s: starting test %d\n", __func__, s->state); - - if (s->state >= s->args.results_max) { - lwsl_err("%s: results array is too small\n", __func__); - - return LWSSEQ_RET_DESTROY; - } - test_abs = s->original_abs; - s->uttt[0].name_index = LTMI_PEER_V_EXPECT_TEST; - s->uttt[0].u.value = (void *)&s->args.tests[s->state]; - s->uttt[1].name_index = LTMI_PEER_V_EXPECT_RESULT_CB; - s->uttt[1].u.value = (void *)unit_test_result_cb; - s->uttt[2].name_index = LTMI_PEER_V_EXPECT_RESULT_CB_ARG; - s->uttt[2].u.value = (void *)s; - /* give the unit test transport the test tokens */ - test_abs.at_tokens = s->uttt; - - s->instance = lws_abs_bind_and_create_instance(&test_abs); - if (!s->instance) { - lwsl_notice("%s: failed to create step %d unit test\n", - __func__, s->state); - - return LWSSEQ_RET_DESTROY; - } - (*s->args.count_tests)++; - break; - - default: - break; - } - - return LWSSEQ_RET_CONTINUE; -} - - -/* - * Creates an lws_sequencer to manage the test sequence - */ - -int -lws_abs_unit_test_sequencer(const lws_test_sequencer_args_t *args) -{ - struct lws_seq_test_sequencer *s; - struct lws_sequencer *seq; - lws_seq_info_t i; - - memset(&i, 0, sizeof(i)); - i.context = args->abs->vh->context; - i.user_size = sizeof(struct lws_seq_test_sequencer); - i.puser = (void **)&s; - i.cb = test_sequencer_cb; - i.name = "test-seq"; - - /* - * Create a sequencer in the event loop to manage the tests - */ - - seq = lws_seq_create(&i); - if (!seq) { - lwsl_err("%s: unable to create sequencer\n", __func__); - return 1; - } - - /* - * Take a copy of the original lws_abs_t we were passed so we can use - * it as the basis of the lws_abs_t we create the individual tests with - */ - s->original_abs = *args->abs; - - s->args = *args; - - s->context = args->abs->vh->context; - s->vhost = args->abs->vh; - s->unit_test_seq = seq; - - *s->args.count_tests = 0; - *s->args.count_passes = 0; - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/abstract/transports/raw-skt.c libwebsockets-2.4.2/lib/abstract/transports/raw-skt.c --- libwebsockets-4.0.20/lib/abstract/transports/raw-skt.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/abstract/transports/raw-skt.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,408 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-abstract.h" - -typedef struct lws_abstxp_raw_skt_priv { - struct lws_abs *abs; - struct lws *wsi; - - lws_dll2_t same_abs_transport_list; - - uint8_t established:1; - uint8_t connecting:1; -} abs_raw_skt_priv_t; - -struct vhd { - lws_dll2_owner_t owner; -}; - -static int -heartbeat_cb(struct lws_dll2 *d, void *user) -{ - abs_raw_skt_priv_t *priv = lws_container_of(d, abs_raw_skt_priv_t, - same_abs_transport_list); - - if (priv->abs->ap->heartbeat) - priv->abs->ap->heartbeat(priv->abs->api); - - return 0; -} - -static int -callback_abs_client_raw_skt(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)user; - struct vhd *vhd = (struct vhd *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), sizeof(struct vhd)); - if (!vhd) - return 1; - lws_timed_callback_vh_protocol(lws_get_vhost(wsi), - lws_get_protocol(wsi), - LWS_CALLBACK_USER, 1); - break; - - case LWS_CALLBACK_USER: - /* - * This comes at 1Hz without a wsi context, so there is no - * valid priv. We need to track the live abstract objects that - * are using our abstract protocol, and pass the heartbeat - * through to the ones that care. - */ - if (!vhd) - break; - - lws_dll2_foreach_safe(&vhd->owner, NULL, heartbeat_cb); - - lws_timed_callback_vh_protocol(lws_get_vhost(wsi), - lws_get_protocol(wsi), - LWS_CALLBACK_USER, 1); - break; - - case LWS_CALLBACK_RAW_CONNECTED: - lwsl_debug("LWS_CALLBACK_RAW_CONNECTED\n"); - priv->connecting = 0; - priv->established = 1; - if (priv->abs->ap->accept) - priv->abs->ap->accept(priv->abs->api); - if (wsi->seq) - /* - * we are bound to a sequencer who wants to know about - * our lifecycle events - */ - - lws_seq_queue_event(wsi->seq, LWSSEQ_WSI_CONNECTED, - wsi, NULL); - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_user("CONNECTION_ERROR\n"); - if (in) - lwsl_user(" %s\n", (const char *)in); - - if (wsi->seq) - /* - * we are bound to a sequencer who wants to know about - * our lifecycle events - */ - - lws_seq_queue_event(wsi->seq, LWSSEQ_WSI_CONN_FAIL, - wsi, NULL); - - goto close_path; - - /* fallthru */ - case LWS_CALLBACK_RAW_CLOSE: - if (!user) - break; - - if (wsi->seq) - /* - * we are bound to a sequencer who wants to know about - * our lifecycle events - */ - - lws_seq_queue_event(wsi->seq, LWSSEQ_WSI_CONN_CLOSE, - wsi, NULL); - -close_path: - lwsl_debug("LWS_CALLBACK_RAW_CLOSE\n"); - priv->established = 0; - priv->connecting = 0; - if (priv->abs && priv->abs->ap->closed) - priv->abs->ap->closed(priv->abs->api); - lws_set_wsi_user(wsi, NULL); - break; - - case LWS_CALLBACK_RAW_RX: - lwsl_debug("LWS_CALLBACK_RAW_RX (%d)\n", (int)len); - return !!priv->abs->ap->rx(priv->abs->api, in, len); - - case LWS_CALLBACK_RAW_WRITEABLE: - lwsl_debug("LWS_CALLBACK_RAW_WRITEABLE\n"); - priv->abs->ap->writeable(priv->abs->api, - lws_get_peer_write_allowance(priv->wsi)); - break; - - case LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL: - lws_dll2_add_tail(&priv->same_abs_transport_list, &vhd->owner); - break; - - case LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL: - lws_dll2_remove(&priv->same_abs_transport_list); - break; - - default: - break; - } - - return 0; -} - -static int -lws_atcrs_close(lws_abs_transport_inst_t *ati) -{ - abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)ati; - struct lws *wsi = priv->wsi; - - if (!priv->wsi) - return 0; - - if (!lws_raw_transaction_completed(priv->wsi)) - return 0; - - priv->wsi = NULL; - lws_set_timeout(wsi, 1, LWS_TO_KILL_SYNC); - - /* priv is destroyed in the CLOSE callback */ - - return 0; -} - - -const struct lws_protocols protocol_abs_client_raw_skt = { - "lws-abs-cli-raw-skt", callback_abs_client_raw_skt, - 0, 1024, 1024, NULL, 0 -}; - -static int -lws_atcrs_tx(lws_abs_transport_inst_t *ati, uint8_t *buf, size_t len) -{ - abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)ati; - - if (!priv->wsi) { - lwsl_err("%s: NULL priv->wsi\n", __func__); - return 1; - } - - lwsl_debug("%s: priv %p, wsi %p, ro %p\n", __func__, - priv, priv->wsi, priv->wsi->role_ops); - - if (lws_write(priv->wsi, buf, len, LWS_WRITE_RAW) < 0) - lws_atcrs_close(ati); - - return 0; -} - -#if defined(LWS_WITH_CLIENT) -static int -lws_atcrs_client_conn(const lws_abs_t *abs) -{ - abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)abs->ati; - struct lws_client_connect_info i; - const lws_token_map_t *tm; - - if (priv->connecting) - return 0; - - if (priv->established) { - lws_set_timeout(priv->wsi, PENDING_TIMEOUT_CLIENT_CONN_IDLE, 5); - - return 0; - } - - memset(&i, 0, sizeof(i)); - - /* address and port are passed-in using the abstract transport tokens */ - - tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_V_DNS_ADDRESS); - if (!tm) { - lwsl_notice("%s: raw_skt needs LTMI_PEER_V_DNS_ADDRESS\n", - __func__); - - return 1; - } - i.address = tm->u.value; - - tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_LV_PORT); - if (!tm) { - lwsl_notice("%s: raw_skt needs LTMI_PEER_LV_PORT\n", __func__); - - return 1; - } - i.port = tm->u.lvalue; - - /* optional */ - i.ssl_connection = 0; - tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_LV_TLS_FLAGS); - if (tm) - i.ssl_connection = tm->u.lvalue; - - - lwsl_debug("%s: raw_skt priv %p connecting to %s:%u %p\n", - __func__, priv, i.address, i.port, abs->vh->context); - - i.path = ""; - i.method = "RAW"; - i.vhost = abs->vh; - i.userdata = priv; - i.host = i.address; - i.pwsi = &priv->wsi; - i.origin = i.address; - i.context = abs->vh->context; - i.local_protocol_name = "lws-abs-cli-raw-skt"; - i.seq = abs->seq; - i.opaque_user_data = abs->opaque_user_data; - - /* - * the protocol itself has some natural attributes we should pass on - */ - - if (abs->ap->flags & LWS_AP_FLAG_PIPELINE_TRANSACTIONS) - i.ssl_connection |= LCCSCF_PIPELINE; - - if (abs->ap->flags & LWS_AP_FLAG_MUXABLE_STREAM) - i.ssl_connection |= LCCSCF_MUXABLE_STREAM; - - priv->wsi = lws_client_connect_via_info(&i); - if (!priv->wsi) - return 1; - - priv->connecting = 1; - - return 0; -} -#endif - -static int -lws_atcrs_ask_for_writeable(lws_abs_transport_inst_t *ati) -{ - abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)ati; - - if (!priv->wsi || !priv->established) - return 1; - - lws_callback_on_writable(priv->wsi); - - return 0; -} - -static int -lws_atcrs_create(struct lws_abs *ai) -{ - abs_raw_skt_priv_t *at = (abs_raw_skt_priv_t *)ai->ati; - - memset(at, 0, sizeof(*at)); - at->abs = ai; - - return 0; -} - -static void -lws_atcrs_destroy(lws_abs_transport_inst_t **pati) -{ - /* - * For ourselves, we don't free anything because the abstract layer - * combined our allocation with that of the abs instance, and it will - * free the whole thing after this. - */ - *pati = NULL; -} - -static int -lws_atcrs_set_timeout(lws_abs_transport_inst_t *ati, int reason, int secs) -{ - abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)ati; - - lws_set_timeout(priv->wsi, reason, secs); - - return 0; -} - -static int -lws_atcrs_state(lws_abs_transport_inst_t *ati) -{ - abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)ati; - - if (!priv || !priv->wsi || (!priv->established && !priv->connecting)) - return 0; - - return 1; -} - -static int -lws_atcrs_compare(lws_abs_t *abs1, lws_abs_t *abs2) -{ - const lws_token_map_t *tm1, *tm2; - - tm1 = lws_abs_get_token(abs1->at_tokens, LTMI_PEER_V_DNS_ADDRESS); - tm2 = lws_abs_get_token(abs2->at_tokens, LTMI_PEER_V_DNS_ADDRESS); - - /* Address token is mandatory and must match */ - if (!tm1 || !tm2 || strcmp(tm1->u.value, tm2->u.value)) - return 1; - - /* Port token is mandatory and must match */ - tm1 = lws_abs_get_token(abs1->at_tokens, LTMI_PEER_LV_PORT); - tm2 = lws_abs_get_token(abs2->at_tokens, LTMI_PEER_LV_PORT); - if (!tm1 || !tm2 || tm1->u.lvalue != tm2->u.lvalue) - return 1; - - /* TLS is optional... */ - tm1 = lws_abs_get_token(abs1->at_tokens, LTMI_PEER_LV_TLS_FLAGS); - tm2 = lws_abs_get_token(abs2->at_tokens, LTMI_PEER_LV_TLS_FLAGS); - - /* ... but both must have the same situation with it given or not... */ - if (!!tm1 != !!tm2) - return 1; - - /* if not using TLS, then that's enough to call it */ - if (!tm1) - return 0; - - /* ...and if there are tls flags, both must have the same tls flags */ - if (tm1->u.lvalue != tm2->u.lvalue) - return 1; - - /* ... and both must use the same client tls ctx / vhost */ - return abs1->vh != abs2->vh; -} - -const lws_abs_transport_t lws_abs_transport_cli_raw_skt = { - .name = "raw_skt", - .alloc = sizeof(abs_raw_skt_priv_t), - - .create = lws_atcrs_create, - .destroy = lws_atcrs_destroy, - .compare = lws_atcrs_compare, - - .tx = lws_atcrs_tx, -#if !defined(LWS_WITH_CLIENT) - .client_conn = NULL, -#else - .client_conn = lws_atcrs_client_conn, -#endif - .close = lws_atcrs_close, - .ask_for_writeable = lws_atcrs_ask_for_writeable, - .set_timeout = lws_atcrs_set_timeout, - .state = lws_atcrs_state, -}; diff -Nru libwebsockets-4.0.20/lib/abstract/transports/unit-test.c libwebsockets-2.4.2/lib/abstract/transports/unit-test.c --- libwebsockets-4.0.20/lib/abstract/transports/unit-test.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/abstract/transports/unit-test.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,540 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * An abstract transport that is useful for unit testing an abstract protocol. - * It doesn't actually connect to anything, but checks the protocol's response - * to provided canned packets from an array of test vectors. - */ - -#include "private-lib-core.h" -#include "private-lib-abstract.h" - -/* this is the transport priv instantiated at abs->ati */ - -typedef struct lws_abstxp_unit_test_priv { - char note[128]; - struct lws_abs *abs; - - struct lws_sequencer *seq; - lws_unit_test_t *current_test; - lws_unit_test_packet_t *expect; - lws_unit_test_packet_test_cb result_cb; - const void *result_cb_arg; - - lws_unit_test_packet_disposition disposition; - /* synthesized protocol timeout */ - time_t timeout; - - uint8_t established:1; - uint8_t connecting:1; -} abs_unit_test_priv_t; - -typedef struct seq_priv { - lws_abs_t *ai; -} seq_priv_t; - -enum { - UTSEQ_MSG_WRITEABLE = LWSSEQ_USER_BASE, - UTSEQ_MSG_CLOSING, - UTSEQ_MSG_TIMEOUT, - UTSEQ_MSG_CONNECTING, - UTSEQ_MSG_POST_TX_KICK, - UTSEQ_MSG_DISPOSITION_KNOWN -}; - -/* - * A definitive result has appeared for the current test - */ - -static lws_unit_test_packet_disposition -lws_unit_test_packet_dispose(abs_unit_test_priv_t *priv, - lws_unit_test_packet_disposition disp, - const char *note) -{ - assert(priv->disposition == LPE_CONTINUE); - - lwsl_info("%s: %d\n", __func__, disp); - - if (note) - lws_strncpy(priv->note, note, sizeof(priv->note)); - - priv->disposition = disp; - - lws_seq_queue_event(priv->seq, UTSEQ_MSG_DISPOSITION_KNOWN, - NULL, NULL); - - return disp; -} - -/* - * start on the next step of the test - */ - -lws_unit_test_packet_disposition -process_expect(abs_unit_test_priv_t *priv) -{ - assert(priv->disposition == LPE_CONTINUE); - - while (priv->expect->flags & LWS_AUT_EXPECT_RX && - priv->disposition == LPE_CONTINUE) { - int f = priv->expect->flags & LWS_AUT_EXPECT_LOCAL_CLOSE, s; - - if (priv->expect->pre) - priv->expect->pre(priv->abs); - - lwsl_info("%s: rx()\n", __func__); - lwsl_hexdump_debug(priv->expect->buffer, priv->expect->len); - s = priv->abs->ap->rx(priv->abs->api, priv->expect->buffer, - priv->expect->len); - - if (!!f != !!s) { - lwsl_notice("%s: expected rx return %d, got %d\n", - __func__, !!f, s); - - return lws_unit_test_packet_dispose(priv, LPE_FAILED, - "rx unexpected return"); - } - - if (priv->expect->flags & LWS_AUT_EXPECT_TEST_END) { - lws_unit_test_packet_dispose(priv, LPE_SUCCEEDED, NULL); - break; - } - - priv->expect++; - } - - return LPE_CONTINUE; -} - -static lws_seq_cb_return_t -unit_test_sequencer_cb(struct lws_sequencer *seq, void *user, int event, - void *data, void *aux) -{ - seq_priv_t *s = (seq_priv_t *)user; - abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)s->ai->ati; - time_t now; - - switch ((int)event) { - case LWSSEQ_CREATED: /* our sequencer just got started */ - lwsl_notice("%s: %s: created\n", __func__, - lws_seq_name(seq)); - if (s->ai->at->client_conn(s->ai)) { - lwsl_notice("%s: %s: abstract client conn failed\n", - __func__, lws_seq_name(seq)); - - return LWSSEQ_RET_DESTROY; - } - break; - - case LWSSEQ_DESTROYED: - /* - * This sequencer is about to be destroyed. If we have any - * other assets in play, detach them from us. - */ - - if (priv->abs) - lws_abs_destroy_instance(&priv->abs); - - break; - - case LWSSEQ_HEARTBEAT: - - /* synthesize a wsi-style timeout */ - - if (!priv->timeout) - goto ph; - - time(&now); - - if (now <= priv->timeout) - goto ph; - - if (priv->expect->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT) { - lwsl_user("%s: test got expected timeout\n", - __func__); - lws_unit_test_packet_dispose(priv, - LPE_FAILED_UNEXPECTED_TIMEOUT, NULL); - - return LWSSEQ_RET_DESTROY; - } - lwsl_user("%s: seq timed out\n", __func__); - -ph: - if (priv->abs->ap->heartbeat) - priv->abs->ap->heartbeat(priv->abs->api); - break; - - case UTSEQ_MSG_DISPOSITION_KNOWN: - - lwsl_info("%s: %s: DISPOSITION_KNOWN %s: %s\n", __func__, - priv->abs->ap->name, - priv->current_test->name, - priv->disposition == LPE_SUCCEEDED ? "OK" : "FAIL"); - - /* - * if the test has a callback, call it back to let it - * know the result - */ - if (priv->result_cb) - priv->result_cb(priv->result_cb_arg, priv->disposition); - - return LWSSEQ_RET_DESTROY; - - case UTSEQ_MSG_CONNECTING: - lwsl_debug("UTSEQ_MSG_CONNECTING\n"); - - if (priv->abs->ap->accept) - priv->abs->ap->accept(priv->abs->api); - - priv->established = 1; - - /* fallthru */ - - case UTSEQ_MSG_POST_TX_KICK: - if (priv->disposition) - break; - - if (process_expect(priv) != LPE_CONTINUE) { - lwsl_notice("%s: UTSEQ_MSG_POST_TX_KICK failed\n", - __func__); - return LWSSEQ_RET_DESTROY; - } - break; - - case UTSEQ_MSG_WRITEABLE: - /* - * inform the protocol our transport is writeable now - */ - priv->abs->ap->writeable(priv->abs->api, 1024); - break; - - case UTSEQ_MSG_CLOSING: - - if (!(priv->expect->flags & LWS_AUT_EXPECT_LOCAL_CLOSE)) { - lwsl_user("%s: got unexpected close\n", __func__); - - lws_unit_test_packet_dispose(priv, - LPE_FAILED_UNEXPECTED_CLOSE, NULL); - goto done; - } - - /* tell the abstract protocol we are closing on them */ - - if (priv->abs && priv->abs->ap->closed) - priv->abs->ap->closed(priv->abs->api); - - goto done; - - case UTSEQ_MSG_TIMEOUT: /* current step timed out */ - - s->ai->at->close(s->ai->ati); - - if (!(priv->expect->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT)) { - lwsl_user("%s: got unexpected timeout\n", __func__); - - lws_unit_test_packet_dispose(priv, - LPE_FAILED_UNEXPECTED_TIMEOUT, NULL); - return LWSSEQ_RET_DESTROY; - } - goto done; - -done: - lws_seq_timeout_us(lws_seq_from_user(s), - LWSSEQTO_NONE); - priv->expect++; - if (!priv->expect->buffer) { - /* the sequence has completed */ - lwsl_user("%s: sequence completed OK\n", __func__); - - return LWSSEQ_RET_DESTROY; - } - break; - - default: - break; - } - - return LWSSEQ_RET_CONTINUE; -} - -static int -lws_atcut_close(lws_abs_transport_inst_t *ati) -{ - abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati; - - lwsl_notice("%s\n", __func__); - - lws_seq_queue_event(priv->seq, UTSEQ_MSG_CLOSING, NULL, NULL); - - return 0; -} - -static int -lws_atcut_tx(lws_abs_transport_inst_t *ati, uint8_t *buf, size_t len) -{ - abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati; - - assert(priv->disposition == LPE_CONTINUE); - - lwsl_info("%s: received tx\n", __func__); - - if (priv->expect->pre) - priv->expect->pre(priv->abs); - - if (!(priv->expect->flags & LWS_AUT_EXPECT_TX)) { - lwsl_notice("%s: unexpected tx\n", __func__); - lwsl_hexdump_notice(buf, len); - lws_unit_test_packet_dispose(priv, LPE_FAILED, "unexpected tx"); - - return 1; - } - - if (len != priv->expect->len) { - lwsl_notice("%s: unexpected tx len %zu, expected %zu\n", - __func__, len, priv->expect->len); - lws_unit_test_packet_dispose(priv, LPE_FAILED, - "tx len mismatch"); - - return 1; - } - - if (memcmp(buf, priv->expect->buffer, len)) { - lwsl_notice("%s: tx mismatch (exp / actual)\n", __func__); - lwsl_hexdump_debug(priv->expect->buffer, len); - lwsl_hexdump_debug(buf, len); - lws_unit_test_packet_dispose(priv, LPE_FAILED, - "tx data mismatch"); - - return 1; - } - - if (priv->expect->flags & LWS_AUT_EXPECT_TEST_END) { - lws_unit_test_packet_dispose(priv, LPE_SUCCEEDED, NULL); - - return 1; - } - - priv->expect++; - - lws_seq_queue_event(priv->seq, UTSEQ_MSG_POST_TX_KICK, NULL, NULL); - - return 0; -} - -#if defined(LWS_WITH_CLIENT) -static int -lws_atcut_client_conn(const lws_abs_t *abs) -{ - abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)abs->ati; - const lws_token_map_t *tm; - - if (priv->established) { - lwsl_err("%s: already established\n", __func__); - return 1; - } - - /* set up the test start pieces... the array of test expects... */ - - tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_V_EXPECT_TEST); - if (!tm) { - lwsl_notice("%s: unit_test needs LTMI_PEER_V_EXPECT_TEST\n", - __func__); - - return 1; - } - priv->current_test = (lws_unit_test_t *)tm->u.value; - - /* ... and the callback to deliver the result to */ - tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_V_EXPECT_RESULT_CB); - if (tm) - priv->result_cb = (lws_unit_test_packet_test_cb)tm->u.value; - else - priv->result_cb = NULL; - - /* ... and the arg to deliver it with */ - tm = lws_abs_get_token(abs->at_tokens, - LTMI_PEER_V_EXPECT_RESULT_CB_ARG); - if (tm) - priv->result_cb_arg = tm->u.value; - - priv->expect = priv->current_test->expect_array; - priv->disposition = LPE_CONTINUE; - priv->note[0] = '\0'; - - lws_seq_timeout_us(priv->seq, priv->current_test->max_secs * - LWS_US_PER_SEC); - - lwsl_notice("%s: %s: test '%s': start\n", __func__, abs->ap->name, - priv->current_test->name); - - lws_seq_queue_event(priv->seq, UTSEQ_MSG_CONNECTING, NULL, NULL); - - return 0; -} -#endif - -static int -lws_atcut_ask_for_writeable(lws_abs_transport_inst_t *ati) -{ - abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati; - - if (!priv->established) - return 1; - - /* - * Queue a writeable event... this won't be handled by teh sequencer - * until we have returned to the event loop, just like a real - * callback_on_writable() - */ - lws_seq_queue_event(priv->seq, UTSEQ_MSG_WRITEABLE, NULL, NULL); - - return 0; -} - -/* - * An abstract protocol + transport has been instantiated - */ - -static int -lws_atcut_create(lws_abs_t *ai) -{ - abs_unit_test_priv_t *priv; - struct lws_sequencer *seq; - lws_seq_info_t i; - seq_priv_t *s; - - memset(&i, 0, sizeof(i)); - i.context = ai->vh->context; - i.user_size = sizeof(*s); - i.puser = (void **)&s; - i.cb = unit_test_sequencer_cb; - i.name = "unit-test-seq"; - - /* - * Create the sequencer for the steps in a single unit test - */ - - seq = lws_seq_create(&i); - if (!seq) { - lwsl_err("%s: unable to create sequencer\n", __func__); - - return 1; - } - - priv = ai->ati; - memset(s, 0, sizeof(*s)); - memset(priv, 0, sizeof(*priv)); - - /* the sequencer priv just points to the lws_abs_t */ - s->ai = ai; - priv->abs = ai; - priv->seq = seq; - - return 0; -} - -static void -lws_atcut_destroy(lws_abs_transport_inst_t **pati) -{ - /* - * We don't free anything because the abstract layer combined our - * allocation with that of the instance, and it will free the whole - * thing after this. - */ - *pati = NULL; -} - -static int -lws_atcut_set_timeout(lws_abs_transport_inst_t *ati, int reason, int secs) -{ - abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati; - time_t now; - - time(&now); - - if (secs) - priv->timeout = now + secs; - else - priv->timeout = 0; - - return 0; -} - -static int -lws_atcut_state(lws_abs_transport_inst_t *ati) -{ - abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati; - - if (!priv || (!priv->established && !priv->connecting)) - return 0; - - return 1; -} - -static const char *dnames[] = { - "INCOMPLETE", - "PASS", - "FAIL", - "FAIL(TIMEOUT)", - "FAIL(UNEXPECTED PASS)", - "FAIL(UNEXPECTED CLOSE)", - "SKIPPED" - "?", - "?" -}; - - -const char * -lws_unit_test_result_name(int in) -{ - if (in < 0 || in > (int)LWS_ARRAY_SIZE(dnames)) - return "unknown"; - - return dnames[in]; -} - -static int -lws_atcut_compare(lws_abs_t *abs1, lws_abs_t *abs2) -{ - return 0; -} - -const lws_abs_transport_t lws_abs_transport_cli_unit_test = { - .name = "unit_test", - .alloc = sizeof(abs_unit_test_priv_t), - - .create = lws_atcut_create, - .destroy = lws_atcut_destroy, - .compare = lws_atcut_compare, - - .tx = lws_atcut_tx, -#if !defined(LWS_WITH_CLIENT) - .client_conn = NULL, -#else - .client_conn = lws_atcut_client_conn, -#endif - .close = lws_atcut_close, - .ask_for_writeable = lws_atcut_ask_for_writeable, - .set_timeout = lws_atcut_set_timeout, - .state = lws_atcut_state, -}; diff -Nru libwebsockets-4.0.20/lib/alloc.c libwebsockets-2.4.2/lib/alloc.c --- libwebsockets-4.0.20/lib/alloc.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/alloc.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,90 @@ +#include "private-libwebsockets.h" + +#if defined(LWS_PLAT_OPTEE) + +#define TEE_USER_MEM_HINT_NO_FILL_ZERO 0x80000000 + +void *__attribute__((weak)) + TEE_Malloc(uint32_t size, uint32_t hint) +{ + return NULL; +} +void *__attribute__((weak)) + TEE_Realloc(void *buffer, uint32_t newSize) +{ + return NULL; +} +void __attribute__((weak)) + TEE_Free(void *buffer) +{ +} + +void *lws_realloc(void *ptr, size_t size, const char *reason) +{ + return TEE_Realloc(ptr, size); +} + +void *lws_malloc(size_t size, const char *reason) +{ + return TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO); +} + +void lws_free(void *p) +{ + TEE_Free(p); +} + +void *lws_zalloc(size_t size, const char *reason) +{ + void *ptr = TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO); + if (ptr) + memset(ptr, 0, size); + return ptr; +} + +void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason)) +{ + (void)cb; +} +#else + +static void *_realloc(void *ptr, size_t size, const char *reason) +{ + if (size) { +#if defined(LWS_PLAT_ESP32) + lwsl_notice("%s: size %lu: %s\n", __func__, (unsigned long)size, reason); +#else + lwsl_debug("%s: size %lu: %s\n", __func__, (unsigned long)size, reason); +#endif +#if defined(LWS_PLAT_OPTEE) + return (void *)TEE_Realloc(ptr, size); +#else + return (void *)realloc(ptr, size); +#endif + } + if (ptr) + free(ptr); + + return NULL; +} + +void *(*_lws_realloc)(void *ptr, size_t size, const char *reason) = _realloc; + +void *lws_realloc(void *ptr, size_t size, const char *reason) +{ + return _lws_realloc(ptr, size, reason); +} + +void *lws_zalloc(size_t size, const char *reason) +{ + void *ptr = _lws_realloc(NULL, size, reason); + if (ptr) + memset(ptr, 0, size); + return ptr; +} + +void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason)) +{ + _lws_realloc = cb; +} +#endif diff -Nru libwebsockets-4.0.20/lib/client/client.c libwebsockets-2.4.2/lib/client/client.c --- libwebsockets-4.0.20/lib/client/client.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/client/client.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1304 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2014 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +int +lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len) +{ + int m; + + switch (wsi->mode) { + case LWSCM_WSCL_WAITING_PROXY_REPLY: + case LWSCM_WSCL_ISSUE_HANDSHAKE: + case LWSCM_WSCL_WAITING_SERVER_REPLY: + case LWSCM_WSCL_WAITING_EXTENSION_CONNECT: + case LWSCM_WS_CLIENT: + while (len) { + /* + * we were accepting input but now we stopped doing so + */ + if (lws_is_flowcontrolled(wsi)) { + lwsl_debug("%s: caching %ld\n", __func__, (long)len); + lws_rxflow_cache(wsi, *buf, 0, len); + return 0; + } + if (wsi->u.ws.rx_draining_ext) { +#if !defined(LWS_NO_CLIENT) + if (wsi->mode == LWSCM_WS_CLIENT) + m = lws_client_rx_sm(wsi, 0); + else +#endif + m = lws_rx_sm(wsi, 0); + if (m < 0) + return -1; + continue; + } + /* account for what we're using in rxflow buffer */ + if (wsi->rxflow_buffer) + wsi->rxflow_pos++; + + if (lws_client_rx_sm(wsi, *(*buf)++)) { + lwsl_debug("client_rx_sm exited\n"); + return -1; + } + len--; + } + lwsl_debug("%s: finished with %ld\n", __func__, (long)len); + return 0; + default: + break; + } + + return 0; +} + +LWS_VISIBLE LWS_EXTERN void +lws_client_http_body_pending(struct lws *wsi, int something_left_to_send) +{ + wsi->client_http_body_pending = !!something_left_to_send; +} + +int +lws_client_socket_service(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pollfd) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + const char *cce = NULL; + unsigned char c; + char *sb = p; + int n = 0; + ssize_t len = 0; +#if defined(LWS_WITH_SOCKS5) + char conn_mode = 0, pending_timeout = 0; +#endif + + switch (wsi->mode) { + + case LWSCM_WSCL_WAITING_CONNECT: + + /* + * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE + * timeout protection set in client-handshake.c + */ + + if (!lws_client_connect_2(wsi)) { + /* closed */ + lwsl_client("closed\n"); + return -1; + } + + /* either still pending connection, or changed mode */ + return 0; + +#if defined(LWS_WITH_SOCKS5) + /* SOCKS Greeting Reply */ + case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY: + case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY: + case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY: + + /* handle proxy hung up on us */ + + if (pollfd->revents & LWS_POLLHUP) { + lwsl_warn("SOCKS connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + goto bail3; + } + + n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); + if (n < 0) { + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_debug("SOCKS read EAGAIN, retrying\n"); + return 0; + } + lwsl_err("ERROR reading from SOCKS socket\n"); + goto bail3; + } + + switch (wsi->mode) { + + case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY: + if (pt->serv_buf[0] != SOCKS_VERSION_5) + goto socks_reply_fail; + + if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) { + lwsl_client("SOCKS greeting reply: No Auth Method\n"); + socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len); + conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY; + pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; + goto socks_send; + } + + if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) { + lwsl_client("SOCKS greeting reply: User/Pw Method\n"); + socks_generate_msg(wsi, SOCKS_MSG_USERNAME_PASSWORD, &len); + conn_mode = LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY; + pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY; + goto socks_send; + } + goto socks_reply_fail; + + case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY: + if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 || + pt->serv_buf[1] != SOCKS_SUBNEGOTIATION_STATUS_SUCCESS) + goto socks_reply_fail; + + lwsl_client("SOCKS password OK, sending connect\n"); + socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len); + conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY; + pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; +socks_send: + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing to socks proxy\n"); + goto bail3; + } + + lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT); + wsi->mode = conn_mode; + break; + +socks_reply_fail: + lwsl_notice("socks reply: v%d, err %d\n", + pt->serv_buf[0], pt->serv_buf[1]); + goto bail3; + + case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY: + if (pt->serv_buf[0] != SOCKS_VERSION_5 || + pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS) + goto socks_reply_fail; + + lwsl_client("socks connect OK\n"); + + /* free stash since we are done with it */ + lws_free_set_NULL(wsi->u.hdr.stash); + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, + wsi->vhost->socks_proxy_address)) + goto bail3; + + wsi->c_port = wsi->vhost->socks_proxy_port; + + /* clear his proxy connection timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + goto start_ws_handshake; + } + break; +#endif + + case LWSCM_WSCL_WAITING_PROXY_REPLY: + + /* handle proxy hung up on us */ + + if (pollfd->revents & LWS_POLLHUP) { + + lwsl_warn("Proxy connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + goto bail3; + } + + n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); + if (n < 0) { + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_debug("Proxy read returned EAGAIN... retrying\n"); + return 0; + } + lwsl_err("ERROR reading from proxy socket\n"); + goto bail3; + } + + pt->serv_buf[13] = '\0'; + if (strcmp(sb, "HTTP/1.0 200 ") && + strcmp(sb, "HTTP/1.1 200 ")) { + lwsl_err("ERROR proxy: %s\n", sb); + goto bail3; + } + + /* clear his proxy connection timeout */ + + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* fallthru */ + + case LWSCM_WSCL_ISSUE_HANDSHAKE: + + /* + * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE + * timeout protection set in client-handshake.c + * + * take care of our lws_callback_on_writable + * happening at a time when there's no real connection yet + */ +#if defined(LWS_WITH_SOCKS5) +start_ws_handshake: +#endif + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) + return -1; + +#ifdef LWS_OPENSSL_SUPPORT + /* we can retry this... just cook the SSL BIO the first time */ + + if (wsi->use_ssl && !wsi->ssl && + lws_ssl_client_bio_create(wsi) < 0) { + cce = "bio_create failed"; + goto bail3; + } + + if (wsi->use_ssl) { + n = lws_ssl_client_connect1(wsi); + if (!n) + return 0; + if (n < 0) { + cce = "lws_ssl_client_connect1 failed"; + goto bail3; + } + } else + wsi->ssl = NULL; + + /* fallthru */ + + case LWSCM_WSCL_WAITING_SSL: + + if (wsi->use_ssl) { + n = lws_ssl_client_connect2(wsi); + if (!n) + return 0; + if (n < 0) { + cce = "lws_ssl_client_connect2 failed"; + goto bail3; + } + } else + wsi->ssl = NULL; +#endif + + wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2; + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, + context->timeout_secs); + + /* fallthru */ + + case LWSCM_WSCL_ISSUE_HANDSHAKE2: + p = lws_generate_client_handshake(wsi, p); + if (p == NULL) { + if (wsi->mode == LWSCM_RAW) + return 0; + + lwsl_err("Failed to generate handshake for client\n"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 0; + } + + /* send our request to the server */ + lws_latency_pre(context, wsi); + + n = lws_ssl_capable_write(wsi, (unsigned char *)sb, p - sb); + lws_latency(context, wsi, "send lws_issue_raw", n, + n == p - sb); + switch (n) { + case LWS_SSL_CAPABLE_ERROR: + lwsl_debug("ERROR writing to client socket\n"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 0; + case LWS_SSL_CAPABLE_MORE_SERVICE: + lws_callback_on_writable(wsi); + break; + } + + if (wsi->client_http_body_pending) { + wsi->mode = LWSCM_WSCL_ISSUE_HTTP_BODY; + lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, + context->timeout_secs); + /* user code must ask for writable callback */ + break; + } + + goto client_http_body_sent; + + case LWSCM_WSCL_ISSUE_HTTP_BODY: + if (wsi->client_http_body_pending) { + lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, + context->timeout_secs); + /* user code must ask for writable callback */ + break; + } +client_http_body_sent: + wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; + wsi->u.hdr.lextable_pos = 0; + wsi->mode = LWSCM_WSCL_WAITING_SERVER_REPLY; + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, + context->timeout_secs); + break; + + case LWSCM_WSCL_WAITING_SERVER_REPLY: + /* + * handle server hanging up on us... + * but if there is POLLIN waiting, handle that first + */ + if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) == + LWS_POLLHUP) { + + lwsl_debug("Server connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + cce = "Peer hung up"; + goto bail3; + } + + if (!(pollfd->revents & LWS_POLLIN)) + break; + + /* interpret the server response + * + * HTTP/1.1 101 Switching Protocols + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= + * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== + * Sec-WebSocket-Protocol: chat + * + * we have to take some care here to only take from the + * socket bytewise. The browser may (and has been seen to + * in the case that onopen() performs websocket traffic) + * coalesce both handshake response and websocket traffic + * in one packet, since at that point the connection is + * definitively ready from browser pov. + */ + len = 1; + while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE && + len > 0) { + n = lws_ssl_capable_read(wsi, &c, 1); + lws_latency(context, wsi, "send lws_issue_raw", n, + n == 1); + switch (n) { + case 0: + case LWS_SSL_CAPABLE_ERROR: + cce = "read failed"; + goto bail3; + case LWS_SSL_CAPABLE_MORE_SERVICE: + return 0; + } + + if (lws_parse(wsi, c)) { + lwsl_warn("problems parsing header\n"); + goto bail3; + } + } + + /* + * hs may also be coming in multiple packets, there is a 5-sec + * libwebsocket timeout still active here too, so if parsing did + * not complete just wait for next packet coming in this state + */ + if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) + break; + + /* + * otherwise deal with the handshake. If there's any + * packet traffic already arrived we'll trigger poll() again + * right away and deal with it that way + */ + return lws_client_interpret_server_handshake(wsi); + +bail3: + lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n"); + if (cce) + lwsl_info("reason: %s\n", cce); + wsi->protocol->callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, cce ? strlen(cce) : 0); + wsi->already_did_cce = 1; + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return -1; + + case LWSCM_WSCL_WAITING_EXTENSION_CONNECT: + lwsl_ext("LWSCM_WSCL_WAITING_EXTENSION_CONNECT\n"); + break; + + case LWSCM_WSCL_PENDING_CANDIDATE_CHILD: + lwsl_ext("LWSCM_WSCL_PENDING_CANDIDATE_CHILD\n"); + break; + default: + break; + } + + return 0; +} + +/* + * In-place str to lower case + */ + +static void +strtolower(char *s) +{ + while (*s) { +#ifdef LWS_PLAT_OPTEE + int tolower_optee(int c); + *s = tolower_optee((int)*s); +#else + *s = tolower((int)*s); +#endif + s++; + } +} + +int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed_client(struct lws *wsi) +{ + lwsl_debug("%s: wsi %p\n", __func__, wsi); + /* if we can't go back to accept new headers, drop the connection */ + if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) { + lwsl_info("%s: %p: close connection\n", __func__, wsi); + return 1; + } + + /* we don't support chained client connections yet */ + return 1; +#if 0 + /* otherwise set ourselves up ready to go again */ + wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED; + wsi->mode = LWSCM_HTTP_CLIENT_ACCEPTED; + wsi->u.http.rx_content_length = 0; + wsi->hdr_parsing_completed = 0; + + /* He asked for it to stay alive indefinitely */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* + * As client, nothing new is going to come until we ask for it + * we can drop the ah, if any + */ + if (wsi->u.hdr.ah) { + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 0); + } + + /* If we're (re)starting on headers, need other implied init */ + wsi->u.hdr.ues = URIES_IDLE; + + lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi); + + return 0; +#endif +} + +LWS_VISIBLE LWS_EXTERN unsigned int +lws_http_client_http_response(struct lws *wsi) +{ + if (!wsi->u.http.ah) + return 0; + + return wsi->u.http.ah->http_response; +} + +int +lws_client_interpret_server_handshake(struct lws *wsi) +{ + int n, len, okay = 0, port = 0, ssl = 0; + int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR; + struct lws_context *context = wsi->context; + const char *pc, *prot, *ads = NULL, *path, *cce = NULL; + struct allocated_headers *ah = NULL; + char *p, *q; + char new_path[300]; +#ifndef LWS_NO_EXTENSIONS + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *sb = (char *)&pt->serv_buf[0]; + const struct lws_ext_options *opts; + const struct lws_extension *ext; + char ext_name[128]; + const char *c, *a; + char ignore; + int more = 1; + void *v; +#endif + if (wsi->u.hdr.stash) + lws_free_set_NULL(wsi->u.hdr.stash); + + ah = wsi->u.hdr.ah; + if (!wsi->do_ws) { + /* we are being an http client... + */ + lws_union_transition(wsi, LWSCM_HTTP_CLIENT_ACCEPTED); + wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED; + wsi->u.http.ah = ah; + ah->http_response = 0; + } + + /* + * well, what the server sent looked reasonable for syntax. + * Now let's confirm it sent all the necessary headers + * + * http (non-ws) client will expect something like this + * + * HTTP/1.0.200 + * server:.libwebsockets + * content-type:.text/html + * content-length:.17703 + * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000 + * + * + * + */ + + wsi->u.http.connection_type = HTTP_CONNECTION_KEEP_ALIVE; + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP); + if (wsi->do_ws && !p) { + lwsl_info("no URI\n"); + cce = "HS: URI missing"; + goto bail3; + } + if (!p) { + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0); + wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE; + } + if (!p) { + cce = "HS: URI missing"; + lwsl_info("no URI\n"); + goto bail3; + } + n = atoi(p); + if (ah) + ah->http_response = n; + + if (n == 301 || n == 302 || n == 303 || n == 307 || n == 308) { + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION); + if (!p) { + cce = "HS: Redirect code but no Location"; + goto bail3; + } + + /* Relative reference absolute path */ + if (p[0] == '/') + { +#ifdef LWS_OPENSSL_SUPPORT + ssl = wsi->use_ssl; +#endif + ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); + port = wsi->c_port; + path = p + 1; /* +1 as lws_client_reset expects leading / to be omitted */ + } + /* Absolute (Full) URI */ + else if (strchr(p, ':')) + { + if (lws_parse_uri(p, &prot, &ads, &port, &path)) { + cce = "HS: URI did not parse"; + goto bail3; + } + + if (!strcmp(prot, "wss") || !strcmp(prot, "https")) + ssl = 1; + } + /* Relative reference relative path */ + else + { + /* This doesn't try to calculate an absolute path, that will be left to the server */ +#ifdef LWS_OPENSSL_SUPPORT + ssl = wsi->use_ssl; +#endif + ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); + port = wsi->c_port; + path = new_path + 1; /* +1 as lws_client_reset expects leading / to be omitted */ + strncpy(new_path, lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI), sizeof(new_path)); + new_path[sizeof(new_path) - 1] = '\0'; + q = strrchr(new_path, '/'); + if (q) + { + strncpy(q + 1, p, sizeof(new_path) - (q - new_path) - 1); + new_path[sizeof(new_path) - 1] = '\0'; + } + else + { + path = p; + } + } + +#ifdef LWS_OPENSSL_SUPPORT + if (wsi->use_ssl && !ssl) { + cce = "HS: Redirect attempted SSL downgrade"; + goto bail3; + } +#endif + + if (!lws_client_reset(&wsi, ssl, ads, port, path, ads)) { + /* there are two ways to fail out with NULL return... + * simple, early problem where the wsi is intact, or + * we went through with the reconnect attempt and the + * wsi is already closed. In the latter case, the wsi + * has beet set to NULL additionally. + */ + lwsl_err("Redirect failed\n"); + cce = "HS: Redirect failed"; + if (wsi) + goto bail3; + + return 1; + } + return 0; + } + + if (!wsi->do_ws) { + +#ifdef LWS_WITH_HTTP_PROXY + wsi->perform_rewrite = 0; + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) { + if (!strncmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE), + "text/html", 9)) + wsi->perform_rewrite = 1; + } +#endif + + /* allocate the per-connection user memory (if any) */ + if (lws_ensure_user_space(wsi)) { + lwsl_err("Problem allocating wsi user mem\n"); + cce = "HS: OOM"; + goto bail2; + } + + /* he may choose to send us stuff in chunked transfer-coding */ + wsi->chunked = 0; + wsi->chunk_remaining = 0; /* ie, next thing is chunk size */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING)) { + wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_TRANSFER_ENCODING), + "chunked"); + /* first thing is hex, after payload there is crlf */ + wsi->chunk_parser = ELCP_HEX; + } + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + wsi->u.http.rx_content_length = + atoll(lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_CONTENT_LENGTH)); + lwsl_notice("%s: incoming content length %llu\n", __func__, + (unsigned long long)wsi->u.http.rx_content_length); + wsi->u.http.rx_content_remain = wsi->u.http.rx_content_length; + } else /* can't do 1.1 without a content length or chunked */ + if (!wsi->chunked) + wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE; + + /* + * we seem to be good to go, give client last chance to check + * headers and OK it + */ + if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, + wsi->user_space, NULL, 0)) { + + cce = "HS: disallowed by client filter"; + goto bail2; + } + + /* clear his proxy connection timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* call him back to inform him he is up */ + if (wsi->protocol->callback(wsi, + LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP, + wsi->user_space, NULL, 0)) { + cce = "HS: disallowed at ESTABLISHED"; + goto bail3; + } + + /* free up his parsing allocations */ + lws_header_table_detach(wsi, 0); + + lwsl_notice("%s: client connection up\n", __func__); + + return 0; + } + + if (p && !strncmp(p, "401", 3)) { + lwsl_warn( + "lws_client_handshake: got bad HTTP response '%s'\n", p); + cce = "HS: ws upgrade unauthorized"; + goto bail3; + } + + if (p && strncmp(p, "101", 3)) { + lwsl_warn( + "lws_client_handshake: got bad HTTP response '%s'\n", p); + cce = "HS: ws upgrade response not 101"; + goto bail3; + } + + if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) { + lwsl_info("no ACCEPT\n"); + cce = "HS: ACCEPT missing"; + goto bail3; + } + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE); + if (!p) { + lwsl_info("no UPGRADE\n"); + cce = "HS: UPGRADE missing"; + goto bail3; + } + strtolower(p); + if (strcmp(p, "websocket")) { + lwsl_warn( + "lws_client_handshake: got bad Upgrade header '%s'\n", p); + cce = "HS: Upgrade to something other than websocket"; + goto bail3; + } + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION); + if (!p) { + lwsl_info("no Connection hdr\n"); + cce = "HS: CONNECTION missing"; + goto bail3; + } + strtolower(p); + if (strcmp(p, "upgrade")) { + lwsl_warn("lws_client_int_s_hs: bad header %s\n", p); + cce = "HS: UPGRADE malformed"; + goto bail3; + } + + pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); + if (!pc) { + lwsl_parser("lws_client_int_s_hs: no protocol list\n"); + } else + lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc); + + /* + * confirm the protocol the server wants to talk was in the list + * of protocols we offered + */ + + len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL); + if (!len) { + lwsl_info("lws_client_int_s_hs: WSI_TOKEN_PROTOCOL is null\n"); + /* + * no protocol name to work from, + * default to first protocol + */ + n = 0; + wsi->protocol = &wsi->vhost->protocols[0]; + goto check_extensions; + } + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL); + len = strlen(p); + + while (pc && *pc && !okay) { + if (!strncmp(pc, p, len) && + (pc[len] == ',' || pc[len] == '\0')) { + okay = 1; + continue; + } + while (*pc && *pc++ != ',') + ; + while (*pc && *pc == ' ') + pc++; + } + + if (!okay) { + lwsl_err("lws_client_int_s_hs: got bad protocol %s\n", p); + cce = "HS: PROTOCOL malformed"; + goto bail2; + } + + /* + * identify the selected protocol struct and set it + */ + n = 0; + wsi->protocol = NULL; + while (wsi->vhost->protocols[n].callback && !wsi->protocol) { + if (strcmp(p, wsi->vhost->protocols[n].name) == 0) { + wsi->protocol = &wsi->vhost->protocols[n]; + break; + } + n++; + } + + if (wsi->protocol == NULL) { + lwsl_err("lws_client_int_s_hs: fail protocol %s\n", p); + cce = "HS: Cannot match protocol"; + goto bail2; + } + +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 + */ + //lwsl_err("%s: pre insert vhost start wsi %p, that wsi prev == %p\n", + // __func__, + // wsi->vhost->same_vh_protocol_list[n], + // wsi->same_vh_protocol_prev); + wsi->same_vh_protocol_prev = /* guy who points to us */ + &wsi->vhost->same_vh_protocol_list[n]; + wsi->same_vh_protocol_next = /* old first guy is our next */ + wsi->vhost->same_vh_protocol_list[n]; + /* we become the new first guy */ + wsi->vhost->same_vh_protocol_list[n] = wsi; + + if (wsi->same_vh_protocol_next) + /* old first guy points back to us now */ + wsi->same_vh_protocol_next->same_vh_protocol_prev = + &wsi->same_vh_protocol_next; + +#ifndef LWS_NO_EXTENSIONS + /* instantiate the accepted extensions */ + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) { + lwsl_ext("no client extensions allowed by server\n"); + goto check_accept; + } + + /* + * break down the list of server accepted extensions + * and go through matching them or identifying bogons + */ + + if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size, WSI_TOKEN_EXTENSIONS) < 0) { + lwsl_warn("ext list from server failed to copy\n"); + cce = "HS: EXT: list too big"; + goto bail2; + } + + c = sb; + n = 0; + ignore = 0; + a = NULL; + while (more) { + + if (*c && (*c != ',' && *c != '\t')) { + if (*c == ';') { + ignore = 1; + if (!a) + a = c + 1; + } + if (ignore || *c == ' ') { + c++; + continue; + } + + ext_name[n] = *c++; + if (n < sizeof(ext_name) - 1) + n++; + continue; + } + ext_name[n] = '\0'; + ignore = 0; + if (!*c) + more = 0; + else { + c++; + if (!n) + continue; + } + + /* check we actually support it */ + + lwsl_notice("checking client ext %s\n", ext_name); + + n = 0; + ext = wsi->vhost->extensions; + while (ext && ext->callback) { + if (strcmp(ext_name, ext->name)) { + ext++; + continue; + } + + n = 1; + lwsl_notice("instantiating client ext %s\n", ext_name); + + /* instantiate the extension on this conn */ + + wsi->active_extensions[wsi->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->act_ext_user[wsi->count_act_ext], + (void *)&opts, 0)) { + lwsl_info(" ext %s failed construction\n", ext_name); + ext++; + continue; + } + + /* + * allow the user code to override ext defaults if it + * wants to + */ + ext_name[0] = '\0'; + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_WS_EXT_DEFAULTS, + (char *)ext->name, ext_name, + sizeof(ext_name))) { + cce = "HS: EXT: failed setting defaults"; + goto bail2; + } + + if (ext_name[0] && + lws_ext_parse_options(ext, wsi, wsi->act_ext_user[ + wsi->count_act_ext], opts, ext_name, + strlen(ext_name))) { + lwsl_err("%s: unable to parse user defaults '%s'", + __func__, ext_name); + cce = "HS: EXT: failed parsing defaults"; + goto bail2; + } + + /* + * give the extension the server options + */ + if (a && lws_ext_parse_options(ext, wsi, + wsi->act_ext_user[wsi->count_act_ext], + opts, a, c - a)) { + lwsl_err("%s: unable to parse remote def '%s'", + __func__, a); + cce = "HS: EXT: failed parsing options"; + goto bail2; + } + + if (ext->callback(lws_get_context(wsi), ext, wsi, + LWS_EXT_CB_OPTION_CONFIRM, + wsi->act_ext_user[wsi->count_act_ext], + NULL, 0)) { + lwsl_err("%s: ext %s rejects server options %s", + __func__, ext->name, a); + cce = "HS: EXT: Rejects server options"; + goto bail2; + } + + wsi->count_act_ext++; + + ext++; + } + + if (n == 0) { + lwsl_warn("Unknown ext '%s'!\n", ext_name); + cce = "HS: EXT: unknown ext"; + goto bail2; + } + + a = NULL; + n = 0; + } + +check_accept: +#endif + + /* + * Confirm his accept token is the one we precomputed + */ + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT); + if (strcmp(p, wsi->u.hdr.ah->initial_handshake_hash_base64)) { + lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p, + wsi->u.hdr.ah->initial_handshake_hash_base64); + cce = "HS: Accept hash wrong"; + goto bail2; + } + + /* allocate the per-connection user memory (if any) */ + if (lws_ensure_user_space(wsi)) { + lwsl_err("Problem allocating wsi user mem\n"); + cce = "HS: OOM"; + goto bail2; + } + + /* + * we seem to be good to go, give client last chance to check + * headers and OK it + */ + if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, + wsi->user_space, NULL, 0)) { + cce = "HS: Rejected by filter cb"; + goto bail2; + } + + /* clear his proxy connection timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* free up his parsing allocations */ + lws_header_table_detach(wsi, 0); + + lws_union_transition(wsi, LWSCM_WS_CLIENT); + wsi->state = LWSS_ESTABLISHED; + lws_restart_ws_ping_pong_timer(wsi); + + wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* + * create the frame buffer for this connection according to the + * size mentioned in the protocol definition. If 0 there, then + * use a big default for compatibility + */ + n = wsi->protocol->rx_buffer_size; + if (!n) + n = context->pt_serv_buf_size; + n += LWS_PRE; + wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "client frame buffer"); + if (!wsi->u.ws.rx_ubuf) { + lwsl_err("Out of Mem allocating rx buffer %d\n", n); + cce = "HS: OOM"; + goto bail2; + } + wsi->u.ws.rx_ubuf_alloc = n; + lwsl_info("Allocating client RX buffer %d\n", n); + +#if !defined(LWS_WITH_ESP32) + if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&n, + sizeof n)) { + lwsl_warn("Failed to set SNDBUF to %d", n); + cce = "HS: SO_SNDBUF failed"; + goto bail3; + } +#endif + + lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name); + + /* call him back to inform him he is up */ + + if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED, + wsi->user_space, NULL, 0)) { + cce = "HS: Rejected at CLIENT_ESTABLISHED"; + goto bail3; + } +#ifndef LWS_NO_EXTENSIONS + /* + * inform all extensions, not just active ones since they + * already know + */ + ext = wsi->vhost->extensions; + + while (ext && ext->callback) { + v = NULL; + for (n = 0; n < wsi->count_act_ext; n++) + if (wsi->active_extensions[n] == ext) + v = wsi->act_ext_user[n]; + + ext->callback(context, ext, wsi, + LWS_EXT_CB_ANY_WSI_ESTABLISHED, v, NULL, 0); + ext++; + } +#endif + + return 0; + +bail3: + close_reason = LWS_CLOSE_STATUS_NOSTATUS; + +bail2: + if (wsi->protocol) + wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, + (unsigned int)strlen(cce)); + wsi->already_did_cce = 1; + + lwsl_info("closing connection due to bail2 connection error\n"); + + /* closing will free up his parsing allocations */ + lws_close_free_wsi(wsi, close_reason); + + return 1; +} + + +char * +lws_generate_client_handshake(struct lws *wsi, char *pkt) +{ + char buf[128], hash[20], key_b64[40], *p = pkt; + struct lws_context *context = wsi->context; + const char *meth; + int n; +#ifndef LWS_NO_EXTENSIONS + const struct lws_extension *ext; + int ext_count = 0; +#endif + const char *pp = lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); + + meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); + if (!meth) { + meth = "GET"; + wsi->do_ws = 1; + } else { + wsi->do_ws = 0; + } + + if (!strcmp(meth, "RAW")) { + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + lwsl_notice("client transition to raw\n"); + + if (pp) { + const struct lws_protocols *pr; + + pr = lws_vhost_name_to_protocol(wsi->vhost, pp); + + if (!pr) { + lwsl_err("protocol %s not enabled on vhost\n", + pp); + return NULL; + } + + lws_bind_protocol(wsi, pr); + } + + if ((wsi->protocol->callback)(wsi, + LWS_CALLBACK_RAW_ADOPT, + wsi->user_space, NULL, 0)) + return NULL; + + lws_header_table_force_to_detachable_state(wsi); + lws_union_transition(wsi, LWSCM_RAW); + lws_header_table_detach(wsi, 1); + + return NULL; + } + + if (wsi->do_ws) { + /* + * create the random key + */ + n = lws_get_random(context, hash, 16); + if (n != 16) { + lwsl_err("Unable to read from random dev %s\n", + SYSTEM_RANDOM_FILEPATH); + return NULL; + } + + lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64)); + } + + /* + * 04 example client handshake + * + * GET /chat HTTP/1.1 + * Host: server.example.com + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== + * Sec-WebSocket-Origin: http://example.com + * Sec-WebSocket-Protocol: chat, superchat + * Sec-WebSocket-Version: 4 + */ + + p += sprintf(p, "%s %s HTTP/1.1\x0d\x0a", meth, + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI)); + + p += sprintf(p, "Pragma: no-cache\x0d\x0a" + "Cache-Control: no-cache\x0d\x0a"); + + p += sprintf(p, "Host: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST)); + + if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) { + if (lws_check_opt(context->options, LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN)) + p += sprintf(p, "Origin: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); + else + p += sprintf(p, "Origin: http://%s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); + } + + if (wsi->do_ws) { + p += sprintf(p, "Upgrade: websocket\x0d\x0a" + "Connection: Upgrade\x0d\x0a" + "Sec-WebSocket-Key: "); + strcpy(p, key_b64); + p += strlen(key_b64); + p += sprintf(p, "\x0d\x0a"); + if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)) + p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)); + + /* tell the server what extensions we could support */ + +#ifndef LWS_NO_EXTENSIONS + ext = wsi->vhost->extensions; + while (ext && ext->callback) { + n = lws_ext_cb_all_exts(context, wsi, + LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION, + (char *)ext->name, 0); + if (n) { /* an extension vetos us */ + lwsl_ext("ext %s vetoed\n", (char *)ext->name); + ext++; + continue; + } + n = wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, + wsi->user_space, (char *)ext->name, 0); + + /* + * zero return from callback means + * go ahead and allow the extension, + * it's what we get if the callback is + * unhandled + */ + + if (n) { + ext++; + continue; + } + + /* apply it */ + + if (ext_count) + *p++ = ','; + else + p += sprintf(p, "Sec-WebSocket-Extensions: "); + p += sprintf(p, "%s", ext->client_offer); + ext_count++; + + ext++; + } + if (ext_count) + p += sprintf(p, "\x0d\x0a"); +#endif + + if (wsi->ietf_spec_revision) + p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a", + wsi->ietf_spec_revision); + + /* prepare the expected server accept response */ + + key_b64[39] = '\0'; /* enforce composed length below buf sizeof */ + n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64); + + lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash); + + lws_b64_encode_string(hash, 20, + wsi->u.hdr.ah->initial_handshake_hash_base64, + sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64)); + } + + /* give userland a chance to append, eg, cookies */ + + if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, + wsi->user_space, &p, (pkt + context->pt_serv_buf_size) - p - 12)) + return NULL; + + p += sprintf(p, "\x0d\x0a"); + + return p; +} + diff -Nru libwebsockets-4.0.20/lib/client/client-handshake.c libwebsockets-2.4.2/lib/client/client-handshake.c --- libwebsockets-4.0.20/lib/client/client-handshake.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/client/client-handshake.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1051 @@ +#include "private-libwebsockets.h" + +static int +lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result) +{ + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + *result = NULL; + +#ifdef LWS_WITH_IPV6 + if (wsi->ipv6) { + +#if !defined(__ANDROID__) + hints.ai_family = AF_INET6; + hints.ai_flags = AI_V4MAPPED; +#endif + } else +#endif + { + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + } + + return getaddrinfo(ads, NULL, &hints, result); +} + +struct lws * +lws_client_connect_2(struct lws *wsi) +{ + sockaddr46 sa46; + struct addrinfo *result; + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pollfd pfd; + const char *cce = "", *iface; + int n, port; + ssize_t plen = 0; + const char *ads; +#ifdef LWS_WITH_IPV6 + char ipv6only = lws_check_opt(wsi->vhost->options, + LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY | + LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE); + +#if defined(__ANDROID__) + ipv6only = 0; +#endif +#endif + + lwsl_client("%s\n", __func__); + + if (!wsi->u.hdr.ah) { + cce = "ah was NULL at cc2"; + lwsl_err("%s\n", cce); + goto oom4; + } + + /* + * start off allowing ipv6 on connection if vhost allows it + */ + wsi->ipv6 = LWS_IPV6_ENABLED(wsi->vhost); + + /* Decide what it is we need to connect to: + * + * Priority 1: connect to http proxy */ + + if (wsi->vhost->http_proxy_port) { + plen = sprintf((char *)pt->serv_buf, + "CONNECT %s:%u HTTP/1.0\x0d\x0a" + "User-agent: libwebsockets\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS), + wsi->c_port); + + if (wsi->vhost->proxy_basic_auth_token[0]) + plen += sprintf((char *)pt->serv_buf + plen, + "Proxy-authorization: basic %s\x0d\x0a", + wsi->vhost->proxy_basic_auth_token); + + plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a"); + ads = wsi->vhost->http_proxy_address; + port = wsi->vhost->http_proxy_port; + +#if defined(LWS_WITH_SOCKS5) + + /* Priority 2: Connect to SOCK5 Proxy */ + + } else if (wsi->vhost->socks_proxy_port) { + socks_generate_msg(wsi, SOCKS_MSG_GREETING, &plen); + lwsl_client("Sending SOCKS Greeting\n"); + ads = wsi->vhost->socks_proxy_address; + port = wsi->vhost->socks_proxy_port; +#endif + } else { + + /* Priority 3: Connect directly */ + + ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); + port = wsi->c_port; + } + + /* + * prepare the actual connection + * to whatever we decided to connect to + */ + + lwsl_notice("%s: %p: address %s\n", __func__, wsi, ads); + + n = lws_getaddrinfo46(wsi, ads, &result); + +#ifdef LWS_WITH_IPV6 + if (wsi->ipv6) { + + if (n) { + /* lws_getaddrinfo46 failed, there is no usable result */ + lwsl_notice("%s: lws_getaddrinfo46 failed %d\n", + __func__, n); + cce = "ipv6 lws_getaddrinfo46 failed"; + goto oom4; + } + + memset(&sa46, 0, sizeof(sa46)); + + sa46.sa6.sin6_family = AF_INET6; + switch (result->ai_family) { + case AF_INET: + if (ipv6only) + break; + /* map IPv4 to IPv6 */ + bzero((char *)&sa46.sa6.sin6_addr, + sizeof(sa46.sa6.sin6_addr)); + sa46.sa6.sin6_addr.s6_addr[10] = 0xff; + sa46.sa6.sin6_addr.s6_addr[11] = 0xff; + memcpy(&sa46.sa6.sin6_addr.s6_addr[12], + &((struct sockaddr_in *)result->ai_addr)->sin_addr, + sizeof(struct in_addr)); + lwsl_notice("uplevelling AF_INET to AF_INET6\n"); + break; + + case AF_INET6: + memcpy(&sa46.sa6.sin6_addr, + &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr, + sizeof(struct in6_addr)); + sa46.sa6.sin6_scope_id = ((struct sockaddr_in6 *)result->ai_addr)->sin6_scope_id; + sa46.sa6.sin6_flowinfo = ((struct sockaddr_in6 *)result->ai_addr)->sin6_flowinfo; + break; + default: + lwsl_err("Unknown address family\n"); + freeaddrinfo(result); + cce = "unknown address family"; + goto oom4; + } + } else +#endif /* use ipv6 */ + + /* use ipv4 */ + { + void *p = NULL; + + if (!n) { + struct addrinfo *res = result; + + /* pick the first AF_INET (IPv4) result */ + + while (!p && res) { + switch (res->ai_family) { + case AF_INET: + p = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + break; + } + + res = res->ai_next; + } +#if defined(LWS_FALLBACK_GETHOSTBYNAME) + } else if (n == EAI_SYSTEM) { + struct hostent *host; + + lwsl_info("getaddrinfo (ipv4) failed, trying gethostbyname\n"); + host = gethostbyname(ads); + if (host) { + p = host->h_addr; + } else { + lwsl_err("gethostbyname failed\n"); + cce = "gethostbyname (ipv4) failed"; + goto oom4; + } +#endif + } else { + lwsl_err("getaddrinfo failed\n"); + cce = "getaddrinfo failed"; + goto oom4; + } + + if (!p) { + if (result) + freeaddrinfo(result); + lwsl_err("Couldn't identify address\n"); + cce = "unable to lookup address"; + goto oom4; + } + + sa46.sa4.sin_family = AF_INET; + sa46.sa4.sin_addr = *((struct in_addr *)p); + bzero(&sa46.sa4.sin_zero, 8); + } + + if (result) + freeaddrinfo(result); + + /* now we decided on ipv4 or ipv6, set the port */ + + if (!lws_socket_is_valid(wsi->desc.sockfd)) { + +#if defined(LWS_WITH_LIBUV) + if (LWS_LIBUV_ENABLED(context)) + if (lws_libuv_check_watcher_active(wsi)) { + lwsl_warn("Waiting for libuv watcher to close\n"); + cce = "waiting for libuv watcher to close"; + goto oom4; + } +#endif + +#ifdef LWS_WITH_IPV6 + if (wsi->ipv6) + wsi->desc.sockfd = socket(AF_INET6, SOCK_STREAM, 0); + else +#endif + wsi->desc.sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (!lws_socket_is_valid(wsi->desc.sockfd)) { + lwsl_warn("Unable to open socket\n"); + cce = "unable to open socket"; + goto oom4; + } + + if (lws_plat_set_socket_options(wsi->vhost, wsi->desc.sockfd)) { + lwsl_err("Failed to set wsi socket options\n"); + compatible_close(wsi->desc.sockfd); + cce = "set socket opts failed"; + goto oom4; + } + + wsi->mode = LWSCM_WSCL_WAITING_CONNECT; + + lws_libev_accept(wsi, wsi->desc); + lws_libuv_accept(wsi, wsi->desc); + lws_libevent_accept(wsi, wsi->desc); + + if (insert_wsi_socket_into_fds(context, wsi)) { + compatible_close(wsi->desc.sockfd); + cce = "insert wsi failed"; + goto oom4; + } + + lws_change_pollfd(wsi, 0, LWS_POLLIN); + + /* + * past here, we can't simply free the structs as error + * handling as oom4 does. We have to run the whole close flow. + */ + + if (!wsi->protocol) + wsi->protocol = &wsi->vhost->protocols[0]; + + wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE, + wsi->user_space, NULL, 0); + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, + AWAITING_TIMEOUT); + + iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); + + if (iface) { + n = lws_socket_bind(wsi->vhost, wsi->desc.sockfd, 0, iface); + if (n < 0) { + cce = "unable to bind socket"; + goto failed; + } + } + } + +#ifdef LWS_WITH_IPV6 + if (wsi->ipv6) { + sa46.sa6.sin6_port = htons(port); + n = sizeof(struct sockaddr_in6); + } else +#endif + { + sa46.sa4.sin_port = htons(port); + n = sizeof(struct sockaddr); + } + + if (connect(wsi->desc.sockfd, (const struct sockaddr *)&sa46, n) == -1 || + LWS_ERRNO == LWS_EISCONN) { + if (LWS_ERRNO == LWS_EALREADY || + LWS_ERRNO == LWS_EINPROGRESS || + LWS_ERRNO == LWS_EWOULDBLOCK +#ifdef _WIN32 + || LWS_ERRNO == WSAEINVAL +#endif + ) { + lwsl_client("nonblocking connect retry (errno = %d)\n", + LWS_ERRNO); + + if (lws_plat_check_connection_error(wsi)) { + cce = "socket connect failed"; + goto failed; + } + + /* + * must do specifically a POLLOUT poll to hear + * about the connect completion + */ + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { + cce = "POLLOUT set failed"; + goto failed; + } + + return wsi; + } + + if (LWS_ERRNO != LWS_EISCONN) { + lwsl_notice("Connect failed errno=%d\n", LWS_ERRNO); + cce = "connect failed"; + goto failed; + } + } + + lwsl_client("connected\n"); + + /* we are connected to server, or proxy */ + + /* http proxy */ + if (wsi->vhost->http_proxy_port) { + + /* + * OK from now on we talk via the proxy, so connect to that + * + * (will overwrite existing pointer, + * leaving old string/frag there but unreferenced) + */ + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, + wsi->vhost->http_proxy_address)) + goto failed; + wsi->c_port = wsi->vhost->http_proxy_port; + + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing to proxy socket\n"); + cce = "proxy write failed"; + goto failed; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_WAITING_PROXY_REPLY; + + return wsi; + } +#if defined(LWS_WITH_SOCKS5) + /* socks proxy */ + else if (wsi->vhost->socks_proxy_port) { + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing socks greeting\n"); + cce = "socks write failed"; + goto failed; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY; + + return wsi; + } +#endif + + /* + * provoke service to issue the handshake directly + * we need to do it this way because in the proxy case, this is the + * next state and executed only if and when we get a good proxy + * response inside the state machine... but notice in SSL case this + * may not have sent anything yet with 0 return, and won't until some + * many retries from main loop. To stop that becoming endless, + * cover with a timeout. + */ + + lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE; + pfd.fd = wsi->desc.sockfd; + pfd.events = LWS_POLLIN; + pfd.revents = LWS_POLLIN; + + n = lws_service_fd(context, &pfd); + if (n < 0) { + cce = "first service failed"; + goto failed; + } + if (n) /* returns 1 on failure after closing wsi */ + return NULL; + + return wsi; + +oom4: + /* we're closing, losing some rx is OK */ + lws_header_table_force_to_detachable_state(wsi); + + if (wsi->mode == LWSCM_HTTP_CLIENT || + wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED || + wsi->mode == LWSCM_WSCL_WAITING_CONNECT) { + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, strlen(cce)); + wsi->already_did_cce = 1; + } + /* take care that we might be inserted in fds already */ + if (wsi->position_in_fds_table != -1) + goto failed1; + lws_remove_from_timeout_list(wsi); + lws_header_table_detach(wsi, 0); + lws_free(wsi); + + return NULL; + +failed: + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, strlen(cce)); + wsi->already_did_cce = 1; +failed1: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return NULL; +} + +/** + * lws_client_reset() - retarget a connected wsi to start over with a new connection (ie, redirect) + * this only works if still in HTTP, ie, not upgraded yet + * wsi: connection to reset + * address: network address of the new server + * port: port to connect to + * path: uri path to connect to on the new server + * host: host header to send to the new server + */ +LWS_VISIBLE struct lws * +lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, + const char *path, const char *host) +{ + char origin[300] = "", protocol[300] = "", method[32] = "", iface[16] = "", *p; + struct lws *wsi = *pwsi; + + if (wsi->redirects == 3) { + lwsl_err("%s: Too many redirects\n", __func__); + return NULL; + } + wsi->redirects++; + + p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN); + if (p) + strncpy(origin, p, sizeof(origin) - 1); + + p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); + if (p) + strncpy(protocol, p, sizeof(protocol) - 1); + + p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); + if (p) + strncpy(method, p, sizeof(method) - 1); + + p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); + if (p) + strncpy(method, p, sizeof(iface) - 1); + + lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d\n", + address, port, path, ssl); + + /* close the connection by hand */ + +#ifdef LWS_OPENSSL_SUPPORT + lws_ssl_close(wsi); +#endif + +#ifdef LWS_WITH_LIBUV + if (LWS_LIBUV_ENABLED(wsi->context)) { + lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); + /* + * libuv has to do his own close handle processing asynchronously + * but once it starts we can do everything else synchronously, + * including trash wsi->desc.sockfd since it took a copy. + * + * When it completes it will call compatible_close() + */ + lws_libuv_closehandle_manually(wsi); + } else +#else + compatible_close(wsi->desc.sockfd); +#endif + + remove_wsi_socket_from_fds(wsi); + +#ifdef LWS_OPENSSL_SUPPORT + wsi->use_ssl = ssl; +#else + if (ssl) { + lwsl_err("%s: not configured for ssl\n", __func__); + return NULL; + } +#endif + + wsi->desc.sockfd = LWS_SOCK_INVALID; + wsi->state = LWSS_CLIENT_UNCONNECTED; + wsi->protocol = NULL; + wsi->pending_timeout = NO_PENDING_TIMEOUT; + wsi->c_port = port; + wsi->hdr_parsing_completed = 0; + _lws_header_table_reset(wsi->u.hdr.ah); + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address)) + return NULL; + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host)) + return NULL; + + if (origin[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, + origin)) + return NULL; + if (protocol[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, + protocol)) + return NULL; + if (method[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, + method)) + return NULL; + + if (iface[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, + iface)) + return NULL; + + origin[0] = '/'; + strncpy(&origin[1], path, sizeof(origin) - 2); + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, origin)) + return NULL; + + *pwsi = lws_client_connect_2(wsi); + + return *pwsi; +} + +#ifdef LWS_WITH_HTTP_PROXY +static hubbub_error +html_parser_cb(const hubbub_token *token, void *pw) +{ + struct lws_rewrite *r = (struct lws_rewrite *)pw; + char buf[1024], *start = buf + LWS_PRE, *p = start, + *end = &buf[sizeof(buf) - 1]; + size_t i; + + switch (token->type) { + case HUBBUB_TOKEN_DOCTYPE: + + p += lws_snprintf(p, end - p, "data.doctype.name.len, + token->data.doctype.name.ptr, + token->data.doctype.force_quirks ? + "(force-quirks) " : ""); + + if (token->data.doctype.public_missing) + lwsl_debug("\tpublic: missing\n"); + else + p += lws_snprintf(p, end - p, "PUBLIC \"%.*s\"\n", + (int) token->data.doctype.public_id.len, + token->data.doctype.public_id.ptr); + + if (token->data.doctype.system_missing) + lwsl_debug("\tsystem: missing\n"); + else + p += lws_snprintf(p, end - p, " \"%.*s\">\n", + (int) token->data.doctype.system_id.len, + token->data.doctype.system_id.ptr); + + break; + case HUBBUB_TOKEN_START_TAG: + p += lws_snprintf(p, end - p, "<%.*s", (int)token->data.tag.name.len, + token->data.tag.name.ptr); + +/* (token->data.tag.self_closing) ? + "(self-closing) " : "", + (token->data.tag.n_attributes > 0) ? + "attributes:" : ""); +*/ + for (i = 0; i < token->data.tag.n_attributes; i++) { + if (!hstrcmp(&token->data.tag.attributes[i].name, "href", 4) || + !hstrcmp(&token->data.tag.attributes[i].name, "action", 6) || + !hstrcmp(&token->data.tag.attributes[i].name, "src", 3)) { + const char *pp = (const char *)token->data.tag.attributes[i].value.ptr; + int plen = (int) token->data.tag.attributes[i].value.len; + + if (strncmp(pp, "http:", 5) && strncmp(pp, "https:", 6)) { + + if (!hstrcmp(&token->data.tag.attributes[i].value, + r->from, r->from_len)) { + pp += r->from_len; + plen -= r->from_len; + } + p += lws_snprintf(p, end - p, " %.*s=\"%s/%.*s\"", + (int) token->data.tag.attributes[i].name.len, + token->data.tag.attributes[i].name.ptr, + r->to, plen, pp); + continue; + } + } + + p += lws_snprintf(p, end - p, " %.*s=\"%.*s\"", + (int) token->data.tag.attributes[i].name.len, + token->data.tag.attributes[i].name.ptr, + (int) token->data.tag.attributes[i].value.len, + token->data.tag.attributes[i].value.ptr); + } + p += lws_snprintf(p, end - p, ">"); + break; + case HUBBUB_TOKEN_END_TAG: + p += lws_snprintf(p, end - p, "data.tag.name.len, + token->data.tag.name.ptr); +/* + (token->data.tag.self_closing) ? + "(self-closing) " : "", + (token->data.tag.n_attributes > 0) ? + "attributes:" : ""); +*/ + for (i = 0; i < token->data.tag.n_attributes; i++) { + p += lws_snprintf(p, end - p, " %.*s='%.*s'\n", + (int) token->data.tag.attributes[i].name.len, + token->data.tag.attributes[i].name.ptr, + (int) token->data.tag.attributes[i].value.len, + token->data.tag.attributes[i].value.ptr); + } + p += lws_snprintf(p, end - p, ">"); + break; + case HUBBUB_TOKEN_COMMENT: + p += lws_snprintf(p, end - p, "\n", + (int) token->data.comment.len, + token->data.comment.ptr); + break; + case HUBBUB_TOKEN_CHARACTER: + if (token->data.character.len == 1) { + if (*token->data.character.ptr == '<') { + p += lws_snprintf(p, end - p, "<"); + break; + } + if (*token->data.character.ptr == '>') { + p += lws_snprintf(p, end - p, ">"); + break; + } + if (*token->data.character.ptr == '&') { + p += lws_snprintf(p, end - p, "&"); + break; + } + } + + p += lws_snprintf(p, end - p, "%.*s", (int) token->data.character.len, + token->data.character.ptr); + break; + case HUBBUB_TOKEN_EOF: + p += lws_snprintf(p, end - p, "\n"); + break; + } + + if (user_callback_handle_rxflow(r->wsi->protocol->callback, + r->wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, + r->wsi->user_space, start, p - start)) + return -1; + + return HUBBUB_OK; +} +#endif + +LWS_VISIBLE struct lws * +lws_client_connect_via_info(struct lws_client_connect_info *i) +{ + struct lws *wsi; + int v = SPEC_LATEST_SUPPORTED; + const struct lws_protocols *p; + + if (i->context->requested_kill) + return NULL; + + if (!i->context->protocol_init_done) + lws_protocol_init(i->context); + + wsi = lws_zalloc(sizeof(struct lws), "client wsi"); + if (wsi == NULL) + goto bail; + + wsi->context = i->context; + /* assert the mode and union status (hdr) clearly */ + lws_union_transition(wsi, LWSCM_HTTP_CLIENT); + wsi->desc.sockfd = LWS_SOCK_INVALID; + + /* 1) fill up the wsi with stuff from the connect_info as far as it + * can go. It's because not only is our connection async, we might + * not even be able to get ahold of an ah at this point. + */ + + /* -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->ietf_spec_revision = v; + wsi->user_space = NULL; + wsi->state = LWSS_CLIENT_UNCONNECTED; + wsi->pending_timeout = NO_PENDING_TIMEOUT; + wsi->position_in_fds_table = -1; + wsi->c_port = i->port; + wsi->vhost = i->vhost; + if (!wsi->vhost) + wsi->vhost = i->context->vhost_list; + + wsi->protocol = &wsi->vhost->protocols[0]; + + /* for http[s] connection, allow protocol selection by name */ + + if (i->method && i->vhost && i->protocol) { + p = lws_vhost_name_to_protocol(i->vhost, i->protocol); + if (p) + wsi->protocol = p; + } + + if (wsi && !wsi->user_space && i->userdata) { + wsi->user_space_externally_allocated = 1; + wsi->user_space = i->userdata; + } else + /* if we stay in http, we can assign the user space now, + * otherwise do it after the protocol negotiated + */ + if (i->method) + if (lws_ensure_user_space(wsi)) + goto bail; + +#ifdef LWS_OPENSSL_SUPPORT + wsi->use_ssl = i->ssl_connection; +#else + if (i->ssl_connection) { + lwsl_err("libwebsockets not configured for ssl\n"); + goto bail; + } +#endif + + /* 2) stash the things from connect_info that we can't process without + * an ah. Because if no ah, we will go on the ah waiting list and + * process those things later (after the connect_info and maybe the + * things pointed to have gone out of scope. + */ + + wsi->u.hdr.stash = lws_malloc(sizeof(*wsi->u.hdr.stash), "client stash"); + if (!wsi->u.hdr.stash) { + lwsl_err("%s: OOM\n", __func__); + goto bail; + } + + wsi->u.hdr.stash->origin[0] = '\0'; + wsi->u.hdr.stash->protocol[0] = '\0'; + wsi->u.hdr.stash->method[0] = '\0'; + wsi->u.hdr.stash->iface[0] = '\0'; + + strncpy(wsi->u.hdr.stash->address, i->address, + sizeof(wsi->u.hdr.stash->address) - 1); + strncpy(wsi->u.hdr.stash->path, i->path, + sizeof(wsi->u.hdr.stash->path) - 1); + strncpy(wsi->u.hdr.stash->host, i->host, + sizeof(wsi->u.hdr.stash->host) - 1); + if (i->origin) + strncpy(wsi->u.hdr.stash->origin, i->origin, + sizeof(wsi->u.hdr.stash->origin) - 1); + if (i->protocol) + strncpy(wsi->u.hdr.stash->protocol, i->protocol, + sizeof(wsi->u.hdr.stash->protocol) - 1); + if (i->method) + strncpy(wsi->u.hdr.stash->method, i->method, + sizeof(wsi->u.hdr.stash->method) - 1); + if (i->iface) + strncpy(wsi->u.hdr.stash->iface, i->iface, + sizeof(wsi->u.hdr.stash->iface) - 1); + + wsi->u.hdr.stash->address[sizeof(wsi->u.hdr.stash->address) - 1] = '\0'; + wsi->u.hdr.stash->path[sizeof(wsi->u.hdr.stash->path) - 1] = '\0'; + wsi->u.hdr.stash->host[sizeof(wsi->u.hdr.stash->host) - 1] = '\0'; + wsi->u.hdr.stash->origin[sizeof(wsi->u.hdr.stash->origin) - 1] = '\0'; + wsi->u.hdr.stash->protocol[sizeof(wsi->u.hdr.stash->protocol) - 1] = '\0'; + wsi->u.hdr.stash->method[sizeof(wsi->u.hdr.stash->method) - 1] = '\0'; + wsi->u.hdr.stash->iface[sizeof(wsi->u.hdr.stash->iface) - 1] = '\0'; + + if (i->pwsi) + *i->pwsi = wsi; + + /* if we went on the waiting list, no probs just return the wsi + * when we get the ah, now or later, he will call + * lws_client_connect_via_info2() below. + */ + if (lws_header_table_attach(wsi, 0) < 0) { + /* + * if we failed here, the connection is already closed + * and freed. + */ + goto bail1; + } + + if (i->parent_wsi) { + lwsl_info("%s: created child %p of parent %p\n", __func__, + wsi, i->parent_wsi); + wsi->parent = i->parent_wsi; + wsi->sibling_list = i->parent_wsi->child_list; + i->parent_wsi->child_list = wsi; + } +#ifdef LWS_WITH_HTTP_PROXY + if (i->uri_replace_to) + wsi->rw = lws_rewrite_create(wsi, html_parser_cb, + i->uri_replace_from, + i->uri_replace_to); +#endif + + return wsi; + +bail: + lws_free(wsi); + +bail1: + if (i->pwsi) + *i->pwsi = NULL; + + return NULL; +} + +struct lws * +lws_client_connect_via_info2(struct lws *wsi) +{ + struct client_info_stash *stash = wsi->u.hdr.stash; + + if (!stash) + return wsi; + + /* + * we're not necessarily in a position to action these right away, + * stash them... we only need during connect phase so u.hdr is fine + */ + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, + stash->address)) + goto bail1; + + /* these only need u.hdr lifetime as well */ + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path)) + goto bail1; + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host)) + goto bail1; + + if (stash->origin[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, + stash->origin)) + goto bail1; + /* + * this is a list of protocols we tell the server we're okay with + * stash it for later when we compare server response with it + */ + if (stash->protocol[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, + stash->protocol)) + goto bail1; + if (stash->method[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, + stash->method)) + goto bail1; + if (stash->iface[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, + stash->iface)) + goto bail1; + +#if defined(LWS_WITH_SOCKS5) + if (!wsi->vhost->socks_proxy_port) + lws_free_set_NULL(wsi->u.hdr.stash); +#endif + + /* + * Check with each extension if it is able to route and proxy this + * connection for us. For example, an extension like x-google-mux + * can handle this and then we don't need an actual socket for this + * connection. + */ + + if (lws_ext_cb_all_exts(wsi->context, wsi, + LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION, + (void *)stash->address, + wsi->c_port) > 0) { + lwsl_client("lws_client_connect: ext handling conn\n"); + + lws_set_timeout(wsi, + PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_WAITING_EXTENSION_CONNECT; + return wsi; + } + lwsl_client("lws_client_connect: direct conn\n"); + wsi->context->count_wsi_allocated++; + + return lws_client_connect_2(wsi); + +bail1: +#if defined(LWS_WITH_SOCKS5) + if (!wsi->vhost->socks_proxy_port) + lws_free_set_NULL(wsi->u.hdr.stash); +#endif + + return NULL; +} + +LWS_VISIBLE struct lws * +lws_client_connect_extended(struct lws_context *context, const char *address, + int port, int ssl_connection, const char *path, + const char *host, const char *origin, + const char *protocol, int ietf_version_or_minus_one, + void *userdata) +{ + struct lws_client_connect_info i; + + memset(&i, 0, sizeof(i)); + + i.context = context; + i.address = address; + i.port = port; + i.ssl_connection = ssl_connection; + i.path = path; + i.host = host; + i.origin = origin; + i.protocol = protocol; + i.ietf_version_or_minus_one = ietf_version_or_minus_one; + i.userdata = userdata; + + return lws_client_connect_via_info(&i); +} + +LWS_VISIBLE struct lws * +lws_client_connect(struct lws_context *context, const char *address, + int port, int ssl_connection, const char *path, + const char *host, const char *origin, + const char *protocol, int ietf_version_or_minus_one) +{ + struct lws_client_connect_info i; + + memset(&i, 0, sizeof(i)); + + i.context = context; + i.address = address; + i.port = port; + i.ssl_connection = ssl_connection; + i.path = path; + i.host = host; + i.origin = origin; + i.protocol = protocol; + i.ietf_version_or_minus_one = ietf_version_or_minus_one; + i.userdata = NULL; + + return lws_client_connect_via_info(&i); +} + +#if defined(LWS_WITH_SOCKS5) +void socks_generate_msg(struct lws *wsi, enum socks_msg_type type, + ssize_t *msg_len) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + ssize_t len = 0, n, passwd_len; + short net_num; + char *p; + + switch (type) { + case SOCKS_MSG_GREETING: + /* socks version, version 5 only */ + pt->serv_buf[len++] = SOCKS_VERSION_5; + /* number of methods */ + pt->serv_buf[len++] = 2; + /* username password method */ + pt->serv_buf[len++] = SOCKS_AUTH_USERNAME_PASSWORD; + /* no authentication method */ + pt->serv_buf[len++] = SOCKS_AUTH_NO_AUTH; + break; + + case SOCKS_MSG_USERNAME_PASSWORD: + n = strlen(wsi->vhost->socks_user); + passwd_len = strlen(wsi->vhost->socks_password); + + /* the subnegotiation version */ + pt->serv_buf[len++] = SOCKS_SUBNEGOTIATION_VERSION_1; + /* length of the user name */ + pt->serv_buf[len++] = n; + /* user name */ + strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user, + context->pt_serv_buf_size - len); + len += n; + /* length of the password */ + pt->serv_buf[len++] = passwd_len; + /* password */ + strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password, + context->pt_serv_buf_size - len); + len += passwd_len; + break; + + case SOCKS_MSG_CONNECT: + p = (char*)&net_num; + + /* socks version */ + pt->serv_buf[len++] = SOCKS_VERSION_5; + /* socks command */ + pt->serv_buf[len++] = SOCKS_COMMAND_CONNECT; + /* reserved */ + pt->serv_buf[len++] = 0; + /* address type */ + pt->serv_buf[len++] = SOCKS_ATYP_DOMAINNAME; + /* skip length, we fill it in at the end */ + n = len++; + + /* the address we tell SOCKS proxy to connect to */ + strncpy((char *)&(pt->serv_buf[len]), wsi->u.hdr.stash->address, + context->pt_serv_buf_size - len); + len += strlen(wsi->u.hdr.stash->address); + net_num = htons(wsi->c_port); + + /* the port we tell SOCKS proxy to connect to */ + pt->serv_buf[len++] = p[0]; + pt->serv_buf[len++] = p[1]; + + /* the length of the address, excluding port */ + pt->serv_buf[n] = strlen(wsi->u.hdr.stash->address); + break; + + default: + return; + } + + *msg_len = len; +} +#endif diff -Nru libwebsockets-4.0.20/lib/client/client-parser.c libwebsockets-2.4.2/lib/client/client-parser.c --- libwebsockets-4.0.20/lib/client/client-parser.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/client/client-parser.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,598 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +/* + * parsers.c: lws_rx_sm() needs to be roughly kept in + * sync with changes here, esp related to ext draining + */ + +int lws_client_rx_sm(struct lws *wsi, unsigned char c) +{ + int callback_action = LWS_CALLBACK_CLIENT_RECEIVE; + int handled, n, m, rx_draining_ext = 0; + unsigned short close_code; + struct lws_tokens eff_buf; + unsigned char *pp; + + if (wsi->u.ws.rx_draining_ext) { + assert(!c); + eff_buf.token = NULL; + eff_buf.token_len = 0; + lws_remove_wsi_from_draining_ext_list(wsi); + rx_draining_ext = 1; + lwsl_debug("%s: doing draining flow\n", __func__); + + goto drain_extension; + } + + if (wsi->socket_is_permanently_unusable) + return -1; + + switch (wsi->lws_rx_parse_state) { + case LWS_RXPS_NEW: + /* control frames (PING) may interrupt checkable sequences */ + wsi->u.ws.defeat_check_utf8 = 0; + + switch (wsi->ietf_spec_revision) { + case 13: + wsi->u.ws.opcode = c & 0xf; + /* revisit if an extension wants them... */ + switch (wsi->u.ws.opcode) { + case LWSWSOPC_TEXT_FRAME: + wsi->u.ws.rsv_first_msg = (c & 0x70); + wsi->u.ws.continuation_possible = 1; + wsi->u.ws.check_utf8 = lws_check_opt( + wsi->context->options, + LWS_SERVER_OPTION_VALIDATE_UTF8); + wsi->u.ws.utf8 = 0; + break; + case LWSWSOPC_BINARY_FRAME: + wsi->u.ws.rsv_first_msg = (c & 0x70); + wsi->u.ws.check_utf8 = 0; + wsi->u.ws.continuation_possible = 1; + break; + case LWSWSOPC_CONTINUATION: + if (!wsi->u.ws.continuation_possible) { + lwsl_info("disordered continuation\n"); + return -1; + } + break; + case LWSWSOPC_CLOSE: + wsi->u.ws.check_utf8 = 0; + wsi->u.ws.utf8 = 0; + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + lwsl_info("illegal opcode\n"); + return -1; + default: + wsi->u.ws.defeat_check_utf8 = 1; + break; + } + wsi->u.ws.rsv = (c & 0x70); + /* revisit if an extension wants them... */ + if ( +#ifndef LWS_NO_EXTENSIONS + !wsi->count_act_ext && +#endif + wsi->u.ws.rsv) { + lwsl_info("illegal rsv bits set\n"); + return -1; + } + wsi->u.ws.final = !!((c >> 7) & 1); + lwsl_ext("%s: This RX frame Final %d\n", __func__, + wsi->u.ws.final); + + if (wsi->u.ws.owed_a_fin && + (wsi->u.ws.opcode == LWSWSOPC_TEXT_FRAME || + wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME)) { + lwsl_info("hey you owed us a FIN\n"); + return -1; + } + if ((!(wsi->u.ws.opcode & 8)) && wsi->u.ws.final) { + wsi->u.ws.continuation_possible = 0; + wsi->u.ws.owed_a_fin = 0; + } + + if ((wsi->u.ws.opcode & 8) && !wsi->u.ws.final) { + lwsl_info("control msg can't be fragmented\n"); + return -1; + } + if (!wsi->u.ws.final) + wsi->u.ws.owed_a_fin = 1; + + switch (wsi->u.ws.opcode) { + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + wsi->u.ws.frame_is_binary = wsi->u.ws.opcode == + LWSWSOPC_BINARY_FRAME; + break; + } + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN; + break; + + default: + lwsl_err("unknown spec version %02d\n", + wsi->ietf_spec_revision); + break; + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN: + + wsi->u.ws.this_frame_masked = !!(c & 0x80); + + switch (c & 0x7f) { + case 126: + /* control frames are not allowed to have big lengths */ + if (wsi->u.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->u.ws.opcode & 8) + goto illegal_ctl_length; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8; + break; + default: + wsi->u.ws.rx_packet_length = c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else { + if (c) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + } + break; + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_2: + wsi->u.ws.rx_packet_length = c << 8; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_1: + wsi->u.ws.rx_packet_length |= c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else { + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_8: + if (c & 0x80) { + lwsl_warn("b63 of length must be zero\n"); + /* kill the connection */ + return -1; + } +#if defined __LP64__ + wsi->u.ws.rx_packet_length = ((size_t)c) << 56; +#else + wsi->u.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->u.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->u.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->u.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->u.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->u.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->u.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->u.ws.rx_packet_length |= (size_t)c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else { + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + } + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_1: + wsi->u.ws.mask[0] = c; + if (c) + wsi->u.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->u.ws.mask[1] = c; + if (c) + wsi->u.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->u.ws.mask[2] = c; + if (c) + wsi->u.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->u.ws.mask[3] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + break; + + case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED: + + assert(wsi->u.ws.rx_ubuf); + + if (wsi->u.ws.rx_draining_ext) + goto drain_extension; + + if (wsi->u.ws.this_frame_masked && !wsi->u.ws.all_zero_nonce) + c ^= wsi->u.ws.mask[(wsi->u.ws.mask_idx++) & 3]; + + wsi->u.ws.rx_ubuf[LWS_PRE + (wsi->u.ws.rx_ubuf_head++)] = c; + + if (--wsi->u.ws.rx_packet_length == 0) { + /* spill because we have the whole frame */ + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + + /* + * if there's no protocol max frame size given, we are + * supposed to default to context->pt_serv_buf_size + */ + if (!wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size) + break; + + if (wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size) + break; + + /* spill because 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->u.ws.opcode) { + case LWSWSOPC_CLOSE: + pp = (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE]; + if (lws_check_opt(wsi->context->options, + LWS_SERVER_OPTION_VALIDATE_UTF8) && + wsi->u.ws.rx_ubuf_head > 2 && + lws_check_utf8(&wsi->u.ws.utf8, pp + 2, + wsi->u.ws.rx_ubuf_head - 2)) + goto utf8_fail; + + /* is this an acknowledgement of our close? */ + if (wsi->state == LWSS_AWAITING_CLOSE_ACK) { + /* + * fine he has told us he is closing too, let's + * finish our close + */ + lwsl_parser("seen server's close ack\n"); + return -1; + } + + lwsl_parser("client sees server close len = %d\n", + wsi->u.ws.rx_ubuf_head); + if (wsi->u.ws.rx_ubuf_head >= 2) { + close_code = (pp[0] << 8) | pp[1]; + if (close_code < 1000 || + close_code == 1004 || + close_code == 1005 || + close_code == 1006 || + close_code == 1012 || + close_code == 1013 || + close_code == 1014 || + close_code == 1015 || + (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->protocol->callback, wsi, + LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, + wsi->user_space, pp, + wsi->u.ws.rx_ubuf_head)) + return -1; + + if (lws_partial_buffered(wsi)) + /* + * if we're in the middle of something, + * we can't do a normal close response and + * have to just close our end. + */ + wsi->socket_is_permanently_unusable = 1; + else + /* + * parrot the close packet payload back + * we do not care about how it went, we are closing + * immediately afterwards + */ + lws_write(wsi, (unsigned char *) + &wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head, + LWS_WRITE_CLOSE); + wsi->state = LWSS_RETURNED_CLOSE_ALREADY; + /* close the connection */ + return -1; + + case LWSWSOPC_PING: + lwsl_info("received %d byte ping, sending pong\n", + wsi->u.ws.rx_ubuf_head); + + /* he set a close reason on this guy, ignore PING */ + if (wsi->u.ws.close_in_ping_buffer_len) + goto ping_drop; + + if (wsi->u.ws.ping_pending_flag) { + /* + * there is already a pending ping payload + * we should just log and drop + */ + lwsl_parser("DROP PING since one pending\n"); + goto ping_drop; + } + + /* control packets can only be < 128 bytes long */ + if (wsi->u.ws.rx_ubuf_head > 128 - 3) { + lwsl_parser("DROP PING payload too large\n"); + goto ping_drop; + } + + /* stash the pong payload */ + memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE, + &wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head; + wsi->u.ws.ping_pending_flag = 1; + + /* get it sent as soon as possible */ + lws_callback_on_writable(wsi); +ping_drop: + wsi->u.ws.rx_ubuf_head = 0; + handled = 1; + break; + + case LWSWSOPC_PONG: + lwsl_info("client receied pong\n"); + lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + if (wsi->pending_timeout == + PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) { + lwsl_info("%p: received expected PONG\n", wsi); + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + } + + /* issue it */ + callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG; + break; + + case LWSWSOPC_CONTINUATION: + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + break; + + default: + + lwsl_parser("Reserved opc 0x%2X\n", wsi->u.ws.opcode); + + /* + * It's something special we can't understand here. + * Pass the payload up to the extension's parsing + * state machine. + */ + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + + if (lws_ext_cb_active(wsi, + LWS_EXT_CB_EXTENDED_PAYLOAD_RX, + &eff_buf, 0) <= 0) { + /* not handled or failed */ + lwsl_ext("Unhandled ext opc 0x%x\n", + wsi->u.ws.opcode); + wsi->u.ws.rx_ubuf_head = 0; + + return 0; + } + handled = 1; + break; + } + + /* + * No it's real payload, pass it up to the user callback. + * It's nicely buffered with the pre-padding taken care of + * so it can be sent straight out again using lws_write + */ + if (handled) + goto already_done; + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + + if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len) + goto already_done; + +drain_extension: + lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len); + + n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0); + lwsl_ext("Ext RX returned %d\n", n); + if (n < 0) { + wsi->socket_is_permanently_unusable = 1; + return -1; + } + + lwsl_ext("post inflate eff_buf len %d\n", eff_buf.token_len); + + if (rx_draining_ext && !eff_buf.token_len) { + lwsl_debug(" --- ending drain on 0 read result\n"); + goto already_done; + } + + if (wsi->u.ws.check_utf8 && !wsi->u.ws.defeat_check_utf8) { + if (lws_check_utf8(&wsi->u.ws.utf8, + (unsigned char *)eff_buf.token, + eff_buf.token_len)) + goto utf8_fail; + + /* we are ending partway through utf-8 character? */ + if (!wsi->u.ws.rx_packet_length && wsi->u.ws.final && + wsi->u.ws.utf8 && !n) { + lwsl_info("FINAL utf8 error\n"); +utf8_fail: + lwsl_info("utf8 error\n"); + return -1; + } + } + + if (eff_buf.token_len < 0 && + callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG) + goto already_done; + + if (!eff_buf.token) + goto already_done; + + eff_buf.token[eff_buf.token_len] = '\0'; + + if (!wsi->protocol->callback) + goto already_done; + + if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG) + lwsl_info("Client doing pong callback\n"); + + if (n && eff_buf.token_len) + /* 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); + + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION || + wsi->state == LWSS_AWAITING_CLOSE_ACK) + goto already_done; + + m = wsi->protocol->callback(wsi, + (enum lws_callback_reasons)callback_action, + wsi->user_space, eff_buf.token, eff_buf.token_len); + + /* if user code wants to close, let caller know */ + if (m) + return 1; + +already_done: + wsi->u.ws.rx_ubuf_head = 0; + break; + default: + lwsl_err("client rx illegal state\n"); + return 1; + } + + return 0; + +illegal_ctl_length: + lwsl_warn("Control frame asking for extended length is illegal\n"); + + /* kill the connection */ + return -1; +} + + diff -Nru libwebsockets-4.0.20/lib/client/ssl-client.c libwebsockets-2.4.2/lib/client/ssl-client.c --- libwebsockets-4.0.20/lib/client/ssl-client.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/client/ssl-client.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,625 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +extern int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +extern void +lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info); + +extern int lws_ssl_get_error(struct lws *wsi, int n); + +#if defined(USE_WOLFSSL) +#else + +static int +OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ +#if defined(LWS_WITH_MBEDTLS) + lwsl_notice("%s\n", __func__); + + return 0; +#else + SSL *ssl; + int n; + struct lws *wsi; + + /* keep old behaviour accepting self-signed server certs */ + if (!preverify_ok) { + int err = X509_STORE_CTX_get_error(x509_ctx); + + if (err != X509_V_OK) { + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + + if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || + err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && + wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED) { + lwsl_notice("accepting self-signed certificate (verify_callback)\n"); + X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); + return 1; // ok + } else if ((err == X509_V_ERR_CERT_NOT_YET_VALID || + err == X509_V_ERR_CERT_HAS_EXPIRED) && + wsi->use_ssl & LCCSCF_ALLOW_EXPIRED) { + if (err == X509_V_ERR_CERT_NOT_YET_VALID) + lwsl_notice("accepting not yet valid certificate (verify_callback)\n"); + else if (err == X509_V_ERR_CERT_HAS_EXPIRED) + lwsl_notice("accepting expired certificate (verify_callback)\n"); + X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); + return 1; // ok + } + } + } + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + + n = lws_get_context_protocol(wsi->context, 0).callback(wsi, + LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION, + x509_ctx, ssl, preverify_ok); + + /* keep old behaviour if something wrong with server certs */ + /* if ssl error is overruled in callback and cert is ok, + * X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); must be set and + * return value is 0 from callback */ + if (!preverify_ok) { + int err = X509_STORE_CTX_get_error(x509_ctx); + + if (err != X509_V_OK) { /* cert validation error was not handled in callback */ + int depth = X509_STORE_CTX_get_error_depth(x509_ctx); + const char* msg = X509_verify_cert_error_string(err); + lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;depth=%d)\n", msg, preverify_ok, err, depth); + return preverify_ok; // not ok + } + } + /* convert callback return code from 0 = OK to verify callback return value 1 = OK */ + return !n; +#endif +} +#endif + +int +lws_ssl_client_bio_create(struct lws *wsi) +{ + char hostname[128], *p; + + if (lws_hdr_copy(wsi, hostname, sizeof(hostname), + _WSI_TOKEN_CLIENT_HOST) <= 0) { + lwsl_err("%s: Unable to get hostname\n", __func__); + + return -1; + } + + /* + * remove any :port part on the hostname... necessary for network + * connection but typical certificates do not contain it + */ + p = hostname; + while (*p) { + if (*p == ':') { + *p = '\0'; + break; + } + p++; + } + + wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx); + if (!wsi->ssl) { + lwsl_err("SSL_new failed: %s\n", + ERR_error_string(lws_ssl_get_error(wsi, 0), NULL)); + lws_ssl_elaborate_error(); + return -1; + } + +#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) + if (wsi->vhost->ssl_info_event_mask) + SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback); +#endif + +#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host + X509_VERIFY_PARAM *param; + (void)param; + + if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { + param = SSL_get0_param(wsi->ssl); + /* Enable automatic hostname checks */ + X509_VERIFY_PARAM_set_hostflags(param, + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + X509_VERIFY_PARAM_set1_host(param, hostname, 0); + } + +#endif + +#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) +#ifndef USE_OLD_CYASSL + /* OpenSSL_client_verify_callback will be called @ SSL_connect() */ + SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback); +#endif +#endif + +#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) + SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#endif + /* + * use server name indication (SNI), if supported, + * when establishing connection + */ +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL +#ifdef CYASSL_SNI_HOST_NAME + CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname)); +#endif +#else +#ifdef WOLFSSL_SNI_HOST_NAME + wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, hostname, strlen(hostname)); +#endif +#endif +#else +#if defined(LWS_WITH_MBEDTLS) + if (wsi->vhost->x509_client_CA) + SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback); + else + SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback); + +#else +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + SSL_set_tlsext_host_name(wsi->ssl, hostname); +#endif +#endif +#endif + +#ifdef USE_WOLFSSL + /* + * wolfSSL/CyaSSL does certificate verification differently + * from OpenSSL. + * If we should ignore the certificate, we need to set + * this before SSL_new and SSL_connect is called. + * Otherwise the connect will simply fail with error code -155 + */ +#ifdef USE_OLD_CYASSL + if (wsi->use_ssl == 2) + CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); +#else + if (wsi->use_ssl == 2) + wolfSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); +#endif +#endif /* USE_WOLFSSL */ + +#if !defined(LWS_WITH_MBEDTLS) + wsi->client_bio = BIO_new_socket(wsi->desc.sockfd, BIO_NOCLOSE); + SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio); +#else + SSL_set_fd(wsi->ssl, wsi->desc.sockfd); +#endif + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + CyaSSL_set_using_nonblock(wsi->ssl, 1); +#else + wolfSSL_set_using_nonblock(wsi->ssl, 1); +#endif +#else +#if !defined(LWS_WITH_MBEDTLS) + BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */ +#endif +#endif + +#if !defined(LWS_WITH_MBEDTLS) + SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index, + wsi); +#endif + + return 0; +} + +#if defined(LWS_WITH_MBEDTLS) +int ERR_get_error(void) +{ + return 0; +} +#endif + +int +lws_ssl_client_connect1(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + int n = 0; + + lws_latency_pre(context, wsi); + + n = SSL_connect(wsi->ssl); + + lws_latency(context, wsi, + "SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0); + + if (n < 0) { + n = lws_ssl_get_error(wsi, n); + + if (n == SSL_ERROR_WANT_READ) + goto some_wait; + + if (n == SSL_ERROR_WANT_WRITE) { + /* + * wants us to retry connect due to + * state of the underlying ssl layer... + * but since it may be stalled on + * blocked write, no incoming data may + * arrive to trigger the retry. + * Force (possibly many times if the SSL + * state persists in returning the + * condition code, but other sockets + * are getting serviced inbetweentimes) + * us to get called back when writable. + */ + lwsl_info("%s: WANT_WRITE... retrying\n", __func__); + lws_callback_on_writable(wsi); +some_wait: + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + + { + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + char *sb = p; + + lwsl_err("ssl hs1 error, X509_V_ERR = %d: %s\n", + n, ERR_error_string(n, sb)); + lws_ssl_elaborate_error(); + } + + n = -1; + } + + if (n <= 0) { + /* + * retry if new data comes until we + * run into the connection timeout or win + */ + + unsigned long error = ERR_get_error(); + + if (error != SSL_ERROR_NONE) { + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + char *sb = p; + lwsl_err("SSL connect error %lu: %s\n", + error, ERR_error_string(error, sb)); + return -1; + } + + return 0; + } + + return 1; +} + +int +lws_ssl_client_connect2(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + char *sb = p; + int n = 0; + + if (wsi->mode == LWSCM_WSCL_WAITING_SSL) { + lws_latency_pre(context, wsi); + n = SSL_connect(wsi->ssl); + lwsl_debug("%s: SSL_connect says %d\n", __func__, n); + + lws_latency(context, wsi, + "SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0); + + if (n < 0) { + n = lws_ssl_get_error(wsi, n); + + if (n == SSL_ERROR_WANT_READ) { + lwsl_info("SSL_connect WANT_READ... retrying\n"); + + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + + if (n == SSL_ERROR_WANT_WRITE) { + /* + * wants us to retry connect due to + * state of the underlying ssl layer... + * but since it may be stalled on + * blocked write, no incoming data may + * arrive to trigger the retry. + * Force (possibly many times if the SSL + * state persists in returning the + * condition code, but other sockets + * are getting serviced inbetweentimes) + * us to get called back when writable. + */ + lwsl_info("SSL_connect WANT_WRITE... retrying\n"); + lws_callback_on_writable(wsi); + + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + + n = -1; + } + + if (n <= 0) { + /* + * retry if new data comes until we + * run into the connection timeout or win + */ + unsigned long error = ERR_get_error(); + if (error != SSL_ERROR_NONE) { + lwsl_err("SSL connect error %lu: %s\n", + error, ERR_error_string(error, sb)); + return -1; + } + } + } + +#if defined(LWS_WITH_MBEDTLS) + { + X509 *peer = SSL_get_peer_certificate(wsi->ssl); + + if (!peer) { + lwsl_notice("peer did not provide cert\n"); + + return -1; + } + lwsl_notice("peer provided cert\n"); + } +#endif + +#ifndef USE_WOLFSSL + /* + * See comment above about wolfSSL certificate + * verification + */ + lws_latency_pre(context, wsi); + n = SSL_get_verify_result(wsi->ssl); + lws_latency(context, wsi, + "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0); + + lwsl_debug("get_verify says %d\n", n); + + if (n != X509_V_OK) { + if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || + n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && + (wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED)) { + lwsl_notice("accepting self-signed certificate\n"); + } else if ((n == X509_V_ERR_CERT_NOT_YET_VALID || + n == X509_V_ERR_CERT_HAS_EXPIRED) && + (wsi->use_ssl & LCCSCF_ALLOW_EXPIRED)) { + lwsl_notice("accepting expired certificate\n"); + } else if (n == X509_V_ERR_CERT_NOT_YET_VALID) { + lwsl_notice("Cert is from the future... " + "probably our clock... accepting...\n"); + } else { + lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n", + n, ERR_error_string(n, sb)); + lws_ssl_elaborate_error(); + return -1; + } + } + +#endif /* USE_WOLFSSL */ + + return 1; +} + + +int lws_context_init_client_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ + SSL_METHOD *method = NULL; + struct lws wsi; + unsigned long error; + const char *ca_filepath = info->ssl_ca_filepath; +#if !defined(LWS_WITH_MBEDTLS) + const char *cipher_list = info->ssl_cipher_list; + const char *private_key_filepath = info->ssl_private_key_filepath; + const char *cert_filepath = info->ssl_cert_filepath; + int n; + + if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW) + return 0; + + /* + * for backwards-compatibility default to using ssl_... members, but + * if the newer client-specific ones are given, use those + */ + if (info->client_ssl_cipher_list) + cipher_list = info->client_ssl_cipher_list; + if (info->client_ssl_cert_filepath) + cert_filepath = info->client_ssl_cert_filepath; + if (info->client_ssl_private_key_filepath) + private_key_filepath = info->client_ssl_private_key_filepath; +#endif + if (info->client_ssl_ca_filepath) + ca_filepath = info->client_ssl_ca_filepath; + + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) + return 0; + + if (vhost->ssl_client_ctx) + return 0; + + if (info->provided_client_ssl_ctx) { + /* use the provided OpenSSL context if given one */ + vhost->ssl_client_ctx = info->provided_client_ssl_ctx; + /* nothing for lib to delete */ + vhost->user_supplied_ssl_ctx = 1; + + return 0; + } + + /* basic openssl init already happened in context init */ + + /* choose the most recent spin of the api */ +#if defined(LWS_HAVE_TLS_CLIENT_METHOD) + method = (SSL_METHOD *)TLS_client_method(); +#elif defined(LWS_HAVE_TLSV1_2_CLIENT_METHOD) + method = (SSL_METHOD *)TLSv1_2_client_method(); +#else + method = (SSL_METHOD *)SSLv23_client_method(); +#endif + if (!method) { + error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + /* create context */ + vhost->ssl_client_ctx = SSL_CTX_new(method); + if (!vhost->ssl_client_ctx) { + error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + + lwsl_notice("created client ssl context for %s\n", vhost->name); + +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION); +#endif + +#if defined(LWS_WITH_MBEDTLS) + if (ca_filepath) { + lws_filepos_t len; + uint8_t *buf; + /* + * prototype this here, the shim does not export it in the + * header, and we need to use the shim unchanged for ESP32 case + */ + X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len); + + if (alloc_file(vhost->context, ca_filepath, &buf, &len)) { + lwsl_err("Load CA cert file %s failed\n", ca_filepath); + return 1; + } + + vhost->x509_client_CA = d2i_X509(NULL, buf, len); + free(buf); + if (!vhost->x509_client_CA) { + lwsl_err("client CA: x509 parse failed\n"); + return 1; + } + + SSL_CTX_add_client_CA(vhost->ssl_client_ctx, + vhost->x509_client_CA); + + lwsl_notice("client loaded CA for verification %s\n", ca_filepath); + } +#else + SSL_CTX_set_options(vhost->ssl_client_ctx, + SSL_OP_CIPHER_SERVER_PREFERENCE); + + if (cipher_list) + SSL_CTX_set_cipher_list(vhost->ssl_client_ctx, cipher_list); + +#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) + /* loads OS default CA certs */ + SSL_CTX_set_default_verify_paths(vhost->ssl_client_ctx); +#endif + + /* openssl init for cert verification (for client sockets) */ + if (!ca_filepath) { + if (!SSL_CTX_load_verify_locations( + vhost->ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS)) + lwsl_err("Unable to load SSL Client certs from %s " + "(set by LWS_OPENSSL_CLIENT_CERTS) -- " + "client ssl isn't going to work\n", + LWS_OPENSSL_CLIENT_CERTS); + } else + if (!SSL_CTX_load_verify_locations( + vhost->ssl_client_ctx, ca_filepath, NULL)) { + lwsl_err( + "Unable to load SSL Client certs " + "file from %s -- client ssl isn't " + "going to work\n", info->client_ssl_ca_filepath); + lws_ssl_elaborate_error(); + } + else + lwsl_info("loaded ssl_ca_filepath\n"); + + /* + * callback allowing user code to load extra verification certs + * helping the client to verify server identity + */ + + /* support for client-side certificate authentication */ + if (cert_filepath) { + lwsl_notice("%s: doing cert filepath\n", __func__); + n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx, + cert_filepath); + if (n < 1) { + lwsl_err("problem %d getting cert '%s'\n", n, + cert_filepath); + lws_ssl_elaborate_error(); + return 1; + } + lwsl_notice("Loaded client cert %s\n", cert_filepath); + } + if (private_key_filepath) { + lwsl_notice("%s: doing private key filepath\n", __func__); + lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info); + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx, + private_key_filepath, SSL_FILETYPE_PEM) != 1) { + lwsl_err("use_PrivateKey_file '%s'\n", + private_key_filepath); + lws_ssl_elaborate_error(); + return 1; + } + lwsl_notice("Loaded client cert private key %s\n", + private_key_filepath); + + /* verify private key */ + if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + return 1; + } + } +#endif + /* + * give him a fake wsi with context set, so he can use + * lws_get_context() in the callback + */ + memset(&wsi, 0, sizeof(wsi)); + wsi.vhost = vhost; + wsi.context = vhost->context; + + vhost->protocols[0].callback(&wsi, + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS, + vhost->ssl_client_ctx, NULL, 0); + + return 0; +} diff -Nru libwebsockets-4.0.20/lib/context.c libwebsockets-2.4.2/lib/context.c --- libwebsockets-4.0.20/lib/context.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/context.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1628 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#ifndef LWS_BUILD_HASH +#define LWS_BUILD_HASH "unknown-build-hash" +#endif + +static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH; + +/** + * lws_get_library_version: get version and git hash library built from + * + * returns a const char * to a string like "1.1 178d78c" + * representing the library version followed by the git head hash it + * was built from + */ +LWS_VISIBLE const char * +lws_get_library_version(void) +{ + return library_version; +} + +static const char * const mount_protocols[] = { + "http://", + "https://", + "file://", + "cgi://", + ">http://", + ">https://", + "callback://" +}; + +#if defined(LWS_WITH_HTTP2) +/* + * These are the standardized defaults. + * Override what actually goes in the vhost settings in platform or user code. + * Leave these alone because they are used to determine "what is different + * from the protocol defaults". + */ +const struct http2_settings lws_h2_defaults = { { + 1, + /* H2SET_HEADER_TABLE_SIZE */ 4096, + /* *** This controls how many entries in the dynamic table *** + * Allows the sender to inform the remote endpoint of the maximum + * size of the header compression table used to decode header + * blocks, in octets. The encoder can select any size equal to or + * less than this value by using signaling specific to the header + * compression format inside a header block (see [COMPRESSION]). + * The initial value is 4,096 octets. + */ + /* H2SET_ENABLE_PUSH */ 1, + /* H2SET_MAX_CONCURRENT_STREAMS */ 0x7fffffff, + /* H2SET_INITIAL_WINDOW_SIZE */ 65535, + /* H2SET_MAX_FRAME_SIZE */ 16384, + /* H2SET_MAX_HEADER_LIST_SIZE */ 0x7fffffff, + /*< This advisory setting informs a peer of the maximum size of + * header list that the sender is prepared to accept, in octets. + * The value is based on the uncompressed size of header fields, + * including the length of the name and value in octets plus an + * overhead of 32 octets for each header field. + */ + +}}; + +const struct http2_settings lws_h2_stock_settings = { { + 1, + /* H2SET_HEADER_TABLE_SIZE */ 4096, + /* *** This controls how many entries in the dynamic table *** + * Allows the sender to inform the remote endpoint of the maximum + * size of the header compression table used to decode header + * blocks, in octets. The encoder can select any size equal to or + * less than this value by using signaling specific to the header + * compression format inside a header block (see [COMPRESSION]). + * The initial value is 4,096 octets. + * + * Can't pass h2spec with less than 4096 here... + */ + /* H2SET_ENABLE_PUSH */ 1, + /* H2SET_MAX_CONCURRENT_STREAMS */ 24, + /* H2SET_INITIAL_WINDOW_SIZE */ 65535, + /* H2SET_MAX_FRAME_SIZE */ 16384, + /* H2SET_MAX_HEADER_LIST_SIZE */ 4096, + /*< This advisory setting informs a peer of the maximum size of + * header list that the sender is prepared to accept, in octets. + * The value is based on the uncompressed size of header fields, + * including the length of the name and value in octets plus an + * overhead of 32 octets for each header field. + */ + +}}; +#endif + +LWS_VISIBLE void * +lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, + const struct lws_protocols *prot, int size) +{ + int n = 0; + + /* allocate the vh priv array only on demand */ + if (!vhost->protocol_vh_privs) { + vhost->protocol_vh_privs = (void **)lws_zalloc( + vhost->count_protocols * sizeof(void *), "protocol_vh_privs"); + if (!vhost->protocol_vh_privs) + return NULL; + } + + while (n < vhost->count_protocols && &vhost->protocols[n] != prot) + n++; + + if (n == vhost->count_protocols) { + n = 0; + while (n < vhost->count_protocols && + strcmp(vhost->protocols[n].name, prot->name)) + n++; + + if (n == vhost->count_protocols) + return NULL; + } + + vhost->protocol_vh_privs[n] = lws_zalloc(size, "vh priv"); + return vhost->protocol_vh_privs[n]; +} + +LWS_VISIBLE void * +lws_protocol_vh_priv_get(struct lws_vhost *vhost, + const struct lws_protocols *prot) +{ + int n = 0; + + if (!vhost || !vhost->protocol_vh_privs) + return NULL; + + while (n < vhost->count_protocols && &vhost->protocols[n] != prot) + n++; + + if (n == vhost->count_protocols) { + n = 0; + while (n < vhost->count_protocols && + strcmp(vhost->protocols[n].name, prot->name)) + n++; + + if (n == vhost->count_protocols) { + lwsl_err("%s: unknown protocol %p\n", __func__, prot); + return NULL; + } + } + + return vhost->protocol_vh_privs[n]; +} + +static const struct lws_protocol_vhost_options * +lws_vhost_protocol_options(struct lws_vhost *vh, const char *name) +{ + const struct lws_protocol_vhost_options *pvo = vh->pvo; + + if (!name) + return NULL; + + while (pvo) { + if (!strcmp(pvo->name, name)) + return pvo; + pvo = pvo->next; + } + + return NULL; +} + +/* + * inform every vhost that hasn't already done it, that + * his protocols are initializing + */ +LWS_VISIBLE int +lws_protocol_init(struct lws_context *context) +{ + struct lws_vhost *vh = context->vhost_list; + const struct lws_protocol_vhost_options *pvo, *pvo1; + struct lws wsi; + int n; + + if (context->doing_protocol_init) + return 0; + + context->doing_protocol_init = 1; + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = context; + + lwsl_info("%s\n", __func__); + + while (vh) { + wsi.vhost = vh; + + /* only do the protocol init once for a given vhost */ + if (vh->created_vhost_protocols) + goto next; + + /* initialize supported protocols on this vhost */ + + for (n = 0; n < vh->count_protocols; n++) { + wsi.protocol = &vh->protocols[n]; + if (!vh->protocols[n].name) + continue; + pvo = lws_vhost_protocol_options(vh, + vh->protocols[n].name); + if (pvo) { + /* + * linked list of options specific to + * vh + protocol + */ + pvo1 = pvo; + pvo = pvo1->options; + + while (pvo) { + lwsl_notice( + " vhost \"%s\", protocol \"%s\", option \"%s\"\n", + vh->name, + vh->protocols[n].name, + pvo->name); + + if (!strcmp(pvo->name, "default")) { + lwsl_notice("Setting default " + "protocol for vh %s to %s\n", + vh->name, + vh->protocols[n].name); + vh->default_protocol_index = n; + } + if (!strcmp(pvo->name, "raw")) { + lwsl_notice("Setting raw " + "protocol for vh %s to %s\n", + vh->name, + vh->protocols[n].name); + vh->raw_protocol_index = n; + } + pvo = pvo->next; + } + + pvo = pvo1->options; + } + + /* + * inform all the protocols that they are doing their + * one-time initialization if they want to. + * + * NOTE the wsi is all zeros except for the context, vh + * + protocol ptrs so lws_get_context(wsi) etc can work + */ + if (vh->protocols[n].callback(&wsi, + LWS_CALLBACK_PROTOCOL_INIT, NULL, + (void *)pvo, 0)) { + lwsl_err("%s: vhost %s failed init\n", __func__, + vh->protocols[n].name); + context->doing_protocol_init = 0; + return 1; + } + } + + vh->created_vhost_protocols = 1; +next: + vh = vh->vhost_next; + } + + context->doing_protocol_init = 0; + + if (!context->protocol_init_done) + lws_finalize_startup(context); + + context->protocol_init_done = 1; + + return 0; +} + +LWS_VISIBLE int +lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct lws_ssl_info *si; +#ifdef LWS_WITH_CGI + struct lws_cgi_args *args; +#endif +#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY) + char buf[512]; + int n; +#endif + + switch (reason) { + case LWS_CALLBACK_HTTP: +#ifndef LWS_NO_SERVER + if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL)) + return -1; + + if (lws_http_transaction_completed(wsi)) +#endif + return -1; + break; +#if !defined(LWS_NO_SERVER) + case LWS_CALLBACK_HTTP_FILE_COMPLETION: + if (lws_http_transaction_completed(wsi)) + return -1; + break; +#endif + + case LWS_CALLBACK_HTTP_WRITEABLE: +#ifdef LWS_WITH_CGI + if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS | + LWS_CB_REASON_AUX_BF__CGI)) { + n = lws_cgi_write_split_stdout_headers(wsi); + if (n < 0) { + lwsl_debug("LWS_CB_REASON_AUX_BF__CGI forcing close\n"); + return -1; + } + if (!n) + lws_rx_flow_control(wsi->cgi->stdwsi[LWS_STDOUT], 1); + + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS) + wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI_HEADERS; + else + wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI; + break; + } + + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) { + if (!wsi->http2_substream) { + memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5); + lwsl_debug("writing chunk terminator and exiting\n"); + n = lws_write(wsi, (unsigned char *)buf + LWS_PRE, + 5, LWS_WRITE_HTTP); + } else + n = lws_write(wsi, (unsigned char *)buf + LWS_PRE, + 0, LWS_WRITE_HTTP_FINAL); + + /* always close after sending it */ + return -1; + } +#endif +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) { + char *px = buf + LWS_PRE; + int lenx = sizeof(buf) - LWS_PRE; + + /* + * our sink is writeable and our source has something + * to read. So read a lump of source material of + * suitable size to send or what's available, whichever + * is the smaller. + */ + wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY; + if (!lws_get_child(wsi)) + break; + if (lws_http_client_read(lws_get_child(wsi), &px, &lenx) < 0) + return -1; + break; + } +#endif + break; + +#if defined(LWS_WITH_HTTP_PROXY) + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: + assert(lws_get_parent(wsi)); + if (!lws_get_parent(wsi)) + break; + lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY; + lws_callback_on_writable(lws_get_parent(wsi)); + break; + + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: + assert(lws_get_parent(wsi)); + n = lws_write(lws_get_parent(wsi), (unsigned char *)in, + len, LWS_WRITE_HTTP); + if (n < 0) + return -1; + break; + + case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: { + unsigned char *p, *end; + char ctype[64], ctlen = 0; + + p = (unsigned char *)buf + LWS_PRE; + end = p + sizeof(buf) - LWS_PRE; + + if (lws_add_http_header_status(lws_get_parent(wsi), + HTTP_STATUS_OK, &p, end)) + return 1; + if (lws_add_http_header_by_token(lws_get_parent(wsi), + WSI_TOKEN_HTTP_SERVER, + (unsigned char *)"libwebsockets", + 13, &p, end)) + return 1; + + ctlen = lws_hdr_copy(wsi, ctype, sizeof(ctype), + WSI_TOKEN_HTTP_CONTENT_TYPE); + if (ctlen > 0) { + if (lws_add_http_header_by_token(lws_get_parent(wsi), + WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)ctype, ctlen, &p, end)) + return 1; + } + + if (lws_finalize_http_header(lws_get_parent(wsi), &p, end)) + return 1; + + *p = '\0'; + n = lws_write(lws_get_parent(wsi), + (unsigned char *)buf + LWS_PRE, + p - ((unsigned char *)buf + LWS_PRE), + LWS_WRITE_HTTP_HEADERS); + if (n < 0) + return -1; + + break; } + +#endif + +#ifdef LWS_WITH_CGI + /* CGI IO events (POLLIN/OUT) appear here, our default policy is: + * + * - POST data goes on subprocess stdin + * - subprocess stdout goes on http via writeable callback + * - subprocess stderr goes to the logs + */ + case LWS_CALLBACK_CGI: + args = (struct lws_cgi_args *)in; + switch (args->ch) { /* which of stdin/out/err ? */ + case LWS_STDIN: + /* TBD stdin rx flow control */ + break; + case LWS_STDOUT: + /* quench POLLIN on STDOUT until MASTER got writeable */ + lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0); + wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI; + /* when writing to MASTER would not block */ + lws_callback_on_writable(wsi); + break; + case LWS_STDERR: + n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]); + if (n < 0) + break; + n = read(n, buf, sizeof(buf) - 2); + if (n > 0) { + if (buf[n - 1] != '\n') + buf[n++] = '\n'; + buf[n] = '\0'; + lwsl_notice("CGI-stderr: %s\n", buf); + } + break; + } + break; + + case LWS_CALLBACK_CGI_TERMINATED: + lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n", + wsi->cgi->explicitly_chunked, + (uint64_t)wsi->cgi->content_length); + if (!wsi->cgi->explicitly_chunked && + !wsi->cgi->content_length) { + /* send terminating chunk */ + lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n"); + wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END; + lws_callback_on_writable(wsi); + lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3); + break; + } + return -1; + + case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */ + args = (struct lws_cgi_args *)in; + args->data[args->len] = '\0'; + n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]); + if (n < 0) + return -1; + n = write(n, args->data, args->len); + if (n < args->len) + lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: " + "sent %d only %d went", n, args->len); + return n; +#endif + + case LWS_CALLBACK_SSL_INFO: + si = in; + + (void)si; + lwsl_notice("LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n", + si->where, si->ret); + break; + + default: + break; + } + + return 0; +} + +/* list of supported protocols and callbacks */ + +static const struct lws_protocols protocols_dummy[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + lws_callback_http_dummy, /* callback */ + 0, /* per_session_data_size */ + 0, /* max frame size / rx buffer */ + 0, NULL, 0 + }, + /* + * the other protocols are provided by lws plugins + */ + { NULL, NULL, 0, 0, 0, NULL, 0} /* terminator */ +}; + +#ifdef LWS_PLAT_OPTEE +#undef LWS_HAVE_GETENV +#endif + +LWS_VISIBLE struct lws_vhost * +lws_create_vhost(struct lws_context *context, + struct lws_context_creation_info *info) +{ + struct lws_vhost *vh = lws_zalloc(sizeof(*vh), "create vhost"), + **vh1 = &context->vhost_list; + const struct lws_http_mount *mounts; + const struct lws_protocol_vhost_options *pvo; +#ifdef LWS_WITH_PLUGINS + struct lws_plugin *plugin = context->plugin_list; +#endif + struct lws_protocols *lwsp; + int m, f = !info->pvo; +#ifdef LWS_HAVE_GETENV + char *p; +#endif + int n; + + if (!vh) + return NULL; + + if (!info->protocols) + info->protocols = &protocols_dummy[0]; + + vh->context = context; + if (!info->vhost_name) + vh->name = "default"; + else + vh->name = info->vhost_name; + + if (info->options & LWS_SERVER_OPTION_ONLY_RAW) + lwsl_info("%s set to only support RAW\n", vh->name); + +#if defined(LWS_WITH_HTTP2) + vh->set = context->set; + if (info->http2_settings[0]) + for (n = 1; n < LWS_H2_SETTINGS_LEN; n++) + vh->set.s[n] = info->http2_settings[n]; +#endif + + vh->iface = info->iface; +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32) + vh->bind_iface = info->bind_iface; +#endif + + for (vh->count_protocols = 0; + info->protocols[vh->count_protocols].callback; + vh->count_protocols++) + ; + + vh->options = info->options; + vh->pvo = info->pvo; + vh->headers = info->headers; + vh->user = info->user; + vh->ssl_info_event_mask = info->ssl_info_event_mask; + if (info->keepalive_timeout) + vh->keepalive_timeout = info->keepalive_timeout; + else + vh->keepalive_timeout = 5; + + if (info->timeout_secs_ah_idle) + vh->timeout_secs_ah_idle = info->timeout_secs_ah_idle; + else + vh->timeout_secs_ah_idle = 10; + + /* + * give the vhost a unified list of protocols including the + * ones that came from plugins + */ + lwsp = lws_zalloc(sizeof(struct lws_protocols) * + (vh->count_protocols + + context->plugin_protocol_count + 1), "vhost-specific plugin table"); + if (!lwsp) { + lwsl_err("OOM\n"); + return NULL; + } + + m = vh->count_protocols; + memcpy(lwsp, info->protocols, sizeof(struct lws_protocols) * m); + + /* for compatibility, all protocols enabled on vhost if only + * the default vhost exists. Otherwise only vhosts who ask + * for a protocol get it enabled. + */ + + if (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) + f = 0; + (void)f; +#ifdef LWS_WITH_PLUGINS + if (plugin) { + + while (plugin) { + for (n = 0; n < plugin->caps.count_protocols; n++) { + /* + * for compatibility's sake, no pvo implies + * allow all protocols + */ + if (f || lws_vhost_protocol_options(vh, + plugin->caps.protocols[n].name)) { + memcpy(&lwsp[m], + &plugin->caps.protocols[n], + sizeof(struct lws_protocols)); + m++; + vh->count_protocols++; + } + } + plugin = plugin->list; + } + } +#endif + + if ( +#ifdef LWS_WITH_PLUGINS + (context->plugin_list) || +#endif + context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) + vh->protocols = lwsp; + else { + vh->protocols = info->protocols; + lws_free(lwsp); + } + + vh->same_vh_protocol_list = (struct lws **) + lws_zalloc(sizeof(struct lws *) * vh->count_protocols, "same vh list"); + + vh->mount_list = info->mounts; + +#ifdef LWS_WITH_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(context)) { + lwsl_notice("Creating Vhost '%s' path \"%s\", %d protocols\n", + vh->name, info->iface, vh->count_protocols); + } else +#endif + lwsl_notice("Creating Vhost '%s' port %d, %d protocols, IPv6 %s\n", + vh->name, info->port, vh->count_protocols, + LWS_IPV6_ENABLED(vh) ? "on" : "off"); + + mounts = info->mounts; + while (mounts) { + (void)mount_protocols[0]; + lwsl_notice(" mounting %s%s to %s\n", + mount_protocols[mounts->origin_protocol], + mounts->origin, mounts->mountpoint); + + /* convert interpreter protocol names to pointers */ + pvo = mounts->interpret; + while (pvo) { + for (n = 0; n < vh->count_protocols; n++) + if (!strcmp(pvo->value, vh->protocols[n].name)) { + ((struct lws_protocol_vhost_options *)pvo)->value = + (const char *)(lws_intptr_t)n; + break; + } + if (n == vh->count_protocols) + lwsl_err("ignoring unknown interpret protocol %s\n", + pvo->value); + pvo = pvo->next; + } + + mounts = mounts->mount_next; + } + +#ifndef LWS_NO_EXTENSIONS +#ifdef LWS_WITH_PLUGINS + if (context->plugin_extension_count) { + + m = 0; + while (info->extensions && info->extensions[m].callback) + m++; + + /* + * give the vhost a unified list of extensions including the + * ones that came from plugins + */ + vh->extensions = lws_zalloc(sizeof(struct lws_extension) * + (m + + context->plugin_extension_count + 1), "extensions"); + if (!vh->extensions) + return NULL; + + memcpy((struct lws_extension *)vh->extensions, info->extensions, + sizeof(struct lws_extension) * m); + plugin = context->plugin_list; + while (plugin) { + memcpy((struct lws_extension *)&vh->extensions[m], + plugin->caps.extensions, + sizeof(struct lws_extension) * + plugin->caps.count_extensions); + m += plugin->caps.count_extensions; + plugin = plugin->list; + } + } else +#endif + vh->extensions = info->extensions; +#endif + + vh->listen_port = info->port; +#if !defined(LWS_WITH_ESP8266) + vh->http_proxy_port = 0; + vh->http_proxy_address[0] = '\0'; +#if defined(LWS_WITH_SOCKS5) + vh->socks_proxy_port = 0; + vh->socks_proxy_address[0] = '\0'; +#endif + + /* either use proxy from info, or try get it from env var */ + + /* http proxy */ + if (info->http_proxy_address) { + /* override for backwards compatibility */ + if (info->http_proxy_port) + vh->http_proxy_port = info->http_proxy_port; + lws_set_proxy(vh, info->http_proxy_address); + } else { +#ifdef LWS_HAVE_GETENV + p = getenv("http_proxy"); + if (p) + lws_set_proxy(vh, p); +#endif + } +#if defined(LWS_WITH_SOCKS5) + /* socks proxy */ + if (info->socks_proxy_address) { + /* override for backwards compatibility */ + if (info->socks_proxy_port) + vh->socks_proxy_port = info->socks_proxy_port; + lws_set_socks(vh, info->socks_proxy_address); + } else { +#ifdef LWS_HAVE_GETENV + p = getenv("socks_proxy"); + if (p) + lws_set_socks(vh, p); +#endif + } +#endif +#endif + + vh->ka_time = info->ka_time; + vh->ka_interval = info->ka_interval; + vh->ka_probes = info->ka_probes; + + if (vh->options & LWS_SERVER_OPTION_STS) + lwsl_notice(" STS enabled\n"); + +#ifdef LWS_WITH_ACCESS_LOG + if (info->log_filepath) { + vh->log_fd = open(info->log_filepath, + O_CREAT | O_APPEND | O_RDWR, 0600); + if (vh->log_fd == (int)LWS_INVALID_FILE) { + lwsl_err("unable to open log filepath %s\n", + info->log_filepath); + goto bail; + } +#ifndef WIN32 + if (context->uid != -1) + if (chown(info->log_filepath, context->uid, + context->gid) == -1) + lwsl_err("unable to chown log file %s\n", + info->log_filepath); +#endif + } else + vh->log_fd = (int)LWS_INVALID_FILE; +#endif + if (lws_context_init_server_ssl(info, vh)) + goto bail; + if (lws_context_init_client_ssl(info, vh)) + goto bail; + if (lws_context_init_server(info, vh)) { + lwsl_err("init server failed\n"); + goto bail; + } + + while (1) { + if (!(*vh1)) { + *vh1 = vh; + break; + } + vh1 = &(*vh1)->vhost_next; + }; + /* for the case we are adding a vhost much later, after server init */ + + if (context->protocol_init_done) + lws_protocol_init(context); + + return vh; + +bail: + lws_free(vh); + + return NULL; +} + +LWS_VISIBLE int +lws_init_vhost_client_ssl(const struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ + struct lws_context_creation_info i; + + memcpy(&i, info, sizeof(i)); + i.port = CONTEXT_PORT_NO_LISTEN; + + return lws_context_init_client_ssl(&i, vhost); +} + +LWS_VISIBLE struct lws_context * +lws_create_context(struct lws_context_creation_info *info) +{ + struct lws_context *context = NULL; + struct lws_plat_file_ops *prev; +#ifndef LWS_NO_DAEMONIZE + int pid_daemon = get_daemonize_pid(); +#endif + int n; +#if defined(__ANDROID__) + struct rlimit rt; +#endif + + lwsl_info("Initial logging level %d\n", log_level); + lwsl_info("Libwebsockets version: %s\n", library_version); +#if defined(GCC_VER) + lwsl_info("Compiled with %s\n", GCC_VER); +#endif +#if LWS_POSIX +#ifdef LWS_WITH_IPV6 + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_IPV6)) + lwsl_info("IPV6 compiled in and enabled\n"); + else + lwsl_info("IPV6 compiled in but disabled\n"); +#else + lwsl_info("IPV6 not compiled in\n"); +#endif +#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_PLAT_ESP32) + lws_feature_status_libev(info); + lws_feature_status_libuv(info); +#endif +#endif + lwsl_info(" LWS_DEF_HEADER_LEN : %u\n", LWS_DEF_HEADER_LEN); + lwsl_info(" LWS_MAX_PROTOCOLS : %u\n", LWS_MAX_PROTOCOLS); + lwsl_info(" LWS_MAX_SMP : %u\n", LWS_MAX_SMP); + lwsl_info(" sizeof (*info) : %ld\n", (long)sizeof(*info)); +#if defined(LWS_WITH_STATS) + lwsl_info(" LWS_WITH_STATS : on\n"); +#endif +#if LWS_POSIX + lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH); +#endif +#if defined(LWS_WITH_HTTP2) + lwsl_info(" HTTP2 support : available\n"); +#else + lwsl_info(" HTTP2 support : not configured"); +#endif + if (lws_plat_context_early_init()) + return NULL; + + context = lws_zalloc(sizeof(struct lws_context), "context"); + if (!context) { + lwsl_err("No memory for websocket context\n"); + return NULL; + } + if (info->pt_serv_buf_size) + context->pt_serv_buf_size = info->pt_serv_buf_size; + else + context->pt_serv_buf_size = 4096; + +#if defined(LWS_WITH_HTTP2) + context->set = lws_h2_stock_settings; +#endif + +#if LWS_MAX_SMP > 1 + pthread_mutex_init(&context->lock, NULL); +#endif + +#if defined(LWS_WITH_ESP32) + context->last_free_heap = esp_get_free_heap_size(); +#endif + + /* default to just the platform fops implementation */ + + context->fops_platform.LWS_FOP_OPEN = _lws_plat_file_open; + context->fops_platform.LWS_FOP_CLOSE = _lws_plat_file_close; + context->fops_platform.LWS_FOP_SEEK_CUR = _lws_plat_file_seek_cur; + context->fops_platform.LWS_FOP_READ = _lws_plat_file_read; + context->fops_platform.LWS_FOP_WRITE = _lws_plat_file_write; + context->fops_platform.fi[0].sig = NULL; + + /* + * arrange a linear linked-list of fops starting from context->fops + * + * platform fops + * [ -> fops_zip (copied into context so .next settable) ] + * [ -> info->fops ] + */ + + context->fops = &context->fops_platform; + prev = (struct lws_plat_file_ops *)context->fops; + +#if defined(LWS_WITH_ZIP_FOPS) + /* make a soft copy so we can set .next */ + context->fops_zip = fops_zip; + prev->next = &context->fops_zip; + prev = (struct lws_plat_file_ops *)prev->next; +#endif + + /* if user provided fops, tack them on the end of the list */ + if (info->fops) + prev->next = info->fops; + + context->reject_service_keywords = info->reject_service_keywords; + if (info->external_baggage_free_on_destroy) + context->external_baggage_free_on_destroy = + info->external_baggage_free_on_destroy; + + context->time_up = time(NULL); + + context->simultaneous_ssl_restriction = info->simultaneous_ssl_restriction; + +#ifndef LWS_NO_DAEMONIZE + if (pid_daemon) { + context->started_with_parent = pid_daemon; + lwsl_info(" Started with daemon pid %d\n", pid_daemon); + } +#endif +#if defined(__ANDROID__) + n = getrlimit ( RLIMIT_NOFILE,&rt); + if (-1 == n) { + lwsl_err("Get RLIMIT_NOFILE failed!\n"); + return NULL; + } + context->max_fds = rt.rlim_cur; +#else + context->max_fds = getdtablesize(); +#endif + + if (info->count_threads) + context->count_threads = info->count_threads; + else + context->count_threads = 1; + + if (context->count_threads > LWS_MAX_SMP) + context->count_threads = LWS_MAX_SMP; + + context->token_limits = info->token_limits; + + context->options = info->options; + + if (info->timeout_secs) + context->timeout_secs = info->timeout_secs; + else + context->timeout_secs = AWAITING_TIMEOUT; + + context->ws_ping_pong_interval = info->ws_ping_pong_interval; + + lwsl_info(" default timeout (secs): %u\n", context->timeout_secs); + + if (info->max_http_header_data) + context->max_http_header_data = info->max_http_header_data; + else + if (info->max_http_header_data2) + context->max_http_header_data = + info->max_http_header_data2; + else + context->max_http_header_data = LWS_DEF_HEADER_LEN; + if (info->max_http_header_pool) + context->max_http_header_pool = info->max_http_header_pool; + else + context->max_http_header_pool = LWS_DEF_HEADER_POOL; + + /* + * Allocate the per-thread storage for scratchpad buffers, + * and header data pool + */ + for (n = 0; n < context->count_threads; n++) { + context->pt[n].serv_buf = lws_malloc(context->pt_serv_buf_size, + "pt_serv_buf"); + if (!context->pt[n].serv_buf) { + lwsl_err("OOM\n"); + return NULL; + } + +#ifdef LWS_WITH_LIBUV + context->pt[n].context = context; +#endif + context->pt[n].tid = n; + context->pt[n].ah_list = NULL; + context->pt[n].ah_pool_length = 0; + + lws_pt_mutex_init(&context->pt[n]); + } + + if (info->fd_limit_per_thread) + context->fd_limit_per_thread = info->fd_limit_per_thread; + else + context->fd_limit_per_thread = context->max_fds / + context->count_threads; + + lwsl_info(" Threads: %d each %d fds\n", context->count_threads, + context->fd_limit_per_thread); + + if (!info->ka_interval && info->ka_time > 0) { + lwsl_err("info->ka_interval can't be 0 if ka_time used\n"); + return NULL; + } + +#ifdef LWS_WITH_LIBEV + /* (Issue #264) In order to *avoid breaking backwards compatibility*, we + * enable libev mediated SIGINT handling with a default handler of + * lws_sigint_cb. The handler can be overridden or disabled + * by invoking lws_sigint_cfg after creating the context, but + * before invoking lws_initloop: + */ + context->use_ev_sigint = 1; + context->lws_ev_sigint_cb = &lws_ev_sigint_cb; +#endif /* LWS_WITH_LIBEV */ +#ifdef LWS_WITH_LIBUV + /* (Issue #264) In order to *avoid breaking backwards compatibility*, we + * enable libev mediated SIGINT handling with a default handler of + * lws_sigint_cb. The handler can be overridden or disabled + * by invoking lws_sigint_cfg after creating the context, but + * before invoking lws_initloop: + */ + context->use_ev_sigint = 1; + context->lws_uv_sigint_cb = &lws_uv_sigint_cb; +#endif +#ifdef LWS_WITH_LIBEVENT + /* (Issue #264) In order to *avoid breaking backwards compatibility*, we + * enable libev mediated SIGINT handling with a default handler of + * lws_sigint_cb. The handler can be overridden or disabled + * by invoking lws_sigint_cfg after creating the context, but + * before invoking lws_initloop: + */ + context->use_ev_sigint = 1; + context->lws_event_sigint_cb = &lws_event_sigint_cb; +#endif /* LWS_WITH_LIBEVENT */ + +#if defined(LWS_WITH_PEER_LIMITS) + /* scale the peer hash table according to the max fds for the process, + * so that the max list depth averages 16. Eg, 1024 fd -> 64, + * 102400 fd -> 6400 + */ + context->pl_hash_elements = + (context->count_threads * context->fd_limit_per_thread) / 16; + context->pl_hash_table = lws_zalloc(sizeof(struct lws_peer *) * + context->pl_hash_elements, "peer limits hash table"); + context->ip_limit_ah = info->ip_limit_ah; + context->ip_limit_wsi = info->ip_limit_wsi; +#endif + + lwsl_info(" mem: context: %5lu bytes (%ld ctx + (%ld thr x %d))\n", + (long)sizeof(struct lws_context) + + (context->count_threads * context->pt_serv_buf_size), + (long)sizeof(struct lws_context), + (long)context->count_threads, + context->pt_serv_buf_size); + + lwsl_info(" mem: http hdr rsvd: %5lu bytes (%u thr x (%u + %lu) x %u))\n", + (long)(context->max_http_header_data + + sizeof(struct allocated_headers)) * + context->max_http_header_pool * context->count_threads, + context->count_threads, + context->max_http_header_data, + (long)sizeof(struct allocated_headers), + context->max_http_header_pool); + n = sizeof(struct lws_pollfd) * context->count_threads * + context->fd_limit_per_thread; + context->pt[0].fds = lws_zalloc(n, "fds table"); + if (context->pt[0].fds == NULL) { + lwsl_err("OOM allocating %d fds\n", context->max_fds); + goto bail; + } + lwsl_info(" mem: pollfd map: %5u\n", n); + + if (info->server_string) { + context->server_string = info->server_string; + context->server_string_len = (short) + strlen(context->server_string); + } + +#if LWS_MAX_SMP > 1 + /* each thread serves his own chunk of fds */ + for (n = 1; n < (int)info->count_threads; n++) + context->pt[n].fds = context->pt[n - 1].fds + + context->fd_limit_per_thread; +#endif + + if (lws_plat_init(context, info)) + goto bail; + +#if defined(LWS_WITH_HTTP2) + /* + * let the user code see what the platform default SETTINGS were, he + * can modify them when he creates the vhosts. + */ + for (n = 1; n < LWS_H2_SETTINGS_LEN; n++) + info->http2_settings[n] = context->set.s[n]; +#endif + + lws_context_init_ssl_library(info); + + context->user_space = info->user; + + /* + * if he's not saying he'll make his own vhosts later then act + * compatibly and make a default vhost using the data in the info + */ + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + if (!lws_create_vhost(context, info)) { + lwsl_err("Failed to create default vhost\n"); + return NULL; + } + + lws_context_init_extensions(info, context); + + lwsl_info(" mem: per-conn: %5lu bytes + protocol rx buf\n", + (unsigned long)sizeof(struct lws)); + + strcpy(context->canonical_hostname, "unknown"); + lws_server_get_canonical_hostname(context, info); + + context->uid = info->uid; + context->gid = info->gid; + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + memcpy(context->caps, info->caps, sizeof(context->caps)); + context->count_caps = info->count_caps; +#endif + + /* + * drop any root privs for this process + * to listen on port < 1023 we would have needed root, but now we are + * listening, we don't want the power for anything else + */ + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lws_plat_drop_app_privileges(info); + + /* + * give all extensions a chance to create any per-context + * allocations they need + */ + if (info->port != CONTEXT_PORT_NO_LISTEN) { + if (lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0) + goto bail; + } else + if (lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0) + goto bail; + + return context; + +bail: + lws_context_destroy(context); + return NULL; +} + +LWS_VISIBLE LWS_EXTERN void +lws_context_deprecate(struct lws_context *context, lws_reload_func cb) +{ + struct lws_vhost *vh = context->vhost_list, *vh1; + struct lws *wsi; + + /* + * "deprecation" means disable the context from accepting any new + * connections and free up listen sockets to be used by a replacement + * context. + * + * Otherwise the deprecated context remains operational, until its + * number of connected sockets falls to zero, when it is deleted. + */ + + /* for each vhost, close his listen socket */ + + while (vh) { + wsi = vh->lserv_wsi; + if (wsi) { + wsi->socket_is_permanently_unusable = 1; + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + wsi->context->deprecation_pending_listen_close_count++; + /* + * other vhosts can share the listen port, they + * point to the same wsi. So zap those too. + */ + vh1 = context->vhost_list; + while (vh1) { + if (vh1->lserv_wsi == wsi) + vh1->lserv_wsi = NULL; + vh1 = vh1->vhost_next; + } + } + vh = vh->vhost_next; + } + + context->deprecated = 1; + context->deprecation_cb = cb; +} + +LWS_VISIBLE LWS_EXTERN int +lws_context_is_deprecated(struct lws_context *context) +{ + return context->deprecated; +} + +LWS_VISIBLE void +lws_context_destroy2(struct lws_context *context); + + +static void +lws_vhost_destroy1(struct lws_vhost *vh) +{ + const struct lws_protocols *protocol = NULL; + struct lws_context_per_thread *pt; + int n, m = vh->context->count_threads; + struct lws_context *context = vh->context; + struct lws wsi; + + lwsl_info("%s\n", __func__); + + if (vh->being_destroyed) + return; + + vh->being_destroyed = 1; + + /* + * Are there other vhosts that are piggybacking on our listen socket? + * If so we need to hand the listen socket off to one of the others + * so it will remain open. If not, leave it attached to the closing + * vhost and it will get closed. + */ + + if (vh->lserv_wsi) + lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) { + if (v != vh && + !v->being_destroyed && + v->listen_port == vh->listen_port && + ((!v->iface && !vh->iface) || + (v->iface && vh->iface && + !strcmp(v->iface, vh->iface)))) { + /* + * this can only be a listen wsi, which is + * restricted... it has no protocol or other + * bindings or states. So we can simply + * swap it to a vhost that has the same + * iface + port, but is not closing. + */ + assert(v->lserv_wsi == NULL); + v->lserv_wsi = vh->lserv_wsi; + vh->lserv_wsi = NULL; + if (v->lserv_wsi) + v->lserv_wsi->vhost = v; + + lwsl_notice("%s: listen skt from %s to %s\n", + __func__, vh->name, v->name); + break; + } + } lws_end_foreach_ll(v, vhost_next); + + /* + * Forcibly close every wsi assoicated with this vhost. That will + * include the listen socket if it is still associated with the closing + * vhost. + */ + + while (m--) { + pt = &context->pt[m]; + + for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) { + struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->vhost != vh) + continue; + + lws_close_free_wsi(wsi, + LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY + /* no protocol close */); + n--; + } + } + + /* + * let the protocols destroy the per-vhost protocol objects + */ + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = vh->context; + wsi.vhost = vh; + protocol = vh->protocols; + if (protocol) { + n = 0; + while (n < vh->count_protocols) { + wsi.protocol = protocol; + protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY, + NULL, NULL, 0); + protocol++; + n++; + } + } + + /* + * remove vhost from context list of vhosts + */ + + lws_start_foreach_llp(struct lws_vhost **, pv, context->vhost_list) { + if (*pv == vh) { + *pv = vh->vhost_next; + break; + } + } lws_end_foreach_llp(pv, vhost_next); + + /* add ourselves to the pending destruction list */ + + vh->vhost_next = vh->context->vhost_pending_destruction_list; + vh->context->vhost_pending_destruction_list = vh; +} + +static void +lws_vhost_destroy2(struct lws_vhost *vh) +{ + const struct lws_protocols *protocol = NULL; + struct lws_context *context = vh->context; + struct lws_deferred_free *df; + int n; + + lwsl_info("%s: %p\n", __func__, vh); + + /* if we are still on deferred free list, remove ourselves */ + + lws_start_foreach_llp(struct lws_deferred_free **, pdf, + context->deferred_free_list) { + if ((*pdf)->payload == vh) { + df = *pdf; + *pdf = df->next; + lws_free(df); + break; + } + } lws_end_foreach_llp(pdf, next); + + /* remove ourselves from the pending destruction list */ + + lws_start_foreach_llp(struct lws_vhost **, pv, + context->vhost_pending_destruction_list) { + if ((*pv) == vh) { + *pv = (*pv)->vhost_next; + break; + } + } lws_end_foreach_llp(pv, vhost_next); + + /* + * Free all the allocations associated with the vhost + */ + + protocol = vh->protocols; + if (protocol) { + n = 0; + while (n < vh->count_protocols) { + if (vh->protocol_vh_privs && + vh->protocol_vh_privs[n]) { + lws_free(vh->protocol_vh_privs[n]); + vh->protocol_vh_privs[n] = NULL; + } + protocol++; + n++; + } + } + if (vh->protocol_vh_privs) + lws_free(vh->protocol_vh_privs); + lws_ssl_SSL_CTX_destroy(vh); + lws_free(vh->same_vh_protocol_list); +#ifdef LWS_WITH_PLUGINS + if (LWS_LIBUV_ENABLED(context)) { + if (context->plugin_list) + lws_free((void *)vh->protocols); + } else +#endif + { + if (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) + lws_free((void *)vh->protocols); + } + +#ifdef LWS_WITH_PLUGINS +#ifndef LWS_NO_EXTENSIONS + if (context->plugin_extension_count) + lws_free((void *)vh->extensions); +#endif +#endif +#ifdef LWS_WITH_ACCESS_LOG + if (vh->log_fd != (int)LWS_INVALID_FILE) + close(vh->log_fd); +#endif + + /* + * although async event callbacks may still come for wsi handles with + * pending close in the case of asycn event library like libuv, + * they do not refer to the vhost. So it's safe to free. + */ + + lwsl_info(" %s: Freeing vhost %p\n", __func__, vh); + + memset(vh, 0, sizeof(*vh)); + lws_free(vh); +} + +int +lws_check_deferred_free(struct lws_context *context, int force) +{ + struct lws_deferred_free *df; + time_t now = lws_now_secs(); + + lws_start_foreach_llp(struct lws_deferred_free **, pdf, + context->deferred_free_list) { + if (now > (*pdf)->deadline || force) { + df = *pdf; + *pdf = df->next; + /* finalize vh destruction */ + lwsl_notice("deferred vh %p destroy\n", df->payload); + lws_vhost_destroy2(df->payload); + lws_free(df); + continue; /* after deletion we already point to next */ + } + } lws_end_foreach_llp(pdf, next); + + return 0; +} + +LWS_VISIBLE void +lws_vhost_destroy(struct lws_vhost *vh) +{ + struct lws_deferred_free *df = lws_malloc(sizeof(*df), "deferred free"); + + if (!df) + return; + + lws_vhost_destroy1(vh); + + /* part 2 is deferred to allow all the handle closes to complete */ + + df->next = vh->context->deferred_free_list; + df->deadline = lws_now_secs() + 5; + df->payload = vh; + vh->context->deferred_free_list = df; +} + +LWS_VISIBLE void +lws_context_destroy(struct lws_context *context) +{ + struct lws_context_per_thread *pt; + struct lws_vhost *vh = NULL; + struct lws wsi; + int n, m; + + if (!context) { + lwsl_notice("%s: ctx %p\n", __func__, context); + return; + } + if (context->being_destroyed1) { + lwsl_notice("%s: ctx %p: already being destroyed\n", + __func__, context); + return; + } + + lwsl_info("%s: ctx %p\n", __func__, context); + + m = context->count_threads; + context->being_destroyed = 1; + context->being_destroyed1 = 1; + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = context; + +#ifdef LWS_LATENCY + if (context->worst_latency_info[0]) + lwsl_notice("Worst latency: %s\n", context->worst_latency_info); +#endif + + while (m--) { + pt = &context->pt[m]; + + for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) { + struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + + lws_close_free_wsi(wsi, + LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY + /* no protocol close */); + n--; + } + lws_pt_mutex_destroy(pt); + } + + /* + * give all extensions a chance to clean up any per-context + * allocations they might have made + */ + + n = lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT, NULL, 0); + + n = lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT, NULL, 0); + + /* + * inform all the protocols that they are done and will have no more + * callbacks. + * + * We can't free things until after the event loop shuts down. + */ + if (context->protocol_init_done) + vh = context->vhost_list; + while (vh) { + struct lws_vhost *vhn = vh->vhost_next; + lws_vhost_destroy1(vh); + vh = vhn; + } + + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + + lws_libev_destroyloop(context, n); + lws_libuv_destroyloop(context, n); + lws_libevent_destroyloop(context, n); + + lws_free_set_NULL(context->pt[n].serv_buf); + + while (pt->ah_list) + _lws_destroy_ah(pt, pt->ah_list); + } + lws_plat_context_early_destroy(context); + + if (context->pt[0].fds) + lws_free_set_NULL(context->pt[0].fds); + + if (!LWS_LIBUV_ENABLED(context)) + lws_context_destroy2(context); +} + +/* + * call the second one after the event loop has been shut down cleanly + */ + +LWS_VISIBLE void +lws_context_destroy2(struct lws_context *context) +{ + struct lws_vhost *vh = NULL, *vh1; +#if defined(LWS_WITH_PEER_LIMITS) + uint32_t n; +#endif + + lwsl_info("%s: ctx %p\n", __func__, context); + + /* + * free all the per-vhost allocations + */ + + vh = context->vhost_list; + while (vh) { + vh1 = vh->vhost_next; + lws_vhost_destroy2(vh); + vh = vh1; + } + + /* remove ourselves from the pending destruction list */ + + while (context->vhost_pending_destruction_list) + /* removes itself from list */ + lws_vhost_destroy2(context->vhost_pending_destruction_list); + + + lws_stats_log_dump(context); + + lws_ssl_context_destroy(context); + lws_plat_context_late_destroy(context); + +#if defined(LWS_WITH_PEER_LIMITS) + for (n = 0; n < context->pl_hash_elements; n++) { + lws_start_foreach_llp(struct lws_peer **, peer, + context->pl_hash_table[n]) { + struct lws_peer *df = *peer; + *peer = df->next; + lws_free(df); + continue; + } lws_end_foreach_llp(peer, next); + } + lws_free(context->pl_hash_table); +#endif + + if (context->external_baggage_free_on_destroy) + free(context->external_baggage_free_on_destroy); + + lws_check_deferred_free(context, 1); + +#if LWS_MAX_SMP > 1 + pthread_mutex_destroy(&context->lock); +#endif + + lws_free(context); +} diff -Nru libwebsockets-4.0.20/lib/core/alloc.c libwebsockets-2.4.2/lib/core/alloc.c --- libwebsockets-4.0.20/lib/core/alloc.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core/alloc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,180 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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_HAVE_MALLOC_USABLE_SIZE) - -#include - -/* the heap is processwide */ -static size_t allocated; -#endif - -#if defined(LWS_PLAT_OPTEE) - -#define TEE_USER_MEM_HINT_NO_FILL_ZERO 0x80000000 -#if defined (LWS_WITH_NETWORK) - -/* normal TA apis */ - -void *__attribute__((weak)) - TEE_Malloc(uint32_t size, uint32_t hint) -{ - return NULL; -} -void *__attribute__((weak)) - TEE_Realloc(void *buffer, uint32_t newSize) -{ - return NULL; -} -void __attribute__((weak)) - TEE_Free(void *buffer) -{ -} -#else - -/* in-OP-TEE core apis */ - -void * - TEE_Malloc(uint32_t size, uint32_t hint) -{ - return malloc(size); -} -void * - TEE_Realloc(void *buffer, uint32_t newSize) -{ - return realloc(buffer, newSize); -} -void - TEE_Free(void *buffer) -{ - free(buffer); -} - -#endif - -void *lws_realloc(void *ptr, size_t size, const char *reason) -{ - return TEE_Realloc(ptr, size); -} - -void *lws_malloc(size_t size, const char *reason) -{ - return TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO); -} - -void lws_free(void *p) -{ - TEE_Free(p); -} - -void *lws_zalloc(size_t size, const char *reason) -{ - void *ptr = TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO); - if (ptr) - memset(ptr, 0, size); - return ptr; -} - -void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason)) -{ - (void)cb; -} -#else - -static void * -_realloc(void *ptr, size_t size, const char *reason) -{ - void *v; - - if (size) { -#if defined(LWS_PLAT_FREERTOS) - lwsl_notice("%s: size %lu: %s (free heap %d)\n", __func__, -#if defined(LWS_AMAZON_RTOS) - (unsigned long)size, reason, (unsigned int)xPortGetFreeHeapSize() - (int)size); -#else - (unsigned long)size, reason, (unsigned int)esp_get_free_heap_size() - (int)size); -#endif -#else - lwsl_debug("%s: size %lu: %s\n", __func__, - (unsigned long)size, reason); -#endif - -#if defined(LWS_HAVE_MALLOC_USABLE_SIZE) - if (ptr) - allocated -= malloc_usable_size(ptr); -#endif - -#if defined(LWS_PLAT_OPTEE) - v = (void *)TEE_Realloc(ptr, size); -#else - v = (void *)realloc(ptr, size); -#endif -#if defined(LWS_HAVE_MALLOC_USABLE_SIZE) - allocated += malloc_usable_size(v); -#endif - return v; - } - if (ptr) { -#if defined(LWS_HAVE_MALLOC_USABLE_SIZE) - allocated -= malloc_usable_size(ptr); -#endif - free(ptr); - } - - return NULL; -} - -void *(*_lws_realloc)(void *ptr, size_t size, const char *reason) = _realloc; - -void *lws_realloc(void *ptr, size_t size, const char *reason) -{ - return _lws_realloc(ptr, size, reason); -} - -void *lws_zalloc(size_t size, const char *reason) -{ - void *ptr = _lws_realloc(NULL, size, reason); - - if (ptr) - memset(ptr, 0, size); - - return ptr; -} - -void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason)) -{ - _lws_realloc = cb; -} - -size_t lws_get_allocated_heap(void) -{ -#if defined(LWS_HAVE_MALLOC_USABLE_SIZE) - return allocated; -#else - return 0; -#endif -} -#endif diff -Nru libwebsockets-4.0.20/lib/core/buflist.c libwebsockets-2.4.2/lib/core/buflist.c --- libwebsockets-4.0.20/lib/core/buflist.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core/buflist.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,225 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -#ifdef LWS_HAVE_SYS_TYPES_H -#include -#endif - -/* lws_buflist */ - -int -lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf, - size_t len) -{ - struct lws_buflist *nbuf; - int first = !*head; - void *p = *head; - int sanity = 1024; - - assert(buf); - assert(len); - - /* append at the tail */ - while (*head) { - if (!--sanity) { - lwsl_err("%s: buflist reached sanity limit\n", __func__); - return -1; - } - if (*head == (*head)->next) { - lwsl_err("%s: corrupt list points to self\n", __func__); - return -1; - } - head = &((*head)->next); - } - - (void)p; - lwsl_info("%s: len %u first %d %p\n", __func__, (unsigned int)len, - first, p); - - nbuf = (struct lws_buflist *)lws_malloc(sizeof(struct lws_buflist) + - len + LWS_PRE + 1, __func__); - if (!nbuf) { - lwsl_err("%s: OOM\n", __func__); - return -1; - } - - nbuf->len = len; - nbuf->pos = 0; - nbuf->next = NULL; - - /* whoever consumes this might need LWS_PRE from the start... */ - p = (uint8_t *)nbuf + sizeof(*nbuf) + LWS_PRE; - memcpy(p, buf, len); - - *head = nbuf; - - return first; /* returns 1 if first segment just created */ -} - -static int -lws_buflist_destroy_segment(struct lws_buflist **head) -{ - struct lws_buflist *old = *head; - - assert(*head); - *head = old->next; - old->next = NULL; - old->pos = old->len = 0; - lws_free(old); - - return !*head; /* returns 1 if last segment just destroyed */ -} - -void -lws_buflist_destroy_all_segments(struct lws_buflist **head) -{ - struct lws_buflist *p = *head, *p1; - - while (p) { - p1 = p->next; - p->next = NULL; - lws_free(p); - p = p1; - } - - *head = NULL; -} - -size_t -lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf) -{ - struct lws_buflist *b = (*head); - - if (buf) - *buf = NULL; - - if (!b) - return 0; /* there is no next segment len */ - - if (!b->len && b->next) - if (lws_buflist_destroy_segment(head)) - return 0; - - b = (*head); - if (!b) - return 0; /* there is no next segment len */ - - assert(b->pos < b->len); - - if (buf) - *buf = ((uint8_t *)b) + sizeof(*b) + b->pos + LWS_PRE; - - return b->len - b->pos; -} - -size_t -lws_buflist_use_segment(struct lws_buflist **head, size_t len) -{ - struct lws_buflist *b = (*head); - - assert(b); - assert(len); - assert(b->pos + len <= b->len); - - b->pos = b->pos + (size_t)len; - - assert(b->pos <= b->len); - - if (b->pos < b->len) - return (int)(b->len - b->pos); - - if (lws_buflist_destroy_segment(head)) - /* last segment was just destroyed */ - return 0; - - return lws_buflist_next_segment_len(head, NULL); -} - -size_t -lws_buflist_total_len(struct lws_buflist **head) -{ - struct lws_buflist *p = *head; - size_t size = 0; - - while (p) { - size += p->len; - p = p->next; - } - - return size; -} - -int -lws_buflist_linear_copy(struct lws_buflist **head, size_t ofs, uint8_t *buf, - size_t len) -{ - struct lws_buflist *p = *head; - uint8_t *obuf = buf; - size_t s; - - while (p && len) { - if (ofs < p->len) { - s = p->len - ofs; - if (s > len) - s = len; - memcpy(buf, ((uint8_t *)&p[1]) + LWS_PRE + ofs, s); - len -= s; - buf += s; - ofs = 0; - } else - ofs -= p->len; - p = p->next; - } - - return lws_ptr_diff(buf, obuf); -} - -#if defined(_DEBUG) -void -lws_buflist_describe(struct lws_buflist **head, void *id, const char *reason) -{ - struct lws_buflist *old; - int n = 0; - - if (*head == NULL) - lwsl_notice("%p: %s: buflist empty\n", id, reason); - - while (*head) { - lwsl_notice("%p: %s: %d: %llu / %llu (%llu left)\n", id, - reason, n, - (unsigned long long)(*head)->pos, - (unsigned long long)(*head)->len, - (unsigned long long)(*head)->len - (*head)->pos); - old = *head; - head = &((*head)->next); - if (*head == old) { - lwsl_err("%s: next points to self\n", __func__); - break; - } - n++; - } -} -#endif diff -Nru libwebsockets-4.0.20/lib/core/context.c libwebsockets-2.4.2/lib/core/context.c --- libwebsockets-4.0.20/lib/core/context.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core/context.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1325 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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" - -#ifndef LWS_BUILD_HASH -#define LWS_BUILD_HASH "unknown-build-hash" -#endif - -static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH; - -#if defined(LWS_WITH_NETWORK) -/* in ms */ -static uint32_t default_backoff_table[] = { 1000, 3000, 9000, 17000 }; -#endif - -/** - * lws_get_library_version: get version and git hash library built from - * - * returns a const char * to a string like "1.1 178d78c" - * representing the library version followed by the git head hash it - * was built from - */ -const char * -lws_get_library_version(void) -{ - return library_version; -} - -#if defined(LWS_WITH_STATS) -static void -lws_sul_stats_cb(lws_sorted_usec_list_t *sul) -{ - struct lws_context_per_thread *pt = lws_container_of(sul, - struct lws_context_per_thread, sul_stats); - - lws_stats_log_dump(pt->context); - - __lws_sul_insert(&pt->pt_sul_owner, &pt->sul_stats, 10 * LWS_US_PER_SEC); -} -#endif -#if defined(LWS_WITH_PEER_LIMITS) -static void -lws_sul_peer_limits_cb(lws_sorted_usec_list_t *sul) -{ - struct lws_context_per_thread *pt = lws_container_of(sul, - struct lws_context_per_thread, sul_peer_limits); - - lws_peer_cull_peer_wait_list(pt->context); - - __lws_sul_insert(&pt->pt_sul_owner, &pt->sul_peer_limits, 10 * LWS_US_PER_SEC); -} -#endif - -#if defined(LWS_WITH_NETWORK) - -#if defined(_DEBUG) -static const char * system_state_names[] = { - "undef", - "CONTEXT_CREATED", - "INITIALIZED", - "IFACE_COLDPLUG", - "DHCP", - "TIME_VALID", - "POLICY_VALID", - "REGISTERED", - "AUTH1", - "AUTH2", - "OPERATIONAL", - "POLICY_INVALID" -}; -#endif - -/* - * Handle provoking protocol init when we pass through the right system state - */ - -static int -lws_state_notify_protocol_init(struct lws_state_manager *mgr, - struct lws_state_notify_link *link, int current, - int target) -{ - struct lws_context *context = lws_container_of(mgr, struct lws_context, - mgr_system); - int n; - - /* - * Deal with any attachments that were waiting for the right state - * to come along - */ - - for (n = 0; n < context->count_threads; n++) - lws_system_do_attach(&context->pt[n]); - -#if defined(LWS_WITH_SYS_DHCP_CLIENT) - if (target == LWS_SYSTATE_DHCP) { - /* - * Don't let it past here until at least one iface has been - * configured for operation with DHCP - */ - - if (!lws_dhcpc_status(context, NULL)) - return 1; - } -#endif - -#if defined(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM) - /* - * Skip this if we are running something without the policy for it - * - * If root token is empty, skip too. - */ - if (target == LWS_SYSTATE_AUTH1 && - context->pss_policies && - !lws_system_blob_get_size(lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, - 0)) && - lws_system_blob_get_size(lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, - 1))) { - lwsl_info("%s: AUTH1 state triggering api.amazon.com auth\n", __func__); - /* - * Start trying to acquire it if it's not already in progress - * returns nonzero if we determine it's not needed - */ - if (!lws_ss_sys_auth_api_amazon_com(context)) - return 1; - } -#endif - -#if defined(LWS_WITH_SECURE_STREAMS) - /* - * Skip this if we are running something without the policy for it - */ - if (target == LWS_SYSTATE_POLICY_VALID && - context->pss_policies && !context->policy_updated) { - /* - * Start trying to acquire it if it's not already in progress - * returns nonzero if we determine it's not needed - */ - if (!lws_ss_sys_fetch_policy(context)) - return 1; - } -#endif - - /* protocol part */ - - if (context->protocol_init_done) - return 0; - - if (target != LWS_SYSTATE_POLICY_VALID) - return 0; - - lwsl_info("%s: doing protocol init on POLICY_VALID\n", __func__); - lws_protocol_init(context); - - return 0; -} - -static void -lws_context_creation_completion_cb(lws_sorted_usec_list_t *sul) -{ - struct lws_context *context = lws_container_of(sul, struct lws_context, - sul_system_state); - - /* if nothing is there to intercept anything, go all the way */ - lws_state_transition_steps(&context->mgr_system, - LWS_SYSTATE_OPERATIONAL); -} -#endif - -struct lws_context * -lws_create_context(const struct lws_context_creation_info *info) -{ - struct lws_context *context = NULL; -#if defined(LWS_WITH_FILE_OPS) - struct lws_plat_file_ops *prev; -#endif -#ifndef LWS_NO_DAEMONIZE - pid_t pid_daemon = get_daemonize_pid(); -#endif -#if defined(LWS_WITH_NETWORK) - int n, count_threads = 1; - uint8_t *u; -#endif -#if defined(__ANDROID__) - struct rlimit rt; -#endif - size_t s1 = 4096, size = sizeof(struct lws_context); - int lpf = info->fd_limit_per_thread; - - if (lpf) { - lpf+= 2; -#if defined(LWS_WITH_SYS_ASYNC_DNS) - lpf++; -#endif -#if defined(LWS_WITH_SYS_NTPCLIENT) - lpf++; -#endif -#if defined(LWS_WITH_SYS_DHCP_CLIENT) - lpf++; -#endif - } - - lwsl_info("Initial logging level %d\n", log_level); - lwsl_info("Libwebsockets version: %s\n", library_version); - -#ifdef LWS_WITH_IPV6 - if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_IPV6)) - lwsl_info("IPV6 compiled in and enabled\n"); - else - lwsl_info("IPV6 compiled in but disabled\n"); -#else - lwsl_info("IPV6 not compiled in\n"); -#endif - - lwsl_info(" LWS_DEF_HEADER_LEN : %u\n", LWS_DEF_HEADER_LEN); - lwsl_info(" LWS_MAX_SMP : %u\n", LWS_MAX_SMP); - lwsl_info(" sizeof (*info) : %ld\n", (long)sizeof(*info)); -#if defined(LWS_WITH_STATS) - lwsl_info(" LWS_WITH_STATS : on\n"); -#endif - lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH); -#if defined(LWS_WITH_HTTP2) - lwsl_info(" HTTP2 support : available\n"); -#else - lwsl_info(" HTTP2 support : not configured\n"); -#endif - if (lws_plat_context_early_init()) - return NULL; - -#if defined(LWS_WITH_NETWORK) - if (info->count_threads) - count_threads = info->count_threads; - - if (count_threads > LWS_MAX_SMP) - count_threads = LWS_MAX_SMP; - - if (info->pt_serv_buf_size) - s1 = info->pt_serv_buf_size; - - /* pt fakewsi and the pt serv buf allocations ride after the context */ - size += count_threads * (s1 + sizeof(struct lws)); -#endif - - context = lws_zalloc(size, "context"); - if (!context) { - lwsl_err("No memory for websocket context\n"); - return NULL; - } - - context->uid = info->uid; - context->gid = info->gid; - context->username = info->username; - context->groupname = info->groupname; - context->system_ops = info->system_ops; - context->pt_serv_buf_size = (unsigned int)s1; - context->udp_loss_sim_tx_pc = info->udp_loss_sim_tx_pc; - context->udp_loss_sim_rx_pc = info->udp_loss_sim_rx_pc; - - if (context->udp_loss_sim_tx_pc || context->udp_loss_sim_rx_pc) - lwsl_warn("%s: simulating udp loss tx: %d%%, rx: %d%%\n", - __func__, context->udp_loss_sim_tx_pc, - context->udp_loss_sim_rx_pc); - -#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) - context->ss_proxy_bind = info->ss_proxy_bind; - context->ss_proxy_port = info->ss_proxy_port; - context->ss_proxy_address = info->ss_proxy_address; - lwsl_notice("%s: using ss proxy bind '%s', port %d, ads '%s'\n", - __func__, context->ss_proxy_bind, context->ss_proxy_port, - context->ss_proxy_address); -#endif - -#if defined(LWS_WITH_NETWORK) - context->count_threads = count_threads; -#if defined(LWS_WITH_DETAILED_LATENCY) - context->detailed_latency_cb = info->detailed_latency_cb; - context->detailed_latency_filepath = info->detailed_latency_filepath; - context->latencies_fd = -1; -#endif -#if defined(LWS_WITHOUT_EXTENSIONS) - if (info->extensions) - lwsl_warn("%s: LWS_WITHOUT_EXTENSIONS but extensions ptr set\n", __func__); -#endif -#endif - -#if defined(LWS_WITH_SECURE_STREAMS) - context->pss_policies_json = info->pss_policies_json; - context->pss_plugins = info->pss_plugins; -#endif - - /* if he gave us names, set the uid / gid */ - if (lws_plat_drop_app_privileges(context, 0)) - goto bail; - - lwsl_info("context created\n"); -#if defined(LWS_WITH_TLS) && defined(LWS_WITH_NETWORK) -#if defined(LWS_WITH_MBEDTLS) - context->tls_ops = &tls_ops_mbedtls; -#else - context->tls_ops = &tls_ops_openssl; -#endif -#endif - -#if LWS_MAX_SMP > 1 - lws_mutex_refcount_init(&context->mr); -#endif - -#if defined(LWS_PLAT_FREERTOS) -#if defined(LWS_AMAZON_RTOS) - context->last_free_heap = xPortGetFreeHeapSize(); -#else - context->last_free_heap = esp_get_free_heap_size(); -#endif -#endif - -#if defined(LWS_WITH_FILE_OPS) - /* default to just the platform fops implementation */ - - context->fops_platform.LWS_FOP_OPEN = _lws_plat_file_open; - context->fops_platform.LWS_FOP_CLOSE = _lws_plat_file_close; - context->fops_platform.LWS_FOP_SEEK_CUR = _lws_plat_file_seek_cur; - context->fops_platform.LWS_FOP_READ = _lws_plat_file_read; - context->fops_platform.LWS_FOP_WRITE = _lws_plat_file_write; - context->fops_platform.fi[0].sig = NULL; - - /* - * arrange a linear linked-list of fops starting from context->fops - * - * platform fops - * [ -> fops_zip (copied into context so .next settable) ] - * [ -> info->fops ] - */ - - context->fops = &context->fops_platform; - prev = (struct lws_plat_file_ops *)context->fops; - -#if defined(LWS_WITH_ZIP_FOPS) - /* make a soft copy so we can set .next */ - context->fops_zip = fops_zip; - prev->next = &context->fops_zip; - prev = (struct lws_plat_file_ops *)prev->next; -#endif - - /* if user provided fops, tack them on the end of the list */ - if (info->fops) - prev->next = info->fops; -#endif - -#if defined(LWS_WITH_SERVER) - context->reject_service_keywords = info->reject_service_keywords; -#endif - if (info->external_baggage_free_on_destroy) - context->external_baggage_free_on_destroy = - info->external_baggage_free_on_destroy; -#if defined(LWS_WITH_NETWORK) - context->time_up = lws_now_usecs(); -#endif - context->pcontext_finalize = info->pcontext; - - context->simultaneous_ssl_restriction = - info->simultaneous_ssl_restriction; - - context->options = info->options; - -#ifndef LWS_NO_DAEMONIZE - if (pid_daemon) { - context->started_with_parent = pid_daemon; - lwsl_info(" Started with daemon pid %u\n", (unsigned int)pid_daemon); - } -#endif -#if defined(__ANDROID__) - n = getrlimit(RLIMIT_NOFILE, &rt); - if (n == -1) { - lwsl_err("Get RLIMIT_NOFILE failed!\n"); - - return NULL; - } - context->max_fds = rt.rlim_cur; -#else -#if defined(WIN32) || defined(_WIN32) || defined(LWS_AMAZON_RTOS) - context->max_fds = getdtablesize(); -#else - context->max_fds = sysconf(_SC_OPEN_MAX); -#endif - if (context->max_fds < 0) { - lwsl_err("%s: problem getting process max files\n", - __func__); - - return NULL; - } -#endif - - /* - * deal with any max_fds override, if it's reducing (setting it to - * more than ulimit -n is meaningless). The platform init will - * figure out what if this is something it can deal with. - */ - if (info->fd_limit_per_thread) { - int mf = lpf * context->count_threads; - - if (mf < context->max_fds) { - context->max_fds_unrelated_to_ulimit = 1; - context->max_fds = mf; - } - } - - context->token_limits = info->token_limits; - -#if defined(LWS_WITH_NETWORK) - - /* - * set the context event loops ops struct - * - * after this, all event_loop actions use the generic ops - */ - -#if defined(LWS_WITH_POLL) - context->event_loop_ops = &event_loop_ops_poll; -#endif - - if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) -#if defined(LWS_WITH_LIBUV) - context->event_loop_ops = &event_loop_ops_uv; -#else - goto fail_event_libs; -#endif - - if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV)) -#if defined(LWS_WITH_LIBEV) - context->event_loop_ops = &event_loop_ops_ev; -#else - goto fail_event_libs; -#endif - - if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT)) -#if defined(LWS_WITH_LIBEVENT) - context->event_loop_ops = &event_loop_ops_event; -#else - goto fail_event_libs; -#endif - - if (lws_check_opt(context->options, LWS_SERVER_OPTION_GLIB)) -#if defined(LWS_WITH_GLIB) - context->event_loop_ops = &event_loop_ops_glib; -#else - goto fail_event_libs; -#endif - - if (!context->event_loop_ops) - goto fail_event_libs; - - lwsl_info("Using event loop: %s\n", context->event_loop_ops->name); -#endif - -#if defined(LWS_WITH_TLS) && defined(LWS_WITH_NETWORK) - time(&context->tls.last_cert_check_s); - if (info->alpn) - context->tls.alpn_default = info->alpn; - else { - char *p = context->tls.alpn_discovered, first = 1; - - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) { - if (ar->alpn) { - if (!first) - *p++ = ','; - p += lws_snprintf(p, - context->tls.alpn_discovered + - sizeof(context->tls.alpn_discovered) - - 2 - p, "%s", ar->alpn); - first = 0; - } - } LWS_FOR_EVERY_AVAILABLE_ROLE_END; - - context->tls.alpn_default = context->tls.alpn_discovered; - } - - lwsl_info("Default ALPN advertisment: %s\n", context->tls.alpn_default); -#endif - - if (info->timeout_secs) - context->timeout_secs = info->timeout_secs; - else - context->timeout_secs = AWAITING_TIMEOUT; - - context->ws_ping_pong_interval = info->ws_ping_pong_interval; - - lwsl_info(" default timeout (secs): %u\n", context->timeout_secs); - - if (info->max_http_header_data) - context->max_http_header_data = info->max_http_header_data; - else - if (info->max_http_header_data2) - context->max_http_header_data = - info->max_http_header_data2; - else - context->max_http_header_data = LWS_DEF_HEADER_LEN; - - if (info->max_http_header_pool) - context->max_http_header_pool = info->max_http_header_pool; - else - if (info->max_http_header_pool2) - context->max_http_header_pool = - info->max_http_header_pool2; - else - context->max_http_header_pool = context->max_fds; - - - if (info->fd_limit_per_thread) - context->fd_limit_per_thread = lpf; - else - if (context->count_threads) - context->fd_limit_per_thread = context->max_fds / - context->count_threads; - -#if defined(LWS_WITH_NETWORK) - - context->default_retry.retry_ms_table = default_backoff_table; - context->default_retry.conceal_count = - context->default_retry.retry_ms_table_count = - LWS_ARRAY_SIZE(default_backoff_table); - context->default_retry.jitter_percent = 20; - context->default_retry.secs_since_valid_ping = 300; - context->default_retry.secs_since_valid_hangup = 310; - - if (info->retry_and_idle_policy && - info->retry_and_idle_policy->secs_since_valid_ping) { - context->default_retry.secs_since_valid_ping = - info->retry_and_idle_policy->secs_since_valid_ping; - context->default_retry.secs_since_valid_hangup = - info->retry_and_idle_policy->secs_since_valid_hangup; - } - - /* - * Allocate the per-thread storage for scratchpad buffers, - * and header data pool - */ - u = (uint8_t *)&context[1]; - for (n = 0; n < context->count_threads; n++) { - context->pt[n].serv_buf = u; - u += context->pt_serv_buf_size; - - context->pt[n].context = context; - context->pt[n].tid = n; - - /* - * We overallocated for a fakewsi (can't compose it in the - * pt because size isn't known at that time). point to it - * and zero it down. Fakewsis are needed to make callbacks work - * when the source of the callback is not actually from a wsi - * context. - */ - context->pt[n].fake_wsi = (struct lws *)u; - u += sizeof(struct lws); - - memset(context->pt[n].fake_wsi, 0, sizeof(struct lws)); - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - context->pt[n].http.ah_list = NULL; - context->pt[n].http.ah_pool_length = 0; -#endif - lws_pt_mutex_init(&context->pt[n]); -#if defined(LWS_WITH_SEQUENCER) - lws_seq_pt_init(&context->pt[n]); -#endif - - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) { - if (ar->pt_init_destroy) - ar->pt_init_destroy(context, info, - &context->pt[n], 0); - } LWS_FOR_EVERY_AVAILABLE_ROLE_END; - -#if defined(LWS_WITH_CGI) - role_ops_cgi.pt_init_destroy(context, info, &context->pt[n], 0); -#endif - } - - lwsl_info(" Threads: %d each %d fds\n", context->count_threads, - context->fd_limit_per_thread); - - if (!info->ka_interval && info->ka_time > 0) { - lwsl_err("info->ka_interval can't be 0 if ka_time used\n"); - return NULL; - } - -#if defined(LWS_WITH_PEER_LIMITS) - /* scale the peer hash table according to the max fds for the process, - * so that the max list depth averages 16. Eg, 1024 fd -> 64, - * 102400 fd -> 6400 - */ - - context->pl_hash_elements = - (context->count_threads * context->fd_limit_per_thread) / 16; - context->pl_hash_table = lws_zalloc(sizeof(struct lws_peer *) * - context->pl_hash_elements, "peer limits hash table"); - - context->ip_limit_ah = info->ip_limit_ah; - context->ip_limit_wsi = info->ip_limit_wsi; -#endif - - lwsl_info(" mem: context: %5lu B (%ld ctx + (%ld thr x %d))\n", - (long)sizeof(struct lws_context) + - (context->count_threads * context->pt_serv_buf_size), - (long)sizeof(struct lws_context), - (long)context->count_threads, - context->pt_serv_buf_size); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - lwsl_info(" mem: http hdr size: (%u + %lu), max count %u\n", - context->max_http_header_data, - (long)sizeof(struct allocated_headers), - context->max_http_header_pool); -#endif - - /* - * fds table contains pollfd structs for as many pollfds as we can - * handle... spread across as many service threads as we have going - */ - n = sizeof(struct lws_pollfd) * context->count_threads * - context->fd_limit_per_thread; - context->pt[0].fds = lws_zalloc(n, "fds table"); - if (context->pt[0].fds == NULL) { - lwsl_err("OOM allocating %d fds\n", context->max_fds); - goto bail; - } - lwsl_info(" mem: pollfd map: %5u B\n", n); -#endif -#if defined(LWS_WITH_SERVER) - if (info->server_string) { - context->server_string = info->server_string; - context->server_string_len = (short) - strlen(context->server_string); - } -#endif - -#if LWS_MAX_SMP > 1 - /* each thread serves his own chunk of fds */ - for (n = 1; n < (int)context->count_threads; n++) - context->pt[n].fds = context->pt[n - 1].fds + - context->fd_limit_per_thread; -#endif - - if (lws_plat_init(context, info)) - goto bail; - -#if defined(LWS_WITH_NETWORK) - if (context->event_loop_ops->init_context) - if (context->event_loop_ops->init_context(context, info)) - goto bail; - - - if (context->event_loop_ops->init_pt) - for (n = 0; n < context->count_threads; n++) { - void *lp = NULL; - - if (info->foreign_loops) - lp = info->foreign_loops[n]; - - if (context->event_loop_ops->init_pt(context, lp, n)) - goto bail; - } - - if (lws_create_event_pipes(context)) - goto bail; -#endif - - lws_context_init_ssl_library(info); - - context->user_space = info->user; - -#if defined(LWS_WITH_SERVER) - strcpy(context->canonical_hostname, "unknown"); -#if defined(LWS_WITH_NETWORK) - lws_server_get_canonical_hostname(context, info); -#endif -#endif - -#if defined(LWS_WITH_STATS) - context->pt[0].sul_stats.cb = lws_sul_stats_cb; - __lws_sul_insert(&context->pt[0].pt_sul_owner, &context->pt[0].sul_stats, - 10 * LWS_US_PER_SEC); -#endif -#if defined(LWS_WITH_PEER_LIMITS) - context->pt[0].sul_peer_limits.cb = lws_sul_peer_limits_cb; - __lws_sul_insert(&context->pt[0].pt_sul_owner, - &context->pt[0].sul_peer_limits, 10 * LWS_US_PER_SEC); -#endif - -#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) - memcpy(context->caps, info->caps, sizeof(context->caps)); - context->count_caps = info->count_caps; -#endif - - -#if defined(LWS_WITH_NETWORK) - -#if defined(LWS_WITH_SYS_ASYNC_DNS) || defined(LWS_WITH_SYS_NTPCLIENT) || \ - defined(LWS_WITH_SYS_DHCP_CLIENT) - { - /* - * system vhost - */ - - struct lws_context_creation_info ii; - const struct lws_protocols *pp[4]; - struct lws_vhost *vh; -#if defined(LWS_WITH_SYS_ASYNC_DNS) - extern const struct lws_protocols lws_async_dns_protocol; -#endif -#if defined(LWS_WITH_SYS_NTPCLIENT) - extern const struct lws_protocols lws_system_protocol_ntpc; -#endif -#if defined(LWS_WITH_SYS_DHCP_CLIENT) - extern const struct lws_protocols lws_system_protocol_dhcpc; -#endif - - n = 0; -#if defined(LWS_WITH_SYS_ASYNC_DNS) - pp[n++] = &lws_async_dns_protocol; -#endif -#if defined(LWS_WITH_SYS_NTPCLIENT) - pp[n++] = &lws_system_protocol_ntpc; -#endif -#if defined(LWS_WITH_SYS_DHCP_CLIENT) - pp[n++] = &lws_system_protocol_dhcpc; -#endif - pp[n] = NULL; - - memset(&ii, 0, sizeof(ii)); - ii.vhost_name = "system"; - ii.pprotocols = pp; - - vh = lws_create_vhost(context, &ii); - if (!vh) { - lwsl_err("%s: failed to create system vhost\n", - __func__); - goto bail; - } - - context->vhost_system = vh; - - if (lws_protocol_init_vhost(vh, NULL)) { - lwsl_err("%s: failed to init system vhost\n", __func__); - goto bail; - } -#if defined(LWS_WITH_SYS_ASYNC_DNS) - if (lws_async_dns_init(context)) - goto bail; -#endif - } -#endif - - /* - * init the lws_state mgr for the system state - */ -#if defined(_DEBUG) - context->mgr_system.state_names = system_state_names; -#endif - context->mgr_system.name = "system"; - context->mgr_system.state = LWS_SYSTATE_CONTEXT_CREATED; - context->mgr_system.parent = context; - - context->protocols_notify.name = "prot_init"; - context->protocols_notify.notify_cb = lws_state_notify_protocol_init; - - lws_state_reg_notifier(&context->mgr_system, &context->protocols_notify); - - /* - * insert user notifiers here so they can participate with vetoing us - * trying to jump straight to operational, or at least observe us - * reaching 'operational', before we returned from context creation. - */ - - lws_state_reg_notifier_list(&context->mgr_system, - info->register_notifier_list); - - /* - * if he's not saying he'll make his own vhosts later then act - * compatibly and make a default vhost using the data in the info - */ - if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) - if (!lws_create_vhost(context, info)) { - lwsl_err("Failed to create default vhost\n"); - -#if defined(LWS_WITH_PEER_LIMITS) - lws_free_set_NULL(context->pl_hash_table); -#endif - goto fail_clean_pipes; - } - -#if defined(LWS_WITH_SECURE_STREAMS) - - if (context->pss_policies_json) { - /* - * You must create your context with the explicit vhosts flag - * in order to use secure streams - */ - assert(lws_check_opt(info->options, - LWS_SERVER_OPTION_EXPLICIT_VHOSTS)); - - if (lws_ss_policy_parse_begin(context)) - goto bail; - - n = lws_ss_policy_parse(context, - (uint8_t *)context->pss_policies_json, - strlen(context->pss_policies_json)); - if (n != LEJP_CONTINUE && n < 0) - goto bail; - - if (lws_ss_policy_set(context, "hardcoded")) { - lwsl_err("%s: policy set failed\n", __func__); - goto bail; - } - } else - lws_create_vhost(context, info); -#endif - - lws_context_init_extensions(info, context); - - lwsl_info(" mem: per-conn: %5lu bytes + protocol rx buf\n", - (unsigned long)sizeof(struct lws)); - - /* - * drop any root privs for this process - * to listen on port < 1023 we would have needed root, but now we are - * listening, we don't want the power for anything else - */ - if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) - if (lws_plat_drop_app_privileges(context, 1)) - goto bail; - - /* - * We want to move on the syste, state as far as it can go towards - * OPERATIONAL now. But we have to return from here first so the user - * code that called us can set its copy of context, which it may be - * relying on to perform operations triggered by the state change. - * - * We set up a sul to come back immediately and do the state change. - */ - - lws_sul_schedule(context, 0, &context->sul_system_state, - lws_context_creation_completion_cb, 1); - - /* expedite post-context init (eg, protocols) */ - lws_cancel_service(context); -#endif - - return context; - -#if defined(LWS_WITH_NETWORK) -fail_clean_pipes: - for (n = 0; n < context->count_threads; n++) - lws_destroy_event_pipe(context->pt[n].pipe_wsi); - - lws_free_set_NULL(context->pt[0].fds); - lws_plat_context_late_destroy(context); - lws_free_set_NULL(context); - - return NULL; -#endif - -bail: - lws_context_destroy(context); - - return NULL; - -#if defined(LWS_WITH_NETWORK) -fail_event_libs: - lwsl_err("Requested event library support not configured, available:\n"); - { - extern const struct lws_event_loop_ops *available_event_libs[]; - const struct lws_event_loop_ops **elops = available_event_libs; - - while (*elops) { - lwsl_err(" - %s\n", (*elops)->name); - elops++; - } - } -#endif - lws_free(context); - - return NULL; -} - -int -lws_context_is_deprecated(struct lws_context *context) -{ - return context->deprecated; -} - -/* - * When using an event loop, the context destruction is in three separate - * parts. This is to cover both internal and foreign event loops cleanly. - * - * - lws_context_destroy() simply starts a soft close of all wsi and - * related allocations. The event loop continues. - * - * As the closes complete in the event loop, reference counting is used - * to determine when everything is closed. It then calls - * lws_context_destroy2(). - * - * - lws_context_destroy2() cleans up the rest of the higher-level logical - * lws pieces like vhosts. If the loop was foreign, it then proceeds to - * lws_context_destroy3(). If it the loop is internal, it stops the - * internal loops and waits for lws_context_destroy() to be called again - * outside the event loop (since we cannot destroy the loop from - * within the loop). That will cause lws_context_destroy3() to run - * directly. - * - * - lws_context_destroy3() destroys any internal event loops and then - * destroys the context itself, setting what was info.pcontext to NULL. - */ - -/* - * destroy the actual context itself - */ - -static void -lws_context_destroy3(struct lws_context *context) -{ - struct lws_context **pcontext_finalize = context->pcontext_finalize; - int n; - -#if defined(LWS_WITH_NETWORK) - - context->finalize_destroy_after_internal_loops_stopped = 1; - if (context->event_loop_ops->destroy_context2) - context->event_loop_ops->destroy_context2(context); - - for (n = 0; n < context->count_threads; n++) { - struct lws_context_per_thread *pt = &context->pt[n]; - (void)pt; -#if defined(LWS_WITH_SEQUENCER) - lws_seq_destroy_all_on_pt(pt); -#endif - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) { - if (ar->pt_init_destroy) - ar->pt_init_destroy(context, NULL, pt, 1); - } LWS_FOR_EVERY_AVAILABLE_ROLE_END; - -#if defined(LWS_WITH_CGI) - role_ops_cgi.pt_init_destroy(context, NULL, pt, 1); -#endif -#if 0 - if (context->event_loop_ops->destroy_pt) - context->event_loop_ops->destroy_pt(context, n); -#endif - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - while (pt->http.ah_list) - _lws_destroy_ah(pt, pt->http.ah_list); -#endif - } - -#if defined(LWS_WITH_SYS_ASYNC_DNS) - lws_async_dns_deinit(&context->async_dns); -#endif -#if defined(LWS_WITH_SYS_DHCP_CLIENT) - lws_dhcpc_remove(context, NULL); -#endif - - if (context->pt[0].fds) - lws_free_set_NULL(context->pt[0].fds); -#endif - lws_context_deinit_ssl_library(context); - -#if defined(LWS_WITH_DETAILED_LATENCIES) - if (context->latencies_fd != -1) - compatible_close(context->latencies_fd); -#endif - - for (n = 0; n < LWS_SYSBLOB_TYPE_COUNT; n++) - lws_system_blob_destroy( - lws_system_get_blob(context, n, 0)); - - lws_free(context); - lwsl_info("%s: ctx %p freed\n", __func__, context); - - if (pcontext_finalize) - *pcontext_finalize = NULL; -} - -/* - * really start destroying things - */ - -void -lws_context_destroy2(struct lws_context *context) -{ -#if defined(LWS_WITH_NETWORK) - struct lws_vhost *vh = NULL, *vh1; - int n; -#endif -#if defined(LWS_WITH_PEER_LIMITS) - uint32_t nu; -#endif - - lwsl_info("%s: ctx %p\n", __func__, context); - - lws_context_lock(context, "context destroy 2"); /* ------ context { */ - - context->being_destroyed2 = 1; -#if defined(LWS_WITH_NETWORK) - - /* - * We're going to trash things like vhost-protocols - * So we need to finish dealing with wsi close that - * might make callbacks first - */ - for (n = 0; n < context->count_threads; n++) { - struct lws_context_per_thread *pt = &context->pt[n]; - - (void)pt; - -#if defined(LWS_WITH_SECURE_STREAMS) - lws_dll2_foreach_safe(&pt->ss_owner, NULL, lws_ss_destroy_dll); - if (context->ac_policy) - lwsac_free(&context->ac_policy); -#endif - -#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) - lws_dll2_foreach_safe(&pt->ss_client_owner, NULL, lws_sspc_destroy_dll); -#endif - -#if defined(LWS_WITH_SEQUENCER) - lws_seq_destroy_all_on_pt(pt); -#endif - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) { - if (ar->pt_init_destroy) - ar->pt_init_destroy(context, NULL, pt, 1); - } LWS_FOR_EVERY_AVAILABLE_ROLE_END; - -#if defined(LWS_WITH_CGI) - role_ops_cgi.pt_init_destroy(context, NULL, pt, 1); -#endif - - if (context->event_loop_ops->destroy_pt) - context->event_loop_ops->destroy_pt(context, n); - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - while (pt->http.ah_list) - _lws_destroy_ah(pt, pt->http.ah_list); -#endif - } - - /* - * free all the per-vhost allocations - */ - - vh = context->vhost_list; - while (vh) { - vh1 = vh->vhost_next; - __lws_vhost_destroy2(vh); - vh = vh1; - } - - lwsl_debug("%p: post vh listl\n", __func__); - - /* remove ourselves from the pending destruction list */ - - while (context->vhost_pending_destruction_list) - /* removes itself from list */ - __lws_vhost_destroy2(context->vhost_pending_destruction_list); -#endif - - lwsl_debug("%p: post pdl\n", __func__); - - lws_stats_log_dump(context); -#if defined(LWS_WITH_NETWORK) - lws_ssl_context_destroy(context); -#endif - lws_plat_context_late_destroy(context); - -#if defined(LWS_WITH_PEER_LIMITS) - for (nu = 0; nu < context->pl_hash_elements; nu++) { - lws_start_foreach_llp(struct lws_peer **, peer, - context->pl_hash_table[nu]) { - struct lws_peer *df = *peer; - *peer = df->next; - lws_free(df); - continue; - } lws_end_foreach_llp(peer, next); - } - lws_free(context->pl_hash_table); -#endif - - lwsl_debug("%p: baggage\n", __func__); - - if (context->external_baggage_free_on_destroy) - free(context->external_baggage_free_on_destroy); - -#if defined(LWS_WITH_NETWORK) - lws_check_deferred_free(context, 0, 1); -#endif - - lws_context_unlock(context); /* } context ------ */ - -#if defined(LWS_WITH_NETWORK) - if (context->event_loop_ops->destroy_context2) - if (context->event_loop_ops->destroy_context2(context)) { - context->finalize_destroy_after_internal_loops_stopped = 1; - return; - } - - lwsl_debug("%p: post dc2\n", __func__); - - if (!context->pt[0].event_loop_foreign) { - int n; - for (n = 0; n < context->count_threads; n++) - if (context->pt[n].inside_service) { - lwsl_debug("%p: bailing as inside service\n", __func__); - return; - } - } -#endif - - lws_context_destroy3(context); -} - -#if defined(LWS_WITH_NETWORK) -static void -lws_pt_destroy(struct lws_context_per_thread *pt) -{ - volatile struct lws_foreign_thread_pollfd *ftp, *next; - volatile struct lws_context_per_thread *vpt; - - assert(!pt->is_destroyed); - pt->destroy_self = 0; - - vpt = (volatile struct lws_context_per_thread *)pt; - ftp = vpt->foreign_pfd_list; - while (ftp) { - next = ftp->next; - lws_free((void *)ftp); - ftp = next; - } - vpt->foreign_pfd_list = NULL; - - if (pt->pipe_wsi) - lws_destroy_event_pipe(pt->pipe_wsi); - pt->pipe_wsi = NULL; - - while (pt->fds_count) { - struct lws *wsi = wsi_from_fd(pt->context, pt->fds[0].fd); - - if (!wsi) - break; - - lws_close_free_wsi(wsi, - LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY, - "ctx destroy" - /* no protocol close */); - } - lws_pt_mutex_destroy(pt); - - pt->is_destroyed = 1; - - lwsl_info("%s: pt destroyed\n", __func__); -} -#endif - -/* - * Begin the context takedown - */ - -void -lws_context_destroy(struct lws_context *context) -{ -#if defined(LWS_WITH_NETWORK) - struct lws_vhost *vh = NULL; - int m, deferred_pt = 0; -#endif - - if (!context || context->inside_context_destroy) - return; - - context->inside_context_destroy = 1; - -#if defined(LWS_WITH_NETWORK) - if (context->finalize_destroy_after_internal_loops_stopped) { - if (context->event_loop_ops->destroy_context2) - context->event_loop_ops->destroy_context2(context); - lws_context_destroy3(context); - /* context is invalid, no need to reset inside flag */ - return; - } -#endif - if (context->being_destroyed1) { - if (!context->being_destroyed2) { - lws_context_destroy2(context); - - return; - } - lwsl_info("%s: ctx %p: already being destroyed\n", - __func__, context); - - lws_context_destroy3(context); - /* context is invalid, no need to reset inside flag */ - return; - } - - lwsl_info("%s: ctx %p\n", __func__, context); - - context->being_destroyed = 1; - -#if defined(LWS_WITH_NETWORK) - lws_state_transition(&context->mgr_system, LWS_SYSTATE_POLICY_INVALID); - m = context->count_threads; - - while (m--) { - struct lws_context_per_thread *pt = &context->pt[m]; - - if (pt->is_destroyed) - continue; - - if (pt->inside_lws_service) { - pt->destroy_self = 1; - deferred_pt = 1; - continue; - } - - lws_pt_destroy(pt); - } - - if (deferred_pt) { - lwsl_info("%s: waiting for deferred pt close\n", __func__); - lws_cancel_service(context); - goto out; - } - - context->being_destroyed1 = 1; - context->requested_kill = 1; - - /* - * inform all the protocols that they are done and will have no more - * callbacks. - * - * We can't free things until after the event loop shuts down. - */ - if (context->protocol_init_done) - vh = context->vhost_list; - while (vh) { - struct lws_vhost *vhn = vh->vhost_next; - lws_vhost_destroy1(vh); - vh = vhn; - } -#endif - - lws_plat_context_early_destroy(context); - -#if defined(LWS_WITH_NETWORK) - - /* - * We face two different needs depending if foreign loop or not. - * - * 1) If foreign loop, we really want to advance the destroy_context() - * past here, and block only for libuv-style async close completion. - * - * 2a) If poll, and we exited by ourselves and are calling a final - * destroy_context() outside of any service already, we want to - * advance all the way in one step. - * - * 2b) If poll, and we are reacting to a SIGINT, service thread(s) may - * be in poll wait or servicing. We can't advance the - * destroy_context() to the point it's freeing things; we have to - * leave that for the final destroy_context() after the service - * thread(s) are finished calling for service. - */ - - if (context->event_loop_ops->destroy_context1) { - context->event_loop_ops->destroy_context1(context); - - goto out; - } -#endif - -#if defined(LWS_PLAT_FREERTOS) -#if defined(LWS_AMAZON_RTOS) - context->last_free_heap = xPortGetFreeHeapSize(); -#else - context->last_free_heap = esp_get_free_heap_size(); -#endif -#endif - - context->inside_context_destroy = 0; - lws_context_destroy2(context); - - return; - -#if defined(LWS_WITH_NETWORK) -out: - context->inside_context_destroy = 0; -#endif -} - -struct lws_context * -lws_system_context_from_system_mgr(lws_state_manager_t *mgr) -{ -#if defined(LWS_WITH_NETWORK) - return lws_container_of(mgr, struct lws_context, mgr_system); -#else - return NULL; -#endif -} diff -Nru libwebsockets-4.0.20/lib/core/libwebsockets.c libwebsockets-2.4.2/lib/core/libwebsockets.c --- libwebsockets-4.0.20/lib/core/libwebsockets.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core/libwebsockets.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1189 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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" - -#ifdef LWS_HAVE_SYS_TYPES_H -#include -#endif - -void -lws_ser_wu16be(uint8_t *b, uint16_t u) -{ - *b++ = (uint8_t)(u >> 8); - *b = (uint8_t)u; -} - -void -lws_ser_wu32be(uint8_t *b, uint32_t u32) -{ - *b++ = (uint8_t)(u32 >> 24); - *b++ = (uint8_t)(u32 >> 16); - *b++ = (uint8_t)(u32 >> 8); - *b = (uint8_t)u32; -} - -void -lws_ser_wu64be(uint8_t *b, uint64_t u64) -{ - lws_ser_wu32be(b, (uint32_t)(u64 >> 32)); - lws_ser_wu32be(b + 4, (uint32_t)u64); -} - -uint16_t -lws_ser_ru16be(const uint8_t *b) -{ - return (b[0] << 8) | b[1]; -} - -uint32_t -lws_ser_ru32be(const uint8_t *b) -{ - return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; -} - -uint64_t -lws_ser_ru64be(const uint8_t *b) -{ - return (((uint64_t)lws_ser_ru32be(b)) << 32) | lws_ser_ru32be(b + 4); -} - -int -lws_vbi_encode(uint64_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, buf); -} - -int -lws_vbi_decode(const void *buf, uint64_t *value, size_t len) -{ - const uint8_t *p = (const uint8_t *)buf, *end = p + len; - uint64_t v = 0; - int s = 0; - - while (p < end) { - v |= (((uint64_t)(*p)) & 0x7f) << s; - if (*p & 0x80) { - *value = v; - - return lws_ptr_diff(p, buf); - } - s += 7; - if (s >= 64) - return 0; - p++; - } - - return 0; -} - -signed char char_to_hex(const char c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - - return -1; -} - -int -lws_hex_to_byte_array(const char *h, uint8_t *dest, int max) -{ - uint8_t *odest = dest; - - while (max-- && *h) { - int t = char_to_hex(*h++), t1; - - if (!*h || t < 0) - return -1; - - t1 = char_to_hex(*h++); - if (t1 < 0) - return -1; - - *dest++ = (t << 4) | t1; - } - - if (max < 0) - return -1; - - return lws_ptr_diff(dest, odest); -} - - -#if !defined(LWS_PLAT_OPTEE) - -#if defined(LWS_WITH_FILE_OPS) -int lws_open(const char *__file, int __oflag, ...) -{ - va_list ap; - int n; - - va_start(ap, __oflag); - if (((__oflag & O_CREAT) == O_CREAT) -#if defined(O_TMPFILE) - || ((__oflag & O_TMPFILE) == O_TMPFILE) -#endif - ) - /* last arg is really a mode_t. But windows... */ - n = open(__file, __oflag, va_arg(ap, uint32_t)); - else - n = open(__file, __oflag); - va_end(ap); - - if (n != -1 && lws_plat_apply_FD_CLOEXEC(n)) { - close(n); - - return -1; - } - - return n; -} -#endif -#endif - -int -lws_pthread_self_to_tsi(struct lws_context *context) -{ -#if LWS_MAX_SMP > 1 - pthread_t ps = pthread_self(); - struct lws_context_per_thread *pt = &context->pt[0]; - int n; - - for (n = 0; n < context->count_threads; n++) { - if (pthread_equal(ps, pt->self)) - return n; - pt++; - } - - return -1; -#else - return 0; -#endif -} - -void * -lws_context_user(struct lws_context *context) -{ - return context->user_space; -} - -void -lws_explicit_bzero(void *p, size_t len) -{ - volatile uint8_t *vp = p; - - while (len--) - *vp++ = 0; -} - -#if !(defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK)) - -/** - * lws_now_secs() - seconds since 1970-1-1 - * - */ -unsigned long -lws_now_secs(void) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - return tv.tv_sec; -} - -#endif - -#if defined(LWS_WITH_SERVER) -const char * -lws_canonical_hostname(struct lws_context *context) -{ - return (const char *)context->canonical_hostname; -} -#endif - -int -lws_get_count_threads(struct lws_context *context) -{ - return context->count_threads; -} - -static const unsigned char e0f4[] = { - 0xa0 | ((2 - 1) << 2) | 1, /* e0 */ - 0x80 | ((4 - 1) << 2) | 1, /* e1 */ - 0x80 | ((4 - 1) << 2) | 1, /* e2 */ - 0x80 | ((4 - 1) << 2) | 1, /* e3 */ - 0x80 | ((4 - 1) << 2) | 1, /* e4 */ - 0x80 | ((4 - 1) << 2) | 1, /* e5 */ - 0x80 | ((4 - 1) << 2) | 1, /* e6 */ - 0x80 | ((4 - 1) << 2) | 1, /* e7 */ - 0x80 | ((4 - 1) << 2) | 1, /* e8 */ - 0x80 | ((4 - 1) << 2) | 1, /* e9 */ - 0x80 | ((4 - 1) << 2) | 1, /* ea */ - 0x80 | ((4 - 1) << 2) | 1, /* eb */ - 0x80 | ((4 - 1) << 2) | 1, /* ec */ - 0x80 | ((2 - 1) << 2) | 1, /* ed */ - 0x80 | ((4 - 1) << 2) | 1, /* ee */ - 0x80 | ((4 - 1) << 2) | 1, /* ef */ - 0x90 | ((3 - 1) << 2) | 2, /* f0 */ - 0x80 | ((4 - 1) << 2) | 2, /* f1 */ - 0x80 | ((4 - 1) << 2) | 2, /* f2 */ - 0x80 | ((4 - 1) << 2) | 2, /* f3 */ - 0x80 | ((1 - 1) << 2) | 2, /* f4 */ - - 0, /* s0 */ - 0x80 | ((4 - 1) << 2) | 0, /* s2 */ - 0x80 | ((4 - 1) << 2) | 1, /* s3 */ -}; - -int -lws_check_byte_utf8(unsigned char state, unsigned char c) -{ - unsigned char s = state; - - if (!s) { - if (c >= 0x80) { - if (c < 0xc2 || c > 0xf4) - return -1; - if (c < 0xe0) - return 0x80 | ((4 - 1) << 2); - else - return e0f4[c - 0xe0]; - } - - return s; - } - if (c < (s & 0xf0) || c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30)) - return -1; - - return e0f4[21 + (s & 3)]; -} - -int -lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len) -{ - unsigned char s = *state; - - while (len--) { - unsigned char c = *buf++; - - if (!s) { - if (c >= 0x80) { - if (c < 0xc2 || c > 0xf4) - return 1; - if (c < 0xe0) - s = 0x80 | ((4 - 1) << 2); - else - s = e0f4[c - 0xe0]; - } - } else { - if (c < (s & 0xf0) || - c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30)) - return 1; - s = e0f4[21 + (s & 3)]; - } - } - - *state = s; - - return 0; -} - - -char * -lws_strdup(const char *s) -{ - char *d = lws_malloc(strlen(s) + 1, "strdup"); - - if (d) - strcpy(d, s); - - return d; -} - -static const char *hex = "0123456789ABCDEF"; - -const char * -lws_sql_purify(char *escaped, const char *string, int len) -{ - const char *p = string; - char *q = escaped; - - while (*p && len-- > 2) { - if (*p == '\'') { - *q++ = '\''; - *q++ = '\''; - len --; - p++; - } else - *q++ = *p++; - } - *q = '\0'; - - return escaped; -} - -int -lws_sql_purify_len(const char *p) -{ - int olen = 0; - - while (*p) { - if (*p++ == '\'') - olen++; - olen++; - } - - return olen; -} - -const char * -lws_json_purify(char *escaped, const char *string, int len, int *in_used) -{ - const char *p = string; - char *q = escaped; - - if (!p) { - escaped[0] = '\0'; - return escaped; - } - - while (*p && len-- > 6) { - if (*p == '\t') { - p++; - *q++ = '\\'; - *q++ = 't'; - continue; - } - - if (*p == '\n') { - p++; - *q++ = '\\'; - *q++ = 'n'; - continue; - } - - if (*p == '\r') { - p++; - *q++ = '\\'; - *q++ = 'r'; - continue; - } - - if (*p == '\"' || *p == '\\' || *p < 0x20) { - *q++ = '\\'; - *q++ = 'u'; - *q++ = '0'; - *q++ = '0'; - *q++ = hex[((*p) >> 4) & 15]; - *q++ = hex[(*p) & 15]; - len -= 5; - p++; - } else - *q++ = *p++; - } - *q = '\0'; - - if (in_used) - *in_used = lws_ptr_diff(p, string); - - return escaped; -} - -int -lws_json_purify_len(const char *string) -{ - int len = 0; - const char *p = string; - - while (*p) { - if (*p == '\t' || *p == '\n' || *p == '\r') { - p++; - len += 2; - continue; - } - - if (*p == '\"' || *p == '\\' || *p < 0x20) { - len += 6; - p++; - continue; - } - p++; - len++; - } - - return len; -} - -void -lws_filename_purify_inplace(char *filename) -{ - while (*filename) { - - if (*filename == '.' && filename[1] == '.') { - *filename = '_'; - filename[1] = '_'; - } - - if (*filename == ':' || - *filename == '\\' || - *filename == '$' || - *filename == '%') - *filename = '_'; - - filename++; - } -} - -const char * -lws_urlencode(char *escaped, const char *string, int len) -{ - const char *p = string; - char *q = escaped; - - while (*p && len-- > 3) { - if (*p == ' ') { - *q++ = '+'; - p++; - continue; - } - if ((*p >= '0' && *p <= '9') || - (*p >= 'A' && *p <= 'Z') || - (*p >= 'a' && *p <= 'z')) { - *q++ = *p++; - continue; - } - *q++ = '%'; - *q++ = hex[(*p >> 4) & 0xf]; - *q++ = hex[*p & 0xf]; - - len -= 2; - p++; - } - *q = '\0'; - - return escaped; -} - -int -lws_urldecode(char *string, const char *escaped, int len) -{ - int state = 0, n; - char sum = 0; - - while (*escaped && len) { - switch (state) { - case 0: - if (*escaped == '%') { - state++; - escaped++; - continue; - } - if (*escaped == '+') { - escaped++; - *string++ = ' '; - len--; - continue; - } - *string++ = *escaped++; - len--; - break; - case 1: - n = char_to_hex(*escaped); - if (n < 0) - return -1; - escaped++; - sum = n << 4; - state++; - break; - - case 2: - n = char_to_hex(*escaped); - if (n < 0) - return -1; - escaped++; - *string++ = sum | n; - len--; - state = 0; - break; - } - - } - *string = '\0'; - - return 0; -} - -int -lws_finalize_startup(struct lws_context *context) -{ - if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) - if (lws_plat_drop_app_privileges(context, 1)) - return 1; - - return 0; -} - -void -lws_get_effective_uid_gid(struct lws_context *context, int *uid, int *gid) -{ - *uid = context->uid; - *gid = context->gid; -} - -int -lws_snprintf(char *str, size_t size, const char *format, ...) -{ - va_list ap; - int n; - - if (!size) - return 0; - - va_start(ap, format); - n = vsnprintf(str, size, format, ap); - va_end(ap); - - if (n >= (int)size) - return (int)size; - - return n; -} - -char * -lws_strncpy(char *dest, const char *src, size_t size) -{ - strncpy(dest, src, size - 1); - dest[size - 1] = '\0'; - - return dest; -} - -int -lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len) -{ - const uint8_t *pa = a, *pb = b; - uint8_t sum = 0; - - while (len--) - sum |= (*pa++ ^ *pb++); - - return sum; -} - - -typedef enum { - LWS_TOKZS_LEADING_WHITESPACE, - LWS_TOKZS_QUOTED_STRING, - LWS_TOKZS_TOKEN, - LWS_TOKZS_TOKEN_POST_TERMINAL -} lws_tokenize_state; - -lws_tokenize_elem -lws_tokenize(struct lws_tokenize *ts) -{ - const char *rfc7230_delims = "(),/:;<=>?@[\\]{}"; - lws_tokenize_state state = LWS_TOKZS_LEADING_WHITESPACE; - char c, flo = 0, d_minus = '-', d_dot = '.', s_minus = '\0', - s_dot = '\0', skipping = 0; - signed char num = (ts->flags & LWS_TOKENIZE_F_NO_INTEGERS) ? 0 : -1; - int utf8 = 0; - - /* for speed, compute the effect of the flags outside the loop */ - - if (ts->flags & LWS_TOKENIZE_F_MINUS_NONTERM) { - d_minus = '\0'; - s_minus = '-'; - } - if (ts->flags & LWS_TOKENIZE_F_DOT_NONTERM) { - d_dot = '\0'; - s_dot = '.'; - } - - ts->token = NULL; - ts->token_len = 0; - - while (ts->len) { - c = *ts->start++; - ts->len--; - - utf8 = lws_check_byte_utf8((unsigned char)utf8, c); - if (utf8 < 0) - return LWS_TOKZE_ERR_BROKEN_UTF8; - - if (!c) - break; - - if (skipping) { - if (c != '\r' && c != '\n') - continue; - else - skipping = 0; - } - - /* comment */ - - if (ts->flags & LWS_TOKENIZE_F_HASH_COMMENT && - state != LWS_TOKZS_QUOTED_STRING && - c == '#') { - skipping = 1; - continue; - } - - /* whitespace */ - - if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || - c == '\f') { - switch (state) { - case LWS_TOKZS_LEADING_WHITESPACE: - case LWS_TOKZS_TOKEN_POST_TERMINAL: - continue; - case LWS_TOKZS_QUOTED_STRING: - ts->token_len++; - continue; - case LWS_TOKZS_TOKEN: - /* we want to scan forward to look for = */ - - state = LWS_TOKZS_TOKEN_POST_TERMINAL; - continue; - } - } - - /* quoted string */ - - if (c == '\"') { - if (state == LWS_TOKZS_QUOTED_STRING) - return LWS_TOKZE_QUOTED_STRING; - - /* starting a quoted string */ - - if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) { - if (ts->delim == LWSTZ_DT_NEED_DELIM) - return LWS_TOKZE_ERR_COMMA_LIST; - ts->delim = LWSTZ_DT_NEED_DELIM; - } - - state = LWS_TOKZS_QUOTED_STRING; - ts->token = ts->start; - ts->token_len = 0; - - continue; - } - - /* token= aggregation */ - - if (c == '=' && (state == LWS_TOKZS_TOKEN_POST_TERMINAL || - state == LWS_TOKZS_TOKEN)) { - if (num == 1) - return LWS_TOKZE_ERR_NUM_ON_LHS; - /* swallow the = */ - return LWS_TOKZE_TOKEN_NAME_EQUALS; - } - - /* optional token: aggregation */ - - if ((ts->flags & LWS_TOKENIZE_F_AGG_COLON) && c == ':' && - (state == LWS_TOKZS_TOKEN_POST_TERMINAL || - state == LWS_TOKZS_TOKEN)) - /* swallow the : */ - return LWS_TOKZE_TOKEN_NAME_COLON; - - /* aggregate . in a number as a float */ - - if (c == '.' && !(ts->flags & LWS_TOKENIZE_F_NO_FLOATS) && - state == LWS_TOKZS_TOKEN && num == 1) { - if (flo) - return LWS_TOKZE_ERR_MALFORMED_FLOAT; - flo = 1; - ts->token_len++; - continue; - } - - /* - * Delimiter... by default anything that: - * - * - isn't matched earlier, or - * - is [A-Z, a-z, 0-9, _], and - * - is not a partial utf8 char - * - * is a "delimiter", it marks the end of a token and is itself - * reported as a single LWS_TOKZE_DELIMITER each time. - * - * However with LWS_TOKENIZE_F_RFC7230_DELIMS flag, tokens may - * contain any noncontrol character that isn't defined in - * rfc7230_delims, and only characters listed there are treated - * as delimiters. - */ - - if (!utf8 && - ((ts->flags & LWS_TOKENIZE_F_RFC7230_DELIMS && - strchr(rfc7230_delims, c) && c > 32) || - ((!(ts->flags & LWS_TOKENIZE_F_RFC7230_DELIMS) && - (c < '0' || c > '9') && (c < 'A' || c > 'Z') && - (c < 'a' || c > 'z') && c != '_') && - c != s_minus && c != s_dot) || - c == d_minus || c == d_dot - ) && - !((ts->flags & LWS_TOKENIZE_F_SLASH_NONTERM) && c == '/')) { - switch (state) { - case LWS_TOKZS_LEADING_WHITESPACE: - if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) { - if (c != ',' || - ts->delim != LWSTZ_DT_NEED_DELIM) - return LWS_TOKZE_ERR_COMMA_LIST; - ts->delim = LWSTZ_DT_NEED_NEXT_CONTENT; - } - - ts->token = ts->start - 1; - ts->token_len = 1; - return LWS_TOKZE_DELIMITER; - - case LWS_TOKZS_QUOTED_STRING: - ts->token_len++; - continue; - - case LWS_TOKZS_TOKEN_POST_TERMINAL: - case LWS_TOKZS_TOKEN: - /* report the delimiter next time */ - ts->start--; - ts->len++; - goto token_or_numeric; - } - } - - /* anything that's not whitespace or delimiter is payload */ - - switch (state) { - case LWS_TOKZS_LEADING_WHITESPACE: - - if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) { - if (ts->delim == LWSTZ_DT_NEED_DELIM) - return LWS_TOKZE_ERR_COMMA_LIST; - ts->delim = LWSTZ_DT_NEED_DELIM; - } - - state = LWS_TOKZS_TOKEN; - ts->token = ts->start - 1; - ts->token_len = 1; - goto checknum; - - case LWS_TOKZS_QUOTED_STRING: - case LWS_TOKZS_TOKEN: - ts->token_len++; -checknum: - if (!(ts->flags & LWS_TOKENIZE_F_NO_INTEGERS)) { - if (c < '0' || c > '9') - num = 0; - else - if (num < 0) - num = 1; - } - continue; - - case LWS_TOKZS_TOKEN_POST_TERMINAL: - /* report the new token next time */ - ts->start--; - ts->len++; - goto token_or_numeric; - } - } - - /* we ran out of content */ - - if (utf8) /* ended partway through a multibyte char */ - return LWS_TOKZE_ERR_BROKEN_UTF8; - - if (state == LWS_TOKZS_QUOTED_STRING) - return LWS_TOKZE_ERR_UNTERM_STRING; - - if (state != LWS_TOKZS_TOKEN_POST_TERMINAL && - state != LWS_TOKZS_TOKEN) { - if ((ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) && - ts->delim == LWSTZ_DT_NEED_NEXT_CONTENT) - return LWS_TOKZE_ERR_COMMA_LIST; - - return LWS_TOKZE_ENDED; - } - - /* report the pending token */ - -token_or_numeric: - - if (num != 1) - return LWS_TOKZE_TOKEN; - if (flo) - return LWS_TOKZE_FLOAT; - - return LWS_TOKZE_INTEGER; -} - - -int -lws_tokenize_cstr(struct lws_tokenize *ts, char *str, size_t max) -{ - if (ts->token_len + 1 >= max) - return 1; - - memcpy(str, ts->token, ts->token_len); - str[ts->token_len] = '\0'; - - return 0; -} - -void -lws_tokenize_init(struct lws_tokenize *ts, const char *start, int flags) -{ - ts->start = start; - ts->len = 0x7fffffff; - ts->flags = flags; - ts->delim = LWSTZ_DT_NEED_FIRST_CONTENT; -} - - -typedef enum { - LWS_EXPS_LITERAL, - LWS_EXPS_OPEN_OR_LIT, - LWS_EXPS_NAME_OR_CLOSE, - LWS_EXPS_DRAIN, -} lws_strexp_state; - -void -lws_strexp_init(lws_strexp_t *exp, void *priv, lws_strexp_expand_cb cb, - char *out, size_t olen) -{ - memset(exp, 0, sizeof(*exp)); - exp->cb = cb; - exp->out = out; - exp->olen = olen; - exp->state = LWS_EXPS_LITERAL; - exp->priv = priv; -} - -void -lws_strexp_reset_out(lws_strexp_t *exp, char *out, size_t olen) -{ - exp->out = out; - exp->olen = olen; - exp->pos = 0; -} - -int -lws_strexp_expand(lws_strexp_t *exp, const char *in, size_t len, - size_t *pused_in, size_t *pused_out) -{ - size_t used = 0; - int n; - - while (used < len) { - - switch (exp->state) { - case LWS_EXPS_LITERAL: - if (*in == '$') { - exp->state = LWS_EXPS_OPEN_OR_LIT; - break; - } - - exp->out[exp->pos++] = *in; - if (exp->olen - exp->pos < 1) { - *pused_in = used + 1; - *pused_out = exp->pos; - return LSTRX_FILLED_OUT; - } - break; - - case LWS_EXPS_OPEN_OR_LIT: - if (*in == '{') { - exp->state = LWS_EXPS_NAME_OR_CLOSE; - exp->name_pos = 0; - exp->exp_ofs = 0; - break; - } - /* treat as a literal */ - if (exp->olen - exp->pos < 3) - return -1; - - exp->out[exp->pos++] = '$'; - exp->out[exp->pos++] = *in; - if (*in != '$') - exp->state = LWS_EXPS_LITERAL; - break; - - case LWS_EXPS_NAME_OR_CLOSE: - if (*in == '}') { - exp->name[exp->name_pos] = '\0'; - exp->state = LWS_EXPS_DRAIN; - goto drain; - } - if (exp->name_pos >= sizeof(exp->name) - 1) - return LSTRX_FATAL_NAME_TOO_LONG; - - exp->name[exp->name_pos++] = *in; - break; - - case LWS_EXPS_DRAIN: -drain: - *pused_in = used; - n = exp->cb(exp->priv, exp->name, exp->out, &exp->pos, - exp->olen, &exp->exp_ofs); - *pused_out = exp->pos; - if (n == LSTRX_FILLED_OUT || - n == LSTRX_FATAL_NAME_UNKNOWN) - return n; - - exp->state = LWS_EXPS_LITERAL; - break; - } - - used++; - in++; - } - - exp->out[exp->pos] = '\0'; - *pused_in = used; - *pused_out = exp->pos; - - return LSTRX_DONE; -} - - -#if LWS_MAX_SMP > 1 - -void -lws_mutex_refcount_init(struct lws_mutex_refcount *mr) -{ - pthread_mutex_init(&mr->lock, NULL); - mr->last_lock_reason = NULL; - mr->lock_depth = 0; - mr->metadata = 0; - mr->lock_owner = 0; -} - -void -lws_mutex_refcount_destroy(struct lws_mutex_refcount *mr) -{ - pthread_mutex_destroy(&mr->lock); -} - -void -lws_mutex_refcount_lock(struct lws_mutex_refcount *mr, const char *reason) -{ - /* if true, this sequence is atomic because our thread has the lock - * - * - if true, only guy who can race to make it untrue is our thread, - * and we are here. - * - * - if false, only guy who could race to make it true is our thread, - * and we are here - * - * - it can be false and change to a different tid that is also false - */ - if (mr->lock_owner == pthread_self()) { - /* atomic because we only change it if we own the lock */ - mr->lock_depth++; - return; - } - - pthread_mutex_lock(&mr->lock); - /* atomic because only we can have the lock */ - mr->last_lock_reason = reason; - mr->lock_owner = pthread_self(); - mr->lock_depth = 1; - //lwsl_notice("tid %d: lock %s\n", mr->tid, reason); -} - -void -lws_mutex_refcount_unlock(struct lws_mutex_refcount *mr) -{ - if (--mr->lock_depth) - /* atomic because only thread that has the lock can unlock */ - return; - - mr->last_lock_reason = "free"; - mr->lock_owner = 0; - //lwsl_notice("tid %d: unlock %s\n", mr->tid, mr->last_lock_reason); - pthread_mutex_unlock(&mr->lock); -} - -#endif /* SMP */ - - -const char * -lws_cmdline_option(int argc, const char **argv, const char *val) -{ - int n = (int)strlen(val), c = argc; - - while (--c > 0) { - - if (!strncmp(argv[c], val, n)) { - if (!*(argv[c] + n) && c < argc - 1) { - /* coverity treats unchecked argv as "tainted" */ - if (!argv[c + 1] || strlen(argv[c + 1]) > 1024) - return NULL; - return argv[c + 1]; - } - - if (argv[c][n] == '=') - return &argv[c][n + 1]; - return argv[c] + n; - } - } - - return NULL; -} - -static const char * const builtins[] = { - "-d", - "--udp-tx-loss", - "--udp-rx-loss" -}; - -void -lws_cmdline_option_handle_builtin(int argc, const char **argv, - struct lws_context_creation_info *info) -{ - const char *p; - int n, m, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - - for (n = 0; n < (int)LWS_ARRAY_SIZE(builtins); n++) { - p = lws_cmdline_option(argc, argv, builtins[n]); - if (!p) - continue; - - m = atoi(p); - - switch (n) { - case 0: - logs = m; - break; - case 1: - info->udp_loss_sim_tx_pc = m; - break; - case 2: - info->udp_loss_sim_rx_pc = m; - break; - } - } - - lws_set_log_level(logs, NULL); -} - - -const lws_humanize_unit_t humanize_schema_si[] = { - { "Pi ", LWS_PI }, { "Ti ", LWS_TI }, { "Gi ", LWS_GI }, - { "Mi ", LWS_MI }, { "Ki ", LWS_KI }, { " ", 1 }, - { NULL, 0 } -}; -const lws_humanize_unit_t humanize_schema_si_bytes[] = { - { "PiB", LWS_PI }, { "TiB", LWS_TI }, { "GiB", LWS_GI }, - { "MiB", LWS_MI }, { "KiB", LWS_KI }, { "B ", 1 }, - { NULL, 0 } -}; -const lws_humanize_unit_t humanize_schema_us[] = { - { "y ", (uint64_t)365 * 24 * 3600 * LWS_US_PER_SEC }, - { "d ", (uint64_t)24 * 3600 * LWS_US_PER_SEC }, - { "hr ", (uint64_t)3600 * LWS_US_PER_SEC }, - { "min", 60 * LWS_US_PER_SEC }, - { "s ", LWS_US_PER_SEC }, - { "ms ", LWS_US_PER_MS }, - { "us ", 1 }, - { NULL, 0 } -}; - -static int -decim(char *r, uint64_t v, char chars, char leading) -{ - int n = chars - 1; - uint64_t q = 1; - - r += n; - - while (n >= 0) { - if (v / q) - *r-- = '0' + ((v / q) % 10); - else - *r-- = leading ? '0' : ' '; - q = q * 10; - n--; - } - - if (v / q) - /* the number is bigger than the allowed chars! */ - r[1] = '!'; - - return chars; -} - -int -lws_humanize(char *p, int len, uint64_t v, const lws_humanize_unit_t *schema) -{ - char *end = p + len; - - do { - if (v >= schema->factor || schema->factor == 1) { - if (schema->factor == 1) { - *p++ = ' '; - p += decim(p, v, 4, 0); - return lws_snprintf(p, lws_ptr_diff(end, p), - "%s ", schema->name); - } - - *p++ = ' '; - p += decim(p, v / schema->factor, 4, 0); - *p++ = '.'; - p += decim(p, (v % schema->factor) / - (schema->factor / 1000), 3, 1); - - return lws_snprintf(p, lws_ptr_diff(end, p), - "%s", schema->name); - } - schema++; - } while (schema->name); - - assert(0); - strncpy(p, "unknown value", len); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/core/logs.c libwebsockets-2.4.2/lib/core/logs.c --- libwebsockets-4.0.20/lib/core/logs.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core/logs.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,270 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -#ifdef LWS_HAVE_SYS_TYPES_H -#include -#endif - -#if defined(LWS_PLAT_OPTEE) -void lwsl_emit_optee(int level, const char *line); -#endif - -int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; -static void (*lwsl_emit)(int level, const char *line) -#ifndef LWS_PLAT_OPTEE - = lwsl_emit_stderr -#else - = lwsl_emit_optee; -#endif - ; -#ifndef LWS_PLAT_OPTEE -static const char * log_level_names ="EWNIDPHXCLUT??"; -#endif - -#if defined(LWS_LOGS_TIMESTAMP) -int -lwsl_timestamp(int level, char *p, int len) -{ -#ifndef LWS_PLAT_OPTEE - time_t o_now; - unsigned long long now; - struct timeval tv; - struct tm *ptm = NULL; -#ifndef WIN32 - struct tm tm; -#endif - int n; - - gettimeofday(&tv, NULL); - o_now = tv.tv_sec; - now = ((unsigned long long)tv.tv_sec * 10000) + (tv.tv_usec / 100); - -#ifndef _WIN32_WCE -#ifdef WIN32 - ptm = localtime(&o_now); -#else - if (localtime_r(&o_now, &tm)) - ptm = &tm; -#endif -#endif - p[0] = '\0'; - for (n = 0; n < LLL_COUNT; n++) { - if (level != (1 << n)) - continue; - - if (ptm) - n = lws_snprintf(p, len, - "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %c: ", - ptm->tm_year + 1900, - ptm->tm_mon + 1, - ptm->tm_mday, - ptm->tm_hour, - ptm->tm_min, - ptm->tm_sec, - (int)(now % 10000), log_level_names[n]); - else - n = lws_snprintf(p, len, "[%llu:%04d] %c: ", - (unsigned long long) now / 10000, - (int)(now % 10000), log_level_names[n]); - return n; - } -#else - p[0] = '\0'; -#endif - - return 0; -} -#endif - -#ifndef LWS_PLAT_OPTEE -static const char * const colours[] = { - "[31;1m", /* LLL_ERR */ - "[36;1m", /* LLL_WARN */ - "[35;1m", /* LLL_NOTICE */ - "[32;1m", /* LLL_INFO */ - "[34;1m", /* LLL_DEBUG */ - "[33;1m", /* LLL_PARSER */ - "[33m", /* LLL_HEADER */ - "[33m", /* LLL_EXT */ - "[33m", /* LLL_CLIENT */ - "[33;1m", /* LLL_LATENCY */ - "[0;1m", /* LLL_USER */ - "[31m", /* LLL_THREAD */ -}; - -static char tty; - -static void -_lwsl_emit_stderr(int level, const char *line, int ts) -{ - char buf[50]; - int n, m = LWS_ARRAY_SIZE(colours) - 1; - - if (!tty) - tty = isatty(2) | 2; - - buf[0] = '\0'; -#if defined(LWS_LOGS_TIMESTAMP) - if (ts) - lwsl_timestamp(level, buf, sizeof(buf)); -#endif - - if (tty == 3) { - n = 1 << (LWS_ARRAY_SIZE(colours) - 1); - while (n) { - if (level & n) - break; - m--; - n >>= 1; - } - fprintf(stderr, "%c%s%s%s%c[0m", 27, colours[m], buf, line, 27); - } else - fprintf(stderr, "%s%s", buf, line); -} - -void -lwsl_emit_stderr(int level, const char *line) -{ - _lwsl_emit_stderr(level, line, 1); -} - -void -lwsl_emit_stderr_notimestamp(int level, const char *line) -{ - _lwsl_emit_stderr(level, line, 0); -} - -#endif - -#if !(defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK)) -void _lws_logv(int filter, const char *format, va_list vl) -{ -#if LWS_MAX_SMP == 1 - static char buf[256]; -#else - char buf[1024]; -#endif - int n; - - if (!(log_level & filter)) - return; - - n = vsnprintf(buf, sizeof(buf) - 1, format, vl); - (void)n; - /* vnsprintf returns what it would have written, even if truncated */ - if (n > (int)sizeof(buf) - 1) { - n = sizeof(buf) - 5; - buf[n++] = '.'; - buf[n++] = '.'; - buf[n++] = '.'; - buf[n++] = '\n'; - buf[n] = '\0'; - } - if (n > 0) - buf[n] = '\0'; - lwsl_emit(filter, buf); -} - -void _lws_log(int filter, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - _lws_logv(filter, format, ap); - va_end(ap); -} -#endif -void lws_set_log_level(int level, void (*func)(int level, const char *line)) -{ - log_level = level; - if (func) - lwsl_emit = func; -} - -int lwsl_visible(int level) -{ - return log_level & level; -} - -void -lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len) -{ - unsigned char *buf = (unsigned char *)vbuf; - unsigned int n; - - if (!lwsl_visible(hexdump_level)) - return; - - if (!len) { - _lws_log(hexdump_level, "(hexdump: zero length)\n"); - return; - } - - if (!vbuf) { - _lws_log(hexdump_level, "(hexdump: NULL ptr)\n"); - return; - } - - _lws_log(hexdump_level, "\n"); - - for (n = 0; n < len;) { - unsigned int start = n, m; - char line[80], *p = line; - - p += lws_snprintf(p, 10, "%04X: ", start); - - for (m = 0; m < 16 && n < len; m++) - p += lws_snprintf(p, 5, "%02X ", buf[n++]); - while (m++ < 16) - p += lws_snprintf(p, 5, " "); - - p += lws_snprintf(p, 6, " "); - - for (m = 0; m < 16 && (start + m) < len; m++) { - if (buf[start + m] >= ' ' && buf[start + m] < 127) - *p++ = buf[start + m]; - else - *p++ = '.'; - } - while (m++ < 16) - *p++ = ' '; - - *p++ = '\n'; - *p = '\0'; - _lws_log(hexdump_level, "%s", line); - (void)line; - } - - _lws_log(hexdump_level, "\n"); -} - -void -lwsl_hexdump(const void *vbuf, size_t len) -{ -#if defined(_DEBUG) - lwsl_hexdump_level(LLL_DEBUG, vbuf, len); -#endif -} diff -Nru libwebsockets-4.0.20/lib/core/lws_dll2.c libwebsockets-2.4.2/lib/core/lws_dll2.c --- libwebsockets-4.0.20/lib/core/lws_dll2.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core/lws_dll2.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,231 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -#ifdef LWS_HAVE_SYS_TYPES_H -#include -#endif - -int -lws_dll2_foreach_safe(struct lws_dll2_owner *owner, void *user, - int (*cb)(struct lws_dll2 *d, void *user)) -{ - lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, owner->head) { - if (cb(p, user)) - return 1; - } lws_end_foreach_dll_safe(p, tp); - - return 0; -} - -void -lws_dll2_add_head(struct lws_dll2 *d, struct lws_dll2_owner *owner) -{ - if (!lws_dll2_is_detached(d)) { - assert(0); /* only wholly detached things can be added */ - return; - } - - /* our next guy is current first guy, if any */ - if (owner->head != d) - d->next = owner->head; - - /* if there is a next guy, set his prev ptr to our next ptr */ - if (d->next) - d->next->prev = d; - /* there is nobody previous to us, we are the head */ - d->prev = NULL; - - /* set the first guy to be us */ - owner->head = d; - - if (!owner->tail) - owner->tail = d; - - d->owner = owner; - owner->count++; -} - -/* - * add us to the list that 'after' is in, just before him - */ - -void -lws_dll2_add_before(struct lws_dll2 *d, struct lws_dll2 *after) -{ - lws_dll2_owner_t *owner = after->owner; - - if (!lws_dll2_is_detached(d)) { - assert(0); /* only wholly detached things can be added */ - return; - } - - if (lws_dll2_is_detached(after)) { - assert(0); /* can't add after something detached */ - return; - } - - d->owner = owner; - - /* we need to point forward to after */ - - d->next = after; - - /* we need to point back to after->prev */ - - d->prev = after->prev; - - /* guy that used to point to after, needs to point to us */ - - if (after->prev) - after->prev->next = d; - else - owner->head = d; - - /* then after needs to point back to us */ - - after->prev = d; - - owner->count++; -} - -void -lws_dll2_add_tail(struct lws_dll2 *d, struct lws_dll2_owner *owner) -{ - if (!lws_dll2_is_detached(d)) { - assert(0); /* only wholly detached things can be added */ - return; - } - - /* our previous guy is current last guy */ - d->prev = owner->tail; - /* if there is a prev guy, set his next ptr to our prev ptr */ - if (d->prev) - d->prev->next = d; - /* our next ptr is NULL */ - d->next = NULL; - /* set the last guy to be us */ - owner->tail = d; - - /* list head is also us if we're the first */ - if (!owner->head) - owner->head = d; - - d->owner = owner; - owner->count++; -} - -void -lws_dll2_remove(struct lws_dll2 *d) -{ - if (lws_dll2_is_detached(d)) - return; - - /* if we have a next guy, set his prev to our prev */ - if (d->next) - d->next->prev = d->prev; - - /* if we have a previous guy, set his next to our next */ - if (d->prev) - d->prev->next = d->next; - - /* if we have phead, track the tail and head if it points to us... */ - - if (d->owner->tail == d) - d->owner->tail = d->prev; - - if (d->owner->head == d) - d->owner->head = d->next; - - d->owner->count--; - - /* we're out of the list, we should not point anywhere any more */ - d->owner = NULL; - d->prev = NULL; - d->next = NULL; -} - -void -lws_dll2_clear(struct lws_dll2 *d) -{ - d->owner = NULL; - d->prev = NULL; - d->next = NULL; -} - -void -lws_dll2_owner_clear(struct lws_dll2_owner *d) -{ - d->head = NULL; - d->tail = NULL; - d->count = 0; -} - -void -lws_dll2_add_sorted(lws_dll2_t *d, lws_dll2_owner_t *own, - int (*compare)(const lws_dll2_t *d, const lws_dll2_t *i)) -{ - lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, - lws_dll2_get_head(own)) { - assert(p != d); - - if (compare(p, d) >= 0) { - /* drop us in before this guy */ - lws_dll2_add_before(d, p); - - // lws_dll2_describe(own, "post-insert"); - - return; - } - } lws_end_foreach_dll_safe(p, tp); - - /* - * Either nobody on the list yet to compare him to, or he's the - * furthest away timeout... stick him at the tail end - */ - - lws_dll2_add_tail(d, own); -} - -#if defined(_DEBUG) - -void -lws_dll2_describe(lws_dll2_owner_t *owner, const char *desc) -{ -#if _LWS_ENABLED_LOGS & LLL_INFO - int n = 1; - - lwsl_info("%s: %s: owner %p: count %d, head %p, tail %p\n", - __func__, desc, owner, (int)owner->count, owner->head, owner->tail); - - lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, - lws_dll2_get_head(owner)) { - lwsl_info("%s: %d: %p: owner %p, prev %p, next %p\n", - __func__, n++, p, p->owner, p->prev, p->next); - } lws_end_foreach_dll_safe(p, tp); -#endif -} - -#endif diff -Nru libwebsockets-4.0.20/lib/core/lws_dll.c libwebsockets-2.4.2/lib/core/lws_dll.c --- libwebsockets-4.0.20/lib/core/lws_dll.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core/lws_dll.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,248 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -#ifdef LWS_HAVE_SYS_TYPES_H -#include -#endif - -void -lws_dll_add_head(struct lws_dll *d, struct lws_dll *phead) -{ - if (!lws_dll_is_detached(d, phead)) { - assert(0); /* only wholly detached things can be added */ - return; - } - - /* our next guy is current first guy, if any */ - if (phead->next != d) - d->next = phead->next; - - /* if there is a next guy, set his prev ptr to our next ptr */ - if (d->next) - d->next->prev = d; - /* there is nobody previous to us, we are the head */ - d->prev = NULL; - - /* set the first guy to be us */ - phead->next = d; - - /* if there was nothing on the list before, we are also now the tail */ - if (!phead->prev) - phead->prev = d; - - assert(d->prev != d); - assert(d->next != d); -} - -void -lws_dll_add_tail(struct lws_dll *d, struct lws_dll *phead) -{ - if (!lws_dll_is_detached(d, phead)) { - assert(0); /* only wholly detached things can be added */ - return; - } - - /* our previous guy is current last guy */ - d->prev = phead->prev; - /* if there is a prev guy, set his next ptr to our prev ptr */ - if (d->prev) - d->prev->next = d; - /* our next ptr is NULL */ - d->next = NULL; - /* set the last guy to be us */ - phead->prev = d; - - /* list head is also us if we're the first */ - if (!phead->next) - phead->next = d; - - assert(d->prev != d); - assert(d->next != d); -} - -void -lws_dll_insert(struct lws_dll *n, struct lws_dll *target, - struct lws_dll *phead, int before) -{ - if (!lws_dll_is_detached(n, phead)) { - assert(0); /* only wholly detached things can be inserted */ - return; - } - if (!target) { - /* - * the case where there's no target identified degenerates to - * a simple add at head or tail - */ - if (before) { - lws_dll_add_head(n, phead); - return; - } - lws_dll_add_tail(n, phead); - return; - } - - /* - * in the case there's a target "cursor", we have to do the work to - * stitch the new guy in appropriately - */ - - if (before) { - /* - * we go before dd - * DDp <-> DD <-> DDn --> DDp <-> us <-> DD <-> DDn - */ - /* we point forward to dd */ - n->next = target; - /* we point back to what dd used to point back to */ - n->prev = target->prev; - /* DDp points forward to us now */ - if (target->prev) - target->prev->next = n; - /* DD points back to us now */ - target->prev = n; - - /* if target was the head, we are now the head */ - if (phead->next == target) - phead->next = n; - - /* since we are before another guy, we cannot become the tail */ - - } else { - /* - * we go after dd - * DDp <-> DD <-> DDn --> DDp <-> DD <-> us <-> DDn - */ - /* we point forward to what dd used to point forward to */ - n->next = target->next; - /* we point back to dd */ - n->prev = target; - /* DDn points back to us */ - if (target->next) - target->next->prev = n; - /* DD points forward to us */ - target->next = n; - - /* if target was the tail, we are now the tail */ - if (phead->prev == target) - phead->prev = n; - - /* since we go after another guy, we cannot become the head */ - } -} - -/* situation is: - * - * HEAD: struct lws_dll * = &entry1 - * - * Entry 1: struct lws_dll .pprev = &HEAD , .next = Entry 2 - * Entry 2: struct lws_dll .pprev = &entry1 , .next = &entry2 - * Entry 3: struct lws_dll .pprev = &entry2 , .next = NULL - * - * Delete Entry1: - * - * - HEAD = &entry2 - * - Entry2: .pprev = &HEAD, .next = &entry3 - * - Entry3: .pprev = &entry2, .next = NULL - * - * Delete Entry2: - * - * - HEAD = &entry1 - * - Entry1: .pprev = &HEAD, .next = &entry3 - * - Entry3: .pprev = &entry1, .next = NULL - * - * Delete Entry3: - * - * - HEAD = &entry1 - * - Entry1: .pprev = &HEAD, .next = &entry2 - * - Entry2: .pprev = &entry1, .next = NULL - * - */ - -void -lws_dll_remove(struct lws_dll *d) -{ - if (!d->prev && !d->next) - return; - - /* - * remove us - * - * USp <-> us <-> USn --> USp <-> USn - */ - - /* if we have a next guy, set his prev to our prev */ - if (d->next) - d->next->prev = d->prev; - - /* set our prev guy to our next guy instead of us */ - if (d->prev) - d->prev->next = d->next; - - /* we're out of the list, we should not point anywhere any more */ - d->prev = NULL; - d->next = NULL; -} - -void -lws_dll_remove_track_tail(struct lws_dll *d, struct lws_dll *phead) -{ - if (lws_dll_is_detached(d, phead)) { - assert(phead->prev != d); - assert(phead->next != d); - return; - } - - /* if we have a next guy, set his prev to our prev */ - if (d->next) - d->next->prev = d->prev; - - /* if we have a previous guy, set his next to our next */ - if (d->prev) - d->prev->next = d->next; - - if (phead->prev == d) - phead->prev = d->prev; - - if (phead->next == d) - phead->next = d->next; - - /* we're out of the list, we should not point anywhere any more */ - d->prev = NULL; - d->next = NULL; -} - - -int -lws_dll_foreach_safe(struct lws_dll *phead, void *user, - int (*cb)(struct lws_dll *d, void *user)) -{ - lws_start_foreach_dll_safe(struct lws_dll *, p, tp, phead->next) { - if (cb(p, user)) - return 1; - } lws_end_foreach_dll_safe(p, tp); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/core/private-lib-core.h libwebsockets-2.4.2/lib/core/private-lib-core.h --- libwebsockets-4.0.20/lib/core/private-lib-core.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core/private-lib-core.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,734 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_PRIVATE_LIB_CORE_H__) -#define __LWS_PRIVATE_LIB_CORE_H__ - -#include "lws_config.h" -#include "lws_config_private.h" - -#if defined(LWS_WITH_CGI) && defined(LWS_HAVE_VFORK) && \ - !defined(NO_GNU_SOURCE_THIS_TIME) && !defined(_GNU_SOURCE) - #define _GNU_SOURCE -#endif - -/* -#if !defined(_POSIX_C_SOURCE) -#define _POSIX_C_SOURCE 200112L -#endif -*/ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef LWS_HAVE_INTTYPES_H -#include -#endif - -#include - -#ifdef LWS_HAVE_SYS_TYPES_H - #include -#endif -#if defined(LWS_HAVE_SYS_STAT_H) && !defined(LWS_PLAT_OPTEE) - #include -#endif - -#if LWS_MAX_SMP > 1 - #include -#endif - -#ifndef LWS_DEF_HEADER_LEN -#define LWS_DEF_HEADER_LEN 4096 -#endif -#ifndef LWS_DEF_HEADER_POOL -#define LWS_DEF_HEADER_POOL 4 -#endif -#ifndef LWS_MAX_PROTOCOLS -#define LWS_MAX_PROTOCOLS 5 -#endif -#ifndef LWS_MAX_EXTENSIONS_ACTIVE -#define LWS_MAX_EXTENSIONS_ACTIVE 1 -#endif -#ifndef LWS_MAX_EXT_OFFERS -#define LWS_MAX_EXT_OFFERS 8 -#endif -#ifndef SPEC_LATEST_SUPPORTED -#define SPEC_LATEST_SUPPORTED 13 -#endif -#ifndef AWAITING_TIMEOUT -#define AWAITING_TIMEOUT 20 -#endif -#ifndef CIPHERS_LIST_STRING -#define CIPHERS_LIST_STRING "DEFAULT" -#endif -#ifndef LWS_SOMAXCONN -#define LWS_SOMAXCONN SOMAXCONN -#endif - -#define MAX_WEBSOCKET_04_KEY_LEN 128 - -#ifndef SYSTEM_RANDOM_FILEPATH -#define SYSTEM_RANDOM_FILEPATH "/dev/urandom" -#endif - -#define LWS_H2_RX_SCRATCH_SIZE 512 - -#define lws_socket_is_valid(x) (x != LWS_SOCK_INVALID) - -#ifndef LWS_HAVE_STRERROR - #define strerror(x) "" -#endif - - /* - * - * ------ private platform defines ------ - * - */ - -#if defined(LWS_PLAT_FREERTOS) - #include "private-lib-plat-freertos.h" -#else - #if defined(WIN32) || defined(_WIN32) - #include "private-lib-plat-windows.h" - #else - #if defined(LWS_PLAT_OPTEE) - #include "private-lib-plat.h" - #else - #include "private-lib-plat-unix.h" - #endif - #endif -#endif - - /* - * - * ------ public api ------ - * - */ - -#include "libwebsockets.h" - -/* - * Generic bidi tx credit management - */ - -struct lws_tx_credit { - int32_t tx_cr; /* our credit to write peer */ - int32_t peer_tx_cr_est; /* peer's credit to write us */ - - int32_t manual_initial_tx_credit; - - uint8_t skint; /* unable to write anything */ - uint8_t manual; -}; - - -#include "private-lib-tls.h" - -#if defined(WIN32) || defined(_WIN32) - // Visual studio older than 2015 and WIN_CE has only _stricmp - #if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_WIN32_WCE) - #define strcasecmp _stricmp - #define strncasecmp _strnicmp - #elif !defined(__MINGW32__) - #define strcasecmp stricmp - #define strncasecmp strnicmp - #endif - #define getdtablesize() 30000 -#endif - -#ifndef LWS_ARRAY_SIZE -#define LWS_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) -#endif - -#ifdef __cplusplus -extern "C" { -#endif - - - -#if defined(__clang__) -#define lws_memory_barrier() __sync_synchronize() -#elif defined(__GNUC__) -#define lws_memory_barrier() __sync_synchronize() -#else -#define lws_memory_barrier() -#endif - - -struct lws_ring { - void *buf; - void (*destroy_element)(void *element); - uint32_t buflen; - uint32_t element_len; - uint32_t head; - uint32_t oldest_tail; -}; - -struct lws_protocols; -struct lws; - -#if defined(LWS_WITH_NETWORK) -#include "private-lib-event-libs.h" - -#if defined(LWS_WITH_SECURE_STREAMS) -#include "private-lib-secure-streams.h" -#endif - -struct lws_io_watcher { -#ifdef LWS_WITH_LIBEV - struct lws_io_watcher_libev ev; -#endif -#ifdef LWS_WITH_LIBUV - struct lws_io_watcher_libuv uv; -#endif -#ifdef LWS_WITH_LIBEVENT - struct lws_io_watcher_libevent event; -#endif -#ifdef LWS_WITH_GLIB - struct lws_io_watcher_glib glib; -#endif - struct lws_context *context; - - uint8_t actual_events; -}; - -struct lws_signal_watcher { -#ifdef LWS_WITH_LIBEV - struct lws_signal_watcher_libev ev; -#endif -#ifdef LWS_WITH_LIBUV - struct lws_signal_watcher_libuv uv; -#endif -#ifdef LWS_WITH_LIBEVENT - struct lws_signal_watcher_libevent event; -#endif - struct lws_context *context; -}; - -struct lws_foreign_thread_pollfd { - struct lws_foreign_thread_pollfd *next; - int fd_index; - int _and; - int _or; -}; -#endif - -#if LWS_MAX_SMP > 1 - -struct lws_mutex_refcount { - pthread_mutex_t lock; - pthread_t lock_owner; - const char *last_lock_reason; - char lock_depth; - char metadata; -}; - -void -lws_mutex_refcount_init(struct lws_mutex_refcount *mr); - -void -lws_mutex_refcount_destroy(struct lws_mutex_refcount *mr); - -void -lws_mutex_refcount_lock(struct lws_mutex_refcount *mr, const char *reason); - -void -lws_mutex_refcount_unlock(struct lws_mutex_refcount *mr); -#endif - -#if defined(LWS_WITH_NETWORK) -#include "private-lib-core-net.h" -#endif - -struct lws_deferred_free -{ - struct lws_deferred_free *next; - time_t deadline; - void *payload; -}; - -struct lws_system_blob { - union { - struct lws_buflist *bl; - struct { - const uint8_t *ptr; - size_t len; - } direct; - } u; - char is_direct; -}; - - -typedef struct lws_attach_item { - lws_dll2_t list; - lws_attach_cb_t cb; - void *opaque; - lws_system_states_t state; -} lws_attach_item_t; - -/* - * the rest is managed per-context, that includes - * - * - processwide single fd -> wsi lookup - * - contextwide headers pool - */ - -struct lws_context { - #if defined(LWS_WITH_SERVER) - char canonical_hostname[96]; - #endif - -#if defined(LWS_WITH_FILE_OPS) - struct lws_plat_file_ops fops_platform; -#endif - -#if defined(LWS_WITH_ZIP_FOPS) - struct lws_plat_file_ops fops_zip; -#endif - - lws_system_blob_t system_blobs[LWS_SYSBLOB_TYPE_COUNT]; - -#if defined(LWS_WITH_NETWORK) - struct lws_context_per_thread pt[LWS_MAX_SMP]; - lws_retry_bo_t default_retry; - lws_sorted_usec_list_t sul_system_state; - -#if defined(LWS_PLAT_FREERTOS) - struct sockaddr_in frt_pipe_si; -#endif - -#if defined(LWS_WITH_HTTP2) - struct http2_settings set; -#endif - -#if defined(LWS_WITH_SERVER_STATUS) - struct lws_conn_stats conn_stats; -#endif -#if LWS_MAX_SMP > 1 - struct lws_mutex_refcount mr; -#endif - -#if defined(LWS_WITH_NETWORK) -#if defined(LWS_WITH_LIBEV) - struct lws_context_eventlibs_libev ev; -#endif -#if defined(LWS_WITH_LIBUV) - struct lws_context_eventlibs_libuv uv; -#endif -#if defined(LWS_WITH_LIBEVENT) - struct lws_context_eventlibs_libevent event; -#endif -#if defined(LWS_WITH_GLIB) - struct lws_context_eventlibs_glib glib; -#endif - -#if defined(LWS_WITH_TLS) - struct lws_context_tls tls; -#endif - -#if defined(LWS_WITH_SYS_ASYNC_DNS) - lws_async_dns_t async_dns; -#endif - -#if defined(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM) - void *pol_args; - struct lws_ss_handle *hss_auth; - struct lws_ss_handle *hss_fetch_policy; - lws_sorted_usec_list_t sul_api_amazon_com; - lws_sorted_usec_list_t sul_api_amazon_com_kick; -#endif - - lws_state_manager_t mgr_system; - lws_state_notify_link_t protocols_notify; -#if defined (LWS_WITH_SYS_DHCP_CLIENT) - lws_dll2_owner_t dhcpc_owner; - /**< list of ifaces with dhcpc */ -#endif - - /* pointers */ - - struct lws_vhost *vhost_list; - struct lws_vhost *no_listener_vhost_list; - struct lws_vhost *vhost_pending_destruction_list; - struct lws_vhost *vhost_system; - -#if defined(LWS_WITH_SERVER) - const char *server_string; -#endif - - struct lws_event_loop_ops *event_loop_ops; -#endif - -#if defined(LWS_WITH_TLS) - const struct lws_tls_ops *tls_ops; -#endif - -#if defined(LWS_WITH_DETAILED_LATENCY) - det_lat_buf_cb_t detailed_latency_cb; -#endif -#if defined(LWS_WITH_PLUGINS) - struct lws_plugin *plugin_list; -#endif -#ifdef _WIN32 -/* different implementation between unix and windows */ - struct lws_fd_hashtable fd_hashtable[FD_HASHTABLE_MODULUS]; -#else - struct lws **lws_lookup; - -#endif -#endif /* NETWORK */ - -#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) - const char *ss_proxy_bind; - const char *ss_proxy_address; -#endif - -#if defined(LWS_WITH_FILE_OPS) - const struct lws_plat_file_ops *fops; -#endif - - struct lws_context **pcontext_finalize; - const char *username, *groupname; -#if defined(LWS_WITH_DETAILED_LATENCY) - const char *detailed_latency_filepath; -#endif - -#if defined(LWS_AMAZON_RTOS) - mbedtls_entropy_context mec; - mbedtls_ctr_drbg_context mcdc; -#endif - - struct lws_deferred_free *deferred_free_list; - -#if defined(LWS_WITH_THREADPOOL) - struct lws_threadpool *tp_list_head; -#endif - -#if defined(LWS_WITH_PEER_LIMITS) - struct lws_peer **pl_hash_table; - struct lws_peer *peer_wait_list; - time_t next_cull; -#endif - - const lws_system_ops_t *system_ops; - -#if defined(LWS_WITH_SECURE_STREAMS) - const char *pss_policies_json; - const lws_ss_policy_t *pss_policies; - const lws_ss_plugin_t **pss_plugins; - struct lwsac *ac_policy; -#endif - - void *external_baggage_free_on_destroy; - const struct lws_token_limits *token_limits; - void *user_space; -#if defined(LWS_WITH_SERVER) - const struct lws_protocol_vhost_options *reject_service_keywords; - lws_reload_func deprecation_cb; -#endif - void (*eventlib_signal_cb)(void *event_lib_handle, int signum); - -#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) - cap_value_t caps[4]; - char count_caps; -#endif - - lws_usec_t time_up; /* monotonic */ - - uint64_t options; - - time_t last_ws_ping_pong_check_s; - -#if defined(LWS_PLAT_FREERTOS) - unsigned long time_last_state_dump; - uint32_t last_free_heap; -#endif - - int max_fds; - int count_event_loop_static_asset_handles; -#if !defined(LWS_NO_DAEMONIZE) - pid_t started_with_parent; -#endif - int uid, gid; - - int fd_random; -#if defined(LWS_WITH_DETAILED_LATENCY) - int latencies_fd; -#endif - int count_wsi_allocated; - int count_cgi_spawned; - unsigned int fd_limit_per_thread; - unsigned int timeout_secs; - unsigned int pt_serv_buf_size; - int max_http_header_data; - int max_http_header_pool; - int simultaneous_ssl_restriction; - int simultaneous_ssl; -#if defined(LWS_WITH_PEER_LIMITS) - uint32_t pl_hash_elements; /* protected by context->lock */ - uint32_t count_peers; /* protected by context->lock */ - unsigned short ip_limit_ah; - unsigned short ip_limit_wsi; -#endif - unsigned int deprecated:1; - unsigned int inside_context_destroy:1; - unsigned int being_destroyed:1; - unsigned int being_destroyed1:1; - unsigned int being_destroyed2:1; - unsigned int requested_kill:1; - unsigned int protocol_init_done:1; - unsigned int doing_protocol_init:1; - unsigned int done_protocol_destroy_cb:1; - unsigned int finalize_destroy_after_internal_loops_stopped:1; - unsigned int max_fds_unrelated_to_ulimit:1; - unsigned int policy_updated:1; - - short count_threads; - short plugin_protocol_count; - short plugin_extension_count; - short server_string_len; - unsigned short ws_ping_pong_interval; - unsigned short deprecation_pending_listen_close_count; -#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) - uint16_t ss_proxy_port; -#endif - - uint8_t max_fi; - uint8_t udp_loss_sim_tx_pc; - uint8_t udp_loss_sim_rx_pc; - -#if defined(LWS_WITH_STATS) - uint8_t updated; -#endif -}; - -int -lws_check_deferred_free(struct lws_context *context, int tsi, int force); - -#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x] -#define lws_get_vh_protocol(vh, x) vh->protocols[x] - -int -lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max); - -void -lws_vhost_destroy1(struct lws_vhost *vh); - - -#if defined(LWS_PLAT_FREERTOS) -LWS_EXTERN int -lws_find_string_in_file(const char *filename, const char *str, int stringlen); -#endif - -signed char char_to_hex(const char c); - -#if defined(LWS_WITH_NETWORK) -int -lws_system_do_attach(struct lws_context_per_thread *pt); -#endif - -struct lws_buflist { - struct lws_buflist *next; - size_t len; - size_t pos; -}; - -LWS_EXTERN char * -lws_strdup(const char *s); - -LWS_EXTERN int log_level; - -LWS_EXTERN int -lws_b64_selftest(void); - - -#ifndef LWS_NO_DAEMONIZE - LWS_EXTERN pid_t get_daemonize_pid(); -#else - #define get_daemonize_pid() (0) -#endif - -LWS_EXTERN void lwsl_emit_stderr(int level, const char *line); - -#if !defined(LWS_WITH_TLS) - #define LWS_SSL_ENABLED(context) (0) - #define lws_context_init_server_ssl(_a, _b) (0) - #define lws_ssl_destroy(_a) - #define lws_context_init_alpn(_a) - #define lws_ssl_capable_read lws_ssl_capable_read_no_ssl - #define lws_ssl_capable_write lws_ssl_capable_write_no_ssl - #define lws_ssl_pending lws_ssl_pending_no_ssl - #define lws_server_socket_service_ssl(_b, _c) (0) - #define lws_ssl_close(_a) (0) - #define lws_ssl_context_destroy(_a) - #define lws_ssl_SSL_CTX_destroy(_a) - #define lws_ssl_remove_wsi_from_buffered_list(_a) - #define __lws_ssl_remove_wsi_from_buffered_list(_a) - #define lws_context_init_ssl_library(_a) - #define lws_context_deinit_ssl_library(_a) - #define lws_tls_check_all_cert_lifetimes(_a) - #define lws_tls_acme_sni_cert_destroy(_a) -#endif - - - -#if LWS_MAX_SMP > 1 -#define lws_context_lock(c, reason) lws_mutex_refcount_lock(&c->mr, reason) -#define lws_context_unlock(c) lws_mutex_refcount_unlock(&c->mr) - -static LWS_INLINE void -lws_vhost_lock(struct lws_vhost *vhost) -{ - pthread_mutex_lock(&vhost->lock); -} - -static LWS_INLINE void -lws_vhost_unlock(struct lws_vhost *vhost) -{ - pthread_mutex_unlock(&vhost->lock); -} - - -#else -#define lws_pt_mutex_init(_a) (void)(_a) -#define lws_pt_mutex_destroy(_a) (void)(_a) -#define lws_pt_lock(_a, b) (void)(_a) -#define lws_pt_unlock(_a) (void)(_a) -#define lws_context_lock(_a, _b) (void)(_a) -#define lws_context_unlock(_a) (void)(_a) -#define lws_vhost_lock(_a) (void)(_a) -#define lws_vhost_unlock(_a) (void)(_a) -#define lws_pt_stats_lock(_a) (void)(_a) -#define lws_pt_stats_unlock(_a) (void)(_a) -#endif - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len); - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len); - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ssl_pending_no_ssl(struct lws *wsi); - -int -lws_tls_check_cert_lifetime(struct lws_vhost *vhost); - -int lws_jws_selftest(void); -int lws_jwe_selftest(void); - -int -lws_protocol_init(struct lws_context *context); - -int -lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p, - const char *reason); - -const struct lws_protocol_vhost_options * -lws_vhost_protocol_options(struct lws_vhost *vh, const char *name); - -const struct lws_http_mount * -lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len); - -/* - * custom allocator - */ -LWS_EXTERN void * -lws_realloc(void *ptr, size_t size, const char *reason); - -LWS_EXTERN void * LWS_WARN_UNUSED_RESULT -lws_zalloc(size_t size, const char *reason); - -#ifdef LWS_PLAT_OPTEE -void *lws_malloc(size_t size, const char *reason); -void lws_free(void *p); -#define lws_free_set_NULL(P) do { lws_free(P); (P) = NULL; } while(0) -#else -#define lws_malloc(S, R) lws_realloc(NULL, S, R) -#define lws_free(P) lws_realloc(P, 0, "lws_free") -#define lws_free_set_NULL(P) do { lws_realloc(P, 0, "free"); (P) = NULL; } while(0) -#endif - -int -lws_create_event_pipes(struct lws_context *context); - -int -lws_plat_apply_FD_CLOEXEC(int n); - -const struct lws_plat_file_ops * -lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, - const char **vpath); - -/* lws_plat_ */ - -LWS_EXTERN int -lws_plat_context_early_init(void); -LWS_EXTERN void -lws_plat_context_early_destroy(struct lws_context *context); -LWS_EXTERN void -lws_plat_context_late_destroy(struct lws_context *context); - -LWS_EXTERN int -lws_plat_init(struct lws_context *context, - const struct lws_context_creation_info *info); -LWS_EXTERN int -lws_plat_drop_app_privileges(struct lws_context *context, int actually_drop); - -#if defined(LWS_WITH_UNIX_SOCK) -int -lws_plat_user_colon_group_to_ids(const char *u_colon_g, uid_t *puid, gid_t *pgid); -#endif - -int -lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len); - -LWS_EXTERN int -lws_check_byte_utf8(unsigned char state, unsigned char c); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len); -LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename, - uint8_t **buf, lws_filepos_t *amount); - -void -lws_context_destroy2(struct lws_context *context); - -#if !defined(PRIu64) -#define PRIu64 "llu" -#endif - -#if defined(LWS_WITH_ABSTRACT) -#include "private-lib-abstract.h" -#endif - -#ifdef __cplusplus -}; -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/core/vfs.c libwebsockets-2.4.2/lib/core/vfs.c --- libwebsockets-4.0.20/lib/core/vfs.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core/vfs.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,136 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -void -lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops) -{ - context->fops = fops; -} - -lws_filepos_t -lws_vfs_tell(lws_fop_fd_t fop_fd) -{ - return fop_fd->pos; -} - -lws_filepos_t -lws_vfs_get_length(lws_fop_fd_t fop_fd) -{ - return fop_fd->len; -} - -uint32_t -lws_vfs_get_mod_time(lws_fop_fd_t fop_fd) -{ - return fop_fd->mod_time; -} - -lws_fileofs_t -lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset) -{ - lws_fileofs_t ofs; - - ofs = fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset - fop_fd->pos); - - return ofs; -} - - -lws_fileofs_t -lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset) -{ - return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, fop_fd->len + - fop_fd->pos + offset); -} - - -const struct lws_plat_file_ops * -lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, - const char **vpath) -{ - const struct lws_plat_file_ops *pf; - const char *p = vfs_path; - int n; - - *vpath = NULL; - - /* no non-platform fops, just use that */ - - if (!fops->next) - return fops; - - /* - * scan the vfs path looking for indications we are to be - * handled by a specific fops - */ - - while (p && *p) { - if (*p != '/') { - p++; - continue; - } - /* the first one is always platform fops, so skip */ - pf = fops->next; - while (pf) { - n = 0; - while (n < (int)LWS_ARRAY_SIZE(pf->fi) && pf->fi[n].sig) { - if (p >= vfs_path + pf->fi[n].len) - if (!strncmp(p - (pf->fi[n].len - 1), - pf->fi[n].sig, - pf->fi[n].len - 1)) { - *vpath = p + 1; - return pf; - } - - n++; - } - pf = pf->next; - } - p++; - } - - return fops; -} - -lws_fop_fd_t LWS_WARN_UNUSED_RESULT -lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path, - lws_fop_flags_t *flags) -{ - const char *vpath = ""; - const struct lws_plat_file_ops *selected; - - selected = lws_vfs_select_fops(fops, vfs_path, &vpath); - - return selected->LWS_FOP_OPEN(fops, vfs_path, vpath, flags); -} - - -struct lws_plat_file_ops * -lws_get_fops(struct lws_context *context) -{ - return (struct lws_plat_file_ops *)context->fops; -} - diff -Nru libwebsockets-4.0.20/lib/core-net/adopt.c libwebsockets-2.4.2/lib/core-net/adopt.c --- libwebsockets-4.0.20/lib/core-net/adopt.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/adopt.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,710 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 -lws_get_idlest_tsi(struct lws_context *context) -{ - unsigned int lowest = ~0; - int n = 0, hit = -1; - - for (; n < context->count_threads; n++) { - lwsl_debug("%s: %d %d\n", __func__, context->pt[n].fds_count, - context->fd_limit_per_thread - 1); - if ((unsigned int)context->pt[n].fds_count != - context->fd_limit_per_thread - 1 && - (unsigned int)context->pt[n].fds_count < lowest) { - lowest = context->pt[n].fds_count; - hit = n; - } - } - - return hit; -} - -struct lws * -lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi) -{ - struct lws *new_wsi; - int n = fixed_tsi; - - if (n < 0) - n = lws_get_idlest_tsi(vhost->context); - - if (n < 0) { - lwsl_err("no space for new conn\n"); - return NULL; - } - - new_wsi = lws_zalloc(sizeof(struct lws), "new server wsi"); - if (new_wsi == NULL) { - lwsl_err("Out of memory for new connection\n"); - return NULL; - } - - new_wsi->wsistate |= LWSIFR_SERVER; - new_wsi->tsi = n; - lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi, - vhost->name, new_wsi->tsi); - - lws_vhost_bind_wsi(vhost, new_wsi); - new_wsi->context = vhost->context; - new_wsi->pending_timeout = NO_PENDING_TIMEOUT; - new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; - new_wsi->retry_policy = vhost->retry_policy; - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (vhost->context->detailed_latency_cb) - new_wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); -#endif - - /* initialize the instance struct */ - - lwsi_set_state(new_wsi, LRS_UNCONNECTED); - new_wsi->hdr_parsing_completed = 0; - -#ifdef LWS_WITH_TLS - new_wsi->tls.use_ssl = LWS_SSL_ENABLED(vhost); -#endif - - /* - * these can only be set once the protocol is known - * we set an un-established connection's protocol pointer - * to the start of the supported list, so it can look - * for matching ones during the handshake - */ - new_wsi->protocol = vhost->protocols; - new_wsi->user_space = NULL; - new_wsi->desc.sockfd = LWS_SOCK_INVALID; - new_wsi->position_in_fds_table = LWS_NO_FDS_POS; - - vhost->context->count_wsi_allocated++; - - /* - * outermost create notification for wsi - * no user_space because no protocol selection - */ - vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, NULL, - NULL, 0); - - return new_wsi; -} - - -/* if not a socket, it's a raw, non-ssl file descriptor */ - -static struct lws * -lws_adopt_descriptor_vhost1(struct lws_vhost *vh, lws_adoption_type type, - const char *vh_prot_name, struct lws *parent, - void *opaque) -{ - struct lws_context *context = vh->context; - struct lws_context_per_thread *pt; - struct lws *new_wsi; - int n; - - /* - * Notice that in SMP case, the wsi may be being created on an - * entirely different pt / tsi for load balancing. In that case as - * we initialize it, it may become "live" concurrently unexpectedly... - */ - - n = -1; - if (parent) - n = parent->tsi; - new_wsi = lws_create_new_server_wsi(vh, n); - if (!new_wsi) - return NULL; - - new_wsi->opaque_user_data = opaque; - - pt = &context->pt[(int)new_wsi->tsi]; - lws_stats_bump(pt, LWSSTATS_C_CONNECTIONS, 1); - - if (parent) { - new_wsi->parent = parent; - new_wsi->sibling_list = parent->child_list; - parent->child_list = new_wsi; - } - - if (vh_prot_name) { - new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost, - vh_prot_name); - if (!new_wsi->protocol) { - lwsl_err("Protocol %s not enabled on vhost %s\n", - vh_prot_name, new_wsi->vhost->name); - goto bail; - } - if (lws_ensure_user_space(new_wsi)) { - lwsl_notice("OOM trying to get user_space\n"); - goto bail; - } - } - - if (lws_role_call_adoption_bind(new_wsi, type, vh_prot_name)) { - lwsl_err("%s: no role for desc type 0x%x\n", __func__, type); - goto bail; - } - - /* - * he's an allocated wsi, but he's not on any fds list or child list, - * join him to the vhost's list of these kinds of incomplete wsi until - * he gets another identity (he may do async dns now...) - */ - lws_dll2_add_head(&new_wsi->vh_awaiting_socket, - &new_wsi->vhost->vh_awaiting_socket_owner); - - return new_wsi; - -bail: - lwsl_notice("%s: exiting on bail\n", __func__); - if (parent) - parent->child_list = new_wsi->sibling_list; - if (new_wsi->user_space) - lws_free(new_wsi->user_space); - - vh->context->count_wsi_allocated--; - - lws_vhost_unbind_wsi(new_wsi); - lws_free(new_wsi); - - return NULL; -} - -static struct lws * -lws_adopt_descriptor_vhost2(struct lws *new_wsi, lws_adoption_type type, - lws_sock_file_fd_type fd) -{ - struct lws_context_per_thread *pt = - &new_wsi->context->pt[(int)new_wsi->tsi]; - int n; - - /* enforce that every fd is nonblocking */ - - if (type & LWS_ADOPT_SOCKET) { - if (lws_plat_set_nonblocking(fd.sockfd)) { - lwsl_err("%s: unable to set sockfd %d nonblocking\n", - __func__, fd.sockfd); - goto fail; - } - } -#if !defined(WIN32) - else - if (lws_plat_set_nonblocking(fd.filefd)) { - lwsl_err("%s: unable to set filefd nonblocking\n", - __func__); - goto fail; - } -#endif - - new_wsi->desc = fd; - - if (!LWS_SSL_ENABLED(new_wsi->vhost) || - !(type & LWS_ADOPT_SOCKET)) - type &= ~LWS_ADOPT_ALLOW_SSL; - - /* - * A new connection was accepted. Give the user a chance to - * set properties of the newly created wsi. There's no protocol - * selected yet so we issue this to the vhosts's default protocol, - * itself by default protocols[0] - */ - new_wsi->wsistate |= LWSIFR_SERVER; - n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED; - if (new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)]) - n = new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)]; - - if (new_wsi->context->event_loop_ops->sock_accept) - if (new_wsi->context->event_loop_ops->sock_accept(new_wsi)) - goto fail; - -#if LWS_MAX_SMP > 1 - /* - * Caution: after this point the wsi is live on its service thread - * which may be concurrent to this. We mark the wsi as still undergoing - * init in another pt so the assigned pt leaves it alone. - */ - new_wsi->undergoing_init_from_other_pt = 1; -#endif - - if (!(type & LWS_ADOPT_ALLOW_SSL)) { - lws_pt_lock(pt, __func__); - if (__insert_wsi_socket_into_fds(new_wsi->context, new_wsi)) { - lws_pt_unlock(pt); - lwsl_err("%s: fail inserting socket\n", __func__); - goto fail; - } - lws_pt_unlock(pt); - } -#if defined(LWS_WITH_SERVER) - else - if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) { -#if defined(LWS_WITH_ACCESS_LOG) - lwsl_notice("%s: fail ssl negotiation: %s\n", __func__, - new_wsi->simple_ip); -#else - lwsl_info("%s: fail ssl negotiation\n", __func__); -#endif - goto fail; - } -#endif - - /* he has fds visibility now, remove from vhost orphan list */ - lws_dll2_remove(&new_wsi->vh_awaiting_socket); - - /* - * by deferring callback to this point, after insertion to fds, - * lws_callback_on_writable() can work from the callback - */ - if ((new_wsi->protocol->callback)(new_wsi, n, new_wsi->user_space, - NULL, 0)) - goto fail; - - /* role may need to do something after all adoption completed */ - - lws_role_call_adoption_bind(new_wsi, type | _LWS_ADOPT_FINISH, - new_wsi->protocol->name); - -#if LWS_MAX_SMP > 1 - /* its actual pt can service it now */ - - new_wsi->undergoing_init_from_other_pt = 0; -#endif - - lws_cancel_service_pt(new_wsi); - - return new_wsi; - -fail: - if (type & LWS_ADOPT_SOCKET) - lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS, - "adopt skt fail"); - - return NULL; -} - - -/* if not a socket, it's a raw, non-ssl file descriptor */ - -struct lws * -lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, - lws_sock_file_fd_type fd, const char *vh_prot_name, - struct lws *parent) -{ - lws_adopt_desc_t info; - - memset(&info, 0, sizeof(info)); - - info.vh = vh; - info.type = type; - info.fd = fd; - info.vh_prot_name = vh_prot_name; - info.parent = parent; - - return lws_adopt_descriptor_vhost_via_info(&info); -} - -struct lws * -lws_adopt_descriptor_vhost_via_info(const lws_adopt_desc_t *info) -{ - struct lws *new_wsi; -#if defined(LWS_WITH_PEER_LIMITS) - struct lws_peer *peer = NULL; - - if (info->type & LWS_ADOPT_SOCKET) { - peer = lws_get_or_create_peer(info->vh, info->fd.sockfd); - - if (peer && info->vh->context->ip_limit_wsi && - peer->count_wsi >= info->vh->context->ip_limit_wsi) { - lwsl_notice("Peer reached wsi limit %d\n", - info->vh->context->ip_limit_wsi); - lws_stats_bump(&info->vh->context->pt[0], - LWSSTATS_C_PEER_LIMIT_WSI_DENIED, - 1); - compatible_close(info->fd.sockfd); - return NULL; - } - } -#endif - - new_wsi = lws_adopt_descriptor_vhost1(info->vh, info->type, - info->vh_prot_name, info->parent, - info->opaque); - if (!new_wsi) { - if (info->type & LWS_ADOPT_SOCKET) - compatible_close(info->fd.sockfd); - return NULL; - } - -#if defined(LWS_WITH_ACCESS_LOG) - lws_get_peer_simple_fd(info->fd.sockfd, new_wsi->simple_ip, - sizeof(new_wsi->simple_ip)); -#endif - -#if defined(LWS_WITH_PEER_LIMITS) - if (peer) - lws_peer_add_wsi(info->vh->context, peer, new_wsi); -#endif - - return lws_adopt_descriptor_vhost2(new_wsi, info->type, info->fd); -} - -struct lws * -lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd) -{ - lws_sock_file_fd_type fd; - - fd.sockfd = accept_fd; - return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET | - LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL); -} - -struct lws * -lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) -{ - return lws_adopt_socket_vhost(context->vhost_list, accept_fd); -} - -/* Common read-buffer adoption for lws_adopt_*_readbuf */ -static struct lws* -adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len) -{ - struct lws_context_per_thread *pt; - struct lws_pollfd *pfd; - int n; - - if (!wsi) - return NULL; - - if (!readbuf || len == 0) - return wsi; - - if (wsi->position_in_fds_table == LWS_NO_FDS_POS) - return wsi; - - pt = &wsi->context->pt[(int)wsi->tsi]; - - n = lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf, - len); - if (n < 0) - goto bail; - if (n) - lws_dll2_add_head(&wsi->dll_buflist, &pt->dll_buflist_owner); - - /* - * we can't process the initial read data until we can attach an ah. - * - * if one is available, get it and place the data in his ah rxbuf... - * wsi with ah that have pending rxbuf get auto-POLLIN service. - * - * no autoservice because we didn't get a chance to attach the - * readbuf data to wsi or ah yet, and we will do it next if we get - * the ah. - */ - if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) { - - lwsl_notice("%s: calling service on readbuf ah\n", __func__); - - /* - * unlike a normal connect, we have the headers already - * (or the first part of them anyway). - * libuv won't come back and service us without a network - * event, so we need to do the header service right here. - */ - pfd = &pt->fds[wsi->position_in_fds_table]; - pfd->revents |= LWS_POLLIN; - lwsl_err("%s: calling service\n", __func__); - if (lws_service_fd_tsi(wsi->context, pfd, wsi->tsi)) - /* service closed us */ - return NULL; - - return wsi; - } - lwsl_err("%s: deferring handling ah\n", __func__); - - return wsi; - -bail: - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "adopt skt readbuf fail"); - - return NULL; -} - -#if defined(LWS_WITH_UDP) -#if defined(LWS_WITH_CLIENT) -static struct lws * -lws_create_adopt_udp2(struct lws *wsi, const char *ads, - const struct addrinfo *r, int n, void *opaque) -{ - lws_sock_file_fd_type sock; - int bc = 1; - - assert(wsi); - - if (!wsi->dns_results) - wsi->dns_results_next = wsi->dns_results = r; - - if (ads && (n < 0 || !r)) { - /* - * DNS lookup failed: there are no usable results. Fail the - * overall connection request. - */ - lwsl_notice("%s: bad: n %d, r %p\n", __func__, n, r); - - /* - * We didn't get a callback on a cache item and bump the - * refcount. So don't let the cleanup continue to think it - * needs to decrement any refcount. - */ - wsi->dns_results_next = wsi->dns_results = NULL; - goto bail; - } - - while (wsi->dns_results_next) { - - /* - * We have done the dns lookup, identify the result we want - * if any, and then complete the adoption by binding wsi to - * socket opened on it. - * - * Ignore the weak assumptions about protocol driven by port - * number and force to DGRAM / UDP since that's what this - * function is for. - */ - -#if !defined(__linux__) - /* PF_PACKET is linux-only */ - sock.sockfd = socket(wsi->dns_results_next->ai_family, - SOCK_DGRAM, IPPROTO_UDP); -#else - sock.sockfd = socket(wsi->pf_packet ? PF_PACKET : - wsi->dns_results_next->ai_family, - SOCK_DGRAM, wsi->pf_packet ? - htons(0x800) : IPPROTO_UDP); -#endif - if (sock.sockfd == LWS_SOCK_INVALID) - goto resume; - - ((struct sockaddr_in *)wsi->dns_results_next->ai_addr)->sin_port = - htons(wsi->c_port); - - if (setsockopt(sock.sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&bc, - sizeof(bc)) < 0) - lwsl_err("%s: failed to set reuse\n", __func__); - - if (wsi->do_broadcast && - setsockopt(sock.sockfd, SOL_SOCKET, SO_BROADCAST, (const char *)&bc, - sizeof(bc)) < 0) - lwsl_err("%s: failed to set broadcast\n", - __func__); - - /* Bind the udp socket to a particular network interface */ - - if (opaque && - lws_plat_BINDTODEVICE(sock.sockfd, (const char *)opaque)) - goto resume; - - if (wsi->do_bind && - bind(sock.sockfd, wsi->dns_results_next->ai_addr, -#if defined(_WIN32) - (int)wsi->dns_results_next->ai_addrlen -#else - sizeof(struct sockaddr)//wsi->dns_results_next->ai_addrlen -#endif - ) == -1) { - lwsl_err("%s: bind failed\n", __func__); - goto resume; - } - - if (!wsi->do_bind && !wsi->pf_packet) { - - if (connect(sock.sockfd, wsi->dns_results_next->ai_addr, - (socklen_t)wsi->dns_results_next->ai_addrlen) == -1) { - lwsl_err("%s: conn fd %d fam %d %s:%u failed " - "(salen %d) errno %d\n", __func__, - sock.sockfd, - wsi->dns_results_next->ai_addr->sa_family, - ads ? ads : "null", wsi->c_port, - (int)wsi->dns_results_next->ai_addrlen, - LWS_ERRNO); - compatible_close(sock.sockfd); - goto resume; - } - - memcpy(&wsi->udp->sa, wsi->dns_results_next->ai_addr, - wsi->dns_results_next->ai_addrlen); - wsi->udp->salen = (socklen_t)wsi->dns_results_next->ai_addrlen; - } - - /* we connected: complete the udp socket adoption flow */ - - lws_addrinfo_clean(wsi); - return lws_adopt_descriptor_vhost2(wsi, - LWS_ADOPT_RAW_SOCKET_UDP, sock); - -resume: - wsi->dns_results_next = wsi->dns_results_next->ai_next; - } - - lwsl_err("%s: unable to create INET socket %d\n", __func__, LWS_ERRNO); - lws_addrinfo_clean(wsi); - -bail: - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt udp2 fail"); - - return NULL; -} - -struct lws * -lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port, - int flags, const char *protocol_name, const char *ifname, - struct lws *parent_wsi, void *opaque, - const lws_retry_bo_t *retry_policy) -{ -#if !defined(LWS_PLAT_OPTEE) - struct lws *wsi; - int n; - - lwsl_info("%s: %s:%u\n", __func__, ads ? ads : "null", port); - - /* create the logical wsi without any valid fd */ - - wsi = lws_adopt_descriptor_vhost1(vhost, LWS_ADOPT_RAW_SOCKET_UDP, - protocol_name, parent_wsi, opaque); - if (!wsi) { - lwsl_err("%s: udp wsi creation failed\n", __func__); - goto bail; - } - wsi->do_bind = !!(flags & LWS_CAUDP_BIND); - wsi->do_broadcast = !!(flags & LWS_CAUDP_BROADCAST); - wsi->pf_packet = !!(flags & LWS_CAUDP_PF_PACKET); - wsi->c_port = port; - if (retry_policy) - wsi->retry_policy = retry_policy; - else - wsi->retry_policy = vhost->retry_policy; - -#if !defined(LWS_WITH_SYS_ASYNC_DNS) - { - struct addrinfo *r, h; - char buf[16]; - - memset(&h, 0, sizeof(h)); - h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - h.ai_socktype = SOCK_DGRAM; - h.ai_protocol = IPPROTO_UDP; - h.ai_flags = AI_PASSIVE; -#ifdef AI_ADDRCONFIG - h.ai_flags |= AI_ADDRCONFIG; -#endif - - /* if the dns lookup is synchronous, do the whole thing now */ - lws_snprintf(buf, sizeof(buf), "%u", port); - n = getaddrinfo(ads, buf, &h, &r); - if (n) { -#if !defined(LWS_PLAT_FREERTOS) - lwsl_info("%s: getaddrinfo error: %s\n", __func__, - gai_strerror(n)); -#else - lwsl_info("%s: getaddrinfo error: %s\n", __func__, - strerror(n)); -#endif - //freeaddrinfo(r); - goto bail1; - } - /* complete it immediately after the blocking dns lookup - * finished... free r when connect either completed or failed */ - wsi = lws_create_adopt_udp2(wsi, ads, r, 0, NULL); - - return wsi; - } -#else - if (ads) { - /* - * with async dns, use the wsi as the point about which to do - * the dns lookup and have it call the second part when it's - * done. - * - * Keep a refcount on the results and free it when we connected - * or definitively failed. - * - * Notice wsi has no socket at this point (we don't know what - * kind to ask for until we get the dns back). But it is bound - * to a vhost and can be cleaned up from that at vhost destroy. - */ - n = lws_async_dns_query(vhost->context, 0, ads, - LWS_ADNS_RECORD_A, - lws_create_adopt_udp2, wsi, (void *)ifname); - lwsl_debug("%s: dns query returned %d\n", __func__, n); - if (n == LADNS_RET_FAILED) { - lwsl_err("%s: async dns failed\n", __func__); - wsi = NULL; - /* - * It was already closed by calling callback with error - * from lws_async_dns_query() - */ - goto bail; - } - } else { - lwsl_debug("%s: udp adopt has no ads\n", __func__); - wsi = lws_create_adopt_udp2(wsi, ads, NULL, 0, (void *)ifname); - } - - /* dns lookup is happening asynchronously */ - - lwsl_debug("%s: returning wsi %p\n", __func__, wsi); - return wsi; -#endif -#if !defined(LWS_WITH_SYS_ASYNC_DNS) -bail1: - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt udp2 fail"); - wsi = NULL; -#endif -bail: - return wsi; -#else - return NULL; -#endif -} -#endif -#endif - -struct lws * -lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, - const char *readbuf, size_t len) -{ - return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd), - readbuf, len); -} - -struct lws * -lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, - lws_sockfd_type accept_fd, - const char *readbuf, size_t len) -{ - return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd), - readbuf, len); -} diff -Nru libwebsockets-4.0.20/lib/core-net/client.c libwebsockets-2.4.2/lib/core-net/client.c --- libwebsockets-4.0.20/lib/core-net/client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,121 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_CLIENT_HTTP_PROXYING) - -int -lws_set_proxy(struct lws_vhost *vhost, const char *proxy) -{ - char authstring[96]; - int brackets = 0; - char *p; - - if (!proxy) - return -1; - - /* we have to deal with a possible redundant leading http:// */ - if (!strncmp(proxy, "http://", 7)) - proxy += 7; - - p = strrchr(proxy, '@'); - if (p) { /* auth is around */ - - if ((unsigned int)(p - proxy) > sizeof(authstring) - 1) - goto auth_too_long; - - lws_strncpy(authstring, proxy, p - proxy + 1); - // null termination not needed on input - if (lws_b64_encode_string(authstring, lws_ptr_diff(p, proxy), - vhost->proxy_basic_auth_token, - sizeof vhost->proxy_basic_auth_token) < 0) - goto auth_too_long; - - lwsl_info(" Proxy auth in use\n"); - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - proxy = p + 1; -#endif - } else - vhost->proxy_basic_auth_token[0] = '\0'; - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - -#if defined(LWS_WITH_IPV6) - /* - * isolating the address / port is complicated by IPv6 overloading - * the meaning of : in the address. The convention to solve it is to - * put [] around the ipv6 address part, eg, "[::1]:443". This must be - * parsed to "::1" as the address and the port as 443. - * - * IPv4 addresses like myproxy:443 continue to be parsed as normal. - */ - - if (proxy[0] == '[') - brackets = 1; -#endif - - lws_strncpy(vhost->http.http_proxy_address, proxy + brackets, - sizeof(vhost->http.http_proxy_address)); - - p = vhost->http.http_proxy_address; - -#if defined(LWS_WITH_IPV6) - if (brackets) { - /* original is IPv6 format "[::1]:443" */ - - p = strchr(vhost->http.http_proxy_address, ']'); - if (!p) { - lwsl_err("%s: malformed proxy '%s'\n", __func__, proxy); - - return -1; - } - *p++ = '\0'; - } -#endif - - p = strchr(p, ':'); - if (!p && !vhost->http.http_proxy_port) { - lwsl_err("http_proxy needs to be ads:port\n"); - - return -1; - } - if (p) { - *p = '\0'; - vhost->http.http_proxy_port = atoi(p + 1); - } - - lwsl_info(" Proxy %s:%u\n", vhost->http.http_proxy_address, - vhost->http.http_proxy_port); -#endif - - return 0; - -auth_too_long: - lwsl_err("proxy auth too long\n"); - - return -1; -} -#endif diff -Nru libwebsockets-4.0.20/lib/core-net/close.c libwebsockets-2.4.2/lib/core-net/close.c --- libwebsockets-4.0.20/lib/core-net/close.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/close.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,664 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_close_trans_q_leader(struct lws_dll2 *d, void *user) -{ - struct lws *w = lws_container_of(d, struct lws, dll2_cli_txn_queue); - - __lws_close_free_wsi(w, -1, "trans q leader closing"); - - return 0; -} -#endif - -void -__lws_reset_wsi(struct lws *wsi) -{ - if (!wsi) - return; - -#if defined(LWS_WITH_CLIENT) - - lws_free_set_NULL(wsi->cli_hostname_copy); - - /* - * if we have wsi in our transaction queue, if we are closing we - * must go through and close all those first - */ - if (wsi->vhost) { - - /* we are no longer an active client connection that can piggyback */ - lws_dll2_remove(&wsi->dll_cli_active_conns); - - lws_dll2_foreach_safe(&wsi->dll2_cli_txn_queue_owner, NULL, - lws_close_trans_q_leader); - - /* - * !!! If we are closing, but we have pending pipelined - * transaction results we already sent headers for, that's going - * to destroy sync for HTTP/1 and leave H2 stream with no live - * swsi.` - * - * However this is normal if we are being closed because the - * transaction queue leader is closing. - */ - lws_dll2_remove(&wsi->dll2_cli_txn_queue); - } -#endif - - if (wsi->vhost) - lws_dll2_remove(&wsi->vh_awaiting_socket); - - /* - * Protocol user data may be allocated either internally by lws - * or by specified the user. We should only free what we allocated. - */ - if (wsi->protocol && wsi->protocol->per_session_data_size && - wsi->user_space && !wsi->user_space_externally_allocated) - lws_free_set_NULL(wsi->user_space); - - lws_buflist_destroy_all_segments(&wsi->buflist); - lws_buflist_destroy_all_segments(&wsi->buflist_out); -#if defined(LWS_WITH_UDP) - lws_free_set_NULL(wsi->udp); -#endif - wsi->retry = 0; - -#if defined(LWS_WITH_CLIENT) - lws_dll2_remove(&wsi->dll2_cli_txn_queue); - lws_dll2_remove(&wsi->dll_cli_active_conns); -#endif - -#if defined(LWS_WITH_SYS_ASYNC_DNS) - lws_async_dns_cancel(wsi); -#endif - -#if defined(LWS_WITH_HTTP_PROXY) - if (wsi->http.buflist_post_body) - lws_buflist_destroy_all_segments(&wsi->http.buflist_post_body); -#endif - - if (wsi->vhost && wsi->vhost->lserv_wsi == wsi) - wsi->vhost->lserv_wsi = NULL; -#if defined(LWS_WITH_CLIENT) - if (wsi->vhost) - lws_dll2_remove(&wsi->dll_cli_active_conns); -#endif - wsi->context->count_wsi_allocated--; - - __lws_same_vh_protocol_remove(wsi); -#if defined(LWS_WITH_CLIENT) - lws_free_set_NULL(wsi->stash); - lws_free_set_NULL(wsi->cli_hostname_copy); -#endif - -#if defined(LWS_WITH_PEER_LIMITS) - lws_peer_track_wsi_close(wsi->context, wsi->peer); - wsi->peer = NULL; -#endif - - /* since we will destroy the wsi, make absolutely sure now */ - -#if defined(LWS_WITH_OPENSSL) - __lws_ssl_remove_wsi_from_buffered_list(wsi); -#endif - __lws_wsi_remove_from_sul(wsi); - - if (wsi->role_ops->destroy_role) - wsi->role_ops->destroy_role(wsi); - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - __lws_header_table_detach(wsi, 0); -#endif -} - -void -__lws_free_wsi(struct lws *wsi) -{ - if (!wsi) - return; - - __lws_reset_wsi(wsi); - - if (wsi->context->event_loop_ops->destroy_wsi) - wsi->context->event_loop_ops->destroy_wsi(wsi); - - lws_vhost_unbind_wsi(wsi); - - lwsl_debug("%s: %p, remaining wsi %d, tsi fds count %d\n", __func__, wsi, - wsi->context->count_wsi_allocated, - wsi->context->pt[(int)wsi->tsi].fds_count); - - lws_free(wsi); -} - - -void -lws_remove_child_from_any_parent(struct lws *wsi) -{ - struct lws **pwsi; - int seen = 0; - - if (!wsi->parent) - return; - - /* detach ourselves from parent's child list */ - pwsi = &wsi->parent->child_list; - while (*pwsi) { - if (*pwsi == wsi) { - lwsl_info("%s: detach %p from parent %p\n", __func__, - wsi, wsi->parent); - - if (wsi->parent->protocol) - wsi->parent->protocol->callback(wsi, - LWS_CALLBACK_CHILD_CLOSING, - wsi->parent->user_space, wsi, 0); - - *pwsi = wsi->sibling_list; - seen = 1; - break; - } - pwsi = &(*pwsi)->sibling_list; - } - if (!seen) - lwsl_err("%s: failed to detach from parent\n", __func__); - - wsi->parent = NULL; -} - -#if defined(LWS_WITH_CLIENT) -void -lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len) -{ - lws_addrinfo_clean(wsi); - - if (wsi->already_did_cce) - return; - - wsi->already_did_cce = 1; - lws_stats_bump(&wsi->context->pt[(int)wsi->tsi], - LWSSTATS_C_CONNS_CLIENT_FAILED, 1); - - if (!wsi->protocol) - return; - - if (!wsi->client_suppress_CONNECTION_ERROR) - wsi->protocol->callback(wsi, - LWS_CALLBACK_CLIENT_CONNECTION_ERROR, - wsi->user_space, arg, len); -} -#endif - -void -lws_addrinfo_clean(struct lws *wsi) -{ -#if defined(LWS_WITH_CLIENT) - if (!wsi->dns_results) - return; - -#if defined(LWS_WITH_SYS_ASYNC_DNS) - lws_async_dns_freeaddrinfo(&wsi->dns_results); -#else - freeaddrinfo((struct addrinfo *)wsi->dns_results); -#endif - wsi->dns_results = NULL; -#endif -} - -void -__lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, - const char *caller) -{ - struct lws_context_per_thread *pt; - const struct lws_protocols *pro; - struct lws_context *context; - struct lws *wsi1, *wsi2; - int n, ccb; - - lwsl_info("%s: %p: caller: %s\n", __func__, wsi, caller); - - if (!wsi) - return; - - lws_access_log(wsi); - - if (!lws_dll2_is_detached(&wsi->dll_buflist)) { - lwsl_info("%s: wsi %p: going down with stuff in buflist\n", - __func__, wsi); } - - context = wsi->context; - pt = &context->pt[(int)wsi->tsi]; - lws_stats_bump(pt, LWSSTATS_C_API_CLOSE, 1); - -#if defined(LWS_WITH_CLIENT) - - lws_free_set_NULL(wsi->cli_hostname_copy); - - lws_addrinfo_clean(wsi); -#endif - -#if defined(LWS_WITH_HTTP2) - if (wsi->mux_stream_immortal) - lws_http_close_immortal(wsi); -#endif - - /* if we have children, close them first */ - if (wsi->child_list) { - wsi2 = wsi->child_list; - while (wsi2) { - wsi1 = wsi2->sibling_list; - wsi2->parent = NULL; - /* stop it doing shutdown processing */ - wsi2->socket_is_permanently_unusable = 1; - __lws_close_free_wsi(wsi2, reason, - "general child recurse"); - wsi2 = wsi1; - } - wsi->child_list = NULL; - } - -#if defined(LWS_ROLE_RAW_FILE) - if (wsi->role_ops == &role_ops_raw_file) { - lws_remove_child_from_any_parent(wsi); - __remove_wsi_socket_from_fds(wsi); - if (wsi->protocol) - wsi->protocol->callback(wsi, wsi->role_ops->close_cb[0], - wsi->user_space, NULL, 0); - goto async_close; - } -#endif - - wsi->wsistate_pre_close = wsi->wsistate; - -#ifdef LWS_WITH_CGI - if (wsi->role_ops == &role_ops_cgi) { - - // lwsl_debug("%s: closing stdwsi index %d\n", __func__, (int)wsi->lsp_channel); - - /* we are not a network connection, but a handler for CGI io */ - if (wsi->parent && wsi->parent->http.cgi) { - - if (wsi->parent->child_list == wsi && !wsi->sibling_list) - lws_cgi_remove_and_kill(wsi->parent); - - /* end the binding between us and master */ - if (wsi->parent->http.cgi) - wsi->parent->http.cgi->lsp->stdwsi[(int)wsi->lsp_channel] = - NULL; - } - wsi->socket_is_permanently_unusable = 1; - - goto just_kill_connection; - } - - if (wsi->http.cgi) - lws_cgi_remove_and_kill(wsi); -#endif - -#if defined(LWS_WITH_CLIENT) - lws_free_set_NULL(wsi->stash); -#endif - - if (wsi->role_ops == &role_ops_raw_skt) { - wsi->socket_is_permanently_unusable = 1; - goto just_kill_connection; - } -#if defined(LWS_WITH_FILE_OPS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)) - if (lwsi_role_http(wsi) && lwsi_role_server(wsi) && - wsi->http.fop_fd != NULL) - lws_vfs_file_close(&wsi->http.fop_fd); -#endif - - if (lwsi_state(wsi) == LRS_DEAD_SOCKET) - return; - - if (wsi->socket_is_permanently_unusable || - reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY || - lwsi_state(wsi) == LRS_SHUTDOWN) - goto just_kill_connection; - - switch (lwsi_state_PRE_CLOSE(wsi)) { - case LRS_DEAD_SOCKET: - return; - - /* we tried the polite way... */ - case LRS_WAITING_TO_SEND_CLOSE: - case LRS_AWAITING_CLOSE_ACK: - case LRS_RETURNED_CLOSE: - goto just_kill_connection; - - case LRS_FLUSHING_BEFORE_CLOSE: - if (lws_has_buffered_out(wsi) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - || wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more -#endif - ) { - lws_callback_on_writable(wsi); - return; - } - lwsl_info("%p: end LRS_FLUSHING_BEFORE_CLOSE\n", wsi); - goto just_kill_connection; - default: - if (lws_has_buffered_out(wsi) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - || wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more -#endif - ) { - lwsl_info("%p: LRS_FLUSHING_BEFORE_CLOSE\n", wsi); - lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE); - __lws_set_timeout(wsi, - PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5); - return; - } - break; - } - - if (lwsi_state(wsi) == LRS_WAITING_CONNECT || - lwsi_state(wsi) == LRS_WAITING_DNS || - lwsi_state(wsi) == LRS_H1C_ISSUE_HANDSHAKE) - goto just_kill_connection; - - if (!wsi->told_user_closed && wsi->user_space && wsi->protocol && - wsi->protocol_bind_balance) { - wsi->protocol->callback(wsi, - wsi->role_ops->protocol_unbind_cb[ - !!lwsi_role_server(wsi)], - wsi->user_space, (void *)__func__, 0); - wsi->protocol_bind_balance = 0; - } - - /* - * signal we are closing, lws_write will - * add any necessary version-specific stuff. If the write fails, - * no worries we are closing anyway. If we didn't initiate this - * close, then our state has been changed to - * LRS_RETURNED_CLOSE and we will skip this. - * - * Likewise if it's a second call to close this connection after we - * sent the close indication to the peer already, we are in state - * LRS_AWAITING_CLOSE_ACK and will skip doing this a second time. - */ - - if (wsi->role_ops->close_via_role_protocol && - wsi->role_ops->close_via_role_protocol(wsi, reason)) - return; - -just_kill_connection: - -#if defined(LWS_WITH_FILE_OPS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)) - if (lwsi_role_http(wsi) && lwsi_role_server(wsi) && - wsi->http.fop_fd != NULL) - lws_vfs_file_close(&wsi->http.fop_fd); -#endif - -#if defined(LWS_WITH_SYS_ASYNC_DNS) - lws_async_dns_cancel(wsi); -#endif - -#if defined(LWS_WITH_HTTP_PROXY) - if (wsi->http.buflist_post_body) - lws_buflist_destroy_all_segments(&wsi->http.buflist_post_body); -#endif -#if defined(LWS_WITH_UDP) - if (wsi->udp) - lws_free_set_NULL(wsi->udp); -#endif - - if (wsi->role_ops->close_kill_connection) - wsi->role_ops->close_kill_connection(wsi, reason); - - n = 0; - - if (!wsi->told_user_closed && wsi->user_space && - wsi->protocol_bind_balance && wsi->protocol) { - lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi, - wsi->protocol ? wsi->protocol->name: "NULL"); - if (wsi->protocol) - wsi->protocol->callback(wsi, - wsi->role_ops->protocol_unbind_cb[ - !!lwsi_role_server(wsi)], - wsi->user_space, (void *)__func__, 0); - wsi->protocol_bind_balance = 0; - } - -#if defined(LWS_WITH_CLIENT) - if ((lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY || - lwsi_state(wsi) == LRS_WAITING_DNS || - lwsi_state(wsi) == LRS_WAITING_CONNECT) && - !wsi->already_did_cce && wsi->protocol) { - static const char _reason[] = "closed before established"; - - lws_inform_client_conn_fail(wsi, - (void *)_reason, sizeof(_reason)); - } -#endif - - /* - * Testing with ab shows that we have to stage the socket close when - * the system is under stress... shutdown any further TX, change the - * state to one that won't emit anything more, and wait with a timeout - * for the POLLIN to show a zero-size rx before coming back and doing - * the actual close. - */ - if (wsi->role_ops != &role_ops_raw_skt && !lwsi_role_client(wsi) && - lwsi_state(wsi) != LRS_SHUTDOWN && - lwsi_state(wsi) != LRS_UNCONNECTED && - reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY && - !wsi->socket_is_permanently_unusable) { - -#if defined(LWS_WITH_TLS) - if (lws_is_ssl(wsi) && wsi->tls.ssl) { - n = 0; - switch (__lws_tls_shutdown(wsi)) { - case LWS_SSL_CAPABLE_DONE: - case LWS_SSL_CAPABLE_ERROR: - case LWS_SSL_CAPABLE_MORE_SERVICE_READ: - case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE: - case LWS_SSL_CAPABLE_MORE_SERVICE: - break; - } - } else -#endif - { - lwsl_info("%s: shutdown conn: %p (sk %d, state 0x%x)\n", - __func__, wsi, (int)(lws_intptr_t)wsi->desc.sockfd, - lwsi_state(wsi)); - if (!wsi->socket_is_permanently_unusable && - lws_socket_is_valid(wsi->desc.sockfd)) { - wsi->socket_is_permanently_unusable = 1; - n = shutdown(wsi->desc.sockfd, SHUT_WR); - } - } - if (n) - lwsl_debug("closing: shutdown (state 0x%x) ret %d\n", - lwsi_state(wsi), LWS_ERRNO); - - /* - * This causes problems on WINCE / ESP32 with disconnection - * when the events are half closing connection - */ -#if !defined(_WIN32_WCE) && !defined(LWS_PLAT_FREERTOS) - /* libuv: no event available to guarantee completion */ - if (!wsi->socket_is_permanently_unusable && - lws_socket_is_valid(wsi->desc.sockfd) && - lwsi_state(wsi) != LRS_SHUTDOWN && - (context->event_loop_ops->flags & LELOF_ISPOLL)) { - __lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN); - lwsi_set_state(wsi, LRS_SHUTDOWN); - __lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH, - context->timeout_secs); - - return; - } -#endif - } - - lwsl_debug("%s: real just_kill_connection: %p (sockfd %d)\n", __func__, - wsi, wsi->desc.sockfd); - -#ifdef LWS_WITH_HUBBUB - if (wsi->http.rw) { - lws_rewrite_destroy(wsi->http.rw); - wsi->http.rw = NULL; - } -#endif - - if (wsi->http.pending_return_headers) - lws_free_set_NULL(wsi->http.pending_return_headers); - - /* - * we won't be servicing or receiving anything further from this guy - * delete socket from the internal poll list if still present - */ - __lws_ssl_remove_wsi_from_buffered_list(wsi); - __lws_wsi_remove_from_sul(wsi); - - //if (wsi->told_event_loop_closed) // cgi std close case (dummy-callback) - // return; - - // lwsl_notice("%s: wsi %p, fd %d\n", __func__, wsi, wsi->desc.sockfd); - - /* checking return redundant since we anyway close */ - if (wsi->desc.sockfd != LWS_SOCK_INVALID) - __remove_wsi_socket_from_fds(wsi); - else - __lws_same_vh_protocol_remove(wsi); - - lwsi_set_state(wsi, LRS_DEAD_SOCKET); - lws_buflist_destroy_all_segments(&wsi->buflist); - lws_dll2_remove(&wsi->dll_buflist); - - if (wsi->role_ops->close_role) - wsi->role_ops->close_role(pt, wsi); - - /* tell the user it's all over for this guy */ - - ccb = 0; - if ((lwsi_state_est_PRE_CLOSE(wsi) || - /* raw skt adopted but didn't complete tls hs should CLOSE */ - (wsi->role_ops == &role_ops_raw_skt && !lwsi_role_client(wsi)) || - lwsi_state_PRE_CLOSE(wsi) == LRS_WAITING_SERVER_REPLY) && - !wsi->told_user_closed && - wsi->role_ops->close_cb[lwsi_role_server(wsi)]) { - if (!wsi->upgraded_to_http2 || !lwsi_role_client(wsi)) - ccb = 1; - /* - * The network wsi for a client h2 connection shouldn't - * call back for its role: the child stream connections - * own the role. Otherwise h2 will call back closed - * one too many times as the children do it and then - * the closing network stream. - */ - } - - if (!wsi->told_user_closed && - !lws_dll2_is_detached(&wsi->vh_awaiting_socket)) - /* - * He's a guy who go started with dns, but failed or is - * caught with a shutdown before he got the result. We have - * to issue him a close cb - */ - ccb = 1; - - pro = wsi->protocol; - -#if defined(LWS_WITH_CLIENT) - if (!ccb && (lwsi_state_PRE_CLOSE(wsi) & LWSIFS_NOT_EST) && - lwsi_role_client(wsi)) { - lws_inform_client_conn_fail(wsi, "Closed before conn", 18); - } -#endif - if (ccb) { - - if (!wsi->protocol && wsi->vhost && wsi->vhost->protocols) - pro = &wsi->vhost->protocols[0]; - - if (pro) - pro->callback(wsi, - wsi->role_ops->close_cb[lwsi_role_server(wsi)], - wsi->user_space, NULL, 0); - wsi->told_user_closed = 1; - } - -#if defined(LWS_ROLE_RAW_FILE) -async_close: -#endif - lws_remove_child_from_any_parent(wsi); - wsi->socket_is_permanently_unusable = 1; - - if (wsi->context->event_loop_ops->wsi_logical_close) - if (wsi->context->event_loop_ops->wsi_logical_close(wsi)) - return; - - __lws_close_free_wsi_final(wsi); -} - -void -__lws_close_free_wsi_final(struct lws *wsi) -{ - int n; - - if (!wsi->shadow && - lws_socket_is_valid(wsi->desc.sockfd) && !lws_ssl_close(wsi)) { - lwsl_debug("%s: wsi %p: fd %d\n", __func__, wsi, wsi->desc.sockfd); - n = compatible_close(wsi->desc.sockfd); - if (n) - lwsl_debug("closing: close ret %d\n", LWS_ERRNO); - - wsi->desc.sockfd = LWS_SOCK_INVALID; - } - - /* outermost destroy notification for wsi (user_space still intact) */ - if (wsi->vhost) - wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, - wsi->user_space, NULL, 0); - -#ifdef LWS_WITH_CGI - if (wsi->http.cgi) { - lws_spawn_piped_destroy(&wsi->http.cgi->lsp); - lws_free_set_NULL(wsi->http.cgi); - } -#endif - - __lws_free_wsi(wsi); -} - - -void -lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *caller) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - lws_pt_lock(pt, __func__); - __lws_close_free_wsi(wsi, reason, caller); - lws_pt_unlock(pt); -} - - diff -Nru libwebsockets-4.0.20/lib/core-net/connect.c libwebsockets-2.4.2/lib/core-net/connect.c --- libwebsockets-4.0.20/lib/core-net/connect.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/connect.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,393 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 -#include "private-lib-core.h" - -struct lws * -lws_client_connect_via_info(const struct lws_client_connect_info *i) -{ - const char *local = i->protocol; - struct lws *wsi, *safe = NULL; - const struct lws_protocols *p; - const char *cisin[CIS_COUNT]; - int tid = 0, n, m; - size_t size; - char *pc; - - if (i->context->requested_kill) - return NULL; - - if (!i->context->protocol_init_done) - if (lws_protocol_init(i->context)) - return NULL; - - /* - * If we have .local_protocol_name, use it to select the local protocol - * handler to bind to. Otherwise use .protocol if http[s]. - */ - if (i->local_protocol_name) - local = i->local_protocol_name; - - lws_stats_bump(&i->context->pt[tid], LWSSTATS_C_CONNS_CLIENT, 1); - - /* PHASE 1: create a bare wsi */ - - wsi = lws_zalloc(sizeof(struct lws), "client wsi"); - if (wsi == NULL) - goto bail; - - /* - * Until we exit, we can report connection failure directly to the - * caller without needing to call through to protocol CONNECTION_ERROR. - */ - wsi->client_suppress_CONNECTION_ERROR = 1; - - wsi->context = i->context; - wsi->desc.sockfd = LWS_SOCK_INVALID; - wsi->seq = i->seq; - wsi->flags = i->ssl_connection; - if (i->retry_and_idle_policy) - wsi->retry_policy = i->retry_and_idle_policy; - else - wsi->retry_policy = &i->context->default_retry; - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (i->context->detailed_latency_cb) - wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); -#endif - - wsi->vhost = NULL; - if (!i->vhost) { - struct lws_vhost *v = i->context->vhost_list; - if (v && !strcmp(v->name, "system")) - v = v->vhost_next; - lws_vhost_bind_wsi(v, wsi); - } else - lws_vhost_bind_wsi(i->vhost, wsi); - - if (!wsi->vhost) { - lwsl_err("%s: No vhost in the context\n", __func__); - - goto bail; - } - -#if LWS_MAX_SMP > 1 - tid = wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_GET_THREAD_ID, - NULL, NULL, 0); -#endif - - /* - * PHASE 2: if SMP, bind the client to whatever tsi the current thread - * represents - */ - -#if LWS_MAX_SMP > 1 - lws_context_lock(i->context, "client find tsi"); - - for (n = 0; n < i->context->count_threads; n++) - if (i->context->pt[n].service_tid == tid) { - lwsl_info("%s: client binds to caller tsi %d\n", - __func__, n); - wsi->tsi = n; -#if defined(LWS_WITH_DETAILED_LATENCY) - wsi->detlat.tsi = n; -#endif - break; - } - - /* - * this binding is sort of provisional, since when we try to insert - * into the pt fds, there may be no space and it will fail - */ - - lws_context_unlock(i->context); -#endif - - /* - * PHASE 3: Choose an initial role for the wsi and do role-specific init - * - * Note the initial role may not reflect the final role, eg, - * we may want ws, but first we have to go through h1 to get that - */ - - if (lws_role_call_client_bind(wsi, i) < 0) { - lwsl_err("%s: unable to bind to role\n", __func__); - - goto bail; - } - lwsl_info("%s: role binding to %s\n", __func__, wsi->role_ops->name); - - /* - * PHASE 4: fill up the wsi with stuff from the connect_info as far as - * it can go. It's uncertain because not only is our connection - * going to complete asynchronously, we might have bound to h1 and not - * even be able to get ahold of an ah immediately. - */ - - wsi->user_space = NULL; - wsi->pending_timeout = NO_PENDING_TIMEOUT; - wsi->position_in_fds_table = LWS_NO_FDS_POS; - wsi->ocport = wsi->c_port = i->port; - wsi->sys_tls_client_cert = i->sys_tls_client_cert; - -#if defined(LWS_ROLE_H2) - wsi->txc.manual_initial_tx_credit = (int32_t)i->manual_initial_tx_credit; -#endif - - wsi->protocol = &wsi->vhost->protocols[0]; - wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE); - wsi->client_no_follow_redirect = !!(i->ssl_connection & - LCCSCF_HTTP_NO_FOLLOW_REDIRECT); - - /* - * PHASE 5: handle external user_space now, generic alloc is done in - * role finalization - */ - - if (i->userdata) { - wsi->user_space_externally_allocated = 1; - wsi->user_space = i->userdata; - } - - if (local) { - lwsl_info("%s: protocol binding to %s\n", __func__, local); - p = lws_vhost_name_to_protocol(wsi->vhost, local); - if (p) - lws_bind_protocol(wsi, p, __func__); - else - lwsl_err("%s: unknown protocol %s\n", __func__, local); - - lwsl_info("%s: wsi %p: %s %s entry\n", - __func__, wsi, wsi->role_ops->name, - wsi->protocol ? wsi->protocol->name : "none"); - } - - /* - * PHASE 5: handle external user_space now, generic alloc is done in - * role finalization - */ - - if (!wsi->user_space && i->userdata) { - wsi->user_space_externally_allocated = 1; - wsi->user_space = i->userdata; - } - -#if defined(LWS_WITH_TLS) - wsi->tls.use_ssl = i->ssl_connection; -#else - if (i->ssl_connection & LCCSCF_USE_SSL) { - lwsl_err("%s: lws not configured for tls\n", __func__); - goto bail; - } -#endif - - /* - * PHASE 6: stash the things from connect_info that we can't process - * right now, eg, if http binding, without an ah. If h1 and no ah, we - * will go on the ah waiting list and process those things later (after - * the connect_info and maybe the things pointed to have gone out of - * scope) - * - * However these things are stashed in a generic way at this point, - * with no relationship to http or ah - */ - - cisin[CIS_ADDRESS] = i->address; - cisin[CIS_PATH] = i->path; - cisin[CIS_HOST] = i->host; - cisin[CIS_ORIGIN] = i->origin; - cisin[CIS_PROTOCOL] = i->protocol; - cisin[CIS_METHOD] = i->method; - cisin[CIS_IFACE] = i->iface; - cisin[CIS_ALPN] = i->alpn; - - size = sizeof(*wsi->stash); - - /* - * Let's overallocate the stash object with space for all the args - * in one hit. - */ - for (n = 0; n < CIS_COUNT; n++) - if (cisin[n]) - size += strlen(cisin[n]) + 1; - - wsi->stash = lws_malloc(size, "client stash"); - if (!wsi->stash) { - lwsl_err("%s: OOM\n", __func__); - goto bail1; - } - /* all the pointers default to NULL, but no need to zero the args */ - memset(wsi->stash, 0, sizeof(*wsi->stash)); - - wsi->opaque_user_data = wsi->stash->opaque_user_data = - i->opaque_user_data; - pc = (char *)&wsi->stash[1]; - - for (n = 0; n < CIS_COUNT; n++) - if (cisin[n]) { - wsi->stash->cis[n] = pc; - m = (int)strlen(cisin[n]) + 1; - memcpy(pc, cisin[n], m); - pc += m; - } - - /* - * at this point user callbacks like - * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER will be interested to - * know the parent... eg for proxying we can grab extra headers from - * the parent's incoming ah and add them to the child client handshake - */ - - if (i->parent_wsi) { - lwsl_info("%s: created child %p of parent %p\n", __func__, - wsi, i->parent_wsi); - wsi->parent = i->parent_wsi; - safe = wsi->sibling_list = i->parent_wsi->child_list; - i->parent_wsi->child_list = wsi; - } - - /* - * PHASE 7: Do any role-specific finalization processing. We can still - * see important info things via wsi->stash - */ - - if (wsi->role_ops->client_bind) { - int n = wsi->role_ops->client_bind(wsi, NULL); - - if (n && i->parent_wsi) { - /* unpick from parent */ - - i->parent_wsi->child_list = safe; - } - - if (n < 0) - /* we didn't survive, wsi is freed */ - goto bail2; - - if (n) - /* something else failed, wsi needs freeing */ - goto bail; - } - - /* let the caller's optional wsi storage have the wsi we created */ - - if (i->pwsi) - *i->pwsi = wsi; - - /* PHASE 8: notify protocol with role-specific connected callback */ - - /* raw socket per se doesn't want this... raw socket proxy wants it... */ - - if (wsi->role_ops != &role_ops_raw_skt || - (i->local_protocol_name && - !strcmp(i->local_protocol_name, "raw-proxy"))) { - lwsl_debug("%s: wsi %p: adoption cb %d to %s %s\n", __func__, - wsi, wsi->role_ops->adoption_cb[0], - wsi->role_ops->name, wsi->protocol->name); - - wsi->protocol->callback(wsi, wsi->role_ops->adoption_cb[0], - wsi->user_space, NULL, 0); - } - -#if defined(LWS_WITH_HUBBUB) - if (i->uri_replace_to) - wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb, - i->uri_replace_from, - i->uri_replace_to); -#endif - - if (i->method && (!strcmp(i->method, "RAW") // || -// !strcmp(i->method, "MQTT") - )) { - - /* - * Not for MQTT here, since we don't know if we will - * pipeline it or not... - */ - -#if defined(LWS_WITH_TLS) - - wsi->tls.ssl = NULL; - - if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { - const char *cce = NULL; - - switch ( -#if !defined(LWS_WITH_SYS_ASYNC_DNS) - lws_client_create_tls(wsi, &cce, 1) -#else - lws_client_create_tls(wsi, &cce, 0) -#endif - ) { - case 1: - return wsi; - case 0: - break; - default: - goto bail3; - } - } -#endif - - /* fallthru */ - - wsi = lws_http_client_connect_via_info2(wsi); - } - - if (wsi) - /* - * If it subsequently fails, report CONNECTION_ERROR, - * because we're going to return a non-error return now. - */ - wsi->client_suppress_CONNECTION_ERROR = 0; - - return wsi; - -#if defined(LWS_WITH_TLS) -bail3: - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "tls start fail"); - - return NULL; -#endif - -bail1: - lws_free_set_NULL(wsi->stash); - -bail: - lws_free(wsi); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) -bail2: -#endif - - if (i->ssl_connection & LCCSCF_USE_SSL) - lws_tls_restrict_return(i->context); - - if (i->pwsi) - *i->pwsi = NULL; - - lws_stats_bump(&i->context->pt[tid], LWSSTATS_C_CONNS_CLIENT_FAILED, 1); - - return NULL; -} diff -Nru libwebsockets-4.0.20/lib/core-net/detailed-latency.c libwebsockets-2.4.2/lib/core-net/detailed-latency.c --- libwebsockets-4.0.20/lib/core-net/detailed-latency.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/detailed-latency.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -int -lws_det_lat_active(struct lws_context *context) -{ - return !!context->detailed_latency_cb; -} - -int -lws_det_lat_cb(struct lws_context *context, lws_detlat_t *d) -{ - int n; - - if (!context->detailed_latency_cb) - return 0; - - n = context->detailed_latency_cb(context, d); - - memset(&d->latencies, 0, sizeof(d->latencies)); - - return n; -} - -static const char types[] = "rwNCTt????"; -int -lws_det_lat_plot_cb(struct lws_context *context, const lws_detlat_t *d) -{ - char buf[80], *p = buf, *end = &p[sizeof(buf) - 1]; - - if (!context->detailed_latency_filepath) - return 1; - - if (context->latencies_fd == -1) { - context->latencies_fd = open(context->detailed_latency_filepath, - LWS_O_CREAT | LWS_O_TRUNC | LWS_O_WRONLY, 0600); - if (context->latencies_fd == -1) - return 1; - } - - p += lws_snprintf(p, lws_ptr_diff(end, p), - "%llu %c %u %u %u %u %u %zu %zu\n", - (unsigned long long)lws_now_usecs(), types[d->type], - d->latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE], - d->latencies[LAT_DUR_PROXY_CLIENT_WRITE_TO_PROXY_RX], - d->latencies[LAT_DUR_PROXY_PROXY_REQ_TO_WRITE], - d->latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE] + - d->latencies[LAT_DUR_PROXY_CLIENT_WRITE_TO_PROXY_RX] + - d->latencies[LAT_DUR_PROXY_PROXY_REQ_TO_WRITE], - d->latencies[LAT_DUR_PROXY_RX_TO_ONWARD_TX], - d->acc_size, d->req_size); - - write(context->latencies_fd, buf, lws_ptr_diff(p, buf)); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/core-net/dummy-callback.c libwebsockets-2.4.2/lib/core-net/dummy-callback.c --- libwebsockets-4.0.20/lib/core-net/dummy-callback.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/dummy-callback.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,827 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_HTTP_PROXY) -static int -proxy_header(struct lws *wsi, struct lws *par, unsigned char *temp, - int temp_len, int index, unsigned char **p, unsigned char *end) -{ - int n = lws_hdr_total_length(par, index); - - if (n < 1) { - lwsl_debug("%s: no index %d:\n", __func__, index); - return 0; - } - - if (lws_hdr_copy(par, (char *)temp, temp_len, index) < 0) - return -1; - - lwsl_debug("%s: index %d: %s\n", __func__, index, (char *)temp); - - if (lws_add_http_header_by_token(wsi, index, temp, n, p, end)) - return -1; - - return 0; -} - -static int -stream_close(struct lws *wsi) -{ - char buf[LWS_PRE + 6], *out = buf + LWS_PRE; - - if (wsi->http.did_stream_close) - return 0; - - wsi->http.did_stream_close = 1; - - if (wsi->mux_substream) { - if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0, - LWS_WRITE_HTTP_FINAL) < 0) { - lwsl_info("%s: COMPL_CLIENT_HTTP: h2 fin wr failed\n", - __func__); - - return -1; - } - } else { - *out++ = '0'; - *out++ = '\x0d'; - *out++ = '\x0a'; - *out++ = '\x0d'; - *out++ = '\x0a'; - - if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 5, - LWS_WRITE_HTTP_FINAL) < 0) { - lwsl_err("%s: COMPL_CLIENT_HTTP: " - "h2 final write failed\n", __func__); - - return -1; - } - } - - return 0; -} - -#endif - -struct lws_proxy_pkt { - struct lws_dll2 pkt_list; - size_t len; - char binary; - char first; - char final; - - /* data follows */ -}; - -#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS) -int -lws_callback_ws_proxy(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct lws_proxy_pkt *pkt; - struct lws_dll2 *dll; - - switch (reason) { - - /* h1 ws proxying... child / client / onward */ - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - if (!wsi->h1_ws_proxied || !wsi->parent) - break; - - lws_process_ws_upgrade2(wsi->parent); - -#if defined(LWS_WITH_HTTP2) - if (wsi->parent->mux_substream) - lwsl_info("%s: proxied h2 -> h1 ws established\n", __func__); -#endif - break; - - case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED: - return 1; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - case LWS_CALLBACK_CLIENT_CLOSED: - lwsl_user("%s: client closed: parent %p\n", __func__, wsi->parent); - if (wsi->parent) - lws_set_timeout(wsi->parent, 1, LWS_TO_KILL_ASYNC); - break; - - case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: - { - unsigned char **p = (unsigned char **)in, *end = (*p) + len, - tmp[128]; - - proxy_header(wsi, wsi->parent, tmp, sizeof(tmp), - WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end); - - proxy_header(wsi, wsi->parent, tmp, sizeof(tmp), - WSI_TOKEN_HTTP_COOKIE, p, end); - - proxy_header(wsi, wsi->parent, tmp, sizeof(tmp), - WSI_TOKEN_HTTP_SET_COOKIE, p, end); - break; - } - - case LWS_CALLBACK_CLIENT_RECEIVE: - wsi->parent->ws->proxy_buffered += len; - if (wsi->parent->ws->proxy_buffered > 10 * 1024 * 1024) { - lwsl_err("%s: proxied ws connection excessive buffering: dropping\n", - __func__); - return -1; - } - pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__); - if (!pkt) - return -1; - - pkt->len = len; - pkt->first = lws_is_first_fragment(wsi); - pkt->final = lws_is_final_fragment(wsi); - pkt->binary = lws_frame_is_binary(wsi); - - memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len); - - lws_dll2_add_tail(&pkt->pkt_list, &wsi->parent->ws->proxy_owner); - lws_callback_on_writable(wsi->parent); - break; - - case LWS_CALLBACK_CLIENT_WRITEABLE: - dll = lws_dll2_get_head(&wsi->ws->proxy_owner); - if (!dll) - break; - - pkt = (struct lws_proxy_pkt *)dll; - if (lws_write(wsi, ((unsigned char *)&pkt[1]) + - LWS_PRE, pkt->len, lws_write_ws_flags( - pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT, - pkt->first, pkt->final)) < 0) - return -1; - - lws_dll2_remove(dll); - lws_free(pkt); - - if (lws_dll2_get_head(&wsi->ws->proxy_owner)) - lws_callback_on_writable(wsi); - break; - - /* h1 ws proxying... parent / server / incoming */ - - case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY: - return 1; - - case LWS_CALLBACK_CLOSED: - lwsl_user("%s: closed\n", __func__); - return -1; - - case LWS_CALLBACK_RECEIVE: - pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__); - if (!pkt) - return -1; - - pkt->len = len; - pkt->first = lws_is_first_fragment(wsi); - pkt->final = lws_is_final_fragment(wsi); - pkt->binary = lws_frame_is_binary(wsi); - - memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len); - - lws_dll2_add_tail(&pkt->pkt_list, &wsi->child_list->ws->proxy_owner); - lws_callback_on_writable(wsi->child_list); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - dll = lws_dll2_get_head(&wsi->ws->proxy_owner); - if (!dll) - break; - - pkt = (struct lws_proxy_pkt *)dll; - if (lws_write(wsi, ((unsigned char *)&pkt[1]) + - LWS_PRE, pkt->len, lws_write_ws_flags( - pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT, - pkt->first, pkt->final)) < 0) - return -1; - - wsi->ws->proxy_buffered -= pkt->len; - - lws_dll2_remove(dll); - lws_free(pkt); - - if (lws_dll2_get_head(&wsi->ws->proxy_owner)) - lws_callback_on_writable(wsi); - break; - - default: - return 0; - } - - return 0; -} - -const struct lws_protocols lws_ws_proxy = { - "lws-ws-proxy", - lws_callback_ws_proxy, - 0, - 8192, - 8192, NULL, 0 -}; - -#endif - -int -lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct lws_ssl_info *si; -#ifdef LWS_WITH_CGI - struct lws_cgi_args *args; -#endif -#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY) - char buf[LWS_PRE + 32 + 8192]; - int n; -#endif -#if defined(LWS_WITH_HTTP_PROXY) - unsigned char **p, *end; - struct lws *parent; -#endif - - switch (reason) { -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - case LWS_CALLBACK_HTTP: -#if defined(LWS_WITH_SERVER) - if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL)) - return -1; - - if (lws_http_transaction_completed(wsi)) -#endif - return -1; - break; -#if defined(LWS_WITH_SERVER) - case LWS_CALLBACK_HTTP_BODY_COMPLETION: -#if defined(LWS_WITH_HTTP_PROXY) - if (wsi->child_list) { - lwsl_user("%s: LWS_CALLBACK_HTTP_BODY_COMPLETION: %d\n", __func__, (int)len); - break; - } -#endif - /* fallthru */ - case LWS_CALLBACK_HTTP_FILE_COMPLETION: - if (lws_http_transaction_completed(wsi)) - return -1; - break; -#endif - -#if defined(LWS_WITH_HTTP_PROXY) - case LWS_CALLBACK_HTTP_BODY: - if (wsi->child_list) { - lwsl_user("%s: LWS_CALLBACK_HTTP_BODY: stashing %d\n", __func__, (int)len); - if (lws_buflist_append_segment(&wsi->http.buflist_post_body, in, len) < 0) - return -1; - lws_callback_on_writable(wsi->child_list); - } - break; -#endif - - case LWS_CALLBACK_HTTP_WRITEABLE: - // lwsl_err("%s: LWS_CALLBACK_HTTP_WRITEABLE\n", __func__); -#ifdef LWS_WITH_CGI - if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS | - LWS_CB_REASON_AUX_BF__CGI)) { - n = lws_cgi_write_split_stdout_headers(wsi); - if (n < 0) { - lwsl_debug("AUX_BF__CGI forcing close\n"); - return -1; - } - if (!n && wsi->http.cgi && - wsi->http.cgi->lsp->stdwsi[LWS_STDOUT]) - lws_rx_flow_control( - wsi->http.cgi->lsp->stdwsi[LWS_STDOUT], 1); - - if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS) - wsi->reason_bf &= - ~LWS_CB_REASON_AUX_BF__CGI_HEADERS; - else - wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI; - - if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) - return -1; - break; - } - - if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) { - if (!wsi->mux_substream) { - memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5); - lwsl_debug("writing chunk term and exiting\n"); - lws_write(wsi, (unsigned char *)buf + - LWS_PRE, 5, LWS_WRITE_HTTP); - } else - lws_write(wsi, (unsigned char *)buf + - LWS_PRE, 0, - LWS_WRITE_HTTP_FINAL); - - /* always close after sending it */ - if (lws_http_transaction_completed(wsi)) - return -1; - return 0; - } -#endif -#if defined(LWS_WITH_HTTP_PROXY) - - if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_HEADERS) { - - wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_HEADERS; - - n = LWS_WRITE_HTTP_HEADERS; - if (!wsi->http.prh_content_length) - n |= LWS_WRITE_H2_STREAM_END; - - lwsl_debug("%s: %p: issuing proxy headers: clen %d\n", - __func__, wsi, (int)wsi->http.prh_content_length); - n = lws_write(wsi, wsi->http.pending_return_headers + - LWS_PRE, - wsi->http.pending_return_headers_len, n); - - lws_free_set_NULL(wsi->http.pending_return_headers); - - if (n < 0) { - lwsl_err("%s: EST_CLIENT_HTTP: write failed\n", - __func__); - return -1; - } - - lws_callback_on_writable(wsi); - break; - } - - if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) { - char *px = buf + LWS_PRE; - int lenx = sizeof(buf) - LWS_PRE - 32; - - /* - * our sink is writeable and our source has something - * to read. So read a lump of source material of - * suitable size to send or what's available, whichever - * is the smaller. - */ - wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY; - if (!lws_get_child(wsi)) - break; - - /* this causes LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ */ - if (lws_http_client_read(lws_get_child(wsi), &px, - &lenx) < 0) { - lwsl_info("%s: LWS_CB_REASON_AUX_BF__PROXY: " - "client closed\n", __func__); - - stream_close(wsi); - - return -1; - } - break; - } - - if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_TRANS_END) { - lwsl_info("%s: LWS_CB_REASON_AUX_BF__PROXY_TRANS_END\n", - __func__); - - wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_TRANS_END; - - if (stream_close(wsi)) - return -1; - - if (lws_http_transaction_completed(wsi)) - return -1; - } -#endif - break; - -#if defined(LWS_WITH_HTTP_PROXY) - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - assert(lws_get_parent(wsi)); - if (!lws_get_parent(wsi)) - break; - lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY; - lws_callback_on_writable(lws_get_parent(wsi)); - break; - - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: { - char *out = buf + LWS_PRE; - - assert(lws_get_parent(wsi)); - - if (wsi->http.proxy_parent_chunked) { - - if (len > sizeof(buf) - LWS_PRE - 16) { - lwsl_err("oversize buf %d %d\n", (int)len, - (int)sizeof(buf) - LWS_PRE - 16); - return -1; - } - - /* - * this only needs dealing with on http/1.1 to allow - * pipelining - */ - n = lws_snprintf(out, 14, "%X\x0d\x0a", (int)len); - out += n; - memcpy(out, in, len); - out += len; - *out++ = '\x0d'; - *out++ = '\x0a'; - - n = lws_write(lws_get_parent(wsi), - (unsigned char *)buf + LWS_PRE, - len + n + 2, LWS_WRITE_HTTP); - } else - n = lws_write(lws_get_parent(wsi), (unsigned char *)in, - len, LWS_WRITE_HTTP); - if (n < 0) - return -1; - break; } - - /* h1 http proxying... */ - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: { - unsigned char *start, *p, *end; - - /* - * We want to proxy these headers, but we are being called - * at the point the onward client was established, which is - * unrelated to the state or writability of our proxy - * connection. - * - * Therefore produce the headers using the onward client ah - * while we have it, and stick them on the output buflist to be - * written on the proxy connection as soon as convenient. - */ - - parent = lws_get_parent(wsi); - - if (!parent) - return 0; - - start = p = (unsigned char *)buf + LWS_PRE; - end = p + sizeof(buf) - LWS_PRE - 256; - - if (lws_add_http_header_status(lws_get_parent(wsi), - lws_http_client_http_response(wsi), &p, end)) - return 1; - - /* - * copy these headers from the client connection to the parent - */ - - proxy_header(parent, wsi, end, 256, - WSI_TOKEN_HTTP_CONTENT_LENGTH, &p, end); - proxy_header(parent, wsi, end, 256, - WSI_TOKEN_HTTP_CONTENT_TYPE, &p, end); - proxy_header(parent, wsi, end, 256, - WSI_TOKEN_HTTP_ETAG, &p, end); - proxy_header(parent, wsi, end, 256, - WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, &p, end); - proxy_header(parent, wsi, end, 256, - WSI_TOKEN_HTTP_CONTENT_ENCODING, &p, end); - proxy_header(parent, wsi, end, 256, - WSI_TOKEN_HTTP_CACHE_CONTROL, &p, end); - proxy_header(parent, wsi, end, 256, - WSI_TOKEN_HTTP_SET_COOKIE, &p, end); - proxy_header(parent, wsi, end, 256, - WSI_TOKEN_HTTP_LOCATION, &p, end); - - if (!parent->mux_substream) - if (lws_add_http_header_by_token(parent, - WSI_TOKEN_CONNECTION, (unsigned char *)"close", - 5, &p, end)) - return -1; - - /* - * We proxy using h1 only atm, and strip any chunking so it - * can go back out on h2 just fine. - * - * However if we are actually going out on h1, we need to add - * our own chunking since we still don't know the size. - */ - - if (!parent->mux_substream && - !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { - lwsl_debug("downstream parent chunked\n"); - if (lws_add_http_header_by_token(parent, - WSI_TOKEN_HTTP_TRANSFER_ENCODING, - (unsigned char *)"chunked", 7, &p, end)) - return -1; - - wsi->http.proxy_parent_chunked = 1; - } - - if (lws_finalize_http_header(parent, &p, end)) - return 1; - - parent->http.prh_content_length = -1; - if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) - parent->http.prh_content_length = atoll( - lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_CONTENT_LENGTH)); - - parent->http.pending_return_headers_len = lws_ptr_diff(p, start); - parent->http.pending_return_headers = - lws_malloc(parent->http.pending_return_headers_len + - LWS_PRE, "return proxy headers"); - if (!parent->http.pending_return_headers) - return -1; - - memcpy(parent->http.pending_return_headers + LWS_PRE, start, - parent->http.pending_return_headers_len); - - parent->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY_HEADERS; - - lwsl_debug("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: " - "prepared %d headers (len %d)\n", __func__, - lws_http_client_http_response(wsi), - (int)parent->http.prh_content_length); - - /* - * so at this point, the onward client connection can bear - * traffic. We might be doing a POST and have pending cached - * inbound stuff to send, it can go now. - */ - - lws_callback_on_writable(parent); - - break; } - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_info("%s: COMPLETED_CLIENT_HTTP: %p (parent %p)\n", - __func__, wsi, lws_get_parent(wsi)); - if (!lws_get_parent(wsi)) - break; - lws_get_parent(wsi)->reason_bf |= - LWS_CB_REASON_AUX_BF__PROXY_TRANS_END; - lws_callback_on_writable(lws_get_parent(wsi)); - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - if (!lws_get_parent(wsi)) - break; - // lwsl_err("%s: LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", __func__); - lws_set_timeout(lws_get_parent(wsi), LWS_TO_KILL_ASYNC, - PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE); - break; - - case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: - parent = lws_get_parent(wsi); - if (!parent) - break; - - p = (unsigned char **)in; - end = (*p) + len; - - /* - * copy these headers from the parent request to the client - * connection's request - */ - - proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), - WSI_TOKEN_HTTP_ETAG, p, end); - proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), - WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, p, end); - proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), - WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end); - proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), - WSI_TOKEN_HTTP_ACCEPT_ENCODING, p, end); - proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), - WSI_TOKEN_HTTP_CACHE_CONTROL, p, end); - proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), - WSI_TOKEN_HTTP_COOKIE, p, end); - - buf[0] = '\0'; - lws_get_peer_simple(parent, buf, sizeof(buf)); - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_X_FORWARDED_FOR, - (unsigned char *)buf, (int)strlen(buf), p, end)) - return -1; - - break; -#endif - -#ifdef LWS_WITH_CGI - /* CGI IO events (POLLIN/OUT) appear here, our default policy is: - * - * - POST data goes on subprocess stdin - * - subprocess stdout goes on http via writeable callback - * - subprocess stderr goes to the logs - */ - case LWS_CALLBACK_CGI: - args = (struct lws_cgi_args *)in; - switch (args->ch) { /* which of stdin/out/err ? */ - case LWS_STDIN: - /* TBD stdin rx flow control */ - break; - case LWS_STDOUT: - /* quench POLLIN on STDOUT until MASTER got writeable */ - lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0); - wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI; - /* when writing to MASTER would not block */ - lws_callback_on_writable(wsi); - break; - case LWS_STDERR: - n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]); - if (n < 0) - break; - n = read(n, buf, sizeof(buf) - 2); - if (n > 0) { - if (buf[n - 1] != '\n') - buf[n++] = '\n'; - buf[n] = '\0'; - lwsl_notice("CGI-stderr: %s\n", buf); - } - break; - } - break; - - case LWS_CALLBACK_CGI_TERMINATED: - lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n", - wsi->http.cgi->explicitly_chunked, - (uint64_t)wsi->http.cgi->content_length); - if (!(wsi->http.cgi->explicitly_chunked && wsi->mux_substream) && - !wsi->http.cgi->content_length) { - /* send terminating chunk */ - lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n"); - wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END; - lws_callback_on_writable(wsi); - lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3); - break; - } - if (wsi->mux_substream && !wsi->cgi_stdout_zero_length) - lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0, - LWS_WRITE_HTTP_FINAL); - - if (lws_http_transaction_completed(wsi)) - return -1; - return 0; - - case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */ - args = (struct lws_cgi_args *)in; - args->data[args->len] = '\0'; - if (!args->stdwsi[LWS_STDIN]) - return -1; - n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]); - if (n < 0) - return -1; - -#if defined(LWS_WITH_ZLIB) - if (wsi->http.cgi->gzip_inflate) { - /* gzip handling */ - - if (!wsi->http.cgi->gzip_init) { - lwsl_info("inflating gzip\n"); - - memset(&wsi->http.cgi->inflate, 0, - sizeof(wsi->http.cgi->inflate)); - - if (inflateInit2(&wsi->http.cgi->inflate, - 16 + 15) != Z_OK) { - lwsl_err("%s: iniflateInit failed\n", - __func__); - return -1; - } - - wsi->http.cgi->gzip_init = 1; - } - - wsi->http.cgi->inflate.next_in = args->data; - wsi->http.cgi->inflate.avail_in = args->len; - - do { - - wsi->http.cgi->inflate.next_out = - wsi->http.cgi->inflate_buf; - wsi->http.cgi->inflate.avail_out = - sizeof(wsi->http.cgi->inflate_buf); - - n = inflate(&wsi->http.cgi->inflate, - Z_SYNC_FLUSH); - - switch (n) { - case Z_NEED_DICT: - case Z_STREAM_ERROR: - case Z_DATA_ERROR: - case Z_MEM_ERROR: - inflateEnd(&wsi->http.cgi->inflate); - wsi->http.cgi->gzip_init = 0; - lwsl_err("zlib error inflate %d\n", n); - return -1; - } - - if (wsi->http.cgi->inflate.avail_out != - sizeof(wsi->http.cgi->inflate_buf)) { - int written; - - written = write(args->stdwsi[LWS_STDIN]->desc.filefd, - wsi->http.cgi->inflate_buf, - sizeof(wsi->http.cgi->inflate_buf) - - wsi->http.cgi->inflate.avail_out); - - if (written != (int)( - sizeof(wsi->http.cgi->inflate_buf) - - wsi->http.cgi->inflate.avail_out)) { - lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: " - "sent %d only %d went", n, args->len); - } - - if (n == Z_STREAM_END) { - lwsl_err("gzip inflate end\n"); - inflateEnd(&wsi->http.cgi->inflate); - wsi->http.cgi->gzip_init = 0; - break; - } - - } else - break; - - if (wsi->http.cgi->inflate.avail_out) - break; - - } while (1); - - return args->len; - } -#endif /* WITH_ZLIB */ - - n = write(n, args->data, args->len); -// lwsl_hexdump_notice(args->data, args->len); - if (n < args->len) - lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: " - "sent %d only %d went", n, args->len); - - if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] && - args->stdwsi[LWS_STDIN]->desc.filefd > 0) { - wsi->http.cgi->post_in_expected -= n; - if (!wsi->http.cgi->post_in_expected) { - struct lws *siwsi = args->stdwsi[LWS_STDIN]; - - lwsl_debug("%s: expected POST in end: " - "closing stdin wsi %p, fd %d\n", - __func__, siwsi, siwsi->desc.sockfd); - - __remove_wsi_socket_from_fds(siwsi); - lwsi_set_state(siwsi, LRS_DEAD_SOCKET); - siwsi->socket_is_permanently_unusable = 1; -// lws_remove_child_from_any_parent(siwsi); - if (wsi->context->event_loop_ops-> - close_handle_manually) { - - wsi->context->event_loop_ops-> - close_handle_manually(siwsi); - siwsi->told_event_loop_closed = 1; - } else { - compatible_close(siwsi->desc.sockfd); - __lws_free_wsi(siwsi); - } - wsi->http.cgi->lsp->pipe_fds[LWS_STDIN][1] = -1; - -// args->stdwsi[LWS_STDIN] = NULL; - } - } - - return n; -#endif /* WITH_CGI */ -#endif /* ROLE_ H1 / H2 */ - case LWS_CALLBACK_SSL_INFO: - si = in; - - (void)si; - lwsl_notice("LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n", - si->where, si->ret); - break; - -#if LWS_MAX_SMP > 1 - case LWS_CALLBACK_GET_THREAD_ID: - return (int)(lws_intptr_t)pthread_self(); -#endif - - default: - break; - } - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/core-net/lws-dsh.c libwebsockets-2.4.2/lib/core-net/lws-dsh.c --- libwebsockets-4.0.20/lib/core-net/lws-dsh.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/lws-dsh.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,504 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -struct lws_dsh_search { - size_t required; - int kind; - lws_dsh_obj_t *best; - lws_dsh_t *dsh; - - lws_dsh_t *already_checked; - lws_dsh_t *this_dsh; -}; - -static int -_lws_dsh_alloc_tail(lws_dsh_t *dsh, int kind, const void *src1, size_t size1, - const void *src2, size_t size2, lws_dll2_t *replace); - -static size_t -lws_dsh_align(size_t length) -{ - size_t align = sizeof(int *); - - if (length & (align - 1)) - length += align - (length & (align - 1)); - - return length; -} - -lws_dsh_t * -lws_dsh_create(lws_dll2_owner_t *owner, size_t buf_len, int count_kinds) -{ - size_t oha_len = sizeof(lws_dsh_obj_head_t) * ++count_kinds; - lws_dsh_obj_t *obj; - lws_dsh_t *dsh; - int n; - - assert(buf_len); - assert(count_kinds > 1); - - dsh = lws_malloc(sizeof(lws_dsh_t) + buf_len + oha_len, __func__); - if (!dsh) - return NULL; - - /* set convenience pointers to the overallocated parts */ - - dsh->oha = (lws_dsh_obj_head_t *)&dsh[1]; - dsh->buf = ((uint8_t *)dsh->oha) + oha_len; - dsh->count_kinds = count_kinds; - dsh->buffer_size = buf_len; - dsh->being_destroyed = 0; - - /* clear down the obj heads array */ - - memset(dsh->oha, 0, oha_len); - for (n = 0; n < count_kinds; n++) - dsh->oha[n].kind = n; - - /* initially the whole buffer is on the free kind (0) list */ - - obj = (lws_dsh_obj_t *)dsh->buf; - memset(obj, 0, sizeof(*obj)); - obj->asize = buf_len - sizeof(*obj); - - lws_dll2_add_head(&obj->list, &dsh->oha[0].owner); - - dsh->locally_free = obj->asize; - dsh->locally_in_use = 0; - - lws_dll2_clear(&dsh->list); - if (owner) - lws_dll2_add_head(&dsh->list, owner); - - // lws_dsh_describe(dsh, "post-init"); - - return dsh; -} - -static int -search_best_free(struct lws_dll2 *d, void *user) -{ - struct lws_dsh_search *s = (struct lws_dsh_search *)user; - lws_dsh_obj_t *obj = lws_container_of(d, lws_dsh_obj_t, list); - - lwsl_debug("%s: obj %p, asize %zu (req %zu)\n", __func__, obj, - obj->asize, s->required); - - if (obj->asize >= s->required && - (!s->best || obj->asize < s->best->asize)) { - s->best = obj; - s->dsh = s->this_dsh; - } - - return 0; -} - -static int -try_foreign(struct lws_dll2 *d, void *user) -{ - struct lws_dsh_search *s = (struct lws_dsh_search *)user; - lws_dsh_t *dsh1 = lws_container_of(d, lws_dsh_t, list); - - if (dsh1 == s->already_checked) - return 0; - - if (dsh1->being_destroyed) - return 0; - - if (dsh1->count_kinds < s->kind + 1) - return 0; - - lwsl_debug("%s: actual try_foreign: dsh %p (free list size %d)\n", - __func__, dsh1, dsh1->oha[0].owner.count); - - s->this_dsh = dsh1; - if (lws_dll2_foreach_safe(&dsh1->oha[0].owner, s, search_best_free)) - return 1; - - return 0; -} - -static int -free_foreign(struct lws_dll2 *d, void *user) -{ - lws_dsh_obj_t *obj = lws_container_of(d, lws_dsh_obj_t, list); - lws_dsh_t *dsh = (lws_dsh_t *)user; - void *p = (void *)&obj[1]; - - if (obj->dsh != dsh) - lws_dsh_free(&p); - - return 0; -} - -static int -evict2(struct lws_dll2 *d, void *user) -{ - lws_dsh_obj_t *obj = lws_container_of(d, lws_dsh_obj_t, list); - lws_dsh_t *dsh = (lws_dsh_t *)user; - void *p; - - if (obj->dsh != dsh) - return 0; - - /* - * If we are here, it means obj is a live object that is allocated on - * the dsh being destroyed, from a different dsh. We need to migrate - * the object to a dsh that isn't being destroyed. - */ - - lwsl_debug("%s: migrating object size %zu\n", __func__, obj->size); - - if (_lws_dsh_alloc_tail(dsh, 0, (void *)&obj[1], obj->size, NULL, 0, &obj->list)) { - lwsl_notice("%s: failed to migrate object\n", __func__); - /* - * only thing we can do is drop the logical object - */ - p = (uint8_t *)&obj[1]; - lws_dsh_free(&p); - } - - return 0; -} - -static int -evict1(struct lws_dll2 *d, void *user) -{ - lws_dsh_t *dsh1 = lws_container_of(d, lws_dsh_t, list); - lws_dsh_t *dsh = (lws_dsh_t *)user; - int n; - - if (dsh1->being_destroyed) - return 0; - - /* - * For every dsh that's not being destroyed, send every object to - * evict2 for checking. - */ - - lwsl_debug("%s: checking dsh %p\n", __func__, dsh1); - - for (n = 1; n < dsh1->count_kinds; n++) { - lws_dll2_describe(&dsh1->oha[n].owner, "check dsh1"); - lws_dll2_foreach_safe(&dsh1->oha[n].owner, dsh, evict2); - } - - return 0; -} - -void -lws_dsh_destroy(lws_dsh_t **pdsh) -{ - lws_dsh_t *dsh = *pdsh; - int n; - - if (!dsh) - return; - - lwsl_debug("%s: destroying dsh %p\n", __func__, dsh); - - dsh->being_destroyed = 1; - - /* we need to explicitly free any of our allocations in foreign dsh */ - - for (n = 1; n < dsh->count_kinds; n++) - lws_dll2_foreach_safe(&dsh->oha[n].owner, dsh, free_foreign); - - /* - * We need to have anybody else with allocations in us evict them - * and make a copy in a buffer that isn't being destroyed - */ - - if (dsh->list.owner) - lws_dll2_foreach_safe(dsh->list.owner, dsh, evict1); - - lws_dll2_remove(&dsh->list); - - /* everything else is in one heap allocation */ - - lws_free_set_NULL(*pdsh); -} - -static int -_lws_dsh_alloc_tail(lws_dsh_t *dsh, int kind, const void *src1, size_t size1, - const void *src2, size_t size2, lws_dll2_t *replace) -{ - size_t asize = sizeof(lws_dsh_obj_t) + lws_dsh_align(size1 + size2); - struct lws_dsh_search s; - - assert(kind >= 0); - kind++; - assert(!dsh || kind < dsh->count_kinds); - - /* - * Search our free list looking for the smallest guy who will fit - * what we want to allocate - */ - s.required = asize; - s.kind = kind; - s.best = NULL; - s.already_checked = NULL; - s.this_dsh = dsh; - - if (dsh && !dsh->being_destroyed) - lws_dll2_foreach_safe(&dsh->oha[0].owner, &s, search_best_free); - - if (!s.best) { - /* - * Let's see if any other buffer has room - */ - s.already_checked = dsh; - - if (dsh && dsh->list.owner) - lws_dll2_foreach_safe(dsh->list.owner, &s, try_foreign); - - if (!s.best) { - lwsl_notice("%s: no buffer has space\n", __func__); - - return 1; - } - } - - /* anything coming out of here must be aligned */ - assert(!(((unsigned long)s.best) & (sizeof(int *) - 1))); - - if (s.best->asize < asize + (2 * sizeof(*s.best))) { - /* - * Exact fit, or close enough we can't / don't want to have to - * track the little bit of free area that would be left. - * - * Move the object from the free list to the oha of the - * desired kind - */ - lws_dll2_remove(&s.best->list); - s.best->dsh = s.dsh; - s.best->size = size1 + size2; - memcpy(&s.best[1], src1, size1); - if (src2) - memcpy((uint8_t *)&s.best[1] + size1, src2, size2); - - if (replace) { - s.best->list.prev = replace->prev; - s.best->list.next = replace->next; - s.best->list.owner = replace->owner; - if (replace->prev) - replace->prev->next = &s.best->list; - if (replace->next) - replace->next->prev = &s.best->list; - } else - if (dsh) - lws_dll2_add_tail(&s.best->list, &dsh->oha[kind].owner); - - assert(s.dsh->locally_free >= s.best->asize); - s.dsh->locally_free -= s.best->asize; - s.dsh->locally_in_use += s.best->asize; - assert(s.dsh->locally_in_use <= s.dsh->buffer_size); - } else { - lws_dsh_obj_t *obj; - - /* - * Free area was oversize enough that we need to split it. - * - * Leave the first part of the free area where it is and - * reduce its extent by our asize. Use the latter part of - * the original free area as the allocation. - */ - lwsl_debug("%s: splitting... free reduce %zu -> %zu\n", - __func__, s.best->asize, s.best->asize - asize); - - s.best->asize -= asize; - - /* latter part becomes new object */ - - obj = (lws_dsh_obj_t *)(((uint8_t *)s.best) + s.best->asize); - - lws_dll2_clear(&obj->list); - obj->dsh = s.dsh; - obj->size = size1 + size2; - obj->asize = asize; - - memcpy(&obj[1], src1, size1); - if (src2) - memcpy((uint8_t *)&obj[1] + size1, src2, size2); - - if (replace) { - s.best->list.prev = replace->prev; - s.best->list.next = replace->next; - s.best->list.owner = replace->owner; - if (replace->prev) - replace->prev->next = &s.best->list; - if (replace->next) - replace->next->prev = &s.best->list; - } else - if (dsh) - lws_dll2_add_tail(&obj->list, &dsh->oha[kind].owner); - - assert(s.dsh->locally_free >= asize); - s.dsh->locally_free -= asize; - s.dsh->locally_in_use += asize; - assert(s.dsh->locally_in_use <= s.dsh->buffer_size); - } - - // lws_dsh_describe(dsh, "post-alloc"); - - return 0; -} - -int -lws_dsh_alloc_tail(lws_dsh_t *dsh, int kind, const void *src1, size_t size1, - const void *src2, size_t size2) -{ - return _lws_dsh_alloc_tail(dsh, kind, src1, size1, src2, size2, NULL); -} - -static int -buf_compare(const lws_dll2_t *d, const lws_dll2_t *i) -{ - return (int)lws_ptr_diff(d, i); -} - -void -lws_dsh_free(void **pobj) -{ - lws_dsh_obj_t *_o = (lws_dsh_obj_t *)((uint8_t *)(*pobj) - sizeof(*_o)), - *_o2; - lws_dsh_t *dsh = _o->dsh; - - /* anything coming out of here must be aligned */ - assert(!(((unsigned long)_o) & (sizeof(int *) - 1))); - - /* - * Remove the object from its list and place on the free list of the - * dsh the buffer space belongs to - */ - - lws_dll2_remove(&_o->list); - *pobj = NULL; - - assert(dsh->locally_in_use >= _o->asize); - dsh->locally_free += _o->asize; - dsh->locally_in_use -= _o->asize; - assert(dsh->locally_in_use <= dsh->buffer_size); - - /* - * The free space list is sorted in buffer address order, so detecting - * coalescing opportunities is cheap. Because the free list should be - * continuously tending to reduce by coalescing, the sorting should not - * be expensive to maintain. - */ - _o->size = 0; /* not meaningful when on free list */ - lws_dll2_add_sorted(&_o->list, &_o->dsh->oha[0].owner, buf_compare); - - /* First check for already-free block at the end we can subsume. - * Because the free list is sorted, if there is such a guy he is - * already our list.next */ - - _o2 = (lws_dsh_obj_t *)_o->list.next; - if (_o2 && (uint8_t *)_o + _o->asize == (uint8_t *)_o2) { - /* - * since we are freeing _obj, we can coalesce with a - * free area immediately ahead of it - * - * [ _o (being freed) ][ _o2 (free) ] -> [ larger _o ] - */ - _o->asize += _o2->asize; - - /* guy next to us was absorbed into us */ - lws_dll2_remove(&_o2->list); - } - - /* Then check if we can be subsumed by a free block behind us. - * Because the free list is sorted, if there is such a guy he is - * already our list.prev */ - - _o2 = (lws_dsh_obj_t *)_o->list.prev; - if (_o2 && (uint8_t *)_o2 + _o2->asize == (uint8_t *)_o) { - /* - * since we are freeing obj, we can coalesce it with - * the previous free area that abuts it - * - * [ _o2 (free) ][ _o (being freed) ] -> [ larger _o2 ] - */ - _o2->asize += _o->asize; - - /* we were absorbed! */ - lws_dll2_remove(&_o->list); - } - - // lws_dsh_describe(dsh, "post-alloc"); -} - -int -lws_dsh_get_head(lws_dsh_t *dsh, int kind, void **obj, size_t *size) -{ - lws_dsh_obj_t *_obj = (lws_dsh_obj_t *) - lws_dll2_get_head(&dsh->oha[kind + 1].owner); - - if (!_obj) { - *obj = 0; - *size = 0; - - return 1; /* there is no head */ - } - - *obj = (void *)(&_obj[1]); - *size = _obj->size; - - /* anything coming out of here must be aligned */ - assert(!(((unsigned long)(*obj)) & (sizeof(int *) - 1))); - - return 0; /* we returned the head */ -} - -#if defined(_DEBUG) - -static int -describe_kind(struct lws_dll2 *d, void *user) -{ - lws_dsh_obj_t *obj = lws_container_of(d, lws_dsh_obj_t, list); - - lwsl_info(" _obj %p - %p, dsh %p, size %zu, asize %zu\n", - obj, (uint8_t *)obj + obj->asize, - obj->dsh, obj->size, obj->asize); - - return 0; -} - -void -lws_dsh_describe(lws_dsh_t *dsh, const char *desc) -{ - int n = 0; - - lwsl_info("%s: dsh %p, bufsize %zu, kinds %d, lf: %zu, liu: %zu, %s\n", - __func__, dsh, dsh->buffer_size, dsh->count_kinds, - dsh->locally_free, dsh->locally_in_use, desc); - - for (n = 0; n < dsh->count_kinds; n++) { - lwsl_info(" Kind %d:\n", n); - lws_dll2_foreach_safe(&dsh->oha[n].owner, dsh, describe_kind); - } -} -#endif diff -Nru libwebsockets-4.0.20/lib/core-net/network.c libwebsockets-2.4.2/lib/core-net/network.c --- libwebsockets-4.0.20/lib/core-net/network.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/network.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,839 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) -static int -interface_to_sa(struct lws_vhost *vh, const char *ifname, - struct sockaddr_in *addr, size_t addrlen, int allow_ipv6) -{ - int ipv6 = 0; -#ifdef LWS_WITH_IPV6 - if (allow_ipv6) - ipv6 = LWS_IPV6_ENABLED(vh); -#endif - (void)vh; - - return lws_interface_to_sa(ipv6, ifname, addr, addrlen); -} -#endif - -#ifndef LWS_PLAT_OPTEE -static int -lws_get_addresses(struct lws_vhost *vh, void *ads, char *name, - int name_len, char *rip, int rip_len) -{ - struct addrinfo ai, *res; - struct sockaddr_in addr4; - - rip[0] = '\0'; - name[0] = '\0'; - addr4.sin_family = AF_UNSPEC; - -#ifdef LWS_WITH_IPV6 - if (LWS_IPV6_ENABLED(vh)) { - if (!lws_plat_inet_ntop(AF_INET6, - &((struct sockaddr_in6 *)ads)->sin6_addr, - rip, rip_len)) { - lwsl_err("inet_ntop: %s", strerror(LWS_ERRNO)); - return -1; - } - - // Strip off the IPv4 to IPv6 header if one exists - if (strncmp(rip, "::ffff:", 7) == 0) - memmove(rip, rip + 7, strlen(rip) - 6); - - getnameinfo((struct sockaddr *)ads, sizeof(struct sockaddr_in6), - name, name_len, NULL, 0, 0); - - return 0; - } else -#endif - { - struct addrinfo *result; - - memset(&ai, 0, sizeof ai); - ai.ai_family = PF_UNSPEC; - ai.ai_socktype = SOCK_STREAM; -#if !defined(LWS_PLAT_FREERTOS) - if (getnameinfo((struct sockaddr *)ads, - sizeof(struct sockaddr_in), - name, name_len, NULL, 0, 0)) - return -1; -#endif - - if (getaddrinfo(name, NULL, &ai, &result)) - return -1; - - res = result; - while (addr4.sin_family == AF_UNSPEC && res) { - switch (res->ai_family) { - case AF_INET: - addr4.sin_addr = - ((struct sockaddr_in *)res->ai_addr)->sin_addr; - addr4.sin_family = AF_INET; - break; - } - - res = res->ai_next; - } - freeaddrinfo(result); - } - - if (addr4.sin_family == AF_UNSPEC) - return -1; - - if (lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len) == NULL) - return -1; - - return 0; -} - -const char * -lws_get_peer_simple_fd(lws_sockfd_type fd, char *name, size_t namelen) -{ - lws_sockaddr46 sa46; - socklen_t len = sizeof(sa46); - - if (getpeername(fd, (struct sockaddr *)&sa46, &len) < 0) { - lws_snprintf(name, namelen, "getpeername: %s", - strerror(LWS_ERRNO)); - return name; - } - - lws_sa46_write_numeric_address(&sa46, name, namelen); - - return name; -} - -const char * -lws_get_peer_simple(struct lws *wsi, char *name, size_t namelen) -{ - wsi = lws_get_network_wsi(wsi); - return lws_get_peer_simple_fd(wsi->desc.sockfd, name, namelen); -} -#endif - -void -lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, - int name_len, char *rip, int rip_len) -{ -#ifndef LWS_PLAT_OPTEE - socklen_t len; -#ifdef LWS_WITH_IPV6 - struct sockaddr_in6 sin6; -#endif - struct sockaddr_in sin4; - void *p; - - rip[0] = '\0'; - name[0] = '\0'; - -#ifdef LWS_WITH_IPV6 - if (LWS_IPV6_ENABLED(wsi->vhost)) { - len = sizeof(sin6); - p = &sin6; - } else -#endif - { - len = sizeof(sin4); - p = &sin4; - } - - if (getpeername(fd, p, &len) < 0) { - lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); - goto bail; - } - - lws_get_addresses(wsi->vhost, p, name, name_len, rip, rip_len); - -bail: -#endif - (void)wsi; - (void)fd; - (void)name; - (void)name_len; - (void)rip; - (void)rip_len; -} - - - -/* note: this returns a random port, or one of these <= 0 return codes: - * - * LWS_ITOSA_USABLE: the interface is usable, returned if so and sockfd invalid - * LWS_ITOSA_NOT_EXIST: the requested iface does not even exist - * LWS_ITOSA_NOT_USABLE: the requested iface exists but is not usable (eg, no IP) - * LWS_ITOSA_BUSY: the port at the requested iface + port is already in use - */ - -int -lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, - const char *iface, int ipv6_allowed) -{ -#ifdef LWS_WITH_UNIX_SOCK - struct sockaddr_un serv_unix; -#endif -#ifdef LWS_WITH_IPV6 - struct sockaddr_in6 serv_addr6; -#endif - struct sockaddr_in serv_addr4; -#ifndef LWS_PLAT_OPTEE - socklen_t len = sizeof(struct sockaddr_storage); -#endif - int n; -#if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) - int m; -#endif - struct sockaddr_storage sin; - struct sockaddr *v; - - memset(&sin, 0, sizeof(sin)); - -#if defined(LWS_WITH_UNIX_SOCK) - if (!port && LWS_UNIX_SOCK_ENABLED(vhost)) { - v = (struct sockaddr *)&serv_unix; - memset(&serv_unix, 0, sizeof(serv_unix)); - serv_unix.sun_family = AF_UNIX; - if (!iface) - return LWS_ITOSA_NOT_EXIST; - if (sizeof(serv_unix.sun_path) <= strlen(iface)) { - lwsl_err("\"%s\" too long for UNIX domain socket\n", - iface); - return LWS_ITOSA_NOT_EXIST; - } - n = sizeof(uint16_t) + strlen(iface); - strcpy(serv_unix.sun_path, iface); - if (serv_unix.sun_path[0] == '@') - serv_unix.sun_path[0] = '\0'; - else - unlink(serv_unix.sun_path); - - //lwsl_hexdump_notice(v, n); - - } else -#endif -#if defined(LWS_WITH_IPV6) && !defined(LWS_PLAT_FREERTOS) - if (ipv6_allowed && LWS_IPV6_ENABLED(vhost)) { - v = (struct sockaddr *)&serv_addr6; - n = sizeof(struct sockaddr_in6); - memset(&serv_addr6, 0, sizeof(serv_addr6)); - if (iface) { - m = interface_to_sa(vhost, iface, - (struct sockaddr_in *)v, n, 1); - if (m == LWS_ITOSA_NOT_USABLE) { - lwsl_info("%s: netif %s: Not usable\n", - __func__, iface); - return m; - } - if (m == LWS_ITOSA_NOT_EXIST) { - lwsl_info("%s: netif %s: Does not exist\n", - __func__, iface); - return m; - } - serv_addr6.sin6_scope_id = lws_get_addr_scope(iface); - } - - serv_addr6.sin6_family = AF_INET6; - serv_addr6.sin6_port = htons(port); - } else -#endif - { - v = (struct sockaddr *)&serv_addr4; - n = sizeof(serv_addr4); - memset(&serv_addr4, 0, sizeof(serv_addr4)); - serv_addr4.sin_addr.s_addr = INADDR_ANY; - serv_addr4.sin_family = AF_INET; - -#if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) - if (iface) { - m = interface_to_sa(vhost, iface, - (struct sockaddr_in *)v, n, 0); - if (m == LWS_ITOSA_NOT_USABLE) { - lwsl_info("%s: netif %s: Not usable\n", - __func__, iface); - return m; - } - if (m == LWS_ITOSA_NOT_EXIST) { - lwsl_info("%s: netif %s: Does not exist\n", - __func__, iface); - return m; - } - } -#endif - serv_addr4.sin_port = htons(port); - } /* ipv4 */ - - /* just checking for the interface extant */ - if (sockfd == LWS_SOCK_INVALID) - return LWS_ITOSA_USABLE; - - n = bind(sockfd, v, n); -#ifdef LWS_WITH_UNIX_SOCK - if (n < 0 && LWS_UNIX_SOCK_ENABLED(vhost)) { - lwsl_err("ERROR on binding fd %d to \"%s\" (%d %d)\n", - sockfd, iface, n, LWS_ERRNO); - return LWS_ITOSA_NOT_EXIST; - } else -#endif - if (n < 0) { - int _lws_errno = LWS_ERRNO; - - lwsl_err("ERROR on binding fd %d to port %d (%d %d)\n", - sockfd, port, n, _lws_errno); - - /* if something already listening, tell caller to fail permanently */ - - if (_lws_errno == LWS_EADDRINUSE) - return LWS_ITOSA_BUSY; - - /* otherwise ask caller to retry later */ - - return LWS_ITOSA_NOT_EXIST; - } - -#if defined(LWS_WITH_UNIX_SOCK) - if (!port && LWS_UNIX_SOCK_ENABLED(vhost)) { - uid_t uid = vhost->context->uid; - gid_t gid = vhost->context->gid; - - if (vhost->unix_socket_perms) { - if (lws_plat_user_colon_group_to_ids( - vhost->unix_socket_perms, &uid, &gid)) { - lwsl_err("%s: Failed to translate %s\n", - __func__, vhost->unix_socket_perms); - return LWS_ITOSA_NOT_EXIST; - } - } - if (uid && gid) { - if (chown(serv_unix.sun_path, uid, gid)) { - lwsl_err("%s: failed to set %s perms %u:%u\n", - __func__, serv_unix.sun_path, - (unsigned int)uid, (unsigned int)gid); - - return LWS_ITOSA_NOT_EXIST; - } - lwsl_notice("%s: vh %s unix skt %s perms %u:%u\n", - __func__, vhost->name, serv_unix.sun_path, - (unsigned int)uid, (unsigned int)gid); - - if (chmod(serv_unix.sun_path, 0660)) { - lwsl_err("%s: failed to set %s to 0600 mode\n", - __func__, serv_unix.sun_path); - - return LWS_ITOSA_NOT_EXIST; - } - } - } -#endif - -#ifndef LWS_PLAT_OPTEE - if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) - lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO)); - else -#endif -#if defined(LWS_WITH_IPV6) - port = (sin.ss_family == AF_INET6) ? - ntohs(((struct sockaddr_in6 *) &sin)->sin6_port) : - ntohs(((struct sockaddr_in *) &sin)->sin_port); -#else - { - struct sockaddr_in sain; - memcpy(&sain, &sin, sizeof(sain)); - port = ntohs(sain.sin_port); - } -#endif - - return port; -} - -unsigned int -lws_retry_get_delay_ms(struct lws_context *context, - const lws_retry_bo_t *retry, uint16_t *ctry, - char *conceal) -{ - uint64_t ms = 3000, pc = 30; /* sane-ish defaults if no retry table */ - uint16_t ra; - - if (conceal) - *conceal = 0; - - if (retry) { - if (*ctry < retry->retry_ms_table_count) - ms = retry->retry_ms_table[*ctry]; - else - ms = retry->retry_ms_table[ - retry->retry_ms_table_count - 1]; - - /* if no percent given, use the default 30% */ - if (retry->jitter_percent) - pc = retry->jitter_percent; - } - - if (lws_get_random(context, &ra, sizeof(ra)) == sizeof(ra)) - ms += ((ms * pc * ra) >> 16) / 100; - else - assert(0); - - if (*ctry < 0xffff) - (*ctry)++; - - if (retry && conceal) - *conceal = (int)*ctry <= retry->conceal_count; - - return (unsigned int)ms; -} - -int -lws_retry_sul_schedule(struct lws_context *context, int tid, - lws_sorted_usec_list_t *sul, - const lws_retry_bo_t *retry, sul_cb_t cb, uint16_t *ctry) -{ - char conceal; - uint64_t ms = lws_retry_get_delay_ms(context, retry, ctry, &conceal); - - if (!conceal) - return 1; - - lwsl_info("%s: sul %p: scheduling retry in %dms\n", __func__, sul, - (int)ms); - - lws_sul_schedule(context, tid, sul, cb, ms * 1000); - - return 0; -} - -int -lws_retry_sul_schedule_retry_wsi(struct lws *wsi, lws_sorted_usec_list_t *sul, - sul_cb_t cb, uint16_t *ctry) -{ - return lws_retry_sul_schedule(wsi->context, wsi->tsi, sul, - wsi->retry_policy, cb, ctry); -} - -#if defined(LWS_WITH_IPV6) -unsigned long -lws_get_addr_scope(const char *ipaddr) -{ - unsigned long scope = 0; - -#ifndef WIN32 - struct ifaddrs *addrs, *addr; - char ip[NI_MAXHOST]; - unsigned int i; - - getifaddrs(&addrs); - for (addr = addrs; addr; addr = addr->ifa_next) { - if (!addr->ifa_addr || - addr->ifa_addr->sa_family != AF_INET6) - continue; - - getnameinfo(addr->ifa_addr, - sizeof(struct sockaddr_in6), - ip, sizeof(ip), - NULL, 0, NI_NUMERICHOST); - - i = 0; - while (ip[i]) - if (ip[i++] == '%') { - ip[i - 1] = '\0'; - break; - } - - if (!strcmp(ip, ipaddr)) { - scope = if_nametoindex(addr->ifa_name); - break; - } - } - freeifaddrs(addrs); -#else - PIP_ADAPTER_ADDRESSES adapter, addrs = NULL; - PIP_ADAPTER_UNICAST_ADDRESS addr; - ULONG size = 0; - DWORD ret; - struct sockaddr_in6 *sockaddr; - char ip[NI_MAXHOST]; - unsigned int i; - int found = 0; - - for (i = 0; i < 5; i++) - { - ret = GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, - NULL, addrs, &size); - if ((ret == NO_ERROR) || (ret == ERROR_NO_DATA)) { - break; - } else if (ret == ERROR_BUFFER_OVERFLOW) - { - if (addrs) - free(addrs); - addrs = (IP_ADAPTER_ADDRESSES *)malloc(size); - } else - { - if (addrs) - { - free(addrs); - addrs = NULL; - } - lwsl_err("Failed to get IPv6 address table (%d)", ret); - break; - } - } - - if ((ret == NO_ERROR) && (addrs)) { - adapter = addrs; - while (adapter && !found) { - addr = adapter->FirstUnicastAddress; - while (addr && !found) { - if (addr->Address.lpSockaddr->sa_family == - AF_INET6) { - sockaddr = (struct sockaddr_in6 *) - (addr->Address.lpSockaddr); - - lws_plat_inet_ntop(sockaddr->sin6_family, - &sockaddr->sin6_addr, - ip, sizeof(ip)); - - if (!strcmp(ip, ipaddr)) { - scope = sockaddr->sin6_scope_id; - found = 1; - break; - } - } - addr = addr->Next; - } - adapter = adapter->Next; - } - } - if (addrs) - free(addrs); -#endif - - return scope; -} -#endif - -/* - * https://en.wikipedia.org/wiki/IPv6_address - * - * An IPv6 address is represented as eight groups of four hexadecimal digits, - * each group representing 16 bits (two octets, a group sometimes also called a - * hextet[6][7]). The groups are separated by colons (:). An example of an IPv6 - * address is: - * - * 2001:0db8:85a3:0000:0000:8a2e:0370:7334 - * - * The hexadecimal digits are case-insensitive, but IETF recommendations suggest - * the use of lower case letters. The full representation of eight 4-digit - * groups may be simplified by several techniques, eliminating parts of the - * representation. - * - * Leading zeroes in a group may be omitted, but each group must retain at least - * one hexadecimal digit.[1] Thus, the example address may be written as: - * - * 2001:db8:85a3:0:0:8a2e:370:7334 - * - * One or more consecutive groups containing zeros only may be replaced with a - * single empty group, using two consecutive colons (::).[1] The substitution - * may only be applied once in the address, however, because multiple - * occurrences would create an ambiguous representation. Thus, the example - * address can be further simplified: - * - * 2001:db8:85a3::8a2e:370:7334 - * - * The localhost (loopback) address, 0:0:0:0:0:0:0:1, and the IPv6 unspecified - * address, 0:0:0:0:0:0:0:0, are reduced to ::1 and ::, respectively. - * - * During the transition of the Internet from IPv4 to IPv6, it is typical to - * operate in a mixed addressing environment. For such use cases, a special - * notation has been introduced, which expresses IPv4-mapped and IPv4-compatible - * IPv6 addresses by writing the least-significant 32 bits of an address in the - * familiar IPv4 dot-decimal notation, whereas the other 96 (most significant) - * bits are written in IPv6 format. For example, the IPv4-mapped IPv6 address - * ::ffff:c000:0280 is written as ::ffff:192.0.2.128, thus expressing clearly - * the original IPv4 address that was mapped to IPv6. - */ - -int -lws_parse_numeric_address(const char *ads, uint8_t *result, size_t max_len) -{ - struct lws_tokenize ts; - uint8_t *orig = result, temp[16]; - int sects = 0, ipv6 = !!strchr(ads, ':'), skip_point = -1, dm = 0; - char t[5]; - size_t n; - long u; - - lws_tokenize_init(&ts, ads, LWS_TOKENIZE_F_NO_INTEGERS | - LWS_TOKENIZE_F_MINUS_NONTERM); - ts.len = strlen(ads); - if (!ipv6 && ts.len < 7) - return -1; - - if (ipv6 && ts.len < 2) - return -2; - - if (!ipv6 && max_len < 4) - return -3; - - if (ipv6 && max_len < 16) - return -4; - - if (ipv6) - memset(result, 0, max_len); - - do { - ts.e = lws_tokenize(&ts); - switch (ts.e) { - case LWS_TOKZE_TOKEN: - dm = 0; - if (ipv6) { - if (ts.token_len > 4) - return -1; - memcpy(t, ts.token, ts.token_len); - t[ts.token_len] = '\0'; - for (n = 0; n < ts.token_len; n++) - if (t[n] < '0' || t[n] > 'f' || - (t[n] > '9' && t[n] < 'A') || - (t[n] > 'F' && t[n] < 'a')) - return -1; - u = strtol(t, NULL, 16); - if (u > 0xffff) - return -5; - *result++ = (uint8_t)(u >> 8); - } else { - if (ts.token_len > 3) - return -1; - memcpy(t, ts.token, ts.token_len); - t[ts.token_len] = '\0'; - for (n = 0; n < ts.token_len; n++) - if (t[n] < '0' || t[n] > '9') - return -1; - u = strtol(t, NULL, 10); - if (u > 0xff) - return -6; - } - if (u < 0) - return -7; - *result++ = (uint8_t)u; - sects++; - break; - - case LWS_TOKZE_DELIMITER: - if (dm++) { - if (dm > 2) - return -8; - if (*ts.token != ':') - return -9; - /* back to back : */ - *result++ = 0; - *result++ = 0; - skip_point = lws_ptr_diff(result, orig); - break; - } - if (ipv6 && orig[2] == 0xff && orig[3] == 0xff && - skip_point == 2) { - /* ipv4 backwards compatible format */ - ipv6 = 0; - memset(orig, 0, max_len); - orig[10] = 0xff; - orig[11] = 0xff; - skip_point = -1; - result = &orig[12]; - sects = 0; - break; - } - if (ipv6 && *ts.token != ':') - return -10; - if (!ipv6 && *ts.token != '.') - return -11; - break; - - case LWS_TOKZE_ENDED: - if (!ipv6 && sects == 4) - return lws_ptr_diff(result, orig); - if (ipv6 && sects == 8) - return lws_ptr_diff(result, orig); - if (skip_point != -1) { - int ow = lws_ptr_diff(result, orig); - /* - * contains ...::... - */ - if (ow == 16) - return 16; - memcpy(temp, &orig[skip_point], ow - skip_point); - memset(&orig[skip_point], 0, 16 - skip_point); - memcpy(&orig[16 - (ow - skip_point)], temp, - ow - skip_point); - - return 16; - } - return -12; - - default: /* includes ENDED */ - lwsl_err("%s: malformed ip address\n", - __func__); - - return -13; - } - } while (ts.e > 0 && result - orig <= (int)max_len); - - lwsl_err("%s: ended on e %d\n", __func__, ts.e); - - return -14; -} - -int -lws_sa46_parse_numeric_address(const char *ads, lws_sockaddr46 *sa46) -{ - uint8_t a[16]; - int n; - - n = lws_parse_numeric_address(ads, a, sizeof(a)); - if (n < 0) - return -1; - -#if defined(LWS_WITH_IPV6) - if (n == 16) { - sa46->sa6.sin6_family = AF_INET6; - memcpy(sa46->sa6.sin6_addr.s6_addr, a, - sizeof(sa46->sa6.sin6_addr.s6_addr)); - - return 0; - } -#endif - - if (n != 4) - return -1; - - sa46->sa4.sin_family = AF_INET; - memcpy(&sa46->sa4.sin_addr.s_addr, a, - sizeof(sa46->sa4.sin_addr.s_addr)); - - return 0; -} - -int -lws_write_numeric_address(const uint8_t *ads, int size, char *buf, size_t len) -{ - char c, elided = 0, soe = 0, zb = -1, n, ipv4 = 0; - const char *e = buf + len; - char *obuf = buf; - int q = 0; - - if (size == 4) - return lws_snprintf(buf, len, "%u.%u.%u.%u", - ads[0], ads[1], ads[2], ads[3]); - - if (size != 16) - return -1; - - for (c = 0; c < (char)size / 2; c++) { - uint16_t v = (ads[q] << 8) | ads[q + 1]; - - if (buf + 8 > e) - return -1; - - q += 2; - if (soe) { - if (v) - *buf++ = ':'; - /* fall thru to print hex value */ - } else - if (!elided && !soe && !v) { - elided = soe = 1; - zb = c; - continue; - } - - if (ipv4) { - n = lws_snprintf(buf, e - buf, "%u.%u", - ads[q - 2], ads[q - 1]); - buf += n; - if (c == 6) - *buf++ = '.'; - } else { - if (soe && !v) - continue; - if (c) - *buf++ = ':'; - - buf += lws_snprintf(buf, e - buf, "%x", v); - - if (soe && v) { - soe = 0; - if (c == 5 && v == 0xffff && !zb) { - ipv4 = 1; - *buf++ = ':'; - } - } - } - } - if (buf + 3 > e) - return -1; - - if (soe) { /* as is the case for all zeros */ - *buf++ = ':'; - *buf++ = ':'; - *buf = '\0'; - } - - return lws_ptr_diff(buf, obuf); -} - -int -lws_sa46_write_numeric_address(lws_sockaddr46 *sa46, char *buf, size_t len) -{ - *buf = '\0'; -#if defined(LWS_WITH_IPV6) - if (sa46->sa4.sin_family == AF_INET6) - return lws_write_numeric_address( - (uint8_t *)&sa46->sa6.sin6_addr, 16, buf, len); -#endif - if (sa46->sa4.sin_family == AF_INET) - return lws_write_numeric_address( - (uint8_t *)&sa46->sa4.sin_addr, 4, buf, len); - - return -1; -} - -int -lws_sa46_compare_ads(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46b) -{ - if (sa46a->sa4.sin_family != sa46b->sa4.sin_family) - return 1; - -#if defined(LWS_WITH_IPV6) - if (sa46a->sa4.sin_family == AF_INET6) - return memcmp(&sa46a->sa6.sin6_addr, &sa46b->sa6.sin6_addr, 16); -#endif - - return sa46a->sa4.sin_addr.s_addr != sa46b->sa4.sin_addr.s_addr; -} - -lws_state_manager_t * -lws_system_get_state_manager(struct lws_context *context) -{ - return &context->mgr_system; -} diff -Nru libwebsockets-4.0.20/lib/core-net/output.c libwebsockets-2.4.2/lib/core-net/output.c --- libwebsockets-4.0.20/lib/core-net/output.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/output.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,405 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -/* - * notice this returns number of bytes consumed, or -1 - */ -int -lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len) -{ - struct lws_context *context = lws_get_context(wsi); - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - size_t real_len = len; - unsigned int n, m; - - // lwsl_notice("%s: len %d\n", __func__, (int)len); - // lwsl_hexdump_level(LLL_NOTICE, buf, len); - - /* - * Detect if we got called twice without going through the - * event loop to handle pending. Since that guarantees extending any - * existing buflist_out it's inefficient. - */ - if (0 && buf && wsi->could_have_pending) { - lwsl_hexdump_level(LLL_INFO, buf, len); - lwsl_info("** %p: vh: %s, prot: %s, role %s: " - "Inefficient back-to-back write of %lu detected...\n", - wsi, wsi->vhost ? wsi->vhost->name : "no vhost", - wsi->protocol->name, wsi->role_ops->name, - (unsigned long)len); - } - - lws_stats_bump(pt, LWSSTATS_C_API_WRITE, 1); - - /* just ignore sends after we cleared the truncation buffer */ - if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE && - !lws_has_buffered_out(wsi) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - && !wsi->http.comp_ctx.may_have_more -#endif - ) - return (int)len; - - if (buf && lws_has_buffered_out(wsi)) { - lwsl_info("** %p: vh: %s, prot: %s, incr buflist_out by %lu\n", - wsi, wsi->vhost ? wsi->vhost->name : "no vhost", - wsi->protocol->name, (unsigned long)len); - - /* - * already buflist ahead of this, add it on the tail of the - * buflist, then ignore it for now and act like we're flushing - * the buflist... - */ - - if (lws_buflist_append_segment(&wsi->buflist_out, buf, len)) - return -1; - - buf = NULL; - len = 0; - } - - if (wsi->buflist_out) { - /* we have to drain the earliest buflist_out stuff first */ - - len = lws_buflist_next_segment_len(&wsi->buflist_out, &buf); - real_len = len; - - lwsl_debug("%s: draining %d\n", __func__, (int)len); - } - - if (!len || !buf) - return 0; - - if (!wsi->mux_substream && !lws_socket_is_valid(wsi->desc.sockfd)) - lwsl_err("%s: invalid sock %p\n", __func__, wsi); - - /* limit sending */ - if (wsi->protocol->tx_packet_size) - n = (int)wsi->protocol->tx_packet_size; - else { - n = (int)wsi->protocol->rx_buffer_size; - if (!n) - n = context->pt_serv_buf_size; - } - n += LWS_PRE + 4; - if (n > len) - n = (int)len; - - /* nope, send it on the socket directly */ - - m = lws_ssl_capable_write(wsi, buf, n); - lwsl_info("%s: ssl_capable_write (%d) says %d\n", __func__, n, m); - - /* something got written, it can have been truncated now */ - wsi->could_have_pending = 1; - - switch (m) { - case LWS_SSL_CAPABLE_ERROR: - /* we're going to close, let close know sends aren't possible */ - wsi->socket_is_permanently_unusable = 1; - return -1; - case LWS_SSL_CAPABLE_MORE_SERVICE: - /* - * nothing got sent, not fatal. Retry the whole thing later, - * ie, implying treat it was a truncated send so it gets - * retried - */ - m = 0; - break; - } - - if ((int)m < 0) - m = 0; - - /* - * we were sending this from buflist_out? Then not sending everything - * is a small matter of advancing ourselves only by the amount we did - * send in the buflist. - */ - if (lws_has_buffered_out(wsi)) { - if (m) { - lwsl_info("%p partial adv %d (vs %ld)\n", wsi, m, - (long)real_len); - lws_buflist_use_segment(&wsi->buflist_out, m); - } - - if (!lws_has_buffered_out(wsi)) { - lwsl_info("%s: wsi %p: buflist_out flushed\n", - __func__, wsi); - - m = (int)real_len; - if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) { - lwsl_info("*%p signalling to close now\n", wsi); - return -1; /* retry closing now */ - } - - if (wsi->close_when_buffered_out_drained) { - wsi->close_when_buffered_out_drained = 0; - return -1; - } - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) -#if defined(LWS_WITH_SERVER) - if (wsi->http.deferred_transaction_completed) { - lwsl_notice("%s: partial completed, doing " - "deferred transaction completed\n", - __func__); - wsi->http.deferred_transaction_completed = 0; - return lws_http_transaction_completed(wsi) ? - -1 : (int)real_len; - } -#endif -#endif -#if defined(LWS_ROLE_WS) - /* Since buflist_out flushed, we're not inside a frame any more */ - if (wsi->ws) - wsi->ws->inside_frame = 0; -#endif - } - /* always callback on writeable */ - lws_callback_on_writable(wsi); - - return m; - } - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - if (wsi->http.comp_ctx.may_have_more) - lws_callback_on_writable(wsi); -#endif - - if (m == real_len) - /* what we just sent went out cleanly */ - return m; - - /* - * We were not able to send everything... and we were not sending from - * an existing buflist_out. So we are starting a fresh buflist_out, by - * buffering the unsent remainder on it. - * (it will get first priority next time the socket is writable). - */ - lwsl_debug("%p new partial sent %d from %lu total\n", wsi, m, - (unsigned long)real_len); - - if (lws_buflist_append_segment(&wsi->buflist_out, buf + m, - real_len - m) < 0) - return -1; - - lws_stats_bump(pt, LWSSTATS_C_WRITE_PARTIALS, 1); - lws_stats_bump(pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, m); - -#if defined(LWS_WITH_UDP) - if (lws_wsi_is_udp(wsi)) { - /* stash original destination for fulfilling UDP partials */ - wsi->udp->sa_pending = wsi->udp->sa; - wsi->udp->salen_pending = wsi->udp->salen; - } -#endif - - /* since something buffered, force it to get another chance to send */ - lws_callback_on_writable(wsi); - - return (int)real_len; -} - -int -lws_write(struct lws *wsi, unsigned char *buf, size_t len, - enum lws_write_protocol wp) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; -#if defined(LWS_WITH_DETAILED_LATENCY) - lws_usec_t us; -#endif - int m; - - lws_stats_bump(pt, LWSSTATS_C_API_LWS_WRITE, 1); - - if ((int)len < 0) { - lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__, - (int)len, (unsigned long)len); - return -1; - } - - lws_stats_bump(pt, LWSSTATS_B_WRITE, len); - -#ifdef LWS_WITH_ACCESS_LOG - wsi->http.access_log.sent += len; -#endif -#if defined(LWS_WITH_SERVER_STATUS) - if (wsi->vhost) - wsi->vhost->conn_stats.tx += len; -#endif -#if defined(LWS_WITH_DETAILED_LATENCY) - us = lws_now_usecs(); -#endif - - assert(wsi->role_ops); - if (!wsi->role_ops->write_role_protocol) - return lws_issue_raw(wsi, buf, len); - - m = wsi->role_ops->write_role_protocol(wsi, buf, len, &wp); - if (m < 0) - return m; - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (wsi->context->detailed_latency_cb) { - wsi->detlat.req_size = len; - wsi->detlat.acc_size = m; - wsi->detlat.type = LDLT_WRITE; - if (wsi->detlat.earliest_write_req_pre_write) - wsi->detlat.latencies[LAT_DUR_PROXY_PROXY_REQ_TO_WRITE] = - us - wsi->detlat.earliest_write_req_pre_write; - else - wsi->detlat.latencies[LAT_DUR_PROXY_PROXY_REQ_TO_WRITE] = 0; - wsi->detlat.latencies[LAT_DUR_USERCB] = lws_now_usecs() - us; - lws_det_lat_cb(wsi->context, &wsi->detlat); - - } -#endif - - return m; -} - -int -lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - int n = 0; - - lws_stats_bump(pt, LWSSTATS_C_API_READ, 1); - - errno = 0; -#if defined(LWS_WITH_UDP) - if (lws_wsi_is_udp(wsi)) { - wsi->udp->salen = sizeof(wsi->udp->sa); - n = recvfrom(wsi->desc.sockfd, (char *)buf, len, 0, - &wsi->udp->sa, &wsi->udp->salen); - } else -#endif - n = recv(wsi->desc.sockfd, (char *)buf, len, 0); - - if (n >= 0) { - - if (!n && wsi->unix_skt) - return LWS_SSL_CAPABLE_ERROR; - - /* - * See https://libwebsockets.org/ - * pipermail/libwebsockets/2019-March/007857.html - */ - if (!n) - return LWS_SSL_CAPABLE_ERROR; - -#if defined(LWS_WITH_SERVER_STATUS) - if (wsi->vhost) - wsi->vhost->conn_stats.rx += n; -#endif - lws_stats_bump(pt, LWSSTATS_B_READ, n); - - return n; - } - - if (LWS_ERRNO == LWS_EAGAIN || - LWS_ERRNO == LWS_EWOULDBLOCK || - LWS_ERRNO == LWS_EINTR) - return LWS_SSL_CAPABLE_MORE_SERVICE; - - lwsl_info("error on reading from skt : %d\n", LWS_ERRNO); - return LWS_SSL_CAPABLE_ERROR; -} - -int -lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len) -{ - int n = 0; -#if defined(LWS_PLAT_OPTEE) - ssize_t send(int sockfd, const void *buf, size_t len, int flags); -#endif - -#if defined(LWS_WITH_UDP) - if (lws_wsi_is_udp(wsi)) { - if (wsi->context->udp_loss_sim_tx_pc) { - uint16_t u16; - /* - * We should randomly drop some of these - */ - - if (lws_get_random(wsi->context, &u16, 2) == 2 && - ((u16 * 100) / 0xffff) <= - wsi->context->udp_loss_sim_tx_pc) { - lwsl_warn("%s: dropping udp tx\n", __func__); - /* pretend it was sent */ - n = len; - goto post_send; - } - } - if (lws_has_buffered_out(wsi)) - n = sendto(wsi->desc.sockfd, (const char *)buf, - len, 0, &wsi->udp->sa_pending, - wsi->udp->salen_pending); - else - n = sendto(wsi->desc.sockfd, (const char *)buf, - len, 0, &wsi->udp->sa, wsi->udp->salen); - } else -#endif - if (wsi->role_ops->file_handle) - n = write((int)(lws_intptr_t)wsi->desc.filefd, buf, len); - else - n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL); -// lwsl_info("%s: sent len %d result %d", __func__, len, n); - -#if defined(LWS_WITH_UDP) -post_send: -#endif - if (n >= 0) - return n; - - if (LWS_ERRNO == LWS_EAGAIN || - LWS_ERRNO == LWS_EWOULDBLOCK || - LWS_ERRNO == LWS_EINTR) { - if (LWS_ERRNO == LWS_EWOULDBLOCK) { - lws_set_blocking_send(wsi); - } - - return LWS_SSL_CAPABLE_MORE_SERVICE; - } - - lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n", - len, wsi->desc.sockfd, n, LWS_ERRNO); - - return LWS_SSL_CAPABLE_ERROR; -} - -int -lws_ssl_pending_no_ssl(struct lws *wsi) -{ - (void)wsi; -#if defined(LWS_PLAT_FREERTOS) - return 100; -#else - return 0; -#endif -} diff -Nru libwebsockets-4.0.20/lib/core-net/pollfd.c libwebsockets-2.4.2/lib/core-net/pollfd.c --- libwebsockets-4.0.20/lib/core-net/pollfd.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/pollfd.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,656 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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" - -int -_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa) -{ -#if !defined(LWS_WITH_LIBUV) && !defined(LWS_WITH_LIBEV) && \ - !defined(LWS_WITH_LIBEVENT) && !defined(LWS_WITH_GLIB) - volatile struct lws_context_per_thread *vpt; -#endif - struct lws_context_per_thread *pt; - struct lws_context *context; - int ret = 0, pa_events; - struct lws_pollfd *pfd; - int sampled_tid, tid; - - if (!wsi) - return 0; - - assert(wsi->position_in_fds_table == LWS_NO_FDS_POS || - wsi->position_in_fds_table >= 0); - - if (wsi->position_in_fds_table == LWS_NO_FDS_POS) - return 0; - - if (((volatile struct lws *)wsi)->handling_pollout && - !_and && _or == LWS_POLLOUT) { - /* - * Happening alongside service thread handling POLLOUT. - * The danger is when he is finished, he will disable POLLOUT, - * countermanding what we changed here. - * - * Instead of changing the fds, inform the service thread - * what happened, and ask it to leave POLLOUT active on exit - */ - ((volatile struct lws *)wsi)->leave_pollout_active = 1; - /* - * by definition service thread is not in poll wait, so no need - * to cancel service - */ - - lwsl_debug("%s: using leave_pollout_active\n", __func__); - - return 0; - } - - context = wsi->context; - pt = &context->pt[(int)wsi->tsi]; - - assert(wsi->position_in_fds_table < (int)pt->fds_count); - -#if !defined(LWS_WITH_LIBUV) && !defined(LWS_WITH_LIBEV) && \ - !defined(LWS_WITH_LIBEVENT) && !defined(LWS_WITH_GLIB) - /* - * This only applies when we use the default poll() event loop. - * - * BSD can revert pa->events at any time, when the kernel decides to - * exit from poll(). We can't protect against it using locking. - * - * Therefore we must check first if the service thread is in poll() - * wait; if so, we know we must be being called from a foreign thread, - * and we must keep a strictly ordered list of changes we made instead - * of trying to apply them, since when poll() exits, which may happen - * at any time it would revert our changes. - * - * The plat code will apply them when it leaves the poll() wait - * before doing anything else. - */ - - vpt = (volatile struct lws_context_per_thread *)pt; - - vpt->foreign_spinlock = 1; - lws_memory_barrier(); - - if (vpt->inside_poll) { - struct lws_foreign_thread_pollfd *ftp, **ftp1; - /* - * We are certainly a foreign thread trying to change events - * while the service thread is in the poll() wait. - * - * Create a list of changes to be applied after poll() exit, - * instead of trying to apply them now. - */ - ftp = lws_malloc(sizeof(*ftp), "ftp"); - if (!ftp) { - vpt->foreign_spinlock = 0; - lws_memory_barrier(); - ret = -1; - goto bail; - } - - ftp->_and = _and; - ftp->_or = _or; - ftp->fd_index = wsi->position_in_fds_table; - ftp->next = NULL; - - lws_pt_lock(pt, __func__); - - /* place at END of list to maintain order */ - ftp1 = (struct lws_foreign_thread_pollfd **) - &vpt->foreign_pfd_list; - while (*ftp1) - ftp1 = &((*ftp1)->next); - - *ftp1 = ftp; - vpt->foreign_spinlock = 0; - lws_memory_barrier(); - - lws_pt_unlock(pt); - - lws_cancel_service_pt(wsi); - - return 0; - } - - vpt->foreign_spinlock = 0; - lws_memory_barrier(); -#endif - -#if !defined(__linux__) - /* OSX couldn't see close on stdin pipe side otherwise */ - _or |= LWS_POLLHUP; -#endif - - pfd = &pt->fds[wsi->position_in_fds_table]; - pa->fd = wsi->desc.sockfd; - lwsl_debug("%s: wsi %p: fd %d events %d -> %d\n", __func__, wsi, - pa->fd, pfd->events, (pfd->events & ~_and) | _or); - pa->prev_events = pfd->events; - pa->events = pfd->events = (pfd->events & ~_and) | _or; - - if (wsi->mux_substream) - return 0; - -#if defined(LWS_WITH_EXTERNAL_POLL) - - if (wsi->vhost && - wsi->vhost->protocols[0].callback(wsi, - LWS_CALLBACK_CHANGE_MODE_POLL_FD, - wsi->user_space, (void *)pa, 0)) { - ret = -1; - goto bail; - } -#endif - - if (context->event_loop_ops->io) { - if (_and & LWS_POLLIN) - context->event_loop_ops->io(wsi, - LWS_EV_STOP | LWS_EV_READ); - - if (_or & LWS_POLLIN) - context->event_loop_ops->io(wsi, - LWS_EV_START | LWS_EV_READ); - - if (_and & LWS_POLLOUT) - context->event_loop_ops->io(wsi, - LWS_EV_STOP | LWS_EV_WRITE); - - if (_or & LWS_POLLOUT) - context->event_loop_ops->io(wsi, - LWS_EV_START | LWS_EV_WRITE); - } - - /* - * if we changed something in this pollfd... - * ... and we're running in a different thread context - * than the service thread... - * ... and the service thread is waiting ... - * then cancel it to force a restart with our changed events - */ - pa_events = pa->prev_events != pa->events; - - if (pa_events) { - if (lws_plat_change_pollfd(context, wsi, pfd)) { - lwsl_info("%s failed\n", __func__); - ret = -1; - goto bail; - } - sampled_tid = pt->service_tid; - if (sampled_tid && wsi->vhost) { - tid = wsi->vhost->protocols[0].callback(wsi, - LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); - if (tid == -1) { - ret = -1; - goto bail; - } - if (tid != sampled_tid) - lws_cancel_service_pt(wsi); - } - } - -bail: - return ret; -} - -#if defined(LWS_WITH_SERVER) -/* - * Enable or disable listen sockets on this pt globally... - * it's modulated according to the pt having space for a new accept. - */ -static void -lws_accept_modulation(struct lws_context *context, - struct lws_context_per_thread *pt, int allow) -{ - struct lws_vhost *vh = context->vhost_list; - struct lws_pollargs pa1; - - while (vh) { - if (vh->lserv_wsi) { - if (allow) - _lws_change_pollfd(vh->lserv_wsi, - 0, LWS_POLLIN, &pa1); - else - _lws_change_pollfd(vh->lserv_wsi, - LWS_POLLIN, 0, &pa1); - } - vh = vh->vhost_next; - } -} -#endif - -#if _LWS_ENABLED_LOGS & LLL_WARN -void -__dump_fds(struct lws_context_per_thread *pt, const char *s) -{ - unsigned int n; - - lwsl_warn("%s: fds_count %u, %s\n", __func__, pt->fds_count, s); - - for (n = 0; n < pt->fds_count; n++) { - struct lws *wsi = wsi_from_fd(pt->context, pt->fds[n].fd); - - lwsl_warn(" %d: fd %d, wsi %p, pos_in_fds: %d\n", - n + 1, pt->fds[n].fd, wsi, - wsi ? wsi->position_in_fds_table : -1); - } -} -#else -#define __dump_fds(x, y) -#endif - -int -__insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi) -{ -#if defined(LWS_WITH_EXTERNAL_POLL) - struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 }; -#endif - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - int ret = 0; - -// __dump_fds(pt, "pre insert"); - - lwsl_debug("%s: %p: tsi=%d, sock=%d, pos-in-fds=%d\n", - __func__, wsi, wsi->tsi, wsi->desc.sockfd, pt->fds_count); - - if ((unsigned int)pt->fds_count >= context->fd_limit_per_thread) { - lwsl_err("Too many fds (%d vs %d)\n", context->max_fds, - context->fd_limit_per_thread ); - return 1; - } - -#if !defined(_WIN32) - if (!wsi->context->max_fds_unrelated_to_ulimit && - wsi->desc.sockfd - lws_plat_socket_offset() >= context->max_fds) { - lwsl_err("Socket fd %d is too high (%d) offset %d\n", - wsi->desc.sockfd, context->max_fds, - lws_plat_socket_offset()); - return 1; - } -#endif - - assert(wsi); - assert(wsi->event_pipe || wsi->vhost); - assert(lws_socket_is_valid(wsi->desc.sockfd)); - -#if defined(LWS_WITH_EXTERNAL_POLL) - - if (wsi->vhost && - wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, - wsi->user_space, (void *) &pa, 1)) - return -1; -#endif - - if (insert_wsi(context, wsi)) - return -1; - pt->count_conns++; - wsi->position_in_fds_table = pt->fds_count; - - pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd; - pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN; -#if defined(LWS_WITH_EXTERNAL_POLL) - pa.events = pt->fds[pt->fds_count].events; -#endif - - lws_plat_insert_socket_into_fds(context, wsi); - -#if defined(LWS_WITH_EXTERNAL_POLL) - - /* external POLL support via protocol 0 */ - if (wsi->vhost && - wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD, - wsi->user_space, (void *) &pa, 0)) - ret = -1; -#endif -#if defined(LWS_WITH_SERVER) - /* if no more room, defeat accepts on this service thread */ - if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1) - lws_accept_modulation(context, pt, 0); -#endif - -#if defined(LWS_WITH_EXTERNAL_POLL) - if (wsi->vhost && - wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, - wsi->user_space, (void *)&pa, 1)) - ret = -1; -#endif - -// __dump_fds(pt, "post insert"); - - return ret; -} - -int -__remove_wsi_socket_from_fds(struct lws *wsi) -{ - struct lws_context *context = wsi->context; -#if defined(LWS_WITH_EXTERNAL_POLL) - struct lws_pollargs pa = { wsi->desc.sockfd, 0, 0 }; -#endif - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - struct lws *end_wsi; - int v, m, ret = 0; - -// __dump_fds(pt, "pre remove"); - -#if !defined(_WIN32) - if (!wsi->context->max_fds_unrelated_to_ulimit && - wsi->desc.sockfd - lws_plat_socket_offset() > context->max_fds) { - lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd, - context->max_fds); - - return 1; - } -#endif -#if defined(LWS_WITH_EXTERNAL_POLL) - if (wsi->vhost && wsi->vhost->protocols && - wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, - wsi->user_space, (void *)&pa, 1)) - return -1; -#endif - - lws_same_vh_protocol_remove(wsi); - - /* the guy who is to be deleted's slot index in pt->fds */ - m = wsi->position_in_fds_table; - - /* these are the only valid possibilities for position_in_fds_table */ - assert(m == LWS_NO_FDS_POS || (m >= 0 && - (unsigned int)m < pt->fds_count)); - - if (context->event_loop_ops->io) - context->event_loop_ops->io(wsi, - LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | - LWS_EV_PREPARE_DELETION); -/* - lwsl_notice("%s: wsi=%p, skt=%d, fds pos=%d, end guy pos=%d, endfd=%d\n", - __func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table, - pt->fds_count, pt->fds[pt->fds_count - 1].fd); */ - - if (m != LWS_NO_FDS_POS) { - char fixup = 0; - - assert(pt->fds_count && (unsigned int)m != pt->fds_count); - - /* deletion guy's lws_lookup entry needs nuking */ - delete_from_fd(context, wsi->desc.sockfd); - - if ((unsigned int)m != pt->fds_count - 1) { - /* have the last guy take up the now vacant slot */ - pt->fds[m] = pt->fds[pt->fds_count - 1]; - fixup = 1; - } - - pt->fds[pt->fds_count - 1].fd = -1; - - /* this decrements pt->fds_count */ - lws_plat_delete_socket_from_fds(context, wsi, m); - pt->count_conns--; - if (fixup) { - v = (int) pt->fds[m].fd; - /* old end guy's "position in fds table" is now the - * deletion guy's old one */ - end_wsi = wsi_from_fd(context, v); - if (!end_wsi) { - lwsl_err("no wsi for fd %d pos %d, " - "pt->fds_count=%d\n", - (int)pt->fds[m].fd, m, pt->fds_count); - assert(0); - } else - end_wsi->position_in_fds_table = m; - } - - /* removed wsi has no position any more */ - wsi->position_in_fds_table = LWS_NO_FDS_POS; - } - -#if defined(LWS_WITH_EXTERNAL_POLL) - /* remove also from external POLL support via protocol 0 */ - if (lws_socket_is_valid(wsi->desc.sockfd) && wsi->vhost && - wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD, - wsi->user_space, (void *) &pa, 0)) - ret = -1; -#endif - -#if defined(LWS_WITH_SERVER) - if (!context->being_destroyed && - /* if this made some room, accept connects on this thread */ - (unsigned int)pt->fds_count < context->fd_limit_per_thread - 1) - lws_accept_modulation(context, pt, 1); -#endif - -#if defined(LWS_WITH_EXTERNAL_POLL) - if (wsi->vhost && - wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, - wsi->user_space, (void *) &pa, 1)) - ret = -1; -#endif - -// __dump_fds(pt, "post remove"); - - return ret; -} - -int -__lws_change_pollfd(struct lws *wsi, int _and, int _or) -{ - struct lws_context *context; - struct lws_pollargs pa; - int ret = 0; - - if (!wsi || (!wsi->protocol && !wsi->event_pipe) || - wsi->position_in_fds_table == LWS_NO_FDS_POS) - return 0; - - context = lws_get_context(wsi); - if (!context) - return 1; - -#if defined(LWS_WITH_EXTERNAL_POLL) - if (wsi->vhost && - wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, - wsi->user_space, (void *) &pa, 0)) - return -1; -#endif - - ret = _lws_change_pollfd(wsi, _and, _or, &pa); - -#if defined(LWS_WITH_EXTERNAL_POLL) - if (wsi->vhost && - wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, - wsi->user_space, (void *) &pa, 0)) - ret = -1; -#endif - - return ret; -} - -int -lws_change_pollfd(struct lws *wsi, int _and, int _or) -{ - struct lws_context_per_thread *pt; - int ret = 0; - - pt = &wsi->context->pt[(int)wsi->tsi]; - - lws_pt_lock(pt, __func__); - ret = __lws_change_pollfd(wsi, _and, _or); - lws_pt_unlock(pt); - - return ret; -} - -int -lws_callback_on_writable(struct lws *wsi) -{ - struct lws_context_per_thread *pt; - struct lws *w = wsi; - - if (lwsi_state(wsi) == LRS_SHUTDOWN) - return 0; - - if (wsi->socket_is_permanently_unusable) - return 0; - - pt = &wsi->context->pt[(int)wsi->tsi]; - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (!wsi->detlat.earliest_write_req) - wsi->detlat.earliest_write_req = lws_now_usecs(); -#endif - - lws_stats_bump(pt, LWSSTATS_C_WRITEABLE_CB_REQ, 1); -#if defined(LWS_WITH_STATS) - if (!wsi->active_writable_req_us) { - wsi->active_writable_req_us = lws_now_usecs(); - lws_stats_bump(pt, LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1); - } -#endif - - if (wsi->role_ops->callback_on_writable) { - int q = wsi->role_ops->callback_on_writable(wsi); - //lwsl_notice("%s: rops_cow says %d\n", __func__, q); - if (q) - return 1; - w = lws_get_network_wsi(wsi); - } else - - if (w->position_in_fds_table == LWS_NO_FDS_POS) { - lwsl_debug("%s: failed to find socket %d\n", __func__, - wsi->desc.sockfd); - return -1; - } - - //lwsl_notice("%s: marking for POLLOUT %p (wsi %p)\n", __func__, w, wsi); - - if (__lws_change_pollfd(w, 0, LWS_POLLOUT)) - return -1; - - return 1; -} - - -/* - * stitch protocol choice into the vh protocol linked list - * We always insert ourselves at the start of the list - * - * X <-> B - * X <-> pAn <-> pB - * - * Illegal to attach more than once without detach inbetween - */ -void -lws_same_vh_protocol_insert(struct lws *wsi, int n) -{ - lws_vhost_lock(wsi->vhost); - - lws_dll2_remove(&wsi->same_vh_protocol); - lws_dll2_add_head(&wsi->same_vh_protocol, - &wsi->vhost->same_vh_protocol_owner[n]); - - wsi->bound_vhost_index = n; - - lws_vhost_unlock(wsi->vhost); -} - -void -__lws_same_vh_protocol_remove(struct lws *wsi) -{ - if (wsi->vhost && wsi->vhost->same_vh_protocol_owner) - lws_dll2_remove(&wsi->same_vh_protocol); -} - -void -lws_same_vh_protocol_remove(struct lws *wsi) -{ - if (!wsi->vhost) - return; - - lws_vhost_lock(wsi->vhost); - - __lws_same_vh_protocol_remove(wsi); - - lws_vhost_unlock(wsi->vhost); -} - - -int -lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost, - const struct lws_protocols *protocol) -{ - struct lws *wsi; - int n; - - if (protocol < vhost->protocols || - protocol >= (vhost->protocols + vhost->count_protocols)) { - lwsl_err("%s: protocol %p is not from vhost %p (%p - %p)\n", - __func__, protocol, vhost->protocols, vhost, - (vhost->protocols + vhost->count_protocols)); - - return -1; - } - - n = (int)(protocol - vhost->protocols); - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - lws_dll2_get_head(&vhost->same_vh_protocol_owner[n])) { - wsi = lws_container_of(d, struct lws, same_vh_protocol); - - assert(wsi->protocol == protocol); - lws_callback_on_writable(wsi); - - } lws_end_foreach_dll_safe(d, d1); - - return 0; -} - -int -lws_callback_on_writable_all_protocol(const struct lws_context *context, - const struct lws_protocols *protocol) -{ - struct lws_vhost *vhost; - int n; - - if (!context) - return 0; - - vhost = context->vhost_list; - - while (vhost) { - for (n = 0; n < vhost->count_protocols; n++) - if (protocol->callback == - vhost->protocols[n].callback && - !strcmp(protocol->name, vhost->protocols[n].name)) - break; - if (n != vhost->count_protocols) - lws_callback_on_writable_all_protocol_vhost( - vhost, &vhost->protocols[n]); - - vhost = vhost->vhost_next; - } - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/core-net/private-lib-core-net.h libwebsockets-2.4.2/lib/core-net/private-lib-core-net.h --- libwebsockets-4.0.20/lib/core-net/private-lib-core-net.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/private-lib-core-net.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,1425 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_CORE_NET_PRIVATE_H__) -#define __LWS_CORE_NET_PRIVATE_H__ - -#if !defined(_POSIX_C_SOURCE) -#define _POSIX_C_SOURCE 200112L -#endif - -/* - * Generic pieces needed to manage muxable stream protocols like h2 - */ - -struct lws_muxable { - struct lws *parent_wsi; - struct lws *child_list; - struct lws *sibling_list; - - unsigned int my_sid; - unsigned int child_count; - - uint32_t highest_sid; - - uint8_t requested_POLLOUT; -}; - -#include "private-lib-roles.h" - -#ifdef LWS_WITH_IPV6 -#if defined(WIN32) || defined(_WIN32) -#include -#else -#include -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * All lws_tls...() functions must return this type, converting the - * native backend result and doing the extra work to determine which one - * as needed. - * - * Native TLS backend return codes are NOT ALLOWED outside the backend. - * - * Non-SSL mode also uses these types. - */ -enum lws_ssl_capable_status { - LWS_SSL_CAPABLE_ERROR = -1, /* it failed */ - LWS_SSL_CAPABLE_DONE = 0, /* it succeeded */ - LWS_SSL_CAPABLE_MORE_SERVICE_READ = -2, /* retry WANT_READ */ - LWS_SSL_CAPABLE_MORE_SERVICE_WRITE = -3, /* retry WANT_WRITE */ - LWS_SSL_CAPABLE_MORE_SERVICE = -4, /* general retry */ -}; - - -/* - * - * ------ roles ------ - * - */ - -/* null-terminated array of pointers to roles lws built with */ -extern const struct lws_role_ops *available_roles[]; - -#define LWS_FOR_EVERY_AVAILABLE_ROLE_START(xx) { \ - const struct lws_role_ops **ppxx = available_roles; \ - while (*ppxx) { \ - const struct lws_role_ops *xx = *ppxx++; - -#define LWS_FOR_EVERY_AVAILABLE_ROLE_END }} - -/* - * - * ------ event_loop ops ------ - * - */ - -/* enums of socks version */ -enum socks_version { - SOCKS_VERSION_4 = 4, - SOCKS_VERSION_5 = 5 -}; - -/* enums of subnegotiation version */ -enum socks_subnegotiation_version { - SOCKS_SUBNEGOTIATION_VERSION_1 = 1, -}; - -/* enums of socks commands */ -enum socks_command { - SOCKS_COMMAND_CONNECT = 1, - SOCKS_COMMAND_BIND = 2, - SOCKS_COMMAND_UDP_ASSOCIATE = 3 -}; - -/* enums of socks address type */ -enum socks_atyp { - SOCKS_ATYP_IPV4 = 1, - SOCKS_ATYP_DOMAINNAME = 3, - SOCKS_ATYP_IPV6 = 4 -}; - -/* enums of socks authentication methods */ -enum socks_auth_method { - SOCKS_AUTH_NO_AUTH = 0, - SOCKS_AUTH_GSSAPI = 1, - SOCKS_AUTH_USERNAME_PASSWORD = 2 -}; - -/* enums of subnegotiation status */ -enum socks_subnegotiation_status { - SOCKS_SUBNEGOTIATION_STATUS_SUCCESS = 0, -}; - -/* enums of socks request reply */ -enum socks_request_reply { - SOCKS_REQUEST_REPLY_SUCCESS = 0, - SOCKS_REQUEST_REPLY_FAILURE_GENERAL = 1, - SOCKS_REQUEST_REPLY_CONNECTION_NOT_ALLOWED = 2, - SOCKS_REQUEST_REPLY_NETWORK_UNREACHABLE = 3, - SOCKS_REQUEST_REPLY_HOST_UNREACHABLE = 4, - SOCKS_REQUEST_REPLY_CONNECTION_REFUSED = 5, - SOCKS_REQUEST_REPLY_TTL_EXPIRED = 6, - SOCKS_REQUEST_REPLY_COMMAND_NOT_SUPPORTED = 7, - SOCKS_REQUEST_REPLY_ATYP_NOT_SUPPORTED = 8 -}; - -/* enums used to generate socks messages */ -enum socks_msg_type { - /* greeting */ - SOCKS_MSG_GREETING, - /* credential, user name and password */ - SOCKS_MSG_USERNAME_PASSWORD, - /* connect command */ - SOCKS_MSG_CONNECT -}; - -enum { - LWS_RXFLOW_ALLOW = (1 << 0), - LWS_RXFLOW_PENDING_CHANGE = (1 << 1), -}; - -typedef enum lws_parser_return { - LPR_FORBIDDEN = -2, - LPR_FAIL = -1, - LPR_OK = 0, - LPR_DO_FALLBACK = 2, -} lws_parser_return_t; - -enum pmd_return { - PMDR_UNKNOWN, - PMDR_DID_NOTHING, - PMDR_HAS_PENDING, - PMDR_EMPTY_NONFINAL, - PMDR_EMPTY_FINAL, - - PMDR_FAILED = -1 -}; - -#if defined(LWS_WITH_PEER_LIMITS) -struct lws_peer { - struct lws_peer *next; - struct lws_peer *peer_wait_list; - - time_t time_created; - time_t time_closed_all; - - uint8_t addr[32]; - uint32_t hash; - uint32_t count_wsi; - uint32_t total_wsi; - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - struct lws_peer_role_http http; -#endif - - uint8_t af; -}; -#endif - -enum { - LWS_EV_READ = (1 << 0), - LWS_EV_WRITE = (1 << 1), - LWS_EV_START = (1 << 2), - LWS_EV_STOP = (1 << 3), - - LWS_EV_PREPARE_DELETION = (1u << 31), -}; - -#ifdef LWS_WITH_IPV6 -#define LWS_IPV6_ENABLED(vh) \ - (!lws_check_opt(vh->context->options, LWS_SERVER_OPTION_DISABLE_IPV6) && \ - !lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_IPV6)) -#else -#define LWS_IPV6_ENABLED(context) (0) -#endif - -#ifdef LWS_WITH_UNIX_SOCK -#define LWS_UNIX_SOCK_ENABLED(vhost) \ - (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK) -#else -#define LWS_UNIX_SOCK_ENABLED(vhost) (0) -#endif - -enum uri_path_states { - URIPS_IDLE, - URIPS_SEEN_SLASH, - URIPS_SEEN_SLASH_DOT, - URIPS_SEEN_SLASH_DOT_DOT, -}; - -enum uri_esc_states { - URIES_IDLE, - URIES_SEEN_PERCENT, - URIES_SEEN_PERCENT_H1, -}; - -#if defined(LWS_WITH_CLIENT) - -enum { - CIS_ADDRESS, - CIS_PATH, - CIS_HOST, - CIS_ORIGIN, - CIS_PROTOCOL, - CIS_METHOD, - CIS_IFACE, - CIS_ALPN, - - - CIS_COUNT -}; - -struct client_info_stash { - char *cis[CIS_COUNT]; - void *opaque_user_data; /* not allocated or freed by lws */ -}; -#endif - -#if defined(LWS_WITH_UDP) -#define lws_wsi_is_udp(___wsi) (!!___wsi->udp) -#endif - -#define LWS_H2_FRAME_HEADER_LENGTH 9 - -int -__lws_sul_insert(lws_dll2_owner_t *own, lws_sorted_usec_list_t *sul, - lws_usec_t us); - -lws_usec_t -__lws_sul_service_ripe(lws_dll2_owner_t *own, lws_usec_t usnow); - -struct lws_timed_vh_protocol { - struct lws_timed_vh_protocol *next; - lws_sorted_usec_list_t sul; - const struct lws_protocols *protocol; - struct lws_vhost *vhost; /* only used for pending processing */ - int reason; - int tsi_req; -}; - -/* - * lws_dsh -*/ - -typedef struct lws_dsh_obj_head { - lws_dll2_owner_t owner; - int kind; -} lws_dsh_obj_head_t; - -typedef struct lws_dsh_obj { - lws_dll2_t list; /* must be first */ - struct lws_dsh *dsh; /* invalid when on free list */ - size_t size; /* invalid when on free list */ - size_t asize; -} lws_dsh_obj_t; - -typedef struct lws_dsh { - lws_dll2_t list; - uint8_t *buf; - lws_dsh_obj_head_t *oha; /* array of object heads/kind */ - size_t buffer_size; - size_t locally_in_use; - size_t locally_free; - int count_kinds; - uint8_t being_destroyed; - /* - * Overallocations at create: - * - * - the buffer itself - * - the object heads array - */ -} lws_dsh_t; - -/* - * lws_async_dns - */ - -typedef struct lws_async_dns { - lws_sockaddr46 sa46; /* nameserver */ - lws_dll2_owner_t waiting; - lws_dll2_owner_t cached; - struct lws *wsi; - time_t time_set_server; - char dns_server_set; -} lws_async_dns_t; - -typedef enum { - LADNS_CONF_SERVER_UNKNOWN = -1, - LADNS_CONF_SERVER_SAME, - LADNS_CONF_SERVER_CHANGED -} lws_async_dns_server_check_t; - -#if defined(LWS_WITH_SYS_ASYNC_DNS) -void -lws_aysnc_dns_completed(struct lws *wsi, void *sa, size_t salen, - lws_async_dns_retcode_t ret); -#endif -void -lws_async_dns_cancel(struct lws *wsi); - -/* - * so we can have n connections being serviced simultaneously, - * these things need to be isolated per-thread. - */ - -struct lws_context_per_thread { -#if LWS_MAX_SMP > 1 - pthread_mutex_t lock_stats; - struct lws_mutex_refcount mr; - pthread_t self; -#endif - struct lws_dll2_owner dll_buflist_owner; /* guys with pending rxflow */ - struct lws_dll2_owner seq_owner; /* list of lws_sequencer-s */ - lws_dll2_owner_t attach_owner; /* pending lws_attach */ - -#if defined(LWS_WITH_SECURE_STREAMS) - lws_dll2_owner_t ss_owner; -#endif -#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) || \ - defined(LWS_WITH_SECURE_STREAMS_THREAD_API) - lws_dll2_owner_t ss_dsh_owner; - lws_dll2_owner_t ss_client_owner; -#endif - - struct lws_dll2_owner pt_sul_owner; - -#if defined (LWS_WITH_SEQUENCER) - lws_sorted_usec_list_t sul_seq_heartbeat; -#endif -#if (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)) && defined(LWS_WITH_SERVER) - lws_sorted_usec_list_t sul_ah_lifecheck; -#endif -#if defined(LWS_WITH_TLS) && defined(LWS_WITH_SERVER) - lws_sorted_usec_list_t sul_tls; -#endif -#if defined(LWS_PLAT_UNIX) - lws_sorted_usec_list_t sul_plat; -#endif -#if defined(LWS_ROLE_CGI) - lws_sorted_usec_list_t sul_cgi; -#endif -#if defined(LWS_WITH_STATS) - uint64_t lws_stats[LWSSTATS_SIZE]; - int updated; - lws_sorted_usec_list_t sul_stats; -#endif -#if defined(LWS_WITH_PEER_LIMITS) - lws_sorted_usec_list_t sul_peer_limits; -#endif - -#if defined(LWS_WITH_TLS) - struct lws_pt_tls tls; -#endif - struct lws *fake_wsi; /* used for callbacks where there's no wsi */ - - struct lws_context *context; - - /* - * usable by anything in the service code, but only if the scope - * does not last longer than the service action (since next service - * of any socket can likewise use it and overwrite) - */ - unsigned char *serv_buf; - - struct lws_pollfd *fds; - volatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list; -#ifdef _WIN32 - WSAEVENT events; - CRITICAL_SECTION interrupt_lock; -#endif - lws_sockfd_type dummy_pipe_fds[2]; - struct lws *pipe_wsi; - - /* --- role based members --- */ - -#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_pt_role_ws ws; -#endif -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - struct lws_pt_role_http http; -#endif -#if defined(LWS_ROLE_DBUS) - struct lws_pt_role_dbus dbus; -#endif - /* --- event library based members --- */ - -#if defined(LWS_WITH_LIBEV) - struct lws_pt_eventlibs_libev ev; -#endif -#if defined(LWS_WITH_LIBUV) - struct lws_pt_eventlibs_libuv uv; -#endif -#if defined(LWS_WITH_LIBEVENT) - struct lws_pt_eventlibs_libevent event; -#endif -#if defined(LWS_WITH_GLIB) - struct lws_pt_eventlibs_glib glib; -#endif - -#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \ - defined(LWS_WITH_LIBEVENT) || defined(LWS_WITH_GLIB) - struct lws_signal_watcher w_sigint; -#endif - -#if defined(LWS_WITH_DETAILED_LATENCY) - lws_usec_t ust_left_poll; -#endif - - /* --- */ - - unsigned long count_conns; - unsigned int fds_count; - - /* - * set to the Thread ID that's doing the service loop just before entry - * to poll indicates service thread likely idling in poll() - * volatile because other threads may check it as part of processing - * for pollfd event change. - */ - volatile int service_tid; - int service_tid_detected; - - volatile unsigned char inside_poll; - volatile unsigned char foreign_spinlock; - - unsigned char tid; - - unsigned char inside_service:1; - unsigned char inside_lws_service:1; - unsigned char event_loop_foreign:1; - unsigned char event_loop_destroy_processing_done:1; - unsigned char destroy_self:1; - unsigned char is_destroyed:1; -#ifdef _WIN32 - unsigned char interrupt_requested:1; -#endif -}; - -#if defined(LWS_WITH_SERVER_STATUS) -struct lws_conn_stats { - unsigned long long rx, tx; - unsigned long h1_conn, h1_trans, h2_trans, ws_upg, h2_alpn, h2_subs, - h2_upg, rejected, mqtt_subs; -}; -#endif - -/* - * virtual host -related context information - * vhostwide SSL context - * vhostwide proxy - * - * hierarchy: - * - * context -> vhost -> wsi - * - * incoming connection non-SSL vhost binding: - * - * listen socket -> wsi -> select vhost after first headers - * - * incoming connection SSL vhost binding: - * - * SSL SNI -> wsi -> bind after SSL negotiation - */ - -struct lws_vhost { -#if defined(LWS_WITH_CLIENT) && defined(LWS_CLIENT_HTTP_PROXYING) - char proxy_basic_auth_token[128]; -#endif -#if LWS_MAX_SMP > 1 - pthread_mutex_t lock; - char close_flow_vs_tsi[LWS_MAX_SMP]; -#endif - -#if defined(LWS_ROLE_H2) - struct lws_vhost_role_h2 h2; -#endif -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - struct lws_vhost_role_http http; -#endif -#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_vhost_role_ws ws; -#endif - -#if defined(LWS_WITH_SOCKS5) - char socks_proxy_address[128]; - char socks_user[96]; - char socks_password[96]; -#endif -#if defined(LWS_WITH_LIBEV) - struct lws_io_watcher w_accept; -#endif -#if defined(LWS_WITH_SERVER_STATUS) - struct lws_conn_stats conn_stats; -#endif - - uint64_t options; - - struct lws_context *context; - struct lws_vhost *vhost_next; - - const lws_retry_bo_t *retry_policy; - - struct lws *lserv_wsi; - const char *name; - const char *iface; - const char *listen_accept_role; - const char *listen_accept_protocol; - const char *unix_socket_perms; - - void (*finalize)(struct lws_vhost *vh, void *arg); - void *finalize_arg; - - const struct lws_protocols *protocols; - void **protocol_vh_privs; - const struct lws_protocol_vhost_options *pvo; - const struct lws_protocol_vhost_options *headers; - struct lws_dll2_owner *same_vh_protocol_owner; - struct lws_vhost *no_listener_vhost_list; - struct lws_dll2_owner abstract_instances_owner; /* vh lock */ - -#if defined(LWS_WITH_CLIENT) - struct lws_dll2_owner dll_cli_active_conns_owner; -#endif - struct lws_dll2_owner vh_awaiting_socket_owner; - -#if defined(LWS_WITH_TLS) - struct lws_vhost_tls tls; -#endif - - struct lws_timed_vh_protocol *timed_vh_protocol_list; - void *user; - - int listen_port; -#if !defined(LWS_PLAT_FREERTOS) && !defined(OPTEE_TA) && !defined(WIN32) - int bind_iface; -#endif - -#if defined(LWS_WITH_SOCKS5) - unsigned int socks_proxy_port; -#endif - int count_protocols; - int ka_time; - int ka_probes; - int ka_interval; - int keepalive_timeout; - int timeout_secs_ah_idle; - - int count_bound_wsi; - -#ifdef LWS_WITH_ACCESS_LOG - int log_fd; -#endif - - uint8_t allocated_vhost_protocols:1; - uint8_t created_vhost_protocols:1; - uint8_t being_destroyed:1; - uint8_t from_ss_policy:1; - - unsigned char default_protocol_index; - unsigned char raw_protocol_index; -}; - -void -__lws_vhost_destroy2(struct lws_vhost *vh); - -#define mux_to_wsi(_m) lws_container_of(_m, struct lws, mux) - -void -lws_wsi_mux_insert(struct lws *wsi, struct lws *parent_wsi, int sid); -int -lws_wsi_mux_mark_parents_needing_writeable(struct lws *wsi); -struct lws * -lws_wsi_mux_move_child_to_tail(struct lws **wsi2); -int -lws_wsi_mux_action_pending_writeable_reqs(struct lws *wsi); - -void -lws_wsi_mux_dump_children(struct lws *wsi); - -void -lws_wsi_mux_close_children(struct lws *wsi, int reason); - -void -lws_wsi_mux_sibling_disconnect(struct lws *wsi); - -void -lws_wsi_mux_dump_waiting_children(struct lws *wsi); - -int -lws_wsi_mux_apply_queue(struct lws *wsi); - -/* - * struct lws - */ - -struct lws { - /* structs */ - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - struct _lws_http_mode_related http; -#endif -#if defined(LWS_ROLE_H2) - struct _lws_h2_related h2; -#endif -#if defined(LWS_ROLE_WS) - struct _lws_websocket_related *ws; /* allocated if we upgrade to ws */ -#endif -#if defined(LWS_ROLE_DBUS) - struct _lws_dbus_mode_related dbus; -#endif -#if defined(LWS_ROLE_MQTT) - struct _lws_mqtt_related *mqtt; -#endif - -#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT) - struct lws_muxable mux; - struct lws_tx_credit txc; -#endif - - /* lifetime members */ - -#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \ - defined(LWS_WITH_LIBEVENT) || defined(LWS_WITH_GLIB) - struct lws_io_watcher w_read; -#endif -#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBEVENT) - struct lws_io_watcher w_write; -#endif - -#if defined(LWS_WITH_DETAILED_LATENCY) - lws_detlat_t detlat; -#endif - - lws_sorted_usec_list_t sul_timeout; - lws_sorted_usec_list_t sul_hrtimer; - lws_sorted_usec_list_t sul_validity; - - struct lws_dll2 dll_buflist; /* guys with pending rxflow */ - struct lws_dll2 same_vh_protocol; - struct lws_dll2 vh_awaiting_socket; -#if defined(LWS_WITH_SYS_ASYNC_DNS) - struct lws_dll2 adns; /* on adns list of guys to tell result */ - lws_async_dns_cb_t adns_cb; /* callback with result */ -#endif -#if defined(LWS_WITH_CLIENT) - struct lws_dll2 dll_cli_active_conns; - struct lws_dll2 dll2_cli_txn_queue; - struct lws_dll2_owner dll2_cli_txn_queue_owner; -#endif - -#if defined(LWS_WITH_ACCESS_LOG) - char simple_ip[(8 * 5)]; -#endif - /* pointers */ - - struct lws_context *context; - struct lws_vhost *vhost; - struct lws *parent; /* points to parent, if any */ - struct lws *child_list; /* points to first child */ - struct lws *sibling_list; /* subsequent children at same level */ - const struct lws_role_ops *role_ops; - const struct lws_protocols *protocol; - struct lws_sequencer *seq; /* associated sequencer if any */ - const lws_retry_bo_t *retry_policy; - -#if defined(LWS_WITH_THREADPOOL) - struct lws_threadpool_task *tp_task; -#endif - -#if defined(LWS_WITH_PEER_LIMITS) - struct lws_peer *peer; -#endif - -#if defined(LWS_WITH_UDP) - struct lws_udp *udp; -#endif -#if defined(LWS_WITH_CLIENT) - struct client_info_stash *stash; - char *cli_hostname_copy; - const struct addrinfo *dns_results; - const struct addrinfo *dns_results_next; -#endif - void *user_space; - void *opaque_parent_data; - void *opaque_user_data; - - struct lws_buflist *buflist; /* input-side buflist */ - struct lws_buflist *buflist_out; /* output-side buflist */ - -#if defined(LWS_WITH_TLS) - struct lws_lws_tls tls; -#endif - - lws_sock_file_fd_type desc; /* .filefd / .sockfd */ -#if defined(LWS_WITH_STATS) - uint64_t active_writable_req_us; -#if defined(LWS_WITH_TLS) - uint64_t accept_start_us; -#endif -#endif - lws_wsi_state_t wsistate; - lws_wsi_state_t wsistate_pre_close; - - /* ints */ -#define LWS_NO_FDS_POS (-1) - int position_in_fds_table; - -#if defined(LWS_WITH_CLIENT) - int chunk_remaining; - int flags; -#endif - unsigned int cache_secs; - - unsigned int hdr_parsing_completed:1; - unsigned int mux_substream:1; - unsigned int upgraded_to_http2:1; - unsigned int mux_stream_immortal:1; - unsigned int h2_stream_carries_ws:1; /* immortal set as well */ - unsigned int h2_stream_carries_sse:1; /* immortal set as well */ - unsigned int h2_acked_settings:1; - unsigned int seen_nonpseudoheader:1; - unsigned int listener:1; - unsigned int pf_packet:1; - unsigned int do_broadcast:1; - unsigned int user_space_externally_allocated:1; - unsigned int socket_is_permanently_unusable:1; - unsigned int rxflow_change_to:2; - unsigned int conn_stat_done:1; - unsigned int cache_reuse:1; - unsigned int cache_revalidate:1; - unsigned int cache_intermediaries:1; - unsigned int favoured_pollin:1; - unsigned int sending_chunked:1; - unsigned int interpreting:1; - unsigned int already_did_cce:1; - unsigned int told_user_closed:1; - unsigned int told_event_loop_closed:1; - unsigned int waiting_to_send_close_frame:1; - unsigned int close_needs_ack:1; - unsigned int ipv6:1; - unsigned int parent_pending_cb_on_writable:1; - unsigned int cgi_stdout_zero_length:1; - unsigned int seen_zero_length_recv:1; - unsigned int rxflow_will_be_applied:1; - unsigned int event_pipe:1; - unsigned int handling_404:1; - unsigned int protocol_bind_balance:1; - unsigned int unix_skt:1; - unsigned int close_when_buffered_out_drained:1; - unsigned int h1_ws_proxied:1; - unsigned int proxied_ws_parent:1; - unsigned int do_bind:1; - unsigned int oom4:1; - unsigned int validity_hup:1; - unsigned int skip_fallback:1; - - unsigned int could_have_pending:1; /* detect back-to-back writes */ - unsigned int outer_will_close:1; - unsigned int shadow:1; /* we do not control fd lifecycle at all */ - -#ifdef LWS_WITH_ACCESS_LOG - unsigned int access_log_pending:1; -#endif -#if defined(LWS_WITH_CLIENT) - unsigned int do_ws:1; /* whether we are doing http or ws flow */ - unsigned int chunked:1; /* if the clientside connection is chunked */ - unsigned int client_rx_avail:1; - unsigned int client_http_body_pending:1; - unsigned int transaction_from_pipeline_queue:1; - unsigned int keepalive_active:1; - unsigned int keepalive_rejected:1; - unsigned int redirected_to_get:1; - unsigned int client_pipeline:1; - unsigned int client_h2_alpn:1; - unsigned int client_mux_substream:1; - unsigned int client_mux_migrated:1; - unsigned int client_subsequent_mime_part:1; - unsigned int client_no_follow_redirect:1; - unsigned int client_suppress_CONNECTION_ERROR:1; - /**< because the client connection creation api is still the parent of - * this activity, and will report the failure */ -#endif - -#ifdef _WIN32 - unsigned int sock_send_blocking:1; -#endif - - uint16_t ocport, c_port; - uint16_t retry; - - /* chars */ - - char lws_rx_parse_state; /* enum lws_rx_parse_state */ - char rx_frame_type; /* enum lws_write_protocol */ - char pending_timeout; /* enum pending_timeout */ - char tsi; /* thread service index we belong to */ - char protocol_interpret_idx; - char redirects; - uint8_t rxflow_bitmap; - uint8_t bound_vhost_index; - uint8_t lsp_channel; /* which of stdin/out/err */ -#ifdef LWS_WITH_CGI - char hdr_state; -#endif -#if defined(LWS_WITH_CLIENT) - char chunk_parser; /* enum lws_chunk_parser */ - uint8_t addrinfo_idx; - uint8_t sys_tls_client_cert; -#endif -#if defined(LWS_WITH_CGI) || defined(LWS_WITH_CLIENT) - char reason_bf; /* internal writeable callback reason bitfield */ -#endif -#if defined(LWS_WITH_STATS) && defined(LWS_WITH_TLS) - char seen_rx; -#endif - uint8_t immortal_substream_count; - /* volatile to make sure code is aware other thread can change */ - volatile char handling_pollout; - volatile char leave_pollout_active; -#if LWS_MAX_SMP > 1 - volatile char undergoing_init_from_other_pt; -#endif - -}; - -#define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap)) - -#if defined(LWS_WITH_SPAWN) - -#if defined(WIN32) || defined(_WIN32) -#else -#include -#include -#endif - -struct lws_spawn_piped { - - struct lws_spawn_piped_info info; - - struct lws_dll2 dll; - lws_sorted_usec_list_t sul; - - struct lws *stdwsi[3]; - int pipe_fds[3][2]; - int count_log_lines; - - lws_usec_t created; /* set by lws_spawn_piped() */ - lws_usec_t reaped; - - lws_usec_t accounting[4]; - - pid_t child_pid; - - siginfo_t si; - - uint8_t pipes_alive:2; - uint8_t we_killed_him_timeout:1; - uint8_t we_killed_him_spew:1; - uint8_t ungraceful:1; -}; - -void -lws_spawn_piped_destroy(struct lws_spawn_piped **lsp); - -int -lws_spawn_reap(struct lws_spawn_piped *lsp); - -#endif - -void -lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt); - -const struct lws_role_ops * -lws_role_by_name(const char *name); - -int -lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, - const char *iface, int ipv6_allowed); - -#if defined(LWS_WITH_IPV6) -unsigned long -lws_get_addr_scope(const char *ipaddr); -#endif - -void -lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller); -void -__lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller); - -void -__lws_free_wsi(struct lws *wsi); - -#if LWS_MAX_SMP > 1 - -static LWS_INLINE void -lws_pt_mutex_init(struct lws_context_per_thread *pt) -{ - lws_mutex_refcount_init(&pt->mr); - pthread_mutex_init(&pt->lock_stats, NULL); -} - -static LWS_INLINE void -lws_pt_mutex_destroy(struct lws_context_per_thread *pt) -{ - pthread_mutex_destroy(&pt->lock_stats); - lws_mutex_refcount_destroy(&pt->mr); -} - -#define lws_pt_lock(pt, reason) lws_mutex_refcount_lock(&pt->mr, reason) -#define lws_pt_unlock(pt) lws_mutex_refcount_unlock(&pt->mr) - -static LWS_INLINE void -lws_pt_stats_lock(struct lws_context_per_thread *pt) -{ - pthread_mutex_lock(&pt->lock_stats); -} - -static LWS_INLINE void -lws_pt_stats_unlock(struct lws_context_per_thread *pt) -{ - pthread_mutex_unlock(&pt->lock_stats); -} -#endif - -/* - * EXTENSIONS - */ - -#if defined(LWS_WITHOUT_EXTENSIONS) -#define lws_any_extension_handled(_a, _b, _c, _d) (0) -#define lws_ext_cb_active(_a, _b, _c, _d) (0) -#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0) -#define lws_issue_raw_ext_access lws_issue_raw -#define lws_context_init_extensions(_a, _b) -#endif - -int LWS_WARN_UNUSED_RESULT -lws_client_interpret_server_handshake(struct lws *wsi); - -int LWS_WARN_UNUSED_RESULT -lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c); - -int LWS_WARN_UNUSED_RESULT -lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len); - -void -lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state, - const struct lws_role_ops *ops); - -int -lws_http_to_fallback(struct lws *wsi, unsigned char *buf, size_t len); - -int LWS_WARN_UNUSED_RESULT -user_callback_handle_rxflow(lws_callback_function, struct lws *wsi, - enum lws_callback_reasons reason, void *user, - void *in, size_t len); - -int -lws_plat_set_nonblocking(lws_sockfd_type fd); - -int -lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd, - int unix_skt); - -int -lws_plat_check_connection_error(struct lws *wsi); - -int LWS_WARN_UNUSED_RESULT -lws_header_table_attach(struct lws *wsi, int autoservice); - -int -lws_header_table_detach(struct lws *wsi, int autoservice); -int -__lws_header_table_detach(struct lws *wsi, int autoservice); - -void -lws_header_table_reset(struct lws *wsi, int autoservice); - -void -__lws_header_table_reset(struct lws *wsi, int autoservice); - -char * LWS_WARN_UNUSED_RESULT -lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h); - -int LWS_WARN_UNUSED_RESULT -lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s); - -int LWS_WARN_UNUSED_RESULT -lws_ensure_user_space(struct lws *wsi); - -int LWS_WARN_UNUSED_RESULT -lws_change_pollfd(struct lws *wsi, int _and, int _or); - -#if defined(LWS_WITH_SERVER) - int _lws_vhost_init_server(const struct lws_context_creation_info *info, - struct lws_vhost *vhost); - LWS_EXTERN struct lws_vhost * - lws_select_vhost(struct lws_context *context, int port, const char *servername); - LWS_EXTERN int LWS_WARN_UNUSED_RESULT - lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len); - LWS_EXTERN void - lws_server_get_canonical_hostname(struct lws_context *context, - const struct lws_context_creation_info *info); -#else - #define _lws_vhost_init_server(_a, _b) (0) - #define lws_parse_ws(_a, _b, _c) (0) - #define lws_server_get_canonical_hostname(_a, _b) -#endif - -int -__remove_wsi_socket_from_fds(struct lws *wsi); - -enum { - LWSRXFC_ERROR = -1, - LWSRXFC_CACHED = 0, - LWSRXFC_ADDITIONAL = 1, - LWSRXFC_TRIMMED = 2, -}; - - -int -_lws_plat_service_forced_tsi(struct lws_context *context, int tsi); - -int -lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len); - -int -lws_service_flag_pending(struct lws_context *context, int tsi); - -static LWS_INLINE int -lws_has_buffered_out(struct lws *wsi) { return !!wsi->buflist_out; } - -int LWS_WARN_UNUSED_RESULT -lws_ws_client_rx_sm(struct lws *wsi, unsigned char c); - -lws_parser_return_t LWS_WARN_UNUSED_RESULT -lws_parse(struct lws *wsi, unsigned char *buf, int *len); - -int LWS_WARN_UNUSED_RESULT -lws_parse_urldecode(struct lws *wsi, uint8_t *_c); - -int LWS_WARN_UNUSED_RESULT -lws_http_action(struct lws *wsi); - -void -__lws_close_free_wsi_final(struct lws *wsi); -void -lws_libuv_closehandle(struct lws *wsi); -int -lws_libuv_check_watcher_active(struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN int -lws_plat_plugins_init(struct lws_context * context, const char * const *d); - -LWS_VISIBLE LWS_EXTERN int -lws_plat_plugins_destroy(struct lws_context * context); - -struct lws * -lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); - -void -lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi); -void -lws_vhost_unbind_wsi(struct lws *wsi); - -void -__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs); -int -__lws_change_pollfd(struct lws *wsi, int _and, int _or); - - -int -lws_callback_as_writeable(struct lws *wsi); - -int -lws_role_call_client_bind(struct lws *wsi, - const struct lws_client_connect_info *i); -void -lws_remove_child_from_any_parent(struct lws *wsi); - -char * -lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1); -int -lws_client_ws_upgrade(struct lws *wsi, const char **cce); -int -lws_create_client_ws_object(const struct lws_client_connect_info *i, - struct lws *wsi); -int -lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len); -int -lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn); -int -lws_tls_server_conn_alpn(struct lws *wsi); - -int -lws_ws_client_rx_sm_block(struct lws *wsi, unsigned char **buf, size_t len); -void -lws_destroy_event_pipe(struct lws *wsi); - -/* socks */ -int -lws_socks5c_generate_msg(struct lws *wsi, enum socks_msg_type type, ssize_t *msg_len); - -#if defined(LWS_WITH_SERVER_STATUS) -void -lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs); -#endif - -int -__lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p); - -int LWS_WARN_UNUSED_RESULT -__insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi); - -int LWS_WARN_UNUSED_RESULT -lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len); - -lws_usec_t -__lws_seq_timeout_check(struct lws_context_per_thread *pt, lws_usec_t usnow); - -lws_usec_t -__lws_ss_timeout_check(struct lws_context_per_thread *pt, lws_usec_t usnow); - -struct lws * LWS_WARN_UNUSED_RESULT -lws_client_connect_2_dnsreq(struct lws *wsi); - -LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT -lws_client_reset(struct lws **wsi, int ssl, const char *address, int port, - const char *path, const char *host, char weak); - -struct lws * LWS_WARN_UNUSED_RESULT -lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi); - -char * LWS_WARN_UNUSED_RESULT -lws_generate_client_handshake(struct lws *wsi, char *pkt); - -int -lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); - -struct lws * -lws_http_client_connect_via_info2(struct lws *wsi); - - -#if defined(LWS_WITH_CLIENT) -int -lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd); - -int LWS_WARN_UNUSED_RESULT -lws_http_transaction_completed_client(struct lws *wsi); -#if !defined(LWS_WITH_TLS) - #define lws_context_init_client_ssl(_a, _b) (0) -#endif -void -lws_decode_ssl_error(void); -#else -#define lws_context_init_client_ssl(_a, _b) (0) -#endif - -int -__lws_rx_flow_control(struct lws *wsi); - -int -_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa); - -#if defined(LWS_WITH_SERVER) -int -lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len); -#else -#define lws_server_socket_service(_b, _c) (0) -#define lws_handshake_server(_a, _b, _c) (0) -#endif - -#ifdef LWS_WITH_ACCESS_LOG -int -lws_access_log(struct lws *wsi); -void -lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int len, int meth); -#else -#define lws_access_log(_a) -#endif - -#if defined(_DEBUG) -void -lws_wsi_txc_describe(struct lws_tx_credit *txc, const char *at, uint32_t sid); -#else -#define lws_wsi_txc_describe(x, y, z) { (void)x; } -#endif - -int -lws_wsi_txc_check_skint(struct lws_tx_credit *txc, int32_t tx_cr); - -int -lws_wsi_txc_report_manual_txcr_in(struct lws *wsi, int32_t bump); - -void -lws_mux_mark_immortal(struct lws *wsi); -void -lws_http_close_immortal(struct lws *wsi); - -int -lws_cgi_kill_terminated(struct lws_context_per_thread *pt); - -void -lws_cgi_remove_and_kill(struct lws *wsi); - -void -lws_plat_delete_socket_from_fds(struct lws_context *context, - struct lws *wsi, int m); -void -lws_plat_insert_socket_into_fds(struct lws_context *context, - struct lws *wsi); - -int -lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi, - struct lws_pollfd *pfd); - - -int -lws_plat_pipe_create(struct lws *wsi); -int -lws_plat_pipe_signal(struct lws *wsi); -void -lws_plat_pipe_close(struct lws *wsi); - -void -lws_addrinfo_clean(struct lws *wsi); - -void -lws_add_wsi_to_draining_ext_list(struct lws *wsi); -void -lws_remove_wsi_from_draining_ext_list(struct lws *wsi); -int -lws_poll_listen_fd(struct lws_pollfd *fd); -int -lws_plat_service(struct lws_context *context, int timeout_ms); -LWS_VISIBLE int -_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi); - -int -lws_pthread_self_to_tsi(struct lws_context *context); -const char * LWS_WARN_UNUSED_RESULT -lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt); -int LWS_WARN_UNUSED_RESULT -lws_plat_inet_pton(int af, const char *src, void *dst); - -void -lws_same_vh_protocol_remove(struct lws *wsi); -void -__lws_same_vh_protocol_remove(struct lws *wsi); -void -lws_same_vh_protocol_insert(struct lws *wsi, int n); - -void -lws_seq_destroy_all_on_pt(struct lws_context_per_thread *pt); - -int -lws_broadcast(struct lws_context_per_thread *pt, int reason, void *in, size_t len); - -#if defined(LWS_WITH_STATS) - void - lws_stats_bump(struct lws_context_per_thread *pt, int i, uint64_t bump); - void - lws_stats_max(struct lws_context_per_thread *pt, int index, uint64_t val); -#else - static LWS_INLINE uint64_t lws_stats_bump( - struct lws_context_per_thread *pt, int index, uint64_t bump) { - (void)pt; (void)index; (void)bump; return 0; } - static LWS_INLINE uint64_t lws_stats_max( - struct lws_context_per_thread *pt, int index, uint64_t val) { - (void)pt; (void)index; (void)val; return 0; } -#endif - - - -#if defined(LWS_WITH_PEER_LIMITS) -void -lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer); -int -lws_peer_confirm_ah_attach_ok(struct lws_context *context, - struct lws_peer *peer); -void -lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer); -void -lws_peer_cull_peer_wait_list(struct lws_context *context); -struct lws_peer * -lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd); -void -lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer, - struct lws *wsi); -void -lws_peer_dump_from_wsi(struct lws *wsi); -#endif - -#ifdef LWS_WITH_HUBBUB -hubbub_error -html_parser_cb(const hubbub_token *token, void *pw); -#endif - -int -lws_threadpool_tsi_context(struct lws_context *context, int tsi); - -void -__lws_wsi_remove_from_sul(struct lws *wsi); - -void -lws_validity_confirmed(struct lws *wsi); -void -_lws_validity_confirmed_role(struct lws *wsi); - -int -lws_seq_pt_init(struct lws_context_per_thread *pt); - -int -lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_tokens *ebuf, char fr, const char *hint); -int -lws_buflist_aware_finished_consuming(struct lws *wsi, struct lws_tokens *ebuf, - int used, int buffered, const char *hint); - -extern const struct lws_protocols protocol_abs_client_raw_skt, - protocol_abs_client_unit_test; - -void -__lws_reset_wsi(struct lws *wsi); - -void -lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len); - -#if defined(LWS_WITH_SYS_ASYNC_DNS) -lws_async_dns_server_check_t -lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa); -int -lws_async_dns_init(struct lws_context *context); -void -lws_async_dns_deinit(lws_async_dns_t *dns); -#endif - -int -lws_protocol_init_vhost(struct lws_vhost *vh, int *any); -int -_lws_generic_transaction_completed_active_conn(struct lws **wsi); - -#define ACTIVE_CONNS_SOLO 0 -#define ACTIVE_CONNS_MUXED 1 -#define ACTIVE_CONNS_QUEUED 2 -#define ACTIVE_CONNS_FAILED 3 - -int -lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin); - -const char * -lws_wsi_client_stash_item(struct lws *wsi, int stash_idx, int hdr_idx); - -int -lws_plat_BINDTODEVICE(lws_sockfd_type fd, const char *ifname); - -int -lws_socks5c_ads_server(struct lws_vhost *vh, - const struct lws_context_creation_info *info); - -int -lws_socks5c_handle_state(struct lws *wsi, struct lws_pollfd *pollfd, - const char **pcce); - -int -lws_socks5c_greet(struct lws *wsi, const char **pcce); - -enum { - LW5CHS_RET_RET0, - LW5CHS_RET_BAIL3, - LW5CHS_RET_STARTHS, - LW5CHS_RET_NOTHING -}; - -#ifdef __cplusplus -}; -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/core-net/README.md libwebsockets-2.4.2/lib/core-net/README.md --- libwebsockets-4.0.20/lib/core-net/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -# Implementation background - -## Client connection Queueing - -By default lws treats each client connection as completely separate, and each is -made from scratch with its own network connection independently. - -If the user code sets the `LCCSCF_PIPELINE` bit on `info.ssl_connection` when -creating the client connection though, lws attempts to optimize multiple client -connections to the same place by sharing any existing connection and its tls -tunnel where possible. - -There are two basic approaches, for h1 additional connections of the same type -and endpoint basically queue on a leader and happen sequentially. - -For muxed protocols like h2, they may also queue if the initial connection is -not up yet, but subsequently the will all join the existing connection -simultaneously "broadside". - -## h1 queueing - -The initial wsi to start the network connection becomes the "leader" that -subsequent connection attempts will queue against. Each vhost has a dll2_owner -`wsi->dll_cli_active_conns_owner` that "leaders" who are actually making network -connections themselves can register on as "active client connections". - -Other client wsi being created who find there is already a leader on the active -client connection list for the vhost, can join their dll2 wsi->dll2_cli_txn_queue -to the leader's wsi->dll2_cli_txn_queue_owner to "queue" on the leader. - -The user code does not know which wsi was first or is queued, it just waits for -stuff to happen the same either way. - -When the "leader" wsi connects, it performs its client transaction as normal, -and at the end arrives at `lws_http_transaction_completed_client()`. Here, it -calls through to the lws_mux `_lws_generic_transaction_completed_active_conn()` -helper. This helper sees if anything else is queued, and if so, migrates assets -like the SSL *, the socket fd, and any remaining queue from the original leader -to the head of the list, which replaces the old leader as the "active client -connection" any subsequent connects would queue on. - -It has to be done this way so that user code which may know each client wsi by -its wsi, or have marked it with an opaque_user_data pointer, is getting its -specific request handled by the wsi it expects it to be handled by. - -A side effect of this, and in order to be able to handle POSTs cleanly, lws -does not attempt to send the headers for the next queued child before the -previous child has finished. - -The process of moving the SSL context and fd etc between the queued wsi continues -until the queue is all handled. - -## muxed protocol queueing and stream binding - -h2 connections act the same as h1 before the initial connection has been made, -but once it is made all the queued connections join the network connection as -child mux streams immediately, "broadside", binding the stream to the existing -network connection. diff -Nru libwebsockets-4.0.20/lib/core-net/sequencer.c libwebsockets-2.4.2/lib/core-net/sequencer.c --- libwebsockets-4.0.20/lib/core-net/sequencer.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/sequencer.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,330 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -/* - * per pending event - */ -typedef struct lws_seq_event { - struct lws_dll2 seq_event_list; - - void *data; - void *aux; - lws_seq_events_t e; -} lws_seq_event_t; - -/* - * per sequencer - */ -typedef struct lws_sequencer { - struct lws_dll2 seq_list; - - lws_sorted_usec_list_t sul_timeout; - lws_sorted_usec_list_t sul_pending; - - struct lws_dll2_owner seq_event_owner; - struct lws_context_per_thread *pt; - lws_seq_event_cb cb; - const char *name; - const lws_retry_bo_t *retry; - - lws_usec_t time_created; - lws_usec_t timeout; /* 0 or time we timeout */ - - char going_down; -} lws_seq_t; - -#define QUEUE_SANITY_LIMIT 10 - -static void -lws_sul_seq_heartbeat_cb(lws_sorted_usec_list_t *sul) -{ - struct lws_context_per_thread *pt = lws_container_of(sul, - struct lws_context_per_thread, sul_seq_heartbeat); - - /* send every sequencer a heartbeat message... it can ignore it */ - - lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, - lws_dll2_get_head(&pt->seq_owner)) { - lws_seq_t *s = lws_container_of(p, lws_seq_t, seq_list); - - /* queue the message to inform the sequencer */ - lws_seq_queue_event(s, LWSSEQ_HEARTBEAT, NULL, NULL); - - } lws_end_foreach_dll_safe(p, tp); - - /* schedule the next one */ - - __lws_sul_insert(&pt->pt_sul_owner, &pt->sul_seq_heartbeat, - LWS_US_PER_SEC); -} - -int -lws_seq_pt_init(struct lws_context_per_thread *pt) -{ - pt->sul_seq_heartbeat.cb = lws_sul_seq_heartbeat_cb; - - /* schedule the first heartbeat */ - __lws_sul_insert(&pt->pt_sul_owner, &pt->sul_seq_heartbeat, - LWS_US_PER_SEC); - - return 0; -} - -lws_seq_t * -lws_seq_create(lws_seq_info_t *i) -{ - struct lws_context_per_thread *pt = &i->context->pt[i->tsi]; - lws_seq_t *seq = lws_zalloc(sizeof(*seq) + i->user_size, __func__); - - if (!seq) - return NULL; - - seq->cb = i->cb; - seq->pt = pt; - seq->name = i->name; - seq->retry = i->retry; - - *i->puser = (void *)&seq[1]; - - /* add the sequencer to the pt */ - - lws_pt_lock(pt, __func__); /* ---------------------------------- pt { */ - - lws_dll2_add_tail(&seq->seq_list, &pt->seq_owner); - - lws_pt_unlock(pt); /* } pt ------------------------------------------ */ - - seq->time_created = lws_now_usecs(); - - /* try to queue the creation cb */ - - if (lws_seq_queue_event(seq, LWSSEQ_CREATED, NULL, NULL)) { - lws_dll2_remove(&seq->seq_list); - lws_free(seq); - - return NULL; - } - - return seq; -} - -static int -seq_ev_destroy(struct lws_dll2 *d, void *user) -{ - lws_seq_event_t *seqe = lws_container_of(d, lws_seq_event_t, - seq_event_list); - - lws_dll2_remove(&seqe->seq_event_list); - lws_free(seqe); - - return 0; -} - -void -lws_seq_destroy(lws_seq_t **pseq) -{ - lws_seq_t *seq = *pseq; - - /* defeat another thread racing to add events while we are destroying */ - seq->going_down = 1; - - seq->cb(seq, (void *)&seq[1], LWSSEQ_DESTROYED, NULL, NULL); - - lws_pt_lock(seq->pt, __func__); /* -------------------------- pt { */ - - lws_dll2_remove(&seq->seq_list); - lws_dll2_remove(&seq->sul_timeout.list); - lws_dll2_remove(&seq->sul_pending.list); - /* remove and destroy any pending events */ - lws_dll2_foreach_safe(&seq->seq_event_owner, NULL, seq_ev_destroy); - - lws_pt_unlock(seq->pt); /* } pt ---------------------------------- */ - - - lws_free_set_NULL(seq); -} - -void -lws_seq_destroy_all_on_pt(struct lws_context_per_thread *pt) -{ - lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, - pt->seq_owner.head) { - lws_seq_t *s = lws_container_of(p, lws_seq_t, - seq_list); - - lws_seq_destroy(&s); - - } lws_end_foreach_dll_safe(p, tp); -} - -static void -lws_seq_sul_pending_cb(lws_sorted_usec_list_t *sul) -{ - lws_seq_t *seq = lws_container_of(sul, lws_seq_t, sul_pending); - lws_seq_event_t *seqe; - struct lws_dll2 *dh; - int n; - - if (!seq->seq_event_owner.count) - return; - - /* events are only added at tail, so no race possible yet... */ - - dh = lws_dll2_get_head(&seq->seq_event_owner); - seqe = lws_container_of(dh, lws_seq_event_t, seq_event_list); - - n = seq->cb(seq, (void *)&seq[1], seqe->e, seqe->data, seqe->aux); - - /* ... have to lock here though, because we will change the list */ - - lws_pt_lock(seq->pt, __func__); /* ----------------------------- pt { */ - - /* detach event from sequencer event list and free it */ - lws_dll2_remove(&seqe->seq_event_list); - lws_free(seqe); - lws_pt_unlock(seq->pt); /* } pt ------------------------------------- */ - - if (n) { - lwsl_info("%s: destroying seq '%s' by request\n", __func__, - seq->name); - lws_seq_destroy(&seq); - } -} - -int -lws_seq_queue_event(lws_seq_t *seq, lws_seq_events_t e, void *data, void *aux) -{ - lws_seq_event_t *seqe; - - if (!seq || seq->going_down) - return 1; - - seqe = lws_zalloc(sizeof(*seqe), __func__); - if (!seqe) - return 1; - - seqe->e = e; - seqe->data = data; - seqe->aux = aux; - - // lwsl_notice("%s: seq %s: event %d\n", __func__, seq->name, e); - - lws_pt_lock(seq->pt, __func__); /* ----------------------------- pt { */ - - if (seq->seq_event_owner.count > QUEUE_SANITY_LIMIT) { - lwsl_err("%s: more than %d events queued\n", __func__, - QUEUE_SANITY_LIMIT); - } - - lws_dll2_add_tail(&seqe->seq_event_list, &seq->seq_event_owner); - - seq->sul_pending.cb = lws_seq_sul_pending_cb; - __lws_sul_insert(&seq->pt->pt_sul_owner, &seq->sul_pending, 1); - - lws_pt_unlock(seq->pt); /* } pt ------------------------------------- */ - - return 0; -} - -/* - * Check if wsi still extant, by peeking in the message queue for a - * LWSSEQ_WSI_CONN_CLOSE message about wsi. (Doesn't need to do the same for - * CONN_FAIL since that will never have produced any messages prior to that). - * - * Use this to avoid trying to perform operations on wsi that have already - * closed but we didn't get to that message yet. - * - * Returns 0 if not closed yet or 1 if it has closed but we didn't process the - * close message yet. - */ - -int -lws_seq_check_wsi(lws_seq_t *seq, struct lws *wsi) -{ - lws_seq_event_t *seqe; - struct lws_dll2 *dh; - - lws_pt_lock(seq->pt, __func__); /* ----------------------------- pt { */ - - dh = lws_dll2_get_head(&seq->seq_event_owner); - while (dh) { - seqe = lws_container_of(dh, lws_seq_event_t, seq_event_list); - - if (seqe->e == LWSSEQ_WSI_CONN_CLOSE && seqe->data == wsi) - break; - - dh = dh->next; - } - - lws_pt_unlock(seq->pt); /* } pt ------------------------------------- */ - - return !!dh; -} - - -static void -lws_seq_sul_timeout_cb(lws_sorted_usec_list_t *sul) -{ - lws_seq_t *s = lws_container_of(sul, lws_seq_t, sul_timeout); - - lws_seq_queue_event(s, LWSSEQ_TIMED_OUT, NULL, NULL); -} - -/* set us to LWS_SET_TIMER_USEC_CANCEL to remove timeout */ - -int -lws_seq_timeout_us(lws_seq_t *seq, lws_usec_t us) -{ - seq->sul_timeout.cb = lws_seq_sul_timeout_cb; - /* list is always at the very top of the sul */ - return __lws_sul_insert(&seq->pt->pt_sul_owner, - (lws_sorted_usec_list_t *)&seq->sul_timeout.list, us); -} - -lws_seq_t * -lws_seq_from_user(void *u) -{ - return &((lws_seq_t *)u)[-1]; -} - -const char * -lws_seq_name(lws_seq_t *seq) -{ - return seq->name; -} - -lws_usec_t -lws_seq_us_since_creation(lws_seq_t *seq) -{ - return lws_now_usecs() - seq->time_created; -} - -struct lws_context * -lws_seq_get_context(lws_seq_t *seq) -{ - return seq->pt->context; -} - diff -Nru libwebsockets-4.0.20/lib/core-net/server.c libwebsockets-2.4.2/lib/core-net/server.c --- libwebsockets-4.0.20/lib/core-net/server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,323 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_SERVER_STATUS) - -void -lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs) -{ - const struct lws_vhost *vh = ctx->vhost_list; - - while (vh) { - - cs->rx += vh->conn_stats.rx; - cs->tx += vh->conn_stats.tx; - cs->h1_conn += vh->conn_stats.h1_conn; - cs->h1_trans += vh->conn_stats.h1_trans; - cs->h2_trans += vh->conn_stats.h2_trans; - cs->ws_upg += vh->conn_stats.ws_upg; - cs->h2_upg += vh->conn_stats.h2_upg; - cs->h2_alpn += vh->conn_stats.h2_alpn; - cs->h2_subs += vh->conn_stats.h2_subs; - cs->rejected += vh->conn_stats.rejected; - - vh = vh->vhost_next; - } -} - -int -lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len) -{ -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - static const char * const prots[] = { - "http://", - "https://", - "file://", - "cgi://", - ">http://", - ">https://", - "callback://" - }; -#endif - char *orig = buf, *end = buf + len - 1, first; - int n; - - if (len < 100) - return 0; - - buf += lws_snprintf(buf, end - buf, - "{\n \"name\":\"%s\",\n" - " \"port\":\"%d\",\n" - " \"use_ssl\":\"%d\",\n" - " \"sts\":\"%d\",\n" - " \"rx\":\"%llu\",\n" - " \"tx\":\"%llu\",\n" - " \"h1_conn\":\"%lu\",\n" - " \"h1_trans\":\"%lu\",\n" - " \"h2_trans\":\"%lu\",\n" - " \"ws_upg\":\"%lu\",\n" - " \"rejected\":\"%lu\",\n" - " \"h2_upg\":\"%lu\",\n" - " \"h2_alpn\":\"%lu\",\n" - " \"h2_subs\":\"%lu\"" - , - vh->name, vh->listen_port, -#if defined(LWS_WITH_TLS) - vh->tls.use_ssl & LCCSCF_USE_SSL, -#else - 0, -#endif - !!(vh->options & LWS_SERVER_OPTION_STS), - vh->conn_stats.rx, vh->conn_stats.tx, - vh->conn_stats.h1_conn, - vh->conn_stats.h1_trans, - vh->conn_stats.h2_trans, - vh->conn_stats.ws_upg, - vh->conn_stats.rejected, - vh->conn_stats.h2_upg, - vh->conn_stats.h2_alpn, - vh->conn_stats.h2_subs - ); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - if (vh->http.mount_list) { - const struct lws_http_mount *m = vh->http.mount_list; - - buf += lws_snprintf(buf, end - buf, ",\n \"mounts\":["); - first = 1; - while (m) { - if (!first) - buf += lws_snprintf(buf, end - buf, ","); - buf += lws_snprintf(buf, end - buf, - "\n {\n \"mountpoint\":\"%s\",\n" - " \"origin\":\"%s%s\",\n" - " \"cache_max_age\":\"%d\",\n" - " \"cache_reuse\":\"%d\",\n" - " \"cache_revalidate\":\"%d\",\n" - " \"cache_intermediaries\":\"%d\"\n" - , - m->mountpoint, - prots[m->origin_protocol], - m->origin, - m->cache_max_age, - m->cache_reusable, - m->cache_revalidate, - m->cache_intermediaries); - if (m->def) - buf += lws_snprintf(buf, end - buf, - ",\n \"default\":\"%s\"", - m->def); - buf += lws_snprintf(buf, end - buf, "\n }"); - first = 0; - m = m->mount_next; - } - buf += lws_snprintf(buf, end - buf, "\n ]"); - } -#endif - if (vh->protocols) { - n = 0; - first = 1; - - buf += lws_snprintf(buf, end - buf, ",\n \"ws-protocols\":["); - while (n < vh->count_protocols) { - if (!first) - buf += lws_snprintf(buf, end - buf, ","); - buf += lws_snprintf(buf, end - buf, - "\n {\n \"%s\":{\n" - " \"status\":\"ok\"\n }\n }" - , - vh->protocols[n].name); - first = 0; - n++; - } - buf += lws_snprintf(buf, end - buf, "\n ]"); - } - - buf += lws_snprintf(buf, end - buf, "\n}"); - - return buf - orig; -} - - -int -lws_json_dump_context(const struct lws_context *context, char *buf, int len, - int hide_vhosts) -{ - char *orig = buf, *end = buf + len - 1, first = 1; - const struct lws_vhost *vh = context->vhost_list; - const struct lws_context_per_thread *pt; - int n, listening = 0, cgi_count = 0, fd; - struct lws_conn_stats cs; - double d = 0; -#ifdef LWS_WITH_CGI - struct lws_cgi * const *pcgi; -#endif - -#ifdef LWS_WITH_LIBUV - uv_uptime(&d); -#endif - - buf += lws_snprintf(buf, end - buf, "{ " - "\"version\":\"%s\",\n" - "\"uptime\":\"%ld\",\n", - lws_get_library_version(), - (long)d); - -#ifdef LWS_HAVE_GETLOADAVG -#if defined(__sun) -#include -#endif - { - double d[3]; - int m; - - m = getloadavg(d, 3); - for (n = 0; n < m; n++) { - buf += lws_snprintf(buf, end - buf, - "\"l%d\":\"%.2f\",\n", - n + 1, d[n]); - } - } -#endif - - fd = lws_open("/proc/self/statm", LWS_O_RDONLY); - if (fd >= 0) { - char contents[96], pure[96]; - n = read(fd, contents, sizeof(contents) - 1); - if (n > 0) { - contents[n] = '\0'; - if (contents[n - 1] == '\n') - contents[--n] = '\0'; - lws_json_purify(pure, contents, sizeof(pure), NULL); - - buf += lws_snprintf(buf, end - buf, - "\"statm\": \"%s\",\n", pure); - } - close(fd); - } - - buf += lws_snprintf(buf, end - buf, "\"heap\":%lld,\n\"contexts\":[\n", - (long long)lws_get_allocated_heap()); - - buf += lws_snprintf(buf, end - buf, "{ " - "\"context_uptime\":\"%llu\",\n" - "\"cgi_spawned\":\"%d\",\n" - "\"pt_fd_max\":\"%d\",\n" - "\"ah_pool_max\":\"%d\",\n" - "\"deprecated\":\"%d\",\n" - "\"wsi_alive\":\"%d\",\n", - (unsigned long long)(lws_now_usecs() - context->time_up) / - LWS_US_PER_SEC, - context->count_cgi_spawned, - context->fd_limit_per_thread, - context->max_http_header_pool, - context->deprecated, - context->count_wsi_allocated); - - buf += lws_snprintf(buf, end - buf, "\"pt\":[\n "); - for (n = 0; n < context->count_threads; n++) { - pt = &context->pt[n]; - if (n) - buf += lws_snprintf(buf, end - buf, ","); - buf += lws_snprintf(buf, end - buf, - "\n {\n" - " \"fds_count\":\"%d\",\n" - " \"ah_pool_inuse\":\"%d\",\n" - " \"ah_wait_list\":\"%d\"\n" - " }", - pt->fds_count, - pt->http.ah_count_in_use, - pt->http.ah_wait_list_length); - } - - buf += lws_snprintf(buf, end - buf, "]"); - - buf += lws_snprintf(buf, end - buf, ", \"vhosts\":[\n "); - - first = 1; - vh = context->vhost_list; - listening = 0; - cs = context->conn_stats; - lws_sum_stats(context, &cs); - while (vh) { - - if (!hide_vhosts) { - if (!first) - if(buf != end) - *buf++ = ','; - buf += lws_json_dump_vhost(vh, buf, end - buf); - first = 0; - } - if (vh->lserv_wsi) - listening++; - vh = vh->vhost_next; - } - - buf += lws_snprintf(buf, end - buf, - "],\n\"listen_wsi\":\"%d\",\n" - " \"rx\":\"%llu\",\n" - " \"tx\":\"%llu\",\n" - " \"h1_conn\":\"%lu\",\n" - " \"h1_trans\":\"%lu\",\n" - " \"h2_trans\":\"%lu\",\n" - " \"ws_upg\":\"%lu\",\n" - " \"rejected\":\"%lu\",\n" - " \"h2_alpn\":\"%lu\",\n" - " \"h2_subs\":\"%lu\",\n" - " \"h2_upg\":\"%lu\"", - listening, cs.rx, cs.tx, - cs.h1_conn, - cs.h1_trans, - cs.h2_trans, - cs.ws_upg, - cs.rejected, - cs.h2_alpn, - cs.h2_subs, - cs.h2_upg); - -#ifdef LWS_WITH_CGI - for (n = 0; n < context->count_threads; n++) { - pt = &context->pt[n]; - pcgi = &pt->http.cgi_list; - - while (*pcgi) { - pcgi = &(*pcgi)->cgi_list; - - cgi_count++; - } - } -#endif - buf += lws_snprintf(buf, end - buf, ",\n \"cgi_alive\":\"%d\"\n ", - cgi_count); - - buf += lws_snprintf(buf, end - buf, "}"); - - - buf += lws_snprintf(buf, end - buf, "]}\n "); - - return buf - orig; -} - -#endif diff -Nru libwebsockets-4.0.20/lib/core-net/service.c libwebsockets-2.4.2/lib/core-net/service.c --- libwebsockets-4.0.20/lib/core-net/service.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/service.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,806 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -int -lws_callback_as_writeable(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - int n, m; - - lws_stats_bump(pt, LWSSTATS_C_WRITEABLE_CB, 1); -#if defined(LWS_WITH_STATS) - if (wsi->active_writable_req_us) { - uint64_t ul = lws_now_usecs() - - wsi->active_writable_req_us; - - lws_stats_bump(pt, LWSSTATS_US_WRITABLE_DELAY_AVG, ul); - lws_stats_max(pt, LWSSTATS_US_WORST_WRITABLE_DELAY, ul); - wsi->active_writable_req_us = 0; - } -#endif -#if defined(LWS_WITH_DETAILED_LATENCY) - if (wsi->context->detailed_latency_cb && lwsi_state_est(wsi)) { - lws_usec_t us = lws_now_usecs(); - - wsi->detlat.earliest_write_req_pre_write = - wsi->detlat.earliest_write_req; - wsi->detlat.earliest_write_req = 0; - wsi->detlat.latencies[LAT_DUR_PROXY_RX_TO_ONWARD_TX] = - ((uint32_t)us - wsi->detlat.earliest_write_req_pre_write); - } -#endif - n = wsi->role_ops->writeable_cb[lwsi_role_server(wsi)]; - m = user_callback_handle_rxflow(wsi->protocol->callback, - wsi, (enum lws_callback_reasons) n, - wsi->user_space, NULL, 0); - - return m; -} - -int -lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) -{ - volatile struct lws *vwsi = (volatile struct lws *)wsi; - int n; - - // lwsl_notice("%s: %p\n", __func__, wsi); - - vwsi->leave_pollout_active = 0; - vwsi->handling_pollout = 1; - /* - * if another thread wants POLLOUT on us, from here on while - * handling_pollout is set, he will only set leave_pollout_active. - * If we are going to disable POLLOUT, we will check that first. - */ - wsi->could_have_pending = 0; /* clear back-to-back write detection */ - - /* - * user callback is lowest priority to get these notifications - * actually, since other pending things cannot be disordered - * - * Priority 1: pending truncated sends are incomplete ws fragments - * If anything else sent first the protocol would be - * corrupted. - * - * These are post- any compression transform - */ - - if (lws_has_buffered_out(wsi)) { - //lwsl_notice("%s: completing partial\n", __func__); - if (lws_issue_raw(wsi, NULL, 0) < 0) { - lwsl_info("%s signalling to close\n", __func__); - goto bail_die; - } - /* leave POLLOUT active either way */ - goto bail_ok; - } else - if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) { - wsi->socket_is_permanently_unusable = 1; - goto bail_die; /* retry closing now */ - } - - /* Priority 2: pre- compression transform */ - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - if (wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more) { - enum lws_write_protocol wp = LWS_WRITE_HTTP; - - lwsl_info("%s: completing comp partial (buflist_comp %p, may %d)\n", - __func__, wsi->http.comp_ctx.buflist_comp, - wsi->http.comp_ctx.may_have_more - ); - - if (wsi->role_ops->write_role_protocol(wsi, NULL, 0, &wp) < 0) { - lwsl_info("%s signalling to close\n", __func__); - goto bail_die; - } - lws_callback_on_writable(wsi); - - goto bail_ok; - } -#endif - -#ifdef LWS_WITH_CGI - /* - * A cgi master's wire protocol remains h1 or h2. He is just getting - * his data from his child cgis. - */ - if (wsi->http.cgi) { - /* also one shot */ - if (pollfd) - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { - lwsl_info("failed at set pollfd\n"); - return 1; - } - goto user_service_go_again; - } -#endif - - /* if we got here, we should have wire protocol ops set on the wsi */ - assert(wsi->role_ops); - - if (!wsi->role_ops->handle_POLLOUT) - goto bail_ok; - - n = wsi->role_ops->handle_POLLOUT(wsi); - switch (n) { - case LWS_HP_RET_BAIL_OK: - goto bail_ok; - case LWS_HP_RET_BAIL_DIE: - goto bail_die; - case LWS_HP_RET_DROP_POLLOUT: - case LWS_HP_RET_USER_SERVICE: - break; - default: - assert(0); - } - - /* one shot */ - - if (pollfd) { - int eff = vwsi->leave_pollout_active; - - if (!eff) { - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { - lwsl_info("failed at set pollfd\n"); - goto bail_die; - } - } - - vwsi->handling_pollout = 0; - - /* cannot get leave_pollout_active set after the above */ - if (!eff && wsi->leave_pollout_active) { - /* - * got set inbetween sampling eff and clearing - * handling_pollout, force POLLOUT on - */ - lwsl_debug("leave_pollout_active\n"); - if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { - lwsl_info("failed at set pollfd\n"); - goto bail_die; - } - } - - vwsi->leave_pollout_active = 0; - } - - if (lwsi_role_client(wsi) && !wsi->hdr_parsing_completed && - lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS && - lwsi_state(wsi) != LRS_ISSUE_HTTP_BODY) - goto bail_ok; - - if (n == LWS_HP_RET_DROP_POLLOUT) - goto bail_ok; - - -#ifdef LWS_WITH_CGI -user_service_go_again: -#endif - - if (wsi->role_ops->perform_user_POLLOUT) { - if (wsi->role_ops->perform_user_POLLOUT(wsi) == -1) - goto bail_die; - else - goto bail_ok; - } - - lwsl_debug("%s: %p: non mux: wsistate 0x%lx, ops %s\n", __func__, wsi, - (unsigned long)wsi->wsistate, wsi->role_ops->name); - - vwsi = (volatile struct lws *)wsi; - vwsi->leave_pollout_active = 0; - - n = lws_callback_as_writeable(wsi); - vwsi->handling_pollout = 0; - - if (vwsi->leave_pollout_active) - if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) - goto bail_die; - - return n; - - /* - * since these don't disable the POLLOUT, they are always doing the - * right thing for leave_pollout_active whether it was set or not. - */ - -bail_ok: - vwsi->handling_pollout = 0; - vwsi->leave_pollout_active = 0; - - return 0; - -bail_die: - vwsi->handling_pollout = 0; - vwsi->leave_pollout_active = 0; - - return -1; -} - -int -lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - uint8_t *buffered; - size_t blen; - int ret = LWSRXFC_CACHED, m; - - /* his RX is flowcontrolled, don't send remaining now */ - blen = lws_buflist_next_segment_len(&wsi->buflist, &buffered); - if (blen) { - if (buf >= buffered && buf + len <= buffered + blen && - blen != (size_t)len) { - /* - * rxflow while we were spilling prev rxflow - * - * len indicates how much was unused, then... so trim - * the head buflist to match that situation - */ - - lws_buflist_use_segment(&wsi->buflist, blen - len); - lwsl_debug("%s: trim existing rxflow %d -> %d\n", - __func__, (int)blen, (int)len); - - return LWSRXFC_TRIMMED; - } - ret = LWSRXFC_ADDITIONAL; - } - - /* a new rxflow, buffer it and warn caller */ - - lwsl_debug("%s: rxflow append %d\n", __func__, len - n); - m = lws_buflist_append_segment(&wsi->buflist, buf + n, len - n); - - if (m < 0) - return LWSRXFC_ERROR; - if (m) { - lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi); - if (lws_dll2_is_detached(&wsi->dll_buflist)) - lws_dll2_add_head(&wsi->dll_buflist, &pt->dll_buflist_owner); - } - - return ret; -} - -/* this is used by the platform service code to stop us waiting for network - * activity in poll() when we have something that already needs service - */ - -int -lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi) -{ - struct lws_context_per_thread *pt; - - if (!context) - return 1; - - pt = &context->pt[tsi]; - - /* - * Figure out if we really want to wait in poll()... we only need to - * wait if really nothing already to do and we have to wait for - * something from network - */ -#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) - /* 1) if we know we are draining rx ext, do not wait in poll */ - if (pt->ws.rx_draining_ext_list) - return 0; -#endif - -#if defined(LWS_WITH_TLS) - /* 2) if we know we have non-network pending data, - * do not wait in poll */ - - if (pt->context->tls_ops && - pt->context->tls_ops->fake_POLLIN_for_buffered && - pt->context->tls_ops->fake_POLLIN_for_buffered(pt)) - return 0; -#endif - - /* - * 4) If there is any wsi with rxflow buffered and in a state to process - * it, we should not wait in poll - */ - - lws_start_foreach_dll(struct lws_dll2 *, d, pt->dll_buflist_owner.head) { - struct lws *wsi = lws_container_of(d, struct lws, dll_buflist); - - if (!lws_is_flowcontrolled(wsi) && - lwsi_state(wsi) != LRS_DEFERRING_ACTION) - return 0; - - /* - * 5) If any guys with http compression to spill, we shouldn't wait in - * poll but hurry along and service them - */ - - } lws_end_foreach_dll(d); - - return timeout_ms; -} - -/* - * POLLIN said there is something... we must read it, and either use it; or - * if other material already in the buflist append it and return the buflist - * head material. - */ -int -lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_tokens *ebuf, char fr, const char *hint) -{ - int n, e, bns; - uint8_t *ep, *b; - - // lwsl_debug("%s: wsi %p: %s: prior %d\n", __func__, wsi, hint, prior); - // lws_buflist_describe(&wsi->buflist, wsi, __func__); - - (void)hint; - if (!ebuf->token) - ebuf->token = pt->serv_buf + LWS_PRE; - if (!ebuf->len || - (unsigned int)ebuf->len > wsi->context->pt_serv_buf_size - LWS_PRE) - ebuf->len = wsi->context->pt_serv_buf_size - LWS_PRE; - - e = ebuf->len; - ep = ebuf->token; - - /* h2 or muxed stream... must force the read due to HOL blocking */ - - if (wsi->mux_substream) - fr = 1; - - /* there's something on the buflist? */ - - bns = (int)lws_buflist_next_segment_len(&wsi->buflist, &ebuf->token); - b = ebuf->token; - - if (!fr && bns) - goto buflist_material; - - /* we're going to read something */ - - ebuf->token = ep; - ebuf->len = n = lws_ssl_capable_read(wsi, ep, e); - - lwsl_info("%s: wsi %p: %s: ssl_capable_read %d\n", __func__, - wsi, hint, ebuf->len); - - if (!bns && /* only acknowledge error when we handled buflist content */ - n == LWS_SSL_CAPABLE_ERROR) { - lwsl_debug("%s: SSL_CAPABLE_ERROR\n", __func__); - return -1; - } - - if (n <= 0 && bns) - /* - * There wasn't anything to read yet, but there's something - * on the buflist to give him - */ - goto buflist_material; - - /* we read something */ - - if (fr && bns) { - /* - * Stash what we read, since there's earlier buflist material - */ - - n = lws_buflist_append_segment(&wsi->buflist, ebuf->token, ebuf->len); - if (n < 0) - return -1; - if (n && lws_dll2_is_detached(&wsi->dll_buflist)) - lws_dll2_add_head(&wsi->dll_buflist, - &pt->dll_buflist_owner); - - goto buflist_material; - } - - /* - * directly return what we read - */ - - return 0; - -buflist_material: - - ebuf->token = b; - if (e < bns) - /* restrict to e, if more than e available */ - ebuf->len = e; - else - ebuf->len = bns; - - return 1; /* from buflist */ -} - -int -lws_buflist_aware_finished_consuming(struct lws *wsi, struct lws_tokens *ebuf, - int used, int buffered, const char *hint) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - int m; - - //lwsl_debug("%s %s consuming buffered %d used %zu / %zu\n", __func__, hint, - // buffered, (size_t)used, (size_t)ebuf->len); - // lws_buflist_describe(&wsi->buflist, wsi, __func__); - - /* it's in the buflist; we didn't use any */ - - if (!used && buffered) - return 0; - - if (used && buffered) { - if (wsi->buflist) { - m = (int)lws_buflist_use_segment(&wsi->buflist, (size_t)used); - // lwsl_notice("%s: used %d, next %d\n", __func__, used, m); - // lws_buflist_describe(&wsi->buflist, wsi, __func__); - if (m) - return 0; - } - - lwsl_info("%s: removed %p from dll_buflist\n", __func__, wsi); - lws_dll2_remove(&wsi->dll_buflist); - - return 0; - } - - /* any remainder goes on the buflist */ - - if (used != ebuf->len) { - // lwsl_notice("%s %s bac appending %d\n", __func__, hint, - // ebuf->len - used); - m = lws_buflist_append_segment(&wsi->buflist, - ebuf->token + used, - ebuf->len - used); - if (m < 0) - return 1; /* OOM */ - if (m) { - lwsl_debug("%s: added %p to rxflow list\n", - __func__, wsi); - if (lws_dll2_is_detached(&wsi->dll_buflist)) - lws_dll2_add_head(&wsi->dll_buflist, - &pt->dll_buflist_owner); - } - // lws_buflist_describe(&wsi->buflist, wsi, __func__); - } - - return 0; -} - -void -lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt) -{ - struct lws_pollfd pfd; - - if (!pt->dll_buflist_owner.head) - return; - - /* - * service all guys with pending rxflow that reached a state they can - * accept the pending data - */ - - lws_pt_lock(pt, __func__); - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - pt->dll_buflist_owner.head) { - struct lws *wsi = lws_container_of(d, struct lws, dll_buflist); - - pfd.events = LWS_POLLIN; - pfd.revents = LWS_POLLIN; - pfd.fd = -1; - - lwsl_debug("%s: rxflow processing: %p fc=%d, 0x%lx\n", __func__, - wsi, lws_is_flowcontrolled(wsi), - (unsigned long)wsi->wsistate); - - if (!lws_is_flowcontrolled(wsi) && - lwsi_state(wsi) != LRS_DEFERRING_ACTION) { - pt->inside_lws_service = 1; - - if ((wsi->role_ops->handle_POLLIN)(pt, wsi, &pfd) == - LWS_HPI_RET_PLEASE_CLOSE_ME) - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "close_and_handled"); - pt->inside_lws_service = 0; - } - - } lws_end_foreach_dll_safe(d, d1); - - lws_pt_unlock(pt); -} - -/* - * guys that need POLLIN service again without waiting for network action - * can force POLLIN here if not flowcontrolled, so they will get service. - * - * Return nonzero if anybody got their POLLIN faked - */ -int -lws_service_flag_pending(struct lws_context *context, int tsi) -{ - struct lws_context_per_thread *pt; - int forced = 0; - - if (!context) - return 1; - - pt = &context->pt[tsi]; - - lws_pt_lock(pt, __func__); - - /* - * 1) If there is any wsi with a buflist and in a state to process - * it, we should not wait in poll - */ - - lws_start_foreach_dll(struct lws_dll2 *, d, pt->dll_buflist_owner.head) { - struct lws *wsi = lws_container_of(d, struct lws, dll_buflist); - - if (!lws_is_flowcontrolled(wsi) && - lwsi_state(wsi) != LRS_DEFERRING_ACTION) { - forced = 1; - break; - } - } lws_end_foreach_dll(d); - -#if defined(LWS_ROLE_WS) - forced |= role_ops_ws.service_flag_pending(context, tsi); -#endif - -#if defined(LWS_WITH_TLS) - /* - * 2) For all guys with buffered SSL read data already saved up, if they - * are not flowcontrolled, fake their POLLIN status so they'll get - * service to use up the buffered incoming data, even though their - * network socket may have nothing - */ - lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1, - lws_dll2_get_head(&pt->tls.dll_pending_tls_owner)) { - struct lws *wsi = lws_container_of(p, struct lws, - tls.dll_pending_tls); - - if (wsi->position_in_fds_table >= 0) { - - pt->fds[wsi->position_in_fds_table].revents |= - pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; - if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) { - forced = 1; - /* - * he's going to get serviced now, take him off the - * list of guys with buffered SSL. If he still has some - * at the end of the service, he'll get put back on the - * list then. - */ - __lws_ssl_remove_wsi_from_buffered_list(wsi); - } - } - - } lws_end_foreach_dll_safe(p, p1); -#endif - - lws_pt_unlock(pt); - - return forced; -} - -int -lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, - int tsi) -{ - struct lws_context_per_thread *pt; - struct lws *wsi; - - if (!context || context->being_destroyed1) - return -1; - - pt = &context->pt[tsi]; - - if (!pollfd) { - /* - * calling with NULL pollfd for periodic background processing - * is no longer needed and is now illegal. - */ - assert(pollfd); - return -1; - } - assert(lws_socket_is_valid(pollfd->fd)); - - /* no, here to service a socket descriptor */ - wsi = wsi_from_fd(context, pollfd->fd); - if (!wsi) - /* not lws connection ... leave revents alone and return */ - return 0; - -#if LWS_MAX_SMP > 1 - if (wsi->undergoing_init_from_other_pt) - /* - * Temporary situation that other service thread is initializing - * this wsi right now for use on our service thread. - */ - return 0; -#endif - - /* - * so that caller can tell we handled, past here we need to - * zero down pollfd->revents after handling - */ - - /* handle session socket closed */ - - if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) && - (pollfd->revents & LWS_POLLHUP)) { - wsi->socket_is_permanently_unusable = 1; - lwsl_debug("Session Socket %p (fd=%d) dead\n", - (void *)wsi, pollfd->fd); - - goto close_and_handled; - } - -#ifdef _WIN32 - if (pollfd->revents & LWS_POLLOUT) - wsi->sock_send_blocking = FALSE; -#endif - - if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) && - (pollfd->revents & LWS_POLLHUP)) { - lwsl_debug("pollhup\n"); - wsi->socket_is_permanently_unusable = 1; - goto close_and_handled; - } - -#if defined(LWS_WITH_TLS) - if (lwsi_state(wsi) == LRS_SHUTDOWN && - lws_is_ssl(wsi) && wsi->tls.ssl) { - switch (__lws_tls_shutdown(wsi)) { - case LWS_SSL_CAPABLE_DONE: - case LWS_SSL_CAPABLE_ERROR: - goto close_and_handled; - - case LWS_SSL_CAPABLE_MORE_SERVICE_READ: - case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE: - case LWS_SSL_CAPABLE_MORE_SERVICE: - goto handled; - } - } -#endif - wsi->could_have_pending = 0; /* clear back-to-back write detection */ - pt->inside_lws_service = 1; - - /* okay, what we came here to do... */ - - /* if we got here, we should have wire protocol ops set on the wsi */ - assert(wsi->role_ops); - - // lwsl_notice("%s: %s: wsistate 0x%x\n", __func__, wsi->role_ops->name, - // wsi->wsistate); - - switch ((wsi->role_ops->handle_POLLIN)(pt, wsi, pollfd)) { - case LWS_HPI_RET_WSI_ALREADY_DIED: - pt->inside_lws_service = 0; - return 1; - case LWS_HPI_RET_HANDLED: - break; - case LWS_HPI_RET_PLEASE_CLOSE_ME: -close_and_handled: - lwsl_debug("%p: Close and handled\n", wsi); - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "close_and_handled"); -#if defined(_DEBUG) && defined(LWS_WITH_LIBUV) - /* - * confirm close has no problem being called again while - * it waits for libuv service to complete the first async - * close - */ - if (context->event_loop_ops == &event_loop_ops_uv) - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "close_and_handled uv repeat test"); -#endif - /* - * pollfd may point to something else after the close - * due to pollfd swapping scheme on delete on some platforms - * we can't clear revents now because it'd be the wrong guy's - * revents - */ - pt->inside_lws_service = 0; - return 1; - default: - assert(0); - } -#if defined(LWS_WITH_TLS) -handled: -#endif - pollfd->revents = 0; - pt->inside_lws_service = 0; - - return 0; -} - -int -lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd) -{ - return lws_service_fd_tsi(context, pollfd, 0); -} - -int -lws_service(struct lws_context *context, int timeout_ms) -{ - struct lws_context_per_thread *pt; - int n; - - if (!context) - return 1; - - pt = &context->pt[0]; - pt->inside_service = 1; - - if (context->event_loop_ops->run_pt) { - /* we are configured for an event loop */ - context->event_loop_ops->run_pt(context, 0); - - pt->inside_service = 0; - - return 1; - } - n = lws_plat_service(context, timeout_ms); - - pt->inside_service = 0; - - return n; -} - -int -lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi) -{ - struct lws_context_per_thread *pt; - int n; - - if (!context) - return 1; - - pt = &context->pt[tsi]; - pt->inside_service = 1; -#if LWS_MAX_SMP > 1 - pt->self = pthread_self(); -#endif - - if (context->event_loop_ops->run_pt) { - /* we are configured for an event loop */ - context->event_loop_ops->run_pt(context, tsi); - - pt->inside_service = 0; - - return 1; - } - - n = _lws_plat_service_tsi(context, timeout_ms, tsi); - - pt->inside_service = 0; - - return n; -} diff -Nru libwebsockets-4.0.20/lib/core-net/socks5-client.c libwebsockets-2.4.2/lib/core-net/socks5-client.c --- libwebsockets-4.0.20/lib/core-net/socks5-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/socks5-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,378 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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. - * - * Socks5 Client -related helpers - */ - -#include "private-lib-core.h" - -int -lws_set_socks(struct lws_vhost *vhost, const char *socks) -{ - char *p_at, *p_colon; - char user[96]; - char password[96]; - - if (!socks) - return -1; - - vhost->socks_user[0] = '\0'; - vhost->socks_password[0] = '\0'; - - p_at = strrchr(socks, '@'); - if (p_at) { /* auth is around */ - if ((unsigned int)(p_at - socks) > (sizeof(user) - + sizeof(password) - 2)) { - lwsl_err("Socks auth too long\n"); - goto bail; - } - - p_colon = strchr(socks, ':'); - if (p_colon) { - if ((unsigned int)(p_colon - socks) > (sizeof(user) - - 1) ) { - lwsl_err("Socks user too long\n"); - goto bail; - } - if ((unsigned int)(p_at - p_colon) > (sizeof(password) - - 1) ) { - lwsl_err("Socks password too long\n"); - goto bail; - } - - lws_strncpy(vhost->socks_user, socks, - p_colon - socks + 1); - lws_strncpy(vhost->socks_password, p_colon + 1, - p_at - (p_colon + 1) + 1); - } - - lwsl_info(" Socks auth, user: %s, password: %s\n", - vhost->socks_user, vhost->socks_password ); - - socks = p_at + 1; - } - - lws_strncpy(vhost->socks_proxy_address, socks, - sizeof(vhost->socks_proxy_address)); - - p_colon = strchr(vhost->socks_proxy_address, ':'); - if (!p_colon && !vhost->socks_proxy_port) { - lwsl_err("socks_proxy needs to be address:port\n"); - return -1; - } else { - if (p_colon) { - *p_colon = '\0'; - vhost->socks_proxy_port = atoi(p_colon + 1); - } - } - - lwsl_debug("%s: Connections via Socks5 %s:%u\n", __func__, - vhost->socks_proxy_address, vhost->socks_proxy_port); - - return 0; - -bail: - return -1; -} - -int -lws_socks5c_generate_msg(struct lws *wsi, enum socks_msg_type type, - ssize_t *msg_len) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - uint8_t *p = pt->serv_buf, *end = &p[context->pt_serv_buf_size]; - ssize_t n, passwd_len; - short net_num; - char *cp; - - switch (type) { - case SOCKS_MSG_GREETING: - if (lws_ptr_diff(end, p) < 4) - return 1; - /* socks version, version 5 only */ - *p++ = SOCKS_VERSION_5; - /* number of methods */ - *p++ = 2; - /* username password method */ - *p++ = SOCKS_AUTH_USERNAME_PASSWORD; - /* no authentication method */ - *p++ = SOCKS_AUTH_NO_AUTH; - break; - - case SOCKS_MSG_USERNAME_PASSWORD: - n = strlen(wsi->vhost->socks_user); - passwd_len = strlen(wsi->vhost->socks_password); - - if (n > 254 || passwd_len > 254) - return 1; - - if (lws_ptr_diff(end, p) < 3 + n + passwd_len) - return 1; - - /* the subnegotiation version */ - *p++ = SOCKS_SUBNEGOTIATION_VERSION_1; - - /* length of the user name */ - *p++ = n; - /* user name */ - memcpy(p, wsi->vhost->socks_user, n); - p += n; - - /* length of the password */ - *p++ = passwd_len; - - /* password */ - memcpy(p, wsi->vhost->socks_password, passwd_len); - p += passwd_len; - break; - - case SOCKS_MSG_CONNECT: - n = strlen(wsi->stash->cis[CIS_ADDRESS]); - - if (n > 254 || lws_ptr_diff(end, p) < 5 + n + 2) - return 1; - - cp = (char *)&net_num; - - /* socks version */ - *p++ = SOCKS_VERSION_5; - /* socks command */ - *p++ = SOCKS_COMMAND_CONNECT; - /* reserved */ - *p++ = 0; - /* address type */ - *p++ = SOCKS_ATYP_DOMAINNAME; - /* length of ---> */ - *p++ = n; - - /* the address we tell SOCKS proxy to connect to */ - memcpy(p, wsi->stash->cis[CIS_ADDRESS], n); - p += n; - - net_num = htons(wsi->c_port); - - /* the port we tell SOCKS proxy to connect to */ - *p++ = cp[0]; - *p++ = cp[1]; - - break; - - default: - return 1; - } - - *msg_len = lws_ptr_diff(p, pt->serv_buf); - - return 0; -} - -int -lws_socks5c_ads_server(struct lws_vhost *vh, - const struct lws_context_creation_info *info) -{ - /* socks proxy */ - if (info->socks_proxy_address) { - /* override for backwards compatibility */ - if (info->socks_proxy_port) - vh->socks_proxy_port = info->socks_proxy_port; - lws_set_socks(vh, info->socks_proxy_address); - - return 0; - } -#ifdef LWS_HAVE_GETENV - { - char *p = getenv("socks_proxy"); - - if (p && strlen(p) > 0 && strlen(p) < 95) - lws_set_socks(vh, p); - } -#endif - - return 0; -} - -/* - * Returns 0 = nothing for caller to do, 1 = return wsi, -1 = goto failed - */ - -int -lws_socks5c_greet(struct lws *wsi, const char **pcce) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - ssize_t plen; - int n; - - /* socks proxy */ - if (!wsi->vhost->socks_proxy_port) - return 0; - - if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_GREETING, &plen)) { - *pcce = "socks msg too large"; - return -1; - } - // lwsl_hexdump_notice(pt->serv_buf, plen); - n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen, - MSG_NOSIGNAL); - if (n < 0) { - lwsl_debug("ERROR writing socks greeting\n"); - *pcce = "socks write failed"; - return -1; - } - - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY, - wsi->context->timeout_secs); - - lwsi_set_state(wsi, LRS_WAITING_SOCKS_GREETING_REPLY); - - return 1; -} - -int -lws_socks5c_handle_state(struct lws *wsi, struct lws_pollfd *pollfd, - const char **pcce) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - int conn_mode = 0, pending_timeout = 0; - ssize_t len; - int n; - - /* handle proxy hung up on us */ - - if (pollfd->revents & LWS_POLLHUP) { - lwsl_warn("SOCKS connection %p (fd=%d) dead\n", - (void *)wsi, pollfd->fd); - *pcce = "socks conn dead"; - return LW5CHS_RET_BAIL3; - } - - n = recv(wsi->desc.sockfd, pt->serv_buf, - wsi->context->pt_serv_buf_size, 0); - if (n < 0) { - if (LWS_ERRNO == LWS_EAGAIN) { - lwsl_debug("SOCKS read EAGAIN, retrying\n"); - return LW5CHS_RET_RET0; - } - lwsl_err("ERROR reading from SOCKS socket\n"); - *pcce = "socks recv fail"; - return LW5CHS_RET_BAIL3; - } - - // lwsl_hexdump_warn(pt->serv_buf, n); - - switch (lwsi_state(wsi)) { - - case LRS_WAITING_SOCKS_GREETING_REPLY: - if (pt->serv_buf[0] != SOCKS_VERSION_5) - goto socks_reply_fail; - - if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) { - lwsl_client("SOCKS GR: No Auth Method\n"); - if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_CONNECT, - &len)) { - lwsl_err("%s: failed to generate connect msg\n", - __func__); - goto socks_send_msg_fail; - } - conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY; - pending_timeout = - PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; - goto socks_send; - } - - if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) { - lwsl_client("SOCKS GR: User/Pw Method\n"); - if (lws_socks5c_generate_msg(wsi, - SOCKS_MSG_USERNAME_PASSWORD, - &len)) - goto socks_send_msg_fail; - conn_mode = LRS_WAITING_SOCKS_AUTH_REPLY; - pending_timeout = - PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY; - goto socks_send; - } - goto socks_reply_fail; - - case LRS_WAITING_SOCKS_AUTH_REPLY: - if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 || - pt->serv_buf[1] != - SOCKS_SUBNEGOTIATION_STATUS_SUCCESS) - goto socks_reply_fail; - - lwsl_client("SOCKS password OK, sending connect\n"); - if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_CONNECT, &len)) { -socks_send_msg_fail: - *pcce = "socks gen msg fail"; - return LW5CHS_RET_BAIL3; - } - conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY; - pending_timeout = - PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; -socks_send: - // lwsl_hexdump_notice(pt->serv_buf, len); - n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len, - MSG_NOSIGNAL); - if (n < 0) { - lwsl_debug("ERROR writing to socks proxy\n"); - *pcce = "socks write fail"; - return LW5CHS_RET_BAIL3; - } - - lws_set_timeout(wsi, pending_timeout, - wsi->context->timeout_secs); - lwsi_set_state(wsi, conn_mode); - break; - -socks_reply_fail: - lwsl_err("%s: socks reply: v%d, err %d\n", __func__, - pt->serv_buf[0], pt->serv_buf[1]); - *pcce = "socks reply fail"; - return LW5CHS_RET_BAIL3; - - case LRS_WAITING_SOCKS_CONNECT_REPLY: - if (pt->serv_buf[0] != SOCKS_VERSION_5 || - pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS) - goto socks_reply_fail; - - lwsl_client("%s: socks connect OK\n", __func__); - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - if (lwsi_role_http(wsi) && - lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, - wsi->vhost->socks_proxy_address)) { - *pcce = "socks connect fail"; - return LW5CHS_RET_BAIL3; - } -#endif - - wsi->c_port = wsi->vhost->socks_proxy_port; - - /* clear his proxy connection timeout */ - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - return LW5CHS_RET_STARTHS; - default: - break; - } - - return LW5CHS_RET_NOTHING; -} diff -Nru libwebsockets-4.0.20/lib/core-net/sorted-usec-list.c libwebsockets-2.4.2/lib/core-net/sorted-usec-list.c --- libwebsockets-4.0.20/lib/core-net/sorted-usec-list.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/sorted-usec-list.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,149 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 -sul_compare(const lws_dll2_t *d, const lws_dll2_t *i) -{ - lws_usec_t a = ((lws_sorted_usec_list_t *)d)->us; - lws_usec_t b = ((lws_sorted_usec_list_t *)i)->us; - - /* - * Simply returning (a - b) in an int - * may lead to an integer overflow bug - */ - - if (a > b) - return 1; - if (a < b) - return -1; - - return 0; -} - -int -__lws_sul_insert(lws_dll2_owner_t *own, lws_sorted_usec_list_t *sul, - lws_usec_t us) -{ - lws_usec_t now = lws_now_usecs(); - lws_dll2_remove(&sul->list); - - if (us == LWS_SET_TIMER_USEC_CANCEL) { - /* we are clearing the timeout */ - sul->us = 0; - - return 0; - } - - sul->us = now + us; - assert(sul->cb); - - /* - * we sort the pt's list of sequencers with pending timeouts, so it's - * cheap to check it every second - */ - - lws_dll2_add_sorted(&sul->list, own, sul_compare); - -#if 0 // defined(_DEBUG) - { - lws_usec_t worst = 0; - int n = 1; - - lwsl_info("%s: own %p: count %d\n", __func__, own, own->count); - - lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, - lws_dll2_get_head(own)) { - lws_sorted_usec_list_t *sul = (lws_sorted_usec_list_t *)p; - lwsl_info("%s: %d: %llu (+%lld)\n", __func__, n++, - (unsigned long long)sul->us, - (long long)(sul->us - now)); - if (sul->us < worst) { - lwsl_err("%s: wrongly sorted sul entry!\n", - __func__); - assert(0); - } - worst = sul->us; - } lws_end_foreach_dll_safe(p, tp); - } -#endif - - return 0; -} - -void -lws_sul_schedule(struct lws_context *context, int tsi, - lws_sorted_usec_list_t *sul, sul_cb_t cb, lws_usec_t us) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - - sul->cb = cb; - - __lws_sul_insert(&pt->pt_sul_owner, sul, us); -} - -lws_usec_t -__lws_sul_service_ripe(lws_dll2_owner_t *own, lws_usec_t usnow) -{ - struct lws_context_per_thread *pt = (struct lws_context_per_thread *) - lws_container_of(own, struct lws_context_per_thread, - pt_sul_owner); - - if (pt->attach_owner.count) - lws_system_do_attach(pt); - - while (lws_dll2_get_head(own)) { - - /* .list is always first member in lws_sorted_usec_list_t */ - lws_sorted_usec_list_t *sul = (lws_sorted_usec_list_t *) - lws_dll2_get_head(own); - - assert(sul->us); /* shouldn't be on the list otherwise */ - - if (sul->us > usnow) - return sul->us - usnow; - - /* his moment has come... remove him from timeout list */ - lws_dll2_remove(&sul->list); - sul->us = 0; - pt->inside_lws_service = 1; - sul->cb(sul); - pt->inside_lws_service = 0; - - /* - * The callback may have done any mixture of delete - * and add sul entries... eg, close a wsi may pull out - * multiple entries making iterating it statefully - * unsafe. Always restart at the current head of list. - */ - } - - /* - * Nothing left to take care of in the list (cannot return 0 otherwise - * because we will service anything equal to usnow rather than return) - */ - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/core-net/state.c libwebsockets-2.4.2/lib/core-net/state.c --- libwebsockets-4.0.20/lib/core-net/state.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/state.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,142 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -void -lws_state_reg_notifier(lws_state_manager_t *mgr, - lws_state_notify_link_t *notify_link) -{ - lws_dll2_add_head(¬ify_link->list, &mgr->notify_list); -} - -void -lws_state_reg_deregister(lws_state_notify_link_t *nl) -{ - lws_dll2_remove(&nl->list); -} - -void -lws_state_reg_notifier_list(lws_state_manager_t *mgr, - lws_state_notify_link_t * const *notify_link_array) -{ - if (notify_link_array) - while (*notify_link_array) - lws_state_reg_notifier(mgr, *notify_link_array++); -} - -#if (_LWS_ENABLED_LOGS & (LLL_INFO | LLL_DEBUG)) -static const char * -_systnm(lws_state_manager_t *mgr, int state, char *temp8) -{ - if (!mgr->state_names) { - lws_snprintf(temp8, 8, "%d", state); - return temp8; - } - - return mgr->state_names[state]; -} -#endif - -static int -_report(lws_state_manager_t *mgr, int a, int b) -{ -#if (_LWS_ENABLED_LOGS & LLL_INFO) - char temp8[8]; -#endif - - lws_start_foreach_dll(struct lws_dll2 *, d, mgr->notify_list.head) { - lws_state_notify_link_t *l = - lws_container_of(d, lws_state_notify_link_t, list); - - if (l->notify_cb(mgr, l, a, b)) { - /* a dependency took responsibility for retry */ - -#if (_LWS_ENABLED_LOGS & LLL_INFO) - lwsl_info("%s: %s: %s: rejected '%s' -> '%s'\n", - __func__, mgr->name, l->name, - _systnm(mgr, a, temp8), - _systnm(mgr, b, temp8)); -#endif - - return 1; - } - - } lws_end_foreach_dll(d); - - return 0; -} - -static int -_lws_state_transition(lws_state_manager_t *mgr, int target) -{ -#if (_LWS_ENABLED_LOGS & LLL_DEBUG) - char temp8[8]; -#endif - - if (_report(mgr, mgr->state, target)) - return 1; - -#if (_LWS_ENABLED_LOGS & LLL_DEBUG) - lwsl_debug("%s: %s: changed %d '%s' -> %d '%s'\n", __func__, mgr->name, - mgr->state, _systnm(mgr, mgr->state, temp8), target, - _systnm(mgr, target, temp8)); -#endif - - mgr->state = target; - - /* Indicate success by calling the notifers again with both args same */ - _report(mgr, target, target); - - return 0; -} - -int -lws_state_transition_steps(lws_state_manager_t *mgr, int target) -{ - int n = 0; -#if (_LWS_ENABLED_LOGS & LLL_INFO) - int i = mgr->state; - char temp8[8]; -#endif - - while (!n && mgr->state != target) - n = _lws_state_transition(mgr, mgr->state + 1); - -#if (_LWS_ENABLED_LOGS & LLL_INFO) - lwsl_info("%s: %s -> %s\n", __func__, _systnm(mgr, i, temp8), - _systnm(mgr, mgr->state, temp8)); -#endif - - return 0; -} - -int -lws_state_transition(lws_state_manager_t *mgr, int target) -{ - if (mgr->state != target) - _lws_state_transition(mgr, target); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/core-net/stats.c libwebsockets-2.4.2/lib/core-net/stats.c --- libwebsockets-4.0.20/lib/core-net/stats.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/stats.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,276 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_STATS) - -uint64_t -lws_stats_get(struct lws_context *context, int index) -{ - struct lws_context_per_thread *pt = &context->pt[0]; - - if (index >= LWSSTATS_SIZE) - return 0; - - return pt->lws_stats[index]; -} - -static const char * stat_names[] = { - "C_CONNECTIONS", - "C_API_CLOSE", - "C_API_READ", - "C_API_LWS_WRITE", - "C_API_WRITE", - "C_WRITE_PARTIALS", - "C_WRITEABLE_CB_REQ", - "C_WRITEABLE_CB_EFF_REQ", - "C_WRITEABLE_CB", - "C_SSL_CONNECTIONS_FAILED", - "C_SSL_CONNECTIONS_ACCEPTED", - "C_SSL_CONNECTIONS_ACCEPT_SPIN", - "C_SSL_CONNS_HAD_RX", - "C_TIMEOUTS", - "C_SERVICE_ENTRY", - "B_READ", - "B_WRITE", - "B_PARTIALS_ACCEPTED_PARTS", - "US_SSL_ACCEPT_LATENCY_AVG", - "US_WRITABLE_DELAY_AVG", - "US_WORST_WRITABLE_DELAY", - "US_SSL_RX_DELAY_AVG", - "C_PEER_LIMIT_AH_DENIED", - "C_PEER_LIMIT_WSI_DENIED", - "C_CONNECTIONS_CLIENT", - "C_CONNECTIONS_CLIENT_FAILED", -}; - -static int -quantify(struct lws_context *context, int tsi, char *p, int len, int idx, - uint64_t *sum) -{ - const lws_humanize_unit_t *schema = humanize_schema_si; - struct lws_context_per_thread *pt = &context->pt[tsi]; - uint64_t u, u1; - - lws_pt_stats_lock(pt); - u = pt->lws_stats[idx]; - - /* it's supposed to be an average? */ - - switch (idx) { - case LWSSTATS_US_SSL_ACCEPT_LATENCY_AVG: - u1 = pt->lws_stats[LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED]; - if (u1) - u = u / u1; - break; - case LWSSTATS_US_SSL_RX_DELAY_AVG: - u1 = pt->lws_stats[LWSSTATS_C_SSL_CONNS_HAD_RX]; - if (u1) - u = u / u1; - break; - case LWSSTATS_US_WRITABLE_DELAY_AVG: - u1 = pt->lws_stats[LWSSTATS_C_WRITEABLE_CB]; - if (u1) - u = u / u1; - break; - } - lws_pt_stats_unlock(pt); - - *sum += u; - - switch (stat_names[idx][0]) { - case 'U': - schema = humanize_schema_us; - break; - case 'B': - schema = humanize_schema_si_bytes; - break; - } - - return lws_humanize(p, len, u, schema); -} - - -void -lws_stats_log_dump(struct lws_context *context) -{ - struct lws_vhost *v = context->vhost_list; - uint64_t summary[LWSSTATS_SIZE]; - char bufline[128], *p, *end = bufline + sizeof(bufline) - 1; - int n, m; - - if (!context->updated) - return; - - context->updated = 0; - memset(summary, 0, sizeof(summary)); - - lwsl_notice("\n"); - lwsl_notice("LWS internal statistics dump ----->\n"); - for (n = 0; n < (int)LWS_ARRAY_SIZE(stat_names); n++) { - uint64_t u = 0; - - /* if it's all zeroes, don't report it */ - - for (m = 0; m < context->count_threads; m++) { - struct lws_context_per_thread *pt = &context->pt[m]; - - u |= pt->lws_stats[n]; - } - if (!u) - continue; - - p = bufline; - p += lws_snprintf(p, lws_ptr_diff(end, p), "%28s: ", - stat_names[n]); - - for (m = 0; m < context->count_threads; m++) - quantify(context, m, p, lws_ptr_diff(end, p), n, &summary[n]); - - lwsl_notice("%s\n", bufline); - } - - lwsl_notice("Simultaneous SSL restriction: %8d/%d\n", - context->simultaneous_ssl, - context->simultaneous_ssl_restriction); - - lwsl_notice("Live wsi: %8d\n", - context->count_wsi_allocated); - - while (v) { - if (v->lserv_wsi && - v->lserv_wsi->position_in_fds_table != LWS_NO_FDS_POS) { - - struct lws_context_per_thread *pt = - &context->pt[(int)v->lserv_wsi->tsi]; - struct lws_pollfd *pfd; - - pfd = &pt->fds[v->lserv_wsi->position_in_fds_table]; - - lwsl_notice(" Listen port %d actual POLLIN: %d\n", - v->listen_port, - (int)pfd->events & LWS_POLLIN); - } - - v = v->vhost_next; - } - - for (n = 0; n < context->count_threads; n++) { - struct lws_context_per_thread *pt = &context->pt[n]; - struct lws *wl; - int m = 0; - - lwsl_notice("PT %d\n", n + 1); - - lws_pt_lock(pt, __func__); - - lwsl_notice(" AH in use / max: %d / %d\n", - pt->http.ah_count_in_use, - context->max_http_header_pool); - - wl = pt->http.ah_wait_list; - while (wl) { - m++; - wl = wl->http.ah_wait_list; - } - - lwsl_notice(" AH wait list count / actual: %d / %d\n", - pt->http.ah_wait_list_length, m); - - lws_pt_unlock(pt); - } - -#if defined(LWS_WITH_PEER_LIMITS) - m = 0; - for (n = 0; n < (int)context->pl_hash_elements; n++) { - lws_start_foreach_llp(struct lws_peer **, peer, - context->pl_hash_table[n]) { - m++; - } lws_end_foreach_llp(peer, next); - } - - lwsl_notice(" Peers: total active %d\n", m); - if (m > 10) { - m = 10; - lwsl_notice(" (showing 10 peers only)\n"); - } - - if (m) { - for (n = 0; n < (int)context->pl_hash_elements; n++) { - char buf[72]; - - lws_start_foreach_llp(struct lws_peer **, peer, - context->pl_hash_table[n]) { - struct lws_peer *df = *peer; - - if (!lws_plat_inet_ntop(df->af, df->addr, buf, - sizeof(buf) - 1)) - strcpy(buf, "unknown"); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - lwsl_notice(" peer %s: count wsi: %d, count ah: %d\n", - buf, df->count_wsi, - df->http.count_ah); -#else - lwsl_notice(" peer %s: count wsi: %d\n", - buf, df->count_wsi); -#endif - - if (!--m) - break; - } lws_end_foreach_llp(peer, next); - } - } -#endif - - lwsl_notice("\n"); -} - -void -lws_stats_bump(struct lws_context_per_thread *pt, int i, uint64_t bump) -{ - lws_pt_stats_lock(pt); - pt->lws_stats[i] += bump; - if (i != LWSSTATS_C_SERVICE_ENTRY) { - pt->updated = 1; - pt->context->updated = 1; - } - lws_pt_stats_unlock(pt); -} - -void -lws_stats_max(struct lws_context_per_thread *pt, int index, uint64_t val) -{ - lws_pt_stats_lock(pt); - if (val > pt->lws_stats[index]) { - pt->lws_stats[index] = val; - pt->updated = 1; - pt->context->updated = 1; - } - lws_pt_stats_unlock(pt); -} - -#endif - - diff -Nru libwebsockets-4.0.20/lib/core-net/vhost.c libwebsockets-2.4.2/lib/core-net/vhost.c --- libwebsockets-4.0.20/lib/core-net/vhost.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/vhost.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1583 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -const struct lws_role_ops *available_roles[] = { -#if defined(LWS_ROLE_H2) - &role_ops_h2, -#endif -#if defined(LWS_ROLE_H1) - &role_ops_h1, -#endif -#if defined(LWS_ROLE_WS) - &role_ops_ws, -#endif -#if defined(LWS_ROLE_DBUS) - &role_ops_dbus, -#endif -#if defined(LWS_ROLE_RAW_PROXY) - &role_ops_raw_proxy, -#endif -#if defined(LWS_ROLE_MQTT) && defined(LWS_WITH_CLIENT) - &role_ops_mqtt, -#endif - NULL -}; - -const struct lws_event_loop_ops *available_event_libs[] = { -#if defined(LWS_WITH_POLL) - &event_loop_ops_poll, -#endif -#if defined(LWS_WITH_LIBUV) - &event_loop_ops_uv, -#endif -#if defined(LWS_WITH_LIBEVENT) - &event_loop_ops_event, -#endif -#if defined(LWS_WITH_LIBEV) - &event_loop_ops_ev, -#endif - NULL -}; - -#if defined(LWS_WITH_ABSTRACT) -const struct lws_protocols *available_abstract_protocols[] = { -#if defined(LWS_ROLE_RAW) - &protocol_abs_client_raw_skt, -#endif - NULL -}; -#endif - -#if defined(LWS_WITH_SECURE_STREAMS) -const struct lws_protocols *available_secstream_protocols[] = { -#if defined(LWS_ROLE_H1) - &protocol_secstream_h1, -#endif -#if defined(LWS_ROLE_H2) - &protocol_secstream_h2, -#endif -#if defined(LWS_ROLE_WS) - &protocol_secstream_ws, -#endif -#if defined(LWS_ROLE_MQTT) - &protocol_secstream_mqtt, -#endif - NULL -}; -#endif - -static const char * const mount_protocols[] = { - "http://", - "https://", - "file://", - "cgi://", - ">http://", - ">https://", - "callback://" -}; - -const struct lws_role_ops * -lws_role_by_name(const char *name) -{ - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (!strcmp(ar->name, name)) - return ar; - LWS_FOR_EVERY_AVAILABLE_ROLE_END; - - if (!strcmp(name, role_ops_raw_skt.name)) - return &role_ops_raw_skt; - -#if defined(LWS_ROLE_RAW_FILE) - if (!strcmp(name, role_ops_raw_file.name)) - return &role_ops_raw_file; -#endif - - return NULL; -} - -int -lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn) -{ -#if defined(LWS_WITH_TLS) - if (!alpn) - return 0; - - lwsl_info("%s: '%s'\n", __func__, alpn); - - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (ar->alpn && !strcmp(ar->alpn, alpn) && ar->alpn_negotiated) - return ar->alpn_negotiated(wsi, alpn); - LWS_FOR_EVERY_AVAILABLE_ROLE_END; -#endif - return 0; -} - -int -lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot) -{ - int n; - - /* - * if the vhost is told to bind accepted sockets to a given role, - * then look it up by name and try to bind to the specific role. - */ - if (lws_check_opt(wsi->vhost->options, - LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG) && - wsi->vhost->listen_accept_role) { - const struct lws_role_ops *role = - lws_role_by_name(wsi->vhost->listen_accept_role); - - if (!prot) - prot = wsi->vhost->listen_accept_protocol; - - if (!role) - lwsl_err("%s: can't find role '%s'\n", __func__, - wsi->vhost->listen_accept_role); - - if (role && role->adoption_bind) { - n = role->adoption_bind(wsi, type, prot); - if (n < 0) - return -1; - if (n) /* did the bind */ - return 0; - } - - if (type & _LWS_ADOPT_FINISH) { - lwsl_debug("%s: leaving bound to role %s\n", __func__, - wsi->role_ops->name); - return 0; - } - - - lwsl_warn("%s: adoption bind to role '%s', " - "protocol '%s', type 0x%x, failed\n", __func__, - wsi->vhost->listen_accept_role, prot, type); - } - - /* - * Otherwise ask each of the roles in order of preference if they - * want to bind to this accepted socket - */ - - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (ar->adoption_bind && ar->adoption_bind(wsi, type, prot)) - return 0; - LWS_FOR_EVERY_AVAILABLE_ROLE_END; - - /* fall back to raw socket role if, eg, h1 not configured */ - - if (role_ops_raw_skt.adoption_bind && - role_ops_raw_skt.adoption_bind(wsi, type, prot)) - return 0; - -#if defined(LWS_ROLE_RAW_FILE) - - /* fall back to raw file role if, eg, h1 not configured */ - - if (role_ops_raw_file.adoption_bind && - role_ops_raw_file.adoption_bind(wsi, type, prot)) - return 0; -#endif - - return 1; -} - -#if defined(LWS_WITH_CLIENT) -int -lws_role_call_client_bind(struct lws *wsi, - const struct lws_client_connect_info *i) -{ - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (ar->client_bind) { - int m = ar->client_bind(wsi, i); - if (m < 0) - return m; - if (m) - return 0; - } - LWS_FOR_EVERY_AVAILABLE_ROLE_END; - - /* fall back to raw socket role if, eg, h1 not configured */ - - if (role_ops_raw_skt.client_bind && - role_ops_raw_skt.client_bind(wsi, i)) - return 0; - - return 1; -} -#endif - -void * -lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, - const struct lws_protocols *prot, int size) -{ - int n = 0; - - /* allocate the vh priv array only on demand */ - if (!vhost->protocol_vh_privs) { - vhost->protocol_vh_privs = (void **)lws_zalloc( - vhost->count_protocols * sizeof(void *), - "protocol_vh_privs"); - if (!vhost->protocol_vh_privs) - return NULL; - } - - while (n < vhost->count_protocols && &vhost->protocols[n] != prot) - n++; - - if (n == vhost->count_protocols) { - n = 0; - while (n < vhost->count_protocols && - strcmp(vhost->protocols[n].name, prot->name)) - n++; - - if (n == vhost->count_protocols) - return NULL; - } - - vhost->protocol_vh_privs[n] = lws_zalloc(size, "vh priv"); - return vhost->protocol_vh_privs[n]; -} - -void * -lws_protocol_vh_priv_get(struct lws_vhost *vhost, - const struct lws_protocols *prot) -{ - int n = 0; - - if (!vhost || !vhost->protocol_vh_privs || !prot) - return NULL; - - while (n < vhost->count_protocols && &vhost->protocols[n] != prot) - n++; - - if (n == vhost->count_protocols) { - n = 0; - while (n < vhost->count_protocols && - strcmp(vhost->protocols[n].name, prot->name)) - n++; - - if (n == vhost->count_protocols) { - lwsl_err("%s: unknown protocol %p\n", __func__, prot); - return NULL; - } - } - - return vhost->protocol_vh_privs[n]; -} - -const struct lws_protocol_vhost_options * -lws_vhost_protocol_options(struct lws_vhost *vh, const char *name) -{ - const struct lws_protocol_vhost_options *pvo = vh->pvo; - - if (!name) - return NULL; - - while (pvo) { - if (!strcmp(pvo->name, name)) - return pvo; - pvo = pvo->next; - } - - return NULL; -} - -int -lws_protocol_init_vhost(struct lws_vhost *vh, int *any) -{ - const struct lws_protocol_vhost_options *pvo, *pvo1; - struct lws *wsi = vh->context->pt[0].fake_wsi; - int n; - - wsi->context = vh->context; - wsi->vhost = vh; - - /* initialize supported protocols on this vhost */ - - for (n = 0; n < vh->count_protocols; n++) { - wsi->protocol = &vh->protocols[n]; - if (!vh->protocols[n].name) - continue; - pvo = lws_vhost_protocol_options(vh, vh->protocols[n].name); - if (pvo) { - /* - * linked list of options specific to - * vh + protocol - */ - pvo1 = pvo; - pvo = pvo1->options; - - while (pvo) { - lwsl_debug( - " vhost \"%s\", " - "protocol \"%s\", " - "option \"%s\"\n", - vh->name, - vh->protocols[n].name, - pvo->name); - - if (!strcmp(pvo->name, "default")) { - lwsl_info("Setting default " - "protocol for vh %s to %s\n", - vh->name, - vh->protocols[n].name); - vh->default_protocol_index = n; - } - if (!strcmp(pvo->name, "raw")) { - lwsl_info("Setting raw " - "protocol for vh %s to %s\n", - vh->name, - vh->protocols[n].name); - vh->raw_protocol_index = n; - } - pvo = pvo->next; - } - - pvo = pvo1->options; - } - -#if defined(LWS_WITH_TLS) - if (any) - *any |= !!vh->tls.ssl_ctx; -#endif - - /* - * inform all the protocols that they are doing their - * one-time initialization if they want to. - * - * NOTE the wsi is all zeros except for the context, vh - * + protocol ptrs so lws_get_context(wsi) etc can work - */ - if (vh->protocols[n].callback(wsi, - LWS_CALLBACK_PROTOCOL_INIT, NULL, - (void *)pvo, 0)) { - if (vh->protocol_vh_privs[n]) { - lws_free(vh->protocol_vh_privs[n]); - vh->protocol_vh_privs[n] = NULL; - } - lwsl_err("%s: protocol %s failed init\n", - __func__, vh->protocols[n].name); - - return 1; - } - } - - vh->created_vhost_protocols = 1; - - return 0; -} - -/* - * inform every vhost that hasn't already done it, that - * his protocols are initializing - */ -int -lws_protocol_init(struct lws_context *context) -{ - struct lws_vhost *vh = context->vhost_list; - int any = 0; - - if (context->doing_protocol_init) - return 0; - - context->doing_protocol_init = 1; - - lwsl_info("%s\n", __func__); - - while (vh) { - - /* only do the protocol init once for a given vhost */ - if (vh->created_vhost_protocols || - (lws_check_opt(vh->options, LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT))) - goto next; - - if (lws_protocol_init_vhost(vh, &any)) - return 1; -next: - vh = vh->vhost_next; - } - - context->doing_protocol_init = 0; - - if (!context->protocol_init_done && lws_finalize_startup(context)) - return 1; - - context->protocol_init_done = 1; - -#if defined(LWS_WITH_SERVER) - if (any) - lws_tls_check_all_cert_lifetimes(context); -#endif - - return 0; -} - - -/* list of supported protocols and callbacks */ - -static const struct lws_protocols protocols_dummy[] = { - /* first protocol must always be HTTP handler */ - - { - "http-only", /* name */ - lws_callback_http_dummy, /* callback */ - 0, /* per_session_data_size */ - 0, /* rx_buffer_size */ - 0, /* id */ - NULL, /* user */ - 0 /* tx_packet_size */ - }, - /* - * the other protocols are provided by lws plugins - */ - { NULL, NULL, 0, 0, 0, NULL, 0} /* terminator */ -}; - - -#ifdef LWS_PLAT_OPTEE -#undef LWS_HAVE_GETENV -#endif - -struct lws_vhost * -lws_create_vhost(struct lws_context *context, - const struct lws_context_creation_info *info) -{ - struct lws_vhost *vh = lws_zalloc(sizeof(*vh), "create vhost"), - **vh1 = &context->vhost_list; - const struct lws_http_mount *mounts; - const struct lws_protocols *pcols = info->protocols; -#ifdef LWS_WITH_PLUGINS - struct lws_plugin *plugin = context->plugin_list; -#endif - struct lws_protocols *lwsp; - int m, f = !info->pvo, fx = 0, abs_pcol_count = 0, sec_pcol_count = 0; - char buf[96]; -#if defined(LWS_CLIENT_HTTP_PROXYING) && defined(LWS_WITH_CLIENT) \ - && defined(LWS_HAVE_GETENV) - char *p; -#endif -#if defined(LWS_WITH_SYS_ASYNC_DNS) - extern struct lws_protocols lws_async_dns_protocol; -#endif - int n; - - if (!vh) - return NULL; - -#if LWS_MAX_SMP > 1 - pthread_mutex_init(&vh->lock, NULL); -#endif - - if (!pcols && !info->pprotocols) - pcols = &protocols_dummy[0]; - - vh->context = context; - if (!info->vhost_name) - vh->name = "default"; - else - vh->name = info->vhost_name; - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - vh->http.error_document_404 = info->error_document_404; -#endif - - if (lws_check_opt(info->options, LWS_SERVER_OPTION_ONLY_RAW)) - lwsl_info("%s set to only support RAW\n", vh->name); - - vh->iface = info->iface; -#if !defined(LWS_PLAT_FREERTOS) && !defined(OPTEE_TA) && !defined(WIN32) - vh->bind_iface = info->bind_iface; -#endif - /* apply the context default lws_retry */ - - if (info->retry_and_idle_policy) - vh->retry_policy = info->retry_and_idle_policy; - else - vh->retry_policy = &context->default_retry; - - /* - * let's figure out how many protocols the user is handing us, using the - * old or new way depending on what he gave us - */ - - if (!pcols) - for (vh->count_protocols = 0; - info->pprotocols[vh->count_protocols]; - vh->count_protocols++) - ; - else - for (vh->count_protocols = 0; - pcols[vh->count_protocols].callback; - vh->count_protocols++) - ; - - vh->options = info->options; - vh->pvo = info->pvo; - vh->headers = info->headers; - vh->user = info->user; - vh->finalize = info->finalize; - vh->finalize_arg = info->finalize_arg; - vh->listen_accept_role = info->listen_accept_role; - vh->listen_accept_protocol = info->listen_accept_protocol; - vh->unix_socket_perms = info->unix_socket_perms; - - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (ar->init_vhost) - if (ar->init_vhost(vh, info)) - return NULL; - LWS_FOR_EVERY_AVAILABLE_ROLE_END; - - - if (info->keepalive_timeout) - vh->keepalive_timeout = info->keepalive_timeout; - else - vh->keepalive_timeout = 5; - - if (info->timeout_secs_ah_idle) - vh->timeout_secs_ah_idle = info->timeout_secs_ah_idle; - else - vh->timeout_secs_ah_idle = 10; - -#if defined(LWS_WITH_TLS) - - vh->tls.alpn = info->alpn; - vh->tls.ssl_info_event_mask = info->ssl_info_event_mask; - - if (info->ecdh_curve) - lws_strncpy(vh->tls.ecdh_curve, info->ecdh_curve, - sizeof(vh->tls.ecdh_curve)); - - /* carefully allocate and take a copy of cert + key paths if present */ - n = 0; - if (info->ssl_cert_filepath) - n += (int)strlen(info->ssl_cert_filepath) + 1; - if (info->ssl_private_key_filepath) - n += (int)strlen(info->ssl_private_key_filepath) + 1; - - if (n) { - vh->tls.key_path = vh->tls.alloc_cert_path = - lws_malloc(n, "vh paths"); - if (info->ssl_cert_filepath) { - n = (int)strlen(info->ssl_cert_filepath) + 1; - memcpy(vh->tls.alloc_cert_path, - info->ssl_cert_filepath, n); - vh->tls.key_path += n; - } - if (info->ssl_private_key_filepath) - memcpy(vh->tls.key_path, info->ssl_private_key_filepath, - strlen(info->ssl_private_key_filepath) + 1); - } -#endif - -#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS) - fx = 1; -#endif -#if defined(LWS_WITH_ABSTRACT) - abs_pcol_count = (int)LWS_ARRAY_SIZE(available_abstract_protocols) - 1; -#endif -#if defined(LWS_WITH_SECURE_STREAMS) - sec_pcol_count = (int)LWS_ARRAY_SIZE(available_secstream_protocols) - 1; -#endif - - /* - * give the vhost a unified list of protocols including: - * - * - internal, async_dns if enabled (first vhost only) - * - internal, abstracted ones - * - the ones that came from plugins - * - his user protocols - */ - lwsp = lws_zalloc(sizeof(struct lws_protocols) * - (vh->count_protocols + - abs_pcol_count + sec_pcol_count + - context->plugin_protocol_count + - fx + 1), - "vhost-specific plugin table"); - if (!lwsp) { - lwsl_err("OOM\n"); - return NULL; - } - - /* - * 1: user protocols (from pprotocols or protocols) - */ - - m = vh->count_protocols; - if (!pcols) { - for (n = 0; n < m; n++) - memcpy(&lwsp[n], info->pprotocols[n], sizeof(lwsp[0])); - } else - memcpy(lwsp, pcols, sizeof(struct lws_protocols) * m); - - /* - * 2: abstract protocols - */ -#if defined(LWS_WITH_ABSTRACT) - for (n = 0; n < abs_pcol_count; n++) { - memcpy(&lwsp[m++], available_abstract_protocols[n], - sizeof(*lwsp)); - vh->count_protocols++; - } -#endif - /* - * 3: async dns protocol (first vhost only) - */ -#if defined(LWS_WITH_SYS_ASYNC_DNS) - if (!context->vhost_list) { - memcpy(&lwsp[m++], &lws_async_dns_protocol, - sizeof(struct lws_protocols)); - vh->count_protocols++; - } -#endif - -#if defined(LWS_WITH_SECURE_STREAMS) - for (n = 0; n < sec_pcol_count; n++) { - memcpy(&lwsp[m++], available_secstream_protocols[n], - sizeof(*lwsp)); - vh->count_protocols++; - } -#endif - - /* - * 3: For compatibility, all protocols enabled on vhost if only - * the default vhost exists. Otherwise only vhosts who ask - * for a protocol get it enabled. - */ - - if (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) - f = 0; - (void)f; -#ifdef LWS_WITH_PLUGINS - if (plugin) { - while (plugin) { - for (n = 0; n < plugin->caps.count_protocols; n++) { - /* - * for compatibility's sake, no pvo implies - * allow all protocols - */ - if (f || lws_vhost_protocol_options(vh, - plugin->caps.protocols[n].name)) { - memcpy(&lwsp[m], - &plugin->caps.protocols[n], - sizeof(struct lws_protocols)); - m++; - vh->count_protocols++; - } - } - plugin = plugin->list; - } - } -#endif - -#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS) - memcpy(&lwsp[m++], &lws_ws_proxy, sizeof(*lwsp)); - vh->count_protocols++; -#endif - - vh->protocols = lwsp; - vh->allocated_vhost_protocols = 1; - - vh->same_vh_protocol_owner = (struct lws_dll2_owner *) - lws_zalloc(sizeof(struct lws_dll2_owner) * - vh->count_protocols, "same vh list"); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - vh->http.mount_list = info->mounts; -#endif - -#ifdef LWS_WITH_UNIX_SOCK - if (LWS_UNIX_SOCK_ENABLED(vh)) { - lwsl_info("Creating Vhost '%s' path \"%s\", %d protocols\n", - vh->name, vh->iface, vh->count_protocols); - } else -#endif - { - switch(info->port) { - case CONTEXT_PORT_NO_LISTEN: - strcpy(buf, "(serving disabled)"); - break; - case CONTEXT_PORT_NO_LISTEN_SERVER: - strcpy(buf, "(no listener)"); - break; - default: - lws_snprintf(buf, sizeof(buf), "port %u", info->port); - break; - } - lwsl_info("Creating Vhost '%s' %s, %d protocols, IPv6 %s\n", - vh->name, buf, vh->count_protocols, - LWS_IPV6_ENABLED(vh) ? "on" : "off"); - } - mounts = info->mounts; - while (mounts) { - (void)mount_protocols[0]; - lwsl_info(" mounting %s%s to %s\n", - mount_protocols[mounts->origin_protocol], - mounts->origin, mounts->mountpoint); - - mounts = mounts->mount_next; - } - - vh->listen_port = info->port; - -#if defined(LWS_WITH_SOCKS5) - vh->socks_proxy_port = 0; - vh->socks_proxy_address[0] = '\0'; -#endif - -#if defined(LWS_WITH_CLIENT) && defined(LWS_CLIENT_HTTP_PROXYING) - /* either use proxy from info, or try get it from env var */ -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - vh->http.http_proxy_port = 0; - vh->http.http_proxy_address[0] = '\0'; - /* http proxy */ - if (info->http_proxy_address) { - /* override for backwards compatibility */ - if (info->http_proxy_port) - vh->http.http_proxy_port = info->http_proxy_port; - lws_set_proxy(vh, info->http_proxy_address); - } else -#endif - { -#ifdef LWS_HAVE_GETENV -#if defined(__COVERITY__) - p = NULL; -#else - p = getenv("http_proxy"); /* coverity[tainted_scalar] */ - if (p) { - lws_strncpy(buf, p, sizeof(buf)); - lws_set_proxy(vh, buf); - } -#endif -#endif - } -#endif -#if defined(LWS_WITH_SOCKS5) - lws_socks5c_ads_server(vh, info); -#endif - - vh->ka_time = info->ka_time; - vh->ka_interval = info->ka_interval; - vh->ka_probes = info->ka_probes; - - if (vh->options & LWS_SERVER_OPTION_STS) - lwsl_notice(" STS enabled\n"); - -#ifdef LWS_WITH_ACCESS_LOG - if (info->log_filepath) { - vh->log_fd = lws_open(info->log_filepath, - O_CREAT | O_APPEND | O_RDWR, 0600); - if (vh->log_fd == (int)LWS_INVALID_FILE) { - lwsl_err("unable to open log filepath %s\n", - info->log_filepath); - goto bail; - } -#ifndef WIN32 - if (context->uid != -1) - if (chown(info->log_filepath, context->uid, - context->gid) == -1) - lwsl_err("unable to chown log file %s\n", - info->log_filepath); -#endif - } else - vh->log_fd = (int)LWS_INVALID_FILE; -#endif - if (lws_context_init_server_ssl(info, vh)) { - lwsl_err("%s: lws_context_init_server_ssl failed\n", __func__); - goto bail1; - } - if (lws_context_init_client_ssl(info, vh)) { - lwsl_err("%s: lws_context_init_client_ssl failed\n", __func__); - goto bail1; - } -#if defined(LWS_WITH_SERVER) - lws_context_lock(context, "create_vhost"); - n = _lws_vhost_init_server(info, vh); - lws_context_unlock(context); - if (n < 0) { - lwsl_err("init server failed\n"); - goto bail1; - } -#endif - n = !!context->vhost_list; - - while (1) { - if (!(*vh1)) { - *vh1 = vh; - break; - } - vh1 = &(*vh1)->vhost_next; - }; - -#if defined(LWS_WITH_SYS_ASYNC_DNS) - if (!n && lws_async_dns_init(context)) - goto bail1; -#endif - - /* for the case we are adding a vhost much later, after server init */ - - if (context->protocol_init_done) - if (lws_protocol_init(context)) { - lwsl_err("%s: lws_protocol_init failed\n", __func__); - goto bail1; - } - - return vh; - -bail1: - lws_vhost_destroy(vh); - - return NULL; - -#ifdef LWS_WITH_ACCESS_LOG -bail: - lws_free(vh); -#endif - - return NULL; -} - -int -lws_init_vhost_client_ssl(const struct lws_context_creation_info *info, - struct lws_vhost *vhost) -{ - struct lws_context_creation_info i; - - memcpy(&i, info, sizeof(i)); - i.port = CONTEXT_PORT_NO_LISTEN; - - return lws_context_init_client_ssl(&i, vhost); -} - -void -lws_cancel_service_pt(struct lws *wsi) -{ - lws_plat_pipe_signal(wsi); -} - -void -lws_cancel_service(struct lws_context *context) -{ - struct lws_context_per_thread *pt = &context->pt[0]; - short m = context->count_threads; - - if (context->being_destroyed1) - return; - - lwsl_info("%s\n", __func__); - - while (m--) { - if (pt->pipe_wsi) - lws_plat_pipe_signal(pt->pipe_wsi); - pt++; - } -} - -int -lws_create_event_pipes(struct lws_context *context) -{ - struct lws *wsi; - int n; - - /* - * Create the pt event pipes... these are unique in that they are - * not bound to a vhost or protocol (both are NULL) - */ - -#if LWS_MAX_SMP > 1 - for (n = 0; n < context->count_threads; n++) { -#else - n = 0; - { -#endif - if (context->pt[n].pipe_wsi) - return 0; - - wsi = lws_zalloc(sizeof(*wsi), "event pipe wsi"); - if (!wsi) { - lwsl_err("%s: Out of mem\n", __func__); - return 1; - } - wsi->context = context; - lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_pipe); - wsi->protocol = NULL; - wsi->tsi = n; - wsi->vhost = NULL; - wsi->event_pipe = 1; - wsi->desc.sockfd = LWS_SOCK_INVALID; - context->pt[n].pipe_wsi = wsi; - context->count_wsi_allocated++; - - if (!lws_plat_pipe_create(wsi)) { - /* - * platform code returns 0 if it actually created pipes - * and initialized pt->dummy_pipe_fds[]. If it used - * some other mechanism outside of signaling in the - * normal event loop, we skip treating the pipe as - * related to dummy_pipe_fds[], adding it to the fds, - * etc. - */ - - wsi->desc.sockfd = context->pt[n].dummy_pipe_fds[0]; - lwsl_debug("event pipe fd %d\n", wsi->desc.sockfd); - - if (context->event_loop_ops->sock_accept) - if (context->event_loop_ops->sock_accept(wsi)) - return 1; - - if (__insert_wsi_socket_into_fds(context, wsi)) - return 1; - } - } - - return 0; -} - -void -lws_destroy_event_pipe(struct lws *wsi) -{ - lwsl_info("%s\n", __func__); - - if (lws_socket_is_valid(wsi->desc.sockfd)) - __remove_wsi_socket_from_fds(wsi); - - if (!wsi->context->event_loop_ops->destroy_wsi && - wsi->context->event_loop_ops->wsi_logical_close) { - wsi->context->event_loop_ops->wsi_logical_close(wsi); - lws_plat_pipe_close(wsi); - return; - } - - if (wsi->context->event_loop_ops->destroy_wsi) - wsi->context->event_loop_ops->destroy_wsi(wsi); - lws_plat_pipe_close(wsi); - wsi->context->count_wsi_allocated--; - lws_free(wsi); -} - - -void -lws_vhost_destroy1(struct lws_vhost *vh) -{ - struct lws_context *context = vh->context; - - lwsl_info("%s\n", __func__); - - lws_context_lock(context, "vhost destroy 1"); /* ---------- context { */ - - if (vh->being_destroyed) - goto out; - - lws_vhost_lock(vh); /* -------------- vh { */ - -#if defined(LWS_WITH_NETWORK) - /* - * PHASE 1: take down or reassign any listen wsi - * - * Are there other vhosts that are piggybacking on our listen socket? - * If so we need to hand the listen socket off to one of the others - * so it will remain open. - * - * If not, leave it attached to the closing vhost, the vh being marked - * being_destroyed will defeat any service and it will get closed in - * later phases. - */ - - if (vh->lserv_wsi) - lws_start_foreach_ll(struct lws_vhost *, v, - context->vhost_list) { - if (v != vh && - !v->being_destroyed && - v->listen_port == vh->listen_port && - ((!v->iface && !vh->iface) || - (v->iface && vh->iface && - !strcmp(v->iface, vh->iface)))) { - /* - * this can only be a listen wsi, which is - * restricted... it has no protocol or other - * bindings or states. So we can simply - * swap it to a vhost that has the same - * iface + port, but is not closing. - */ - assert(v->lserv_wsi == NULL); - v->lserv_wsi = vh->lserv_wsi; - - lwsl_notice("%s: listen skt from %s to %s\n", - __func__, vh->name, v->name); - - if (v->lserv_wsi) { - lws_vhost_unbind_wsi(vh->lserv_wsi); - lws_vhost_bind_wsi(v, v->lserv_wsi); - } - - break; - } - } lws_end_foreach_ll(v, vhost_next); - -#endif - - lws_vhost_unlock(vh); /* } vh -------------- */ - - /* - * lws_check_deferred_free() will notice there is a vhost that is - * marked for destruction during the next 1s, for all tsi. - * - * It will start closing all wsi on this vhost. When the last wsi - * is closed, it will trigger lws_vhost_destroy2() - */ - -out: - lws_context_unlock(context); /* --------------------------- context { */ -} - -#if defined(LWS_WITH_ABSTRACT) -static int -destroy_ais(struct lws_dll2 *d, void *user) -{ - lws_abs_t *ai = lws_container_of(d, lws_abs_t, abstract_instances); - - lws_abs_destroy_instance(&ai); - - return 0; -} -#endif - -void -__lws_vhost_destroy2(struct lws_vhost *vh) -{ - const struct lws_protocols *protocol = NULL; - struct lws_context *context = vh->context; - struct lws_deferred_free *df; - struct lws wsi; - int n; - - vh->being_destroyed = 0; - -#if defined(LWS_WITH_CLIENT) - /* - * destroy any wsi that are associated with us but have no socket - * (and will otherwise be missed for destruction) - */ - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - vh->vh_awaiting_socket_owner.head) { - struct lws *w = - lws_container_of(d, struct lws, vh_awaiting_socket); - - lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, - "awaiting skt"); - - } lws_end_foreach_dll_safe(d, d1); -#endif - - /* - * destroy any pending timed events - */ - - while (vh->timed_vh_protocol_list) - __lws_timed_callback_remove(vh, vh->timed_vh_protocol_list); - - /* - * let the protocols destroy the per-vhost protocol objects - */ - - memset(&wsi, 0, sizeof(wsi)); - wsi.context = vh->context; - wsi.vhost = vh; /* not a real bound wsi */ - protocol = vh->protocols; - if (protocol && vh->created_vhost_protocols) { - n = 0; - while (n < vh->count_protocols) { - wsi.protocol = protocol; - - if (protocol->callback) - protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY, - NULL, NULL, 0); - protocol++; - n++; - } - } - - /* - * remove vhost from context list of vhosts - */ - - lws_start_foreach_llp(struct lws_vhost **, pv, context->vhost_list) { - if (*pv == vh) { - *pv = vh->vhost_next; - break; - } - } lws_end_foreach_llp(pv, vhost_next); - - /* add ourselves to the pending destruction list */ - - vh->vhost_next = vh->context->vhost_pending_destruction_list; - vh->context->vhost_pending_destruction_list = vh; - - lwsl_info("%s: %p\n", __func__, vh); - - /* if we are still on deferred free list, remove ourselves */ - - lws_start_foreach_llp(struct lws_deferred_free **, pdf, - context->deferred_free_list) { - if ((*pdf)->payload == vh) { - df = *pdf; - *pdf = df->next; - lws_free(df); - break; - } - } lws_end_foreach_llp(pdf, next); - - /* remove ourselves from the pending destruction list */ - - lws_start_foreach_llp(struct lws_vhost **, pv, - context->vhost_pending_destruction_list) { - if ((*pv) == vh) { - *pv = (*pv)->vhost_next; - break; - } - } lws_end_foreach_llp(pv, vhost_next); - - /* - * Free all the allocations associated with the vhost - */ - - protocol = vh->protocols; - if (protocol) { - n = 0; - while (n < vh->count_protocols) { - if (vh->protocol_vh_privs && - vh->protocol_vh_privs[n]) { - lws_free(vh->protocol_vh_privs[n]); - vh->protocol_vh_privs[n] = NULL; - } - protocol++; - n++; - } - } - if (vh->protocol_vh_privs) - lws_free(vh->protocol_vh_privs); - lws_ssl_SSL_CTX_destroy(vh); - lws_free(vh->same_vh_protocol_owner); - - if ( -#if defined(LWS_WITH_PLUGINS) - context->plugin_list || -#endif - (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) || - vh->allocated_vhost_protocols) - lws_free((void *)vh->protocols); -#if defined(LWS_WITH_NETWORK) - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (ar->destroy_vhost) - ar->destroy_vhost(vh); - LWS_FOR_EVERY_AVAILABLE_ROLE_END; -#endif - -#ifdef LWS_WITH_ACCESS_LOG - if (vh->log_fd != (int)LWS_INVALID_FILE) - close(vh->log_fd); -#endif - -#if defined (LWS_WITH_TLS) - lws_free_set_NULL(vh->tls.alloc_cert_path); -#endif - -#if LWS_MAX_SMP > 1 - pthread_mutex_destroy(&vh->lock); -#endif - -#if defined(LWS_WITH_UNIX_SOCK) - if (LWS_UNIX_SOCK_ENABLED(vh)) { - n = unlink(vh->iface); - if (n) - lwsl_info("Closing unix socket %s: errno %d\n", - vh->iface, errno); - } -#endif - /* - * although async event callbacks may still come for wsi handles with - * pending close in the case of asycn event library like libuv, - * they do not refer to the vhost. So it's safe to free. - */ - - if (vh->finalize) - vh->finalize(vh, vh->finalize_arg); - -#if defined(LWS_WITH_ABSTRACT) - /* - * abstract instances - */ - - lws_dll2_foreach_safe(&vh->abstract_instances_owner, NULL, destroy_ais); -#endif - - lwsl_info(" %s: Freeing vhost %p\n", __func__, vh); - - memset(vh, 0, sizeof(*vh)); - lws_free(vh); -} - -/* - * each service thread calls this once a second or so - */ - -int -lws_check_deferred_free(struct lws_context *context, int tsi, int force) -{ - struct lws_context_per_thread *pt; - int n; - - /* - * If we see a vhost is being destroyed, forcibly close every wsi on - * this tsi associated with this vhost. That will include the listen - * socket if it is still associated with the closing vhost. - * - * For SMP, we do this once per tsi per destroyed vhost. The reference - * counting on the vhost as the bound wsi close will notice that there - * are no bound wsi left, that vhost destruction can complete, - * and perform it. It doesn't matter which service thread does that - * because there is nothing left using the vhost to conflict. - */ - - lws_context_lock(context, "check deferred free"); /* ------ context { */ - - lws_start_foreach_ll_safe(struct lws_vhost *, v, context->vhost_list, vhost_next) { - if (v->being_destroyed -#if LWS_MAX_SMP > 1 - && !v->close_flow_vs_tsi[tsi] -#endif - ) { - - pt = &context->pt[tsi]; - - lws_pt_lock(pt, "vhost removal"); /* -------------- pt { */ - -#if LWS_MAX_SMP > 1 - v->close_flow_vs_tsi[tsi] = 1; -#endif - - for (n = 0; (unsigned int)n < pt->fds_count; n++) { - struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd); - if (!wsi) - continue; - if (wsi->vhost != v) - continue; - - __lws_close_free_wsi(wsi, - LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY, - "vh destroy" - /* no protocol close */); - n--; - } - - lws_pt_unlock(pt); /* } pt -------------- */ - } - } lws_end_foreach_ll_safe(v); - - - lws_context_unlock(context); /* } context ------------------- */ - - return 0; -} - - -void -lws_vhost_destroy(struct lws_vhost *vh) -{ - struct lws_deferred_free *df = lws_malloc(sizeof(*df), "deferred free"); - struct lws_context *context = vh->context; - - if (!df) - return; - - lws_context_lock(context, __func__); /* ------ context { */ - - lws_vhost_destroy1(vh); - - lwsl_debug("%s: count_bound_wsi %d\n", __func__, vh->count_bound_wsi); - - if (!vh->count_bound_wsi) { - /* - * After listen handoff, there are already no wsi bound to this - * vhost by any pt: nothing can be servicing any wsi belonging - * to it any more. - * - * Finalize the vh destruction immediately - */ - __lws_vhost_destroy2(vh); - lws_free(df); - - goto out; - } - - /* part 2 is deferred to allow all the handle closes to complete */ - - df->next = vh->context->deferred_free_list; - df->deadline = lws_now_secs(); - df->payload = vh; - vh->context->deferred_free_list = df; - -out: - lws_context_unlock(context); /* } context ------------------- */ -} - - -void * -lws_vhost_user(struct lws_vhost *vhost) -{ - return vhost->user; -} - -int -lws_get_vhost_listen_port(struct lws_vhost *vhost) -{ - return vhost->listen_port; -} - -#if defined(LWS_WITH_SERVER) -void -lws_context_deprecate(struct lws_context *context, lws_reload_func cb) -{ - struct lws_vhost *vh = context->vhost_list, *vh1; - - /* - * "deprecation" means disable the context from accepting any new - * connections and free up listen sockets to be used by a replacement - * context. - * - * Otherwise the deprecated context remains operational, until its - * number of connected sockets falls to zero, when it is deleted. - */ - - /* for each vhost, close his listen socket */ - - while (vh) { - struct lws *wsi = vh->lserv_wsi; - - if (wsi) { - wsi->socket_is_permanently_unusable = 1; - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "ctx deprecate"); - wsi->context->deprecation_pending_listen_close_count++; - /* - * other vhosts can share the listen port, they - * point to the same wsi. So zap those too. - */ - vh1 = context->vhost_list; - while (vh1) { - if (vh1->lserv_wsi == wsi) - vh1->lserv_wsi = NULL; - vh1 = vh1->vhost_next; - } - } - vh = vh->vhost_next; - } - - context->deprecated = 1; - context->deprecation_cb = cb; -} -#endif - -#if defined(LWS_WITH_NETWORK) - -struct lws_vhost * -lws_get_vhost_by_name(struct lws_context *context, const char *name) -{ - lws_start_foreach_ll(struct lws_vhost *, v, - context->vhost_list) { - if (!strcmp(v->name, name)) - return v; - - } lws_end_foreach_ll(v, vhost_next); - - return NULL; -} - - -#if defined(LWS_WITH_CLIENT) -/* - * This is the logic checking to see if the new connection wsi should have a - * pipelining or muxing relationship with an existing "active connection" to - * the same endpoint under the same conditions. - * - * This was originally in the client code but since the list is held on the - * vhost (to ensure the same client tls ctx is involved) it's cleaner in vhost.c - */ - -int -lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin) -{ - if (!lws_dll2_is_detached(&wsi->dll2_cli_txn_queue)) { - struct lws *w = lws_container_of( - wsi->dll2_cli_txn_queue.owner, struct lws, - dll2_cli_txn_queue_owner); - *nwsi = w; - - return ACTIVE_CONNS_QUEUED; - } - -#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT) - if (wsi->mux.parent_wsi) { - /* - * We already decided... - */ - - *nwsi = wsi->mux.parent_wsi; - - return ACTIVE_CONNS_MUXED; - } -#endif - - lws_vhost_lock(wsi->vhost); /* ----------------------------------- { */ - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - wsi->vhost->dll_cli_active_conns_owner.head) { - struct lws *w = lws_container_of(d, struct lws, - dll_cli_active_conns); - - lwsl_debug("%s: check %p %p %s %s %d %d\n", __func__, wsi, w, - adsin, w->cli_hostname_copy, wsi->c_port, w->c_port); - - if (w != wsi && - /* - * "same internet protocol"... this is a bit tricky, - * since h2 start out as h1 - */ - (w->role_ops == wsi->role_ops || - (lwsi_role_http(w) && lwsi_role_http(wsi))) && - w->cli_hostname_copy && - !strcmp(adsin, w->cli_hostname_copy) && -#if defined(LWS_WITH_TLS) - (wsi->tls.use_ssl & LCCSCF_USE_SSL) == - (w->tls.use_ssl & LCCSCF_USE_SSL) && -#endif - wsi->c_port == w->c_port) { - - /* - * There's already an active connection. - * - * The server may have told the existing active - * connection that it doesn't support pipelining... - */ - if (w->keepalive_rejected) { - lwsl_notice("defeating pipelining due to no " - "keepalive on server\n"); - goto solo; - } - -#if defined(LWS_WITH_HTTP2) - /* - * h2: if in usable state already: just use it without - * going through the queue - */ - if (w->client_h2_alpn && w->client_mux_migrated && - (lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS || - lwsi_state(w) == LRS_ESTABLISHED || - lwsi_state(w) == LRS_IDLING)) { - - lwsl_notice("%s: just join h2 directly 0x%x\n", - __func__, lwsi_state(w)); - - if (lwsi_state(w) == LRS_IDLING) { - // lwsi_set_state(w, LRS_ESTABLISHED); - _lws_generic_transaction_completed_active_conn(&w); - } - - //lwsi_set_state(w, LRS_H1C_ISSUE_HANDSHAKE2); - - wsi->client_h2_alpn = 1; - lws_wsi_h2_adopt(w, wsi); - lws_vhost_unlock(wsi->vhost); /* } ---------- */ - - *nwsi = w; - - return ACTIVE_CONNS_MUXED; - } -#endif - -#if defined(LWS_ROLE_MQTT) - /* - * MQTT: if in usable state already: just use it without - * going through the queue - */ - - if (lwsi_role_mqtt(wsi) && w->client_mux_migrated && - lwsi_state(w) == LRS_ESTABLISHED) { - - if (lws_wsi_mqtt_adopt(w, wsi)) { - lwsl_notice("%s: join mqtt directly\n", __func__); - lws_dll2_remove(&wsi->dll2_cli_txn_queue); - wsi->client_mux_substream = 1; - - lws_vhost_unlock(wsi->vhost); /* } ---------- */ - - - return ACTIVE_CONNS_MUXED; - } - } -#endif - - /* - * If the connection is viable but not yet in a usable - * state, let's attach ourselves to it and wait for it - * to get there or fail. - */ - - lwsl_notice("%s: apply %p to txn queue on %p state 0x%lx\n", - __func__, wsi, w, (unsigned long)w->wsistate); - /* - * ...let's add ourselves to his transaction queue... - * we are adding ourselves at the TAIL - */ - lws_dll2_add_tail(&wsi->dll2_cli_txn_queue, - &w->dll2_cli_txn_queue_owner); - - if (lwsi_state(w) == LRS_IDLING) { - // lwsi_set_state(w, LRS_ESTABLISHED); - _lws_generic_transaction_completed_active_conn(&w); - } - - /* - * For eg, h1 next we'd pipeline our headers out on him, - * and wait for our turn at client transaction_complete - * to take over parsing the rx. - */ - lws_vhost_unlock(wsi->vhost); /* } ---------- */ - - *nwsi = w; - - return ACTIVE_CONNS_QUEUED; - } - - } lws_end_foreach_dll_safe(d, d1); - -solo: - lws_vhost_unlock(wsi->vhost); /* } ---------------------------------- */ - - /* there is nobody already connected in the same way */ - - return ACTIVE_CONNS_SOLO; -} -#endif -#endif diff -Nru libwebsockets-4.0.20/lib/core-net/wsi.c libwebsockets-2.4.2/lib/core-net/wsi.c --- libwebsockets-4.0.20/lib/core-net/wsi.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/wsi.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1374 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 (_DEBUG) -void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role) -{ - wsi->wsistate = (wsi->wsistate & (~LWSI_ROLE_MASK)) | role; - - lwsl_debug("lwsi_set_role(%p, 0x%lx)\n", wsi, - (unsigned long)wsi->wsistate); -} - -void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs) -{ - wsi->wsistate = (wsi->wsistate & (~LRS_MASK)) | lrs; - - lwsl_debug("lwsi_set_state(%p, 0x%lx)\n", wsi, - (unsigned long)wsi->wsistate); -} -#endif - - -void -lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi) -{ - if (wsi->vhost == vh) - return; - lws_context_lock(vh->context, __func__); /* ---------- context { */ - wsi->vhost = vh; - vh->count_bound_wsi++; - lws_context_unlock(vh->context); /* } context ---------- */ - lwsl_debug("%s: vh %s: wsi %s/%s, count_bound_wsi %d\n", __func__, - vh->name, wsi->role_ops ? wsi->role_ops->name : "none", - wsi->protocol ? wsi->protocol->name : "none", - vh->count_bound_wsi); - assert(wsi->vhost->count_bound_wsi > 0); -} - -void -lws_vhost_unbind_wsi(struct lws *wsi) -{ - if (!wsi->vhost) - return; - - lws_context_lock(wsi->context, __func__); /* ---------- context { */ - - assert(wsi->vhost->count_bound_wsi > 0); - wsi->vhost->count_bound_wsi--; - lwsl_debug("%s: vh %s: count_bound_wsi %d\n", __func__, - wsi->vhost->name, wsi->vhost->count_bound_wsi); - - if (!wsi->vhost->count_bound_wsi && - wsi->vhost->being_destroyed) { - /* - * We have closed all wsi that were bound to this vhost - * by any pt: nothing can be servicing any wsi belonging - * to it any more. - * - * Finalize the vh destruction - */ - __lws_vhost_destroy2(wsi->vhost); - } - wsi->vhost = NULL; - - lws_context_unlock(wsi->context); /* } context ---------- */ -} - -struct lws * -lws_get_network_wsi(struct lws *wsi) -{ - if (!wsi) - return NULL; - -#if defined(LWS_WITH_HTTP2) || defined(LWS_ROLE_MQTT) - if (!wsi->mux_substream -#if defined(LWS_WITH_CLIENT) - && !wsi->client_mux_substream -#endif - ) - return wsi; - - while (wsi->mux.parent_wsi) - wsi = wsi->mux.parent_wsi; -#endif - - return wsi; -} - - -const struct lws_protocols * -lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name) -{ - int n; - - for (n = 0; n < vh->count_protocols; n++) - if (vh->protocols[n].name && !strcmp(name, vh->protocols[n].name)) - return &vh->protocols[n]; - - return NULL; -} - -int -lws_callback_all_protocol(struct lws_context *context, - const struct lws_protocols *protocol, int reason) -{ - struct lws_context_per_thread *pt = &context->pt[0]; - unsigned int n, m = context->count_threads; - struct lws *wsi; - - while (m--) { - for (n = 0; n < pt->fds_count; n++) { - wsi = wsi_from_fd(context, pt->fds[n].fd); - if (!wsi) - continue; - if (wsi->protocol == protocol) - protocol->callback(wsi, reason, wsi->user_space, - NULL, 0); - } - pt++; - } - - return 0; -} - -int -lws_callback_all_protocol_vhost_args(struct lws_vhost *vh, - const struct lws_protocols *protocol, int reason, - void *argp, size_t len) -{ - struct lws_context *context = vh->context; - struct lws_context_per_thread *pt = &context->pt[0]; - unsigned int n, m = context->count_threads; - struct lws *wsi; - - while (m--) { - for (n = 0; n < pt->fds_count; n++) { - wsi = wsi_from_fd(context, pt->fds[n].fd); - if (!wsi) - continue; - if (wsi->vhost == vh && (wsi->protocol == protocol || - !protocol)) - wsi->protocol->callback(wsi, reason, - wsi->user_space, argp, len); - } - pt++; - } - - return 0; -} - -int -lws_callback_all_protocol_vhost(struct lws_vhost *vh, - const struct lws_protocols *protocol, int reason) -{ - return lws_callback_all_protocol_vhost_args(vh, protocol, reason, NULL, 0); -} - -int -lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len) -{ - int n; - - for (n = 0; n < wsi->vhost->count_protocols; n++) - if (wsi->vhost->protocols[n].callback(wsi, reason, NULL, in, len)) - return 1; - - return 0; -} - -int -lws_callback_vhost_protocols_vhost(struct lws_vhost *vh, int reason, void *in, - size_t len) -{ - int n; - struct lws *wsi = lws_zalloc(sizeof(*wsi), "fake wsi"); - - if (!wsi) - return 1; - - wsi->context = vh->context; - lws_vhost_bind_wsi(vh, wsi); - - for (n = 0; n < wsi->vhost->count_protocols; n++) { - wsi->protocol = &vh->protocols[n]; - if (wsi->protocol->callback(wsi, reason, NULL, in, len)) { - lws_free(wsi); - return 1; - } - } - - lws_free(wsi); - - return 0; -} - - -int -lws_rx_flow_control(struct lws *wsi, int _enable) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - int en = _enable; - - // h2 ignores rx flow control atm - if (lwsi_role_h2(wsi) || wsi->mux_substream || - lwsi_role_h2_ENCAPSULATION(wsi)) - return 0; // !!! - - lwsl_info("%s: %p 0x%x\n", __func__, wsi, _enable); - - if (!(_enable & LWS_RXFLOW_REASON_APPLIES)) { - /* - * convert user bool style to bitmap style... in user simple - * bool style _enable = 0 = flow control it, = 1 = allow rx - */ - en = LWS_RXFLOW_REASON_APPLIES | LWS_RXFLOW_REASON_USER_BOOL; - if (_enable & 1) - en |= LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT; - } - - lws_pt_lock(pt, __func__); - - /* any bit set in rxflow_bitmap DISABLEs rxflow control */ - if (en & LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT) - wsi->rxflow_bitmap &= ~(en & 0xff); - else - wsi->rxflow_bitmap |= en & 0xff; - - if ((LWS_RXFLOW_PENDING_CHANGE | (!wsi->rxflow_bitmap)) == - wsi->rxflow_change_to) - goto skip; - - wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | - (!wsi->rxflow_bitmap); - - lwsl_info("%s: %p: bitmap 0x%x: en 0x%x, ch 0x%x\n", __func__, wsi, - wsi->rxflow_bitmap, en, wsi->rxflow_change_to); - - if (_enable & LWS_RXFLOW_REASON_FLAG_PROCESS_NOW || - !wsi->rxflow_will_be_applied) { - en = __lws_rx_flow_control(wsi); - lws_pt_unlock(pt); - - return en; - } - -skip: - lws_pt_unlock(pt); - - return 0; -} - -void -lws_rx_flow_allow_all_protocol(const struct lws_context *context, - const struct lws_protocols *protocol) -{ - const struct lws_context_per_thread *pt = &context->pt[0]; - struct lws *wsi; - unsigned int n, m = context->count_threads; - - while (m--) { - for (n = 0; n < pt->fds_count; n++) { - wsi = wsi_from_fd(context, pt->fds[n].fd); - if (!wsi) - continue; - if (wsi->protocol == protocol) - lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW); - } - pt++; - } -} - -int user_callback_handle_rxflow(lws_callback_function callback_function, - struct lws *wsi, - enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - int n; - - wsi->rxflow_will_be_applied = 1; - n = callback_function(wsi, reason, user, in, len); - wsi->rxflow_will_be_applied = 0; - if (!n) - n = __lws_rx_flow_control(wsi); - - return n; -} - -int -__lws_rx_flow_control(struct lws *wsi) -{ - struct lws *wsic = wsi->child_list; - - // h2 ignores rx flow control atm - if (lwsi_role_h2(wsi) || wsi->mux_substream || - lwsi_role_h2_ENCAPSULATION(wsi)) - return 0; // !!! - - /* if he has children, do those if they were changed */ - while (wsic) { - if (wsic->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE) - __lws_rx_flow_control(wsic); - - wsic = wsic->sibling_list; - } - - /* there is no pending change */ - if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) - return 0; - - /* stuff is still buffered, not ready to really accept new input */ - if (lws_buflist_next_segment_len(&wsi->buflist, NULL)) { - /* get ourselves called back to deal with stashed buffer */ - lws_callback_on_writable(wsi); - // return 0; - } - - /* now the pending is cleared, we can change rxflow state */ - - wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE; - - lwsl_info("rxflow: wsi %p change_to %d\n", wsi, - wsi->rxflow_change_to & LWS_RXFLOW_ALLOW); - - /* adjust the pollfd for this wsi */ - - if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) { - lwsl_info("%s: reenable POLLIN\n", __func__); - // lws_buflist_describe(&wsi->buflist, NULL, __func__); - if (__lws_change_pollfd(wsi, 0, LWS_POLLIN)) { - lwsl_info("%s: fail\n", __func__); - return -1; - } - } else - if (__lws_change_pollfd(wsi, LWS_POLLIN, 0)) - return -1; - - return 0; -} - - -const struct lws_protocols * -lws_get_protocol(struct lws *wsi) -{ - return wsi->protocol; -} - - -int -lws_ensure_user_space(struct lws *wsi) -{ - if (!wsi->protocol) - return 0; - - /* allocate the per-connection user memory (if any) */ - - if (wsi->protocol->per_session_data_size && !wsi->user_space) { - wsi->user_space = lws_zalloc( - wsi->protocol->per_session_data_size, "user space"); - if (wsi->user_space == NULL) { - lwsl_err("%s: OOM\n", __func__); - return 1; - } - } else - lwsl_debug("%s: %p protocol pss %lu, user_space=%p\n", __func__, - wsi, (long)wsi->protocol->per_session_data_size, - wsi->user_space); - return 0; -} - -void * -lws_adjust_protocol_psds(struct lws *wsi, size_t new_size) -{ - ((struct lws_protocols *)lws_get_protocol(wsi))->per_session_data_size = - new_size; - - if (lws_ensure_user_space(wsi)) - return NULL; - - return wsi->user_space; -} - -int -lws_get_tsi(struct lws *wsi) -{ - return (int)wsi->tsi; -} - -int -lws_is_ssl(struct lws *wsi) -{ -#if defined(LWS_WITH_TLS) - return wsi->tls.use_ssl & LCCSCF_USE_SSL; -#else - (void)wsi; - return 0; -#endif -} - -#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS) -lws_tls_conn* -lws_get_ssl(struct lws *wsi) -{ - return wsi->tls.ssl; -} -#endif - -int -lws_partial_buffered(struct lws *wsi) -{ - return lws_has_buffered_out(wsi); -} - -lws_fileofs_t -lws_get_peer_write_allowance(struct lws *wsi) -{ - if (!wsi->role_ops->tx_credit) - return -1; - return wsi->role_ops->tx_credit(wsi, LWSTXCR_US_TO_PEER, 0); -} - -void -lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state, - const struct lws_role_ops *ops) -{ -#if (_LWS_ENABLED_LOGS & LLL_DEBUG) - const char *name = "(unset)"; -#endif - wsi->wsistate = role | state; - if (ops) - wsi->role_ops = ops; -#if (_LWS_ENABLED_LOGS & LLL_DEBUG) - if (wsi->role_ops) - name = wsi->role_ops->name; - lwsl_debug("%s: %p: wsistate 0x%lx, ops %s\n", __func__, wsi, - (unsigned long)wsi->wsistate, name); -#endif -} - -int -lws_parse_uri(char *p, const char **prot, const char **ads, int *port, - const char **path) -{ - const char *end; - char unix_skt = 0; - - /* cut up the location into address, port and path */ - *prot = p; - while (*p && (*p != ':' || p[1] != '/' || p[2] != '/')) - p++; - if (!*p) { - end = p; - p = (char *)*prot; - *prot = end; - } else { - *p = '\0'; - p += 3; - } - if (*p == '+') /* unix skt */ - unix_skt = 1; - - *ads = p; - if (!strcmp(*prot, "http") || !strcmp(*prot, "ws")) - *port = 80; - else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss")) - *port = 443; - - if (*p == '[') { - ++(*ads); - while (*p && *p != ']') - p++; - if (*p) - *p++ = '\0'; - } else - while (*p && *p != ':' && (unix_skt || *p != '/')) - p++; - - if (*p == ':') { - *p++ = '\0'; - *port = atoi(p); - while (*p && *p != '/') - p++; - } - *path = "/"; - if (*p) { - *p++ = '\0'; - if (*p) - *path = p; - } - - return 0; -} - -/* ... */ - -const char * -lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len) -{ - int n = 0, sl = (int)strlen(name); - - while (lws_hdr_copy_fragment(wsi, buf, len, - WSI_TOKEN_HTTP_URI_ARGS, n) >= 0) { - - if (!strncmp(buf, name, sl)) - return buf + sl; - - n++; - } - - return NULL; -} - - -#if defined(LWS_WITHOUT_EXTENSIONS) - -/* we need to provide dummy callbacks for internal exts - * so user code runs when faced with a lib compiled with - * extensions disabled. - */ - -int -lws_extension_callback_pm_deflate(struct lws_context *context, - const struct lws_extension *ext, - struct lws *wsi, - enum lws_extension_callback_reasons reason, - void *user, void *in, size_t len) -{ - (void)context; - (void)ext; - (void)wsi; - (void)reason; - (void)user; - (void)in; - (void)len; - - return 0; -} - -int -lws_set_extension_option(struct lws *wsi, const char *ext_name, - const char *opt_name, const char *opt_val) -{ - return -1; -} -#endif - -int -lws_is_cgi(struct lws *wsi) { -#ifdef LWS_WITH_CGI - return !!wsi->http.cgi; -#else - return 0; -#endif -} - -const struct lws_protocol_vhost_options * -lws_pvo_search(const struct lws_protocol_vhost_options *pvo, const char *name) -{ - while (pvo) { - if (!strcmp(pvo->name, name)) - break; - - pvo = pvo->next; - } - - return pvo; -} - -int -lws_pvo_get_str(void *in, const char *name, const char **result) -{ - const struct lws_protocol_vhost_options *pv = - lws_pvo_search((const struct lws_protocol_vhost_options *)in, - name); - - if (!pv) - return 1; - - *result = (const char *)pv->value; - - return 0; -} - -int -lws_broadcast(struct lws_context_per_thread *pt, int reason, void *in, size_t len) -{ - struct lws_vhost *v = pt->context->vhost_list; - int n, ret = 0; - - pt->fake_wsi->context = pt->context; - - while (v) { - const struct lws_protocols *p = v->protocols; - pt->fake_wsi->vhost = v; /* not a real bound wsi */ - - for (n = 0; n < v->count_protocols; n++) { - pt->fake_wsi->protocol = p; - if (p->callback && - p->callback(pt->fake_wsi, reason, NULL, in, len)) - ret |= 1; - p++; - } - v = v->vhost_next; - } - - return ret; -} - -void * -lws_wsi_user(struct lws *wsi) -{ - return wsi->user_space; -} - -void -lws_set_wsi_user(struct lws *wsi, void *data) -{ - if (!wsi->user_space_externally_allocated && wsi->user_space) - lws_free(wsi->user_space); - - wsi->user_space_externally_allocated = 1; - wsi->user_space = data; -} - -struct lws * -lws_get_parent(const struct lws *wsi) -{ - return wsi->parent; -} - -struct lws * -lws_get_child(const struct lws *wsi) -{ - return wsi->child_list; -} - -void * -lws_get_opaque_parent_data(const struct lws *wsi) -{ - return wsi->opaque_parent_data; -} - -void -lws_set_opaque_parent_data(struct lws *wsi, void *data) -{ - wsi->opaque_parent_data = data; -} - -void * -lws_get_opaque_user_data(const struct lws *wsi) -{ - return wsi->opaque_user_data; -} - -void -lws_set_opaque_user_data(struct lws *wsi, void *data) -{ - wsi->opaque_user_data = data; -} - -int -lws_get_child_pending_on_writable(const struct lws *wsi) -{ - return wsi->parent_pending_cb_on_writable; -} - -void -lws_clear_child_pending_on_writable(struct lws *wsi) -{ - wsi->parent_pending_cb_on_writable = 0; -} - - - -const char * -lws_get_vhost_name(struct lws_vhost *vhost) -{ - return vhost->name; -} - -int -lws_get_vhost_port(struct lws_vhost *vhost) -{ - return vhost->listen_port; -} - -void * -lws_get_vhost_user(struct lws_vhost *vhost) -{ - return vhost->user; -} - -const char * -lws_get_vhost_iface(struct lws_vhost *vhost) -{ - return vhost->iface; -} - -lws_sockfd_type -lws_get_socket_fd(struct lws *wsi) -{ - if (!wsi) - return -1; - return wsi->desc.sockfd; -} - - -struct lws_vhost * -lws_vhost_get(struct lws *wsi) -{ - return wsi->vhost; -} - -struct lws_vhost * -lws_get_vhost(struct lws *wsi) -{ - return wsi->vhost; -} - -const struct lws_protocols * -lws_protocol_get(struct lws *wsi) -{ - return wsi->protocol; -} - -#if defined(LWS_WITH_UDP) -const struct lws_udp * -lws_get_udp(const struct lws *wsi) -{ - return wsi->udp; -} -#endif - -struct lws_context * -lws_get_context(const struct lws *wsi) -{ - return wsi->context; -} - -#if defined(LWS_WITH_CLIENT) -int -_lws_generic_transaction_completed_active_conn(struct lws **_wsi) -{ - struct lws *wnew, *wsi = *_wsi; - - /* - * Are we constitutionally capable of having a queue, ie, we are on - * the "active client connections" list? - * - * If not, that's it for us. - */ - - if (lws_dll2_is_detached(&wsi->dll_cli_active_conns)) - return 0; /* no new transaction */ - - /* - * With h1 queuing, the original "active client" moves his attributes - * like fd, ssl, queue and active client list entry to the next guy in - * the queue before closing... it's because the user code knows the - * individual wsi and the action must take place in the correct wsi - * context. Note this means we don't truly pipeline headers. - * - * Trying to keep the original "active client" in place to do the work - * of the wsi breaks down when dealing with queued POSTs otherwise; it's - * also competing with the real mux child arrangements and complicating - * the code. - * - * For that reason, see if we have any queued child now... - */ - - if (!wsi->dll2_cli_txn_queue_owner.head) { - /* - * Nothing pipelined... we should hang around a bit - * in case something turns up... otherwise we'll close - */ - lwsl_info("%s: nothing pipelined waiting\n", __func__); - lwsi_set_state(wsi, LRS_IDLING); - - lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_CONN_IDLE, 5); - - return 0; /* no new transaction right now */ - } - - /* - * We have a queued child wsi we should bequeath our assets to, before - * closing ourself - */ - - lws_vhost_lock(wsi->vhost); - - wnew = lws_container_of(wsi->dll2_cli_txn_queue_owner.head, struct lws, - dll2_cli_txn_queue); - - assert(wsi != wnew); - - lws_dll2_remove(&wnew->dll2_cli_txn_queue); - - assert(lws_socket_is_valid(wsi->desc.sockfd)); - - /* copy the fd */ - wnew->desc = wsi->desc; - - assert(lws_socket_is_valid(wnew->desc.sockfd)); - - /* disconnect the fd from association with old wsi */ - - if (__remove_wsi_socket_from_fds(wsi)) - return -1; - wsi->desc.sockfd = LWS_SOCK_INVALID; - - /* point the fd table entry to new guy */ - - assert(lws_socket_is_valid(wnew->desc.sockfd)); - - if (__insert_wsi_socket_into_fds(wsi->context, wnew)) - return -1; - -#if defined(LWS_WITH_TLS) - /* pass on the tls */ - - wnew->tls = wsi->tls; - wsi->tls.client_bio = NULL; - wsi->tls.ssl = NULL; - wsi->tls.use_ssl = 0; -#endif - - /* take over his copy of his endpoint as an active connection */ - - wnew->cli_hostname_copy = wsi->cli_hostname_copy; - wsi->cli_hostname_copy = NULL; - - - /* - * selected queued guy now replaces the original leader on the - * active client conn list - */ - - lws_dll2_remove(&wsi->dll_cli_active_conns); - lws_dll2_add_tail(&wnew->dll_cli_active_conns, - &wsi->vhost->dll_cli_active_conns_owner); - - /* move any queued guys to queue on new active conn */ - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - wsi->dll2_cli_txn_queue_owner.head) { - struct lws *ww = lws_container_of(d, struct lws, - dll2_cli_txn_queue); - - lws_dll2_remove(&ww->dll2_cli_txn_queue); - lws_dll2_add_tail(&ww->dll2_cli_txn_queue, - &wnew->dll2_cli_txn_queue_owner); - - } lws_end_foreach_dll_safe(d, d1); - - lws_vhost_unlock(wsi->vhost); - - /* - * The original leader who passed on all his powers already can die... - * in the call stack above us there are guys who still want to touch - * him, so have him die next time around the event loop, not now. - */ - - wsi->already_did_cce = 1; /* so the close doesn't trigger a CCE */ - lws_set_timeout(wsi, 1, LWS_TO_KILL_ASYNC); - - /* after the first one, they can only be coming from the queue */ - wnew->transaction_from_pipeline_queue = 1; - - lwsl_notice("%s: pipeline queue passed wsi %p on to queued wsi %p\n", - __func__, wsi, wnew); - - *_wsi = wnew; /* inform caller we swapped */ - - return 1; /* new transaction */ -} -#endif - -int LWS_WARN_UNUSED_RESULT -lws_raw_transaction_completed(struct lws *wsi) -{ - if (lws_has_buffered_out(wsi)) { - /* - * ...so he tried to send something large, but it went out - * as a partial, but he immediately called us to say he wants - * to close the connection. - * - * Defer the close until the last part of the partial is sent. - * - */ - lwsl_debug("%s: %p: deferring due to partial\n", __func__, wsi); - wsi->close_when_buffered_out_drained = 1; - lws_callback_on_writable(wsi); - - return 0; - } - - return -1; -} - -int -lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p, - const char *reason) -{ -// if (wsi->protocol == p) -// return 0; - const struct lws_protocols *vp = wsi->vhost->protocols, *vpo; - - if (wsi->protocol && wsi->protocol_bind_balance) { - wsi->protocol->callback(wsi, - wsi->role_ops->protocol_unbind_cb[!!lwsi_role_server(wsi)], - wsi->user_space, (void *)reason, 0); - wsi->protocol_bind_balance = 0; - } - if (!wsi->user_space_externally_allocated) - lws_free_set_NULL(wsi->user_space); - - lws_same_vh_protocol_remove(wsi); - - wsi->protocol = p; - if (!p) - return 0; - - if (lws_ensure_user_space(wsi)) - return 1; - - if (p > vp && p < &vp[wsi->vhost->count_protocols]) - lws_same_vh_protocol_insert(wsi, (int)(p - vp)); - else { - int n = wsi->vhost->count_protocols; - int hit = 0; - - vpo = vp; - - while (n--) { - if (p->name && vp->name && !strcmp(p->name, vp->name)) { - hit = 1; - lws_same_vh_protocol_insert(wsi, (int)(vp - vpo)); - break; - } - vp++; - } - if (!hit) - lwsl_err("%s: %p is not in vhost '%s' protocols list\n", - __func__, p, wsi->vhost->name); - } - - if (wsi->protocol->callback(wsi, wsi->role_ops->protocol_bind_cb[ - !!lwsi_role_server(wsi)], - wsi->user_space, NULL, 0)) - return 1; - - wsi->protocol_bind_balance = 1; - - return 0; -} - -void -lws_http_close_immortal(struct lws *wsi) -{ - struct lws *nwsi; - - if (!wsi->mux_substream) - return; - - assert(wsi->mux_stream_immortal); - wsi->mux_stream_immortal = 0; - - nwsi = lws_get_network_wsi(wsi); - lwsl_debug("%s: %p %p %d\n", __func__, wsi, nwsi, - nwsi->immortal_substream_count); - assert(nwsi->immortal_substream_count); - nwsi->immortal_substream_count--; - if (!nwsi->immortal_substream_count) - /* - * since we closed the only immortal stream on this nwsi, we - * need to reapply a normal timeout regime to the nwsi - */ - lws_set_timeout(nwsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, - wsi->vhost->keepalive_timeout ? - wsi->vhost->keepalive_timeout : 31); -} - -void -lws_mux_mark_immortal(struct lws *wsi) -{ - struct lws *nwsi; - - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - if (!wsi->mux_substream -#if defined(LWS_WITH_CLIENT) - && !wsi->client_mux_substream -#endif - ) { - lwsl_err("%s: not h2 substream\n", __func__); - return; - } - - nwsi = lws_get_network_wsi(wsi); - - lwsl_debug("%s: %p %p %d\n", __func__, wsi, nwsi, - nwsi->immortal_substream_count); - - wsi->mux_stream_immortal = 1; - assert(nwsi->immortal_substream_count < 255); /* largest count */ - nwsi->immortal_substream_count++; - if (nwsi->immortal_substream_count == 1) - lws_set_timeout(nwsi, NO_PENDING_TIMEOUT, 0); -} - - -int -lws_http_mark_sse(struct lws *wsi) -{ - lws_http_headers_detach(wsi); - lws_mux_mark_immortal(wsi); - - if (wsi->mux_substream) - wsi->h2_stream_carries_sse = 1; - - return 0; -} - -#if defined(LWS_WITH_CLIENT) - -const char * -lws_wsi_client_stash_item(struct lws *wsi, int stash_idx, int hdr_idx) -{ - /* try the generic client stash */ - if (wsi->stash) - return wsi->stash->cis[stash_idx]; - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - /* if not, use the ah stash if applicable */ - return lws_hdr_simple_ptr(wsi, hdr_idx); -#else - return NULL; -#endif -} -#endif - -#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT) - -void -lws_wsi_mux_insert(struct lws *wsi, struct lws *parent_wsi, int sid) -{ - lwsl_info("%s: wsi %p, par %p: assign sid %d (curr %d)\n", __func__, - wsi, parent_wsi, sid, wsi->mux.my_sid); - - if (wsi->mux.my_sid && wsi->mux.my_sid != (unsigned int)sid) - assert(0); - - wsi->mux.my_sid = sid; - wsi->mux.parent_wsi = parent_wsi; - wsi->role_ops = parent_wsi->role_ops; - - /* new guy's sibling is whoever was the first child before */ - wsi->mux.sibling_list = parent_wsi->mux.child_list; - - /* first child is now the new guy */ - parent_wsi->mux.child_list = wsi; - - parent_wsi->mux.child_count++; -} - -struct lws * -lws_wsi_mux_from_id(struct lws *parent_wsi, unsigned int sid) -{ - lws_start_foreach_ll(struct lws *, wsi, parent_wsi->mux.child_list) { - if (wsi->mux.my_sid == sid) - return wsi; - } lws_end_foreach_ll(wsi, mux.sibling_list); - - return NULL; -} - -void -lws_wsi_mux_dump_children(struct lws *wsi) -{ -#if defined(_DEBUG) - if (!wsi->mux.parent_wsi || !lwsl_visible(LLL_INFO)) - return; - - lws_start_foreach_llp(struct lws **, w, - wsi->mux.parent_wsi->mux.child_list) { - lwsl_info(" \\---- child %s %p\n", - (*w)->role_ops ? (*w)->role_ops->name : "?", *w); - assert(*w != (*w)->mux.sibling_list); - } lws_end_foreach_llp(w, mux.sibling_list); -#endif -} - -void -lws_wsi_mux_close_children(struct lws *wsi, int reason) -{ - struct lws *wsi2; - - if (!wsi->mux.child_list) - return; - - lws_start_foreach_llp(struct lws **, w, wsi->mux.child_list) { - lwsl_info(" closing child %p\n", *w); - /* disconnect from siblings */ - wsi2 = (*w)->mux.sibling_list; - assert (wsi2 != *w); - (*w)->mux.sibling_list = NULL; - (*w)->socket_is_permanently_unusable = 1; - __lws_close_free_wsi(*w, reason, "mux child recurse"); - *w = wsi2; - continue; - } lws_end_foreach_llp(w, mux.sibling_list); -} - - -void -lws_wsi_mux_sibling_disconnect(struct lws *wsi) -{ - struct lws *wsi2; - - lws_start_foreach_llp(struct lws **, w, - wsi->mux.parent_wsi->mux.child_list) { - - /* disconnect from siblings */ - if (*w == wsi) { - wsi2 = (*w)->mux.sibling_list; - (*w)->mux.sibling_list = NULL; - *w = wsi2; - lwsl_debug(" %p disentangled from sibling %p\n", - wsi, wsi2); - break; - } - } lws_end_foreach_llp(w, mux.sibling_list); - wsi->mux.parent_wsi->mux.child_count--; - - wsi->mux.parent_wsi = NULL; -} - -void -lws_wsi_mux_dump_waiting_children(struct lws *wsi) -{ -#if defined(_DEBUG) - lwsl_info("%s: %p: children waiting for POLLOUT service:\n", - __func__, wsi); - - wsi = wsi->mux.child_list; - while (wsi) { - lwsl_info(" %c %p: sid %u: 0x%x %s %s\n", - wsi->mux.requested_POLLOUT ? '*' : ' ', - wsi, wsi->mux.my_sid, lwsi_state(wsi), - wsi->role_ops->name, - wsi->protocol ? wsi->protocol->name : "noprotocol"); - - wsi = wsi->mux.sibling_list; - } -#endif -} - -int -lws_wsi_mux_mark_parents_needing_writeable(struct lws *wsi) -{ - struct lws /* *network_wsi = lws_get_network_wsi(wsi), */ *wsi2; - //int already = network_wsi->mux.requested_POLLOUT; - - /* mark everybody above him as requesting pollout */ - - wsi2 = wsi; - while (wsi2) { - wsi2->mux.requested_POLLOUT = 1; - lwsl_info("%s: mark wsi: %p, sid %u, pending writable\n", - __func__, wsi2, wsi2->mux.my_sid); - wsi2 = wsi2->mux.parent_wsi; - } - - return 0; // already; -} - -struct lws * -lws_wsi_mux_move_child_to_tail(struct lws **wsi2) -{ - struct lws *w = *wsi2; - - while (w) { - if (!w->mux.sibling_list) { /* w is the current last */ - lwsl_debug("w=%p, *wsi2 = %p\n", w, *wsi2); - - if (w == *wsi2) /* we are already last */ - break; - - /* last points to us as new last */ - w->mux.sibling_list = *wsi2; - - /* guy pointing to us until now points to - * our old next */ - *wsi2 = (*wsi2)->mux.sibling_list; - - /* we point to nothing because we are last */ - w->mux.sibling_list->mux.sibling_list = NULL; - - /* w becomes us */ - w = w->mux.sibling_list; - break; - } - w = w->mux.sibling_list; - } - - /* clear the waiting for POLLOUT on the guy that was chosen */ - - if (w) - w->mux.requested_POLLOUT = 0; - - return w; -} - -int -lws_wsi_mux_action_pending_writeable_reqs(struct lws *wsi) -{ - struct lws *w = wsi->mux.child_list; - - while (w) { - if (w->mux.requested_POLLOUT) { - if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) - return -1; - return 0; - } - w = w->mux.sibling_list; - } - - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) - return -1; - - return 0; -} - -int -lws_wsi_txc_check_skint(struct lws_tx_credit *txc, int32_t tx_cr) -{ - if (txc->tx_cr <= 0) { - /* - * If other side is not able to cope with us sending any DATA - * so no matter if we have POLLOUT on our side if it's DATA we - * want to send. - */ - - if (!txc->skint) - lwsl_info("%s: %p: skint (%d)\n", __func__, txc, - (int)txc->tx_cr); - - txc->skint = 1; - - return 1; - } - - if (txc->skint) - lwsl_info("%s: %p: unskint (%d)\n", __func__, txc, - (int)txc->tx_cr); - - txc->skint = 0; - - return 0; -} - -#if defined(_DEBUG) -void -lws_wsi_txc_describe(struct lws_tx_credit *txc, const char *at, uint32_t sid) -{ - lwsl_info("%s: %p: %s: sid %d: %speer-to-us: %d, us-to-peer: %d\n", - __func__, txc, at, (int)sid, txc->skint ? "SKINT, " : "", - (int)txc->peer_tx_cr_est, (int)txc->tx_cr); -} -#endif - -int -lws_wsi_tx_credit(struct lws *wsi, char peer_to_us, int add) -{ - if (wsi->role_ops && wsi->role_ops->tx_credit) - return wsi->role_ops->tx_credit(wsi, peer_to_us, add); - - return 0; -} - -/* - * Let the protocol know about incoming tx credit window updates if it's - * managing the flow control manually (it may want to proxy this information) - */ - -int -lws_wsi_txc_report_manual_txcr_in(struct lws *wsi, int32_t bump) -{ - if (!wsi->txc.manual) - /* - * If we don't care about managing it manually, no need to - * report it - */ - return 0; - - return user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_WSI_TX_CREDIT_GET, - wsi->user_space, NULL, (size_t)bump); -} - -#if defined(LWS_WITH_CLIENT) - -int -lws_wsi_mux_apply_queue(struct lws *wsi) -{ - /* we have a transaction queue that wants to pipeline */ - - lws_vhost_lock(wsi->vhost); - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - wsi->dll2_cli_txn_queue_owner.head) { - struct lws *w = lws_container_of(d, struct lws, - dll2_cli_txn_queue); - -#if defined(LWS_ROLE_H2) - if (lwsi_role_http(wsi) && - lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS) { - lwsl_info("%s: cli pipeq %p to be h2\n", __func__, w); - - lwsi_set_state(w, LRS_H1C_ISSUE_HANDSHAKE2); - - /* remove ourselves from client queue */ - lws_dll2_remove(&w->dll2_cli_txn_queue); - - /* attach ourselves as an h2 stream */ - lws_wsi_h2_adopt(wsi, w); - } -#endif - -#if defined(LWS_ROLE_MQTT) - if (lwsi_role_mqtt(wsi) && - lwsi_state(wsi) == LRS_ESTABLISHED) { - lwsl_info("%s: cli pipeq %p to be mqtt\n", __func__, w); - - /* remove ourselves from client queue */ - lws_dll2_remove(&w->dll2_cli_txn_queue); - - /* attach ourselves as an h2 stream */ - lws_wsi_mqtt_adopt(wsi, w); - } -#endif - - } lws_end_foreach_dll_safe(d, d1); - - lws_vhost_unlock(wsi->vhost); - - return 0; -} - -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/core-net/wsi-timeout.c libwebsockets-2.4.2/lib/core-net/wsi-timeout.c --- libwebsockets-4.0.20/lib/core-net/wsi-timeout.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/core-net/wsi-timeout.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,359 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -void -__lws_wsi_remove_from_sul(struct lws *wsi) -{ - //struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - //lwsl_notice("%s: wsi %p, to %p, hr %p\n", __func__, wsi, - // &wsi->sul_timeout.list, &wsi->sul_hrtimer.list); - - // lws_dll2_describe(&pt->pt_sul_owner, "pre-remove"); - lws_dll2_remove(&wsi->sul_timeout.list); - lws_dll2_remove(&wsi->sul_hrtimer.list); - lws_dll2_remove(&wsi->sul_validity.list); - // lws_dll2_describe(&pt->pt_sul_owner, "post-remove"); -} - -/* - * hrtimer - */ - -static void -lws_sul_hrtimer_cb(lws_sorted_usec_list_t *sul) -{ - struct lws *wsi = lws_container_of(sul, struct lws, sul_hrtimer); - - if (wsi->protocol && - wsi->protocol->callback(wsi, LWS_CALLBACK_TIMER, - wsi->user_space, NULL, 0)) - __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "hrtimer cb errored"); -} - -void -__lws_set_timer_usecs(struct lws *wsi, lws_usec_t us) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - wsi->sul_hrtimer.cb = lws_sul_hrtimer_cb; - __lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_hrtimer, us); -} - -void -lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs) -{ - __lws_set_timer_usecs(wsi, usecs); -} - -/* - * wsi timeout - */ - -static void -lws_sul_wsitimeout_cb(lws_sorted_usec_list_t *sul) -{ - struct lws *wsi = lws_container_of(sul, struct lws, sul_timeout); - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - if (wsi->pending_timeout != PENDING_TIMEOUT_USER_OK) - lws_stats_bump(pt, LWSSTATS_C_TIMEOUTS, 1); - - /* no need to log normal idle keepalive timeout */ -// if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE) -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - if (wsi->pending_timeout != PENDING_TIMEOUT_USER_OK) - lwsl_info("wsi %p: TIMEDOUT WAITING on %d " - "(did hdr %d, ah %p, wl %d)\n", - (void *)wsi, wsi->pending_timeout, - wsi->hdr_parsing_completed, wsi->http.ah, - pt->http.ah_wait_list_length); -#if defined(LWS_WITH_CGI) - if (wsi->http.cgi) - lwsl_notice("CGI timeout: %s\n", wsi->http.cgi->summary); -#endif -#else - if (wsi->pending_timeout != PENDING_TIMEOUT_USER_OK) - lwsl_info("wsi %p: TIMEDOUT WAITING on %d ", (void *)wsi, - wsi->pending_timeout); -#endif - /* cgi timeout */ - if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE) - /* - * Since he failed a timeout, he already had a chance to - * do something and was unable to... that includes - * situations like half closed connections. So process - * this "failed timeout" close as a violent death and - * don't try to do protocol cleanup like flush partials. - */ - wsi->socket_is_permanently_unusable = 1; -#if defined(LWS_WITH_CLIENT) - if (lwsi_state(wsi) == LRS_WAITING_SSL) - lws_inform_client_conn_fail(wsi, - (void *)"Timed out waiting SSL", 21); -#endif - - __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "timeout"); -} - -void -__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - wsi->sul_timeout.cb = lws_sul_wsitimeout_cb; - __lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_timeout, - ((lws_usec_t)secs) * LWS_US_PER_SEC); - - lwsl_debug("%s: %p: %d secs, reason %d\n", __func__, wsi, secs, reason); - - wsi->pending_timeout = reason; -} - -void -lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - lws_pt_lock(pt, __func__); - lws_dll2_remove(&wsi->sul_timeout.list); - lws_pt_unlock(pt); - - if (!secs) - return; - - if (secs == LWS_TO_KILL_SYNC) { - lwsl_debug("synchronously killing %p\n", wsi); - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "to sync kill"); - return; - } - - if (secs == LWS_TO_KILL_ASYNC) - secs = 0; - - // assert(!secs || !wsi->mux_stream_immortal); - if (secs && wsi->mux_stream_immortal) - lwsl_err("%s: on immortal stream %d %d\n", __func__, reason, secs); - - lws_pt_lock(pt, __func__); - __lws_set_timeout(wsi, reason, secs); - lws_pt_unlock(pt); -} - -void -lws_set_timeout_us(struct lws *wsi, enum pending_timeout reason, lws_usec_t us) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - lws_pt_lock(pt, __func__); - lws_dll2_remove(&wsi->sul_timeout.list); - lws_pt_unlock(pt); - - if (!us) - return; - - lws_pt_lock(pt, __func__); - __lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_timeout, us); - - lwsl_notice("%s: %p: %llu us, reason %d\n", __func__, wsi, - (unsigned long long)us, reason); - - wsi->pending_timeout = reason; - lws_pt_unlock(pt); -} - -/* requires context + vh lock */ - -int -__lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p) -{ - lws_start_foreach_llp_safe(struct lws_timed_vh_protocol **, pt, - vh->timed_vh_protocol_list, next) { - if (*pt == p) { - *pt = p->next; - lws_dll2_remove(&p->sul.list); - lws_free(p); - - return 0; - } - } lws_end_foreach_llp_safe(pt); - - return 1; -} - -void -lws_sul_timed_callback_vh_protocol_cb(lws_sorted_usec_list_t *sul) -{ - struct lws_timed_vh_protocol *tvp = lws_container_of(sul, - struct lws_timed_vh_protocol, sul); - struct lws_context_per_thread *pt = - &tvp->vhost->context->pt[tvp->tsi_req]; - - pt->fake_wsi->context = tvp->vhost->context; - - pt->fake_wsi->vhost = tvp->vhost; /* not a real bound wsi */ - pt->fake_wsi->protocol = tvp->protocol; - - lwsl_debug("%s: timed cb: vh %s, protocol %s, reason %d\n", __func__, - tvp->vhost->name, tvp->protocol->name, tvp->reason); - - tvp->protocol->callback(pt->fake_wsi, tvp->reason, NULL, NULL, 0); - - __lws_timed_callback_remove(tvp->vhost, tvp); -} - -int -lws_timed_callback_vh_protocol_us(struct lws_vhost *vh, - const struct lws_protocols *prot, int reason, - lws_usec_t us) -{ - struct lws_timed_vh_protocol *p = (struct lws_timed_vh_protocol *) - lws_malloc(sizeof(*p), "timed_vh"); - - if (!p) - return 1; - - memset(p, 0, sizeof(*p)); - - p->tsi_req = lws_pthread_self_to_tsi(vh->context); - if (p->tsi_req < 0) /* not called from a service thread --> tsi 0 */ - p->tsi_req = 0; - - lws_context_lock(vh->context, __func__); /* context ----------------- */ - - p->protocol = prot; - p->reason = reason; - p->vhost = vh; - - p->sul.cb = lws_sul_timed_callback_vh_protocol_cb; - /* list is always at the very top of the sul */ - __lws_sul_insert(&vh->context->pt[p->tsi_req].pt_sul_owner, - (lws_sorted_usec_list_t *)&p->sul.list, us); - - // lwsl_notice("%s: %s.%s %d\n", __func__, vh->name, prot->name, secs); - - lws_vhost_lock(vh); /* vhost ---------------------------------------- */ - p->next = vh->timed_vh_protocol_list; - vh->timed_vh_protocol_list = p; - lws_vhost_unlock(vh); /* -------------------------------------- vhost */ - - lws_context_unlock(vh->context); /* ------------------------- context */ - - return 0; -} - -int -lws_timed_callback_vh_protocol(struct lws_vhost *vh, - const struct lws_protocols *prot, int reason, - int secs) -{ - return lws_timed_callback_vh_protocol_us(vh, prot, reason, - ((lws_usec_t)secs) * LWS_US_PER_SEC); -} - -static void -lws_validity_cb(lws_sorted_usec_list_t *sul) -{ - struct lws *wsi = lws_container_of(sul, struct lws, sul_validity); - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - const lws_retry_bo_t *rbo = wsi->retry_policy; - - /* one of either the ping or hangup validity threshold was crossed */ - - if (wsi->validity_hup) { - lwsl_info("%s: wsi %p: validity too old\n", __func__, wsi); - __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "validity timeout"); - return; - } - - /* schedule a protocol-dependent ping */ - - lwsl_info("%s: wsi %p: scheduling validity check\n", __func__, wsi); - - if (wsi->role_ops && wsi->role_ops->issue_keepalive) - wsi->role_ops->issue_keepalive(wsi, 0); - - /* - * We arrange to come back here after the additional ping to hangup time - * and do the hangup, unless we get validated (by, eg, a PONG) and - * reset the timer - */ - - assert(rbo->secs_since_valid_hangup > rbo->secs_since_valid_ping); - - wsi->validity_hup = 1; - __lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_validity, - ((uint64_t)rbo->secs_since_valid_hangup - - rbo->secs_since_valid_ping) * LWS_US_PER_SEC); -} - -/* - * The role calls this back to actually confirm validity on a particular wsi - * (which may not be the original wsi) - */ - -void -_lws_validity_confirmed_role(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - const lws_retry_bo_t *rbo = wsi->retry_policy; - - if (!rbo || !rbo->secs_since_valid_hangup) - return; - - wsi->validity_hup = 0; - wsi->sul_validity.cb = lws_validity_cb; - - wsi->validity_hup = rbo->secs_since_valid_ping >= - rbo->secs_since_valid_hangup; - - lwsl_info("%s: wsi %p: setting validity timer %ds (hup %d)\n", - __func__, wsi, - wsi->validity_hup ? rbo->secs_since_valid_hangup : - rbo->secs_since_valid_ping, - wsi->validity_hup); - - __lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_validity, - ((uint64_t)(wsi->validity_hup ? - rbo->secs_since_valid_hangup : - rbo->secs_since_valid_ping)) * LWS_US_PER_SEC); -} - -void -lws_validity_confirmed(struct lws *wsi) -{ - /* - * This may be a stream inside a muxed network connection... leave it - * to the role to figure out who actually needs to understand their - * validity was confirmed. - */ - if (!wsi->h2_stream_carries_ws && /* only if not encapsulated */ - wsi->role_ops && wsi->role_ops->issue_keepalive) - wsi->role_ops->issue_keepalive(wsi, 1); -} diff -Nru libwebsockets-4.0.20/lib/event-libs/glib/glib.c libwebsockets-2.4.2/lib/event-libs/glib/glib.c --- libwebsockets-4.0.20/lib/event-libs/glib/glib.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/glib/glib.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,464 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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 - -#define wsi_to_subclass(_w) ((_w)->w_read.glib.source) -#define wsi_to_gsource(_w) ((GSource *)wsi_to_subclass(_w)) -#define pt_to_loop(_pt) ((_pt)->glib.loop) -#define pt_to_g_main_context(_pt) g_main_loop_get_context(pt_to_loop(_pt)) - -static gboolean -lws_glib_idle_timer_cb(void *p); - -static gboolean -lws_glib_hrtimer_cb(void *p); - -static gboolean -lws_glib_check(GSource *src) -{ - struct lws_io_watcher_glib_subclass *sub = - (struct lws_io_watcher_glib_subclass *)src; - - return !!g_source_query_unix_fd(src, sub->tag); -} - -/* - * These helpers attach only to the main_context that belongs to the pt's glib - * mainloop. The simpler g_timeout_add() and g_idle_add() are forbidden - * because they implicitly choose the default main context to attach to - * instead of specifically the loop bound to the pt. - * - * https://developer.gnome.org/programming-guidelines/unstable/main-contexts.html.en#what-is-gmaincontext - */ - -static int -lws_glib_set_idle(struct lws_context_per_thread *pt) -{ - GSource *gis; - - if (pt->glib.idle_tag) - return 0; - - gis = g_idle_source_new(); - if (!gis) - return 1; - - g_source_set_callback(gis, lws_glib_idle_timer_cb, pt, NULL); - pt->glib.idle_tag = g_source_attach(gis, pt_to_g_main_context(pt)); - - return 0; -} - -static int -lws_glib_set_timeout(struct lws_context_per_thread *pt, unsigned int ms) -{ - GSource *gts; - - gts = g_timeout_source_new(ms); - if (!gts) - return 1; - - g_source_set_callback(gts, lws_glib_hrtimer_cb, pt, NULL); - pt->glib.hrtimer_tag = g_source_attach(gts, pt_to_g_main_context(pt)); - - return 0; -} - -static gboolean -lws_glib_dispatch(GSource *src, GSourceFunc x, gpointer userData) -{ - struct lws_io_watcher_glib_subclass *sub = - (struct lws_io_watcher_glib_subclass *)src; - struct lws_context_per_thread *pt; - struct lws_pollfd eventfd; - GIOCondition cond; - - cond = g_source_query_unix_fd(src, sub->tag); - eventfd.revents = cond; - - /* translate from glib event namespace to platform */ - - if (cond & G_IO_IN) - eventfd.revents |= LWS_POLLIN; - if (cond & G_IO_OUT) - eventfd.revents |= LWS_POLLOUT; - if (cond & G_IO_ERR) - eventfd.revents |= LWS_POLLHUP; - if (cond & G_IO_HUP) - eventfd.revents |= LWS_POLLHUP; - - eventfd.events = eventfd.revents; - eventfd.fd = sub->wsi->desc.sockfd; - - lwsl_debug("%s: wsi %p: fd %d, events %d\n", __func__, sub->wsi, - eventfd.fd, eventfd.revents); - - pt = &sub->wsi->context->pt[(int)sub->wsi->tsi]; - if (pt->is_destroyed) - return G_SOURCE_CONTINUE; - - lws_service_fd_tsi(sub->wsi->context, &eventfd, sub->wsi->tsi); - - if (!pt->glib.idle_tag) - lws_glib_set_idle(pt); - - if (pt->destroy_self) - lws_context_destroy(pt->context); - - return G_SOURCE_CONTINUE; -} - -static const GSourceFuncs lws_glib_source_ops = { - .prepare = NULL, - .check = lws_glib_check, - .dispatch = lws_glib_dispatch, - .finalize = NULL, -}; - -/* - * This is the callback for a timer object that is set to the earliest scheduled - * lws event... it services any lws scheduled events that are ready, and then - * resets the event loop timer to the earliest remaining event, if any. - */ - -static gboolean -lws_glib_hrtimer_cb(void *p) -{ - struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p; - unsigned int ms; - lws_usec_t us; - - lws_pt_lock(pt, __func__); - us = __lws_sul_service_ripe(&pt->pt_sul_owner, lws_now_usecs()); - if (us) { - ms = us / LWS_US_PER_MS; - if (!ms) - ms = 1; - - lws_glib_set_timeout(pt, ms); - } - - lws_pt_unlock(pt); - - lws_glib_set_idle(pt); - - return FALSE; /* stop it repeating */ -} - -static gboolean -lws_glib_idle_timer_cb(void *p) -{ - struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p; - - if (pt->is_destroyed) - return FALSE; - - lws_service_do_ripe_rxflow(pt); - lws_glib_hrtimer_cb(pt); - - /* - * is there anybody with pending stuff that needs service forcing? - */ - if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) { - /* -1 timeout means just do forced service */ - _lws_plat_service_forced_tsi(pt->context, pt->tid); - /* still somebody left who wants forced service? */ - if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) - return TRUE; - } - - if (pt->destroy_self) - lws_context_destroy(pt->context); - - /* - * For glib, this disables the idle callback. Otherwise we keep - * coming back here immediately endlessly. - * - * We reenable the idle callback on the next network or scheduled event - */ - - pt->glib.idle_tag = 0; - - return FALSE; -} - -void -lws_glib_sigint_cb(void *ctx) -{ - struct lws_context_per_thread *pt = ctx; - - pt->inside_service = 1; - - if (pt->context->eventlib_signal_cb) { - pt->context->eventlib_signal_cb(NULL, 0); - - return; - } - if (!pt->event_loop_foreign) - g_main_loop_quit(pt_to_loop(pt)); -} - -static int -elops_init_context_glib(struct lws_context *context, - const struct lws_context_creation_info *info) -{ - int n; - - context->eventlib_signal_cb = info->signal_cb; - - for (n = 0; n < context->count_threads; n++) - context->pt[n].w_sigint.context = context; - - return 0; -} - -static int -elops_accept_glib(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - int fd; - - assert(!wsi_to_subclass(wsi)); - - wsi_to_subclass(wsi) = (struct lws_io_watcher_glib_subclass *) - g_source_new((GSourceFuncs *)&lws_glib_source_ops, - sizeof(*wsi_to_subclass(wsi))); - if (!wsi_to_subclass(wsi)) - return 1; - - wsi->w_read.context = wsi->context; - wsi_to_subclass(wsi)->wsi = wsi; - - if (wsi->role_ops->file_handle) - fd = wsi->desc.filefd; - else - fd = wsi->desc.sockfd; - - wsi_to_subclass(wsi)->tag = g_source_add_unix_fd(wsi_to_gsource(wsi), - fd, (GIOCondition)LWS_POLLIN); - wsi->w_read.actual_events = LWS_POLLIN; - - g_source_set_callback(wsi_to_gsource(wsi), - G_SOURCE_FUNC(lws_service_fd), wsi->context, NULL); - - g_source_attach(wsi_to_gsource(wsi), pt_to_g_main_context(pt)); - - return 0; -} - -static int -elops_init_pt_glib(struct lws_context *context, void *_loop, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - struct lws_vhost *vh = context->vhost_list; - GMainLoop *loop = (GMainLoop *)_loop; - - if (!loop) - loop = g_main_loop_new(NULL, 0); - else - context->pt[tsi].event_loop_foreign = 1; - - if (!loop) { - lwsl_err("%s: creating glib loop failed\n", __func__); - - return -1; - } - - pt->glib.loop = loop; - - /* - * Initialize all events with the listening sockets - * and register a callback for read operations - */ - - while (vh) { - if (vh->lserv_wsi) - elops_accept_glib(vh->lserv_wsi); - - vh = vh->vhost_next; - } - - lws_glib_set_idle(pt); - - /* Register the signal watcher unless it's a foreign loop */ - - if (pt->event_loop_foreign) - return 0; - - pt->glib.sigint_tag = g_unix_signal_add(SIGINT, - G_SOURCE_FUNC(lws_glib_sigint_cb), pt); - - return 0; -} - -/* - * We are changing the event wait for this guy - */ - -static void -elops_io_glib(struct lws *wsi, int flags) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - GIOCondition cond = wsi->w_read.actual_events | G_IO_ERR; - - if (!pt_to_loop(pt) || wsi->context->being_destroyed || pt->is_destroyed) - return; - - /* - * We are being given individual set / clear operations using - * LWS_EV_ common namespace, convert them to glib namespace bitfield - */ - - if (flags & LWS_EV_READ) { - if (flags & LWS_EV_STOP) - cond &= ~(G_IO_IN | G_IO_HUP); - else - cond |= G_IO_IN | G_IO_HUP; - } - - if (flags & LWS_EV_WRITE) { - if (flags & LWS_EV_STOP) - cond &= ~G_IO_OUT; - else - cond |= G_IO_OUT; - } - - wsi->w_read.actual_events = cond; - - lwsl_debug("%s: wsi %p, fd %d, 0x%x/0x%x\n", __func__, wsi, - wsi->desc.sockfd, flags, (int)cond); - - g_source_modify_unix_fd(wsi_to_gsource(wsi), wsi_to_subclass(wsi)->tag, - cond); -} - -static void -elops_run_pt_glib(struct lws_context *context, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - - if (pt_to_loop(pt)) - g_main_loop_run(pt_to_loop(pt)); -} - -static void -elops_destroy_wsi_glib(struct lws *wsi) -{ - struct lws_context_per_thread *pt; - - if (!wsi) - return; - - pt = &wsi->context->pt[(int)wsi->tsi]; - if (pt->is_destroyed) - return; - - if (!wsi_to_gsource(wsi)) - return; - - if (wsi_to_subclass(wsi)->tag) { - g_source_remove_unix_fd(wsi_to_gsource(wsi), - wsi_to_subclass(wsi)->tag); - wsi_to_subclass(wsi)->tag = NULL; - } - - g_source_destroy(wsi_to_gsource(wsi)); - wsi_to_subclass(wsi) = NULL; -} - -static void -elops_destroy_pt_glib(struct lws_context *context, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - struct lws_vhost *vh = context->vhost_list; - - if (!pt_to_loop(pt)) - return; - - /* - * Free all events with the listening sockets - */ - while (vh) { - if (vh->lserv_wsi) - elops_destroy_wsi_glib(vh->lserv_wsi); - - vh = vh->vhost_next; - } - - if (pt->glib.hrtimer_tag) - g_source_remove(pt->glib.hrtimer_tag); - - if (!pt->event_loop_foreign) { - g_main_loop_quit(pt_to_loop(pt)); - g_source_remove(pt->glib.sigint_tag); - g_main_loop_unref(pt_to_loop(pt)); - } - - pt_to_loop(pt) = NULL; -} - -static int -elops_destroy_context2_glib(struct lws_context *context) -{ - struct lws_context_per_thread *pt = &context->pt[0]; - int n; - - for (n = 0; n < (int)context->count_threads; n++) { - if (!pt->event_loop_foreign) - g_main_loop_quit(pt_to_loop(pt)); - pt++; - } - - return 0; -} - -static int -elops_wsi_logical_close_glib(struct lws *wsi) -{ - elops_destroy_wsi_glib(wsi); - - return 0; -} - -struct lws_event_loop_ops event_loop_ops_glib = { - /* name */ "glib", - /* init_context */ elops_init_context_glib, - /* destroy_context1 */ NULL, - /* destroy_context2 */ elops_destroy_context2_glib, - /* init_vhost_listen_wsi */ elops_accept_glib, - /* init_pt */ elops_init_pt_glib, - /* wsi_logical_close */ elops_wsi_logical_close_glib, - /* check_client_connect_ok */ NULL, - /* close_handle_manually */ NULL, - /* accept */ elops_accept_glib, - /* io */ elops_io_glib, - /* run_pt */ elops_run_pt_glib, - /* destroy_pt */ elops_destroy_pt_glib, - /* destroy wsi */ elops_destroy_wsi_glib, - - /* flags */ LELOF_DESTROY_FINAL, -}; diff -Nru libwebsockets-4.0.20/lib/event-libs/glib/private-lib-event-libs-glib.h libwebsockets-2.4.2/lib/event-libs/glib/private-lib-event-libs-glib.h --- libwebsockets-4.0.20/lib/event-libs/glib/private-lib-event-libs-glib.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/glib/private-lib-event-libs-glib.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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_GLIB) -#include -#endif /* LWS_WITH_GLIB */ - -struct lws_pt_eventlibs_glib { - GMainLoop *loop; - guint hrtimer_tag; - guint sigint_tag; - guint idle_tag; -}; - -struct lws_io_watcher_glib_subclass { - GSource base; - struct lws *wsi; - gpointer tag; -}; - -/* - * One of these is embedded in each wsi - */ - -struct lws_io_watcher_glib { - struct lws_io_watcher_glib_subclass *source; /* these are created and destroyed by glib */ -}; - -struct lws_context_eventlibs_glib { - //int placeholder; -}; - -extern struct lws_event_loop_ops event_loop_ops_glib; diff -Nru libwebsockets-4.0.20/lib/event-libs/libev/libev.c libwebsockets-2.4.2/lib/event-libs/libev/libev.c --- libwebsockets-4.0.20/lib/event-libs/libev/libev.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/libev/libev.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,393 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 void -lws_ev_hrtimer_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents) -{ - struct lws_context_per_thread *pt = - (struct lws_context_per_thread *)watcher->data; - lws_usec_t us; - - lws_pt_lock(pt, __func__); - us = __lws_sul_service_ripe(&pt->pt_sul_owner, lws_now_usecs()); - if (us) { - ev_timer_set(&pt->ev.hrtimer, ((float)us) / 1000000.0, 0); - ev_timer_start(pt->ev.io_loop, &pt->ev.hrtimer); - } - lws_pt_unlock(pt); -} - -static void -lws_ev_idle_cb(struct ev_loop *loop, struct ev_idle *handle, int revents) -{ - struct lws_context_per_thread *pt = lws_container_of(handle, - struct lws_context_per_thread, ev.idle); - lws_usec_t us; - int reschedule = 0; - - lws_service_do_ripe_rxflow(pt); - - /* - * is there anybody with pending stuff that needs service forcing? - */ - if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) - /* -1 timeout means just do forced service */ - reschedule = _lws_plat_service_forced_tsi(pt->context, pt->tid); - - /* account for hrtimer */ - - lws_pt_lock(pt, __func__); - us = __lws_sul_service_ripe(&pt->pt_sul_owner, lws_now_usecs()); - if (us) { - ev_timer_set(&pt->ev.hrtimer, ((float)us) / 1000000.0, 0); - ev_timer_start(pt->ev.io_loop, &pt->ev.hrtimer); - } - lws_pt_unlock(pt); - - /* there is nobody who needs service forcing, shut down idle */ - if (!reschedule) - ev_idle_stop(loop, handle); - - if (pt->destroy_self) - lws_context_destroy(pt->context); -} - -static void -lws_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) -{ - struct lws_io_watcher *lws_io = lws_container_of(watcher, - struct lws_io_watcher, ev.watcher); - struct lws_context *context = lws_io->context; - struct lws_context_per_thread *pt; - struct lws_pollfd eventfd; - struct lws *wsi; - - if (revents & EV_ERROR) - return; - - eventfd.fd = watcher->fd; - eventfd.events = 0; - eventfd.revents = EV_NONE; - - if (revents & EV_READ) { - eventfd.events |= LWS_POLLIN; - eventfd.revents |= LWS_POLLIN; - } - if (revents & EV_WRITE) { - eventfd.events |= LWS_POLLOUT; - eventfd.revents |= LWS_POLLOUT; - } - - wsi = wsi_from_fd(context, watcher->fd); - pt = &context->pt[(int)wsi->tsi]; - - lws_service_fd_tsi(context, &eventfd, (int)wsi->tsi); - - ev_idle_start(pt->ev.io_loop, &pt->ev.idle); -} - -void -lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents) -{ - struct lws_context *context = watcher->data; - - if (context->eventlib_signal_cb) { - context->eventlib_signal_cb((void *)watcher, watcher->signum); - - return; - } - ev_break(loop, EVBREAK_ALL); -} - -static int -elops_init_pt_ev(struct lws_context *context, void *_loop, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev.watcher; - struct ev_loop *loop = (struct ev_loop *)_loop; - struct lws_vhost *vh = context->vhost_list; - const char *backend_name; - int status = 0; - int backend; - - lwsl_info("%s: loop %p\n", __func__, _loop); - - if (!loop) - loop = ev_loop_new(0); - else - context->pt[tsi].event_loop_foreign = 1; - - if (!loop) { - lwsl_err("%s: creating event base failed\n", __func__); - - return -1; - } - - pt->ev.io_loop = loop; - - /* - * Initialize the accept w_accept with all the listening sockets - * and register a callback for read operations - */ - while (vh) { - if (vh->lserv_wsi) { - vh->lserv_wsi->w_read.context = context; - vh->w_accept.context = context; - - ev_io_init(&vh->w_accept.ev.watcher, lws_accept_cb, - vh->lserv_wsi->desc.sockfd, EV_READ); - ev_io_start(loop, &vh->w_accept.ev.watcher); - - } - vh = vh->vhost_next; - } - - /* Register the signal watcher unless it's a foreign loop */ - if (!context->pt[tsi].event_loop_foreign) { - ev_signal_init(w_sigint, lws_ev_sigint_cb, SIGINT); - w_sigint->data = context; - ev_signal_start(loop, w_sigint); - } - - backend = ev_backend(loop); - switch (backend) { - case EVBACKEND_SELECT: - backend_name = "select"; - break; - case EVBACKEND_POLL: - backend_name = "poll"; - break; - case EVBACKEND_EPOLL: - backend_name = "epoll"; - break; -#if defined(LWS_HAVE_EVBACKEND_LINUXAIO) - case EVBACKEND_LINUXAIO: - backend_name = "Linux AIO"; - break; -#endif -#if defined(LWS_HAVE_EVBACKEND_IOURING) - case EVBACKEND_IOURING: - backend_name = "Linux io_uring"; - break; -#endif - case EVBACKEND_KQUEUE: - backend_name = "kqueue"; - break; - case EVBACKEND_DEVPOLL: - backend_name = "/dev/poll"; - break; - case EVBACKEND_PORT: - backend_name = "Solaris 10 \"port\""; - break; - default: - backend_name = "Unknown libev backend"; - break; - } - - lwsl_info(" libev backend: %s\n", backend_name); - (void)backend_name; - - ev_timer_init(&pt->ev.hrtimer, lws_ev_hrtimer_cb, 0, 0); - pt->ev.hrtimer.data = pt; - - ev_idle_init(&pt->ev.idle, lws_ev_idle_cb); - - return status; -} - -static void -elops_destroy_pt_ev(struct lws_context *context, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - struct lws_vhost *vh = context->vhost_list; - - while (vh) { - if (vh->lserv_wsi) - ev_io_stop(pt->ev.io_loop, &vh->w_accept.ev.watcher); - vh = vh->vhost_next; - } - - /* static assets */ - - ev_timer_stop(pt->ev.io_loop, &pt->ev.hrtimer); - ev_idle_stop(pt->ev.io_loop, &pt->ev.idle); - - if (!pt->event_loop_foreign) - ev_signal_stop(pt->ev.io_loop, &pt->w_sigint.ev.watcher); -} - -static int -elops_init_context_ev(struct lws_context *context, - const struct lws_context_creation_info *info) -{ - int n; - - context->eventlib_signal_cb = info->signal_cb; - - for (n = 0; n < context->count_threads; n++) - context->pt[n].w_sigint.context = context; - - return 0; -} - -static int -elops_accept_ev(struct lws *wsi) -{ - int fd; - - if (wsi->role_ops->file_handle) - fd = wsi->desc.filefd; - else - fd = wsi->desc.sockfd; - - wsi->w_read.context = wsi->context; - wsi->w_write.context = wsi->context; - - ev_io_init(&wsi->w_read.ev.watcher, lws_accept_cb, fd, EV_READ); - ev_io_init(&wsi->w_write.ev.watcher, lws_accept_cb, fd, EV_WRITE); - - return 0; -} - -static void -elops_io_ev(struct lws *wsi, int flags) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - if (!pt->ev.io_loop || pt->is_destroyed) - return; - - assert((flags & (LWS_EV_START | LWS_EV_STOP)) && - (flags & (LWS_EV_READ | LWS_EV_WRITE))); - - if (flags & LWS_EV_START) { - if (flags & LWS_EV_WRITE) - ev_io_start(pt->ev.io_loop, &wsi->w_write.ev.watcher); - if (flags & LWS_EV_READ) - ev_io_start(pt->ev.io_loop, &wsi->w_read.ev.watcher); - } else { - if (flags & LWS_EV_WRITE) - ev_io_stop(pt->ev.io_loop, &wsi->w_write.ev.watcher); - if (flags & LWS_EV_READ) - ev_io_stop(pt->ev.io_loop, &wsi->w_read.ev.watcher); - } - - if (pt->destroy_self) - lws_context_destroy(pt->context); -} - -static void -elops_run_pt_ev(struct lws_context *context, int tsi) -{ - if (context->pt[tsi].ev.io_loop) - ev_run(context->pt[tsi].ev.io_loop, 0); -} - -static int -elops_destroy_context2_ev(struct lws_context *context) -{ - struct lws_context_per_thread *pt; - int n, m; - - lwsl_debug("%s\n", __func__); - - for (n = 0; n < context->count_threads; n++) { - int budget = 1000; - - pt = &context->pt[n]; - - /* only for internal loops... */ - - if (pt->event_loop_foreign || !pt->ev.io_loop) - continue; - - if (!context->finalize_destroy_after_internal_loops_stopped) { - ev_break(pt->ev.io_loop, EVBREAK_ONE); - continue; - } - while (budget-- && - (m = ev_run(pt->ev.io_loop, 0))) - ; - - ev_loop_destroy(pt->ev.io_loop); - } - - return 0; -} - -static int -elops_init_vhost_listen_wsi_ev(struct lws *wsi) -{ - int fd; - - if (!wsi) { - assert(0); - return 0; - } - - wsi->w_read.context = wsi->context; - wsi->w_write.context = wsi->context; - - if (wsi->role_ops->file_handle) - fd = wsi->desc.filefd; - else - fd = wsi->desc.sockfd; - - ev_io_init(&wsi->w_read.ev.watcher, lws_accept_cb, fd, EV_READ); - ev_io_init(&wsi->w_write.ev.watcher, lws_accept_cb, fd, EV_WRITE); - - elops_io_ev(wsi, LWS_EV_START | LWS_EV_READ); - - return 0; -} - -static void -elops_destroy_wsi_ev(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - ev_io_stop(pt->ev.io_loop, &wsi->w_read.ev.watcher); - ev_io_stop(pt->ev.io_loop, &wsi->w_write.ev.watcher); -} - -struct lws_event_loop_ops event_loop_ops_ev = { - /* name */ "libev", - /* init_context */ elops_init_context_ev, - /* destroy_context1 */ NULL, - /* destroy_context2 */ elops_destroy_context2_ev, - /* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_ev, - /* init_pt */ elops_init_pt_ev, - /* wsi_logical_close */ NULL, - /* check_client_connect_ok */ NULL, - /* close_handle_manually */ NULL, - /* accept */ elops_accept_ev, - /* io */ elops_io_ev, - /* run_pt */ elops_run_pt_ev, - /* destroy_pt */ elops_destroy_pt_ev, - /* destroy wsi */ elops_destroy_wsi_ev, - - /* flags */ 0, -}; diff -Nru libwebsockets-4.0.20/lib/event-libs/libev/private-lib-event-libs-libev.h libwebsockets-2.4.2/lib/event-libs/libev/private-lib-event-libs-libev.h --- libwebsockets-4.0.20/lib/event-libs/libev/private-lib-event-libs-libev.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/libev/private-lib-event-libs-libev.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -#define LWS_EV_REFCOUNT_STATIC_HANDLE_NEW(_x, _ctx) \ - { (_x)->data = _ctx; \ - _ctx->count_event_loop_static_asset_handles++; } -#define LWS_EV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x) \ - ((struct lws_context *)(_x)->data))) -#define LWS_EV_REFCOUNT_STATIC_HANDLE_DESTROYED(_x) \ - (--(LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x)-> \ - count_event_loop_static_asset_handles)) - -struct lws_pt_eventlibs_libev { - struct ev_loop *io_loop; - struct ev_timer hrtimer; - struct ev_idle idle; -}; - -struct lws_io_watcher_libev { - ev_io watcher; -}; - -struct lws_signal_watcher_libev { - ev_signal watcher; -}; - -struct lws_context_eventlibs_libev { - int placeholder; -}; - -extern struct lws_event_loop_ops event_loop_ops_ev; diff -Nru libwebsockets-4.0.20/lib/event-libs/libev.c libwebsockets-2.4.2/lib/event-libs/libev.c --- libwebsockets-4.0.20/lib/event-libs/libev.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/libev.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,236 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +void lws_feature_status_libev(struct lws_context_creation_info *info) +{ + if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBEV)) + lwsl_info("libev support compiled in and enabled\n"); + else + lwsl_info("libev support compiled in but disabled\n"); +} + +static void +lws_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) +{ + struct lws_io_watcher *lws_io = lws_container_of(watcher, + struct lws_io_watcher, ev_watcher); + struct lws_context *context = lws_io->context; + struct lws_pollfd eventfd; + + if (revents & EV_ERROR) + return; + + eventfd.fd = watcher->fd; + eventfd.events = 0; + eventfd.revents = EV_NONE; + + if (revents & EV_READ) { + eventfd.events |= LWS_POLLIN; + eventfd.revents |= LWS_POLLIN; + } + if (revents & EV_WRITE) { + eventfd.events |= LWS_POLLOUT; + eventfd.revents |= LWS_POLLOUT; + } + + lws_service_fd(context, &eventfd); +} + +LWS_VISIBLE void +lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents) +{ + ev_break(loop, EVBREAK_ALL); +} + +LWS_VISIBLE int +lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint, + lws_ev_signal_cb_t *cb) +{ + context->use_ev_sigint = use_ev_sigint; + if (cb) + context->lws_ev_sigint_cb = cb; + else + context->lws_ev_sigint_cb = &lws_ev_sigint_cb; + + return 0; +} + +LWS_VISIBLE int +lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi) +{ + struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev_watcher; + struct ev_io *w_accept = &context->pt[tsi].w_accept.ev_watcher; + struct lws_vhost *vh = context->vhost_list; + const char *backend_name; + int status = 0; + int backend; + + if (!loop) + loop = ev_loop_new(0); + else + context->pt[tsi].ev_loop_foreign = 1; + + context->pt[tsi].io_loop_ev = loop; + + /* + * Initialize the accept w_accept with all the listening sockets + * and register a callback for read operations + */ + while (vh) { + if (vh->lserv_wsi) { + vh->lserv_wsi->w_read.context = context; + ev_io_init(w_accept, lws_accept_cb, + vh->lserv_wsi->desc.sockfd, EV_READ); + } + vh = vh->vhost_next; + } + ev_io_start(context->pt[tsi].io_loop_ev, w_accept); + + /* Register the signal watcher unless the user says not to */ + if (context->use_ev_sigint) { + ev_signal_init(w_sigint, context->lws_ev_sigint_cb, SIGINT); + ev_signal_start(context->pt[tsi].io_loop_ev, w_sigint); + } + backend = ev_backend(loop); + + switch (backend) { + case EVBACKEND_SELECT: + backend_name = "select"; + break; + case EVBACKEND_POLL: + backend_name = "poll"; + break; + case EVBACKEND_EPOLL: + backend_name = "epoll"; + break; + case EVBACKEND_KQUEUE: + backend_name = "kqueue"; + break; + case EVBACKEND_DEVPOLL: + backend_name = "/dev/poll"; + break; + case EVBACKEND_PORT: + backend_name = "Solaris 10 \"port\""; + break; + default: + backend_name = "Unknown libev backend"; + break; + } + + lwsl_info(" libev backend: %s\n", backend_name); + (void)backend_name; + + return status; +} + +void +lws_libev_destroyloop(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + + if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV)) + return; + + if (!pt->io_loop_ev) + return; + + ev_io_stop(pt->io_loop_ev, &pt->w_accept.ev_watcher); + if (context->use_ev_sigint) + ev_signal_stop(pt->io_loop_ev, + &pt->w_sigint.ev_watcher); + if (!pt->ev_loop_foreign) + ev_loop_destroy(pt->io_loop_ev); +} + +LWS_VISIBLE void +lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc) +{ + struct lws_context *context = lws_get_context(new_wsi); + struct ev_io *r = &new_wsi->w_read.ev_watcher; + struct ev_io *w = &new_wsi->w_write.ev_watcher; + int fd; + + if (!LWS_LIBEV_ENABLED(context)) + return; + + if (new_wsi->mode == LWSCM_RAW_FILEDESC) + fd = desc.filefd; + else + fd = desc.sockfd; + + new_wsi->w_read.context = context; + new_wsi->w_write.context = context; + ev_io_init(r, lws_accept_cb, fd, EV_READ); + ev_io_init(w, lws_accept_cb, fd, EV_WRITE); +} + +LWS_VISIBLE void +lws_libev_io(struct lws *wsi, int flags) +{ + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + if (!LWS_LIBEV_ENABLED(context)) + return; + + if (!pt->io_loop_ev) + return; + + assert((flags & (LWS_EV_START | LWS_EV_STOP)) && + (flags & (LWS_EV_READ | LWS_EV_WRITE))); + + if (flags & LWS_EV_START) { + if (flags & LWS_EV_WRITE) + ev_io_start(pt->io_loop_ev, &wsi->w_write.ev_watcher); + if (flags & LWS_EV_READ) + ev_io_start(pt->io_loop_ev, &wsi->w_read.ev_watcher); + } else { + if (flags & LWS_EV_WRITE) + ev_io_stop(pt->io_loop_ev, &wsi->w_write.ev_watcher); + if (flags & LWS_EV_READ) + ev_io_stop(pt->io_loop_ev, &wsi->w_read.ev_watcher); + } +} + +LWS_VISIBLE int +lws_libev_init_fd_table(struct lws_context *context) +{ + int n; + + if (!LWS_LIBEV_ENABLED(context)) + return 0; + + for (n = 0; n < context->count_threads; n++) { + context->pt[n].w_accept.context = context; + context->pt[n].w_sigint.context = context; + } + + return 1; +} + +LWS_VISIBLE void +lws_libev_run(const struct lws_context *context, int tsi) +{ + if (context->pt[tsi].io_loop_ev && LWS_LIBEV_ENABLED(context)) + ev_run(context->pt[tsi].io_loop_ev, 0); +} diff -Nru libwebsockets-4.0.20/lib/event-libs/libevent/libevent.c libwebsockets-2.4.2/lib/event-libs/libevent/libevent.c --- libwebsockets-4.0.20/lib/event-libs/libevent/libevent.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/libevent/libevent.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,470 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 void -lws_event_hrtimer_cb(int fd, short event, void *p) -{ - struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p; - struct timeval tv; - lws_usec_t us; - - lws_pt_lock(pt, __func__); - us = __lws_sul_service_ripe(&pt->pt_sul_owner, lws_now_usecs()); - if (us) { - tv.tv_sec = us / LWS_US_PER_SEC; - tv.tv_usec = us - (tv.tv_sec * LWS_US_PER_SEC); - evtimer_add(pt->event.hrtimer, &tv); - } - lws_pt_unlock(pt); -} - -static void -lws_event_idle_timer_cb(int fd, short event, void *p) -{ - struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p; - struct timeval tv; - lws_usec_t us; - - if (pt->is_destroyed) - return; - - lws_service_do_ripe_rxflow(pt); - - /* - * is there anybody with pending stuff that needs service forcing? - */ - if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) { - /* -1 timeout means just do forced service */ - _lws_plat_service_forced_tsi(pt->context, pt->tid); - /* still somebody left who wants forced service? */ - if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) { - /* yes... come back again later */ - - tv.tv_sec = 0; - tv.tv_usec = 1000; - evtimer_add(pt->event.idle_timer, &tv); - - return; - } - } - - lwsl_debug("%s: wait\n", __func__); - - /* account for hrtimer */ - - lws_pt_lock(pt, __func__); - us = __lws_sul_service_ripe(&pt->pt_sul_owner, lws_now_usecs()); - if (us) { - tv.tv_sec = us / LWS_US_PER_SEC; - tv.tv_usec = us - (tv.tv_sec * LWS_US_PER_SEC); - evtimer_add(pt->event.hrtimer, &tv); - } - lws_pt_unlock(pt); - - - if (pt->destroy_self) - lws_context_destroy(pt->context); -} - -static void -lws_event_cb(evutil_socket_t sock_fd, short revents, void *ctx) -{ - struct lws_io_watcher *lws_io = (struct lws_io_watcher *)ctx; - struct lws_context *context = lws_io->context; - struct lws_context_per_thread *pt; - struct lws_pollfd eventfd; - struct timeval tv; - struct lws *wsi; - - if (revents & EV_TIMEOUT) - return; - - /* !!! EV_CLOSED doesn't exist in libevent2 */ -#if LIBEVENT_VERSION_NUMBER < 0x02000000 - if (revents & EV_CLOSED) { - event_del(lws_io->event.watcher); - event_free(lws_io->event.watcher); - return; - } -#endif - - eventfd.fd = sock_fd; - eventfd.events = 0; - eventfd.revents = 0; - if (revents & EV_READ) { - eventfd.events |= LWS_POLLIN; - eventfd.revents |= LWS_POLLIN; - } - if (revents & EV_WRITE) { - eventfd.events |= LWS_POLLOUT; - eventfd.revents |= LWS_POLLOUT; - } - - wsi = wsi_from_fd(context, sock_fd); - if (!wsi) - return; - - pt = &context->pt[(int)wsi->tsi]; - if (pt->is_destroyed) - return; - - lws_service_fd_tsi(context, &eventfd, wsi->tsi); - - if (pt->destroy_self) { - lws_context_destroy(pt->context); - return; - } - - /* set the idle timer for 1ms ahead */ - - tv.tv_sec = 0; - tv.tv_usec = 1000; - evtimer_add(pt->event.idle_timer, &tv); -} - -void -lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, void *ctx) -{ - struct lws_context_per_thread *pt = ctx; - struct event *signal = (struct event *)ctx; - - if (pt->context->eventlib_signal_cb) { - pt->context->eventlib_signal_cb((void *)(lws_intptr_t)sock_fd, - event_get_signal(signal)); - - return; - } - if (!pt->event_loop_foreign) - event_base_loopbreak(pt->event.io_loop); -} - - -static int -elops_init_pt_event(struct lws_context *context, void *_loop, int tsi) -{ - struct lws_vhost *vh = context->vhost_list; - struct event_base *loop = (struct event_base *)_loop; - struct lws_context_per_thread *pt = &context->pt[tsi]; - - lwsl_info("%s: loop %p\n", __func__, _loop); - - if (!loop) - loop = event_base_new(); - else - context->pt[tsi].event_loop_foreign = 1; - - if (!loop) { - lwsl_err("%s: creating event base failed\n", __func__); - - return -1; - } - - pt->event.io_loop = loop; - - /* - * Initialize all events with the listening sockets - * and register a callback for read operations - */ - - while (vh) { - if (vh->lserv_wsi) { - vh->lserv_wsi->w_read.context = context; - vh->lserv_wsi->w_read.event.watcher = event_new( - loop, vh->lserv_wsi->desc.sockfd, - (EV_READ | EV_PERSIST), lws_event_cb, - &vh->lserv_wsi->w_read); - event_add(vh->lserv_wsi->w_read.event.watcher, NULL); - vh->lserv_wsi->w_read.event.set = 1; - } - vh = vh->vhost_next; - } - - /* static event loop objects */ - - pt->event.hrtimer = event_new(loop, -1, EV_PERSIST, - lws_event_hrtimer_cb, pt); - - pt->event.idle_timer = event_new(loop, -1, 0, - lws_event_idle_timer_cb, pt); - - /* Register the signal watcher unless it's a foreign loop */ - - if (pt->event_loop_foreign) - return 0; - - pt->w_sigint.event.watcher = evsignal_new(loop, SIGINT, - lws_event_sigint_cb, pt); - event_add(pt->w_sigint.event.watcher, NULL); - - return 0; -} - -static int -elops_init_context_event(struct lws_context *context, - const struct lws_context_creation_info *info) -{ - int n; - - context->eventlib_signal_cb = info->signal_cb; - - for (n = 0; n < context->count_threads; n++) - context->pt[n].w_sigint.context = context; - - return 0; -} - -static int -elops_accept_event(struct lws *wsi) -{ - struct lws_context *context = lws_get_context(wsi); - struct lws_context_per_thread *pt; - int fd; - - wsi->w_read.context = context; - wsi->w_write.context = context; - - // Initialize the event - pt = &context->pt[(int)wsi->tsi]; - - if (wsi->role_ops->file_handle) - fd = wsi->desc.filefd; - else - fd = wsi->desc.sockfd; - - wsi->w_read.event.watcher = event_new(pt->event.io_loop, fd, - (EV_READ | EV_PERSIST), lws_event_cb, &wsi->w_read); - wsi->w_write.event.watcher = event_new(pt->event.io_loop, fd, - (EV_WRITE | EV_PERSIST), lws_event_cb, &wsi->w_write); - - return 0; -} - -static void -elops_io_event(struct lws *wsi, int flags) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - if (!pt->event.io_loop || wsi->context->being_destroyed || - pt->is_destroyed) - return; - - assert((flags & (LWS_EV_START | LWS_EV_STOP)) && - (flags & (LWS_EV_READ | LWS_EV_WRITE))); - - if (flags & LWS_EV_START) { - if ((flags & LWS_EV_WRITE) && !wsi->w_write.event.set) { - event_add(wsi->w_write.event.watcher, NULL); - wsi->w_write.event.set = 1; - } - - if ((flags & LWS_EV_READ) && !wsi->w_read.event.set) { - event_add(wsi->w_read.event.watcher, NULL); - wsi->w_read.event.set = 1; - } - } else { - if ((flags & LWS_EV_WRITE) && wsi->w_write.event.set) { - event_del(wsi->w_write.event.watcher); - wsi->w_write.event.set = 0; - } - - if ((flags & LWS_EV_READ) && wsi->w_read.event.set) { - event_del(wsi->w_read.event.watcher); - wsi->w_read.event.set = 0; - } - } -} - -static void -elops_run_pt_event(struct lws_context *context, int tsi) -{ - /* Run / Dispatch the event_base loop */ - if (context->pt[tsi].event.io_loop) - event_base_dispatch(context->pt[tsi].event.io_loop); -} - -static void -elops_destroy_pt_event(struct lws_context *context, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - struct lws_vhost *vh = context->vhost_list; - - lwsl_info("%s\n", __func__); - - if (!pt->event.io_loop) - return; - - /* - * Free all events with the listening sockets - */ - while (vh) { - if (vh->lserv_wsi) { - event_free(vh->lserv_wsi->w_read.event.watcher); - vh->lserv_wsi->w_read.event.watcher = NULL; - event_free(vh->lserv_wsi->w_write.event.watcher); - vh->lserv_wsi->w_write.event.watcher = NULL; - } - vh = vh->vhost_next; - } - - event_free(pt->event.hrtimer); - event_free(pt->event.idle_timer); - - if (!pt->event_loop_foreign) { - event_del(pt->w_sigint.event.watcher); - event_free(pt->w_sigint.event.watcher); - event_base_loopexit(pt->event.io_loop, NULL); - // event_base_free(pt->event.io_loop); - // pt->event.io_loop = NULL; - lwsl_notice("%s: set to exit loop\n", __func__); - } -} - -static void -elops_destroy_wsi_event(struct lws *wsi) -{ - struct lws_context_per_thread *pt; - - if (!wsi) - return; - - pt = &wsi->context->pt[(int)wsi->tsi]; - if (pt->is_destroyed) - return; - - if (wsi->w_read.event.watcher) { - event_free(wsi->w_read.event.watcher); - wsi->w_read.event.watcher = NULL; - } - - if (wsi->w_write.event.watcher) { - event_free(wsi->w_write.event.watcher); - wsi->w_write.event.watcher = NULL; - } -} - -static int -elops_wsi_logical_close_event(struct lws *wsi) -{ - elops_destroy_wsi_event(wsi); - - return 0; -} - -static int -elops_init_vhost_listen_wsi_event(struct lws *wsi) -{ - struct lws_context_per_thread *pt; - int fd; - - if (!wsi) { - assert(0); - return 0; - } - - wsi->w_read.context = wsi->context; - wsi->w_write.context = wsi->context; - - pt = &wsi->context->pt[(int)wsi->tsi]; - - if (wsi->role_ops->file_handle) - fd = wsi->desc.filefd; - else - fd = wsi->desc.sockfd; - - wsi->w_read.event.watcher = event_new(pt->event.io_loop, fd, - (EV_READ | EV_PERSIST), - lws_event_cb, &wsi->w_read); - wsi->w_write.event.watcher = event_new(pt->event.io_loop, fd, - (EV_WRITE | EV_PERSIST), - lws_event_cb, &wsi->w_write); - - elops_io_event(wsi, LWS_EV_START | LWS_EV_READ); - - return 0; -} - -static int -elops_destroy_context2_event(struct lws_context *context) -{ - struct lws_context_per_thread *pt; - int n, m; - - lwsl_debug("%s: in\n", __func__); - - for (n = 0; n < context->count_threads; n++) { - int budget = 1000; - - pt = &context->pt[n]; - - /* only for internal loops... */ - - if (pt->event_loop_foreign || !pt->event.io_loop) - continue; - - if (!context->finalize_destroy_after_internal_loops_stopped) { - event_base_loopexit(pt->event.io_loop, NULL); - continue; - } - while (budget-- && - (m = event_base_loop(pt->event.io_loop, EVLOOP_NONBLOCK))) - ; -#if 0 - if (m) { - lwsl_err("%s: tsi %d: NOT everything closed\n", - __func__, n); - event_base_dump_events(pt->event.io_loop, stderr); - } else - lwsl_debug("%s: %d: everything closed OK\n", __func__, n); -#endif - lwsl_err("%s: event_base_free\n", __func__); - event_base_free(pt->event.io_loop); - pt->event.io_loop = NULL; - - } - - lwsl_debug("%s: out\n", __func__); - - return 0; -} - -struct lws_event_loop_ops event_loop_ops_event = { - /* name */ "libevent", - /* init_context */ elops_init_context_event, - /* destroy_context1 */ NULL, - /* destroy_context2 */ elops_destroy_context2_event, - /* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_event, - /* init_pt */ elops_init_pt_event, - /* wsi_logical_close */ elops_wsi_logical_close_event, - /* check_client_connect_ok */ NULL, - /* close_handle_manually */ NULL, - /* accept */ elops_accept_event, - /* io */ elops_io_event, - /* run_pt */ elops_run_pt_event, - /* destroy_pt */ elops_destroy_pt_event, - /* destroy wsi */ elops_destroy_wsi_event, - - /* flags */ 0, -}; diff -Nru libwebsockets-4.0.20/lib/event-libs/libevent/private-lib-event-libs-libevent.h libwebsockets-2.4.2/lib/event-libs/libevent/private-lib-event-libs-libevent.h --- libwebsockets-4.0.20/lib/event-libs/libevent/private-lib-event-libs-libevent.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/libevent/private-lib-event-libs-libevent.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -struct lws_pt_eventlibs_libevent { - struct event_base *io_loop; - struct event *hrtimer; - struct event *idle_timer; -}; - -struct lws_io_watcher_libevent { - struct event *watcher; - char set; -}; - -struct lws_signal_watcher_libevent { - struct event *watcher; -}; - -struct lws_context_eventlibs_libevent { - int placeholder; -}; - -extern struct lws_event_loop_ops event_loop_ops_event; diff -Nru libwebsockets-4.0.20/lib/event-libs/libevent.c libwebsockets-2.4.2/lib/event-libs/libevent.c --- libwebsockets-4.0.20/lib/event-libs/libevent.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/libevent.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,235 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +void lws_feature_status_libevent(struct lws_context_creation_info *info) +{ + if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBEVENT)) + lwsl_info("libevent support compiled in and enabled\n"); + else + lwsl_info("libevent support compiled in but disabled\n"); +} + +static void +lws_event_cb(evutil_socket_t sock_fd, short revents, void *ctx) +{ + struct lws_io_watcher *lws_io = (struct lws_io_watcher *)ctx; + struct lws_context *context = lws_io->context; + struct lws_pollfd eventfd; + + if (revents & EV_TIMEOUT) + return; + + /* !!! EV_CLOSED doesn't exist in libevent2 */ + #if LIBEVENT_VERSION_NUMBER < 0x02000000 + if (revents & EV_CLOSED) { + event_del(lws_io->event_watcher); + event_free(lws_io->event_watcher); + return; + } + #endif + + eventfd.fd = sock_fd; + eventfd.events = 0; + eventfd.revents = 0; + if (revents & EV_READ) { + eventfd.events |= LWS_POLLIN; + eventfd.revents |= LWS_POLLIN; + } + if (revents & EV_WRITE) { + eventfd.events |= LWS_POLLOUT; + eventfd.revents |= LWS_POLLOUT; + } + + lws_service_fd(context, &eventfd); +} + +LWS_VISIBLE void +lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, void *ctx) +{ + struct lws_context_per_thread *pt = ctx; + + if (!pt->ev_loop_foreign) + event_base_loopbreak(pt->io_loop_event_base); +} + +LWS_VISIBLE int +lws_event_sigint_cfg(struct lws_context *context, int use_event_sigint, +lws_event_signal_cb_t *cb) +{ + context->use_ev_sigint = use_event_sigint; + if (cb) + context->lws_event_sigint_cb = cb; + else + context->lws_event_sigint_cb = &lws_event_sigint_cb; + + return 0; +} + +LWS_VISIBLE int +lws_event_initloop(struct lws_context *context, struct event_base *loop, +int tsi) +{ + struct lws_vhost *vh = context->vhost_list; + + if (!loop) + context->pt[tsi].io_loop_event_base = event_base_new(); + else { + context->pt[tsi].ev_loop_foreign = 1; + context->pt[tsi].io_loop_event_base = loop; + } + + /* + * Initialize all events with the listening sockets + * and register a callback for read operations + */ + + while (vh) { + if (vh->lserv_wsi) { + vh->lserv_wsi->w_read.context = context; + vh->lserv_wsi->w_read.event_watcher = event_new( + loop, vh->lserv_wsi->desc.sockfd, + (EV_READ | EV_PERSIST), lws_event_cb, + &vh->lserv_wsi->w_read); + event_add(vh->lserv_wsi->w_read.event_watcher, NULL); + } + vh = vh->vhost_next; + } + + /* Register the signal watcher unless the user says not to */ + if (!context->use_ev_sigint) + return 0; + + context->pt[tsi].w_sigint.event_watcher = evsignal_new(loop, SIGINT, + context->lws_event_sigint_cb, &context->pt[tsi]); + event_add(context->pt[tsi].w_sigint.event_watcher, NULL); + + return 0; +} + +void +lws_libevent_destroyloop(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_vhost *vh = context->vhost_list; + + if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT)) + return; + + if (!pt->io_loop_event_base) + return; + + /* + * Free all events with the listening sockets + */ + while (vh) { + if (vh->lserv_wsi) { + event_free(vh->lserv_wsi->w_read.event_watcher); + vh->lserv_wsi->w_read.event_watcher = NULL; + } + vh = vh->vhost_next; + } + + if (context->use_ev_sigint) + event_free(pt->w_sigint.event_watcher); + if (!pt->ev_loop_foreign) + event_base_free(pt->io_loop_event_base); +} + +LWS_VISIBLE void +lws_libevent_accept(struct lws *new_wsi, lws_sock_file_fd_type desc) +{ + struct lws_context *context = lws_get_context(new_wsi); + struct lws_context_per_thread *pt; + int fd; + + if (!LWS_LIBEVENT_ENABLED(context)) + return; + + new_wsi->w_read.context = context; + new_wsi->w_write.context = context; + + // Initialize the event + pt = &context->pt[(int)new_wsi->tsi]; + + if (new_wsi->mode == LWSCM_RAW_FILEDESC) + fd = desc.filefd; + else + fd = desc.sockfd; + + new_wsi->w_read.event_watcher = event_new(pt->io_loop_event_base, fd, + (EV_READ | EV_PERSIST), lws_event_cb, &new_wsi->w_read); + new_wsi->w_write.event_watcher = event_new(pt->io_loop_event_base, fd, + (EV_WRITE | EV_PERSIST), lws_event_cb, &new_wsi->w_write); +} + +LWS_VISIBLE void +lws_libevent_io(struct lws *wsi, int flags) +{ + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + if (!LWS_LIBEVENT_ENABLED(context)) + return; + + if (!pt->io_loop_event_base || context->being_destroyed) + return; + + assert((flags & (LWS_EV_START | LWS_EV_STOP)) && + (flags & (LWS_EV_READ | LWS_EV_WRITE))); + + if (flags & LWS_EV_START) { + if (flags & LWS_EV_WRITE) + event_add(wsi->w_write.event_watcher, NULL); + if (flags & LWS_EV_READ) + event_add(wsi->w_read.event_watcher, NULL); + } else { + if (flags & LWS_EV_WRITE) + event_del(wsi->w_write.event_watcher); + + if (flags & LWS_EV_READ) + event_del(wsi->w_read.event_watcher); + } +} + +LWS_VISIBLE int +lws_libevent_init_fd_table(struct lws_context *context) +{ + int n; + + if (!LWS_LIBEVENT_ENABLED(context)) + return 0; + + for (n = 0; n < context->count_threads; n++) + context->pt[n].w_sigint.context = context; + + return 1; +} + +LWS_VISIBLE void +lws_libevent_run(const struct lws_context *context, int tsi) +{ + /* Run / Dispatch the event_base loop */ + if (context->pt[tsi].io_loop_event_base && + LWS_LIBEVENT_ENABLED(context)) + event_base_dispatch(context->pt[tsi].io_loop_event_base); +} diff -Nru libwebsockets-4.0.20/lib/event-libs/libuv/libuv.c libwebsockets-2.4.2/lib/event-libs/libuv/libuv.c --- libwebsockets-4.0.20/lib/event-libs/libuv/libuv.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/libuv/libuv.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,980 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 void -lws_uv_sultimer_cb(uv_timer_t *timer -#if UV_VERSION_MAJOR == 0 - , int status -#endif -) -{ - struct lws_context_per_thread *pt = lws_container_of(timer, - struct lws_context_per_thread, uv.sultimer); - lws_usec_t us; - - lws_pt_lock(pt, __func__); - us = __lws_sul_service_ripe(&pt->pt_sul_owner, lws_now_usecs()); - if (us) - uv_timer_start(&pt->uv.sultimer, lws_uv_sultimer_cb, - LWS_US_TO_MS(us), 0); - lws_pt_unlock(pt); -} - -static void -lws_uv_idle(uv_idle_t *handle -#if UV_VERSION_MAJOR == 0 - , int status -#endif -) -{ - struct lws_context_per_thread *pt = lws_container_of(handle, - struct lws_context_per_thread, uv.idle); - lws_usec_t us; - - lws_service_do_ripe_rxflow(pt); - - /* - * is there anybody with pending stuff that needs service forcing? - */ - if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) - /* -1 timeout means just do forced service */ - _lws_plat_service_forced_tsi(pt->context, pt->tid); - - /* account for sultimer */ - - lws_pt_lock(pt, __func__); - us = __lws_sul_service_ripe(&pt->pt_sul_owner, lws_now_usecs()); - if (us) - uv_timer_start(&pt->uv.sultimer, lws_uv_sultimer_cb, - LWS_US_TO_MS(us), 0); - lws_pt_unlock(pt); - - /* there is nobody who needs service forcing, shut down idle */ - uv_idle_stop(handle); -} - -static void -lws_io_cb(uv_poll_t *watcher, int status, int revents) -{ - struct lws *wsi = (struct lws *)((uv_handle_t *)watcher)->data; - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - struct lws_pollfd eventfd; - - if (pt->is_destroyed) - return; - -#if defined(WIN32) || defined(_WIN32) - eventfd.fd = watcher->socket; -#else - eventfd.fd = watcher->io_watcher.fd; -#endif - eventfd.events = 0; - eventfd.revents = 0; - - if (status < 0) { - /* - * At this point status will be an UV error, like UV_EBADF, - * we treat all errors as LWS_POLLHUP - * - * You might want to return; instead of servicing the fd in - * some cases */ - if (status == UV_EAGAIN) - return; - - eventfd.events |= LWS_POLLHUP; - eventfd.revents |= LWS_POLLHUP; - } else { - if (revents & UV_READABLE) { - eventfd.events |= LWS_POLLIN; - eventfd.revents |= LWS_POLLIN; - } - if (revents & UV_WRITABLE) { - eventfd.events |= LWS_POLLOUT; - eventfd.revents |= LWS_POLLOUT; - } - } - lws_service_fd_tsi(context, &eventfd, wsi->tsi); - - if (pt->destroy_self) { - lws_context_destroy(pt->context); - return; - } - - uv_idle_start(&pt->uv.idle, lws_uv_idle); -} - -/* - * This does not actually stop the event loop. The reason is we have to pass - * libuv handle closures through its event loop. So this tries to close all - * wsi, and set a flag; when all the wsi closures are finalized then we - * actually stop the libuv event loops. - */ -static void -lws_libuv_stop(struct lws_context *context) -{ - struct lws_context_per_thread *pt; - int n, m; - - lwsl_err("%s\n", __func__); - - if (context->requested_kill) { - lwsl_err("%s: ignoring\n", __func__); - return; - } - - context->requested_kill = 1; - - m = context->count_threads; - context->being_destroyed = 1; - - /* - * Phase 1: start the close of every dynamic uv handle - */ - - while (m--) { - pt = &context->pt[m]; - - if (pt->pipe_wsi) { - uv_poll_stop(pt->pipe_wsi->w_read.uv.pwatcher); - lws_destroy_event_pipe(pt->pipe_wsi); - pt->pipe_wsi = NULL; - } - - for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) { - struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd); - - if (!wsi) - continue; - lws_close_free_wsi(wsi, - LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY, - __func__ /* no protocol close */); - n--; - } - } - - lwsl_info("%s: started closing all wsi\n", __func__); - - /* we cannot have completed... there are at least the cancel pipes */ -} - -static void -lws_uv_signal_handler(uv_signal_t *watcher, int signum) -{ - struct lws_context *context = watcher->data; - - if (context->eventlib_signal_cb) { - context->eventlib_signal_cb((void *)watcher, signum); - - return; - } - - lwsl_err("internal signal handler caught signal %d\n", signum); - lws_libuv_stop(watcher->data); -} - -static const int sigs[] = { SIGINT, SIGTERM, SIGSEGV, SIGFPE, SIGHUP }; - -/* - * Closing Phase 2: Close callback for a static UV asset - */ - -static void -lws_uv_close_cb_sa(uv_handle_t *handle) -{ - struct lws_context *context = - LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(handle); - int n; - - lwsl_info("%s: sa left %d: dyn left: %d\n", __func__, - context->count_event_loop_static_asset_handles, - context->count_wsi_allocated); - - /* any static assets left? */ - - if (LWS_UV_REFCOUNT_STATIC_HANDLE_DESTROYED(handle) || - context->count_wsi_allocated) - return; - - /* - * That's it... all wsi were down, and now every - * static asset lws had a UV handle for is down. - * - * Stop the loop so we can get out of here. - */ - - for (n = 0; n < context->count_threads; n++) { - struct lws_context_per_thread *pt = &context->pt[n]; - - if (pt->uv.io_loop && !pt->event_loop_foreign) - uv_stop(pt->uv.io_loop); - } - - if (!context->pt[0].event_loop_foreign) { - lwsl_info("%s: calling lws_context_destroy2\n", __func__); - lws_context_destroy2(context); - } - - lwsl_info("%s: all done\n", __func__); -} - -/* - * These must be called by protocols that want to use libuv objects directly... - * - * .... when the libuv object is created... - */ - -void -lws_libuv_static_refcount_add(uv_handle_t *h, struct lws_context *context) -{ - LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(h, context); -} - -/* - * ... and in the close callback when the object is closed. - */ - -void -lws_libuv_static_refcount_del(uv_handle_t *h) -{ - lws_uv_close_cb_sa(h); -} - - -static void lws_uv_close_cb(uv_handle_t *handle) -{ -} - -static void lws_uv_walk_cb(uv_handle_t *handle, void *arg) -{ - if (!uv_is_closing(handle)) - uv_close(handle, lws_uv_close_cb); -} - -void -lws_close_all_handles_in_loop(uv_loop_t *loop) -{ - uv_walk(loop, lws_uv_walk_cb, NULL); -} - - -void -lws_libuv_stop_without_kill(const struct lws_context *context, int tsi) -{ - if (context->pt[tsi].uv.io_loop) - uv_stop(context->pt[tsi].uv.io_loop); -} - - - -uv_loop_t * -lws_uv_getloop(struct lws_context *context, int tsi) -{ - if (context->pt[tsi].uv.io_loop) - return context->pt[tsi].uv.io_loop; - - return NULL; -} - -int -lws_libuv_check_watcher_active(struct lws *wsi) -{ - uv_handle_t *h = (uv_handle_t *)wsi->w_read.uv.pwatcher; - - if (!h) - return 0; - - return uv_is_active(h); -} - - -#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) - -int -lws_uv_plugins_init(struct lws_context *context, const char * const *d) -{ - struct lws_plugin_capability lcaps; - struct lws_plugin *plugin; - lws_plugin_init_func initfunc; - int m, ret = 0; - void *v; - uv_dirent_t dent; - uv_fs_t req; - char path[256]; - uv_lib_t lib; - int pofs = 0; - -#if defined(__MINGW32__) || !defined(WIN32) - pofs = 3; -#endif - - lib.errmsg = NULL; - lib.handle = NULL; - - uv_loop_init(&context->uv.loop); - - lwsl_notice(" Plugins:\n"); - - while (d && *d) { - - lwsl_notice(" Scanning %s\n", *d); - m =uv_fs_scandir(&context->uv.loop, &req, *d, 0, NULL); - if (m < 1) { - lwsl_err("Scandir on %s failed\n", *d); - return 1; - } - - while (uv_fs_scandir_next(&req, &dent) != UV_EOF) { - if (strlen(dent.name) < 7) - continue; - - lwsl_notice(" %s\n", dent.name); - - lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d, - dent.name); - if (uv_dlopen(path, &lib)) { - uv_dlerror(&lib); - lwsl_err("Error loading DSO: %s\n", lib.errmsg); - uv_dlclose(&lib); - goto bail; - } - - /* we could open it, can we get his init function? */ - -#if !defined(WIN32) && !defined(__MINGW32__) - m = lws_snprintf(path, sizeof(path) - 1, "init_%s", - dent.name + pofs /* snip lib... */); - path[m - 3] = '\0'; /* snip the .so */ -#else - m = lws_snprintf(path, sizeof(path) - 1, "init_%s", - dent.name + pofs); - path[m - 4] = '\0'; /* snip the .dll */ -#endif - if (uv_dlsym(&lib, path, &v)) { - uv_dlerror(&lib); - lwsl_err("%s: Failed to get '%s' on %s: %s\n", - __func__, path, dent.name, lib.errmsg); - uv_dlclose(&lib); - goto bail; - } - initfunc = (lws_plugin_init_func)v; - lcaps.api_magic = LWS_PLUGIN_API_MAGIC; - m = initfunc(context, &lcaps); - if (m) { - lwsl_err("Init %s failed %d\n", dent.name, m); - goto skip; - } - - plugin = lws_malloc(sizeof(*plugin), "plugin"); - if (!plugin) { - uv_dlclose(&lib); - lwsl_err("OOM\n"); - goto bail; - } - plugin->list = context->plugin_list; - context->plugin_list = plugin; - lws_strncpy(plugin->name, dent.name, sizeof(plugin->name)); - plugin->lib = lib; - plugin->caps = lcaps; - context->plugin_protocol_count += lcaps.count_protocols; - context->plugin_extension_count += lcaps.count_extensions; - - continue; - -skip: - uv_dlclose(&lib); - } -bail: - uv_fs_req_cleanup(&req); - d++; - } - - return ret; -} - -int -lws_uv_plugins_destroy(struct lws_context *context) -{ - struct lws_plugin *plugin = context->plugin_list, *p; - lws_plugin_destroy_func func; - char path[256]; - int pofs = 0; - void *v; - int m; - -#if defined(__MINGW32__) || !defined(WIN32) - pofs = 3; -#endif - - if (!plugin) - return 0; - - while (plugin) { - p = plugin; - -#if !defined(WIN32) && !defined(__MINGW32__) - m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", - plugin->name + pofs); - path[m - 3] = '\0'; -#else - m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", - plugin->name + pofs); - path[m - 4] = '\0'; -#endif - - if (uv_dlsym(&plugin->lib, path, &v)) { - uv_dlerror(&plugin->lib); - lwsl_err("Failed to get %s on %s: %s", path, - plugin->name, plugin->lib.errmsg); - } else { - func = (lws_plugin_destroy_func)v; - m = func(context); - if (m) - lwsl_err("Destroying %s failed %d\n", - plugin->name, m); - } - - uv_dlclose(&p->lib); - plugin = p->list; - p->list = NULL; - free(p); - } - - context->plugin_list = NULL; - - while (uv_loop_close(&context->uv.loop)) - ; - - return 0; -} - -#endif - -static int -elops_init_context_uv(struct lws_context *context, - const struct lws_context_creation_info *info) -{ - int n; - - context->eventlib_signal_cb = info->signal_cb; - - for (n = 0; n < context->count_threads; n++) - context->pt[n].w_sigint.context = context; - - return 0; -} - -static int -elops_destroy_context1_uv(struct lws_context *context) -{ - struct lws_context_per_thread *pt; - int n, m = 0; - - for (n = 0; n < context->count_threads; n++) { - int budget = 10000; - pt = &context->pt[n]; - - /* only for internal loops... */ - - if (!pt->event_loop_foreign) { - - while (budget-- && (m = uv_run(pt->uv.io_loop, - UV_RUN_NOWAIT))) - ; - if (m) - lwsl_info("%s: tsi %d: not all closed\n", - __func__, n); - - } - } - - /* call destroy2 if internal loop */ - return !context->pt[0].event_loop_foreign; -} - -static int -elops_destroy_context2_uv(struct lws_context *context) -{ - struct lws_context_per_thread *pt; - int n, internal = 0; - - for (n = 0; n < context->count_threads; n++) { - pt = &context->pt[n]; - - /* only for internal loops... */ - - if (!pt->event_loop_foreign && pt->uv.io_loop) { - internal = 1; - if (!context->finalize_destroy_after_internal_loops_stopped) - uv_stop(pt->uv.io_loop); - else { -#if UV_VERSION_MAJOR > 0 - uv_loop_close(pt->uv.io_loop); -#endif - lws_free_set_NULL(pt->uv.io_loop); - } - } - } - - return internal; -} - -static int -elops_wsi_logical_close_uv(struct lws *wsi) -{ - if (!lws_socket_is_valid(wsi->desc.sockfd)) - return 0; - - if (wsi->listener || wsi->event_pipe) { - lwsl_debug("%s: %p: %d %d stop listener / pipe poll\n", - __func__, wsi, wsi->listener, wsi->event_pipe); - if (wsi->w_read.uv.pwatcher) - uv_poll_stop(wsi->w_read.uv.pwatcher); - } - lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); - /* - * libuv has to do his own close handle processing asynchronously - */ - lws_libuv_closehandle(wsi); - - return 1; /* do not complete the wsi close, uv close cb will do it */ -} - -static int -elops_check_client_connect_ok_uv(struct lws *wsi) -{ - if (lws_libuv_check_watcher_active(wsi)) { - lwsl_warn("Waiting for libuv watcher to close\n"); - return 1; - } - - return 0; -} - -static void -lws_libuv_closewsi_m(uv_handle_t* handle) -{ - lws_sockfd_type sockfd = (lws_sockfd_type)(lws_intptr_t)handle->data; - lwsl_debug("%s: sockfd %d\n", __func__, sockfd); - compatible_close(sockfd); - lws_free(handle); -} - -static void -elops_close_handle_manually_uv(struct lws *wsi) -{ - uv_handle_t *h = (uv_handle_t *)wsi->w_read.uv.pwatcher; - - lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); - - /* - * the "manual" variant only closes the handle itself and the - * related fd. handle->data is the fd. - */ - h->data = (void *)(lws_intptr_t)wsi->desc.sockfd; - - /* - * We take responsibility to close / destroy these now. - * Remove any trace from the wsi. - */ - - wsi->desc.sockfd = LWS_SOCK_INVALID; - wsi->w_read.uv.pwatcher = NULL; - wsi->told_event_loop_closed = 1; - - uv_close(h, lws_libuv_closewsi_m); -} - -static int -elops_accept_uv(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - wsi->w_read.context = wsi->context; - - wsi->w_read.uv.pwatcher = - lws_malloc(sizeof(*wsi->w_read.uv.pwatcher), "uvh"); - if (!wsi->w_read.uv.pwatcher) - return -1; - - if (wsi->role_ops->file_handle) - uv_poll_init(pt->uv.io_loop, wsi->w_read.uv.pwatcher, - (int)(lws_intptr_t)wsi->desc.filefd); - else - uv_poll_init_socket(pt->uv.io_loop, - wsi->w_read.uv.pwatcher, - wsi->desc.sockfd); - - ((uv_handle_t *)wsi->w_read.uv.pwatcher)->data = (void *)wsi; - - return 0; -} - -static void -elops_io_uv(struct lws *wsi, int flags) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct lws_io_watcher *w = &wsi->w_read; - int current_events = w->actual_events & (UV_READABLE | UV_WRITABLE); - - lwsl_debug("%s: %p: %d\n", __func__, wsi, flags); - - /* w->context is set after the loop is initialized */ - - if (!pt->uv.io_loop || !w->context) { - lwsl_info("%s: no io loop yet\n", __func__); - return; - } - - if (!((flags & (LWS_EV_START | LWS_EV_STOP)) && - (flags & (LWS_EV_READ | LWS_EV_WRITE)))) { - lwsl_err("%s: assert: flags %d", __func__, flags); - assert(0); - } - - if (!w->uv.pwatcher || wsi->told_event_loop_closed) { - lwsl_err("%s: no watcher\n", __func__); - - return; - } - - if (flags & LWS_EV_START) { - if (flags & LWS_EV_WRITE) - current_events |= UV_WRITABLE; - - if (flags & LWS_EV_READ) - current_events |= UV_READABLE; - - uv_poll_start(w->uv.pwatcher, current_events, lws_io_cb); - } else { - if (flags & LWS_EV_WRITE) - current_events &= ~UV_WRITABLE; - - if (flags & LWS_EV_READ) - current_events &= ~UV_READABLE; - - if (!(current_events & (UV_READABLE | UV_WRITABLE))) - uv_poll_stop(w->uv.pwatcher); - else - uv_poll_start(w->uv.pwatcher, current_events, - lws_io_cb); - } - - w->actual_events = current_events; -} - -static int -elops_init_vhost_listen_wsi_uv(struct lws *wsi) -{ - struct lws_context_per_thread *pt; - int n; - - if (!wsi) - return 0; - if (wsi->w_read.context) - return 0; - - pt = &wsi->context->pt[(int)wsi->tsi]; - if (!pt->uv.io_loop) - return 0; - - wsi->w_read.context = wsi->context; - - wsi->w_read.uv.pwatcher = - lws_malloc(sizeof(*wsi->w_read.uv.pwatcher), "uvh"); - if (!wsi->w_read.uv.pwatcher) - return -1; - - n = uv_poll_init_socket(pt->uv.io_loop, wsi->w_read.uv.pwatcher, - wsi->desc.sockfd); - if (n) { - lwsl_err("uv_poll_init failed %d, sockfd=%p\n", n, - (void *)(lws_intptr_t)wsi->desc.sockfd); - - return -1; - } - - ((uv_handle_t *)wsi->w_read.uv.pwatcher)->data = (void *)wsi; - - elops_io_uv(wsi, LWS_EV_START | LWS_EV_READ); - - return 0; -} - -static void -elops_run_pt_uv(struct lws_context *context, int tsi) -{ - if (context->pt[tsi].uv.io_loop) - uv_run(context->pt[tsi].uv.io_loop, 0); -} - -static void -elops_destroy_pt_uv(struct lws_context *context, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - int m, ns; - - lwsl_info("%s: %d\n", __func__, tsi); - - if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) - return; - - if (!pt->uv.io_loop) - return; - - if (pt->event_loop_destroy_processing_done) - return; - - pt->event_loop_destroy_processing_done = 1; - - if (!pt->event_loop_foreign) { - uv_signal_stop(&pt->w_sigint.uv.watcher); - - ns = LWS_ARRAY_SIZE(sigs); - if (lws_check_opt(context->options, - LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN)) - ns = 2; - - for (m = 0; m < ns; m++) { - uv_signal_stop(&pt->uv.signals[m]); - uv_close((uv_handle_t *)&pt->uv.signals[m], - lws_uv_close_cb_sa); - } - } else - lwsl_debug("%s: not closing pt signals\n", __func__); - - uv_timer_stop(&pt->uv.sultimer); - uv_close((uv_handle_t *)&pt->uv.sultimer, lws_uv_close_cb_sa); - - uv_idle_stop(&pt->uv.idle); - uv_close((uv_handle_t *)&pt->uv.idle, lws_uv_close_cb_sa); -} - -/* - * This needs to be called after vhosts have been defined. - * - * If later, after server start, another vhost is added, this must be - * called again to bind the vhost - */ - -int -elops_init_pt_uv(struct lws_context *context, void *_loop, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - struct lws_vhost *vh = context->vhost_list; - int status = 0, n, ns, first = 1; - uv_loop_t *loop = (uv_loop_t *)_loop; - - if (!pt->uv.io_loop) { - if (!loop) { - loop = lws_malloc(sizeof(*loop), "libuv loop"); - if (!loop) { - lwsl_err("OOM\n"); - return -1; - } - #if UV_VERSION_MAJOR > 0 - uv_loop_init(loop); - #else - lwsl_err("This libuv is too old to work...\n"); - return 1; - #endif - pt->event_loop_foreign = 0; - } else { - lwsl_notice(" Using foreign event loop...\n"); - pt->event_loop_foreign = 1; - } - - pt->uv.io_loop = loop; - uv_idle_init(loop, &pt->uv.idle); - LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.idle, context); - - - ns = LWS_ARRAY_SIZE(sigs); - if (lws_check_opt(context->options, - LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN)) - ns = 2; - - if (!pt->event_loop_foreign) { - assert(ns <= (int)LWS_ARRAY_SIZE(pt->uv.signals)); - for (n = 0; n < ns; n++) { - uv_signal_init(loop, &pt->uv.signals[n]); - LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.signals[n], - context); - pt->uv.signals[n].data = pt->context; - uv_signal_start(&pt->uv.signals[n], - lws_uv_signal_handler, sigs[n]); - } - } - } else - first = 0; - - /* - * Initialize the accept wsi read watcher with all the listening sockets - * and register a callback for read operations - * - * We have to do it here because the uv loop(s) are not - * initialized until after context creation. - */ - while (vh) { - if (elops_init_vhost_listen_wsi_uv(vh->lserv_wsi) == -1) - return -1; - vh = vh->vhost_next; - } - - if (!first) - return status; - - uv_timer_init(pt->uv.io_loop, &pt->uv.sultimer); - LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.sultimer, context); - - return status; -} - -static void -lws_libuv_closewsi(uv_handle_t* handle) -{ - struct lws *wsi = (struct lws *)handle->data; - struct lws_context *context = lws_get_context(wsi); - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; -#if defined(LWS_WITH_SERVER) - int lspd = 0; -#endif - - lwsl_info("%s: %p\n", __func__, wsi); - - /* - * We get called back here for every wsi that closes - */ - -#if defined(LWS_WITH_SERVER) - if (wsi->role_ops == &role_ops_listen && wsi->context->deprecated) { - lspd = 1; - context->deprecation_pending_listen_close_count--; - if (!context->deprecation_pending_listen_close_count) - lspd = 2; - } -#endif - - lws_pt_lock(pt, __func__); - __lws_close_free_wsi_final(wsi); - lws_pt_unlock(pt); - - /* it's our job to close the handle finally */ - lws_free(handle); - -#if defined(LWS_WITH_SERVER) - if (lspd == 2 && context->deprecation_cb) { - lwsl_notice("calling deprecation callback\n"); - context->deprecation_cb(); - } -#endif - - lwsl_info("%s: sa left %d: dyn left: %d (rk %d)\n", __func__, - context->count_event_loop_static_asset_handles, - context->count_wsi_allocated, context->requested_kill); - - /* - * eventually, we closed all the wsi... - */ - - if (context->requested_kill && !context->count_wsi_allocated) { - struct lws_vhost *vh = context->vhost_list; - int m; - - /* - * Start Closing Phase 2: close of static handles - */ - - lwsl_info("%s: all lws dynamic handles down, closing static\n", - __func__); - - for (m = 0; m < context->count_threads; m++) - elops_destroy_pt_uv(context, m); - - /* protocols may have initialized libuv objects */ - - while (vh) { - lws_vhost_destroy1(vh); - vh = vh->vhost_next; - } - - if (!context->count_event_loop_static_asset_handles && - context->pt[0].event_loop_foreign) { - lwsl_info("%s: call lws_context_destroy2\n", __func__); - lws_context_destroy2(context); - } - } -} - -void -lws_libuv_closehandle(struct lws *wsi) -{ - uv_handle_t* handle; - - if (!wsi->w_read.uv.pwatcher) - return; - - if (wsi->told_event_loop_closed) { - // assert(0); - return; - } - - lwsl_debug("%s: %p\n", __func__, wsi); - - wsi->told_event_loop_closed = 1; - - /* - * The normal close path attaches the related wsi as the - * handle->data. - */ - - handle = (uv_handle_t *)wsi->w_read.uv.pwatcher; - - /* ensure we can only do this once */ - - wsi->w_read.uv.pwatcher = NULL; - - uv_close(handle, lws_libuv_closewsi); -} - -struct lws_event_loop_ops event_loop_ops_uv = { - /* name */ "libuv", - /* init_context */ elops_init_context_uv, - /* destroy_context1 */ elops_destroy_context1_uv, - /* destroy_context2 */ elops_destroy_context2_uv, - /* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_uv, - /* init_pt */ elops_init_pt_uv, - /* wsi_logical_close */ elops_wsi_logical_close_uv, - /* check_client_connect_ok */ elops_check_client_connect_ok_uv, - /* close_handle_manually */ elops_close_handle_manually_uv, - /* accept */ elops_accept_uv, - /* io */ elops_io_uv, - /* run_pt */ elops_run_pt_uv, - /* destroy_pt */ elops_destroy_pt_uv, - /* destroy wsi */ NULL, - - /* flags */ 0, -}; diff -Nru libwebsockets-4.0.20/lib/event-libs/libuv/private-lib-event-libs-libuv.h libwebsockets-2.4.2/lib/event-libs/libuv/private-lib-event-libs-libuv.h --- libwebsockets-4.0.20/lib/event-libs/libuv/private-lib-event-libs-libuv.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/libuv/private-lib-event-libs-libuv.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -/* - * libuv's async destroy cb means that asking to close something doesn't mean - * you can destroy it or parent things until after the close completes. - * - * So we must reference-count creation and close completions with libuv. - * - * All "static" (per-pt or per-context) uv handles must - * - * - have their .data set to point to the context - * - * - contribute to context->uv_count_static_asset_handles - * counting - */ -#define LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(_x, _ctx) \ - { uv_handle_t *_uht = (uv_handle_t *)(_x); _uht->data = _ctx; \ - _ctx->count_event_loop_static_asset_handles++; } -#define LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x) \ - ((struct lws_context *)((uv_handle_t *)((_x)->data))) -#define LWS_UV_REFCOUNT_STATIC_HANDLE_DESTROYED(_x) \ - (--(LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x)-> \ - count_event_loop_static_asset_handles)) - -struct lws_pt_eventlibs_libuv { - uv_loop_t *io_loop; - uv_signal_t signals[8]; - uv_timer_t sultimer; - uv_idle_t idle; -}; - -struct lws_context_eventlibs_libuv { - uv_loop_t loop; -}; - -struct lws_io_watcher_libuv { - uv_poll_t *pwatcher; -}; - -struct lws_signal_watcher_libuv { - uv_signal_t watcher; -}; - -extern struct lws_event_loop_ops event_loop_ops_uv; - -uv_loop_t * -lws_uv_getloop(struct lws_context *context, int tsi); - -int -lws_uv_plugins_init(struct lws_context *context, const char * const *d); - -int -lws_uv_plugins_destroy(struct lws_context *context); diff -Nru libwebsockets-4.0.20/lib/event-libs/libuv.c libwebsockets-2.4.2/lib/event-libs/libuv.c --- libwebsockets-4.0.20/lib/event-libs/libuv.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/libuv.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,706 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +void +lws_feature_status_libuv(struct lws_context_creation_info *info) +{ + if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBUV)) + lwsl_info("libuv support compiled in and enabled\n"); + else + lwsl_info("libuv support compiled in but disabled\n"); +} + +static void +lws_uv_idle(uv_idle_t *handle +#if UV_VERSION_MAJOR == 0 + , int status +#endif +) +{ + struct lws_context_per_thread *pt = lws_container_of(handle, + struct lws_context_per_thread, uv_idle); + + /* + * is there anybody with pending stuff that needs service forcing? + */ + if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) { + /* -1 timeout means just do forced service */ + _lws_plat_service_tsi(pt->context, -1, pt->tid); + /* still somebody left who wants forced service? */ + if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) + /* yes... come back again later */ + return; + } + + /* there is nobody who needs service forcing, shut down idle */ + uv_idle_stop(handle); +} + +static void +lws_io_cb(uv_poll_t *watcher, int status, int revents) +{ + struct lws_io_watcher *lws_io = lws_container_of(watcher, + struct lws_io_watcher, uv_watcher); + struct lws *wsi = lws_container_of(lws_io, struct lws, w_read); + struct lws_context *context = wsi->context; + struct lws_pollfd eventfd; + +#if defined(WIN32) || defined(_WIN32) + eventfd.fd = watcher->socket; +#else + eventfd.fd = watcher->io_watcher.fd; +#endif + eventfd.events = 0; + eventfd.revents = 0; + + if (status < 0) { + /* + * At this point status will be an UV error, like UV_EBADF, + * we treat all errors as LWS_POLLHUP + * + * You might want to return; instead of servicing the fd in + * some cases */ + if (status == UV_EAGAIN) + return; + + eventfd.events |= LWS_POLLHUP; + eventfd.revents |= LWS_POLLHUP; + } else { + if (revents & UV_READABLE) { + eventfd.events |= LWS_POLLIN; + eventfd.revents |= LWS_POLLIN; + } + if (revents & UV_WRITABLE) { + eventfd.events |= LWS_POLLOUT; + eventfd.revents |= LWS_POLLOUT; + } + } + lws_service_fd(context, &eventfd); + + uv_idle_start(&context->pt[(int)wsi->tsi].uv_idle, lws_uv_idle); +} + +LWS_VISIBLE void +lws_uv_sigint_cb(uv_signal_t *watcher, int signum) +{ + lwsl_err("internal signal handler caught signal %d\n", signum); + lws_libuv_stop(watcher->data); +} + +LWS_VISIBLE int +lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint, + uv_signal_cb cb) +{ + context->use_ev_sigint = use_uv_sigint; + if (cb) + context->lws_uv_sigint_cb = cb; + else + context->lws_uv_sigint_cb = &lws_uv_sigint_cb; + + return 0; +} + +static void +lws_uv_timeout_cb(uv_timer_t *timer +#if UV_VERSION_MAJOR == 0 + , int status +#endif +) +{ + struct lws_context_per_thread *pt = lws_container_of(timer, + struct lws_context_per_thread, uv_timeout_watcher); + + if (pt->context->requested_kill) + return; + + lwsl_debug("%s\n", __func__); + + lws_service_fd_tsi(pt->context, NULL, pt->tid); +} + +static const int sigs[] = { SIGINT, SIGTERM, SIGSEGV, SIGFPE, SIGHUP }; + +int +lws_uv_initvhost(struct lws_vhost* vh, struct lws* wsi) +{ + struct lws_context_per_thread *pt; + int n; + + if (!LWS_LIBUV_ENABLED(vh->context)) + return 0; + if (!wsi) + wsi = vh->lserv_wsi; + if (!wsi) + return 0; + if (wsi->w_read.context) + return 0; + + pt = &vh->context->pt[(int)wsi->tsi]; + if (!pt->io_loop_uv) + return 0; + + wsi->w_read.context = vh->context; + n = uv_poll_init_socket(pt->io_loop_uv, + &wsi->w_read.uv_watcher, wsi->desc.sockfd); + if (n) { + lwsl_err("uv_poll_init failed %d, sockfd=%p\n", + n, (void *)(lws_intptr_t)wsi->desc.sockfd); + + return -1; + } + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); + + return 0; +} + +/* + * This needs to be called after vhosts have been defined. + * + * If later, after server start, another vhost is added, this must be + * called again to bind the vhost + */ + +LWS_VISIBLE int +lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_vhost *vh = context->vhost_list; + int status = 0, n, ns, first = 1; + + if (!pt->io_loop_uv) { + if (!loop) { + loop = lws_malloc(sizeof(*loop), "libuv loop"); + if (!loop) { + lwsl_err("OOM\n"); + return -1; + } + #if UV_VERSION_MAJOR > 0 + uv_loop_init(loop); + #else + lwsl_err("This libuv is too old to work...\n"); + return 1; + #endif + pt->ev_loop_foreign = 0; + } else { + lwsl_notice(" Using foreign event loop...\n"); + pt->ev_loop_foreign = 1; + } + + pt->io_loop_uv = loop; + uv_idle_init(loop, &pt->uv_idle); + + ns = ARRAY_SIZE(sigs); + if (lws_check_opt(context->options, + LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN)) + ns = 2; + + if (pt->context->use_ev_sigint) { + assert(ns <= ARRAY_SIZE(pt->signals)); + for (n = 0; n < ns; n++) { + uv_signal_init(loop, &pt->signals[n]); + pt->signals[n].data = pt->context; + uv_signal_start(&pt->signals[n], + context->lws_uv_sigint_cb, + sigs[n]); + } + } + } else + first = 0; + + /* + * Initialize the accept wsi read watcher with all the listening sockets + * and register a callback for read operations + * + * We have to do it here because the uv loop(s) are not + * initialized until after context creation. + */ + while (vh) { + if (lws_uv_initvhost(vh, vh->lserv_wsi) == -1) + return -1; + vh = vh->vhost_next; + } + + if (first) { + uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher); + uv_timer_start(&pt->uv_timeout_watcher, lws_uv_timeout_cb, + 10, 1000); + } + + return status; +} + +static void lws_uv_close_cb(uv_handle_t *handle) +{ +} + +static void lws_uv_walk_cb(uv_handle_t *handle, void *arg) +{ + if (!uv_is_closing(handle)) + uv_close(handle, lws_uv_close_cb); +} + +LWS_VISIBLE void +lws_close_all_handles_in_loop(uv_loop_t *loop) +{ + uv_walk(loop, lws_uv_walk_cb, NULL); +} + +void +lws_libuv_destroyloop(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + int m, budget = 100, ns; + + if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) + return; + + if (!pt->io_loop_uv) + return; + + if (context->use_ev_sigint) { + uv_signal_stop(&pt->w_sigint.uv_watcher); + + ns = ARRAY_SIZE(sigs); + if (lws_check_opt(context->options, + LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN)) + ns = 2; + + for (m = 0; m < ns; m++) { + uv_signal_stop(&pt->signals[m]); + uv_close((uv_handle_t *)&pt->signals[m], lws_uv_close_cb); + } + } + + uv_timer_stop(&pt->uv_timeout_watcher); + uv_close((uv_handle_t *)&pt->uv_timeout_watcher, lws_uv_close_cb); + + uv_idle_stop(&pt->uv_idle); + uv_close((uv_handle_t *)&pt->uv_idle, lws_uv_close_cb); + + if (pt->ev_loop_foreign) + return; + + while (budget-- && uv_run(pt->io_loop_uv, UV_RUN_NOWAIT)) + ; + + uv_stop(pt->io_loop_uv); + uv_walk(pt->io_loop_uv, lws_uv_walk_cb, NULL); + while (uv_run(pt->io_loop_uv, UV_RUN_NOWAIT)) + ; +#if UV_VERSION_MAJOR > 0 + m = uv_loop_close(pt->io_loop_uv); + if (m == UV_EBUSY) + lwsl_err("%s: uv_loop_close: UV_EBUSY\n", __func__); +#endif + lws_free(pt->io_loop_uv); +} + +void +lws_libuv_accept(struct lws *wsi, lws_sock_file_fd_type desc) +{ + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + if (!LWS_LIBUV_ENABLED(context)) + return; + + wsi->w_read.context = context; + if (wsi->mode == LWSCM_RAW_FILEDESC) + uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher, + (int)desc.filefd); + else + uv_poll_init_socket(pt->io_loop_uv, &wsi->w_read.uv_watcher, + desc.sockfd); +} + +void +lws_libuv_io(struct lws *wsi, int flags) +{ + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct lws_io_watcher *w = &wsi->w_read; +//#if defined(WIN32) || defined(_WIN32) +// int current_events = w->uv_watcher.events & +// (UV_READABLE | UV_WRITABLE); +//#else + int current_events = w->actual_events & (UV_READABLE | UV_WRITABLE); +//#endif + + if (!LWS_LIBUV_ENABLED(context)) + return; + + // w->context is set after the loop is initialized + + if (!pt->io_loop_uv || !w->context) { + lwsl_info("%s: no io loop yet\n", __func__); + return; + } + + if (!((flags & (LWS_EV_START | LWS_EV_STOP)) && + (flags & (LWS_EV_READ | LWS_EV_WRITE)))) { + lwsl_err("%s: assert: flags %d", __func__, flags); + assert(0); + } + + if (flags & LWS_EV_START) { + if (flags & LWS_EV_WRITE) + current_events |= UV_WRITABLE; + + if (flags & LWS_EV_READ) + current_events |= UV_READABLE; + + uv_poll_start(&w->uv_watcher, current_events, lws_io_cb); + } else { + if (flags & LWS_EV_WRITE) + current_events &= ~UV_WRITABLE; + + if (flags & LWS_EV_READ) + current_events &= ~UV_READABLE; + + if (!(current_events & (UV_READABLE | UV_WRITABLE))) + uv_poll_stop(&w->uv_watcher); + else + uv_poll_start(&w->uv_watcher, current_events, + lws_io_cb); + } + + w->actual_events = current_events; +} + +int +lws_libuv_init_fd_table(struct lws_context *context) +{ + int n; + + if (!LWS_LIBUV_ENABLED(context)) + return 0; + + for (n = 0; n < context->count_threads; n++) + context->pt[n].w_sigint.context = context; + + return 1; +} + +LWS_VISIBLE void +lws_libuv_run(const struct lws_context *context, int tsi) +{ + if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context)) + uv_run(context->pt[tsi].io_loop_uv, 0); +} + +LWS_VISIBLE void +lws_libuv_stop_without_kill(const struct lws_context *context, int tsi) +{ + if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context)) + uv_stop(context->pt[tsi].io_loop_uv); +} + +static void +lws_libuv_kill(const struct lws_context *context) +{ + int n; + + for (n = 0; n < context->count_threads; n++) + if (context->pt[n].io_loop_uv && + LWS_LIBUV_ENABLED(context)) + uv_stop(context->pt[n].io_loop_uv); +} + +/* + * This does not actually stop the event loop. The reason is we have to pass + * libuv handle closures through its event loop. So this tries to close all + * wsi, and set a flag; when all the wsi closures are finalized then we + * actually stop the libuv event loops. + */ +LWS_VISIBLE void +lws_libuv_stop(struct lws_context *context) +{ + struct lws_context_per_thread *pt; + int n, m; + + if (context->requested_kill) + return; + + context->requested_kill = 1; + + m = context->count_threads; + context->being_destroyed = 1; + + while (m--) { + pt = &context->pt[m]; + + for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) { + struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd); + + if (!wsi) + continue; + lws_close_free_wsi(wsi, + LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY + /* no protocol close */); + n--; + } + } + + lwsl_info("%s: feels everything closed\n", __func__); + if (context->count_wsi_allocated == 0) + lws_libuv_kill(context); +} + +LWS_VISIBLE uv_loop_t * +lws_uv_getloop(struct lws_context *context, int tsi) +{ + if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context)) + return context->pt[tsi].io_loop_uv; + + return NULL; +} + +static void +lws_libuv_closewsi(uv_handle_t* handle) +{ + struct lws *n = NULL, *wsi = (struct lws *)(((char *)handle) - + (char *)(&n->w_read.uv_watcher)); + struct lws_context *context = lws_get_context(wsi); + int lspd = 0; + + if (wsi->mode == LWSCM_SERVER_LISTENER && + wsi->context->deprecated) { + lspd = 1; + context->deprecation_pending_listen_close_count--; + if (!context->deprecation_pending_listen_close_count) + lspd = 2; + } + + lws_close_free_wsi_final(wsi); + + if (lspd == 2 && context->deprecation_cb) { + lwsl_notice("calling deprecation callback\n"); + context->deprecation_cb(); + } + + if (context->requested_kill && context->count_wsi_allocated == 0) + lws_libuv_kill(context); +} + +void +lws_libuv_closehandle(struct lws *wsi) +{ + struct lws_context *context = lws_get_context(wsi); + + /* required to defer actual deletion until libuv has processed it */ + uv_close((uv_handle_t*)&wsi->w_read.uv_watcher, lws_libuv_closewsi); + + if (context->requested_kill && context->count_wsi_allocated == 0) + lws_libuv_kill(context); +} + +static void +lws_libuv_closewsi_m(uv_handle_t* handle) +{ + lws_sockfd_type sockfd = (lws_sockfd_type)(lws_intptr_t)handle->data; + + compatible_close(sockfd); +} + +void +lws_libuv_closehandle_manually(struct lws *wsi) +{ + uv_handle_t *h = (void *)&wsi->w_read.uv_watcher; + + h->data = (void *)(lws_intptr_t)wsi->desc.sockfd; + /* required to defer actual deletion until libuv has processed it */ + uv_close((uv_handle_t*)&wsi->w_read.uv_watcher, lws_libuv_closewsi_m); +} + +int +lws_libuv_check_watcher_active(struct lws *wsi) +{ + uv_handle_t *h = (void *)&wsi->w_read.uv_watcher; + + return uv_is_active(h); +} + + +#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) + +LWS_VISIBLE int +lws_plat_plugins_init(struct lws_context *context, const char * const *d) +{ + struct lws_plugin_capability lcaps; + struct lws_plugin *plugin; + lws_plugin_init_func initfunc; + int m, ret = 0; + void *v; + uv_dirent_t dent; + uv_fs_t req; + char path[256]; + uv_lib_t lib; + int pofs = 0; + +#if defined(__MINGW32__) || !defined(WIN32) + pofs = 3; +#endif + + lib.errmsg = NULL; + lib.handle = NULL; + + uv_loop_init(&context->pu_loop); + + lwsl_notice(" Plugins:\n"); + + while (d && *d) { + + lwsl_notice(" Scanning %s\n", *d); + m =uv_fs_scandir(&context->pu_loop, &req, *d, 0, NULL); + if (m < 1) { + lwsl_err("Scandir on %s failed\n", *d); + return 1; + } + + while (uv_fs_scandir_next(&req, &dent) != UV_EOF) { + if (strlen(dent.name) < 7) + continue; + + lwsl_notice(" %s\n", dent.name); + + lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d, + dent.name); + if (uv_dlopen(path, &lib)) { + uv_dlerror(&lib); + lwsl_err("Error loading DSO: %s\n", lib.errmsg); + uv_dlclose(&lib); + goto bail; + } + + /* we could open it, can we get his init function? */ + +#if !defined(WIN32) && !defined(__MINGW32__) + m = lws_snprintf(path, sizeof(path) - 1, "init_%s", + dent.name + pofs /* snip lib... */); + path[m - 3] = '\0'; /* snip the .so */ +#else + m = lws_snprintf(path, sizeof(path) - 1, "init_%s", + dent.name + pofs); + path[m - 4] = '\0'; /* snip the .dll */ +#endif + if (uv_dlsym(&lib, path, &v)) { + uv_dlerror(&lib); + lwsl_err("Failed to get %s on %s: %s", path, + dent.name, lib.errmsg); + uv_dlclose(&lib); + goto bail; + } + initfunc = (lws_plugin_init_func)v; + lcaps.api_magic = LWS_PLUGIN_API_MAGIC; + m = initfunc(context, &lcaps); + if (m) { + lwsl_err("Init %s failed %d\n", dent.name, m); + goto skip; + } + + plugin = lws_malloc(sizeof(*plugin), "plugin"); + if (!plugin) { + uv_dlclose(&lib); + lwsl_err("OOM\n"); + goto bail; + } + plugin->list = context->plugin_list; + context->plugin_list = plugin; + strncpy(plugin->name, dent.name, sizeof(plugin->name) - 1); + plugin->name[sizeof(plugin->name) - 1] = '\0'; + plugin->lib = lib; + plugin->caps = lcaps; + context->plugin_protocol_count += lcaps.count_protocols; + context->plugin_extension_count += lcaps.count_extensions; + + continue; + +skip: + uv_dlclose(&lib); + } +bail: + uv_fs_req_cleanup(&req); + d++; + } + + return ret; +} + +LWS_VISIBLE int +lws_plat_plugins_destroy(struct lws_context *context) +{ + struct lws_plugin *plugin = context->plugin_list, *p; + lws_plugin_destroy_func func; + char path[256]; + int pofs = 0; + void *v; + int m; + +#if defined(__MINGW32__) || !defined(WIN32) + pofs = 3; +#endif + + if (!plugin) + return 0; + + while (plugin) { + p = plugin; + +#if !defined(WIN32) && !defined(__MINGW32__) + m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", + plugin->name + pofs); + path[m - 3] = '\0'; +#else + m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", + plugin->name + pofs); + path[m - 4] = '\0'; +#endif + + if (uv_dlsym(&plugin->lib, path, &v)) { + uv_dlerror(&plugin->lib); + lwsl_err("Failed to get %s on %s: %s", path, + plugin->name, plugin->lib.errmsg); + } else { + func = (lws_plugin_destroy_func)v; + m = func(context); + if (m) + lwsl_err("Destroying %s failed %d\n", + plugin->name, m); + } + + uv_dlclose(&p->lib); + plugin = p->list; + p->list = NULL; + free(p); + } + + context->plugin_list = NULL; + + while (uv_loop_close(&context->pu_loop)) + ; + + return 0; +} + +#endif + diff -Nru libwebsockets-4.0.20/lib/event-libs/poll/poll.c libwebsockets-2.4.2/lib/event-libs/poll/poll.c --- libwebsockets-4.0.20/lib/event-libs/poll/poll.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/poll/poll.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - */ - -#include - -struct lws_event_loop_ops event_loop_ops_poll = { - /* name */ "poll", - /* init_context */ NULL, - /* destroy_context1 */ NULL, - /* destroy_context2 */ NULL, - /* init_vhost_listen_wsi */ NULL, - /* init_pt */ NULL, - /* wsi_logical_close */ NULL, - /* check_client_connect_ok */ NULL, - /* close_handle_manually */ NULL, - /* accept */ NULL, - /* io */ NULL, - /* run */ NULL, - /* destroy_pt */ NULL, - /* destroy wsi */ NULL, - - /* flags */ LELOF_ISPOLL, -}; diff -Nru libwebsockets-4.0.20/lib/event-libs/poll/private-lib-event-libs-poll.h libwebsockets-2.4.2/lib/event-libs/poll/private-lib-event-libs-poll.h --- libwebsockets-4.0.20/lib/event-libs/poll/private-lib-event-libs-poll.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/poll/private-lib-event-libs-poll.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 struct lws_event_loop_ops event_loop_ops_poll; diff -Nru libwebsockets-4.0.20/lib/event-libs/private-lib-event-libs.h libwebsockets-2.4.2/lib/event-libs/private-lib-event-libs.h --- libwebsockets-4.0.20/lib/event-libs/private-lib-event-libs.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/private-lib-event-libs.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - */ - -enum lws_event_lib_ops_flags { - LELOF_ISPOLL = (1 >> 0), - LELOF_DESTROY_FINAL = (1 >> 1), -}; - -struct lws_event_loop_ops { - const char *name; - /* event loop-specific context init during context creation */ - int (*init_context)(struct lws_context *context, - const struct lws_context_creation_info *info); - /* called during lws_destroy_context */ - int (*destroy_context1)(struct lws_context *context); - /* called during lws_destroy_context2 */ - int (*destroy_context2)(struct lws_context *context); - /* init vhost listening wsi */ - int (*init_vhost_listen_wsi)(struct lws *wsi); - /* init the event loop for a pt */ - int (*init_pt)(struct lws_context *context, void *_loop, int tsi); - /* called at end of first phase of close_free_wsi() */ - int (*wsi_logical_close)(struct lws *wsi); - /* return nonzero if client connect not allowed */ - int (*check_client_connect_ok)(struct lws *wsi); - /* close handle manually */ - void (*close_handle_manually)(struct lws *wsi); - /* event loop accept processing */ - int (*sock_accept)(struct lws *wsi); - /* control wsi active events */ - void (*io)(struct lws *wsi, int flags); - /* run the event loop for a pt */ - void (*run_pt)(struct lws_context *context, int tsi); - /* called before pt is destroyed */ - void (*destroy_pt)(struct lws_context *context, int tsi); - /* called just before wsi is freed */ - void (*destroy_wsi)(struct lws *wsi); - - uint8_t flags; -}; - -/* bring in event libs private declarations */ - -#if defined(LWS_WITH_POLL) -#include "private-lib-event-libs-poll.h" -#endif - -#if defined(LWS_WITH_LIBUV) -#include "private-lib-event-libs-libuv.h" -#endif - -#if defined(LWS_WITH_LIBEVENT) -#include "private-lib-event-libs-libevent.h" -#endif - -#if defined(LWS_WITH_GLIB) -#include "private-lib-event-libs-glib.h" -#endif - -#if defined(LWS_WITH_LIBEV) -#include "private-lib-event-libs-libev.h" -#endif - diff -Nru libwebsockets-4.0.20/lib/event-libs/README.md libwebsockets-2.4.2/lib/event-libs/README.md --- libwebsockets-4.0.20/lib/event-libs/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/event-libs/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -## Information for new event lib implementers - -### Introduction - -By default lws has built-in support for POSIX poll() as the event loop. - -However either to get access to epoll() or other platform specific better -poll waits, or to integrate with existing applications already using a -specific event loop, it can be desirable for lws to use another external -event library, like libuv, libevent or libev. - -### Code placement - -The code specific to the event library should live in `./lib/event-libs/**lib name**` - -### Allowing control over enabling event libs - -All event libs should add a cmake define `LWS_WITH_**lib 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 event lib is available in the lws build it is -trying to bind to. - -If the event lib is disabled in cmake, nothing in its directory is built or referenced. - -### Event loop ops struct - -The event lib support is defined by `struct lws_event_loop_ops` in `lib/event-libs/private-lib-event-libs.h`, -each event lib support instantiates one of these and fills in the appropriate ops -callbacks to perform its job. By convention that lives in -`./lib/event-libs/**lib name**/**lib_name**.c`. - -### Private event lib declarations - -Truly private declarations for the event lib can go in the event-libs directory as you like. -However when the declarations must be accessible to other things in lws build, eg, -the event lib support adds members to `struct lws` when enabled, they should be in the -event lib support directory in a file `private-lib-event-libs-myeventlib.h`. - -Search for "bring in event libs private declarations" in `./lib/core/private-lib-core.h -and add your private event lib support file there following the style used for the other -event libs, eg, - -``` -#if defined(LWS_WITH_LIBUV) - #include "event-libs/libuv/private-lib-event-libs-libuv.h" -#endif -``` - -If the event lib support is disabled at cmake, nothing from its private.h should be used anywhere. - -### Integrating event lib assets to lws - -If your event lib 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 libuv event - lib support need to add its own assets in the perthread struct, it declares in its private.h - -``` -struct lws_pt_eventlibs_libuv { - uv_loop_t *io_loop; - uv_signal_t signals[8]; - uv_timer_t timeout_watcher; - uv_timer_t hrtimer; - uv_idle_t idle; -}; -``` - - - add your event lib content in one place in the related lws struct, protected by `#if defined(LWS_WITH_**lib name**)`, - eg, again for LWS_WITH_LIBUV - -``` -struct lws_context_per_thread { - -... - -#if defined(LWS_WITH_LIBUV) - struct lws_pt_eventlibs_libuv uv; -#endif - -... -``` - -### Adding to lws available event libs list - -Edit the NULL-terminated array `available_event_libs` at the top of `./lib/context.c` to include -a pointer to your new event lib support's ops struct, following the style already there. - -``` -const struct lws_event_loop_ops *available_event_libs[] = { -#if defined(LWS_WITH_POLL) - &event_loop_ops_poll, -#endif -#if defined(LWS_WITH_LIBUV) - &event_loop_ops_uv, -#endif -... -``` - -This is used to provide a list of avilable configured backends. - -### Enabling event lib adoption - -You need to add a `LWS_SERVER_OPTION...` flag as necessary in `./lib/libwebsockets.h` -`enum lws_context_options`, and follow the existing code in `lws_create_context()` -to convert the flag into binding your ops struct to the context. - -### Implementation of the event lib bindings - -Study eg libuv implementation, using the available ops in the struct lws_event_loop_ops -as a guide. - -### Destruction - -Ending the event loop is generally a bit tricky, because if the event loop is internal -to the lws context, you cannot destroy it while the event loop is running. - -Don't add special exports... we tried that, it's a huge mess. The same user code should be able -work with any of the event loops including poll. - -The solution we found was hide the different processing necessary for the different cases in -lws_destroy_context(). To help with that there are ops available at two different places in -the context destroy processing. - diff -Nru libwebsockets-4.0.20/lib/ext/extension.c libwebsockets-2.4.2/lib/ext/extension.c --- libwebsockets-4.0.20/lib/ext/extension.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/ext/extension.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,344 @@ +#include "private-libwebsockets.h" + +#include "extension-permessage-deflate.h" + +LWS_VISIBLE void +lws_context_init_extensions(struct lws_context_creation_info *info, + struct lws_context *context) +{ + lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE); +} + +enum lws_ext_option_parser_states { + LEAPS_SEEK_NAME, + LEAPS_EAT_NAME, + LEAPS_SEEK_VAL, + LEAPS_EAT_DEC, + LEAPS_SEEK_ARG_TERM +}; + +LWS_VISIBLE int +lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi, + void *ext_user, const struct lws_ext_options *opts, + const char *in, int len) +{ + enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME; + unsigned int match_map = 0, n, m, w = 0, count_options = 0, + pending_close_quote = 0; + struct lws_ext_option_arg oa; + + oa.option_name = NULL; + + while (opts[count_options].name) + count_options++; + while (len) { + lwsl_ext("'%c' %d", *in, leap); + switch (leap) { + case LEAPS_SEEK_NAME: + if (*in == ' ') + break; + if (*in == ',') { + len = 1; + break; + } + match_map = (1 << count_options) - 1; + leap = LEAPS_EAT_NAME; + w = 0; + + /* fallthru */ + + case LEAPS_EAT_NAME: + oa.start = NULL; + oa.len = 0; + m = match_map; + n = 0; + pending_close_quote = 0; + while (m) { + if (m & 1) { + lwsl_ext(" m=%d, n=%d, w=%d\n", m, n, w); + + if (*in == opts[n].name[w]) { + if (!opts[n].name[w + 1]) { + oa.option_index = n; + lwsl_ext("hit %d\n", oa.option_index); + leap = LEAPS_SEEK_VAL; + if (len == 1) + goto set_arg; + break; + } + } else { + match_map &= ~(1 << n); + if (!match_map) { + lwsl_ext("empty match map\n"); + return -1; + } + } + } + m >>= 1; + n++; + } + w++; + break; + case LEAPS_SEEK_VAL: + if (*in == ' ') + break; + if (*in == ',') { + len = 1; + break; + } + if (*in == ';' || len == 1) { /* ie,nonoptional */ + if (opts[oa.option_index].type == EXTARG_DEC) + return -1; + leap = LEAPS_SEEK_NAME; + goto set_arg; + } + if (*in == '=') { + w = 0; + pending_close_quote = 0; + if (opts[oa.option_index].type == EXTARG_NONE) + return -1; + + leap = LEAPS_EAT_DEC; + break; + } + return -1; + + case LEAPS_EAT_DEC: + if (*in >= '0' && *in <= '9') { + if (!w) + oa.start = in; + w++; + if (len != 1) + break; + } + if (!w && *in =='"') { + pending_close_quote = 1; + break; + } + if (!w) + return -1; + if (pending_close_quote && *in != '"' && len != 1) + return -1; + leap = LEAPS_SEEK_ARG_TERM; + if (oa.start) + oa.len = in - oa.start; + if (len == 1) + oa.len++; + +set_arg: + ext->callback(lws_get_context(wsi), + ext, wsi, LWS_EXT_CB_OPTION_SET, + ext_user, (char *)&oa, 0); + if (len == 1) + break; + if (pending_close_quote && *in == '"') + break; + + /* fallthru */ + + case LEAPS_SEEK_ARG_TERM: + if (*in == ' ') + break; + if (*in == ';') { + leap = LEAPS_SEEK_NAME; + break; + } + if (*in == ',') { + len = 1; + break; + } + return -1; + } + len--; + in++; + } + + return 0; +} + + +/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */ + +int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len) +{ + int n, m, handled = 0; + + for (n = 0; n < wsi->count_act_ext; n++) { + m = wsi->active_extensions[n]->callback(lws_get_context(wsi), + wsi->active_extensions[n], wsi, reason, + wsi->act_ext_user[n], arg, len); + if (m < 0) { + lwsl_ext("Ext '%s' failed to handle callback %d!\n", + wsi->active_extensions[n]->name, reason); + return -1; + } + /* valgrind... */ + if (reason == LWS_EXT_CB_DESTROY) + wsi->act_ext_user[n] = NULL; + if (m > handled) + handled = m; + } + + return handled; +} + +int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, + int reason, void *arg, int len) +{ + int n = 0, m, handled = 0; + const struct lws_extension *ext; + + if (!wsi || !wsi->vhost) + return 0; + + ext = wsi->vhost->extensions; + + while (ext && ext->callback && !handled) { + m = ext->callback(context, ext, wsi, reason, + (void *)(lws_intptr_t)n, arg, len); + if (m < 0) { + lwsl_ext("Ext '%s' failed to handle callback %d!\n", + wsi->active_extensions[n]->name, reason); + return -1; + } + if (m) + handled = 1; + + ext++; + n++; + } + + return 0; +} + +int +lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len) +{ + struct lws_tokens eff_buf; + int ret, m, n = 0; + + eff_buf.token = (char *)buf; + eff_buf.token_len = len; + + /* + * while we have original buf to spill ourselves, or extensions report + * more in their pipeline + */ + + ret = 1; + while (ret == 1) { + + /* default to nobody has more to spill */ + + ret = 0; + + /* show every extension the new incoming data */ + m = lws_ext_cb_active(wsi, + LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0); + if (m < 0) + return -1; + if (m) /* handled */ + ret = 1; + + if ((char *)buf != eff_buf.token) + /* + * extension recreated it: + * need to buffer this if not all sent + */ + wsi->u.ws.clean_buffer = 0; + + /* assuming they left us something to send, send it */ + + if (eff_buf.token_len) { + n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) { + lwsl_info("closing from ext access\n"); + return -1; + } + + /* always either sent it all or privately buffered */ + if (wsi->u.ws.clean_buffer) + len = n; + } + + lwsl_parser("written %d bytes to client\n", n); + + /* no extension has more to spill? Then we can go */ + + if (!ret) + break; + + /* we used up what we had */ + + eff_buf.token = NULL; + eff_buf.token_len = 0; + + /* + * Did that leave the pipe choked? + * Or we had to hold on to some of it? + */ + + if (!lws_send_pipe_choked(wsi) && !wsi->trunc_len) + /* no we could add more, lets's do that */ + continue; + + lwsl_debug("choked\n"); + + /* + * Yes, he's choked. Don't spill the rest now get a callback + * when he is ready to send and take care of it there + */ + lws_callback_on_writable(wsi); + wsi->extension_data_pending = 1; + ret = 0; + } + + return len; +} + +int +lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r, + void *v, size_t len) +{ + struct lws_context *context = wsi->context; + int n, handled = 0; + + /* maybe an extension will take care of it for us */ + + for (n = 0; n < wsi->count_act_ext && !handled; n++) { + if (!wsi->active_extensions[n]->callback) + continue; + + handled |= wsi->active_extensions[n]->callback(context, + wsi->active_extensions[n], wsi, + r, wsi->act_ext_user[n], v, len); + } + + return handled; +} + +int +lws_set_extension_option(struct lws *wsi, const char *ext_name, + const char *opt_name, const char *opt_val) +{ + struct lws_ext_option_arg oa; + int idx = 0; + + /* first identify if the ext is active on this wsi */ + while (idx < wsi->count_act_ext && + strcmp(wsi->active_extensions[idx]->name, ext_name)) + idx++; + + if (idx == wsi->count_act_ext) + return -1; /* request ext not active on this wsi */ + + oa.option_name = opt_name; + oa.option_index = 0; + oa.start = opt_val; + oa.len = 0; + + return wsi->active_extensions[idx]->callback( + wsi->context, wsi->active_extensions[idx], wsi, + LWS_EXT_CB_NAMED_OPTION_SET, wsi->act_ext_user[idx], &oa, 0); +} diff -Nru libwebsockets-4.0.20/lib/ext/extension-permessage-deflate.c libwebsockets-2.4.2/lib/ext/extension-permessage-deflate.c --- libwebsockets-4.0.20/lib/ext/extension-permessage-deflate.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/ext/extension-permessage-deflate.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,473 @@ +/* + * ./lib/extension-permessage-deflate.c + * + * Copyright (C) 2016 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" +#include "extension-permessage-deflate.h" +#include +#include +#include + +#define LWS_ZLIB_MEMLEVEL 8 + +const struct lws_ext_options lws_ext_pm_deflate_options[] = { + /* public RFC7692 settings */ + { "server_no_context_takeover", EXTARG_NONE }, + { "client_no_context_takeover", EXTARG_NONE }, + { "server_max_window_bits", EXTARG_OPT_DEC }, + { "client_max_window_bits", EXTARG_OPT_DEC }, + /* ones only user code can set */ + { "rx_buf_size", EXTARG_DEC }, + { "tx_buf_size", EXTARG_DEC }, + { "compression_level", EXTARG_DEC }, + { "mem_level", EXTARG_DEC }, + { NULL, 0 }, /* sentinel */ +}; + +static void +lws_extension_pmdeflate_restrict_args(struct lws *wsi, + struct lws_ext_pm_deflate_priv *priv) +{ + int n, extra; + + /* cap the RX buf at the nearest power of 2 to protocol rx buf */ + + n = wsi->context->pt_serv_buf_size; + if (wsi->protocol->rx_buffer_size) + n = wsi->protocol->rx_buffer_size; + + extra = 7; + while (n >= 1 << (extra + 1)) + extra++; + + if (extra < priv->args[PMD_RX_BUF_PWR2]) { + priv->args[PMD_RX_BUF_PWR2] = extra; + lwsl_info(" Capping pmd rx to %d\n", 1 << extra); + } +} + +LWS_VISIBLE int +lws_extension_callback_pm_deflate(struct lws_context *context, + const struct lws_extension *ext, + struct lws *wsi, + enum lws_extension_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct lws_ext_pm_deflate_priv *priv = + (struct lws_ext_pm_deflate_priv *)user; + struct lws_tokens *eff_buf = (struct lws_tokens *)in; + static unsigned char trail[] = { 0, 0, 0xff, 0xff }; + int n, ret = 0, was_fin = 0, extra; + struct lws_ext_option_arg *oa; + + switch (reason) { + case LWS_EXT_CB_NAMED_OPTION_SET: + oa = in; + if (!oa->option_name) + break; + for (n = 0; n < ARRAY_SIZE(lws_ext_pm_deflate_options); n++) + if (!strcmp(lws_ext_pm_deflate_options[n].name, oa->option_name)) + break; + + if (n == ARRAY_SIZE(lws_ext_pm_deflate_options)) + break; + oa->option_index = n; + + /* fallthru */ + + case LWS_EXT_CB_OPTION_SET: + oa = in; + lwsl_notice("%s: option set: idx %d, %s, len %d\n", __func__, + oa->option_index, oa->start, oa->len); + if (oa->start) + priv->args[oa->option_index] = atoi(oa->start); + else + priv->args[oa->option_index] = 1; + + if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8) + priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9; + + lws_extension_pmdeflate_restrict_args(wsi, priv); + break; + + case LWS_EXT_CB_OPTION_CONFIRM: + if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 || + priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 || + priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 || + priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15) + return -1; + break; + + case LWS_EXT_CB_CLIENT_CONSTRUCT: + case LWS_EXT_CB_CONSTRUCT: + + n = context->pt_serv_buf_size; + if (wsi->protocol->rx_buffer_size) + n = wsi->protocol->rx_buffer_size; + + if (n < 128) { + lwsl_info(" permessage-deflate requires the protocol (%s) to have an RX buffer >= 128\n", + wsi->protocol->name); + return -1; + } + + /* fill in **user */ + priv = lws_zalloc(sizeof(*priv), "pmd priv"); + *((void **)user) = priv; + lwsl_ext("%s: LWS_EXT_CB_*CONSTRUCT\n", __func__); + memset(priv, 0, sizeof(*priv)); + + /* fill in pointer to options list */ + if (in) + *((const struct lws_ext_options **)in) = + lws_ext_pm_deflate_options; + + /* fallthru */ + + case LWS_EXT_CB_OPTION_DEFAULT: + + /* set the public, RFC7692 defaults... */ + + priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0, + priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0; + priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15; + priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15; + + /* ...and the ones the user code can override */ + + priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */ + priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */ + priv->args[PMD_COMP_LEVEL] = 1; + priv->args[PMD_MEM_LEVEL] = 8; + + lws_extension_pmdeflate_restrict_args(wsi, priv); + break; + + case LWS_EXT_CB_DESTROY: + lwsl_ext("%s: LWS_EXT_CB_DESTROY\n", __func__); + lws_free(priv->buf_rx_inflated); + lws_free(priv->buf_tx_deflated); + if (priv->rx_init) + (void)inflateEnd(&priv->rx); + if (priv->tx_init) + (void)deflateEnd(&priv->tx); + lws_free(priv); + return ret; + + case LWS_EXT_CB_PAYLOAD_RX: + lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n", + __func__, eff_buf->token_len, priv->rx.avail_in); + if (!(wsi->u.ws.rsv_first_msg & 0x40)) + return 0; + +#if 0 + for (n = 0; n < eff_buf->token_len; n++) { + printf("%02X ", (unsigned char)eff_buf->token[n]); + if ((n & 15) == 15) + printf("\n"); + } + printf("\n"); +#endif + if (!priv->rx_init) + if (inflateInit2(&priv->rx, -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) { + lwsl_err("%s: iniflateInit failed\n", __func__); + return -1; + } + priv->rx_init = 1; + if (!priv->buf_rx_inflated) + priv->buf_rx_inflated = lws_malloc(LWS_PRE + 7 + 5 + + (1 << priv->args[PMD_RX_BUF_PWR2]), "pmd rx inflate buf"); + if (!priv->buf_rx_inflated) { + lwsl_err("%s: OOM\n", __func__); + return -1; + } + + /* + * We have to leave the input stream alone if we didn't + * finish with it yet. The input stream is held in the wsi + * rx buffer by the caller, so this assumption is safe while + * we block new rx while draining the existing rx + */ + if (!priv->rx.avail_in && eff_buf->token && eff_buf->token_len) { + priv->rx.next_in = (unsigned char *)eff_buf->token; + priv->rx.avail_in = eff_buf->token_len; + } + priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE; + eff_buf->token = (char *)priv->rx.next_out; + priv->rx.avail_out = 1 << priv->args[PMD_RX_BUF_PWR2]; + + if (priv->rx_held_valid) { + lwsl_ext("-- RX piling on held byte --\n"); + *(priv->rx.next_out++) = priv->rx_held; + priv->rx.avail_out--; + priv->rx_held_valid = 0; + } + + /* if... + * + * - he has no remaining input content for this message, and + * - and this is the final fragment, and + * - we used everything that could be drained on the input side + * + * ...then put back the 00 00 FF FF the sender stripped as our + * input to zlib + */ + if (!priv->rx.avail_in && wsi->u.ws.final && + !wsi->u.ws.rx_packet_length) { + lwsl_ext("RX APPEND_TRAILER-DO\n"); + was_fin = 1; + priv->rx.next_in = trail; + priv->rx.avail_in = sizeof(trail); + } + + n = inflate(&priv->rx, Z_NO_FLUSH); + lwsl_ext("inflate ret %d, avi %d, avo %d, wsifinal %d\n", n, + priv->rx.avail_in, priv->rx.avail_out, wsi->u.ws.final); + switch (n) { + case Z_NEED_DICT: + case Z_STREAM_ERROR: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + lwsl_info("zlib error inflate %d: %s\n", + n, priv->rx.msg); + return -1; + } + /* + * If we did not already send in the 00 00 FF FF, and he's + * out of input, he did not EXACTLY fill the output buffer + * (which is ambiguous and we will force it to go around + * again by withholding a byte), and he's otherwise working on + * being a FIN fragment, then do the FIN message processing + * of faking up the 00 00 FF FF that the sender stripped. + */ + if (!priv->rx.avail_in && wsi->u.ws.final && + !wsi->u.ws.rx_packet_length && !was_fin && + priv->rx.avail_out /* ambiguous as to if it is the end */ + ) { + lwsl_ext("RX APPEND_TRAILER-DO\n"); + was_fin = 1; + priv->rx.next_in = trail; + priv->rx.avail_in = sizeof(trail); + n = inflate(&priv->rx, Z_SYNC_FLUSH); + lwsl_ext("RX trailer inf returned %d, avi %d, avo %d\n", n, + priv->rx.avail_in, priv->rx.avail_out); + switch (n) { + case Z_NEED_DICT: + case Z_STREAM_ERROR: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + lwsl_info("zlib error inflate %d: %s\n", + n, priv->rx.msg); + return -1; + } + } + /* + * we must announce in our returncode now if there is more + * output to be expected from inflate, so we can decide to + * set the FIN bit on this bufferload or not. However zlib + * is ambiguous when we exactly filled the inflate buffer. It + * does not give us a clue as to whether we should understand + * that to mean he ended on a buffer boundary, or if there is + * more in the pipeline. + * + * So to work around that safely, if it used all output space + * exactly, we ALWAYS say there is more coming and we withhold + * the last byte of the buffer to guarantee that is true. + * + * That still leaves us at least one byte to finish with a FIN + * on, even if actually nothing more is coming from the next + * inflate action itself. + */ + if (!priv->rx.avail_out) { /* he used all available out buf */ + lwsl_ext("-- rx grabbing held --\n"); + /* snip the last byte and hold it for next time */ + priv->rx_held = *(--priv->rx.next_out); + priv->rx_held_valid = 1; + } + + eff_buf->token_len = (char *)priv->rx.next_out - eff_buf->token; + priv->count_rx_between_fin += eff_buf->token_len; + + lwsl_ext(" %s: RX leaving with new effbuff len %d, " + "ret %d, rx.avail_in=%d, TOTAL RX since FIN %lu\n", + __func__, eff_buf->token_len, priv->rx_held_valid, + priv->rx.avail_in, + (unsigned long)priv->count_rx_between_fin); + + if (was_fin) { + priv->count_rx_between_fin = 0; + if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) { + (void)inflateEnd(&priv->rx); + priv->rx_init = 0; + } + } +#if 0 + for (n = 0; n < eff_buf->token_len; n++) + putchar(eff_buf->token[n]); + puts("\n"); +#endif + + return priv->rx_held_valid; + + case LWS_EXT_CB_PAYLOAD_TX: + + if (!priv->tx_init) { + n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL], + Z_DEFLATED, + -priv->args[PMD_SERVER_MAX_WINDOW_BITS + + (wsi->vhost->listen_port <= 0)], + priv->args[PMD_MEM_LEVEL], + Z_DEFAULT_STRATEGY); + if (n != Z_OK) { + lwsl_ext("inflateInit2 failed %d\n", n); + return 1; + } + } + priv->tx_init = 1; + if (!priv->buf_tx_deflated) + priv->buf_tx_deflated = lws_malloc(LWS_PRE + 7 + 5 + + (1 << priv->args[PMD_TX_BUF_PWR2]), "pmd tx deflate buf"); + if (!priv->buf_tx_deflated) { + lwsl_err("%s: OOM\n", __func__); + return -1; + } + + if (eff_buf->token) { + lwsl_ext("%s: TX: eff_buf length %d\n", __func__, + eff_buf->token_len); + priv->tx.next_in = (unsigned char *)eff_buf->token; + priv->tx.avail_in = eff_buf->token_len; + } + +#if 0 + for (n = 0; n < eff_buf->token_len; n++) { + printf("%02X ", (unsigned char)eff_buf->token[n]); + if ((n & 15) == 15) + printf("\n"); + } + printf("\n"); +#endif + + priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5; + eff_buf->token = (char *)priv->tx.next_out; + priv->tx.avail_out = 1 << priv->args[PMD_TX_BUF_PWR2]; + + n = deflate(&priv->tx, Z_SYNC_FLUSH); + if (n == Z_STREAM_ERROR) { + lwsl_ext("%s: Z_STREAM_ERROR\n", __func__); + return -1; + } + + if (priv->tx_held_valid) { + priv->tx_held_valid = 0; + if (priv->tx.avail_out == 1 << priv->args[PMD_TX_BUF_PWR2]) + /* + * we can get a situation he took something in + * but did not generate anything out, at the end + * of a message (eg, next thing he sends is 80 + * 00, a zero length FIN, like Authobahn can + * send). + * If we have come back as a FIN, we must not + * place the pending trailer 00 00 FF FF, just + * the 1 byte of live data + */ + *(--eff_buf->token) = priv->tx_held[0]; + else { + /* he generated data, prepend whole pending */ + eff_buf->token -= 5; + for (n = 0; n < 5; n++) + eff_buf->token[n] = priv->tx_held[n]; + + } + } + priv->compressed_out = 1; + eff_buf->token_len = (int)(priv->tx.next_out - + (unsigned char *)eff_buf->token); + + /* + * we must announce in our returncode now if there is more + * output to be expected from inflate, so we can decide to + * set the FIN bit on this bufferload or not. However zlib + * is ambiguous when we exactly filled the inflate buffer. It + * does not give us a clue as to whether we should understand + * that to mean he ended on a buffer boundary, or if there is + * more in the pipeline. + * + * Worse, the guy providing the stuff we are sending may not + * know until after that this was, actually, the last chunk, + * that can happen even if we did not fill the output buf, ie + * he may send after this a zero-length FIN fragment. + * + * This is super difficult because we must snip the last 4 + * bytes in the case this is the last compressed output of the + * message. The only way to deal with it is defer sending the + * last 5 bytes of each frame until the next one, when we will + * be in a position to understand if that has a FIN or not. + */ + + extra = !!(len & LWS_WRITE_NO_FIN) || !priv->tx.avail_out; + + if (eff_buf->token_len >= 4 + extra) { + lwsl_ext("tx held %d\n", 4 + extra); + priv->tx_held_valid = extra; + for (n = 3 + extra; n >= 0; n--) + priv->tx_held[n] = *(--priv->tx.next_out); + eff_buf->token_len -= 4 + extra; + } + lwsl_ext(" TX rewritten with new effbuff len %d, ret %d\n", + eff_buf->token_len, !priv->tx.avail_out); + + return !priv->tx.avail_out; /* 1 == have more tx pending */ + + case LWS_EXT_CB_PACKET_TX_PRESEND: + if (!priv->compressed_out) + break; + priv->compressed_out = 0; + + if ((*(eff_buf->token) & 0x80) && + priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) { + lwsl_debug("PMD_CLIENT_NO_CONTEXT_TAKEOVER\n"); + (void)deflateEnd(&priv->tx); + priv->tx_init = 0; + } + + n = *(eff_buf->token) & 15; + /* set RSV1, but not on CONTINUATION */ + if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME) + *eff_buf->token |= 0x40; +#if 0 + for (n = 0; n < eff_buf->token_len; n++) { + printf("%02X ", (unsigned char)eff_buf->token[n]); + if ((n & 15) == 15) + puts("\n"); + } + puts("\n"); +#endif + lwsl_ext("%s: tx opcode 0x%02X\n", __func__, + (unsigned char)*eff_buf->token); + break; + + default: + break; + } + + return 0; +} + diff -Nru libwebsockets-4.0.20/lib/ext/extension-permessage-deflate.h libwebsockets-2.4.2/lib/ext/extension-permessage-deflate.h --- libwebsockets-4.0.20/lib/ext/extension-permessage-deflate.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/ext/extension-permessage-deflate.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,41 @@ + +#include + +#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1 +#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION + +enum arg_indexes { + PMD_SERVER_NO_CONTEXT_TAKEOVER, + PMD_CLIENT_NO_CONTEXT_TAKEOVER, + PMD_SERVER_MAX_WINDOW_BITS, + PMD_CLIENT_MAX_WINDOW_BITS, + PMD_RX_BUF_PWR2, + PMD_TX_BUF_PWR2, + PMD_COMP_LEVEL, + PMD_MEM_LEVEL, + + PMD_ARG_COUNT +}; + +struct lws_ext_pm_deflate_priv { + z_stream rx; + z_stream tx; + + unsigned char *buf_rx_inflated; /* RX inflated output buffer */ + unsigned char *buf_tx_deflated; /* TX deflated output buffer */ + + size_t count_rx_between_fin; + + unsigned char args[PMD_ARG_COUNT]; + unsigned char tx_held[5]; + unsigned char rx_held; + + unsigned char tx_init:1; + unsigned char rx_init:1; + unsigned char compressed_out:1; + unsigned char rx_held_valid:1; + unsigned char tx_held_valid:1; + unsigned char rx_append_trailer:1; + unsigned char pending_tx_trailer:1; +}; + diff -Nru libwebsockets-4.0.20/lib/handshake.c libwebsockets-2.4.2/lib/handshake.c --- libwebsockets-4.0.20/lib/handshake.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/handshake.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,280 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +/* + * -04 of the protocol (actually the 80th version) has a radically different + * handshake. The 04 spec gives the following idea + * + * The handshake from the client looks as follows: + * + * GET /chat HTTP/1.1 + * Host: server.example.com + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== + * Sec-WebSocket-Origin: http://example.com + * Sec-WebSocket-Protocol: chat, superchat + * Sec-WebSocket-Version: 4 + * + * The handshake from the server looks as follows: + * + * HTTP/1.1 101 Switching Protocols + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= + * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== + * Sec-WebSocket-Protocol: chat + */ + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * We have to take care about parsing because the headers may be split + * into multiple fragments. They may contain unknown headers with arbitrary + * argument lengths. So, we parse using a single-character at a time state + * machine that is completely independent of packet size. + * + * Returns <0 for error or length of chars consumed from buf (up to len) + */ + +LWS_VISIBLE int +lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len) +{ + unsigned char *last_char, *oldbuf = buf; + lws_filepos_t body_chunk_len; + size_t n; + + switch (wsi->state) { +#ifdef LWS_WITH_HTTP2 + case LWSS_HTTP2_AWAIT_CLIENT_PREFACE: + case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS: + case LWSS_HTTP2_ESTABLISHED: + n = 0; + //lwsl_debug("%s: starting new block of %d\n", __func__, (int)len); + /* + * wsi here is always the network connection wsi, not a stream + * wsi. + */ + while (n < len) { + /* + * we were accepting input but now we stopped doing so + */ + if (lws_is_flowcontrolled(wsi)) { + lws_rxflow_cache(wsi, buf, n, len); + + return 1; + } + + /* account for what we're using in rxflow buffer */ + if (wsi->rxflow_buffer) { + wsi->rxflow_pos++; + assert(wsi->rxflow_pos <= wsi->rxflow_len); + } + + if (lws_h2_parser(wsi, buf[n++])) { + lwsl_debug("%s: http2_parser bailed\n", __func__); + goto bail; + } + } + lwsl_debug("%s: used up block of %d\n", __func__, (int)len); + break; +#endif + + case LWSS_HTTP_ISSUING_FILE: + return 0; + + case LWSS_CLIENT_HTTP_ESTABLISHED: + break; + + case LWSS_HTTP: + wsi->hdr_parsing_completed = 0; + + /* fallthru */ + + case LWSS_HTTP_HEADERS: + if (!wsi->u.hdr.ah) { + lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__); + assert(0); + } + lwsl_parser("issuing %d bytes to parser\n", (int)len); + + lwsl_hexdump(buf, (size_t)len); + + if (lws_handshake_client(wsi, &buf, (size_t)len)) + goto bail; + + last_char = buf; + if (lws_handshake_server(wsi, &buf, (size_t)len)) + /* Handshake indicates this session is done. */ + goto bail; + + /* we might have transitioned to RAW */ + if (wsi->mode == LWSCM_RAW) + /* we gave the read buffer to RAW handler already */ + goto read_ok; + + /* + * It's possible that we've exhausted our data already, or + * rx flow control has stopped us dealing with this early, + * but lws_handshake_server doesn't update len for us. + * Figure out how much was read, so that we can proceed + * appropriately: + */ + len -= (buf - last_char); + lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len); + + if (!wsi->hdr_parsing_completed) + /* More header content on the way */ + goto read_ok; + + switch (wsi->state) { + case LWSS_HTTP: + case LWSS_HTTP_HEADERS: + goto read_ok; + case LWSS_HTTP_ISSUING_FILE: + goto read_ok; + case LWSS_HTTP_BODY: + wsi->u.http.rx_content_remain = + wsi->u.http.rx_content_length; + if (wsi->u.http.rx_content_remain) + goto http_postbody; + + /* there is no POST content */ + goto postbody_completion; + default: + break; + } + break; + + case LWSS_HTTP_BODY: +http_postbody: + //lwsl_notice("http post body\n"); + while (len && wsi->u.http.rx_content_remain) { + /* Copy as much as possible, up to the limit of: + * what we have in the read buffer (len) + * remaining portion of the POST body (content_remain) + */ + body_chunk_len = min(wsi->u.http.rx_content_remain, len); + wsi->u.http.rx_content_remain -= body_chunk_len; + len -= body_chunk_len; +#ifdef LWS_WITH_CGI + if (wsi->cgi) { + struct lws_cgi_args args; + + args.ch = LWS_STDIN; + args.stdwsi = &wsi->cgi->stdwsi[0]; + args.data = buf; + args.len = body_chunk_len; + + /* returns how much used */ + n = user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, LWS_CALLBACK_CGI_STDIN_DATA, + wsi->user_space, + (void *)&args, 0); + if ((int)n < 0) + goto bail; + } else { +#endif + n = wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_BODY, wsi->user_space, + buf, (size_t)body_chunk_len); + if (n) + goto bail; + n = (size_t)body_chunk_len; +#ifdef LWS_WITH_CGI + } +#endif + buf += n; + + if (wsi->u.http.rx_content_remain) { + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, + wsi->context->timeout_secs); + break; + } + /* he sent all the content in time */ +postbody_completion: +#ifdef LWS_WITH_CGI + /* + * If we're running a cgi, we can't let him off the + * hook just because he sent his POST data + */ + if (wsi->cgi) + lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, + wsi->context->timeout_secs); + else +#endif + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); +#ifdef LWS_WITH_CGI + if (!wsi->cgi) +#endif + { + lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); + n = wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_BODY_COMPLETION, + wsi->user_space, NULL, 0); + if (n) + goto bail; + + if (wsi->http2_substream) + wsi->state = LWSS_HTTP2_ESTABLISHED; + } + + break; + } + break; + + case LWSS_ESTABLISHED: + case LWSS_AWAITING_CLOSE_ACK: + case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION: + case LWSS_SHUTDOWN: + if (lws_handshake_client(wsi, &buf, (size_t)len)) + goto bail; + switch (wsi->mode) { + case LWSCM_WS_SERVING: + + if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) { + lwsl_info("interpret_incoming_packet has bailed\n"); + goto bail; + } + break; + } + break; + default: + lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state); + break; + } + +read_ok: + /* Nothing more to do for now */ + lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf)); + + return buf - oldbuf; + +bail: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return -1; +} diff -Nru libwebsockets-4.0.20/lib/header.c libwebsockets-2.4.2/lib/header.c --- libwebsockets-4.0.20/lib/header.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/header.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,355 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#include "lextable-strings.h" + + +const unsigned char *lws_token_to_string(enum lws_token_indexes token) +{ + if ((unsigned int)token >= ARRAY_SIZE(set)) + return NULL; + + return (unsigned char *)set[token]; +} + +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 (wsi->mode == LWSCM_HTTP2_SERVING) + return lws_add_http2_header_by_name(wsi, name, + value, length, p, end); +#else + (void)wsi; +#endif + if (name) { + while (*p < end && *name) + *((*p)++) = *name++; + if (*p == end) + return 1; + *((*p)++) = ' '; + } + if (*p + length + 3 >= end) + return 1; + + memcpy(*p, value, 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 (wsi->mode == LWSCM_HTTP2_SERVING) + return 0; +#else + (void)wsi; +#endif + if ((lws_intptr_t)(end - *p) < 3) + return 1; + *((*p)++) = '\x0d'; + *((*p)++) = '\x0a'; + + 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 (wsi->mode == LWSCM_HTTP2_SERVING) + 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 = sprintf(b, "%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->u.http.tx_content_length = content_length; + wsi->u.http.tx_content_remain = content_length; + + return 0; +} + +STORE_IN_ROM 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" +}; + +STORE_IN_ROM static const char * const err500[] = { + "Internal Server Error", + "Not Implemented", + "Bad Gateway", + "Service Unavailable", + "Gateway Timeout", + "HTTP Version Not Supported" +}; + +int +lws_add_http_header_status(struct lws *wsi, unsigned int _code, + unsigned char **p, unsigned char *end) +{ + STORE_IN_ROM 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; + +#ifdef LWS_WITH_ACCESS_LOG + wsi->access_log.response = code; +#endif + +#ifdef LWS_WITH_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) + return lws_add_http2_header_status(wsi, code, p, end); +#endif + if (code >= 400 && code < (400 + ARRAY_SIZE(err400))) + description = err400[code - 400]; + if (code >= 500 && code < (500 + 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->u.http.request_version < ARRAY_SIZE(hver)) + p1 = hver[wsi->u.http.request_version]; + else + p1 = hver[0]; + + n = sprintf((char *)code_and_desc, "%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->vhost->headers; + while (headers) { + if (lws_add_http_header_by_name(wsi, + (const unsigned char *)headers->name, + (unsigned char *)headers->value, + strlen(headers->value), p, end)) + return 1; + + headers = headers->next; + } + + if (wsi->context->server_string && + !(_code & LWSAHH_FLAG_NO_SERVER_NAME)) + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, + (unsigned char *)wsi->context->server_string, + wsi->context->server_string_len, p, end)) + return 1; + + if (wsi->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; + + return 0; +} + +LWS_VISIBLE 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; + int n = 0, m = 0, len; + char slen[20]; + + 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 = 35 + strlen(html_body) + sprintf(slen, "%d", code); + n = sprintf(slen, "%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->http2_substream) { + unsigned char *body = p + 512; + + /* + * 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, p - start, LWS_WRITE_HTTP_HEADERS); + if (m != (int)(p - start)) + return 1; + + /* + * ... but stash the body and send it as a priority next + * handle_POLLOUT + */ + + len = sprintf((char *)body, + "

%u

%s", + code, html_body); + wsi->u.http.tx_content_length = len; + wsi->u.http.tx_content_remain = len; + + wsi->u.h2.pending_status_body = lws_malloc(len + LWS_PRE + 1, + "pending status body"); + if (!wsi->u.h2.pending_status_body) + return -1; + + strcpy(wsi->u.h2.pending_status_body + LWS_PRE, + (const char *)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. + */ + p += lws_snprintf((char *)p, end - p - 1, + "

%u

%s", + code, html_body); + + n = (int)(p - start); + + m = lws_write(wsi, start, n, LWS_WRITE_HTTP); + if (m != n) + return 1; + } + + lwsl_notice("%s: return\n", __func__); + + return m != n; +} + +LWS_VISIBLE 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; + int n; + + if (lws_add_http_header_status(wsi, 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; + + n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END); + + return n; +} diff -Nru libwebsockets-4.0.20/lib/http2/hpack.c libwebsockets-2.4.2/lib/http2/hpack.c --- libwebsockets-4.0.20/lib/http2/hpack.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/http2/hpack.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1387 @@ +/* + * lib/hpack.c + * + * Copyright (C) 2014-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +/* + * Official static header table for HPACK + * +-------+-----------------------------+---------------+ + | 1 | :authority | | + | 2 | :method | GET | + | 3 | :method | POST | + | 4 | :path | / | + | 5 | :path | /index.html | + | 6 | :scheme | http | + | 7 | :scheme | https | + | 8 | :status | 200 | + | 9 | :status | 204 | + | 10 | :status | 206 | + | 11 | :status | 304 | + | 12 | :status | 400 | + | 13 | :status | 404 | + | 14 | :status | 500 | + | 15 | accept-charset | | + | 16 | accept-encoding | gzip, deflate | + | 17 | accept-language | | + | 18 | accept-ranges | | + | 19 | accept | | + | 20 | access-control-allow-origin | | + | 21 | age | | + | 22 | allow | | + | 23 | authorization | | + | 24 | cache-control | | + | 25 | content-disposition | | + | 26 | content-encoding | | + | 27 | content-language | | + | 28 | content-length | | + | 29 | content-location | | + | 30 | content-range | | + | 31 | content-type | | + | 32 | cookie | | + | 33 | date | | + | 34 | etag | | + | 35 | expect | | + | 36 | expires | | + | 37 | from | | + | 38 | host | | + | 39 | if-match | | + | 40 | if-modified-since | | + | 41 | if-none-match | | + | 42 | if-range | | + | 43 | if-unmodified-since | | + | 44 | last-modified | | + | 45 | link | | + | 46 | location | | + | 47 | max-forwards | | + | 48 | proxy-authenticate | | + | 49 | proxy-authorization | | + | 50 | range | | + | 51 | referer | | + | 52 | refresh | | + | 53 | retry-after | | + | 54 | server | | + | 55 | set-cookie | | + | 56 | strict-transport-security | | + | 57 | transfer-encoding | | + | 58 | user-agent | | + | 59 | vary | | + | 60 | via | | + | 61 | www-authenticate | | + +-------+-----------------------------+---------------+ +*/ + +static const uint8_t static_hdr_len[62] = { + 0, /* starts at 1 */ + 10, 7, 7, 5, 5, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 14, 15, 15, 13, 6, 27, + 3, 5, 13, 13, 19, 16, 16, 14, 16, 13, + 12, 6, 4, 4, 6, 7, 4, 4, 8, 17, + 13, 8, 19, 13, 4, 8, 12, 18, 19, 5, + 7, 7, 11, 6, 10, 25, 17, 10, 4, 3, + 16 +}; + +static const unsigned char static_token[] = { + 0, + WSI_TOKEN_HTTP_COLON_AUTHORITY, + WSI_TOKEN_HTTP_COLON_METHOD, + WSI_TOKEN_HTTP_COLON_METHOD, + WSI_TOKEN_HTTP_COLON_PATH, + WSI_TOKEN_HTTP_COLON_PATH, + WSI_TOKEN_HTTP_COLON_SCHEME, + WSI_TOKEN_HTTP_COLON_SCHEME, + WSI_TOKEN_HTTP_COLON_STATUS, + WSI_TOKEN_HTTP_COLON_STATUS, + WSI_TOKEN_HTTP_COLON_STATUS, + WSI_TOKEN_HTTP_COLON_STATUS, + WSI_TOKEN_HTTP_COLON_STATUS, + WSI_TOKEN_HTTP_COLON_STATUS, + WSI_TOKEN_HTTP_COLON_STATUS, + WSI_TOKEN_HTTP_ACCEPT_CHARSET, + WSI_TOKEN_HTTP_ACCEPT_ENCODING, + WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, + WSI_TOKEN_HTTP_ACCEPT_RANGES, + WSI_TOKEN_HTTP_ACCEPT, + WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN, + WSI_TOKEN_HTTP_AGE, + WSI_TOKEN_HTTP_ALLOW, + WSI_TOKEN_HTTP_AUTHORIZATION, + WSI_TOKEN_HTTP_CACHE_CONTROL, + WSI_TOKEN_HTTP_CONTENT_DISPOSITION, + WSI_TOKEN_HTTP_CONTENT_ENCODING, + WSI_TOKEN_HTTP_CONTENT_LANGUAGE, + WSI_TOKEN_HTTP_CONTENT_LENGTH, + WSI_TOKEN_HTTP_CONTENT_LOCATION, + WSI_TOKEN_HTTP_CONTENT_RANGE, + WSI_TOKEN_HTTP_CONTENT_TYPE, + WSI_TOKEN_HTTP_COOKIE, + WSI_TOKEN_HTTP_DATE, + WSI_TOKEN_HTTP_ETAG, + WSI_TOKEN_HTTP_EXPECT, + WSI_TOKEN_HTTP_EXPIRES, + WSI_TOKEN_HTTP_FROM, + WSI_TOKEN_HOST, + WSI_TOKEN_HTTP_IF_MATCH, + WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, + WSI_TOKEN_HTTP_IF_NONE_MATCH, + WSI_TOKEN_HTTP_IF_RANGE, + WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE, + WSI_TOKEN_HTTP_LAST_MODIFIED, + WSI_TOKEN_HTTP_LINK, + WSI_TOKEN_HTTP_LOCATION, + WSI_TOKEN_HTTP_MAX_FORWARDS, + WSI_TOKEN_HTTP_PROXY_AUTHENTICATE, + WSI_TOKEN_HTTP_PROXY_AUTHORIZATION, + WSI_TOKEN_HTTP_RANGE, + WSI_TOKEN_HTTP_REFERER, + WSI_TOKEN_HTTP_REFRESH, + WSI_TOKEN_HTTP_RETRY_AFTER, + WSI_TOKEN_HTTP_SERVER, + WSI_TOKEN_HTTP_SET_COOKIE, + WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY, + WSI_TOKEN_HTTP_TRANSFER_ENCODING, + WSI_TOKEN_HTTP_USER_AGENT, + WSI_TOKEN_HTTP_VARY, + WSI_TOKEN_HTTP_VIA, + WSI_TOKEN_HTTP_WWW_AUTHENTICATE, +}; + +/* some of the entries imply values as well as header names */ + +static const char * const http2_canned[] = { + "", + "", + "GET", + "POST", + "/", + "/index.html", + "http", + "https", + "200", + "204", + "206", + "304", + "400", + "404", + "500", + "", + "gzip, deflate" +}; + +/* see minihuf.c */ + +#include "huftable.h" + +static int huftable_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); +} + +static int lws_frag_start(struct lws *wsi, int hdr_token_idx) +{ + struct allocated_headers *ah = wsi->u.h2.http.ah; + + if (!ah) { + lwsl_notice("%s: no ah\n", __func__); + return 1; + } + + ah->hdr_token_idx = -1; + + lwsl_header("%s: token %d ah->pos = %d, ah->nfrag = %d\n", + __func__, hdr_token_idx, ah->pos, ah->nfrag); + + if (!hdr_token_idx) { + lwsl_err("%s: zero hdr_token_idx\n", __func__); + return 1; + } + + if (ah->nfrag >= ARRAY_SIZE(ah->frag_index)) { + lwsl_err("%s: frag index %d too big\n", __func__, ah->nfrag); + return 1; + } + + if ((hdr_token_idx == WSI_TOKEN_HTTP_COLON_AUTHORITY || + hdr_token_idx == WSI_TOKEN_HTTP_COLON_METHOD || + hdr_token_idx == WSI_TOKEN_HTTP_COLON_PATH || + hdr_token_idx == WSI_TOKEN_HTTP_COLON_SCHEME) && + ah->frag_index[hdr_token_idx]) { + if (!(ah->frags[ah->frag_index[hdr_token_idx]].flags & 1)) { + lws_h2_goaway(lws_get_network_wsi(wsi), + H2_ERR_PROTOCOL_ERROR, + "Duplicated pseudoheader"); + return 1; + } + } + + if (ah->nfrag == 0) + ah->nfrag = 1; + + ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].len = 0; + ah->frags[ah->nfrag].nfrag = 0; + ah->frags[ah->nfrag].flags = 2; /* we had reason to set it */ + + ah->hdr_token_idx = hdr_token_idx; + + /* + * Okay, but we could be, eg, the second or subsequent cookie: header + */ + + if (ah->frag_index[hdr_token_idx]) { + int n; + + /* find the last fragment for this header... */ + n = ah->frag_index[hdr_token_idx]; + while (ah->frags[n].nfrag) + n = ah->frags[n].nfrag; + /* and point it to continue in our continuation fragment */ + ah->frags[n].nfrag = ah->nfrag; + + /* cookie continuations need a separator token of ';' */ + if (hdr_token_idx == WSI_TOKEN_HTTP_COOKIE) { + ah->data[ah->pos++] = ';'; + ah->frags[ah->nfrag].len++; + } + } else + ah->frag_index[hdr_token_idx] = ah->nfrag; + + return 0; +} + +static int lws_frag_append(struct lws *wsi, unsigned char c) +{ + struct allocated_headers * ah = wsi->u.h2.http.ah; + + ah->data[ah->pos++] = c; + ah->frags[ah->nfrag].len++; + + return ah->pos >= wsi->context->max_http_header_data; +} + +static int lws_frag_end(struct lws *wsi) +{ + lwsl_header("%s\n", __func__); + if (lws_frag_append(wsi, 0)) + return 1; + + /* don't account for the terminating NUL in the logical length */ + wsi->u.h2.http.ah->frags[wsi->u.h2.http.ah->nfrag].len--; + + wsi->u.h2.http.ah->nfrag++; + return 0; +} + +int +lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h) +{ + struct allocated_headers *ah = wsi->u.h2.http.ah; + int n; + + if (!ah) + return 0; + + n = ah->frag_index[h]; + if (!n) + return 0; + + return !!(ah->frags[n].flags & 2); +} + +static void lws_dump_header(struct lws *wsi, int hdr) +{ + char s[200]; + const unsigned char *p; + int len; + + if (hdr == LWS_HPACK_IGNORE_ENTRY) { + lwsl_notice("hdr tok ignored\n"); + return; + } + + (void)p; + + len = lws_hdr_copy(wsi, s, sizeof(s) - 1, hdr); + if (len < 0) + strcpy(s, "(too big to show)"); + else + s[len] = '\0'; + p = lws_token_to_string(hdr); + lwsl_header(" hdr tok %d (%s) = '%s' (len %d)\n", hdr, + p ? (char *)p : (char *)"null", s, len); +} + +/* + * dynamic table + * + * [ 0 .... num_entries - 1] + * + * Starts filling at 0+ + * + * #62 is *most recently entered* + * + * Number of entries is not restricted, but aggregated size of the entry + * payloads is. Unfortunately the way HPACK does this is specific to an + * imagined implementation, and lws implementation is much more efficient + * (ignoring unknown headers and using the lws token index for the header + * name part). + */ + +/* + * returns 0 if dynamic entry (arg and len are filled) + * returns -1 if failure + * returns nonzero token index if actually static token + */ +static int +lws_token_from_index(struct lws *wsi, int index, const char **arg, int *len, + uint32_t *hdr_len) +{ + struct hpack_dynamic_table *dyn; + + if (index == LWS_HPACK_IGNORE_ENTRY) + return LWS_HPACK_IGNORE_ENTRY; + + /* dynamic table only belongs to network wsi */ + wsi = lws_get_network_wsi(wsi); + if (!wsi->u.h2.h2n) + return -1; + + dyn = &wsi->u.h2.h2n->hpack_dyn_table; + + if (index < 0) + return -1; + + if (index < ARRAY_SIZE(static_token)) { + if (arg && index < ARRAY_SIZE(http2_canned)) { + *arg = http2_canned[index]; + *len = strlen(http2_canned[index]); + } + if (hdr_len) + *hdr_len = static_hdr_len[index]; + + return static_token[index]; + } + + if (!dyn) { + lwsl_notice("no dynamic table\n"); + return -1; + } + + if (index < ARRAY_SIZE(static_token) || + index >= ARRAY_SIZE(static_token) + dyn->used_entries) { + lwsl_err(" %s: adjusted index %d >= %d\n", __func__, index, + dyn->used_entries); + lws_h2_goaway(wsi, H2_ERR_COMPRESSION_ERROR, + "index out of range"); + return -1; + } + + index -= ARRAY_SIZE(static_token); + index = (dyn->pos - 1 - index) % dyn->num_entries; + if (index < 0) + index += dyn->num_entries; + + lwsl_header("%s: dyn index %d, tok %d\n", __func__, index, + dyn->entries[index].lws_hdr_idx); + + if (arg && len) { + *arg = dyn->entries[index].value; + *len = dyn->entries[index].value_len; + } + + if (hdr_len) + *hdr_len = dyn->entries[index].hdr_len; + + return dyn->entries[index].lws_hdr_idx; +} + +static int +lws_h2_dynamic_table_dump(struct lws *wsi) +{ +#if 0 + struct lws *nwsi = lws_get_network_wsi(wsi); + struct hpack_dynamic_table *dyn; + int n, m; + const char *p; + + if (!nwsi->u.h2.h2n) + return 1; + dyn = &nwsi->u.h2.h2n->hpack_dyn_table; + + lwsl_header("Dump dyn table for nwsi %p (%d / %d members, pos = %d, " + "start index %d, virt used %d / %d)\n", nwsi, + dyn->used_entries, dyn->num_entries, dyn->pos, + (uint32_t)ARRAY_SIZE(static_token), + dyn->virtual_payload_usage, dyn->virtual_payload_max); + + for (n = 0; n < dyn->used_entries; n++) { + m = (dyn->pos - 1 - n) % dyn->num_entries; + if (m < 0) + m += dyn->num_entries; + if (dyn->entries[m].lws_hdr_idx != LWS_HPACK_IGNORE_ENTRY) + p = (const char *)lws_token_to_string( + dyn->entries[m].lws_hdr_idx); + else + p = "(ignored)"; + lwsl_header(" %3d: tok %s: (len %d) val '%s'\n", + (int)(n + ARRAY_SIZE(static_token)), p, + dyn->entries[m].hdr_len, dyn->entries[m].value ? + dyn->entries[m].value : "null"); + } +#endif + return 0; +} + +static void +lws_dynamic_free(struct hpack_dynamic_table *dyn, int idx) +{ + lwsl_header("freeing %d for reuse\n", idx); + dyn->virtual_payload_usage -= dyn->entries[idx].value_len + + dyn->entries[idx].hdr_len; + lws_free_set_NULL(dyn->entries[idx].value); + dyn->entries[idx].value = NULL; + dyn->entries[idx].value_len = 0; + dyn->entries[idx].hdr_len = 0; + dyn->entries[idx].lws_hdr_idx = LWS_HPACK_IGNORE_ENTRY; + dyn->used_entries--; +} + +/* + * There are two address spaces, 1) internal ringbuffer and 2) HPACK indexes. + * + * Internal ringbuffer: + * + * The internal ringbuffer wraps as we keep filling it, dyn->pos points to + * the next index to be written. + * + * HPACK indexes: + * + * The last-written entry becomes entry 0, the previously-last-written entry + * becomes entry 1 etc. + */ + +static int +lws_dynamic_token_insert(struct lws *wsi, int hdr_len, + int lws_hdr_index, char *arg, int len) +{ + struct hpack_dynamic_table *dyn; + int new_index, n; + + /* dynamic table only belongs to network wsi */ + wsi = lws_get_network_wsi(wsi); + if (!wsi->u.h2.h2n) + return 1; + dyn = &wsi->u.h2.h2n->hpack_dyn_table; + + if (!dyn->entries) { + lwsl_err("%s: unsized dyn table\n", __func__); + + return 1; + } + lws_h2_dynamic_table_dump(wsi); + + new_index = (dyn->pos) % dyn->num_entries; + if (dyn->num_entries && dyn->used_entries == dyn->num_entries) { + if (dyn->virtual_payload_usage < dyn->virtual_payload_max) + lwsl_err("Dropping header content before limit!\n"); + /* we have to drop the oldest to make space */ + lws_dynamic_free(dyn, new_index); + } + + /* + * evict guys to make room, allowing for some overage. We have to + * take care about getting a single huge header, and evicting + * everything + */ + + while (dyn->virtual_payload_usage && + dyn->used_entries && + dyn->virtual_payload_usage + hdr_len + len > + dyn->virtual_payload_max + 1024) { + n = (dyn->pos - dyn->used_entries) % dyn->num_entries; + if (n < 0) + n += dyn->num_entries; + lws_dynamic_free(dyn, n); + } + + if (dyn->used_entries < dyn->num_entries) + dyn->used_entries++; + + dyn->entries[new_index].value_len = 0; + + if (lws_hdr_index != LWS_HPACK_IGNORE_ENTRY) { + if (dyn->entries[new_index].value) + lws_free_set_NULL(dyn->entries[new_index].value); + dyn->entries[new_index].value = lws_malloc(len + 1, "hpack dyn"); + if (!dyn->entries[new_index].value) + return 1; + + memcpy(dyn->entries[new_index].value, arg, len); + dyn->entries[new_index].value[len] = '\0'; + dyn->entries[new_index].value_len = len; + } else + dyn->entries[new_index].value = NULL; + + dyn->entries[new_index].lws_hdr_idx = lws_hdr_index; + dyn->entries[new_index].hdr_len = hdr_len; + + dyn->virtual_payload_usage += hdr_len + len; + + lwsl_info("%s: index %ld: lws_hdr_index 0x%x, hdr len %d, '%s' len %d\n", + __func__, (long)ARRAY_SIZE(static_token), + lws_hdr_index, hdr_len, dyn->entries[new_index].value ? + dyn->entries[new_index].value : "null", len); + + dyn->pos = (dyn->pos + 1) % dyn->num_entries; + + lws_h2_dynamic_table_dump(wsi); + + return 0; +} + +int +lws_hpack_dynamic_size(struct lws *wsi, int size) +{ + struct hpack_dynamic_table *dyn; + struct hpack_dt_entry *dte; + struct lws *nwsi; + int min, n = 0, m; + + /* + * "size" here is coming from the http/2 SETTING + * SETTINGS_HEADER_TABLE_SIZE. This is a (virtual, in our case) + * linear buffer containing dynamic header names and values... when it + * is full, old entries are evicted. + * + * We encode the header as an lws_hdr_idx, which is all the rest of + * lws cares about; if there is no matching header we store an empty + * entry in the dyn table as a placeholder. + * + * So to make the two systems work together we keep an accounting of + * what we are using to decide when to evict... we must only evict + * things when the remote peer's accounting also makes him feel he + * should evict something. + */ + + nwsi = lws_get_network_wsi(wsi); + if (!nwsi->u.h2.h2n) + goto bail; + + dyn = &nwsi->u.h2.h2n->hpack_dyn_table; + lwsl_info("%s: from %d to %d, lim %d\n", __func__, + (int)dyn->num_entries, size, + nwsi->u.h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]); + + if (size > nwsi->u.h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]) { + lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, + "Asked for header table bigger than we told"); + goto bail; + } + + dyn->virtual_payload_max = size; + + size = size / 8; + min = size; + if (min > dyn->used_entries) + min = dyn->used_entries; + + if (size == dyn->num_entries) + return 0; + + if (dyn->num_entries < min) + min = dyn->num_entries; + + dte = lws_zalloc(sizeof(*dte) * (size + 1), "dynamic table entries"); + if (!dte) + goto bail; + + while (dyn->virtual_payload_usage && dyn->used_entries && + dyn->virtual_payload_usage > dyn->virtual_payload_max) { + n = (dyn->pos - dyn->used_entries) % dyn->num_entries; + if (n < 0) + n += dyn->num_entries; + lws_dynamic_free(dyn, n); + } + + if (min > dyn->used_entries) + min = dyn->used_entries; + + if (dyn->entries) { + for (n = 0; n < min; n++) { + m = (dyn->pos - dyn->used_entries + n) % dyn->num_entries; + if (m < 0) + m += dyn->num_entries; + dte[n] = dyn->entries[m]; + } + + lws_free(dyn->entries); + } + + dyn->entries = dte; + dyn->num_entries = size; + dyn->used_entries = min; + if (size) + dyn->pos = min % size; + else + dyn->pos = 0; + + lws_h2_dynamic_table_dump(wsi); + + return 0; + +bail: + lwsl_info("%s: failed to resize to %d\n", __func__, size); + + return 1; +} + +void +lws_hpack_destroy_dynamic_header(struct lws *wsi) +{ + struct hpack_dynamic_table *dyn; + int n; + + if (!wsi->u.h2.h2n) + return; + + dyn = &wsi->u.h2.h2n->hpack_dyn_table; + + if (!dyn->entries) + return; + + for (n = 0; n < dyn->num_entries; n++) + if (dyn->entries[n].value) + lws_free_set_NULL(dyn->entries[n].value); + + lws_free_set_NULL(dyn->entries); +} + +static int +lws_hpack_use_idx_hdr(struct lws *wsi, int idx, int known_token) +{ + const char *arg = NULL; + int len = 0; + const char *p = NULL; + int tok = lws_token_from_index(wsi, idx, &arg, &len, NULL); + + if (tok == LWS_HPACK_IGNORE_ENTRY) { + lwsl_header("%s: lws_token says ignore, returning\n", __func__); + return 0; + } + + if (tok == -1) { + lwsl_info("%s: idx %d mapped to tok %d\n", __func__, idx, tok); + return 1; + } + + if (arg) { + /* dynamic result */ + if (known_token > 0) + tok = known_token; + lwsl_header("%s: dyn: idx %d '%s' tok %d\n", __func__, idx, arg, + tok); + } else + lwsl_header("writing indexed hdr %d (tok %d '%s')\n", idx, tok, + lws_token_to_string(tok)); + + if (tok == LWS_HPACK_IGNORE_ENTRY) + return 0; + + if (arg) + p = arg; + + if (idx < ARRAY_SIZE(http2_canned)) + p = http2_canned[idx]; + + if (lws_frag_start(wsi, tok)) + return 1; + + if (p) + while (*p && len--) + if (lws_frag_append(wsi, *p++)) + return 1; + + if (lws_frag_end(wsi)) + return 1; + + lws_dump_header(wsi, tok); + + return 0; +} + +static uint8_t lws_header_implies_psuedoheader_map[] = { + 0x07, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00 /* <-64 */, + 0x0e /* <- 72 */, 0x04 /* <- 80 */, 0, 0, 0, 0 +}; + +static int +lws_hpack_handle_pseudo_rules(struct lws *nwsi, struct lws *wsi, int m) +{ + if (m == LWS_HPACK_IGNORE_ENTRY || m == -1) + return 0; + + if (wsi->seen_nonpseudoheader && + (lws_header_implies_psuedoheader_map[m >> 3] & (1 << (m & 7)))) { + + lwsl_notice("lws tok %d seems to be a pseudoheader\n", m); + + /* + * it's not legal to see a + * pseudoheader after normal + * headers + */ + lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, + "Pseudoheader after normal hdrs"); + return 1; + } + + if (!(lws_header_implies_psuedoheader_map[m >> 3] & (1 << (m & 7)))) + wsi->seen_nonpseudoheader = 1; + + return 0; +} + +int lws_hpack_interpret(struct lws *wsi, unsigned char c) +{ + struct lws *nwsi = lws_get_network_wsi(wsi); + struct lws_h2_netconn *h2n = nwsi->u.h2.h2n; + struct allocated_headers *ah = wsi->u.h2.http.ah; + unsigned int prev; + unsigned char c1; + int n, m; + + if (!h2n) + return -1; + + /* + * HPKT_INDEXED_HDR_7 1xxxxxxx: just "header field" + * HPKT_INDEXED_HDR_6_VALUE_INCR 01xxxxxx: NEW indexed hdr + val + * HPKT_LITERAL_HDR_VALUE_INCR 01000000: NEW literal hdr + val + * HPKT_INDEXED_HDR_4_VALUE 0000xxxx: indexed hdr + val + * HPKT_INDEXED_HDR_4_VALUE_NEVER 0001xxxx: NEVER NEW indexed hdr + val + * HPKT_LITERAL_HDR_VALUE 00000000: literal hdr + val + * HPKT_LITERAL_HDR_VALUE_NEVER 00010000: NEVER NEW literal hdr + val + */ + switch (h2n->hpack) { + + case HPKS_TYPE: + h2n->is_first_header_char = 1; + h2n->huff_pad = 0; + h2n->zero_huff_padding = 0; + h2n->last_action_dyntable_resize = 0; + h2n->ext_count = 0; + h2n->hpack_hdr_len = 0; + h2n->unknown_header = 0; + wsi->u.hdr.parser_state = 255; + + if (c & 0x80) { /* 1.... indexed header field only */ + /* just a possibly-extended integer */ + h2n->hpack_type = HPKT_INDEXED_HDR_7; + lwsl_header("HPKT_INDEXED_HDR_7 hdr %d\n", c & 0x7f); + lws_h2_dynamic_table_dump(wsi); + + h2n->hdr_idx = c & 0x7f; + if ((c & 0x7f) == 0x7f) { + h2n->hpack_len = 0; + h2n->hpack_m = 0x7f; + h2n->hpack = HPKS_IDX_EXT; + break; + } + if (!h2n->hdr_idx) { + lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, + "hdr index 0 seen"); + return 1; + } + + m = lws_token_from_index(wsi, h2n->hdr_idx, + NULL, NULL, NULL); + if (lws_hpack_handle_pseudo_rules(nwsi, wsi, m)) + return 1; + + lwsl_header("HPKT_INDEXED_HDR_7: hdr %d\n", c & 0x7f); + if (lws_hpack_use_idx_hdr(wsi, c & 0x7f, -1)) { + lwsl_header("%s: idx hdr wr fail\n", __func__); + return 1; + } + /* stay at same state */ + break; + } + if (c & 0x40) { /* 01.... indexed or literal header incr idx */ + /* + * [possibly-ext hdr idx (6) | new literal hdr name] + * H + possibly-ext value length + * literal value + */ + h2n->hdr_idx = 0; + if (c == 0x40) { /* literal header */ + lwsl_header(" HPKT_LITERAL_HDR_VALUE_INCR\n"); + h2n->hpack_type = HPKT_LITERAL_HDR_VALUE_INCR; + h2n->value = 0; + h2n->hpack_len = 0; + h2n->hpack = HPKS_HLEN; + break; + } + /* indexed header */ + h2n->hpack_type = HPKT_INDEXED_HDR_6_VALUE_INCR; + lwsl_header(" HPKT_INDEXED_HDR_6_VALUE_INCR (hdr %d)\n", + c & 0x3f); + h2n->hdr_idx = c & 0x3f; + if ((c & 0x3f) == 0x3f) { + h2n->hpack_m = 0x3f; + h2n->hpack_len = 0; + h2n->hpack = HPKS_IDX_EXT; + break; + } + + h2n->value = 1; + h2n->hpack = HPKS_HLEN; + if (!h2n->hdr_idx) { + lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, + "hdr index 0 seen"); + return 1; + } + break; + } + switch(c & 0xf0) { + case 0x10: /* literal header never index */ + case 0: /* literal header without indexing */ + /* + * follows 0x40 except 4-bit hdr idx + * and don't add to index + */ + if (c == 0) { /* literal name */ + h2n->hpack_type = HPKT_LITERAL_HDR_VALUE; + lwsl_header(" HPKT_LITERAL_HDR_VALUE\n"); + h2n->hpack = HPKS_HLEN; + h2n->value = 0; + break; + } + if (c == 0x10) { /* literal name NEVER */ + h2n->hpack_type = HPKT_LITERAL_HDR_VALUE_NEVER; + lwsl_header(" HPKT_LITERAL_HDR_VALUE_NEVER\n"); + h2n->hpack = HPKS_HLEN; + h2n->value = 0; + break; + } + lwsl_header("indexed\n"); + /* indexed name */ + if (c & 0x10) { + h2n->hpack_type = HPKT_INDEXED_HDR_4_VALUE_NEVER; + lwsl_header(" HPKT_LITERAL_HDR_4_VALUE_NEVER\n"); + } else { + h2n->hpack_type = HPKT_INDEXED_HDR_4_VALUE; + lwsl_header(" HPKT_INDEXED_HDR_4_VALUE\n"); + } + h2n->hdr_idx = 0; + if ((c & 0xf) == 0xf) { + h2n->hpack_len = c & 0xf; + h2n->hpack_m = 0xf; + h2n->hpack_len = 0; + h2n->hpack = HPKS_IDX_EXT; + break; + } + h2n->hdr_idx = c & 0xf; + h2n->value = 1; + h2n->hpack = HPKS_HLEN; + break; + + case 0x20: + case 0x30: /* header table size update */ + /* possibly-extended size value (5) */ + lwsl_header("HPKT_SIZE_5 %x\n", c &0x1f); + h2n->hpack_type = HPKT_SIZE_5; + h2n->hpack_len = c & 0x1f; + if (h2n->hpack_len == 0x1f) { + h2n->hpack_m = 0x1f; + h2n->hpack_len = 0; + h2n->hpack = HPKS_IDX_EXT; + break; + } + h2n->last_action_dyntable_resize = 1; + if (lws_hpack_dynamic_size(wsi, h2n->hpack_len)) + return 1; + break; + } + break; + + case HPKS_IDX_EXT: + h2n->hpack_len = h2n->hpack_len | + ((c & 0x7f) << h2n->ext_count); + h2n->ext_count += 7; + if (c & 0x80) /* extended int not complete yet */ + break; + + /* extended integer done */ + h2n->hpack_len += h2n->hpack_m; + lwsl_header("HPKS_IDX_EXT: hpack_len %d\n", h2n->hpack_len); + + switch (h2n->hpack_type) { + case HPKT_INDEXED_HDR_7: + if (lws_hpack_use_idx_hdr(wsi, h2n->hpack_len, + h2n->hdr_idx)) { + lwsl_notice("%s: hd7 use fail\n", __func__); + return 1; + } + h2n->hpack = HPKS_TYPE; + break; + + case HPKT_SIZE_5: + h2n->last_action_dyntable_resize = 1; + if (lws_hpack_dynamic_size(wsi, h2n->hpack_len)) + return 1; + h2n->hpack = HPKS_TYPE; + break; + + default: + h2n->hdr_idx = h2n->hpack_len; + if (!h2n->hdr_idx) { + lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, + "extended header index was 0"); + return 1; + } + h2n->value = 1; + h2n->hpack = HPKS_HLEN; + break; + } + break; + + case HPKS_HLEN: /* [ H | 7+ ] */ + h2n->huff = !!(c & 0x80); + h2n->hpack_pos = 0; + h2n->hpack_len = c & 0x7f; + + if (h2n->hpack_len == 0x7f) { + h2n->hpack_m = 0x7f; + h2n->hpack_len = 0; + h2n->ext_count = 0; + h2n->hpack = HPKS_HLEN_EXT; + break; + } +pre_data: + h2n->hpack = HPKS_DATA; + if (!h2n->value || !h2n->hdr_idx) { + wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; + wsi->u.hdr.lextable_pos = 0; + h2n->unknown_header = 0; + break; + } + + if (h2n->hpack_type == HPKT_LITERAL_HDR_VALUE || + h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR || + h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER) { + n = wsi->u.hdr.parser_state; + if (n == 255) { + n = -1; + h2n->hdr_idx = -1; + } else + h2n->hdr_idx = 1; + } else { + n = lws_token_from_index(wsi, h2n->hdr_idx, NULL, + NULL, NULL); + lwsl_header(" lws_tok_from_idx(%d) says %d\n", + h2n->hdr_idx, n); + } + + if (n == LWS_HPACK_IGNORE_ENTRY || n == -1) + h2n->hdr_idx = LWS_HPACK_IGNORE_ENTRY; + + switch (h2n->hpack_type) { + /* + * hpack types with literal headers were parsed by the lws + * header SM... on recognition of a known lws header, it does + * the correct lws_frag_start() for us already. Other types + * (ie, indexed header) need us to do it here. + */ + case HPKT_LITERAL_HDR_VALUE_INCR: + case HPKT_LITERAL_HDR_VALUE: + case HPKT_LITERAL_HDR_VALUE_NEVER: + break; + default: + if (n != -1 && n != LWS_HPACK_IGNORE_ENTRY && + lws_frag_start(wsi, n)) { + lwsl_header("%s: frag start failed\n", __func__); + return 1; + } + break; + } + break; + + case HPKS_HLEN_EXT: + h2n->hpack_len = h2n->hpack_len | + ((c & 0x7f) << h2n->ext_count); + h2n->ext_count += 7; + if (c & 0x80) /* extended integer not complete yet */ + break; + + h2n->hpack_len += h2n->hpack_m; + goto pre_data; + + case HPKS_DATA: + //lwsl_header(" 0x%02X huff %d\n", c, h2n->huff); + c1 = c; + + for (n = 0; n < 8; n++) { + if (h2n->huff) { + char b = (c >> 7) & 1; + prev = h2n->hpack_pos; + h2n->hpack_pos = huftable_decode( + h2n->hpack_pos, b); + c <<= 1; + if (h2n->hpack_pos == 0xffff) { + lwsl_notice("Huffman err\n"); + return 1; + } + if (!(h2n->hpack_pos & 0x8000)) { + if (!b) + h2n->zero_huff_padding = 1; + h2n->huff_pad++; + continue; + } + c1 = h2n->hpack_pos & 0x7fff; + h2n->hpack_pos = 0; + h2n->huff_pad = 0; + h2n->zero_huff_padding = 0; + + /* EOS |11111111|11111111|11111111|111111 */ + if (!c1 && prev == HUFTABLE_0x100_PREV) { + lws_h2_goaway(nwsi, + H2_ERR_COMPRESSION_ERROR, + "Huffman EOT seen"); + return 1; + } + } else + n = 8; + + if (h2n->value) { /* value */ + + if (h2n->hdr_idx && + h2n->hdr_idx != LWS_HPACK_IGNORE_ENTRY) { + + if (ah->hdr_token_idx == + WSI_TOKEN_HTTP_COLON_PATH) { + + switch (lws_parse_urldecode( + wsi, &c1)) { + case LPUR_CONTINUE: + break; + case LPUR_SWALLOW: + goto swallow; + case LPUR_EXCESSIVE: + case LPUR_FORBID: + lws_h2_goaway(nwsi, + H2_ERR_PROTOCOL_ERROR, + "Evil URI"); + return 1; + + default: + return -1; + } + } + if (lws_frag_append(wsi, c1)) { + lwsl_notice("%s: frag app fail\n", + __func__); + return 1; + } + } //else + //lwsl_header("ignoring %c\n", c1); + } else { + /* + * Convert name using existing parser, + * If h2n->unknown_header == 0, result is + * in wsi->u.hdr.parser_state + * using WSI_TOKEN_GET_URI. + * + * If unknown header h2n->unknown_header + * will be set. + */ + h2n->hpack_hdr_len++; + if (h2n->is_first_header_char) { + h2n->is_first_header_char = 0; + h2n->first_hdr_char = c1; + } + lwsl_header("parser: %c\n", c1); + /* uppercase header names illegal */ + if (c1 >= 'A' && c1 <= 'Z') { + lws_h2_goaway(nwsi, + H2_ERR_COMPRESSION_ERROR, + "Uppercase literal hpack hdr"); + return 1; + } + if (!h2n->unknown_header && lws_parse(wsi, c1)) + h2n->unknown_header = 1; + } +swallow: + (void)n; + } // for n + + if (--h2n->hpack_len) + break; + + /* + * The header (h2n->value = 0) or the payload (h2n->value = 1) + * is complete. + */ + + if (h2n->huff && (h2n->huff_pad > 7 || + (h2n->zero_huff_padding && h2n->huff_pad))) { + lwsl_notice("zero_huff_padding: %d huff_pad: %d\n", + h2n->zero_huff_padding, h2n->huff_pad); + lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, + "Huffman padding excessive or wrong"); + return 1; + } + + if (!h2n->value && ( + h2n->hpack_type == HPKT_LITERAL_HDR_VALUE || + h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR || + h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER)) { + h2n->hdr_idx = LWS_HPACK_IGNORE_ENTRY; + lwsl_header("wsi->u.hdr.parser_state: %d\n", + wsi->u.hdr.parser_state); + + if (wsi->u.hdr.parser_state == WSI_TOKEN_NAME_PART) { + /* h2 headers come without the colon */ + n = lws_parse(wsi, ':'); + (void)n; + } + + if (wsi->u.hdr.parser_state == WSI_TOKEN_NAME_PART || + wsi->u.hdr.parser_state == WSI_TOKEN_SKIPPING) { + h2n->unknown_header = 1; + wsi->u.hdr.parser_state = -1; + wsi->seen_nonpseudoheader = 1; + } + } + + n = 8; + + /* we have the header */ + if (!h2n->value) { + h2n->value = 1; + h2n->hpack = HPKS_HLEN; + h2n->huff_pad = 0; + h2n->zero_huff_padding = 0; + h2n->ext_count = 0; + break; + } + + /* + * we have got both the header and value + */ + + m = -1; + switch (h2n->hpack_type) { + /* + * These are the only two that insert to the dyntable + */ + /* NEW indexed hdr with value */ + case HPKT_INDEXED_HDR_6_VALUE_INCR: + /* header length is determined by known index */ + m = lws_token_from_index(wsi, h2n->hdr_idx, NULL, NULL, + &h2n->hpack_hdr_len); + goto add_it; + /* NEW literal hdr with value */ + case HPKT_LITERAL_HDR_VALUE_INCR: + /* + * hdr is a new literal, so length is already in + * h2n->hpack_hdr_len + */ + m = wsi->u.hdr.parser_state; + if (h2n->unknown_header || + wsi->u.hdr.parser_state == WSI_TOKEN_NAME_PART || + wsi->u.hdr.parser_state == WSI_TOKEN_SKIPPING) { + if (h2n->first_hdr_char == ':') { + lwsl_info("HPKT_LITERAL_HDR_VALUE_INCR:" + " end state %d unk hdr %d\n", + wsi->u.hdr.parser_state, + h2n->unknown_header); + /* unknown pseudoheaders are illegal */ + lws_h2_goaway(nwsi, + H2_ERR_PROTOCOL_ERROR, + "Unknown pseudoheader"); + return 1; + } + m = LWS_HPACK_IGNORE_ENTRY; + } +add_it: + /* + * mark us as having been set at the time of dynamic + * token insertion. + */ + ah->frags[ah->nfrag].flags |= 1; + + if (lws_dynamic_token_insert(wsi, h2n->hpack_hdr_len, m, + &ah->data[ah->frags[ah->nfrag].offset], + ah->frags[ah->nfrag].len)) { + lwsl_notice("%s: tok_insert fail\n", __func__); + return 1; + } + break; + + default: + break; + } + + if (h2n->hdr_idx != LWS_HPACK_IGNORE_ENTRY && lws_frag_end(wsi)) + return 1; + + if (h2n->hpack_type != HPKT_INDEXED_HDR_6_VALUE_INCR) { + + if (h2n->hpack_type == HPKT_LITERAL_HDR_VALUE || + h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR || + h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER) { + m = wsi->u.hdr.parser_state; + if (m == 255) + m = -1; + } else { + m = lws_token_from_index(wsi, h2n->hdr_idx, NULL, NULL, + NULL); + lwsl_notice("token from index(%d) says %d\n", h2n->hdr_idx, m); + } + } + + if (m != -1 && m != LWS_HPACK_IGNORE_ENTRY) + lws_dump_header(wsi, m); + + if (lws_hpack_handle_pseudo_rules(nwsi, wsi, m)) + return 1; + + h2n->is_first_header_char = 1; + h2n->hpack = HPKS_TYPE; + break; + } + + return 0; +} + + + +static int +lws_h2_num_start(int starting_bits, unsigned long num) +{ + int mask = (1 << starting_bits) - 1; + + if (num < mask) + return (int)num; + + return mask; +} + +static int +lws_h2_num(int starting_bits, unsigned long num, + unsigned char **p, unsigned char *end) +{ + int mask = (1 << starting_bits) - 1; + + if (num < mask) + return 0; + + num -= mask; + do { + if (num > 127) + *((*p)++) = 0x80 | (num & 0x7f); + else + *((*p)++) = 0x00 | (num & 0x7f); + if (*p >= end) + return 1; + num >>= 7; + } while (num); + + return 0; +} + +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 len; + + lwsl_header("%s: %p %s:%s\n", __func__, *p, name, value); + + len = strlen((char *)name); + if (len) + if (name[len - 1] == ':') + len--; + + if (wsi->http2_substream && !strncmp((const char *)name, + "transfer-encoding", len)) { + lwsl_header("rejecting %s\n", name); + + return 0; + } + + if (end - *p < len + length + 8) + return 1; + + *((*p)++) = 0; /* literal hdr, literal name, */ + + *((*p)++) = 0 | lws_h2_num_start(7, len); /* non-HUF */ + if (lws_h2_num(7, len, p, end)) + return 1; + memcpy(*p, name, len); + *p += len; + + *((*p)++) = 0 | lws_h2_num_start(7, length); /* non-HUF */ + if (lws_h2_num(7, length, p, end)) + return 1; + + memcpy(*p, value, length); + *p += length; + + //lwsl_hexdump(op, *p -op); + + return 0; +} + +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) +{ + const unsigned char *name; + + name = lws_token_to_string(token); + if (!name) + return 1; + + return lws_add_http2_header_by_name(wsi, name, value, length, p, end); +} + +int lws_add_http2_header_status(struct lws *wsi, unsigned int code, + unsigned char **p, unsigned char *end) +{ + unsigned char status[10]; + int n; + + wsi->u.h2.send_END_STREAM = 0; // !!(code >= 400); + + n = sprintf((char *)status, "%u", code); + if (lws_add_http2_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_STATUS, + status, n, p, end)) + + return 1; + + return 0; +} diff -Nru libwebsockets-4.0.20/lib/http2/http2.c libwebsockets-2.4.2/lib/http2/http2.c --- libwebsockets-4.0.20/lib/http2/http2.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/http2/http2.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1581 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + + +#include "private-libwebsockets.h" + +/* + * bitmap of control messages that are valid to receive for each http2 state + */ + +static const uint16_t http2_rx_validity[] = { + /* LWS_H2S_IDLE */ + (1 << LWS_H2_FRAME_TYPE_SETTINGS) | + (1 << LWS_H2_FRAME_TYPE_PRIORITY) | +// (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE)| /* ignore */ + (1 << LWS_H2_FRAME_TYPE_HEADERS) | + (1 << LWS_H2_FRAME_TYPE_CONTINUATION), + /* LWS_H2S_RESERVED_LOCAL */ + (1 << LWS_H2_FRAME_TYPE_SETTINGS) | + (1 << LWS_H2_FRAME_TYPE_RST_STREAM) | + (1 << LWS_H2_FRAME_TYPE_PRIORITY) | + (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE), + /* LWS_H2S_RESERVED_REMOTE */ + (1 << LWS_H2_FRAME_TYPE_SETTINGS) | + (1 << LWS_H2_FRAME_TYPE_HEADERS) | + (1 << LWS_H2_FRAME_TYPE_CONTINUATION) | + (1 << LWS_H2_FRAME_TYPE_RST_STREAM) | + (1 << LWS_H2_FRAME_TYPE_PRIORITY), + /* LWS_H2S_OPEN */ + (1 << LWS_H2_FRAME_TYPE_DATA) | + (1 << LWS_H2_FRAME_TYPE_HEADERS) | + (1 << LWS_H2_FRAME_TYPE_PRIORITY) | + (1 << LWS_H2_FRAME_TYPE_RST_STREAM) | + (1 << LWS_H2_FRAME_TYPE_SETTINGS) | + (1 << LWS_H2_FRAME_TYPE_PUSH_PROMISE) | + (1 << LWS_H2_FRAME_TYPE_PING) | + (1 << LWS_H2_FRAME_TYPE_GOAWAY) | + (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) | + (1 << LWS_H2_FRAME_TYPE_CONTINUATION), + /* LWS_H2S_HALF_CLOSED_REMOTE */ + (1 << LWS_H2_FRAME_TYPE_SETTINGS) | + (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) | + (1 << LWS_H2_FRAME_TYPE_PRIORITY) | + (1 << LWS_H2_FRAME_TYPE_RST_STREAM), + /* LWS_H2S_HALF_CLOSED_LOCAL */ + (1 << LWS_H2_FRAME_TYPE_DATA) | + (1 << LWS_H2_FRAME_TYPE_HEADERS) | + (1 << LWS_H2_FRAME_TYPE_PRIORITY) | + (1 << LWS_H2_FRAME_TYPE_RST_STREAM) | + (1 << LWS_H2_FRAME_TYPE_SETTINGS) | + (1 << LWS_H2_FRAME_TYPE_PUSH_PROMISE) | + (1 << LWS_H2_FRAME_TYPE_PING) | + (1 << LWS_H2_FRAME_TYPE_GOAWAY) | + (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) | + (1 << LWS_H2_FRAME_TYPE_CONTINUATION), + /* LWS_H2S_CLOSED */ + (1 << LWS_H2_FRAME_TYPE_SETTINGS) | + (1 << LWS_H2_FRAME_TYPE_PRIORITY) | + (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) | + (1 << LWS_H2_FRAME_TYPE_RST_STREAM), +}; + +static const char *preface = "PRI * HTTP/2.0\x0d\x0a\x0d\x0aSM\x0d\x0a\x0d\x0a"; + +static const char * const h2_state_names[] = { + "LWS_H2S_IDLE", + "LWS_H2S_RESERVED_LOCAL", + "LWS_H2S_RESERVED_REMOTE", + "LWS_H2S_OPEN", + "LWS_H2S_HALF_CLOSED_REMOTE", + "LWS_H2S_HALF_CLOSED_LOCAL", + "LWS_H2S_CLOSED", +}; + +#if 0 +static const char * const h2_setting_names[] = { + "", + "H2SET_HEADER_TABLE_SIZE", + "H2SET_ENABLE_PUSH", + "H2SET_MAX_CONCURRENT_STREAMS", + "H2SET_INITIAL_WINDOW_SIZE", + "H2SET_MAX_FRAME_SIZE", + "H2SET_MAX_HEADER_LIST_SIZE", +}; + +void +lws_h2_dump_settings(struct http2_settings *set) +{ + int n; + + for (n = 1; n < H2SET_COUNT; n++) + lwsl_notice(" %30s: %10d\n", h2_setting_names[n], set->s[n]); +} +#else +void +lws_h2_dump_settings(struct http2_settings *set) +{ +} +#endif + +void lws_h2_init(struct lws *wsi) +{ + wsi->u.h2.h2n->set = wsi->vhost->set; +} + +static void +lws_h2_state(struct lws *wsi, enum lws_h2_states s) +{ + if (!wsi) + return; + lwsl_info("%s: wsi %p: state %s -> %s\n", __func__, wsi, + h2_state_names[wsi->u.h2.h2_state], + h2_state_names[s]); + + (void)h2_state_names; + wsi->u.h2.h2_state = (uint8_t)s; +} + +struct lws * +lws_wsi_server_new(struct lws_vhost *vh, struct lws *parent_wsi, + unsigned int sid) +{ + struct lws *wsi; + struct lws *nwsi = lws_get_network_wsi(parent_wsi); + struct lws_h2_netconn *h2n = nwsi->u.h2.h2n; + + /* + * The identifier of a newly established stream MUST be numerically + * greater than all streams that the initiating endpoint has opened or + * reserved. This governs streams that are opened using a HEADERS frame + * and streams that are reserved using PUSH_PROMISE. An endpoint that + * receives an unexpected stream identifier MUST respond with a + * connection error (Section 5.4.1) of type PROTOCOL_ERROR. + */ + if (sid <= h2n->highest_sid_opened) { + lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, "Bad sid"); + return NULL; + } + + /* no more children allowed by parent */ + if (parent_wsi->u.h2.child_count + 1 > + parent_wsi->u.h2.h2n->set.s[H2SET_MAX_CONCURRENT_STREAMS]) { + lwsl_notice("reached concurrent stream limit\n"); + return NULL; + } + wsi = lws_create_new_server_wsi(vh); + if (!wsi) { + lwsl_notice("new server wsi failed (vh %p)\n", vh); + return NULL; + } + + h2n->highest_sid_opened = sid; + wsi->u.h2.my_sid = sid; + wsi->http2_substream = 1; + wsi->seen_nonpseudoheader = 0; + + wsi->u.h2.parent_wsi = parent_wsi; + /* new guy's sibling is whoever was the first child before */ + wsi->u.h2.sibling_list = parent_wsi->u.h2.child_list; + /* first child is now the new guy */ + parent_wsi->u.h2.child_list = wsi; + parent_wsi->u.h2.child_count++; + + wsi->u.h2.my_priority = 16; + wsi->u.h2.tx_cr = nwsi->u.h2.h2n->set.s[H2SET_INITIAL_WINDOW_SIZE]; + wsi->u.h2.peer_tx_cr_est = nwsi->vhost->set.s[H2SET_INITIAL_WINDOW_SIZE]; + + wsi->state = LWSS_HTTP2_ESTABLISHED; + wsi->mode = parent_wsi->mode; + + wsi->protocol = &vh->protocols[0]; + if (lws_ensure_user_space(wsi)) + goto bail1; + + wsi->vhost->conn_stats.h2_subs++; + + lwsl_info("%s: %p new ch %p, sid %d, usersp=%p, tx cr %d, " + "peer_credit %d (nwsi tx_cr %d)\n", + __func__, parent_wsi, wsi, sid, wsi->user_space, + wsi->u.h2.tx_cr, wsi->u.h2.peer_tx_cr_est, nwsi->u.h2.tx_cr); + + return wsi; + +bail1: + /* undo the insert */ + parent_wsi->u.h2.child_list = wsi->u.h2.sibling_list; + parent_wsi->u.h2.child_count--; + + if (wsi->user_space) + lws_free_set_NULL(wsi->user_space); + vh->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, NULL, NULL, 0); + lws_free(wsi); + + return NULL; +} + +struct lws * +lws_h2_wsi_from_id(struct lws *parent_wsi, unsigned int sid) +{ + lws_start_foreach_ll(struct lws *, wsi, parent_wsi->u.h2.child_list) { + if (wsi->u.h2.my_sid == sid) + return wsi; + } lws_end_foreach_ll(wsi, u.h2.sibling_list); + + return NULL; +} + +int lws_remove_server_child_wsi(struct lws_context *context, struct lws *wsi) +{ + lws_start_foreach_llp(struct lws **, w, wsi->u.h2.child_list) { + if (*w == wsi) { + *w = wsi->u.h2.sibling_list; + (wsi->u.h2.parent_wsi)->u.h2.child_count--; + return 0; + } + } lws_end_foreach_llp(w, u.h2.sibling_list); + + lwsl_err("%s: can't find %p\n", __func__, wsi); + + return 1; +} + +void +lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pps) +{ + struct lws *nwsi = lws_get_network_wsi(wsi); + struct lws_h2_netconn *h2n = nwsi->u.h2.h2n; + + pps->next = h2n->pps; + h2n->pps = pps; + lws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_DISABLE | + LWS_RXFLOW_REASON_H2_PPS_PENDING); + lws_callback_on_writable(wsi); +} + +static struct lws_h2_protocol_send * +lws_h2_new_pps(enum lws_h2_protocol_send_type type) +{ + struct lws_h2_protocol_send *pps = lws_malloc(sizeof(*pps), "pps"); + + if (pps) + pps->type = type; + + return pps; +} + +int +lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason) +{ + struct lws_h2_netconn *h2n = wsi->u.h2.h2n; + struct lws_h2_protocol_send *pps; + + if (h2n->type == LWS_H2_FRAME_TYPE_COUNT) + return 0; + + pps = lws_h2_new_pps(LWS_H2_PPS_GOAWAY); + if (!pps) + return 1; + + lwsl_info("%s: %p: ERR 0x%x, '%s'\n", __func__, wsi, err, reason); + + pps->u.ga.err = err; + pps->u.ga.highest_sid = h2n->highest_sid; + strncpy(pps->u.ga.str, reason, sizeof(pps->u.ga.str) - 1); + pps->u.ga.str[sizeof(pps->u.ga.str) - 1] = '\0'; + lws_pps_schedule(wsi, pps); + + h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */ + + return 0; +} + +int +lws_h2_rst_stream(struct lws *wsi, uint32_t err, const char *reason) +{ + struct lws *nwsi = lws_get_network_wsi(wsi); + struct lws_h2_netconn *h2n = nwsi->u.h2.h2n; + struct lws_h2_protocol_send *pps; + + if (h2n->type == LWS_H2_FRAME_TYPE_COUNT) + return 0; + + pps = lws_h2_new_pps(LWS_H2_PPS_RST_STREAM); + if (!pps) + return 1; + + lwsl_info("%s: RST_STREAM 0x%x, REASON '%s'\n", __func__, err, reason); + + pps->u.rs.sid = h2n->sid; + pps->u.rs.err = err; + lws_pps_schedule(wsi, pps); + + h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */ + lws_h2_state(wsi, LWS_H2_STATE_CLOSED); + + return 0; +} + +int +lws_h2_settings(struct lws *wsi, struct http2_settings *settings, + unsigned char *buf, int len) +{ + struct lws *nwsi = lws_get_network_wsi(wsi); + unsigned int a, b; + + if (!len) + return 0; + + if (len < LWS_H2_SETTINGS_LEN) + return 1; + + while (len >= LWS_H2_SETTINGS_LEN) { + a = (buf[0] << 8) | buf[1]; + if (!a || a >= H2SET_COUNT) + goto skip; + b = buf[2] << 24 | buf[3] << 16 | buf[4] << 8 | buf[5]; + + switch (a) { + case H2SET_HEADER_TABLE_SIZE: + break; + case H2SET_ENABLE_PUSH: + if (b > 1) { + lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, + "ENABLE_PUSH invalid arg"); + return 1; + } + break; + case H2SET_MAX_CONCURRENT_STREAMS: + break; + case H2SET_INITIAL_WINDOW_SIZE: + if (b > 0x7fffffff) { + lws_h2_goaway(nwsi, H2_ERR_FLOW_CONTROL_ERROR, + "Inital Window beyond max"); + return 1; + } + /* + * In addition to changing the flow-control window for + * streams that are not yet active, a SETTINGS frame + * can alter the initial flow-control window size for + * streams with active flow-control windows (that is, + * streams in the "open" or "half-closed (remote)" + * state). When the value of + * SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver + * MUST adjust the size of all stream flow-control + * windows that it maintains by the difference between + * the new value and the old value. + */ + + lws_start_foreach_ll(struct lws *, w, + nwsi->u.h2.child_list) { + lwsl_info("%s: adi child tc cr %d +%d -> %d", + __func__, + w->u.h2.tx_cr, b - settings->s[a], + w->u.h2.tx_cr + b - settings->s[a]); + w->u.h2.tx_cr += b - settings->s[a]; + if (w->u.h2.tx_cr > 0 && + w->u.h2.tx_cr <= b - settings->s[a]) + lws_callback_on_writable(w); + } lws_end_foreach_ll(w, u.h2.sibling_list); + + break; + case H2SET_MAX_FRAME_SIZE: + if (b < wsi->vhost->set.s[H2SET_MAX_FRAME_SIZE]) { + lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, + "Frame size < initial"); + return 1; + } + if (b > 0x007fffff) { + lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, + "Settings Frame size above max"); + return 1; + } + break; + case H2SET_MAX_HEADER_LIST_SIZE: + break; + } + settings->s[a] = b; + lwsl_info("http2 settings %d <- 0x%x\n", a, b); +skip: + len -= LWS_H2_SETTINGS_LEN; + buf += LWS_H2_SETTINGS_LEN; + } + + if (len) + return 1; + + lws_h2_dump_settings(settings); + + return 0; +} + +/* RFC7640 Sect 6.9 + * + * The WINDOW_UPDATE frame can be specific to a stream or to the entire + * connection. In the former case, the frame's stream identifier + * indicates the affected stream; in the latter, the value "0" indicates + * that the entire connection is the subject of the frame. + * + * ... + * + * Two flow-control windows are applicable: the stream flow-control + * window and the connection flow-control window. The sender MUST NOT + * send a flow-controlled frame with a length that exceeds the space + * available in either of the flow-control windows advertised by the + * receiver. Frames with zero length with the END_STREAM flag set (that + * is, an empty DATA frame) MAY be sent if there is no available space + * in either flow-control window. + */ + +int +lws_h2_tx_cr_get(struct lws *wsi) +{ + int c = wsi->u.h2.tx_cr; + struct lws *nwsi; + + if (!wsi->http2_substream && !wsi->upgraded_to_http2) + return ~0x80000000; + + nwsi = lws_get_network_wsi(wsi); + + lwsl_info ("%s: %p: own tx credit %d: nwsi credit %d\n", + __func__, wsi, c, nwsi->u.h2.tx_cr); + + if (nwsi->u.h2.tx_cr < c) + c = nwsi->u.h2.tx_cr; + + if (c < 0) + return 0; + + return c; +} + +void +lws_h2_tx_cr_consume(struct lws *wsi, int consumed) +{ + struct lws *nwsi = lws_get_network_wsi(wsi); + + wsi->u.h2.tx_cr -= consumed; + + if (nwsi != wsi) + nwsi->u.h2.tx_cr -= consumed; +} + +int lws_h2_frame_write(struct lws *wsi, int type, int flags, + unsigned int sid, unsigned int len, unsigned char *buf) +{ + struct lws *nwsi = lws_get_network_wsi(wsi); + unsigned char *p = &buf[-LWS_H2_FRAME_HEADER_LENGTH]; + int n; + + *p++ = len >> 16; + *p++ = len >> 8; + *p++ = len; + *p++ = type; + *p++ = flags; + *p++ = sid >> 24; + *p++ = sid >> 16; + *p++ = sid >> 8; + *p++ = sid; + + lwsl_debug("%s: %p (eff %p). typ %d, fl 0x%x, sid=%d, len=%d, " + "txcr=%d, nwsi->txcr=%d\n", __func__, wsi, nwsi, type, flags, + sid, len, wsi->u.h2.tx_cr, nwsi->u.h2.tx_cr); + + if (type == LWS_H2_FRAME_TYPE_DATA) { + if (wsi->u.h2.tx_cr < len) + lwsl_err("%s: %p: sending payload len %d" + " but tx_cr only %d!\n", __func__, wsi, + len, wsi->u.h2.tx_cr); + lws_h2_tx_cr_consume(wsi, len); + } + + n = lws_issue_raw(nwsi, &buf[-LWS_H2_FRAME_HEADER_LENGTH], + len + LWS_H2_FRAME_HEADER_LENGTH); + if (n < 0) + return n; + + if (n >= LWS_H2_FRAME_HEADER_LENGTH) + return n - LWS_H2_FRAME_HEADER_LENGTH; + + return n; +} + +static void lws_h2_set_bin(struct lws *wsi, int n, unsigned char *buf) +{ + *buf++ = n >> 8; + *buf++ = n; + *buf++ = wsi->u.h2.h2n->set.s[n] >> 24; + *buf++ = wsi->u.h2.h2n->set.s[n] >> 16; + *buf++ = wsi->u.h2.h2n->set.s[n] >> 8; + *buf = wsi->u.h2.h2n->set.s[n]; +} + +int lws_h2_do_pps_send(struct lws *wsi) +{ + struct lws_h2_netconn *h2n = wsi->u.h2.h2n; + struct lws_h2_protocol_send *pps = NULL; + struct lws *cwsi; + uint8_t set[LWS_PRE + 64], *p = &set[LWS_PRE], *q; + int n, m = 0; + + if (!h2n) + return 1; + + /* get the oldest pps */ + + lws_start_foreach_llp(struct lws_h2_protocol_send **, pps1, h2n->pps) { + if ((*pps1)->next == NULL) { /* we are the oldest in the list */ + pps = *pps1; /* remove us from the list */ + *pps1 = NULL; + continue; + } + } lws_end_foreach_llp(pps1, next); + + if (!pps) + return 1; + + lwsl_info("%s: %p: %d\n", __func__, wsi, pps->type); + + switch (pps->type) { + + case LWS_H2_PPS_MY_SETTINGS: + /* + * if any of our settings varies from h2 "default defaults" + * then we must inform the perr + */ + for (n = 1; n < H2SET_COUNT; n++) + if (h2n->set.s[n] != lws_h2_defaults.s[n]) { + lwsl_debug("sending SETTING %d 0x%x\n", n, + wsi->u.h2.h2n->set.s[n]); + lws_h2_set_bin(wsi, n, &set[LWS_PRE + m]); + m += sizeof(h2n->one_setting); + } + n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_SETTINGS, + 0, LWS_H2_STREAM_ID_MASTER, m, + &set[LWS_PRE]); + if (n != m) { + lwsl_info("send %d %d\n", n, m); + goto bail; + } + break; + + case LWS_H2_PPS_ACK_SETTINGS: + /* send ack ... always empty */ + n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_SETTINGS, 1, + LWS_H2_STREAM_ID_MASTER, 0, &set[LWS_PRE]); + if (n) { + lwsl_err("ack tells %d\n", n); + goto bail; + } + /* this is the end of the preface dance then? */ + if (wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS) { + wsi->state = LWSS_HTTP2_ESTABLISHED; + wsi->u.http.fop_fd = NULL; + if (lws_is_ssl(lws_get_network_wsi(wsi))) + break; + /* + * we need to treat the headers from the upgrade as the + * first job. So these need to get shifted to sid 1. + */ + h2n->swsi = lws_wsi_server_new(wsi->vhost, wsi, 1); + if (!h2n->swsi) + goto bail; + + /* pass on the initial headers to SID 1 */ + h2n->swsi->u.http.ah = wsi->u.http.ah; + wsi->u.http.ah = NULL; + + lwsl_info("%s: inherited headers %p\n", __func__, + h2n->swsi->u.http.ah); + h2n->swsi->u.h2.tx_cr = + h2n->set.s[H2SET_INITIAL_WINDOW_SIZE]; + lwsl_info("initial tx credit on conn %p: %d\n", + h2n->swsi, h2n->swsi->u.h2.tx_cr); + h2n->swsi->u.h2.initialized = 1; + /* demanded by HTTP2 */ + h2n->swsi->u.h2.END_STREAM = 1; + lwsl_info("servicing initial http request\n"); + + wsi->vhost->conn_stats.h2_trans++; + + if (lws_http_action(h2n->swsi)) + goto bail; + + break; + } + break; + case LWS_H2_PPS_PONG: + lwsl_debug("sending PONG\n"); + memcpy(&set[LWS_PRE], pps->u.ping.ping_payload, 8); + n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_PING, + LWS_H2_FLAG_SETTINGS_ACK, + LWS_H2_STREAM_ID_MASTER, 8, + &set[LWS_PRE]); + if (n != 8) { + lwsl_info("send %d %d\n", n, m); + goto bail; + } + break; + + case LWS_H2_PPS_GOAWAY: + lwsl_info("LWS_H2_PPS_GOAWAY\n"); + *p++ = pps->u.ga.highest_sid >> 24; + *p++ = pps->u.ga.highest_sid >> 16; + *p++ = pps->u.ga.highest_sid >> 8; + *p++ = pps->u.ga.highest_sid; + *p++ = pps->u.ga.err >> 24; + *p++ = pps->u.ga.err >> 16; + *p++ = pps->u.ga.err >> 8; + *p++ = pps->u.ga.err; + q = (unsigned char *)pps->u.ga.str; + n = 0; + while (*q && n++ < sizeof(pps->u.ga.str)) + *p++ = *q++; + h2n->we_told_goaway = 1; + n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_GOAWAY, 0, + LWS_H2_STREAM_ID_MASTER, + p - &set[LWS_PRE], &set[LWS_PRE]); + if (n != 4) { + lwsl_info("send %d %d\n", n, m); + goto bail; + } + goto bail; + + case LWS_H2_PPS_RST_STREAM: + lwsl_info("LWS_H2_PPS_RST_STREAM\n"); + *p++ = pps->u.rs.err >> 24; + *p++ = pps->u.rs.err >> 16; + *p++ = pps->u.rs.err >> 8; + *p++ = pps->u.rs.err; + n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_RST_STREAM, + 0, pps->u.rs.sid, 4, &set[LWS_PRE]); + if (n != 4) { + lwsl_info("send %d %d\n", n, m); + goto bail; + } + cwsi = lws_h2_wsi_from_id(wsi, pps->u.rs.sid); + if (cwsi) + lws_close_free_wsi(cwsi, 0); + break; + + case LWS_H2_PPS_UPDATE_WINDOW: + lwsl_notice("LWS_H2_PPS_UPDATE_WINDOW: sid %d: add %d\n", + pps->u.update_window.sid, + pps->u.update_window.credit); + *p++ = pps->u.update_window.credit >> 24; + *p++ = pps->u.update_window.credit >> 16; + *p++ = pps->u.update_window.credit >> 8; + *p++ = pps->u.update_window.credit; + n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_WINDOW_UPDATE, + 0, pps->u.update_window.sid, 4, + &set[LWS_PRE]); + if (n != 4) { + lwsl_info("send %d %d\n", n, m); + goto bail; + } + break; + + default: + break; + } + + lws_free(pps); + + return 0; + +bail: + lws_free(pps); + + return 1; +} + +/* + * The frame header part has just completely arrived. + * Perform actions for frame completion. + */ +static int +lws_h2_parse_frame_header(struct lws *wsi) +{ + struct lws_h2_netconn *h2n = wsi->u.h2.h2n; + struct lws_h2_protocol_send *pps; + int n; + + /* + * We just got the frame header + */ + h2n->count = 0; + h2n->swsi = wsi; + /* b31 is a reserved bit */ + h2n->sid = h2n->sid & 0x7fffffff; + + if (h2n->sid && !(h2n->sid & 1)) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "Even Stream ID"); + + return 0; + } + + /* let the network wsi live a bit longer if subs are active */ + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 10); + + /* let the network wsi live a bit longer if subs are active */ + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 10); + + if (h2n->sid) + h2n->swsi = lws_h2_wsi_from_id(wsi, h2n->sid); + + lwsl_info("%p (%p): fr hdr: typ 0x%x, flags 0x%x, sid 0x%x, len 0x%x\n", + wsi, h2n->swsi, h2n->type, h2n->flags, h2n->sid, + h2n->length); + + if (h2n->we_told_goaway && h2n->sid > h2n->highest_sid) + h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */ + + if (h2n->type == LWS_H2_FRAME_TYPE_COUNT) + return 0; + + if (h2n->length > h2n->set.s[H2SET_MAX_FRAME_SIZE]) { + /* + * peer sent us something bigger than we told + * it we would allow + */ + lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, + "Peer ignored our frame size setting"); + return 0; + } + + if (h2n->swsi) + lwsl_info("%s: wsi %p, State: %s, received cmd %d\n", + __func__, h2n->swsi, + h2_state_names[h2n->swsi->u.h2.h2_state], h2n->type); + else { + /* if it's data, either way no swsi means CLOSED state */ + if (h2n->type == LWS_H2_FRAME_TYPE_DATA) { + lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED, + "Data for nonexistent sid"); + return 0; + } + /* if the sid is credible, treat as wsi for it closed */ + if (h2n->sid > h2n->highest_sid_opened && + h2n->type != LWS_H2_FRAME_TYPE_HEADERS && + h2n->type != LWS_H2_FRAME_TYPE_PRIORITY) { + /* if not credible, reject it */ + lwsl_info("%s: wsi %p, No child for sid %d, rx cmd %d\n", + __func__, h2n->swsi, h2n->sid, h2n->type); + lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED, + "Data for nonexistent sid"); + return 0; + } + } + + if (h2n->swsi && h2n->sid && + !(http2_rx_validity[h2n->swsi->u.h2.h2_state] & (1 << h2n->type))) { + lwsl_info("%s: wsi %p, State: %s, ILLEGAL cmdrx %d (OK 0x%x)\n", + __func__, h2n->swsi, + h2_state_names[h2n->swsi->u.h2.h2_state], h2n->type, + http2_rx_validity[h2n->swsi->u.h2.h2_state]); + + if (h2n->swsi->u.h2.h2_state == LWS_H2_STATE_CLOSED || + h2n->swsi->u.h2.h2_state == LWS_H2_STATE_HALF_CLOSED_REMOTE) + n = H2_ERR_STREAM_CLOSED; + else + n = H2_ERR_PROTOCOL_ERROR; + lws_h2_goaway(wsi, n, "invalid rx for state"); + + return 0; + } + + if (h2n->cont_exp && (h2n->cont_exp_sid != h2n->sid || + h2n->type != LWS_H2_FRAME_TYPE_CONTINUATION)) { + lwsl_info("%s: expected cont on sid %d (got %d on sid %d)\n", + __func__, h2n->cont_exp_sid, h2n->type, h2n->sid); + h2n->cont_exp = 0; + if (h2n->cont_exp_headers) + n = H2_ERR_COMPRESSION_ERROR; + else + n = H2_ERR_PROTOCOL_ERROR; + lws_h2_goaway(wsi, n, "Continuation hdrs State"); + + return 0; + } + + switch (h2n->type) { + case LWS_H2_FRAME_TYPE_DATA: + lwsl_info("seen incoming LWS_H2_FRAME_TYPE_DATA start\n"); + if (!h2n->sid) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "DATA 0 sid"); + break; + } + lwsl_info("Frame header DATA: sid %d\n", h2n->sid); + + if (!h2n->swsi) + break; + + h2n->swsi->u.h2.peer_tx_cr_est -= h2n->length; + lwsl_debug(" peer_tx_cr_est %d\n", + h2n->swsi->u.h2.peer_tx_cr_est); + if (h2n->swsi->u.h2.peer_tx_cr_est < 32768) { + h2n->swsi->u.h2.peer_tx_cr_est += 65536; + pps = lws_h2_new_pps(LWS_H2_PPS_UPDATE_WINDOW); + if (!pps) + return 1; + pps->u.update_window.sid = h2n->sid; + pps->u.update_window.credit = 65536; + lws_pps_schedule(wsi, pps); + pps = lws_h2_new_pps(LWS_H2_PPS_UPDATE_WINDOW); + if (!pps) + return 1; + pps->u.update_window.sid = 0; + pps->u.update_window.credit = 65536; + lws_pps_schedule(wsi, pps); + } + + if ( + h2n->swsi->u.h2.h2_state == LWS_H2_STATE_HALF_CLOSED_REMOTE || + h2n->swsi->u.h2.h2_state == LWS_H2_STATE_CLOSED) { + lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED, "conn closed"); + break; + } + break; + case LWS_H2_FRAME_TYPE_PRIORITY: + lwsl_info("LWS_H2_FRAME_TYPE_PRIORITY complete frame\n"); + if (!h2n->sid) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "Priority has 0 sid"); + break; + } + if (h2n->length != 5) { + lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, + "Priority has length other than 5"); + break; + } + break; + case LWS_H2_FRAME_TYPE_PUSH_PROMISE: + lwsl_info("LWS_H2_FRAME_TYPE_PUSH_PROMISE complete frame\n"); + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "Server only"); + break; + + case LWS_H2_FRAME_TYPE_GOAWAY: + break; + + case LWS_H2_FRAME_TYPE_RST_STREAM: + if (!h2n->sid) + return 1; + if (!h2n->swsi) { + if (h2n->sid <= h2n->highest_sid_opened) + break; + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "crazy sid on RST_STREAM"); + return 1; + } + if (h2n->length != 4) { + lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, + "RST_STREAM can only be length 4"); + break; + } + lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED); + break; + + case LWS_H2_FRAME_TYPE_SETTINGS: + lwsl_info("LWS_H2_FRAME_TYPE_SETTINGS complete frame\n"); + /* nonzero sid on settings is illegal */ + if (h2n->sid) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "Settings has nonzero sid"); + break; + } + + if (!(h2n->flags & LWS_H2_FLAG_SETTINGS_ACK)) { + if ((!h2n->length) || h2n->length % 6) { + lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, + "Settings length error"); + break; + } + lwsl_info("scheduled settings ack PPS\n"); + /* non-ACK coming in means we must ACK it */ + + + if (h2n->type == LWS_H2_FRAME_TYPE_COUNT) + return 0; + + pps = lws_h2_new_pps(LWS_H2_PPS_ACK_SETTINGS); + if (!pps) + return 1; + lws_pps_schedule(wsi, pps); + break; + } + /* came to us with ACK set... not allowed to have payload */ + + if (h2n->length) { + lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, + "Settings with ACK not allowed payload"); + break; + } + break; + case LWS_H2_FRAME_TYPE_PING: + if (h2n->sid) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "Ping has nonzero sid"); + break; + } + if (h2n->length != 8) { + lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, + "Ping payload can only be 8"); + break; + } + break; + case LWS_H2_FRAME_TYPE_CONTINUATION: + lwsl_info("LWS_H2_FRAME_TYPE_CONTINUATION: sid = %d\n", + h2n->sid); + + if (!h2n->cont_exp || + h2n->cont_exp_sid != h2n->sid || + !h2n->sid || + !h2n->swsi) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "unexpected CONTINUATION"); + break; + } + if (h2n->swsi->u.h2.END_HEADERS) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "END_HEADERS already seen"); + break; + } + /* END_STREAM is in HEADERS, skip resetting it */ + goto update_end_headers; + + case LWS_H2_FRAME_TYPE_HEADERS: + lwsl_info("HEADERS: frame header: sid = %d\n", h2n->sid); + if (!h2n->sid) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "sid 0"); + return 1; + } + + if (!h2n->swsi) { + /* no more children allowed by parent */ + if (wsi->u.h2.child_count + 1 > + wsi->u.h2.h2n->set.s[H2SET_MAX_CONCURRENT_STREAMS]) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "Another stream not allowed"); + + return 1; + } + + h2n->swsi = lws_wsi_server_new(wsi->vhost, wsi, + h2n->sid); + if (!h2n->swsi) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "OOM"); + + return 1; + } + } + + /* + * ah needs attaching to child wsi, even though + * we only fill it from network wsi + */ + if (!h2n->swsi->u.hdr.ah) + if (lws_header_table_attach(h2n->swsi, 0)) { + lwsl_err("%s: Failed to get ah\n", __func__); + return 1; + } + + /* + * The first use of a new stream identifier implicitly closes + * all streams in the "idle" state that might have been + * initiated by that peer with a lower-valued stream identifier. + * + * For example, if a client sends a HEADERS frame on stream 7 + * without ever sending a frame on stream 5, then stream 5 + * transitions to the "closed" state when the first frame for + * stream 7 is sent or received. + */ + lws_start_foreach_ll(struct lws *, w, wsi->u.h2.child_list) { + if (w->u.h2.my_sid < h2n->sid && + w->u.h2.h2_state == LWS_H2_STATE_IDLE) + lws_close_free_wsi(w, 0); + } lws_end_foreach_ll(w, u.h2.sibling_list); + + + /* END_STREAM means after servicing this, close the stream */ + h2n->swsi->u.h2.END_STREAM = + !!(h2n->flags & LWS_H2_FLAG_END_STREAM); + lwsl_info("%s: hdr END_STREAM = %d\n",__func__, + h2n->swsi->u.h2.END_STREAM); + + h2n->cont_exp = !(h2n->flags & LWS_H2_FLAG_END_HEADERS); + h2n->cont_exp_sid = h2n->sid; + h2n->cont_exp_headers = 1; + lws_header_table_reset(h2n->swsi, 0); + +update_end_headers: + /* no END_HEADERS means CONTINUATION must come */ + h2n->swsi->u.h2.END_HEADERS = + !!(h2n->flags & LWS_H2_FLAG_END_HEADERS); + if (h2n->swsi->u.h2.END_HEADERS) + h2n->cont_exp = 0; + lwsl_debug("END_HEADERS %d\n", h2n->swsi->u.h2.END_HEADERS); + break; + + case LWS_H2_FRAME_TYPE_WINDOW_UPDATE: + if (h2n->length != 4) { + lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, + "window update frame not 4"); + break; + } + lwsl_info("LWS_H2_FRAME_TYPE_WINDOW_UPDATE\n"); + break; + default: + lwsl_info("%s: ILLEGAL FRAME TYPE %d\n", __func__, h2n->type); + h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */ + break; + } + if (h2n->length == 0) + h2n->frame_state = 0; + + return 0; +} + +/* + * The last byte of the whole frame has been handled. + * Perform actions for frame completion. + */ +static int +lws_h2_parse_end_of_frame(struct lws *wsi) +{ + struct lws_h2_netconn *h2n = wsi->u.h2.h2n; + struct lws_h2_protocol_send *pps; + struct lws *eff_wsi = wsi; + const char *p; + int n; + + h2n->frame_state = 0; + h2n->count = 0; + + if (h2n->sid) + h2n->swsi = lws_h2_wsi_from_id(wsi, h2n->sid); + + if (h2n->sid > h2n->highest_sid) + h2n->highest_sid = h2n->sid; + + /* set our initial window size */ + if (!wsi->u.h2.initialized) { + wsi->u.h2.tx_cr = h2n->set.s[H2SET_INITIAL_WINDOW_SIZE]; + lwsl_info("initial tx credit on master %p: %d\n", wsi, + wsi->u.h2.tx_cr); + wsi->u.h2.initialized = 1; + } + + if (h2n->collected_priority && (h2n->dep & ~(1 << 31)) == h2n->sid) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "depends on own sid"); + return 0; + } + + switch (h2n->type) { + case LWS_H2_FRAME_TYPE_CONTINUATION: + case LWS_H2_FRAME_TYPE_HEADERS: + + /* service the http request itself */ + + if (h2n->last_action_dyntable_resize) { + lws_h2_goaway(wsi, H2_ERR_COMPRESSION_ERROR, + "dyntable resize last in headers"); + break; + } + + if (!h2n->swsi->u.h2.END_HEADERS) { + /* we are not finished yet */ + lwsl_info("witholding http action for continuation\n"); + break; + } + + /* confirm the hpack stream state is reasonable for finishing */ + + if (h2n->hpack != HPKS_TYPE) { + /* hpack incomplete */ + lwsl_info("hpack incomplete %d (type %d, len %d)\n", + h2n->hpack, h2n->type, h2n->hpack_len); + lws_h2_goaway(wsi, H2_ERR_COMPRESSION_ERROR, + "hpack incomplete"); + break; + } + + /* this is the last part of HEADERS */ + switch (h2n->swsi->u.h2.h2_state) { + case LWS_H2_STATE_IDLE: + lws_h2_state(h2n->swsi, LWS_H2_STATE_OPEN); + break; + case LWS_H2_STATE_RESERVED_REMOTE: + lws_h2_state(h2n->swsi, LWS_H2_STATE_HALF_CLOSED_LOCAL); + break; + } + + lwsl_info("http req, wsi=%p, h2n->swsi=%p\n", wsi, h2n->swsi); + h2n->swsi->hdr_parsing_completed = 1; + + if (lws_hdr_extant(h2n->swsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + h2n->swsi->u.http.rx_content_length = atoll( + lws_hdr_simple_ptr(h2n->swsi, + WSI_TOKEN_HTTP_CONTENT_LENGTH)); + h2n->swsi->u.http.rx_content_remain = + h2n->swsi->u.http.rx_content_length; + lwsl_info("setting rx_content_length %lld\n", + (long long)h2n->swsi->u.http.rx_content_length); + } + + { + int n = 0, len; + char buf[256]; + const unsigned char *c; + + do { + c = lws_token_to_string(n); + if (!c) { + n++; + continue; + } + + len = lws_hdr_total_length(h2n->swsi, n); + if (!len || len > sizeof(buf) - 1) { + n++; + continue; + } + + lws_hdr_copy(h2n->swsi, buf, sizeof buf, n); + buf[sizeof(buf) - 1] = '\0'; + + lwsl_info(" %s = %s\n", (char *)c, buf); + n++; + } while (c); + } + + if (h2n->swsi->u.h2.h2_state == LWS_H2_STATE_HALF_CLOSED_REMOTE || + h2n->swsi->u.h2.h2_state == LWS_H2_STATE_CLOSED) { + lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED, + "Banning service on CLOSED_REMOTE"); + break; + } + + switch (h2n->swsi->u.h2.h2_state) { + case LWS_H2_STATE_OPEN: + if (h2n->swsi->u.h2.END_STREAM) + lws_h2_state(h2n->swsi, + LWS_H2_STATE_HALF_CLOSED_REMOTE); + break; + case LWS_H2_STATE_HALF_CLOSED_LOCAL: + if (h2n->swsi->u.h2.END_STREAM) + lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED); + break; + } + + if (!lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_PATH) || + !lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_METHOD) || + !lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_SCHEME) || + lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_STATUS) || + lws_hdr_extant(h2n->swsi, WSI_TOKEN_CONNECTION)) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "Pseudoheader checks"); + break; + } + + if (lws_hdr_extant(h2n->swsi, WSI_TOKEN_TE)) { + n = lws_hdr_total_length(h2n->swsi, WSI_TOKEN_TE); + + if (n != 8 || + strncmp(lws_hdr_simple_ptr(h2n->swsi, WSI_TOKEN_TE), + "trailers", n)) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "Illegal transfer-encoding"); + break; + } + } + + p = lws_hdr_simple_ptr(h2n->swsi, WSI_TOKEN_HTTP_COLON_METHOD); + if (!strcmp(p, "POST")) + h2n->swsi->u.hdr.ah->frag_index[WSI_TOKEN_POST_URI] = + h2n->swsi->u.hdr.ah->frag_index[WSI_TOKEN_HTTP_COLON_PATH]; + + wsi->vhost->conn_stats.h2_trans++; + + lwsl_info(" action start...\n"); + n = lws_http_action(h2n->swsi); + lwsl_info(" action result %d " + "(wsi->u.http.rx_content_remain %lld)\n", + n, h2n->swsi->u.http.rx_content_remain); + + /* + * Commonly we only managed to start a larger transfer that will + * complete asynchronously. In those cases we will hear about + * END_STREAM going out in the POLLOUT handler. + */ + if (n || h2n->swsi->u.h2.send_END_STREAM) { + lws_close_free_wsi(h2n->swsi, 0); + h2n->swsi = NULL; + break; + } + break; + + case LWS_H2_FRAME_TYPE_DATA: + if (!h2n->swsi) + break; + + if (lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) && + h2n->swsi->u.h2.END_STREAM && + h2n->swsi->u.http.rx_content_length && + h2n->swsi->u.http.rx_content_remain) { + lws_h2_rst_stream(h2n->swsi, H2_ERR_PROTOCOL_ERROR, + "Not enough rx content"); + break; + } + + if (h2n->swsi->u.h2.END_STREAM && + h2n->swsi->u.h2.h2_state == LWS_H2_STATE_OPEN) + lws_h2_state(h2n->swsi, LWS_H2_STATE_HALF_CLOSED_REMOTE); + + if (h2n->swsi->u.h2.END_STREAM && + h2n->swsi->u.h2.h2_state == LWS_H2_STATE_HALF_CLOSED_LOCAL) + lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED); + break; + + case LWS_H2_FRAME_TYPE_PING: + if (h2n->flags & LWS_H2_FLAG_SETTINGS_ACK) { // ack + } else {/* they're sending us a ping request */ + lwsl_info("rx ping, preparing pong\n"); + pps = lws_h2_new_pps(LWS_H2_PPS_PONG); + if (!pps) + return 1; + memcpy(pps->u.ping.ping_payload, h2n->ping_payload, 8); + lws_pps_schedule(wsi, pps); + } + + break; + + case LWS_H2_FRAME_TYPE_WINDOW_UPDATE: + h2n->hpack_e_dep &= ~(1 << 31); + lwsl_info("WINDOW_UPDATE: sid %d %u (0x%x)\n", h2n->sid, + h2n->hpack_e_dep, h2n->hpack_e_dep); + + if (h2n->sid) + eff_wsi = h2n->swsi; + + if (!eff_wsi) { + if (h2n->sid > h2n->highest_sid_opened) + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "alien sid"); + break; /* ignore */ + } + + if ((uint64_t)eff_wsi->u.h2.tx_cr + (uint64_t)h2n->hpack_e_dep > + (uint64_t)0x7fffffff) { + if (h2n->sid) + lws_h2_rst_stream(h2n->swsi, + H2_ERR_FLOW_CONTROL_ERROR, + "Flow control exceeded max"); + else + lws_h2_goaway(wsi, H2_ERR_FLOW_CONTROL_ERROR, + "Flow control exceeded max"); + break; + } + + if (!h2n->hpack_e_dep) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "Zero length window update"); + break; + } + n = eff_wsi->u.h2.tx_cr; + eff_wsi->u.h2.tx_cr += h2n->hpack_e_dep; + + if (n <= 0 && eff_wsi->u.h2.tx_cr <= 0) + /* it helps, but won't change sendability for anyone */ + break; + + /* + * It did change sendability... for us and any children waiting + * on us... reassess blockage for all children first + */ + lws_start_foreach_ll(struct lws *, w, wsi->u.h2.child_list) { + lws_callback_on_writable(w); + } lws_end_foreach_ll(w, u.h2.sibling_list); + + if (eff_wsi->u.h2.skint && lws_h2_tx_cr_get(eff_wsi)) { + lwsl_info("%s: %p: skint\n", __func__, wsi); + eff_wsi->u.h2.skint = 0; + lws_callback_on_writable(eff_wsi); + } + break; + + case LWS_H2_FRAME_TYPE_GOAWAY: + lwsl_info("GOAWAY: last sid %d, error 0x%08X, string '%s'\n", + h2n->goaway_last_sid, h2n->goaway_err, + h2n->goaway_str); + wsi->u.h2.GOING_AWAY = 1; + + return 1; + + case LWS_H2_FRAME_TYPE_COUNT: /* IGNORING FRAME */ + break; + } + + return 0; +} + +int +lws_h2_parser(struct lws *wsi, unsigned char c) +{ + struct lws_h2_netconn *h2n = wsi->u.h2.h2n; + struct lws_h2_protocol_send *pps; + int n; + + if (!h2n) + return 1; + + switch (wsi->state) { + case LWSS_HTTP2_AWAIT_CLIENT_PREFACE: + if (preface[h2n->count++] != c) + return 1; + + if (preface[h2n->count]) + break; + + lwsl_info("http2: %p: established\n", wsi); + wsi->state = LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS; + h2n->count = 0; + wsi->u.h2.tx_cr = 65535; + + /* + * we must send a settings frame -- empty one is OK... + * that must be the first thing sent by server + * and the peer must send a SETTINGS with ACK flag... + */ + pps = lws_h2_new_pps(LWS_H2_PPS_MY_SETTINGS); + if (!pps) + return 1; + lws_pps_schedule(wsi, pps); + break; + + case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS: + case LWSS_HTTP2_ESTABLISHED: + if (h2n->frame_state != LWS_H2_FRAME_HEADER_LENGTH) + goto try_frame_start; + + /* + * post-header, preamble / payload / padding part + */ + h2n->count++; + + if (h2n->flags & LWS_H2_FLAG_PADDED && !h2n->pad_length) { + /* + * Get the padding count... actual padding is + * at the end of the frame. + */ + h2n->padding = c; + h2n->pad_length = 1; + h2n->preamble++; + + if (h2n->padding > h2n->length - 1) + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "execssive padding"); + break; /* we consumed this */ + } + + if (h2n->flags & LWS_H2_FLAG_PRIORITY && + !h2n->collected_priority) { + /* going to be 5 preamble bytes */ + + lwsl_debug("PRIORITY FLAG: 0x%x\n", c); + + if (h2n->preamble++ - h2n->pad_length < 4) { + h2n->dep = ((h2n->dep) << 8) | c; + break; /* we consumed this */ + } + h2n->weight_temp = c; + h2n->collected_priority = 1; + lwsl_debug("PRI FL: dep 0x%x, weight 0x%02X\n", + h2n->dep, h2n->weight_temp); + break; /* we consumed this */ + } + if (h2n->padding && h2n->count > (h2n->length - h2n->padding)) { + if (c) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "nonzero padding"); + break; + } + goto frame_end; + } + + /* applies to wsi->u.h2.swsi which may be wsi */ + switch(h2n->type) { + + case LWS_H2_FRAME_TYPE_SETTINGS: + n = (h2n->count - 1 - h2n->preamble) % + LWS_H2_SETTINGS_LEN; + h2n->one_setting[n] = c; + if (n != LWS_H2_SETTINGS_LEN - 1) + break; + lws_h2_settings(wsi, &h2n->set, h2n->one_setting, + LWS_H2_SETTINGS_LEN); + break; + + case LWS_H2_FRAME_TYPE_CONTINUATION: + case LWS_H2_FRAME_TYPE_HEADERS: + if (!h2n->swsi) + break; + if (lws_hpack_interpret(h2n->swsi, c)) { + lwsl_info("%s: hpack failed\n", __func__); + return 1; + } + break; + + case LWS_H2_FRAME_TYPE_GOAWAY: + switch (h2n->inside++) { + case 0: + case 1: + case 2: + case 3: + h2n->goaway_last_sid <<= 8; + h2n->goaway_last_sid |= c; + h2n->goaway_str[0] = '\0'; + break; + + case 4: + case 5: + case 6: + case 7: + h2n->goaway_err <<= 8; + h2n->goaway_err |= c; + break; + + default: + if (h2n->inside - 9 < + sizeof(h2n->goaway_str) - 1) + h2n->goaway_str[h2n->inside - 9] = c; + h2n->goaway_str[sizeof(h2n->goaway_str) - 1] = '\0'; + break; + } + break; + + case LWS_H2_FRAME_TYPE_DATA: + //lwsl_notice("incoming LWS_H2_FRAME_TYPE_DATA content\n"); + if (!h2n->swsi) { + //lwsl_notice("data sid %d has no swsi\n", h2n->sid); + break; + } + + h2n->swsi->state = LWSS_HTTP_BODY; + h2n->inside++; + if (lws_hdr_total_length(h2n->swsi, + WSI_TOKEN_HTTP_CONTENT_LENGTH) && + h2n->swsi->u.http.rx_content_length && + h2n->swsi->u.http.rx_content_remain == 1 && /* last */ + h2n->inside < h2n->length) { /* unread data in frame */ + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "More rx than content_length told"); + break; + } + + n = lws_read(h2n->swsi, &c, 1); + if (n < 0) { + // lws_h2_rst_stream(wsi, LWS_H2_PPS_RST_STREAM, + // "post body done"); + break; + } + break; + + case LWS_H2_FRAME_TYPE_PRIORITY: + if (h2n->count <= 4) { + h2n->dep <<= 8; + h2n->dep |= c; + } else { + h2n->weight_temp = c; + lwsl_info("PRIORITY: dep 0x%x, weight 0x%02X\n", + h2n->dep, h2n->weight_temp); + + if ((h2n->dep & ~(1 << 31)) == h2n->sid) { + lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, + "cant depend on own sid"); + break; + } + } + break; + + case LWS_H2_FRAME_TYPE_RST_STREAM: + break; + + case LWS_H2_FRAME_TYPE_PUSH_PROMISE: + break; + + case LWS_H2_FRAME_TYPE_PING: + if (h2n->flags & LWS_H2_FLAG_SETTINGS_ACK) { // ack + } else { /* they're sending us a ping request */ + if (h2n->count > 8) + return 1; + h2n->ping_payload[h2n->count - 1] = c; + } + break; + + case LWS_H2_FRAME_TYPE_WINDOW_UPDATE: + h2n->hpack_e_dep <<= 8; + h2n->hpack_e_dep |= c; + break; + + case LWS_H2_FRAME_TYPE_COUNT: /* IGNORING FRAME */ + break; + + default: + lwsl_notice("%s: unhandled frame type %d\n", + __func__, h2n->type); + + return 1; + } + +frame_end: + if (h2n->count != h2n->length) + break; + + /* + * end of frame just happened + */ + if (lws_h2_parse_end_of_frame(wsi)) + return 1; + break; + +try_frame_start: + if (h2n->frame_state <= 8) { + + switch (h2n->frame_state++) { + case 0: + h2n->pad_length = 0; + h2n->collected_priority = 0; + h2n->padding = 0; + h2n->preamble = 0; + h2n->length = c; + h2n->inside = 0; + break; + case 1: + case 2: + h2n->length <<= 8; + h2n->length |= c; + break; + case 3: + h2n->type = c; + break; + case 4: + h2n->flags = c; + break; + + case 5: + case 6: + case 7: + case 8: + h2n->sid <<= 8; + h2n->sid |= c; + break; + } + } + if (h2n->frame_state == LWS_H2_FRAME_HEADER_LENGTH) + if (lws_h2_parse_frame_header(wsi)) + return 1; + break; + } + + return 0; +} + diff -Nru libwebsockets-4.0.20/lib/http2/huftable.h libwebsockets-2.4.2/lib/http2/huftable.h --- libwebsockets-4.0.20/lib/http2/huftable.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/http2/huftable.h 2018-03-08 10:28:37.000000000 +0000 @@ -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 diff -Nru libwebsockets-4.0.20/lib/http2/minihuf.c libwebsockets-2.4.2/lib/http2/minihuf.c --- libwebsockets-4.0.20/lib/http2/minihuf.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/http2/minihuf.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,518 @@ +/* + * minilex.c + * + * High efficiency lexical state parser + * + * Copyright (C)2011-2014 Andy Green + * + * Licensed under LGPL2 + * + * Usage: gcc minihuf.c -o minihuf && ./minihuf > huftable.h + * + * Run it twice to test parsing on the generated table on stderr + */ + +#include +#include +#include + +#define 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 q; + int pos = 0; + int biggest = 0; + int fails = 0; + + m = 0; + while (m < ARRAY_SIZE(state)) { + for (j = 0; j < PARALLEL; j++) { + state[m].state[j] = 0xffff; + state[m].terminal = 0; + } + m++; + } + + while (n < 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; + q = 0; + for (n = 0; n < next; n++) { + q = pos; + 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 < 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; +} diff -Nru libwebsockets-4.0.20/lib/http2/ssl-http2.c libwebsockets-2.4.2/lib/http2/ssl-http2.c --- libwebsockets-4.0.20/lib/http2/ssl-http2.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/http2/ssl-http2.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,157 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Some or all of this file is based on code from nghttp2, which has the + * following license. Since it's more liberal than lws license, you're also + * at liberty to get the original code from + * https://github.com/tatsuhiro-t/nghttp2 under his liberal terms alone. + * + * nghttp2 - HTTP/2.0 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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-libwebsockets.h" + +#if !defined(LWS_NO_SERVER) +#if defined(LWS_OPENSSL_SUPPORT) + +#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \ + OPENSSL_VERSION_NUMBER >= 0x10002000L) + +struct alpn_ctx { + unsigned char *data; + unsigned short len; +}; + + +static int +alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) +{ +#if !defined(LWS_WITH_MBEDTLS) + struct alpn_ctx *alpn_ctx = arg; + + if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_ctx->data, + alpn_ctx->len, in, inlen) != + OPENSSL_NPN_NEGOTIATED) + return SSL_TLSEXT_ERR_NOACK; +#endif + return SSL_TLSEXT_ERR_OK; +} +#endif + +LWS_VISIBLE void +lws_context_init_http2_ssl(struct lws_vhost *vhost) +{ +#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \ + OPENSSL_VERSION_NUMBER >= 0x10002000L) + static struct alpn_ctx protos = { (unsigned char *)"\x02h2" + "\x08http/1.1", 6 + 9 }; + + SSL_CTX_set_alpn_select_cb(vhost->ssl_ctx, alpn_cb, &protos); + lwsl_notice(" HTTP2 / ALPN enabled\n"); +#else + lwsl_notice( + " HTTP2 / ALPN configured but not supported by OpenSSL 0x%lx\n", + OPENSSL_VERSION_NUMBER); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L +} + +int lws_h2_configure_if_upgraded(struct lws *wsi) +{ +#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \ + OPENSSL_VERSION_NUMBER >= 0x10002000L) + struct allocated_headers *ah; + const unsigned char *name = NULL; + char cstr[10]; + unsigned len; + + if (!wsi->ssl) + return 0; + + SSL_get0_alpn_selected(wsi->ssl, &name, &len); + if (!len) { + lwsl_info("no ALPN upgrade\n"); + return 0; + } + + if (len > sizeof(cstr) - 1) + len = sizeof(cstr) - 1; + + memcpy(cstr, name, len); + cstr[len] = '\0'; + + lwsl_info("negotiated '%s' using ALPN\n", cstr); + wsi->use_ssl = 1; + if (strncmp((char *)name, "http/1.1", 8) == 0) + return 0; + + /* http2 */ + + wsi->upgraded_to_http2 = 1; + wsi->vhost->conn_stats.h2_alpn++; + + /* adopt the header info */ + + ah = wsi->u.hdr.ah; + + lws_union_transition(wsi, LWSCM_HTTP2_SERVING); + wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE; + + /* http2 union member has http union struct at start */ + wsi->u.http.ah = ah; + + wsi->u.h2.h2n = lws_zalloc(sizeof(*wsi->u.h2.h2n), "h2n"); + if (!wsi->u.h2.h2n) + return 1; + + lws_h2_init(wsi); + + /* HTTP2 union */ + + lws_hpack_dynamic_size(wsi, wsi->u.h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]); + wsi->u.h2.tx_cr = 65535; + + lwsl_info("%s: wsi %p: configured for h2\n", __func__, wsi); +#endif + return 0; +} +#endif +#endif diff -Nru libwebsockets-4.0.20/lib/jose/jwe/enc/aescbc.c libwebsockets-2.4.2/lib/jose/jwe/enc/aescbc.c --- libwebsockets-4.0.20/lib/jose/jwe/enc/aescbc.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jwe/enc/aescbc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,271 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-jose-jwe.h" - -int -lws_jwe_encrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *cek, - uint8_t *aad, int aad_len) -{ - int n, hlen = lws_genhmac_size(jwe->jose.enc_alg->hmac_type); - uint8_t digest[LWS_GENHASH_LARGEST]; - struct lws_gencrypto_keyelem el; - struct lws_genhmac_ctx hmacctx; - struct lws_genaes_ctx aesctx; - size_t paddedlen; - uint8_t al[8]; - - /* Caller must have prepared space for the results */ - - if (jwe->jws.map.len[LJWE_ATAG] != (unsigned int)hlen / 2) { - lwsl_notice("%s: expected tag len %d, got %d\n", __func__, - hlen / 2, jwe->jws.map.len[LJWE_ATAG]); - return -1; - } - - if (jwe->jws.map.len[LJWE_IV] != 16) { - lwsl_notice("expected iv len %d, got %d\n", 16, - jwe->jws.map.len[LJWE_IV]); - return -1; - } - - /* first create the authentication hmac */ - - /* JWA Section 5.2.2.1 - * - * 1. The secondary keys MAC_KEY and ENC_KEY are generated from the - * input key K as follows. Each of these two keys is an octet - * string. - * - * MAC_KEY consists of the initial MAC_KEY_LEN octets of K, in - * order. - * ENC_KEY consists of the final ENC_KEY_LEN octets of K, in - * order. - */ - - /* - * 2. The IV used is a 128-bit value generated randomly or - * pseudorandomly for use in the cipher. - */ - lws_get_random(jwe->jws.context, (void *)jwe->jws.map.buf[LJWE_IV], 16); - - /* - * 3. The plaintext is CBC encrypted using PKCS #7 padding using - * ENC_KEY as the key and the IV. We denote the ciphertext output - * from this step as E. - */ - - /* second half is the AES ENC_KEY */ - el.buf = cek + (hlen / 2); - el.len = hlen / 2; - - if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_CBC, &el, - LWS_GAESP_WITH_PADDING, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - - return -1; - } - - /* - * the plaintext gets delivered to us in LJWE_CTXT, this replaces the - * plaintext there with the ciphertext, which will be larger by some - * padding bytes - */ - n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT], - jwe->jws.map.len[LJWE_CTXT], - (uint8_t *)jwe->jws.map.buf[LJWE_CTXT], - (uint8_t *)jwe->jws.map.buf[LJWE_IV], - NULL, NULL, LWS_AES_CBC_BLOCKLEN); - paddedlen = lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN, - jwe->jws.map.len[LJWE_CTXT]); - jwe->jws.map.len[LJWE_CTXT] = paddedlen; - lws_genaes_destroy(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT] + - paddedlen - LWS_AES_CBC_BLOCKLEN, LWS_AES_CBC_BLOCKLEN); - if (n) { - lwsl_err("%s: lws_genaes_crypt failed\n", __func__); - return -1; - } - - /* - * 4. The octet string AL is equal to the number of bits in the - * Additional Authenticated Data A expressed as a 64-bit unsigned - * big-endian integer. - */ - lws_jwe_be64(aad_len * 8, al); - - /* first half of the CEK is the MAC key */ - if (lws_genhmac_init(&hmacctx, jwe->jose.enc_alg->hmac_type, - cek, hlen / 2)) - return -1; - - /* - * 5. A message Authentication Tag T is computed by applying HMAC - * [RFC2104] to the following data, in order: - * - * - the Additional Authenticated Data A, - * - the Initialization Vector IV, - * - the ciphertext E computed in the previous step, and - * - the octet string AL defined above. - * - * The string MAC_KEY is used as the MAC key. We denote the output - * of the MAC computed in this step as M. The first T_LEN octets of - * M are used as T. - */ - - if (lws_genhmac_update(&hmacctx, aad, aad_len) || - lws_genhmac_update(&hmacctx, jwe->jws.map.buf[LJWE_IV], - LWS_JWE_AES_IV_BYTES) || - /* since we encrypted it, this is the ciphertext */ - lws_genhmac_update(&hmacctx, - (uint8_t *)jwe->jws.map.buf[LJWE_CTXT], - jwe->jws.map.len[LJWE_CTXT]) || - lws_genhmac_update(&hmacctx, al, 8)) { - lwsl_err("%s: hmac computation failed\n", __func__); - lws_genhmac_destroy(&hmacctx, NULL); - return -1; - } - - if (lws_genhmac_destroy(&hmacctx, digest)) { - lwsl_err("%s: problem destroying hmac\n", __func__); - return -1; - } - - /* create tag */ - memcpy((void *)jwe->jws.map.buf[LJWE_ATAG], digest, hlen / 2); - - return jwe->jws.map.len[LJWE_CTXT]; -} - -int -lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *enc_cek, - uint8_t *aad, int aad_len) -{ - int n, hlen = lws_genhmac_size(jwe->jose.enc_alg->hmac_type); - uint8_t digest[LWS_GENHASH_LARGEST]; - struct lws_gencrypto_keyelem el; - struct lws_genhmac_ctx hmacctx; - struct lws_genaes_ctx aesctx; - uint8_t al[8]; - - /* Some sanity checks on what came in */ - - if (jwe->jws.map.len[LJWE_ATAG] != (unsigned int)hlen / 2) { - lwsl_notice("%s: expected tag len %d, got %d\n", __func__, - hlen / 2, jwe->jws.map.len[LJWE_ATAG]); - return -1; - } - - if (jwe->jws.map.len[LJWE_IV] != 16) { - lwsl_notice("expected iv len %d, got %d\n", 16, - jwe->jws.map.len[LJWE_IV]); - return -1; - } - - /* Prepare to check authentication - * - * AAD is the b64 JOSE header. - * - * The octet string AL, which is the number of bits in AAD expressed as - * a big-endian 64-bit unsigned integer is: - * - * [0, 0, 0, 0, 0, 0, 1, 152] - * - * Concatenate the AAD, the Initialization Vector, the ciphertext, and - * the AL value. - * - */ - - lws_jwe_be64(aad_len * 8, al); - - /* first half of enc_cek is the MAC key */ - if (lws_genhmac_init(&hmacctx, jwe->jose.enc_alg->hmac_type, enc_cek, - hlen / 2)) { - lwsl_err("%s: lws_genhmac_init fail\n", __func__); - return -1; - } - - if (lws_genhmac_update(&hmacctx, aad, aad_len) || - lws_genhmac_update(&hmacctx, (uint8_t *)jwe->jws.map.buf[LJWE_IV], - jwe->jws.map.len[LJWE_IV]) || - lws_genhmac_update(&hmacctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT], - jwe->jws.map.len[LJWE_CTXT]) || - lws_genhmac_update(&hmacctx, al, 8)) { - lwsl_err("%s: hmac computation failed\n", __func__); - lws_genhmac_destroy(&hmacctx, NULL); - return -1; - } - - if (lws_genhmac_destroy(&hmacctx, digest)) { - lwsl_err("%s: problem destroying hmac\n", __func__); - return -1; - } - - /* first half of digest is the auth tag */ - - if (lws_timingsafe_bcmp(digest, jwe->jws.map.buf[LJWE_ATAG], hlen / 2)) { - lwsl_err("%s: auth failed: hmac tag (%d) != ATAG (%d)\n", - __func__, hlen / 2, jwe->jws.map.len[LJWE_ATAG]); - lwsl_hexdump_notice(jwe->jws.map.buf[LJWE_ATAG], hlen / 2); - lwsl_hexdump_notice(digest, hlen / 2); - return -1; - } - - /* second half of enc cek is the CEK KEY */ - el.buf = enc_cek + (hlen / 2); - el.len = hlen / 2; - - if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_CBC, - &el, LWS_GAESP_NO_PADDING, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - - return -1; - } - - n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT], - jwe->jws.map.len[LJWE_CTXT], - (uint8_t *)jwe->jws.map.buf[LJWE_CTXT], - (uint8_t *)jwe->jws.map.buf[LJWE_IV], NULL, NULL, 16); - - /* Strip the PKCS #7 padding */ - - if (jwe->jws.map.len[LJWE_CTXT] < LWS_AES_CBC_BLOCKLEN || - jwe->jws.map.len[LJWE_CTXT] <= (unsigned char)jwe->jws.map.buf[LJWE_CTXT] - [jwe->jws.map.len[LJWE_CTXT] - 1]) { - lwsl_err("%s: invalid padded ciphertext length: %d. Corrupt data?\n", - __func__, jwe->jws.map.len[LJWE_CTXT]); - return -1; - } - jwe->jws.map.len[LJWE_CTXT] -= jwe->jws.map.buf[LJWE_CTXT][ - jwe->jws.map.len[LJWE_CTXT] - 1]; - - n |= lws_genaes_destroy(&aesctx, NULL, 0); - if (n) { - lwsl_err("%s: lws_genaes_crypt failed\n", __func__); - return -1; - } - - return jwe->jws.map.len[LJWE_CTXT]; -} - diff -Nru libwebsockets-4.0.20/lib/jose/jwe/enc/aesgcm.c libwebsockets-2.4.2/lib/jose/jwe/enc/aesgcm.c --- libwebsockets-4.0.20/lib/jose/jwe/enc/aesgcm.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jwe/enc/aesgcm.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,173 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-jose-jwe.h" - -/* - * NOTICE this is AESGCM content encryption, it's not AES GCM key wrapping - * - * - * This section defines the specifics of performing authenticated - * encryption with AES in Galois/Counter Mode (GCM) ([AES] and - * [NIST.800-38D]). - * - * The CEK is used as the encryption key. - * - * Use of an IV of size 96 bits is REQUIRED with this algorithm. - * - * The requested size of the Authentication Tag output MUST be 128 bits, - * regardless of the key size. - * - * For decrypt: decrypt the KEK, then decrypt the payload - * - * For encrypt: encrypt the payload, then encrypt the KEK - */ - -/* - * encrypting... enc_cek is unencrypted - */ - -int -lws_jwe_encrypt_gcm(struct lws_jwe *jwe, - uint8_t *enc_cek, uint8_t *aad, int aad_len) -{ - struct lws_gencrypto_keyelem el; - struct lws_genaes_ctx aesctx; - size_t ivs = LWS_AESGCM_IV; - int n; - - /* Some sanity checks on what came in */ - - /* MUST be 128-bit for all sizes */ - if (jwe->jws.map.len[LJWE_ATAG] != LWS_AESGCM_TAG) { - lwsl_notice("%s: AESGCM tag size must be 128b, got %d\n", - __func__, jwe->jws.map.len[LJWE_ATAG]); - return -1; - } - - if (jwe->jws.map.len[LJWE_IV] != LWS_AESGCM_IV) { /* MUST be 96-bit */ - lwsl_notice("%s: AESGCM IV must be 128b, got %d\n", __func__, - jwe->jws.map.len[LJWE_IV]); - return -1; - } - - /* EKEY is directly the CEK KEY */ - el.buf = enc_cek; - el.len = jwe->jose.enc_alg->keybits_fixed / 8; - - if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_GCM, - &el, LWS_GAESP_NO_PADDING, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - - return -1; - } - - /* aad */ - - n = lws_genaes_crypt(&aesctx, aad, aad_len, NULL, - (uint8_t *)jwe->jws.map.buf[LJWE_IV], - (uint8_t *)jwe->jws.map.buf[LJWE_ATAG], &ivs, - LWS_AESGCM_TAG); - if (n) { - lwsl_err("%s: lws_genaes_crypt aad failed\n", __func__); - return -1; - } - - /* payload */ - n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT], - jwe->jws.map.len[LJWE_CTXT], - (uint8_t *)jwe->jws.map.buf[LJWE_CTXT], - (uint8_t *)jwe->jws.map.buf[LJWE_IV], - NULL, &ivs, - LWS_AESGCM_TAG); - - n |= lws_genaes_destroy(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_ATAG], - LWS_AESGCM_TAG); - if (n) { - lwsl_err("%s: lws_genaes_crypt failed\n", __func__); - return -1; - } - - return jwe->jws.map.len[LJWE_CTXT]; -} - -int -lws_jwe_auth_and_decrypt_gcm(struct lws_jwe *jwe, - uint8_t *enc_cek, uint8_t *aad, int aad_len) -{ - struct lws_gencrypto_keyelem el; - struct lws_genaes_ctx aesctx; - size_t ivs = LWS_AESGCM_IV; - uint8_t tag[LWS_AESGCM_TAG]; - int n; - - /* Some sanity checks on what came in */ - - /* Tag MUST be 128-bit for all sizes */ - if (jwe->jws.map.len[LJWE_ATAG] != LWS_AESGCM_TAG) { - lwsl_notice("%s: AESGCM tag size must be 128b, got %d\n", - __func__, jwe->jws.map.len[LJWE_ATAG]); - return -1; - } - - if (jwe->jws.map.len[LJWE_IV] != LWS_AESGCM_IV) { /* MUST be 96-bit */ - lwsl_notice("%s: AESGCM IV must be 128b, got %d\n", __func__, - jwe->jws.map.len[LJWE_IV]); - return -1; - } - - /* EKEY is directly the CEK KEY */ - el.buf = enc_cek; - el.len = jwe->jose.enc_alg->keybits_fixed / 8; - - if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_GCM, - &el, LWS_GAESP_NO_PADDING, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - - return -1; - } - - n = lws_genaes_crypt(&aesctx, aad, aad_len, - NULL, - (uint8_t *)jwe->jws.map.buf[LJWE_IV], - (uint8_t *)jwe->jws.map.buf[LJWE_ATAG], &ivs, 16); - if (n) { - lwsl_err("%s: lws_genaes_crypt aad failed\n", __func__); - return -1; - } - n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT], - jwe->jws.map.len[LJWE_CTXT], - (uint8_t *)jwe->jws.map.buf[LJWE_CTXT], - (uint8_t *)jwe->jws.map.buf[LJWE_IV], - (uint8_t *)jwe->jws.map.buf[LJWE_ATAG], &ivs, 16); - - n |= lws_genaes_destroy(&aesctx, tag, sizeof(tag)); - if (n) { - lwsl_err("%s: lws_genaes_crypt failed\n", __func__); - return -1; - } - - return jwe->jws.map.len[LJWE_CTXT]; -} diff -Nru libwebsockets-4.0.20/lib/jose/jwe/enc/aeskw.c libwebsockets-2.4.2/lib/jose/jwe/enc/aeskw.c --- libwebsockets-4.0.20/lib/jose/jwe/enc/aeskw.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jwe/enc/aeskw.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,177 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-jose-jwe.h" - -/* - * RFC3394 Key Wrap uses a 128-bit key, and bloats what it is wrapping by - * one 8-byte block. So, if you had a 32 byte plaintext CEK to wrap, after - * wrapping it becomes a 40 byte wrapped, enciphered, key. - * - * The CEK comes in from and goes out in LJWE_EKEY. So LJWE_EKEY length - * increases by 8 from calling this. - */ - -int -lws_jwe_encrypt_aeskw_cbc_hs(struct lws_jwe *jwe, char *temp, int *temp_len) -{ - struct lws_genaes_ctx aesctx; - /* we are wrapping a key, so size for the worst case after wrap */ - uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES + - LWS_JWE_RFC3394_OVERHEAD_BYTES]; - int n, m, hlen = lws_genhmac_size(jwe->jose.enc_alg->hmac_type), - ot = *temp_len; - - if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_OCT) { - lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty); - - return -1; - } - - /* create a b64 version of the JOSE header, needed for hashing */ - - if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE, - temp, temp_len, - jwe->jws.map.buf[LJWE_JOSE], - jwe->jws.map.len[LJWE_JOSE])) - return -1; - - /* Allocate temp space for ATAG and IV */ - - if (lws_jws_alloc_element(&jwe->jws.map, LJWE_ATAG, temp + (ot - *temp_len), - temp_len, hlen / 2, 0)) - return -1; - - if (lws_jws_alloc_element(&jwe->jws.map, LJWE_IV, temp + (ot - *temp_len), - temp_len, LWS_JWE_AES_IV_BYTES, 0)) - return -1; - - /* 1) Encrypt the payload... */ - - /* the CEK is 256-bit in the example encrypted with a 128-bit key */ - - n = lws_jwe_encrypt_cbc_hs(jwe, (uint8_t *)jwe->jws.map.buf[LJWE_EKEY], - (uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE], - jwe->jws.map_b64.len[LJWE_JOSE]); - if (n < 0) { - lwsl_err("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__); - return -1; - } - - /* 2) Encrypt the JWE Encrypted Key: RFC3394 Key Wrap uses 64 bit blocks - * and 128-bit input key*/ - - if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_KW, - jwe->jws.jwk->e, 1, NULL)) { - - lwsl_notice("%s: lws_genaes_create\n", __func__); - return -1; - } - - /* tag size is determined by enc cipher key length */ - - n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_EKEY], - jwe->jws.map.len[LJWE_EKEY], enc_cek, NULL, NULL, NULL, - lws_gencrypto_bits_to_bytes( - jwe->jose.enc_alg->keybits_fixed)); - m = lws_genaes_destroy(&aesctx, NULL, 0); - if (n < 0) { - lwsl_err("%s: encrypt cek fail\n", __func__); - return -1; - } - if (m < 0) { - lwsl_err("%s: lws_genaes_destroy fail\n", __func__); - return -1; - } - - jwe->jws.map.len[LJWE_EKEY] += LWS_JWE_RFC3394_OVERHEAD_BYTES; - memcpy((uint8_t *)jwe->jws.map.buf[LJWE_EKEY], enc_cek, - jwe->jws.map.len[LJWE_EKEY]); - - return jwe->jws.map.len[LJWE_CTXT]; -} - - -int -lws_jwe_auth_and_decrypt_aeskw_cbc_hs(struct lws_jwe *jwe) -{ - struct lws_genaes_ctx aesctx; - uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES + - LWS_JWE_RFC3394_OVERHEAD_BYTES]; - int n, m; - - if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_OCT) { - lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty); - - return -1; - } - - /* the CEK is 256-bit in the example encrypted with a 128-bit key */ - - if (jwe->jws.map.len[LJWE_EKEY] > sizeof(enc_cek)) - return -1; - - /* 1) Decrypt the JWE Encrypted Key to get the raw MAC / CEK */ - - if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_KW, - jwe->jws.jwk->e, 1, NULL)) { - - lwsl_notice("%s: lws_genaes_create\n", __func__); - return -1; - } - - /* - * Decrypt the CEK into enc_cek - * tag size is determined by enc cipher key length */ - - n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_EKEY], - jwe->jws.map.len[LJWE_EKEY], enc_cek, NULL, NULL, NULL, - lws_gencrypto_bits_to_bytes( - jwe->jose.enc_alg->keybits_fixed)); - m = lws_genaes_destroy(&aesctx, NULL, 0); - if (n < 0) { - lwsl_err("%s: decrypt CEK fail\n", __func__); - return -1; - } - if (m < 0) { - lwsl_err("%s: lws_genaes_destroy fail\n", __func__); - return -1; - } - - /* 2) Decrypt the payload */ - - n = lws_jwe_auth_and_decrypt_cbc_hs(jwe, enc_cek, - (uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE], - jwe->jws.map_b64.len[LJWE_JOSE]); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs failed\n", - __func__); - return -1; - } - - return jwe->jws.map.len[LJWE_CTXT]; -} - - diff -Nru libwebsockets-4.0.20/lib/jose/jwe/jwe.c libwebsockets-2.4.2/lib/jose/jwe/jwe.c --- libwebsockets-4.0.20/lib/jose/jwe/jwe.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jwe/jwe.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,791 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-jose.h" -#include "private-lib-jose-jwe.h" - -/* - * Currently only support flattened or compact (implicitly single signature) - */ - -static const char * const jwe_json[] = { - "protected", - "iv", - "ciphertext", - "tag", - "encrypted_key" -}; - -enum enum_jwe_complete_tokens { - LWS_EJCT_PROTECTED, - LWS_EJCT_IV, - LWS_EJCT_CIPHERTEXT, - LWS_EJCT_TAG, - LWS_EJCT_RECIP_ENC_KEY, -}; - -/* parse a JWS complete or flattened JSON object */ - -struct jwe_cb_args { - struct lws_jws *jws; - - char *temp; - int *temp_len; -}; - -static signed char -lws_jwe_json_cb(struct lejp_ctx *ctx, char reason) -{ - struct jwe_cb_args *args = (struct jwe_cb_args *)ctx->user; - int n, m; - - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - switch (ctx->path_match - 1) { - - /* strings */ - - case LWS_EJCT_PROTECTED: /* base64u: JOSE: must contain 'alg' */ - m = LJWS_JOSE; - goto append_string; - case LWS_EJCT_IV: /* base64u */ - m = LJWE_IV; - goto append_string; - case LWS_EJCT_CIPHERTEXT: /* base64u */ - m = LJWE_CTXT; - goto append_string; - case LWS_EJCT_TAG: /* base64u */ - m = LJWE_ATAG; - goto append_string; - case LWS_EJCT_RECIP_ENC_KEY: /* base64u */ - m = LJWE_EKEY; - goto append_string; - - default: - return -1; - } - - return 0; - -append_string: - - if (*args->temp_len < ctx->npos) { - lwsl_err("%s: out of parsing space\n", __func__); - return -1; - } - - /* - * We keep both b64u and decoded in temp mapped using map / map_b64, - * the jws signature is actually over the b64 content not the plaintext, - * and we can't do it until we see the protected alg. - */ - - if (!args->jws->map_b64.buf[m]) { - args->jws->map_b64.buf[m] = args->temp; - args->jws->map_b64.len[m] = 0; - } - - memcpy(args->temp, ctx->buf, ctx->npos); - args->temp += ctx->npos; - *args->temp_len -= ctx->npos; - args->jws->map_b64.len[m] += ctx->npos; - - if (reason == LEJPCB_VAL_STR_END) { - args->jws->map.buf[m] = args->temp; - - n = lws_b64_decode_string_len( - (const char *)args->jws->map_b64.buf[m], - args->jws->map_b64.len[m], - (char *)args->temp, *args->temp_len); - if (n < 0) { - lwsl_err("%s: b64 decode failed\n", __func__); - return -1; - } - - args->temp += n; - *args->temp_len -= n; - args->jws->map.len[m] = n; - } - - return 0; -} - -int -lws_jwe_json_parse(struct lws_jwe *jwe, const uint8_t *buf, int len, - char *temp, int *temp_len) -{ - struct jwe_cb_args args; - struct lejp_ctx jctx; - int m = 0; - - args.jws = &jwe->jws; - args.temp = temp; - args.temp_len = temp_len; - - lejp_construct(&jctx, lws_jwe_json_cb, &args, jwe_json, - LWS_ARRAY_SIZE(jwe_json)); - - m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, len); - lejp_destruct(&jctx); - if (m < 0) { - lwsl_notice("%s: parse returned %d\n", __func__, m); - return -1; - } - - return 0; -} - -void -lws_jwe_init(struct lws_jwe *jwe, struct lws_context *context) -{ - lws_jose_init(&jwe->jose); - lws_jws_init(&jwe->jws, &jwe->jwk, context); - memset(&jwe->jwk, 0, sizeof(jwe->jwk)); - jwe->recip = 0; - jwe->cek_valid = 0; -} - -void -lws_jwe_destroy(struct lws_jwe *jwe) -{ - lws_jws_destroy(&jwe->jws); - lws_jose_destroy(&jwe->jose); - lws_jwk_destroy(&jwe->jwk); - /* cleanse the CEK we held on to in case of further encryptions of it */ - lws_explicit_bzero(jwe->cek, sizeof(jwe->cek)); - jwe->cek_valid = 0; -} - -static uint8_t * -be32(uint32_t i, uint32_t *p32) -{ - uint8_t *p = (uint8_t *)p32; - - *p++ = (i >> 24) & 0xff; - *p++ = (i >> 16) & 0xff; - *p++ = (i >> 8) & 0xff; - *p++ = i & 0xff; - - return (uint8_t *)p32; -} - -/* - * The key derivation process derives the agreed-upon key from the - * shared secret Z established through the ECDH algorithm, per - * Section 6.2.2.2 of [NIST.800-56A]. - * - * - * Key derivation is performed using the Concat KDF, as defined in - * Section 5.8.1 of [NIST.800-56A], where the Digest Method is SHA-256. - * - * out must be prepared to take at least 32 bytes or the encrypted key size, - * whichever is larger. - */ - -int -lws_jwa_concat_kdf(struct lws_jwe *jwe, int direct, uint8_t *out, - const uint8_t *shared_secret, int sslen) -{ - int hlen = lws_genhash_size(LWS_GENHASH_TYPE_SHA256), aidlen; - struct lws_genhash_ctx hash_ctx; - uint32_t ctr = 1, t; - const char *aid; - - if (!jwe->jose.enc_alg || !jwe->jose.alg) - return -1; - - /* - * Hash - * - * AlgorithmID || PartyUInfo || PartyVInfo - * {|| SuppPubInfo }{|| SuppPrivInfo } - * - * AlgorithmID - * - * The AlgorithmID value is of the form Datalen || Data, where Data - * is a variable-length string of zero or more octets, and Datalen is - * a fixed-length, big-endian 32-bit counter that indicates the - * length (in octets) of Data. In the Direct Key Agreement case, - * Data is set to the octets of the ASCII representation of the "enc" - * Header Parameter value. In the Key Agreement with Key Wrapping - * case, Data is set to the octets of the ASCII representation of the - * "alg" (algorithm) Header Parameter value. - */ - - aid = direct ? jwe->jose.enc_alg->alg : jwe->jose.alg->alg; - aidlen = strlen(aid); - - /* - * PartyUInfo (PartyVInfo is the same deal) - * - * The PartyUInfo value is of the form Datalen || Data, where Data is - * a variable-length string of zero or more octets, and Datalen is a - * fixed-length, big-endian 32-bit counter that indicates the length - * (in octets) of Data. If an "apu" (agreement PartyUInfo) Header - * Parameter is present, Data is set to the result of base64url - * decoding the "apu" value and Datalen is set to the number of - * octets in Data. Otherwise, Datalen is set to 0 and Data is set to - * the empty octet sequence - * - * SuppPubInfo - * - * This is set to the keydatalen represented as a 32-bit big-endian - * integer. - * - * keydatalen - * - * This is set to the number of bits in the desired output key. For - * "ECDH-ES", this is length of the key used by the "enc" algorithm. - * For "ECDH-ES+A128KW", "ECDH-ES+A192KW", and "ECDH-ES+A256KW", this - * is 128, 192, and 256, respectively. - * - * Compute Hash i = H(counter || Z || OtherInfo). - * - * We must iteratively hash over key material that's larger than - * one hash output size (256b for SHA-256) - */ - - while (ctr <= (uint32_t)((jwe->jose.enc_alg->keybits_fixed + (hlen - 1)) / hlen)) { - - /* - * Key derivation is performed using the Concat KDF, as defined - * in Section 5.8.1 of [NIST.800-56A], where the Digest Method - * is SHA-256. - */ - - if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256)) - return -1; - - if (/* counter */ - lws_genhash_update(&hash_ctx, be32(ctr++, &t), 4) || - /* Z */ - lws_genhash_update(&hash_ctx, shared_secret, sslen) || - /* other info */ - lws_genhash_update(&hash_ctx, be32(strlen(aid), &t), 4) || - lws_genhash_update(&hash_ctx, aid, aidlen) || - lws_genhash_update(&hash_ctx, - be32(jwe->jose.e[LJJHI_APU].len, &t), 4) || - lws_genhash_update(&hash_ctx, jwe->jose.e[LJJHI_APU].buf, - jwe->jose.e[LJJHI_APU].len) || - lws_genhash_update(&hash_ctx, - be32(jwe->jose.e[LJJHI_APV].len, &t), 4) || - lws_genhash_update(&hash_ctx, jwe->jose.e[LJJHI_APV].buf, - jwe->jose.e[LJJHI_APV].len) || - lws_genhash_update(&hash_ctx, - be32(jwe->jose.enc_alg->keybits_fixed, &t), - 4) || - lws_genhash_destroy(&hash_ctx, out)) { - lwsl_err("%s: fail\n", __func__); - lws_genhash_destroy(&hash_ctx, NULL); - - return -1; - } - - out += hlen; - } - - return 0; -} - -void -lws_jwe_be64(uint64_t c, uint8_t *p8) -{ - int n; - - for (n = 56; n >= 0; n -= 8) - *p8++ = (uint8_t)((c >> n) & 0xff); -} - -int -lws_jwe_auth_and_decrypt(struct lws_jwe *jwe, char *temp, int *temp_len) -{ - int valid_aescbc_hmac, valid_aesgcm; - char dotstar[96]; - - if (lws_jwe_parse_jose(&jwe->jose, jwe->jws.map.buf[LJWS_JOSE], - jwe->jws.map.len[LJWS_JOSE], - temp, temp_len) < 0) { - lws_strnncpy(dotstar, jwe->jws.map.buf[LJWS_JOSE], - jwe->jws.map.len[LJWS_JOSE], sizeof(dotstar)); - lwsl_err("%s: JOSE parse '%s' failed\n", __func__, dotstar); - return -1; - } - - if (!jwe->jose.alg) { - lws_strnncpy(dotstar, jwe->jws.map.buf[LJWS_JOSE], - jwe->jws.map.len[LJWS_JOSE], sizeof(dotstar)); - lwsl_err("%s: no jose.alg: %s\n", __func__, dotstar); - - return -1; - } - - valid_aescbc_hmac = jwe->jose.enc_alg && - jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_CBC && - (jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA256 || - jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA384 || - jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA512); - - valid_aesgcm = jwe->jose.enc_alg && - jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_GCM; - - if ((jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5 || - jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP)) { - /* RSA + AESCBC */ - if (valid_aescbc_hmac) - return lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(jwe); - /* RSA + AESGCM */ - if (valid_aesgcm) - return lws_jwe_auth_and_decrypt_rsa_aes_gcm(jwe); - } - - /* AESKW */ - - if (jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_AES_ECB && - valid_aescbc_hmac) - return lws_jwe_auth_and_decrypt_aeskw_cbc_hs(jwe); - - /* ECDH-ES + AESKW */ - - if (jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_ECDHES && - valid_aescbc_hmac) - return lws_jwe_auth_and_decrypt_ecdh_cbc_hs(jwe, - temp, temp_len); - - lwsl_err("%s: unknown cipher alg combo %s / %s\n", __func__, - jwe->jose.alg->alg, jwe->jose.enc_alg ? - jwe->jose.enc_alg->alg : "NULL"); - - return -1; -} -int -lws_jwe_encrypt(struct lws_jwe *jwe, char *temp, int *temp_len) -{ - int valid_aescbc_hmac, valid_aesgcm, ot = *temp_len, ret = -1; - - if (jwe->jose.recipients >= (int)LWS_ARRAY_SIZE(jwe->jose.recipient)) { - lwsl_err("%s: max recipients reached\n", __func__); - - return -1; - } - - valid_aesgcm = jwe->jose.enc_alg && - jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_GCM; - - if (lws_jwe_parse_jose(&jwe->jose, jwe->jws.map.buf[LJWS_JOSE], - jwe->jws.map.len[LJWS_JOSE], temp, temp_len) < 0) { - lwsl_err("%s: JOSE parse failed\n", __func__); - goto bail; - } - - temp += ot - *temp_len; - - valid_aescbc_hmac = jwe->jose.enc_alg && - jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_CBC && - (jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA256 || - jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA384 || - jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA512); - - if ((jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5 || - jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP)) { - /* RSA + AESCBC */ - if (valid_aescbc_hmac) { - ret = lws_jwe_encrypt_rsa_aes_cbc_hs(jwe, temp, temp_len); - goto bail; - } - /* RSA + AESGCM */ - if (valid_aesgcm) { - ret = lws_jwe_encrypt_rsa_aes_gcm(jwe, temp, temp_len); - goto bail; - } - } - - /* AESKW */ - - if (jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_AES_ECB && - valid_aescbc_hmac) { - ret = lws_jwe_encrypt_aeskw_cbc_hs(jwe, temp, temp_len); - goto bail; - } - - /* ECDH-ES + AESKW */ - - if (jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_ECDHES && - valid_aescbc_hmac) { - ret = lws_jwe_encrypt_ecdh_cbc_hs(jwe, temp, temp_len); - goto bail; - } - - lwsl_err("%s: unknown cipher alg combo %s / %s\n", __func__, - jwe->jose.alg->alg, jwe->jose.enc_alg ? - jwe->jose.enc_alg->alg : "NULL"); - -bail: - if (ret) - memset(&jwe->jose.recipient[jwe->jose.recipients], 0, - sizeof(jwe->jose.recipient[0])); - else - jwe->jose.recipients++; - - return ret; -} - -/* - * JWE Compact Serialization consists of - * - * BASE64URL(UTF8(JWE Protected Header)) || '.' || - * BASE64URL(JWE Encrypted Key) || '.' || - * BASE64URL(JWE Initialization Vector) || '.' || - * BASE64URL(JWE Ciphertext) || '.' || - * BASE64URL(JWE Authentication Tag) - * - * - * In the JWE Compact Serialization, no JWE Shared Unprotected Header or - * JWE Per-Recipient Unprotected Header are used. In this case, the - * JOSE Header and the JWE Protected Header are the same. - * - * Therefore: - * - * - Everything needed in the header part must go in the protected header - * (it's the only part emitted). We expect the caller did this. - * - * - You can't emit Compact representation if there are multiple recipients - */ - -int -lws_jwe_render_compact(struct lws_jwe *jwe, char *out, size_t out_len) -{ - size_t orig = out_len; - int n; - - if (jwe->jose.recipients > 1) { - lwsl_notice("%s: can't issue compact representation for" - " multiple recipients (%d)\n", __func__, - jwe->jose.recipients); - - return -1; - } - - n = lws_jws_base64_enc(jwe->jws.map.buf[LJWS_JOSE], - jwe->jws.map.len[LJWS_JOSE], out, out_len); - if (n < 0 || (int)out_len == n) { - lwsl_info("%s: unable to encode JOSE\n", __func__); - return -1; - } - - out += n; - *out++ = '.'; - out_len -= n + 1; - - n = lws_jws_base64_enc(jwe->jws.map.buf[LJWE_EKEY], - jwe->jws.map.len[LJWE_EKEY], out, out_len); - if (n < 0 || (int)out_len == n) { - lwsl_info("%s: unable to encode EKEY\n", __func__); - return -1; - } - - out += n; - *out++ = '.'; - out_len -= n + 1; - n = lws_jws_base64_enc(jwe->jws.map.buf[LJWE_IV], - jwe->jws.map.len[LJWE_IV], out, out_len); - if (n < 0 || (int)out_len == n) { - lwsl_info("%s: unable to encode IV\n", __func__); - return -1; - } - - out += n; - *out++ = '.'; - out_len -= n + 1; - - n = lws_jws_base64_enc(jwe->jws.map.buf[LJWE_CTXT], - jwe->jws.map.len[LJWE_CTXT], out, out_len); - if (n < 0 || (int)out_len == n) { - lwsl_info("%s: unable to encode CTXT\n", __func__); - return -1; - } - - out += n; - *out++ = '.'; - out_len -= n + 1; - n = lws_jws_base64_enc(jwe->jws.map.buf[LJWE_ATAG], - jwe->jws.map.len[LJWE_ATAG], out, out_len); - if (n < 0 || (int)out_len == n) { - lwsl_info("%s: unable to encode ATAG\n", __func__); - return -1; - } - - out += n; - *out++ = '\0'; - out_len -= n; - - return orig - out_len; -} - -int -lws_jwe_create_packet(struct lws_jwe *jwe, const char *payload, size_t len, - const char *nonce, char *out, size_t out_len, - struct lws_context *context) -{ - char *buf, *start, *p, *end, *p1, *end1; - struct lws_jws jws; - int n, m; - - lws_jws_init(&jws, &jwe->jwk, context); - - /* - * This buffer is local to the function, the actual output is prepared - * into out. Only the plaintext protected header - * (which contains the public key, 512 bytes for 4096b) goes in - * here temporarily. - */ - n = LWS_PRE + 2048; - buf = malloc(n); - if (!buf) { - lwsl_notice("%s: malloc %d failed\n", __func__, n); - return -1; - } - - p = start = buf + LWS_PRE; - end = buf + n - LWS_PRE - 1; - - /* - * temporary JWS protected header plaintext - */ - - if (!jwe->jose.alg || !jwe->jose.alg->alg) - goto bail; - - p += lws_snprintf(p, end - p, "{\"alg\":\"%s\",\"jwk\":", - jwe->jose.alg->alg); - m = end - p; - n = lws_jwk_export(&jwe->jwk, 0, p, &m); - if (n < 0) { - lwsl_notice("failed to export jwk\n"); - - goto bail; - } - p += n; - p += lws_snprintf(p, end - p, ",\"nonce\":\"%s\"}", nonce); - - /* - * prepare the signed outer JSON with all the parts in - */ - - p1 = out; - end1 = out + out_len - 1; - - p1 += lws_snprintf(p1, end1 - p1, "{\"protected\":\""); - jws.map_b64.buf[LJWS_JOSE] = p1; - n = lws_jws_base64_enc(start, p - start, p1, end1 - p1); - if (n < 0) { - lwsl_notice("%s: failed to encode protected\n", __func__); - goto bail; - } - jws.map_b64.len[LJWS_JOSE] = n; - p1 += n; - - p1 += lws_snprintf(p1, end1 - p1, "\",\"payload\":\""); - jws.map_b64.buf[LJWS_PYLD] = p1; - n = lws_jws_base64_enc(payload, len, p1, end1 - p1); - if (n < 0) { - lwsl_notice("%s: failed to encode payload\n", __func__); - goto bail; - } - jws.map_b64.len[LJWS_PYLD] = n; - p1 += n; - - p1 += lws_snprintf(p1, end1 - p1, "\",\"header\":\""); - jws.map_b64.buf[LJWS_UHDR] = p1; - n = lws_jws_base64_enc(payload, len, p1, end1 - p1); - if (n < 0) { - lwsl_notice("%s: failed to encode payload\n", __func__); - goto bail; - } - jws.map_b64.len[LJWS_UHDR] = n; - - p1 += n; - p1 += lws_snprintf(p1, end1 - p1, "\",\"signature\":\""); - - /* - * taking the b64 protected header and the b64 payload, sign them - * and place the signature into the packet - */ - n = lws_jws_sign_from_b64(&jwe->jose, &jws, p1, end1 - p1); - if (n < 0) { - lwsl_notice("sig gen failed\n"); - - goto bail; - } - jws.map_b64.buf[LJWS_SIG] = p1; - jws.map_b64.len[LJWS_SIG] = n; - - p1 += n; - p1 += lws_snprintf(p1, end1 - p1, "\"}"); - - free(buf); - - return p1 - out; - -bail: - lws_jws_destroy(&jws); - free(buf); - - return -1; -} - -static const char *protected_en[] = { - "encrypted_key", "aad", "iv", "ciphertext", "tag" -}; - -static int protected_idx[] = { - LJWE_EKEY, LJWE_AAD, LJWE_IV, LJWE_CTXT, LJWE_ATAG -}; - -/* - * The complete JWE may look something like this: - * - * { - * "protected": - * "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0", - * "unprotected": - * {"jku":"https://server.example.com/keys.jwks"}, - * "recipients":[ - * {"header": - * {"alg":"RSA1_5","kid":"2011-04-29"}, - * "encrypted_key": - * "UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0- - * kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx - * GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3 - * YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh - * cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg - * wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A"}, - * {"header": - * {"alg":"A128KW","kid":"7"}, - * "encrypted_key": - * "6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ"}], - * "iv": - * "AxY8DCtDaGlsbGljb3RoZQ", - * "ciphertext": - * "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY", - * "tag": - * "Mz-VPPyU4RlcuYv1IwIvzw" - * } - * - * The flattened JWE ends up like this - * - * { - * "protected": "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0", - * "unprotected": {"jku":"https://server.example.com/keys.jwks"}, - * "header": {"alg":"A128KW","kid":"7"}, - * "encrypted_key": "6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ", - * "iv": "AxY8DCtDaGlsbGljb3RoZQ", - * "ciphertext": "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY", - * "tag": "Mz-VPPyU4RlcuYv1IwIvzw" - * } - * - * { - * "protected":"", - * "unprotected":, - * "header":, - * "encrypted_key":"", - * "aad":"", - * "iv":"", - * "ciphertext":"", - * "tag":"" - * } - */ - -int -lws_jwe_render_flattened(struct lws_jwe *jwe, char *out, size_t out_len) -{ - char buf[3072], *p1, *end1, protected[128]; - int m, n, jlen, plen; - - jlen = lws_jose_render(&jwe->jose, jwe->jws.jwk, buf, sizeof(buf)); - if (jlen < 0) { - lwsl_err("%s: lws_jose_render failed\n", __func__); - - return -1; - } - - /* - * prepare the JWE JSON with all the parts in - */ - - p1 = out; - end1 = out + out_len - 1; - - /* - * The protected header is b64url encoding of the JOSE header part - */ - - plen = lws_snprintf(protected, sizeof(protected), - "{\"alg\":\"%s\",\"enc\":\"%s\"}", - jwe->jose.alg->alg, jwe->jose.enc_alg->alg); - - p1 += lws_snprintf(p1, end1 - p1, "{\"protected\":\""); - jwe->jws.map_b64.buf[LJWS_JOSE] = p1; - n = lws_jws_base64_enc(protected, plen, p1, end1 - p1); - if (n < 0) { - lwsl_notice("%s: failed to encode protected\n", __func__); - goto bail; - } - jwe->jws.map_b64.len[LJWS_JOSE] = n; - p1 += n; - - /* unprotected not supported atm */ - - p1 += lws_snprintf(p1, end1 - p1, "\",\n\"header\":"); - lws_strnncpy(p1, buf, jlen, end1 - p1); - p1 += strlen(p1); - - for (m = 0; m < (int)LWS_ARRAY_SIZE(protected_en); m++) - if (jwe->jws.map.buf[protected_idx[m]]) { - p1 += lws_snprintf(p1, end1 - p1, ",\n\"%s\":\"", - protected_en[m]); - //jwe->jws.map_b64.buf[protected_idx[m]] = p1; - n = lws_jws_base64_enc(jwe->jws.map.buf[protected_idx[m]], - jwe->jws.map.len[protected_idx[m]], - p1, end1 - p1); - if (n < 0) { - lwsl_notice("%s: failed to encode %s\n", - __func__, protected_en[m]); - goto bail; - } - //jwe->jws.map_b64.len[protected_idx[m]] = n; - p1 += n; - p1 += lws_snprintf(p1, end1 - p1, "\""); - } - - p1 += lws_snprintf(p1, end1 - p1, "\n}\n"); - - return p1 - out; - -bail: - lws_jws_destroy(&jwe->jws); - - return -1; -} diff -Nru libwebsockets-4.0.20/lib/jose/jwe/jwe-ecdh-es-aeskw.c libwebsockets-2.4.2/lib/jose/jwe/jwe-ecdh-es-aeskw.c --- libwebsockets-4.0.20/lib/jose/jwe/jwe-ecdh-es-aeskw.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jwe/jwe-ecdh-es-aeskw.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,616 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-jose-jwe.h" - -/* - * From RFC7518 JWA - * - * 4.6. Key Agreement with Elliptic Curve Diffie-Hellman Ephemeral Static - * (ECDH-ES) - * - * This section defines the specifics of key agreement with Elliptic - * Curve Diffie-Hellman Ephemeral Static [RFC6090], in combination with - * the Concat KDF, as defined in Section 5.8.1 of [NIST.800-56A]. The - * key agreement result can be used in one of two ways: - * - * 1. directly as the Content Encryption Key (CEK) for the "enc" - * algorithm, in the Direct Key Agreement mode, or - * - * 2. as a symmetric key used to wrap the CEK with the "A128KW", - * "A192KW", or "A256KW" algorithms, in the Key Agreement with Key - * Wrapping mode. - * - * A new ephemeral public key value MUST be generated for each key - * agreement operation. - * - * In Direct Key Agreement mode, the output of the Concat KDF MUST be a - * key of the same length as that used by the "enc" algorithm. In this - * case, the empty octet sequence is used as the JWE Encrypted Key - * value. The "alg" (algorithm) Header Parameter value "ECDH-ES" is - * used in the Direct Key Agreement mode. - * - * In Key Agreement with Key Wrapping mode, the output of the Concat KDF - * MUST be a key of the length needed for the specified key wrapping - * algorithm. In this case, the JWE Encrypted Key is the CEK wrapped - * with the agreed-upon key. - * - * The following "alg" (algorithm) Header Parameter values are used to - * indicate that the JWE Encrypted Key is the result of encrypting the - * CEK using the result of the key agreement algorithm as the key - * encryption key for the corresponding key wrapping algorithm: - * - * +-----------------+-------------------------------------------------+ - * | "alg" Param | Key Management Algorithm | - * | Value | | - * +-----------------+-------------------------------------------------+ - * | ECDH-ES+A128KW | ECDH-ES using Concat KDF and CEK wrapped with | - * | | "A128KW" | - * | ECDH-ES+A192KW | ECDH-ES using Concat KDF and CEK wrapped with | - * | | "A192KW" | - * | ECDH-ES+A256KW | ECDH-ES using Concat KDF and CEK wrapped with | - * | | "A256KW" | - * +-----------------+-------------------------------------------------+ - * - * 4.6.1. Header Parameters Used for ECDH Key Agreement - * - * The following Header Parameter names are used for key agreement as - * defined below. - * - * 4.6.1.1. "epk" (Ephemeral Public Key) Header Parameter - * - * The "epk" (ephemeral public key) value created by the originator for - * the use in key agreement algorithms. This key is represented as a - * JSON Web Key [JWK] public key value. It MUST contain only public key - * parameters and SHOULD contain only the minimum JWK parameters - * necessary to represent the key; other JWK parameters included can be - * checked for consistency and honored, or they can be ignored. This - * Header Parameter MUST be present and MUST be understood and processed - * by implementations when these algorithms are used. - * - * 4.6.1.2. "apu" (Agreement PartyUInfo) Header Parameter - * - * The "apu" (agreement PartyUInfo) value for key agreement algorithms - * using it (such as "ECDH-ES"), represented as a base64url-encoded - * string. When used, the PartyUInfo value contains information about - * the producer. Use of this Header Parameter is OPTIONAL. This Header - * Parameter MUST be understood and processed by implementations when - * these algorithms are used. - * - * 4.6.1.3. "apv" (Agreement PartyVInfo) Header Parameter - * - * The "apv" (agreement PartyVInfo) value for key agreement algorithms - * using it (such as "ECDH-ES"), represented as a base64url encoded - * string. When used, the PartyVInfo value contains information about - * the recipient. Use of this Header Parameter is OPTIONAL. This - * Header Parameter MUST be understood and processed by implementations - * when these algorithms are used. - * - * 4.6.2. Key Derivation for ECDH Key Agreement - * - * The key derivation process derives the agreed-upon key from the - * shared secret Z established through the ECDH algorithm, per - * Section 6.2.2.2 of [NIST.800-56A]. - * - * Key derivation is performed using the Concat KDF, as defined in - * Section 5.8.1 of [NIST.800-56A], where the Digest Method is SHA-256. - * The Concat KDF parameters are set as follows: - * - * Z - * This is set to the representation of the shared secret Z as an - * octet sequence. - * - * keydatalen - * This is set to the number of bits in the desired output key. For - * "ECDH-ES", this is length of the key used by the "enc" algorithm. - * For "ECDH-ES+A128KW", "ECDH-ES+A192KW", and "ECDH-ES+A256KW", this - * is 128, 192, and 256, respectively. - * - * AlgorithmID - * The AlgorithmID value is of the form Datalen || Data, where Data - * is a variable-length string of zero or more octets, and Datalen is - * a fixed-length, big-endian 32-bit counter that indicates the - * length (in octets) of Data. In the Direct Key Agreement case, - * Data is set to the octets of the ASCII representation of the "enc" - * Header Parameter value. In the Key Agreement with Key Wrapping - * case, Data is set to the octets of the ASCII representation of the - * "alg" (algorithm) Header Parameter value. - * - * PartyUInfo - * The PartyUInfo value is of the form Datalen || Data, where Data is - * a variable-length string of zero or more octets, and Datalen is a - * fixed-length, big-endian 32-bit counter that indicates the length - * (in octets) of Data. If an "apu" (agreement PartyUInfo) Header - * Parameter is present, Data is set to the result of base64url - * decoding the "apu" value and Datalen is set to the number of - * octets in Data. Otherwise, Datalen is set to 0 and Data is set to - * the empty octet sequence. - * - * PartyVInfo - * The PartyVInfo value is of the form Datalen || Data, where Data is - * a variable-length string of zero or more octets, and Datalen is a - * fixed-length, big-endian 32-bit counter that indicates the length - * (in octets) of Data. If an "apv" (agreement PartyVInfo) Header - * Parameter is present, Data is set to the result of base64url - * decoding the "apv" value and Datalen is set to the number of - * octets in Data. Otherwise, Datalen is set to 0 and Data is set to - * the empty octet sequence. - * - * SuppPubInfo - * This is set to the keydatalen represented as a 32-bit big-endian - * integer. - * - * SuppPrivInfo - * This is set to the empty octet sequence. - * - * Applications need to specify how the "apu" and "apv" Header - * Parameters are used for that application. The "apu" and "apv" values - * MUST be distinct, when used. Applications wishing to conform to - * [NIST.800-56A] need to provide values that meet the requirements of - * that document, e.g., by using values that identify the producer and - * consumer. Alternatively, applications MAY conduct key derivation in - * a manner similar to "Diffie-Hellman Key Agreement Method" [RFC2631]: - * in that case, the "apu" parameter MAY either be omitted or represent - * a random 512-bit value (analogous to PartyAInfo in Ephemeral-Static - * mode in RFC 2631) and the "apv" parameter SHOULD NOT be present. - * - */ - - -/* - * - ECDH-ES[-variant] comes in the jose "alg" and just covers key agreement. - * The "enc" action is completely separate and handled elsewhere. However - * the key size throughout is determined by the needs of the "enc" action. - * - * - The jwe->jws.jwk is the PEER - the encryption consumer's - public key. - * - * - The public part of the ephemeral key comes out in jose.jwk_ephemeral - * - * - Return shared secret length or < 0 for error - * - * - Unwrapped CEK in EKEY. If any, wrapped CEK in "wrapped". - * - * - Caller responsibility to cleanse EKEY. - */ - -static int -lws_jwe_encrypt_ecdh(struct lws_jwe *jwe, char *temp, int *temp_len, - uint8_t *cek) -{ - uint8_t shared_secret[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES], - derived[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES]; - int m, n, ret = -1, ot = *temp_len, ss_len = sizeof(shared_secret), - // kw_hlen = lws_genhash_size(jwe->jose.alg->hash_type), - enc_hlen = lws_genhmac_size(jwe->jose.enc_alg->hmac_type), - ekbytes = 32; //jwe->jose.alg->keybits_fixed / 8; - struct lws_genec_ctx ecctx; - struct lws_jwk *ephem = &jwe->jose.recipient[jwe->recip].jwk_ephemeral; - - if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_EC) { - lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty); - - return -1; - } - - ephem->kty = LWS_GENCRYPTO_KTY_EC; - ephem->private_key = 1; - - /* Generate jose.jwk_ephemeral on the peer public key curve */ - - if (lws_genecdh_create(&ecctx, jwe->jws.context, NULL)) - goto bail; - - /* ephemeral context gets random key on same curve as recip pubkey */ - if (lws_genecdh_new_keypair(&ecctx, LDHS_OURS, (const char *) - jwe->jws.jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, - ephem->e)) - goto bail; - - /* peer context gets js->jwk key */ - if (lws_genecdh_set_key(&ecctx, jwe->jws.jwk->e, LDHS_THEIRS)) { - lwsl_err("%s: setting peer pubkey failed\n", __func__); - goto bail; - } - - /* combine our ephemeral key and the peer pubkey to get the secret */ - - if (lws_genecdh_compute_shared_secret(&ecctx, shared_secret, &ss_len)) { - lwsl_notice("%s: lws_genecdh_compute_shared_secret failed\n", - __func__); - - goto bail; - } - - /* - * The private part of the ephemeral key is finished with... - * cleanse and free it. We need to keep the public part around so we - * can publish it with the JWE as "epk". - */ - - lws_explicit_bzero(ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].buf, - ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].len); - lws_free_set_NULL(ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].buf); - ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].len = 0; - ephem->private_key = 0; - - /* - * Derive the CEK from the shared secret... amount of bytes written to - * derived matches bitcount in jwe->jose.enc_alg->keybits_fixed - * - * In Direct Key Agreement mode, the output of the Concat KDF MUST be a - * key of the same length as that used by the "enc" algorithm. - */ - - if (lws_jwa_concat_kdf(jwe, - jwe->jose.alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE, - derived, shared_secret, ss_len)) { - lwsl_notice("%s: lws_jwa_concat_kdf failed\n", __func__); - - goto bail; - } - - /* in P-521 case, we get a 66-byte shared secret for a 64-byte key */ - if (ss_len < enc_hlen) { - lwsl_err("%s: concat KDF bad derived key len %d\n", __func__, - ss_len); - goto bail; - } - - /* - * For "ECDH-ES", that was it, and we use what we just wrapped in - * wrapped as the CEK without publishing it. - * - * For "ECDH-ES-AES[128,192,256]KW", we generate a new, random CEK and - * then wrap it using the key we just wrapped, and make the wrapped - * version available in EKEY. - */ - - if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) { - struct lws_gencrypto_keyelem el; - struct lws_genaes_ctx aesctx; - - /* generate the actual CEK in cek */ - - if (lws_get_random(jwe->jws.context, cek, enc_hlen) != - (size_t)enc_hlen) { - lwsl_err("Problem getting random\n"); - goto bail; - } - - /* wrap with the derived key */ - - el.buf = derived; - el.len = enc_hlen / 2; - - if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_KW, &el, - 1, NULL)) { - - lwsl_notice("%s: lws_genaes_create\n", __func__); - goto bail; - } - - /* wrap CEK into EKEY */ - - n = lws_genaes_crypt(&aesctx, cek, enc_hlen, - (void *)jwe->jws.map.buf[LJWE_EKEY], - NULL, NULL, NULL, 0); - m = lws_genaes_destroy(&aesctx, NULL, 0); - if (n < 0) { - lwsl_err("%s: encrypt cek fail\n", __func__); - goto bail; - } - if (m < 0) { - lwsl_err("%s: lws_genaes_destroy fail\n", __func__); - goto bail; - } - - jwe->jws.map.len[LJWE_EKEY] = enc_hlen + 8; - - /* Wrapped CEK is in EKEY. Random CEK is in cek. */ - - } else /* direct derived CEK is in cek */ - memcpy(cek, derived, enc_hlen); - - /* rewrite the protected JOSE header to have the epk pieces */ - - jwe->jws.map.buf[LJWE_JOSE] = temp; - - m = n = lws_snprintf(temp, *temp_len, - "{\"alg\":\"%s\", \"enc\":\"%s\", \"epk\":", - jwe->jose.alg->alg, jwe->jose.enc_alg->alg); - *temp_len -= n; - - n = lws_jwk_export(ephem, 0, temp + (ot - *temp_len), temp_len); - if (n < 0) { - lwsl_err("%s: ephemeral export failed\n", __func__); - goto bail; - } - m += n; - - n = lws_snprintf(temp + (ot - *temp_len), *temp_len, "}"); - *temp_len -= n + 1; - m += n; - jwe->jws.map.len[LJWE_JOSE] = m; - - /* create a b64 version of the JOSE header, needed later for AAD */ - - if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE, - temp + (ot - *temp_len), temp_len, - jwe->jws.map.buf[LJWE_JOSE], - jwe->jws.map.len[LJWE_JOSE])) - return -1; - - ret = enc_hlen; - -bail: - lws_genec_destroy(&ecctx); - - /* cleanse the shared secret (watch out for cek at parent too) */ - lws_explicit_bzero(shared_secret, ekbytes); - lws_explicit_bzero(derived, ekbytes); - - return ret; -} - -int -lws_jwe_encrypt_ecdh_cbc_hs(struct lws_jwe *jwe, char *temp, int *temp_len) -{ - int ss_len, // kw_hlen = lws_genhash_size(jwe->jose.alg->hash_type), - enc_hlen = lws_genhmac_size(jwe->jose.enc_alg->hmac_type); - uint8_t cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES]; - int ekbytes = jwe->jose.alg->keybits_fixed / 8; - int n, ot = *temp_len, ret = -1; - - /* if we will produce an EKEY, make space for it */ - - if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) { - if (lws_jws_alloc_element(&jwe->jws.map, LJWE_EKEY, - temp + (ot - *temp_len), temp_len, - enc_hlen + 8, 0)) - goto bail; - } - - /* decrypt the CEK */ - - ss_len = lws_jwe_encrypt_ecdh(jwe, temp + (ot - *temp_len), temp_len, cek); - if (ss_len < 0) { - lwsl_err("%s: lws_jwe_encrypt_ecdh failed\n", __func__); - return -1; - } - - /* cek contains the unwrapped CEK. EKEY may contain wrapped CEK */ - - /* make space for the payload encryption pieces */ - - if (lws_jws_alloc_element(&jwe->jws.map, LJWE_ATAG, - temp + (ot - *temp_len), - temp_len, enc_hlen / 2, 0)) - goto bail; - - if (lws_jws_alloc_element(&jwe->jws.map, LJWE_IV, - temp + (ot - *temp_len), - temp_len, LWS_JWE_AES_IV_BYTES, 0)) - goto bail; - - /* Perform the authenticated encryption on CTXT... - * ...the AAD is b64u(protected JOSE header) */ - - n = lws_jwe_encrypt_cbc_hs(jwe, cek, - (uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE], - jwe->jws.map_b64.len[LJWE_JOSE]); - if (n < 0) { - lwsl_notice("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__); - goto bail; - } - - ret = 0; - -bail: - /* if fail or direct CEK, cleanse and remove EKEY */ - if (ret || jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE) { - if (jwe->jws.map.len[LJWE_EKEY]) - lws_explicit_bzero((void *)jwe->jws.map.buf[LJWE_EKEY], - jwe->jws.map.len[LJWE_EKEY]); - jwe->jws.map.len[LJWE_EKEY] = 0; - } - - lws_explicit_bzero(cek, ekbytes); - - return ret; -} - -/* - * jwe->jws.jwk is recipient private key - * - * If kw mode, then EKEY is the wrapped CEK - * - * - */ - -static int -lws_jwe_auth_and_decrypt_ecdh(struct lws_jwe *jwe) -{ - uint8_t shared_secret[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES], - derived[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES]; - int ekbytes = jwe->jose.enc_alg->keybits_fixed / 8, - enc_hlen = lws_genhmac_size(jwe->jose.enc_alg->hmac_type); - struct lws_genec_ctx ecctx; - int n, ret = -1, ss_len = sizeof(shared_secret); - - if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_EC) { - lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty); - - return -1; - } - - if (jwe->jose.recipient[jwe->recip].jwk_ephemeral.kty != - LWS_GENCRYPTO_KTY_EC) { - lwsl_err("%s: missing epk\n", __func__); - - return -1; - } - - /* - * Recompute the shared secret... - * - * - direct: it's the CEK - * - * - aeskw: apply it as AES keywrap to EKEY to get the CEK - */ - - /* Generate jose.jwk_ephemeral on the peer public key curve */ - - if (lws_genecdh_create(&ecctx, jwe->jws.context, NULL)) - goto bail; - - /* Load our private key into our side of the ecdh context */ - - if (lws_genecdh_set_key(&ecctx, jwe->jws.jwk->e, LDHS_OURS)) { - lwsl_err("%s: setting our private key failed\n", __func__); - goto bail; - } - - /* Import the ephemeral public key into the peer side */ - if (lws_genecdh_set_key(&ecctx, - jwe->jose.recipient[jwe->recip].jwk_ephemeral.e, - LDHS_THEIRS)) { - lwsl_err("%s: setting epk pubkey failed\n", __func__); - goto bail; - } - - /* combine their ephemeral key and our private key to get the secret */ - - if (lws_genecdh_compute_shared_secret(&ecctx, shared_secret, &ss_len)) { - lwsl_notice("%s: lws_genecdh_compute_shared_secret failed\n", - __func__); - - goto bail; - } - - lws_genec_destroy(&ecctx); - - if (ss_len < enc_hlen) { - lwsl_err("%s: ss_len %d ekbytes %d\n", __func__, ss_len, enc_hlen); - goto bail; - } - - /* - * Derive the CEK from the shared secret... amount of bytes written to - * cek[] matches bitcount in jwe->jose.enc_alg->keybits_fixed - */ - - if (lws_jwa_concat_kdf(jwe, - jwe->jose.alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE, - derived, shared_secret, ss_len)) { - lwsl_notice("%s: lws_jwa_concat_kdf failed\n", __func__); - - goto bail; - } - - /* - * "ECDH-ES": derived is the CEK - * "ECDH-ES-AES[128,192,256]KW": wrapped key is in EKEY, - * "derived" contains KEK - */ - - if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) { - struct lws_gencrypto_keyelem el; - struct lws_genaes_ctx aesctx; - int m; - - /* Confirm space for EKEY */ - - if (jwe->jws.map.len[LJWE_EKEY] < (unsigned int)enc_hlen) { - lwsl_err("%s: missing EKEY\n", __func__); - - goto bail; - } - - /* unwrap with the KEK we derived */ - - el.buf = derived; - el.len = enc_hlen / 2; - - if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_KW, - &el, 1, NULL)) { - - lwsl_notice("%s: lws_genaes_create\n", __func__); - goto bail; - } - - /* decrypt the EKEY to end up with CEK in "shared_secret" */ - - n = lws_genaes_crypt(&aesctx, - (const uint8_t *)jwe->jws.map.buf[LJWE_EKEY], - jwe->jws.map.len[LJWE_EKEY], - (uint8_t *)shared_secret, - NULL, NULL, NULL, 0); - m = lws_genaes_destroy(&aesctx, NULL, 0); - if (n < 0) { - lwsl_err("%s: decrypt cek fail\n", __func__); - goto bail; - } - if (m < 0) { - lwsl_err("%s: lws_genaes_destroy fail\n", __func__); - goto bail; - } - } else - memcpy(shared_secret, derived, enc_hlen); - - /* either way, the recovered CEK is in shared_secret */ - - if (lws_jwe_auth_and_decrypt_cbc_hs(jwe, shared_secret, - (uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE], - jwe->jws.map_b64.len[LJWE_JOSE]) < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs fail\n", __func__); - goto bail; - } - - /* if all went well, then CTXT is now the plaintext */ - ret = 0; - -bail: - /* cleanse wrapped on stack that contained the CEK / wrapped key */ - lws_explicit_bzero(derived, ekbytes); - /* cleanse the shared secret */ - lws_explicit_bzero(shared_secret, ekbytes); - - return ret; -} - -int -lws_jwe_auth_and_decrypt_ecdh_cbc_hs(struct lws_jwe *jwe, - char *temp, int *temp_len) -{ - /* create a b64 version of the JOSE header, needed later for AAD */ - - if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE, - temp, temp_len, - jwe->jws.map.buf[LJWE_JOSE], - jwe->jws.map.len[LJWE_JOSE])) - return -1; - - return lws_jwe_auth_and_decrypt_ecdh(jwe); -} diff -Nru libwebsockets-4.0.20/lib/jose/jwe/jwe-rsa-aescbc.c libwebsockets-2.4.2/lib/jose/jwe/jwe-rsa-aescbc.c --- libwebsockets-4.0.20/lib/jose/jwe/jwe-rsa-aescbc.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jwe/jwe-rsa-aescbc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,195 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-jose-jwe.h" - -/* - * Requirements on entry: - * - * - jwe->jws.map LJWE_JOSE contains the ASCII JOSE header - * - jwe->jws.map LJWE_EKEY contains cek of enc_alg hmac length - * - jwe->jws.map LJWE_CTXT contains the plaintext - * - * On successful exit: - * - * - jwe->jws.map LJWE_ATAG contains the tag - * - jwe->jws.map LJWE_IV contains the new random IV that was used - * - jwe->jws.map LJWE_EKEY contains the encrypted CEK - * - jwe->jws.map LJWE_CTXT contains the ciphertext - * - * Return the amount of temp used, or -1 - */ - -int -lws_jwe_encrypt_rsa_aes_cbc_hs(struct lws_jwe *jwe, - char *temp, int *temp_len) -{ - int n, hlen = lws_genhmac_size(jwe->jose.enc_alg->hmac_type), ot = *temp_len; - char ekey[LWS_GENHASH_LARGEST]; - struct lws_genrsa_ctx rsactx; - - if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_RSA) { - lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty); - - return -1; - } - - /* - * Notice that the unencrypted EKEY coming in is smaller than the - * RSA-encrypted EKEY going out, which is going to be the RSA key size - * - * Create a b64 version of the JOSE header, needed as aad - */ - if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE, - temp, temp_len, - jwe->jws.map.buf[LJWE_JOSE], - jwe->jws.map.len[LJWE_JOSE])) - return -1; - - if (lws_jws_alloc_element(&jwe->jws.map, LJWE_ATAG, temp + (ot - *temp_len), - temp_len, hlen / 2, 0)) - return -1; - - if (lws_jws_alloc_element(&jwe->jws.map, LJWE_IV, temp + (ot - *temp_len), - temp_len, LWS_JWE_AES_IV_BYTES, 0)) - return -1; - - /* - * Without changing the unencrypted CEK in EKEY, reallocate enough - * space to write the RSA-encrypted version in-situ. - */ - if (lws_jws_dup_element(&jwe->jws.map, LJWE_EKEY, temp + (ot - *temp_len), - temp_len, jwe->jws.map.buf[LJWE_EKEY], - jwe->jws.map.len[LJWE_EKEY], - jwe->jws.jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len)) - return -1; - - /* Encrypt using the raw CEK (treated as MAC KEY | ENC KEY) */ - - n = lws_jwe_encrypt_cbc_hs(jwe, (uint8_t *)jwe->jws.map.buf[LJWE_EKEY], - (uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE], - jwe->jws.map_b64.len[LJWE_JOSE]); - if (n < 0) { - lwsl_err("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__); - return -1; - } - - if (lws_genrsa_create(&rsactx, jwe->jws.jwk->e, jwe->jws.context, - !strcmp(jwe->jose.alg->alg, "RSA-OAEP") ? - LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5, - LWS_GENHASH_TYPE_UNKNOWN)) { - lwsl_notice("%s: lws_genrsa_create\n", - __func__); - return -1; - } - - /* encrypt the CEK using RSA, mbedtls can't handle both in and out are - * the EKEY, so copy the unencrypted ekey out temporarily */ - - memcpy(ekey, jwe->jws.map.buf[LJWE_EKEY], hlen); - - n = lws_genrsa_public_encrypt(&rsactx, (uint8_t *)ekey, hlen, - (uint8_t *)jwe->jws.map.buf[LJWE_EKEY]); - lws_genrsa_destroy(&rsactx); - lws_explicit_bzero(ekey, hlen); /* cleanse the temp CEK copy */ - if (n < 0) { - lwsl_err("%s: encrypt cek fail\n", __func__); - return -1; - } - jwe->jws.map.len[LJWE_EKEY] = n; /* update to encrypted EKEY size */ - - /* - * We end up with IV, ATAG, set, EKEY encrypted and CTXT is ciphertext, - * and b64u version of ATAG in map_b64. - */ - - return 0; -} - -int -lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(struct lws_jwe *jwe) -{ - int n; - struct lws_genrsa_ctx rsactx; - uint8_t enc_cek[512]; - - if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_RSA) { - lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty); - - return -1; - } - - if (jwe->jws.map.len[LJWE_EKEY] < 40) { - lwsl_err("%s: EKEY length too short %d\n", __func__, - jwe->jws.map.len[LJWE_EKEY]); - - return -1; - } - - /* Decrypt the JWE Encrypted Key to get the raw MAC || CEK */ - - if (lws_genrsa_create(&rsactx, jwe->jws.jwk->e, jwe->jws.context, - !strcmp(jwe->jose.alg->alg, "RSA-OAEP") ? - LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5, - LWS_GENHASH_TYPE_UNKNOWN)) { - lwsl_notice("%s: lws_genrsa_public_decrypt_create\n", - __func__); - return -1; - } - - n = lws_genrsa_private_decrypt(&rsactx, - (uint8_t *)jwe->jws.map.buf[LJWE_EKEY], - jwe->jws.map.len[LJWE_EKEY], enc_cek, - sizeof(enc_cek)); - lws_genrsa_destroy(&rsactx); - if (n < 0) { - lwsl_err("%s: decrypt cek fail: \n", __func__); - return -1; - } - - n = lws_jwe_auth_and_decrypt_cbc_hs(jwe, enc_cek, - (uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE], - jwe->jws.map_b64.len[LJWE_JOSE]); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs failed\n", - __func__); - return -1; - } - -#if defined(LWS_WITH_MBEDTLS) && defined(LWS_PLAT_OPTEE) - - /* strip padding */ - - n = jwe->jws.map.buf[LJWE_CTXT][jwe->jws.map.len[LJWE_CTXT] - 1]; - if (n > 16) { - lwsl_err("%s: n == %d, plen %d\n", __func__, n, - (int)jwe->jws.map.len[LJWE_CTXT]); - return -1; - } - jwe->jws.map.len[LJWE_CTXT] -= n; -#endif - - return jwe->jws.map.len[LJWE_CTXT]; -} diff -Nru libwebsockets-4.0.20/lib/jose/jwe/jwe-rsa-aesgcm.c libwebsockets-2.4.2/lib/jose/jwe/jwe-rsa-aesgcm.c --- libwebsockets-4.0.20/lib/jose/jwe/jwe-rsa-aesgcm.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jwe/jwe-rsa-aesgcm.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,183 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-jose-jwe.h" - -#define LWS_AESGCM_IV 12 - - -int -lws_jwe_encrypt_rsa_aes_gcm(struct lws_jwe *jwe, char *temp, int *temp_len) -{ - int ekbytes = jwe->jose.enc_alg->keybits_fixed / 8; - struct lws_genrsa_ctx rsactx; - int n, ret = -1, ot = *temp_len; - - if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_RSA) { - lwsl_err("%s: wrong kty %d\n", __func__, jwe->jws.jwk->kty); - - return -1; - } - - /* create the IV + CEK */ - - if (lws_jws_randomize_element(jwe->jws.context, &jwe->jws.map, LJWE_IV, - temp, temp_len, - LWS_AESGCM_IV, 0)) - return -1; - - if (lws_jws_alloc_element(&jwe->jws.map, LJWE_ATAG, - temp + (ot - *temp_len), - temp_len, LWS_AESGCM_TAG, 0)) - return -1; - - /* create a b64 version of the JOSE header, needed as aad */ - - if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE, - temp + (ot - *temp_len), temp_len, - jwe->jws.map.buf[LJWE_JOSE], - jwe->jws.map.len[LJWE_JOSE])) - return -1; - - /* - * If none already, create a new, random CEK in the JWE (so it can be - * reused for other recipients on same payload). If it already exists, - * just reuse it. It will be cleansed in the JWE destroy. - */ - if (!jwe->cek_valid) { - if (lws_get_random(jwe->jws.context, jwe->cek, ekbytes) != - (size_t)ekbytes) { - lwsl_err("%s: Problem getting random\n", __func__); - return -1; - } - jwe->cek_valid = 1; - } - - if (lws_jws_dup_element(&jwe->jws.map, LJWE_EKEY, - temp + (ot - *temp_len), temp_len, - jwe->cek, ekbytes, 0)) - return -1; - - /* encrypt the payload */ - - n = lws_jwe_encrypt_gcm(jwe, (uint8_t *)jwe->jws.map.buf[LJWE_EKEY], - (uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE], - jwe->jws.map_b64.len[LJWE_JOSE]); - if (n < 0) { - lwsl_err("%s: lws_jwe_encrypt_gcm failed\n", - __func__); - goto bail; - } - - /* Encrypt the CEK into EKEY to make the JWE Encrypted Key */ - - if (lws_genrsa_create(&rsactx, jwe->jws.jwk->e, jwe->jws.context, - !strcmp(jwe->jose.alg->alg, "RSA-OAEP") ? - LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5, - LWS_GENHASH_TYPE_SHA1 /* !!! */)) { - lwsl_notice("%s: lws_genrsa_public_decrypt_create\n", - __func__); - goto bail; - } - - n = lws_genrsa_public_encrypt(&rsactx, jwe->cek, ekbytes, - (uint8_t *)jwe->jws.map.buf[LJWE_EKEY]); - lws_genrsa_destroy(&rsactx); - if (n < 0) { - lwsl_err("%s: encrypt cek fail: \n", __func__); - goto bail; - } - - /* set the EKEY length to the actual enciphered length */ - jwe->jws.map.len[LJWE_EKEY] = n; - - ret = jwe->jws.map.len[LJWE_CTXT]; - -bail: - - return ret; -} - -int -lws_jwe_auth_and_decrypt_rsa_aes_gcm(struct lws_jwe *jwe) -{ - int n; - struct lws_genrsa_ctx rsactx; - uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES]; - - if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_RSA) { - lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty); - - return -1; - } - - if (jwe->jws.map.len[LJWE_EKEY] < 32) { - lwsl_err("%s: EKEY length too short %d\n", __func__, - jwe->jws.map.len[LJWE_EKEY]); - - return -1; - } - - /* Decrypt the JWE Encrypted Key to get the direct CEK */ - - if (lws_genrsa_create(&rsactx, jwe->jws.jwk->e, jwe->jws.context, - !strcmp(jwe->jose.alg->alg, "RSA-OAEP") ? - LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5, - LWS_GENHASH_TYPE_SHA1 /* !!! */)) { - lwsl_notice("%s: lws_genrsa_public_decrypt_create\n", - __func__); - return -1; - } - - n = lws_genrsa_private_decrypt(&rsactx, - (uint8_t *)jwe->jws.map.buf[LJWE_EKEY], - jwe->jws.map.len[LJWE_EKEY], enc_cek, - sizeof(enc_cek)); - lws_genrsa_destroy(&rsactx); - if (n < 0) { - lwsl_err("%s: decrypt cek fail: \n", __func__); - return -1; - } - - n = lws_jwe_auth_and_decrypt_gcm(jwe, enc_cek, - (uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE], - jwe->jws.map_b64.len[LJWE_JOSE]); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt_gcm_hs failed\n", - __func__); - return -1; - } - -#if defined(LWS_WITH_MBEDTLS) && defined(LWS_PLAT_OPTEE) - /* strip padding */ - - n = jwe->jws.map.buf[LJWE_CTXT][jwe->jws.map.len[LJWE_CTXT] - 1]; - if (n > 16) - return -1; - jwe->jws.map.len[LJWE_CTXT] -= n; -#endif - - return jwe->jws.map.len[LJWE_CTXT]; -} diff -Nru libwebsockets-4.0.20/lib/jose/jwe/private-lib-jose-jwe.h libwebsockets-2.4.2/lib/jose/jwe/private-lib-jose-jwe.h --- libwebsockets-4.0.20/lib/jose/jwe/private-lib-jose-jwe.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jwe/private-lib-jose-jwe.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -#define LWS_AESGCM_IV 12 -#define LWS_AESGCM_TAG 16 - -/* jwe-rsa-aescbc.c */ - -int -lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(struct lws_jwe *jwe); - - -int -lws_jwe_encrypt_rsa_aes_cbc_hs(struct lws_jwe *jwe, - char *temp, int *temp_len); - -int -lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *enc_cek, - uint8_t *aad, int aad_len); - - -/* jws-rsa-aesgcm.c */ - -int -lws_jwe_auth_and_decrypt_gcm(struct lws_jwe *jwe, uint8_t *enc_cek, - uint8_t *aad, int aad_len); - -int -lws_jwe_auth_and_decrypt_rsa_aes_gcm(struct lws_jwe *jwe); - -int -lws_jwe_encrypt_gcm(struct lws_jwe *jwe, - uint8_t *enc_cek, uint8_t *aad, int aad_len); - -int -lws_jwe_encrypt_rsa_aes_gcm(struct lws_jwe *jwe, - char *temp, int *temp_len); - - - - -/* jwe-rsa-aeskw.c */ - -int -lws_jwe_encrypt_aeskw_cbc_hs(struct lws_jwe *jwe, - char *temp, int *temp_len); - -int -lws_jwe_auth_and_decrypt_aeskw_cbc_hs(struct lws_jwe *jwe); - -/* aescbc.c */ - -int -lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *enc_cek, - uint8_t *aad, int aad_len); - -int -lws_jwe_encrypt_cbc_hs(struct lws_jwe *jwe, - uint8_t *cek, uint8_t *aad, int aad_len); - -int -lws_jwe_auth_and_decrypt_ecdh_cbc_hs(struct lws_jwe *jwe, - char *temp, int *temp_len); - -int -lws_jwe_encrypt_ecdh_cbc_hs(struct lws_jwe *jwe, - char *temp, int *temp_len); diff -Nru libwebsockets-4.0.20/lib/jose/jwk/jwk.c libwebsockets-2.4.2/lib/jose/jwk/jwk.c --- libwebsockets-4.0.20/lib/jose/jwk/jwk.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jwk/jwk.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,914 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-jose.h" - -#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT) -#include -#endif - -static const char * const kty_names[] = { - "unknown", /* LWS_GENCRYPTO_KTY_UNKNOWN */ - "oct", /* LWS_GENCRYPTO_KTY_OCT */ - "RSA", /* LWS_GENCRYPTO_KTY_RSA */ - "EC" /* LWS_GENCRYPTO_KTY_EC */ -}; - -/* - * These are the entire legal token set for names in jwk. - * - * The first version is used to parse a detached single jwk that don't have any - * parent JSON context. The second version is used to parse full jwk objects - * that has a "keys": [ ] array containing the keys. - */ - -static const char * const jwk_tok[] = { - "keys[]", /* dummy */ - "e", "n", "d", "p", "q", "dp", "dq", "qi", /* RSA */ - "kty", /* generic */ - "k", /* symmetric key data */ - "crv", "x", "y", /* EC (also "D") */ - "kid", /* generic */ - "use" /* mutually exclusive with "key_ops" */, - "key_ops" /* mutually exclusive with "use" */, - "x5c", /* generic */ - "alg" /* generic */ -}, * const jwk_outer_tok[] = { - "keys[]", - "keys[].e", "keys[].n", "keys[].d", "keys[].p", "keys[].q", "keys[].dp", - "keys[].dq", "keys[].qi", - - "keys[].kty", "keys[].k", /* generic */ - "keys[].crv", "keys[].x", "keys[].y", /* EC (also "D") */ - "keys[].kid", "keys[].use" /* mutually exclusive with "key_ops" */, - "keys[].key_ops", /* mutually exclusive with "use" */ - "keys[].x5c", "keys[].alg" -}; - -/* information about each token declared above */ - -#define F_M (1 << 9) /* Mandatory for key type */ -#define F_B64 (1 << 10) /* Base64 coded octets */ -#define F_B64U (1 << 11) /* Base64 Url coded octets */ -#define F_META (1 << 12) /* JWK key metainformation */ -#define F_RSA (1 << 13) /* RSA key */ -#define F_EC (1 << 14) /* Elliptic curve key */ -#define F_OCT (1 << 15) /* octet key */ - -static unsigned short tok_map[] = { - F_RSA | F_EC | F_OCT | F_META | 0xff, - F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_E, - F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_N, - F_RSA | F_EC | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_D, - F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_P, - F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_Q, - F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DP, - F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DQ, - F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_QI, - - F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY, - F_OCT | F_B64U | F_M | LWS_GENCRYPTO_OCT_KEYEL_K, - - F_EC | F_M | LWS_GENCRYPTO_EC_KEYEL_CRV, - F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_X, - F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_Y, - - F_RSA | F_EC | F_OCT | F_META | JWK_META_KID, - F_RSA | F_EC | F_OCT | F_META | JWK_META_USE, - - F_RSA | F_EC | F_OCT | F_META | JWK_META_KEY_OPS, - F_RSA | F_EC | F_OCT | F_META | F_B64 | JWK_META_X5C, - F_RSA | F_EC | F_OCT | F_META | JWK_META_ALG, -}; - -static const char *meta_names[] = { - "kty", "kid", "use", "key_ops", "x5c", "alg" -}; - -struct lexico { - const char *name; - int idx; - char meta; -} lexico_ec[] = { - { "alg", JWK_META_ALG, 1 }, - { "crv", LWS_GENCRYPTO_EC_KEYEL_CRV, 0 }, - { "d", LWS_GENCRYPTO_EC_KEYEL_D, 2 | 0 }, - { "key_ops", JWK_META_KEY_OPS, 1 }, - { "kid", JWK_META_KID, 1 }, - { "kty", JWK_META_KTY, 1 }, - { "use", JWK_META_USE, 1 }, - { "x", LWS_GENCRYPTO_EC_KEYEL_X, 0 }, - { "x5c", JWK_META_X5C, 1 }, - { "y", LWS_GENCRYPTO_EC_KEYEL_Y, 0 } -}, lexico_oct[] = { - { "alg", JWK_META_ALG, 1 }, - { "k", LWS_GENCRYPTO_OCT_KEYEL_K, 0 }, - { "key_ops", JWK_META_KEY_OPS, 1 }, - { "kid", JWK_META_KID, 1 }, - { "kty", JWK_META_KTY, 1 }, - { "use", JWK_META_USE, 1 }, - { "x5c", JWK_META_X5C, 1 } -}, lexico_rsa[] = { - { "alg", JWK_META_ALG, 1 }, - { "d", LWS_GENCRYPTO_RSA_KEYEL_D, 2 | 0 }, - { "dp", LWS_GENCRYPTO_RSA_KEYEL_DP, 2 | 0 }, - { "dq", LWS_GENCRYPTO_RSA_KEYEL_DQ, 2 | 0 }, - { "e", LWS_GENCRYPTO_RSA_KEYEL_E, 0 }, - { "key_ops", JWK_META_KEY_OPS, 1 }, - { "kid", JWK_META_KID, 1 }, - { "kty", JWK_META_KTY, 1 }, - { "n", LWS_GENCRYPTO_RSA_KEYEL_N, 0 }, - { "p", LWS_GENCRYPTO_RSA_KEYEL_P, 2 | 0 }, - { "q", LWS_GENCRYPTO_RSA_KEYEL_Q, 2 | 0 }, - { "qi", LWS_GENCRYPTO_RSA_KEYEL_QI, 2 | 0 }, - { "use", JWK_META_USE, 1 }, - { "x5c", JWK_META_X5C, 1 } -}; - -static const char meta_b64[] = { 0, 0, 0, 0, 1, 0 }; - -static const char *oct_names[] = { - "k" -}; -static const char oct_b64[] = { 1 }; - -static const char *rsa_names[] = { - "e", "n", "d", "p", "q", "dp", "dq", "qi" -}; -static const char rsa_b64[] = { 1, 1, 1, 1, 1, 1, 1, 1 }; - -static const char *ec_names[] = { - "crv", "x", "d", "y", -}; -static const char ec_b64[] = { 0, 1, 1, 1 }; - -int -lws_jwk_dump(struct lws_jwk *jwk) -{ - const char **enames, *b64; - int elems; - int n; - - (void)enames; - (void)meta_names; - - switch (jwk->kty) { - default: - case LWS_GENCRYPTO_KTY_UNKNOWN: - lwsl_err("%s: jwk %p: unknown type\n", __func__, jwk); - - return 1; - case LWS_GENCRYPTO_KTY_OCT: - elems = LWS_GENCRYPTO_OCT_KEYEL_COUNT; - enames = oct_names; - b64 = oct_b64; - break; - case LWS_GENCRYPTO_KTY_RSA: - elems = LWS_GENCRYPTO_RSA_KEYEL_COUNT; - enames = rsa_names; - b64 = rsa_b64; - break; - case LWS_GENCRYPTO_KTY_EC: - elems = LWS_GENCRYPTO_EC_KEYEL_COUNT; - enames = ec_names; - b64 = ec_b64; - break; - } - - lwsl_info("%s: jwk %p\n", __func__, jwk); - - for (n = 0; n < LWS_COUNT_JWK_ELEMENTS; n++) { - if (jwk->meta[n].buf && meta_b64[n]) { - lwsl_info(" meta: %s\n", meta_names[n]); - lwsl_hexdump_info(jwk->meta[n].buf, jwk->meta[n].len); - } - if (jwk->meta[n].buf && !meta_b64[n]) - lwsl_info(" meta: %s: '%s'\n", meta_names[n], - jwk->meta[n].buf); - } - - for (n = 0; n < elems; n++) { - if (jwk->e[n].buf && b64[n]) { - lwsl_info(" e: %s\n", enames[n]); - lwsl_hexdump_info(jwk->e[n].buf, jwk->e[n].len); - } - if (jwk->e[n].buf && !b64[n]) - lwsl_info(" e: %s: '%s'\n", enames[n], jwk->e[n].buf); - } - - return 0; -} - -static int -_lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, int len) -{ - e->buf = lws_malloc(len + 1, "jwk"); - if (!e->buf) - return -1; - - memcpy(e->buf, in, len); - e->buf[len] = '\0'; - e->len = len; - - return 0; -} - -static int -_lws_jwk_set_el_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len) -{ - int dec_size = lws_base64_size(len), n; - - e->buf = lws_malloc(dec_size, "jwk"); - if (!e->buf) - return -1; - - /* same decoder accepts both url or original styles */ - - n = lws_b64_decode_string_len(in, len, (char *)e->buf, dec_size - 1); - if (n < 0) - return -1; - e->len = n; - - return 0; -} - -static int -_lws_jwk_set_el_jwk_b64u(struct lws_gencrypto_keyelem *e, char *in, int len) -{ - int dec_size = lws_base64_size(len), n; - - e->buf = lws_malloc(dec_size, "jwk"); - if (!e->buf) - return -1; - - /* same decoder accepts both url or original styles */ - - n = lws_b64_decode_string_len(in, len, (char *)e->buf, dec_size - 1); - if (n < 0) - return -1; - e->len = n; - - return 0; -} - -void -lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m) -{ - int n; - - for (n = 0; n < m; n++) - if (el[n].buf) { - /* wipe all key material when it goes out of scope */ - lws_explicit_bzero(el[n].buf, el[n].len); - lws_free_set_NULL(el[n].buf); - el[n].len = 0; - } -} - -void -lws_jwk_destroy(struct lws_jwk *jwk) -{ - lws_jwk_destroy_elements(jwk->e, LWS_ARRAY_SIZE(jwk->e)); - lws_jwk_destroy_elements(jwk->meta, LWS_ARRAY_SIZE(jwk->meta)); -} - -static signed char -cb_jwk(struct lejp_ctx *ctx, char reason) -{ - struct lws_jwk_parse_state *jps = (struct lws_jwk_parse_state *)ctx->user; - struct lws_jwk *jwk = jps->jwk; - unsigned int idx, poss, n; - char dotstar[64]; - - if (reason == LEJPCB_VAL_STR_START) - jps->pos = 0; - - if (reason == LEJPCB_OBJECT_START && ctx->path_match == 0 + 1) - /* - * new keys[] member is starting - * - * Until we see some JSON names, it could be anything... - * there is no requirement for kty to be given first and eg, - * ACME specifies the keys must be ordered in lexographic - * order - where kty is not first. - */ - jps->possible = F_RSA | F_EC | F_OCT; - - if (reason == LEJPCB_OBJECT_END && ctx->path_match == 0 + 1) { - /* we completed parsing a key */ - if (jps->per_key_cb && jps->possible) { - if (jps->per_key_cb(jps->jwk, jps->user)) { - - lwsl_notice("%s: user cb halts import\n", - __func__); - - return -2; - } - - /* clear it down */ - lws_jwk_destroy(jps->jwk); - jps->possible = 0; - } - } - - if (reason == LEJPCB_COMPLETE) { - - /* - * Now we saw the whole jwk and know the key type, let'jwk insist - * that as a whole, it must be consistent and complete. - * - * The tracking of ->possible bits from even before we know the - * kty already makes certain we cannot have key element members - * defined that are inconsistent with the key type. - */ - - for (n = 0; n < LWS_ARRAY_SIZE(tok_map); n++) - /* - * All mandataory elements for the key type - * must be present - */ - if ((tok_map[n] & jps->possible) && ( - ((tok_map[n] & (F_M | F_META)) == (F_M | F_META) && - !jwk->meta[tok_map[n] & 0xff].buf) || - ((tok_map[n] & (F_M | F_META)) == F_M && - !jwk->e[tok_map[n] & 0xff].buf))) { - lwsl_notice("%s: missing %s\n", __func__, - jwk_tok[n]); - return -3; - } - - /* - * When the key may be public or public + private, ensure the - * intra-key members related to that are consistent. - * - * Only RSA keys need extra care, since EC keys are already - * confirmed by making CRV, X and Y mandatory and only D - * (the singular private part) optional. For RSA, N and E are - * also already known to be present using mandatory checking. - */ - - /* - * If a private key, it must have all D, P and Q. Public key - * must have none of them. - */ - if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && - !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) && - (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) && - (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) || - (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) - ) { - lwsl_notice("%s: RSA requires D, P and Q for private\n", - __func__); - return -3; - } - - /* - * If the precomputed private key terms appear, they must all - * appear together. - */ - if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && - !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf) && - (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) && - (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) || - (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) - ) { - lwsl_notice("%s: RSA DP, DQ, QI must all appear " - "or none\n", __func__); - return -3; - } - - /* - * The precomputed private key terms must not appear without - * the private key itself also appearing. - */ - if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && - !jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) { - lwsl_notice("%s: RSA DP, DQ, QI can appear only with " - "private key\n", __func__); - return -3; - } - - if ((jwk->kty == LWS_GENCRYPTO_KTY_RSA || - jwk->kty == LWS_GENCRYPTO_KTY_EC) && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) - jwk->private_key = 1; - } - - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - if (ctx->path_match == 0 + 1) - return 0; - - idx = tok_map[ctx->path_match - 1]; - if ((idx & 0xff) == 0xff) - return 0; - - switch (idx) { - /* note: kty is not necessarily first... we have to keep track of - * what could match given which element names have already been - * seen. Once kty comes, we confirm it'jwk still possible (ie, it'jwk - * not trying to tell us that it'jwk RSA now when we saw a "crv" - * earlier) and then reduce the possibilities to just the one that - * kty told. */ - case F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY: - - if (ctx->npos == 3 && !strncmp(ctx->buf, "oct", 3)) { - if (!(jps->possible & F_OCT)) - goto elements_mismatch; - jwk->kty = LWS_GENCRYPTO_KTY_OCT; - jps->possible = F_OCT; - goto cont; - } - if (ctx->npos == 3 && !strncmp(ctx->buf, "RSA", 3)) { - if (!(jps->possible & F_RSA)) - goto elements_mismatch; - jwk->kty = LWS_GENCRYPTO_KTY_RSA; - jps->possible = F_RSA; - goto cont; - } - if (ctx->npos == 2 && !strncmp(ctx->buf, "EC", 2)) { - if (!(jps->possible & F_EC)) - goto elements_mismatch; - jwk->kty = LWS_GENCRYPTO_KTY_EC; - jps->possible = F_EC; - goto cont; - } - lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar)); - lwsl_err("%s: Unknown KTY '%s'\n", __func__, dotstar); - return -1; - - default: -cont: - if (jps->pos + ctx->npos >= (int)sizeof(jps->b64)) - goto bail; - - memcpy(jps->b64 + jps->pos, ctx->buf, ctx->npos); - jps->pos += ctx->npos; - - if (reason == LEJPCB_VAL_STR_CHUNK) - return 0; - - /* chunking has been collated */ - - poss = idx & (F_RSA | F_EC | F_OCT); - jps->possible &= poss; - if (!jps->possible) - goto elements_mismatch; - - if (idx & F_META) { - if (_lws_jwk_set_el_jwk(&jwk->meta[idx & 0x7f], - jps->b64, jps->pos) < 0) - goto bail; - - break; - } - - if (idx & F_B64U) { - /* key data... do the base64 decode as needed */ - if (_lws_jwk_set_el_jwk_b64u(&jwk->e[idx & 0x7f], - jps->b64, jps->pos) < 0) - goto bail; - - if (jwk->e[idx & 0x7f].len > - LWS_JWE_LIMIT_KEY_ELEMENT_BYTES) { - lwsl_notice("%s: oversize keydata\n", __func__); - goto bail; - } - - return 0; - } - - if (idx & F_B64) { - - /* cert data... do non-urlcoded base64 decode */ - if (_lws_jwk_set_el_jwk_b64(&jwk->e[idx & 0x7f], - jps->b64, jps->pos) < 0) - goto bail; - return 0; - } - - if (_lws_jwk_set_el_jwk(&jwk->e[idx & 0x7f], - jps->b64, jps->pos) < 0) - goto bail; - break; - } - - return 0; - -elements_mismatch: - lwsl_err("%s: jwk elements mismatch\n", __func__); - -bail: - lwsl_err("%s: element failed\n", __func__); - - return -1; -} - -void -lws_jwk_init_jps(struct lejp_ctx *jctx, struct lws_jwk_parse_state *jps, - struct lws_jwk *jwk, lws_jwk_key_import_callback cb, - void *user) -{ - if (jwk) - memset(jwk, 0, sizeof(*jwk)); - - jps->jwk = jwk; - jps->possible = F_RSA | F_EC | F_OCT; - jps->per_key_cb = cb; - jps->user = user; - jps->pos = 0; - - lejp_construct(jctx, cb_jwk, jps, cb ? jwk_outer_tok: jwk_tok, - LWS_ARRAY_SIZE(jwk_tok)); -} - -int -lws_jwk_dup_oct(struct lws_jwk *jwk, const void *key, int len) -{ - jwk->e[LWS_GENCRYPTO_KTY_OCT].buf = lws_malloc(len, __func__); - if (!jwk->e[LWS_GENCRYPTO_KTY_OCT].buf) - return -1; - - jwk->kty = LWS_GENCRYPTO_KTY_OCT; - jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = len; - - memcpy(jwk->e[LWS_GENCRYPTO_KTY_OCT].buf, key, len); - - return 0; -} - -int -lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk, - enum lws_gencrypto_kty kty, int bits, const char *curve) -{ - size_t sn; - int n; - - memset(jwk, 0, sizeof(*jwk)); - - jwk->kty = kty; - jwk->private_key = 1; - - switch (kty) { - case LWS_GENCRYPTO_KTY_RSA: - { - struct lws_genrsa_ctx ctx; - - lwsl_notice("%s: generating %d bit RSA key\n", __func__, bits); - n = lws_genrsa_new_keypair(context, &ctx, LGRSAM_PKCS1_1_5, - jwk->e, bits); - lws_genrsa_destroy(&ctx); - if (n) { - lwsl_err("%s: problem generating RSA key\n", __func__); - return 1; - } - } - break; - case LWS_GENCRYPTO_KTY_OCT: - sn = lws_gencrypto_bits_to_bytes(bits); - jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = lws_malloc(sn, "oct"); - jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = sn; - if (lws_get_random(context, - jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, sn) != sn) { - lwsl_err("%s: problem getting random\n", __func__); - return 1; - } - break; - case LWS_GENCRYPTO_KTY_EC: - { - struct lws_genec_ctx ctx; - - if (!curve) { - lwsl_err("%s: must have a named curve\n", __func__); - - return 1; - } - - if (lws_genecdsa_create(&ctx, context, NULL)) - return 1; - - lwsl_notice("%s: generating ECDSA key on curve %s\n", __func__, - curve); - - n = lws_genecdsa_new_keypair(&ctx, curve, jwk->e); - lws_genec_destroy(&ctx); - if (n) { - lwsl_err("%s: problem generating ECDSA key\n", __func__); - return 1; - } - } - break; - - case LWS_GENCRYPTO_KTY_UNKNOWN: - default: - lwsl_err("%s: unknown kty\n", __func__); - return 1; - } - - return 0; -} - -int -lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user, - const char *in, size_t len) -{ - struct lejp_ctx jctx; - struct lws_jwk_parse_state jps; - int m; - - lws_jwk_init_jps(&jctx, &jps, jwk, cb, user); - - m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)in, len); - lejp_destruct(&jctx); - - if (m < 0) { - lwsl_notice("%s: parse got %d\n", __func__, m); - lws_jwk_destroy(jwk); - return -1; - } - - switch (jwk->kty) { - case LWS_GENCRYPTO_KTY_UNKNOWN: - lwsl_notice("%s: missing or unknown kyt\n", __func__); - lws_jwk_destroy(jwk); - return -1; - default: - break; - } - - return 0; -} - - -int -lws_jwk_export(struct lws_jwk *jwk, int flags, char *p, int *len) -{ - char *start = p, *end = &p[*len - 1]; - int n, m, limit, first = 1, asym = 0; - struct lexico *l; - - /* RFC7638 lexicographic order requires - * RSA: e -> kty -> n - * oct: k -> kty - * - * ie, meta and key data elements appear interleaved in name alpha order - */ - - p += lws_snprintf(p, end - p, "{"); - - switch (jwk->kty) { - case LWS_GENCRYPTO_KTY_OCT: - l = lexico_oct; - limit = LWS_ARRAY_SIZE(lexico_oct); - break; - case LWS_GENCRYPTO_KTY_RSA: - l = lexico_rsa; - limit = LWS_ARRAY_SIZE(lexico_rsa); - asym = 1; - break; - case LWS_GENCRYPTO_KTY_EC: - l = lexico_ec; - limit = LWS_ARRAY_SIZE(lexico_ec); - asym = 1; - break; - default: - return -1; - } - - for (n = 0; n < limit; n++) { - const char *q, *q_end; - char tok[12]; - int pos = 0, f = 1; - - if ((l->meta & 1) && (jwk->meta[l->idx].buf || - l->idx == (int)JWK_META_KTY)) { - - switch (l->idx) { - case JWK_META_KTY: - if (!first) - *p++ = ','; - first = 0; - p += lws_snprintf(p, end - p, "\"%s\":\"%s\"", - l->name, kty_names[jwk->kty]); - break; - case JWK_META_KEY_OPS: - if (!first) - *p++ = ','; - first = 0; - q = (const char *)jwk->meta[l->idx].buf; - q_end = q + jwk->meta[l->idx].len; - - p += lws_snprintf(p, end - p, - "\"%s\":[", l->name); - /* - * For the public version, usages that - * require the private part must be - * snipped - */ - - while (q < q_end) { - if (*q != ' ' && pos < (int)sizeof(tok) - 1) { - tok[pos++] = *q++; - if (q != q_end) - continue; - } - tok[pos] = '\0'; - pos = 0; - if ((flags & LWSJWKF_EXPORT_PRIVATE) || - !asym || (strcmp(tok, "sign") && - strcmp(tok, "encrypt"))) { - if (!f) - *p++ = ','; - f = 0; - p += lws_snprintf(p, end - p, - "\"%s\"", tok); - } - q++; - } - - *p++ = ']'; - - break; - - default: - /* both sig and enc require asym private key */ - if (!(flags & LWSJWKF_EXPORT_PRIVATE) && - asym && l->idx == (int)JWK_META_USE) - break; - if (!first) - *p++ = ','; - first = 0; - p += lws_snprintf(p, end - p, "\"%s\":\"", - l->name); - lws_strnncpy(p, (const char *)jwk->meta[l->idx].buf, - jwk->meta[l->idx].len, end - p); - p += strlen(p); - p += lws_snprintf(p, end - p, "\""); - break; - } - } - - if ((!(l->meta & 1)) && jwk->e[l->idx].buf && - ((flags & LWSJWKF_EXPORT_PRIVATE) || !(l->meta & 2))) { - if (!first) - *p++ = ','; - first = 0; - - p += lws_snprintf(p, end - p, "\"%s\":\"", l->name); - - if (jwk->kty == LWS_GENCRYPTO_KTY_EC && - l->idx == (int)LWS_GENCRYPTO_EC_KEYEL_CRV) { - lws_strnncpy(p, - (const char *)jwk->e[l->idx].buf, - jwk->e[l->idx].len, end - p); - m = strlen(p); - } else - m = lws_jws_base64_enc( - (const char *)jwk->e[l->idx].buf, - jwk->e[l->idx].len, p, end - p - 4); - if (m < 0) { - lwsl_notice("%s: enc failed\n", __func__); - return -1; - } - p += m; - p += lws_snprintf(p, end - p, "\""); - } - - l++; - } - - p += lws_snprintf(p, end - p, - (flags & LWSJWKF_EXPORT_NOCRLF) ? "}" : "}\n"); - - *len -= p - start; - - return p - start; -} - -int -lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32) -{ - struct lws_genhash_ctx hash_ctx; - int tmpsize = 2536, n; - char *tmp; - - tmp = lws_malloc(tmpsize, "rfc7638 tmp"); - - n = lws_jwk_export(jwk, LWSJWKF_EXPORT_NOCRLF, tmp, &tmpsize); - if (n < 0) - goto bail; - - if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256)) - goto bail; - - if (lws_genhash_update(&hash_ctx, tmp, n)) { - lws_genhash_destroy(&hash_ctx, NULL); - - goto bail; - } - lws_free(tmp); - - if (lws_genhash_destroy(&hash_ctx, digest32)) - return -1; - - return 0; - -bail: - lws_free(tmp); - - return -1; -} - -int -lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx, - const char *in, int len) -{ - jwk->meta[idx].buf = lws_malloc(len, __func__); - if (!jwk->meta[idx].buf) - return 1; - jwk->meta[idx].len = len; - memcpy(jwk->meta[idx].buf, in, len); - - return 0; -} - -int -lws_jwk_load(struct lws_jwk *jwk, const char *filename, - lws_jwk_key_import_callback cb, void *user) -{ - int buflen = 4096; - char *buf = lws_malloc(buflen, "jwk-load"); - int n; - - if (!buf) - return -1; - - n = lws_plat_read_file(filename, buf, buflen); - if (n < 0) - goto bail; - - n = lws_jwk_import(jwk, cb, user, buf, n); - lws_free(buf); - - return n; -bail: - lws_free(buf); - - return -1; -} - -int -lws_jwk_save(struct lws_jwk *jwk, const char *filename) -{ - int buflen = 4096; - char *buf = lws_malloc(buflen, "jwk-save"); - int n, m; - - if (!buf) - return -1; - - n = lws_jwk_export(jwk, LWSJWKF_EXPORT_PRIVATE, buf, &buflen); - if (n < 0) - goto bail; - - m = lws_plat_write_file(filename, buf, n); - - lws_free(buf); - if (m) - return -1; - - return 0; - -bail: - lws_free(buf); - - return -1; -} diff -Nru libwebsockets-4.0.20/lib/jose/jws/jose.c libwebsockets-2.4.2/lib/jose/jws/jose.c --- libwebsockets-4.0.20/lib/jose/jws/jose.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jws/jose.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,605 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * JOSE is actually specified as part of JWS RFC7515. JWE references RFC7515 - * to specify its JOSE JSON object. So it lives in ./lib/jose/jws/jose.c. - */ - -#include "private-lib-core.h" -#include "jose/private-lib-jose.h" - -#include - -static const char * const jws_jose[] = { - "alg", /* REQUIRED */ - "jku", - "jwk", - "kid", - "x5u", - "x5c", - "x5t", - "x5t#S256", - "typ", - "cty", - "crit", - - /* valid for JWE only below here */ - - "recipients[].header", - "recipients[].header.alg", - "recipients[].header.kid", - "recipients[].encrypted_key", - - "enc", - "zip", /* ("DEF" = deflate) */ - - "epk", /* valid for JWE ECDH only */ - "apu", /* valid for JWE ECDH only */ - "apv", /* valid for JWE ECDH only */ - "iv", /* valid for JWE AES only */ - "tag", /* valid for JWE AES only */ - "p2s", /* valid for JWE PBES2 only */ - "p2c" /* valid for JWE PBES2 only */ -}; - -struct jose_cb_args { - struct lws_jose *jose; - - struct lejp_ctx jwk_jctx; /* fake lejp context used to parse epk */ - struct lws_jwk_parse_state jps; /* fake jwk parse state */ - - char *temp; - int *temp_len; - - unsigned int is_jwe; - unsigned int recipients_array; - - int recip; -}; - -/* - * JWE A.4.7 Complete JWE JSON Serialization example - * - * LEJPCB_CONSTRUCTED - * LEJPCB_START - * LEJPCB_OBJECT_START - * - * protected LEJPCB_PAIR_NAME - * protected LEJPCB_VAL_STR_START - * protected LEJPCB_VAL_STR_END - * - * unprotected LEJPCB_PAIR_NAME - * unprotected LEJPCB_OBJECT_START - * unprotected.jku LEJPCB_PAIR_NAME - * unprotected.jku LEJPCB_VAL_STR_START - * unprotected.jku LEJPCB_VAL_STR_END - * unprotected.jku LEJPCB_OBJECT_END - * - * recipients LEJPCB_PAIR_NAME - * recipients[] LEJPCB_ARRAY_START - * - * recipients[] LEJPCB_OBJECT_START - * recipients[].header LEJPCB_PAIR_NAME - * recipients[].header LEJPCB_OBJECT_START - * recipients[].header.alg LEJPCB_PAIR_NAME - * recipients[].header.alg LEJPCB_VAL_STR_START - * recipients[].header.alg LEJPCB_VAL_STR_END - * recipients[].header.kid LEJPCB_PAIR_NAME - * recipients[].header.kid LEJPCB_VAL_STR_START - * recipients[].header.kid LEJPCB_VAL_STR_END - * recipients[] LEJPCB_OBJECT_END - * recipients[].encrypted_key LEJPCB_PAIR_NAME - * recipients[].encrypted_key LEJPCB_VAL_STR_START - * recipients[].encrypted_key LEJPCB_VAL_STR_CHUNK - * recipients[].encrypted_key LEJPCB_VAL_STR_END - * recipients[] LEJPCB_OBJECT_END (ctx->sp = 1) - * - * recipients[] LEJPCB_OBJECT_START - * recipients[].header LEJPCB_PAIR_NAME - * recipients[].header LEJPCB_OBJECT_START - * recipients[].header.alg LEJPCB_PAIR_NAME - * recipients[].header.alg LEJPCB_VAL_STR_START - * recipients[].header.alg LEJPCB_VAL_STR_END - * recipients[].header.kid LEJPCB_PAIR_NAME - * recipients[].header.kid LEJPCB_VAL_STR_START - * recipients[].header.kid LEJPCB_VAL_STR_END - * recipients[] LEJPCB_OBJECT_END - * recipients[].encrypted_key LEJPCB_PAIR_NAME - * recipients[].encrypted_key LEJPCB_VAL_STR_START - * recipients[].encrypted_key LEJPCB_VAL_STR_END - * recipients[] LEJPCB_OBJECT_END (ctx->sp = 1) - * - * recipients[] LEJPCB_ARRAY_END - * - * iv LEJPCB_PAIR_NAME - * iv LEJPCB_VAL_STR_START - * iv LEJPCB_VAL_STR_END - * ciphertext LEJPCB_PAIR_NAME - * ciphertext LEJPCB_VAL_STR_START - * ciphertext LEJPCB_VAL_STR_END - * tag LEJPCB_PAIR_NAME - * tag LEJPCB_VAL_STR_START - * tag LEJPCB_VAL_STR_END - * - * tag LEJPCB_OBJECT_END - * tag LEJPCB_COMPLETE - * tag LEJPCB_DESTRUCTED - * - */ - -/* - * RFC7516 7.2.2 - * - * Note that when using the flattened syntax, just as when using the - * general syntax, any unprotected Header Parameter values can reside in - * either the "unprotected" member or the "header" member, or in both. - */ - -static signed char -lws_jws_jose_cb(struct lejp_ctx *ctx, char reason) -{ - struct jose_cb_args *args = (struct jose_cb_args *)ctx->user; - int n; //, dest; - - /* - * In JOSE JSON, the element "epk" contains a fully-formed JWK. - * - * For JOSE paths beginning "epk.", we pass them through to a JWK - * LEJP subcontext to parse using the JWK parser directly. - */ - - if (args->is_jwe && !strncmp(ctx->path, "epk.", 4)) { - memcpy(args->jwk_jctx.path, ctx->path + 4, - sizeof(ctx->path) - 4); - memcpy(args->jwk_jctx.buf, ctx->buf, ctx->npos); - args->jwk_jctx.npos = ctx->npos; - - if (!ctx->path_match) - args->jwk_jctx.path_match = 0; - lejp_check_path_match(&args->jwk_jctx); - - if (args->jwk_jctx.path_match) - args->jwk_jctx.pst[args->jwk_jctx.pst_sp]. - callback(&args->jwk_jctx, reason); - } - - // lwsl_notice("%s: %s %d (%d)\n", __func__, ctx->path, reason, ctx->sp); - - /* at the end of each recipients[] entry, bump recipients count */ - - if (args->is_jwe && reason == LEJPCB_OBJECT_END && ctx->sp == 1 && - !strcmp(ctx->path, "recipients[]")) - args->jose->recipients++; - - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - //dest = ctx->path_match - 1; - - switch (ctx->path_match - 1) { - - /* strings */ - - case LJJHI_ALG: /* REQUIRED */ - - /* - * look up whether we support this alg and point the caller at - * its definition if so - */ - - if (!args->is_jwe && - lws_gencrypto_jws_alg_to_definition(ctx->buf, - &args->jose->alg)) { - lwsl_notice("%s: unknown alg '%s'\n", __func__, - ctx->buf); - - return -1; - } - - if (args->is_jwe && - lws_gencrypto_jwe_alg_to_definition(ctx->buf, - &args->jose->alg)) { - lwsl_notice("%s: unknown JWE alg '%s'\n", __func__, - ctx->buf); - - return -1; - } - - return 0; - - case LJJHI_TYP: /* Optional: string: media type */ - lws_strnncpy(args->jose->typ, ctx->buf, ctx->npos, - sizeof(args->jose->typ)); - break; - - case LJJHI_JKU: /* Optional: string */ - case LJJHI_KID: /* Optional: string */ - case LJJHI_X5U: /* Optional: string: url of public key cert / chain */ - case LJJHI_CTY: /* Optional: string: content media type */ - - /* base64 */ - - case LJJHI_X5C: /* Optional: base64 (NOT -url): actual cert */ - - /* base64-url */ - - case LJJHI_X5T: /* Optional: base64url: SHA-1 of actual cert */ - case LJJHI_X5T_S256: /* Optional: base64url: SHA-256 of actual cert */ - - /* array of strings */ - - case LJJHI_CRIT: /* Optional for send, REQUIRED: array of strings: - * mustn't contain standardized strings or null set */ - break; - - /* jwk child */ - - case LJJHI_JWK: /* Optional: jwk JSON object: public key: */ - - /* past here, JWE only */ - - case LJJHI_RECIPS_HDR: - if (!args->is_jwe) { - lwsl_info("%s: recipients in jws\n", __func__); - return -1; - } - args->recipients_array = 1; - break; - - case LJJHI_RECIPS_HDR_ALG: - case LJJHI_RECIPS_HDR_KID: - break; - - case LJJHI_RECIPS_EKEY: - if (!args->is_jwe) { - lwsl_info("%s: recipients in jws\n", __func__); - return -1; - } - args->recipients_array = 1; - //dest = ; - goto append_string; - - case LJJHI_ENC: /* JWE only: Mandatory: string */ - if (!args->is_jwe) { - lwsl_info("%s: enc in jws\n", __func__); - return -1; - } - if (lws_gencrypto_jwe_enc_to_definition(ctx->buf, - &args->jose->enc_alg)) { - lwsl_notice("%s: unknown enc '%s'\n", __func__, - ctx->buf); - - return -1; - } - break; - - case LJJHI_ZIP: /* JWE only: Optional: string ("DEF" = deflate) */ - if (!args->is_jwe) - return -1; - goto append_string; - - case LJJHI_EPK: /* Additional arg for JWE ECDH */ - if (!args->is_jwe) - return -1; - /* Ephemeral key... this JSON subsection is actually a JWK */ - lwsl_err("LJJHI_EPK\n"); - break; - - case LJJHI_APU: /* Additional arg for JWE ECDH */ - if (!args->is_jwe) - return -1; - /* Agreement Party U */ - goto append_string; - - case LJJHI_APV: /* Additional arg for JWE ECDH */ - if (!args->is_jwe) - return -1; - /* Agreement Party V */ - goto append_string; - - case LJJHI_IV: /* Additional arg for JWE AES */ - if (!args->is_jwe) - return -1; - goto append_string; - - case LJJHI_TAG: /* Additional arg for JWE AES */ - if (!args->is_jwe) - return -1; - goto append_string; - - case LJJHI_P2S: /* Additional arg for JWE PBES2 */ - if (!args->is_jwe) - return -1; - goto append_string; - case LJJHI_P2C: /* Additional arg for JWE PBES2 */ - if (!args->is_jwe) - return -1; - goto append_string; - - /* ignore what we don't understand */ - - default: - return 0; - } - - return 0; - -append_string: - - if (*args->temp_len < ctx->npos) { - lwsl_err("%s: out of parsing space\n", __func__); - return -1; - } - - if (!args->jose->e[ctx->path_match - 1].buf) { - args->jose->e[ctx->path_match - 1].buf = (uint8_t *)args->temp; - args->jose->e[ctx->path_match - 1].len = 0; - } - - memcpy(args->temp, ctx->buf, ctx->npos); - args->temp += ctx->npos; - *args->temp_len -= ctx->npos; - args->jose->e[ctx->path_match - 1].len += ctx->npos; - - if (reason == LEJPCB_VAL_STR_END) { - n = lws_b64_decode_string_len( - (const char *)args->jose->e[ctx->path_match - 1].buf, - args->jose->e[ctx->path_match - 1].len, - (char *)args->jose->e[ctx->path_match - 1].buf, - args->jose->e[ctx->path_match - 1].len + 1); - if (n < 0) { - lwsl_err("%s: b64 decode failed\n", __func__); - return -1; - } - - args->temp -= args->jose->e[ctx->path_match - 1].len - n - 1; - *args->temp_len += - args->jose->e[ctx->path_match - 1].len - n - 1; - - args->jose->e[ctx->path_match - 1].len = n; - } - - return 0; -} - -void -lws_jose_init(struct lws_jose *jose) -{ - memset(jose, 0, sizeof(*jose)); -} - -static void -lws_jose_recip_destroy(struct lws_jws_recpient *r) -{ - lws_jwk_destroy(&r->jwk_ephemeral); - lws_jwk_destroy(&r->jwk); -} - -void -lws_jose_destroy(struct lws_jose *jose) -{ - int n; - - for (n = 0; n < (int)LWS_ARRAY_SIZE(jose->recipient); n++) - lws_jose_recip_destroy(&jose->recipient[n]); -} - - -static int -lws_jose_parse(struct lws_jose *jose, const uint8_t *buf, int n, - char *temp, int *temp_len, int is_jwe) -{ - struct lejp_ctx jctx; - struct jose_cb_args args; - int m; - - if (is_jwe) - /* prepare a context for JOSE epk ephemeral jwk parsing */ - lws_jwk_init_jps(&args.jwk_jctx, &args.jps, - &jose->recipient[jose->recipients].jwk_ephemeral, - NULL, NULL); - - args.is_jwe = is_jwe; - args.temp = temp; - args.temp_len = temp_len; - args.jose = jose; - args.recip = 0; - args.recipients_array = 0; - jose->recipients = 0; - - lejp_construct(&jctx, lws_jws_jose_cb, &args, jws_jose, - LWS_ARRAY_SIZE(jws_jose)); - - m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, n); - lejp_destruct(&jctx); - if (m < 0) { - lwsl_notice("%s: parse returned %d\n", __func__, m); - return -1; - } - - if (!args.recipients_array && jose->recipient[0].unprot[LJJHI_ALG].buf) - /* if no explicit recipients[], we got one */ - jose->recipients++; - - return 0; -} - -int -lws_jws_parse_jose(struct lws_jose *jose, - const char *buf, int len, char *temp, int *temp_len) -{ - return lws_jose_parse(jose, (const uint8_t *)buf, len, - temp, temp_len, 0); -} - -int -lws_jwe_parse_jose(struct lws_jose *jose, - const char *buf, int len, char *temp, int *temp_len) -{ - return lws_jose_parse(jose, - (const uint8_t *)buf, len, temp, temp_len, 1); -} - -int -lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk, - char *out, size_t out_len) -{ - struct lws_jwk *jwk; - char *end = out + out_len - 1; - int n, m, f, sub = 0, vl; - - /* JOSE requires an alg */ - if (!jose->alg || !jose->alg->alg) - goto bail; - - *out++ = '{'; - - for (n = 0; n < LWS_COUNT_JOSE_HDR_ELEMENTS; n++) { - switch (n) { - - /* strings */ - - case LJJHI_ALG: /* REQUIRED */ - case LJJHI_JKU: /* Optional: string */ - case LJJHI_KID: /* Optional: string */ - case LJJHI_TYP: /* Optional: string: media type */ - case LJJHI_CTY: /* Optional: string: content media type */ - case LJJHI_X5U: /* Optional: string: pubkey cert / chain URL */ - case LJJHI_ENC: /* JWE only: Optional: string */ - case LJJHI_ZIP: /* JWE only: Optional: string ("DEF"=deflate) */ - if (jose->e[n].buf) { - out += lws_snprintf(out, end - out, - "%s\"%s\":\"%s\"", sub ? ",\n" : "", - jws_jose[n], jose->e[n].buf); - sub = 1; - } - break; - - case LJJHI_X5T: /* Optional: base64url: SHA-1 of actual cert */ - case LJJHI_X5T_S256: /* Optional: base64url: SHA-256 of cert */ - case LJJHI_APU: /* Additional arg for JWE ECDH: b64url */ - case LJJHI_APV: /* Additional arg for JWE ECDH: b64url */ - case LJJHI_IV: /* Additional arg for JWE AES: b64url */ - case LJJHI_TAG: /* Additional arg for JWE AES: b64url */ - case LJJHI_P2S: /* Additional arg for JWE PBES2: b64url: salt */ - if (jose->e[n].buf) { - out += lws_snprintf(out, end - out, - "%s\"%s\":\"", sub ? ",\n" : "", - jws_jose[n]); - sub = 1; - m = lws_b64_encode_string_url((const char *) - jose->e[n].buf, jose->e[n].len, - out, end - out); - if (m < 0) - return -1; - out += m; - out += lws_snprintf(out, end - out, "\""); - } - break; - - case LJJHI_P2C: /* Additional arg for JWE PBES2: int: count */ - break; /* don't support atm */ - - case LJJHI_X5C: /* Optional: base64 (NOT -url): actual cert */ - if (jose->e[n].buf) { - out += lws_snprintf(out, end - out, - "%s\"%s\":\"", sub ? ",\n" : "", - jws_jose[n]); - sub = 1; - m = lws_b64_encode_string((const char *) - jose->e[n].buf, jose->e[n].len, - out, end - out); - if (m < 0) - return -1; - out += m; - out += lws_snprintf(out, end - out, "\""); - } - break; - - case LJJHI_EPK: /* Additional arg for JWE ECDH: eph pubkey */ - case LJJHI_JWK: /* Optional: jwk JSON object: public key: */ - - jwk = n == LJJHI_EPK ? &jose->recipient[0].jwk_ephemeral : aux_jwk; - if (!jwk || !jwk->kty) - break; - - out += lws_snprintf(out, end - out, "%s\"%s\":", - sub ? ",\n" : "", jws_jose[n]); - sub = 1; - vl = end - out; - m = lws_jwk_export(jwk, 0, out, &vl); - if (m < 0) { - lwsl_notice("%s: failed to export key\n", - __func__); - - return -1; - } - out += m; - break; - - case LJJHI_CRIT:/* Optional for send, REQUIRED: array of strings: - * mustn't contain standardized strings or null set */ - if (!jose->e[n].buf) - break; - - out += lws_snprintf(out, end - out, - "%s\"%s\":[", sub ? ",\n" : "", jws_jose[n]); - sub = 1; - - m = 0; - f = 1; - while ((unsigned int)m < jose->e[n].len && (end - out) > 1) { - if (jose->e[n].buf[m] == ' ') { - if (!f) - *out++ = '\"'; - - m++; - f = 1; - continue; - } - - if (f) { - if (m) - *out++ = ','; - *out++ = '\"'; - f = 0; - } - - *out++ = jose->e[n].buf[m]; - m++; - } - - break; - } - } - - *out++ = '}'; - - if (out > end - 2) - return -1; - - return out_len - (end - out) - 1; - -bail: - return -1; -} diff -Nru libwebsockets-4.0.20/lib/jose/jws/jws.c libwebsockets-2.4.2/lib/jose/jws/jws.c --- libwebsockets-4.0.20/lib/jose/jws/jws.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jws/jws.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,944 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-jose-jws.h" - -/* - * Currently only support flattened or compact (implicitly single signature) - */ - -static const char * const jws_json[] = { - "protected", /* base64u */ - "header", /* JSON */ - "payload", /* base64u payload */ - "signature", /* base64u signature */ - - //"signatures[].protected", - //"signatures[].header", - //"signatures[].signature" -}; - -enum lws_jws_json_tok { - LJWSJT_PROTECTED, - LJWSJT_HEADER, - LJWSJT_PAYLOAD, - LJWSJT_SIGNATURE, - - // LJWSJT_SIGNATURES_PROTECTED, - // LJWSJT_SIGNATURES_HEADER, - // LJWSJT_SIGNATURES_SIGNATURE, -}; - -/* parse a JWS complete or flattened JSON object */ - -struct jws_cb_args { - struct lws_jws *jws; - - char *temp; - int *temp_len; -}; - -static signed char -lws_jws_json_cb(struct lejp_ctx *ctx, char reason) -{ - struct jws_cb_args *args = (struct jws_cb_args *)ctx->user; - int n, m; - - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - switch (ctx->path_match - 1) { - - /* strings */ - - case LJWSJT_PROTECTED: /* base64u: JOSE: must contain 'alg' */ - m = LJWS_JOSE; - goto append_string; - case LJWSJT_PAYLOAD: /* base64u */ - m = LJWS_PYLD; - goto append_string; - case LJWSJT_SIGNATURE: /* base64u */ - m = LJWS_SIG; - goto append_string; - - case LJWSJT_HEADER: /* unprotected freeform JSON */ - break; - - default: - return -1; - } - - return 0; - -append_string: - - if (*args->temp_len < ctx->npos) { - lwsl_err("%s: out of parsing space\n", __func__); - return -1; - } - - /* - * We keep both b64u and decoded in temp mapped using map / map_b64, - * the jws signature is actually over the b64 content not the plaintext, - * and we can't do it until we see the protected alg. - */ - - if (!args->jws->map_b64.buf[m]) { - args->jws->map_b64.buf[m] = args->temp; - args->jws->map_b64.len[m] = 0; - } - - memcpy(args->temp, ctx->buf, ctx->npos); - args->temp += ctx->npos; - *args->temp_len -= ctx->npos; - args->jws->map_b64.len[m] += ctx->npos; - - if (reason == LEJPCB_VAL_STR_END) { - args->jws->map.buf[m] = args->temp; - - n = lws_b64_decode_string_len( - (const char *)args->jws->map_b64.buf[m], - args->jws->map_b64.len[m], - (char *)args->temp, *args->temp_len); - if (n < 0) { - lwsl_err("%s: b64 decode failed: in len %d, m %d\n", __func__, (int)args->jws->map_b64.len[m], m); - return -1; - } - - args->temp += n; - *args->temp_len -= n; - args->jws->map.len[m] = n; - } - - return 0; -} - -static int -lws_jws_json_parse(struct lws_jws *jws, const uint8_t *buf, int len, - char *temp, int *temp_len) -{ - struct jws_cb_args args; - struct lejp_ctx jctx; - int m = 0; - - args.jws = jws; - args.temp = temp; - args.temp_len = temp_len; - - lejp_construct(&jctx, lws_jws_json_cb, &args, jws_json, - LWS_ARRAY_SIZE(jws_json)); - - m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, len); - lejp_destruct(&jctx); - if (m < 0) { - lwsl_notice("%s: parse returned %d\n", __func__, m); - return -1; - } - - return 0; -} - -void -lws_jws_init(struct lws_jws *jws, struct lws_jwk *jwk, - struct lws_context *context) -{ - memset(jws, 0, sizeof(*jws)); - jws->context = context; - jws->jwk = jwk; -} - -static void -lws_jws_map_bzero(struct lws_jws_map *map) -{ - int n; - - /* no need to scrub first jose header element (it can be canned then) */ - - for (n = 1; n < LWS_JWS_MAX_COMPACT_BLOCKS; n++) - if (map->buf[n]) - lws_explicit_bzero((void *)map->buf[n], map->len[n]); -} - -void -lws_jws_destroy(struct lws_jws *jws) -{ - lws_jws_map_bzero(&jws->map); - jws->jwk = NULL; -} - -int -lws_jws_dup_element(struct lws_jws_map *map, int idx, char *temp, int *temp_len, - const void *in, size_t in_len, size_t actual_alloc) -{ - if (!actual_alloc) - actual_alloc = in_len; - - if ((size_t)*temp_len < actual_alloc) - return -1; - - memcpy(temp, in, in_len); - - map->len[idx] = in_len; - map->buf[idx] = temp; - - *temp_len -= actual_alloc; - - return 0; -} - -int -lws_jws_encode_b64_element(struct lws_jws_map *map, int idx, - char *temp, int *temp_len, const void *in, - size_t in_len) -{ - int n; - - if (*temp_len < lws_base64_size((int)in_len)) - return -1; - - n = lws_jws_base64_enc(in, in_len, temp, *temp_len); - if (n < 0) - return -1; - - map->len[idx] = n; - map->buf[idx] = temp; - - *temp_len -= n; - - return 0; -} - -int -lws_jws_randomize_element(struct lws_context *context, struct lws_jws_map *map, - int idx, char *temp, int *temp_len, size_t random_len, - size_t actual_alloc) -{ - if (!actual_alloc) - actual_alloc = random_len; - - if ((size_t)*temp_len < actual_alloc) - return -1; - - map->len[idx] = random_len; - map->buf[idx] = temp; - - if (lws_get_random(context, temp, random_len) != random_len) { - lwsl_err("Problem getting random\n"); - return -1; - } - - *temp_len -= actual_alloc; - - return 0; -} - -int -lws_jws_alloc_element(struct lws_jws_map *map, int idx, char *temp, - int *temp_len, size_t len, size_t actual_alloc) -{ - if (!actual_alloc) - actual_alloc = len; - - if ((size_t)*temp_len < actual_alloc) - return -1; - - map->len[idx] = len; - map->buf[idx] = temp; - *temp_len -= actual_alloc; - - return 0; -} - -int -lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max) -{ - int n; - - n = lws_b64_encode_string_url(in, in_len, out, out_max - 1); - if (n < 0) { - lwsl_notice("%s: in len %d too large for %d out buf\n", - __func__, (int)in_len, (int)out_max); - return n; /* too large for output buffer */ - } - - /* trim the terminal = */ - while (n && out[n - 1] == '=') - n--; - - out[n] = '\0'; - - return n; -} - -int -lws_jws_b64_compact_map(const char *in, int len, struct lws_jws_map *map) -{ - int me = 0; - - memset(map, 0, sizeof(*map)); - - map->buf[me] = (char *)in; - map->len[me] = 0; - - while (len--) { - if (*in++ == '.') { - if (++me == LWS_JWS_MAX_COMPACT_BLOCKS) - return -1; - map->buf[me] = (char *)in; - map->len[me] = 0; - continue; - } - map->len[me]++; - } - - return me + 1; -} - -/* b64 in, map contains decoded elements, if non-NULL, - * map_b64 set to b64 elements - */ - -int -lws_jws_compact_decode(const char *in, int len, struct lws_jws_map *map, - struct lws_jws_map *map_b64, char *out, - int *out_len) -{ - int blocks, n, m = 0; - - if (!map_b64) - map_b64 = map; - - memset(map_b64, 0, sizeof(*map_b64)); - memset(map, 0, sizeof(*map)); - - blocks = lws_jws_b64_compact_map(in, len, map_b64); - - if (blocks > LWS_JWS_MAX_COMPACT_BLOCKS) - return -1; - - while (m < blocks) { - n = lws_b64_decode_string_len(map_b64->buf[m], map_b64->len[m], - out, *out_len); - if (n < 0) { - lwsl_err("%s: b64 decode failed\n", __func__); - return -1; - } - /* replace the map entry with the decoded content */ - if (n) - map->buf[m] = out; - else - map->buf[m] = NULL; - map->len[m++] = n; - out += n; - *out_len -= n; - - if (*out_len < 1) - return -1; - } - - return blocks; -} - -static int -lws_jws_compact_decode_map(struct lws_jws_map *map_b64, struct lws_jws_map *map, - char *out, int *out_len) -{ - int n, m = 0; - - for (n = 0; n < LWS_JWS_MAX_COMPACT_BLOCKS; n++) { - n = lws_b64_decode_string_len(map_b64->buf[m], map_b64->len[m], - out, *out_len); - if (n < 0) { - lwsl_err("%s: b64 decode failed\n", __func__); - return -1; - } - /* replace the map entry with the decoded content */ - map->buf[m] = out; - map->len[m++] = n; - out += n; - *out_len -= n; - - if (*out_len < 1) - return -1; - } - - return 0; -} - -int -lws_jws_encode_section(const char *in, size_t in_len, int first, char **p, - char *end) -{ - int n, len = (end - *p) - 1; - char *p_entry = *p; - - if (len < 3) - return -1; - - if (!first) - *(*p)++ = '.'; - - n = lws_jws_base64_enc(in, in_len, *p, len - 1); - if (n < 0) - return -1; - - *p += n; - - return (*p) - p_entry; -} - -int -lws_jws_compact_encode(struct lws_jws_map *map_b64, /* b64-encoded */ - const struct lws_jws_map *map, /* non-b64 */ - char *buf, int *len) -{ - int n, m; - - for (n = 0; n < LWS_JWS_MAX_COMPACT_BLOCKS; n++) { - if (!map->buf[n]) { - map_b64->buf[n] = NULL; - map_b64->len[n] = 0; - continue; - } - m = lws_jws_base64_enc(map->buf[n], map->len[n], buf, *len); - if (m < 0) - return -1; - buf += m; - *len -= m; - if (*len < 1) - return -1; - } - - return 0; -} - -/* - * This takes both a base64 -encoded map and a plaintext map. - * - * JWS demands base-64 encoded elements for hash computation and at least for - * the JOSE header and signature, decoded versions too. - */ - -int -lws_jws_sig_confirm(struct lws_jws_map *map_b64, struct lws_jws_map *map, - struct lws_jwk *jwk, struct lws_context *context) -{ - enum enum_genrsa_mode padding = LGRSAM_PKCS1_1_5; - char temp[256]; - int n, h_len, b = 3, temp_len = sizeof(temp); - uint8_t digest[LWS_GENHASH_LARGEST]; - struct lws_genhash_ctx hash_ctx; - struct lws_genec_ctx ecdsactx; - struct lws_genrsa_ctx rsactx; - struct lws_genhmac_ctx ctx; - struct lws_jose jose; - - lws_jose_init(&jose); - - /* only valid if no signature or key */ - if (!map_b64->buf[LJWS_SIG] && !map->buf[LJWS_UHDR]) - b = 2; - - if (lws_jws_parse_jose(&jose, map->buf[LJWS_JOSE], map->len[LJWS_JOSE], - temp, &temp_len) < 0 || !jose.alg) { - lwsl_notice("%s: parse failed\n", __func__); - return -1; - } - - if (!strcmp(jose.alg->alg, "none")) { - /* "none" compact serialization has 2 blocks: jose.payload */ - if (b != 2 || jwk) - return -1; - - /* the lack of a key matches the lack of a signature */ - return 0; - } - - /* all other have 3 blocks: jose.payload.sig */ - if (b != 3 || !jwk) { - lwsl_notice("%s: %d blocks\n", __func__, b); - return -1; - } - - switch (jose.alg->algtype_signing) { - case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS: - case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP: - padding = LGRSAM_PKCS1_OAEP_PSS; - /* fallthru */ - case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5: - - /* RSASSA-PKCS1-v1_5 or OAEP using SHA-256/384/512 */ - - if (jwk->kty != LWS_GENCRYPTO_KTY_RSA) - return -1; - - /* 6(RSA): compute the hash of the payload into "digest" */ - - if (lws_genhash_init(&hash_ctx, jose.alg->hash_type)) - return -1; - - /* - * JWS Signing Input value: - * - * BASE64URL(UTF8(JWS Protected Header)) || '.' || - * BASE64URL(JWS Payload) - */ - - if (lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_JOSE], - map_b64->len[LJWS_JOSE]) || - lws_genhash_update(&hash_ctx, ".", 1) || - lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_PYLD], - map_b64->len[LJWS_PYLD]) || - lws_genhash_destroy(&hash_ctx, digest)) { - lws_genhash_destroy(&hash_ctx, NULL); - - return -1; - } - // h_len = lws_genhash_size(jose.alg->hash_type); - - if (lws_genrsa_create(&rsactx, jwk->e, context, padding, - LWS_GENHASH_TYPE_UNKNOWN)) { - lwsl_notice("%s: lws_genrsa_public_decrypt_create\n", - __func__); - return -1; - } - - n = lws_genrsa_hash_sig_verify(&rsactx, digest, - jose.alg->hash_type, - (uint8_t *)map->buf[LJWS_SIG], - map->len[LJWS_SIG]); - - lws_genrsa_destroy(&rsactx); - if (n < 0) { - lwsl_notice("%s: decrypt fail\n", __func__); - return -1; - } - - break; - - case LWS_JOSE_ENCTYPE_NONE: /* HSxxx */ - - /* SHA256/384/512 HMAC */ - - h_len = lws_genhmac_size(jose.alg->hmac_type); - - /* 6) compute HMAC over payload */ - - if (lws_genhmac_init(&ctx, jose.alg->hmac_type, - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf, - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].len)) - return -1; - - /* - * JWS Signing Input value: - * - * BASE64URL(UTF8(JWS Protected Header)) || '.' || - * BASE64URL(JWS Payload) - */ - - if (lws_genhmac_update(&ctx, map_b64->buf[LJWS_JOSE], - map_b64->len[LJWS_JOSE]) || - lws_genhmac_update(&ctx, ".", 1) || - lws_genhmac_update(&ctx, map_b64->buf[LJWS_PYLD], - map_b64->len[LJWS_PYLD]) || - lws_genhmac_destroy(&ctx, digest)) { - lws_genhmac_destroy(&ctx, NULL); - - return -1; - } - - /* 7) Compare the computed and decoded hashes */ - - if (lws_timingsafe_bcmp(digest, map->buf[2], h_len)) { - lwsl_notice("digest mismatch\n"); - - return -1; - } - - break; - - case LWS_JOSE_ENCTYPE_ECDSA: - - /* ECDSA using SHA-256/384/512 */ - - /* Confirm the key coming in with this makes sense */ - - /* has to be an EC key :-) */ - if (jwk->kty != LWS_GENCRYPTO_KTY_EC) - return -1; - - /* key must state its curve */ - if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf) - return -1; - - /* key must match the selected alg curve */ - if (strcmp((const char *)jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, - jose.alg->curve_name)) - return -1; - - /* - * JWS Signing Input value: - * - * BASE64URL(UTF8(JWS Protected Header)) || '.' || - * BASE64URL(JWS Payload) - * - * Validating the JWS Signature is a bit different from the - * previous examples. We need to split the 64 member octet - * sequence of the JWS Signature (which is base64url decoded - * from the value encoded in the JWS representation) into two - * 32 octet sequences, the first representing R and the second - * S. We then pass the public key (x, y), the signature (R, S), - * and the JWS Signing Input (which is the initial substring of - * the JWS Compact Serialization representation up until but not - * including the second period character) to an ECDSA signature - * verifier that has been configured to use the P-256 curve with - * the SHA-256 hash function. - */ - - if (lws_genhash_init(&hash_ctx, jose.alg->hash_type) || - lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_JOSE], - map_b64->len[LJWS_JOSE]) || - lws_genhash_update(&hash_ctx, ".", 1) || - lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_PYLD], - map_b64->len[LJWS_PYLD]) || - lws_genhash_destroy(&hash_ctx, digest)) { - lws_genhash_destroy(&hash_ctx, NULL); - - return -1; - } - - h_len = lws_genhash_size(jose.alg->hash_type); - - if (lws_genecdsa_create(&ecdsactx, context, NULL)) { - lwsl_notice("%s: lws_genrsa_public_decrypt_create\n", - __func__); - return -1; - } - - if (lws_genecdsa_set_key(&ecdsactx, jwk->e)) { - lws_genec_destroy(&ecdsactx); - lwsl_notice("%s: ec key import fail\n", __func__); - return -1; - } - - n = lws_genecdsa_hash_sig_verify_jws(&ecdsactx, digest, - jose.alg->hash_type, - jose.alg->keybits_fixed, - (uint8_t *)map->buf[LJWS_SIG], - map->len[LJWS_SIG]); - lws_genec_destroy(&ecdsactx); - if (n < 0) { - lwsl_notice("%s: verify fail\n", __func__); - return -1; - } - - break; - - default: - lwsl_err("%s: unknown alg from jose\n", __func__); - return -1; - } - - return 0; -} - -/* it's already a b64 map, we will make a temp plain version */ - -int -lws_jws_sig_confirm_compact_b64_map(struct lws_jws_map *map_b64, - struct lws_jwk *jwk, - struct lws_context *context, - char *temp, int *temp_len) -{ - struct lws_jws_map map; - int n; - - n = lws_jws_compact_decode_map(map_b64, &map, temp, temp_len); - if (n > 3 || n < 0) - return -1; - - return lws_jws_sig_confirm(map_b64, &map, jwk, context); -} - -/* - * it's already a compact / concatenated b64 string, we will make a temp - * plain version - */ - -int -lws_jws_sig_confirm_compact_b64(const char *in, size_t len, - struct lws_jws_map *map, struct lws_jwk *jwk, - struct lws_context *context, - char *temp, int *temp_len) -{ - struct lws_jws_map map_b64; - int n; - - if (lws_jws_b64_compact_map(in, len, &map_b64) < 0) - return -1; - - n = lws_jws_compact_decode(in, len, map, &map_b64, temp, temp_len); - if (n > 3 || n < 0) - return -1; - - return lws_jws_sig_confirm(&map_b64, map, jwk, context); -} - -/* it's already plain, we will make a temp b64 version */ - -int -lws_jws_sig_confirm_compact(struct lws_jws_map *map, struct lws_jwk *jwk, - struct lws_context *context, char *temp, - int *temp_len) -{ - struct lws_jws_map map_b64; - - if (lws_jws_compact_encode(&map_b64, map, temp, temp_len) < 0) - return -1; - - return lws_jws_sig_confirm(&map_b64, map, jwk, context); -} - -int -lws_jws_sig_confirm_json(const char *in, size_t len, - struct lws_jws *jws, struct lws_jwk *jwk, - struct lws_context *context, - char *temp, int *temp_len) -{ - if (lws_jws_json_parse(jws, (const uint8_t *)in, len, temp, temp_len)) { - lwsl_err("%s: lws_jws_json_parse failed\n", __func__); - - return -1; - } - return lws_jws_sig_confirm(&jws->map_b64, &jws->map, jwk, context); -} - - -int -lws_jws_sign_from_b64(struct lws_jose *jose, struct lws_jws *jws, - char *b64_sig, size_t sig_len) -{ - enum enum_genrsa_mode pad = LGRSAM_PKCS1_1_5; - uint8_t digest[LWS_GENHASH_LARGEST]; - struct lws_genhash_ctx hash_ctx; - struct lws_genec_ctx ecdsactx; - struct lws_genrsa_ctx rsactx; - uint8_t *buf; - int n, m; - - if (jose->alg->hash_type == LWS_GENHASH_TYPE_UNKNOWN && - jose->alg->hmac_type == LWS_GENHMAC_TYPE_UNKNOWN && - !strcmp(jose->alg->alg, "none")) - return 0; - - if (lws_genhash_init(&hash_ctx, jose->alg->hash_type) || - lws_genhash_update(&hash_ctx, jws->map_b64.buf[LJWS_JOSE], - jws->map_b64.len[LJWS_JOSE]) || - lws_genhash_update(&hash_ctx, ".", 1) || - lws_genhash_update(&hash_ctx, jws->map_b64.buf[LJWS_PYLD], - jws->map_b64.len[LJWS_PYLD]) || - lws_genhash_destroy(&hash_ctx, digest)) { - lws_genhash_destroy(&hash_ctx, NULL); - - return -1; - } - - switch (jose->alg->algtype_signing) { - case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS: - case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP: - pad = LGRSAM_PKCS1_OAEP_PSS; - /* fallthru */ - case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5: - - if (jws->jwk->kty != LWS_GENCRYPTO_KTY_RSA) - return -1; - - if (lws_genrsa_create(&rsactx, jws->jwk->e, jws->context, - pad, LWS_GENHASH_TYPE_UNKNOWN)) { - lwsl_notice("%s: lws_genrsa_public_decrypt_create\n", - __func__); - return -1; - } - - n = jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len; - buf = lws_malloc(lws_base64_size(n), "jws sign"); - if (!buf) - return -1; - - n = lws_genrsa_hash_sign(&rsactx, digest, jose->alg->hash_type, - buf, n); - lws_genrsa_destroy(&rsactx); - if (n < 0) { - lwsl_err("%s: lws_genrsa_hash_sign failed\n", __func__); - lws_free(buf); - - return -1; - } - - n = lws_jws_base64_enc((char *)buf, n, b64_sig, sig_len); - lws_free(buf); - if (n < 0) { - lwsl_err("%s: lws_jws_base64_enc failed\n", __func__); - } - - return n; - - case LWS_JOSE_ENCTYPE_NONE: - return lws_jws_base64_enc((char *)digest, - lws_genhash_size(jose->alg->hash_type), - b64_sig, sig_len); - case LWS_JOSE_ENCTYPE_ECDSA: - /* ECDSA using SHA-256/384/512 */ - - /* the key coming in with this makes sense, right? */ - - /* has to be an EC key :-) */ - if (jws->jwk->kty != LWS_GENCRYPTO_KTY_EC) - return -1; - - /* key must state its curve */ - if (!jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf) - return -1; - - /* must have all his pieces for a private key */ - if (!jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_X].buf || - !jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_Y].buf || - !jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf) - return -1; - - /* key must match the selected alg curve */ - if (strcmp((const char *) - jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, - jose->alg->curve_name)) - return -1; - - if (lws_genecdsa_create(&ecdsactx, jws->context, NULL)) { - lwsl_notice("%s: lws_genrsa_public_decrypt_create\n", - __func__); - return -1; - } - - if (lws_genecdsa_set_key(&ecdsactx, jws->jwk->e)) { - lws_genec_destroy(&ecdsactx); - lwsl_notice("%s: ec key import fail\n", __func__); - return -1; - } - m = lws_gencrypto_bits_to_bytes(jose->alg->keybits_fixed) * 2; - buf = lws_malloc(m, "jws sign"); - if (!buf) - return -1; - - n = lws_genecdsa_hash_sign_jws(&ecdsactx, digest, - jose->alg->hash_type, - jose->alg->keybits_fixed, - (uint8_t *)buf, m); - lws_genec_destroy(&ecdsactx); - if (n < 0) { - lws_free(buf); - lwsl_notice("%s: lws_genecdsa_hash_sign_jws fail\n", - __func__); - return -1; - } - - n = lws_jws_base64_enc((char *)buf, m, b64_sig, sig_len); - lws_free(buf); - - return n; - - default: - break; - } - - /* unknown key type */ - - return -1; -} - -/* - * Flattened JWS JSON: - * - * { - * "payload": "", - * "protected": "", - * "header": , - * "signature": "" - * } - */ - -int -lws_jws_write_flattened_json(struct lws_jws *jws, char *flattened, size_t len) -{ - size_t n = 0; - - if (len < 1) - return 1; - - n += lws_snprintf(flattened + n, len - n , "{\"payload\": \""); - lws_strnncpy(flattened + n, jws->map_b64.buf[LJWS_PYLD], - jws->map_b64.len[LJWS_PYLD], len - n); - n += strlen(flattened + n); - - n += lws_snprintf(flattened + n, len - n , "\",\n \"protected\": \""); - lws_strnncpy(flattened + n, jws->map_b64.buf[LJWS_JOSE], - jws->map_b64.len[LJWS_JOSE], len - n); - n += strlen(flattened + n); - - if (jws->map_b64.buf[LJWS_UHDR]) { - n += lws_snprintf(flattened + n, len - n , "\",\n \"header\": "); - lws_strnncpy(flattened + n, jws->map_b64.buf[LJWS_UHDR], - jws->map_b64.len[LJWS_UHDR], len - n); - n += strlen(flattened + n); - } - - n += lws_snprintf(flattened + n, len - n , "\",\n \"signature\": \""); - lws_strnncpy(flattened + n, jws->map_b64.buf[LJWS_SIG], - jws->map_b64.len[LJWS_SIG], len - n); - n += strlen(flattened + n); - - n += lws_snprintf(flattened + n, len - n , "\"}\n"); - - return (n >= len - 1); -} - -int -lws_jws_write_compact(struct lws_jws *jws, char *compact, size_t len) -{ - size_t n = 0; - - if (len < 1) - return 1; - - lws_strnncpy(compact + n, jws->map_b64.buf[LJWS_JOSE], - jws->map_b64.len[LJWS_JOSE], len - n); - n += strlen(compact + n); - lws_strnncpy(compact + n, jws->map_b64.buf[LJWS_PYLD], - jws->map_b64.len[LJWS_PYLD], len - n); - n += strlen(compact + n); - lws_strnncpy(compact + n, jws->map_b64.buf[LJWS_SIG], - jws->map_b64.len[LJWS_SIG], len - n); - n += strlen(compact + n); - - return n >= len - 1; -} diff -Nru libwebsockets-4.0.20/lib/jose/jws/private-lib-jose-jws.h libwebsockets-2.4.2/lib/jose/jws/private-lib-jose-jws.h --- libwebsockets-4.0.20/lib/jose/jws/private-lib-jose-jws.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/jws/private-lib-jose-jws.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * JOSE is actually specified as part of JWS RFC7515. JWE references RFC7515 - * to specify its JOSE JSON object. So it lives in ./lib/jose/jws/jose.c. - */ - diff -Nru libwebsockets-4.0.20/lib/jose/private-lib-jose.h libwebsockets-2.4.2/lib/jose/private-lib-jose.h --- libwebsockets-4.0.20/lib/jose/private-lib-jose.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/private-lib-jose.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - -void -lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m); - -void -lws_jwk_init_jps(struct lejp_ctx *jctx, struct lws_jwk_parse_state *jps, - struct lws_jwk *jwk, lws_jwk_key_import_callback cb, - void *user); - -int -lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk, - char *out, size_t out_len); diff -Nru libwebsockets-4.0.20/lib/jose/README.md libwebsockets-2.4.2/lib/jose/README.md --- libwebsockets-4.0.20/lib/jose/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/jose/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -# JOSE support - -JOSE is a set of web standards aimed at encapsulating crypto -operations flexibly inside JSON objects. - -Lws provides lightweight apis to performs operations on JWK, JWS and JWE -independent of the tls backend in use. The JSON parsing is handled by the lws -lejp stream parser. - -|Part|RFC|Function| -|---|---|---| -|JWS|[RFC7515](https://tools.ietf.org/html/rfc7515)|JSON Web Signatures| -|JWE|[RFC7516](https://tools.ietf.org/html/rfc7516)|JSON Web Encryption| -|JWK|[RFC7517](https://tools.ietf.org/html/rfc7517)|JSON Web Keys| -|JWA|[RFC7518](https://tools.ietf.org/html/rfc7518)|JSON Web Algorithms| - -JWA is a set of recommendations for which combinations of algorithms -are deemed desirable and secure, which implies what must be done for -useful implementations of JWS, JWE and JWK. - -## Supported algorithms - -### Supported keys - - - All RFC7517 / JWK forms: octet, RSA and EC - - - singleton and keys[] arrays of keys supported - -### Symmetric ciphers - - - All common AES varaiants: CBC, CFB128, CFB8, CTR, EVB, OFB, KW and XTS - -### Asymmetric ciphers - - - RSA - - - EC (P-256, P-384 and P-521 JWA curves) - -### Payload auth and crypt - - - AES_128_CBC_HMAC_SHA_256 - - AES_192_CBC_HMAC_SHA_384 - - AES_256_CBC_HMAC_SHA_512 - - AES_128_GCM - -For the required and recommended asymmetric algorithms, support currently -looks like this - -|JWK kty|JWA|lws| -|---|---|---| -|EC|Recommended+|yes| -|RSA|Required|yes| -|oct|Required|yes| - -|JWE alg|JWA|lws| -|---|---|---| -|RSA1_5|Recommended-|yes| -|RSA-OAEP|Recommended+|no| -|ECDH-ES|Recommended+|no| - -|JWS alg|JWA|lws| -|---|---|---| -|HS256|Required|yes| -|RS256|Recommended+|yes| -|ES256|Recommended|yes| - -## Minimal Example tools - -[JWK](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwk) - -[JWS](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jws) - -[JWE](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwe) - -## API tests - -See `./minimal-examples/api-tests/api-test-jose/` for example test code. -The tests are built and confirmed during CI. - diff -Nru libwebsockets-4.0.20/lib/lextable.h libwebsockets-2.4.2/lib/lextable.h --- libwebsockets-4.0.20/lib/lextable.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/lextable.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,805 @@ +/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */, + 0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */, + 0x6F /* 'o' */, 0x51, 0x00 /* (to 0x0057 state 10) */, + 0x68 /* 'h' */, 0x5D, 0x00 /* (to 0x0066 state 18) */, + 0x63 /* 'c' */, 0x69, 0x00 /* (to 0x0075 state 23) */, + 0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x0099 state 34) */, + 0x73 /* 's' */, 0xA0, 0x00 /* (to 0x00B2 state 48) */, + 0x0D /* '.' */, 0xD9, 0x00 /* (to 0x00EE state 68) */, + 0x61 /* 'a' */, 0x31, 0x01 /* (to 0x0149 state 129) */, + 0x69 /* 'i' */, 0x70, 0x01 /* (to 0x018B state 163) */, + 0x64 /* 'd' */, 0x19, 0x02 /* (to 0x0237 state 265) */, + 0x72 /* 'r' */, 0x22, 0x02 /* (to 0x0243 state 270) */, + 0x3A /* ':' */, 0x53, 0x02 /* (to 0x0277 state 299) */, + 0x65 /* 'e' */, 0xDF, 0x02 /* (to 0x0306 state 409) */, + 0x66 /* 'f' */, 0xFB, 0x02 /* (to 0x0325 state 425) */, + 0x6C /* 'l' */, 0x1D, 0x03 /* (to 0x034A state 458) */, + 0x6D /* 'm' */, 0x40, 0x03 /* (to 0x0370 state 484) */, + 0x74 /* 't' */, 0xAF, 0x03 /* (to 0x03E2 state 578) */, + 0x76 /* 'v' */, 0xD0, 0x03 /* (to 0x0406 state 606) */, + 0x77 /* 'w' */, 0xDD, 0x03 /* (to 0x0416 state 614) */, + 0x78 /* 'x' */, 0x04, 0x04 /* (to 0x0440 state 650) */, + 0x08, /* fail */ +/* pos 0040: 1 */ 0xE5 /* 'e' -> */, +/* pos 0041: 2 */ 0xF4 /* 't' -> */, +/* pos 0042: 3 */ 0xA0 /* ' ' -> */, +/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, +/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */, + 0x72 /* 'r' */, 0x95, 0x01 /* (to 0x01DD state 211) */, + 0x61 /* 'a' */, 0xDD, 0x03 /* (to 0x0428 state 631) */, + 0x75 /* 'u' */, 0xDF, 0x03 /* (to 0x042D state 635) */, + 0x08, /* fail */ +/* pos 0052: 6 */ 0xF3 /* 's' -> */, +/* pos 0053: 7 */ 0xF4 /* 't' -> */, +/* pos 0054: 8 */ 0xA0 /* ' ' -> */, +/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, +/* pos 0057: 10 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x005E state 11) */, + 0x72 /* 'r' */, 0x51, 0x00 /* (to 0x00AB state 42) */, + 0x08, /* fail */ +/* pos 005e: 11 */ 0xF4 /* 't' -> */, +/* pos 005f: 12 */ 0xE9 /* 'i' -> */, +/* pos 0060: 13 */ 0xEF /* 'o' -> */, +/* pos 0061: 14 */ 0xEE /* 'n' -> */, +/* pos 0062: 15 */ 0xF3 /* 's' -> */, +/* pos 0063: 16 */ 0xA0 /* ' ' -> */, +/* pos 0064: 17 */ 0x00, 0x02 /* - terminal marker 2 - */, +/* pos 0066: 18 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x0070 state 19) */, + 0x74 /* 't' */, 0xBF, 0x00 /* (to 0x0128 state 110) */, + 0x65 /* 'e' */, 0xF8, 0x03 /* (to 0x0464 state 676) */, + 0x08, /* fail */ +/* pos 0070: 19 */ 0xF3 /* 's' -> */, +/* pos 0071: 20 */ 0xF4 /* 't' -> */, +/* pos 0072: 21 */ 0xBA /* ':' -> */, +/* pos 0073: 22 */ 0x00, 0x03 /* - terminal marker 3 - */, +/* pos 0075: 23 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x007C state 24) */, + 0x61 /* 'a' */, 0x72, 0x01 /* (to 0x01EA state 217) */, + 0x08, /* fail */ +/* pos 007c: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0083 state 25) */, + 0x6F /* 'o' */, 0x87, 0x01 /* (to 0x0206 state 243) */, + 0x08, /* fail */ +/* pos 0083: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x008A state 26) */, + 0x74 /* 't' */, 0x86, 0x01 /* (to 0x020C state 248) */, + 0x08, /* fail */ +/* pos 008a: 26 */ 0xE5 /* 'e' -> */, +/* pos 008b: 27 */ 0xE3 /* 'c' -> */, +/* pos 008c: 28 */ 0xF4 /* 't' -> */, +/* pos 008d: 29 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0094 state 30) */, + 0x20 /* ' ' */, 0xD2, 0x03 /* (to 0x0462 state 675) */, + 0x08, /* fail */ +/* pos 0094: 30 */ 0xEF /* 'o' -> */, +/* pos 0095: 31 */ 0xEE /* 'n' -> */, +/* pos 0096: 32 */ 0xBA /* ':' -> */, +/* pos 0097: 33 */ 0x00, 0x04 /* - terminal marker 4 - */, +/* pos 0099: 34 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x00A3 state 35) */, + 0x73 /* 's' */, 0x5F, 0x03 /* (to 0x03FB state 596) */, + 0x72 /* 'r' */, 0x97, 0x03 /* (to 0x0436 state 642) */, + 0x08, /* fail */ +/* pos 00a3: 35 */ 0xE7 /* 'g' -> */, +/* pos 00a4: 36 */ 0xF2 /* 'r' -> */, +/* pos 00a5: 37 */ 0xE1 /* 'a' -> */, +/* pos 00a6: 38 */ 0xE4 /* 'd' -> */, +/* pos 00a7: 39 */ 0xE5 /* 'e' -> */, +/* pos 00a8: 40 */ 0xBA /* ':' -> */, +/* pos 00a9: 41 */ 0x00, 0x05 /* - terminal marker 5 - */, +/* pos 00ab: 42 */ 0xE9 /* 'i' -> */, +/* pos 00ac: 43 */ 0xE7 /* 'g' -> */, +/* pos 00ad: 44 */ 0xE9 /* 'i' -> */, +/* pos 00ae: 45 */ 0xEE /* 'n' -> */, +/* pos 00af: 46 */ 0xBA /* ':' -> */, +/* pos 00b0: 47 */ 0x00, 0x06 /* - terminal marker 6 - */, +/* pos 00b2: 48 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x00B9 state 49) */, + 0x74 /* 't' */, 0x13, 0x03 /* (to 0x03C8 state 553) */, + 0x08, /* fail */ +/* pos 00b9: 49 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x00C3 state 50) */, + 0x72 /* 'r' */, 0xFC, 0x02 /* (to 0x03B8 state 539) */, + 0x74 /* 't' */, 0xFF, 0x02 /* (to 0x03BE state 544) */, + 0x08, /* fail */ +/* pos 00c3: 50 */ 0xAD /* '-' -> */, +/* pos 00c4: 51 */ 0xF7 /* 'w' -> */, +/* pos 00c5: 52 */ 0xE5 /* 'e' -> */, +/* pos 00c6: 53 */ 0xE2 /* 'b' -> */, +/* pos 00c7: 54 */ 0xF3 /* 's' -> */, +/* pos 00c8: 55 */ 0xEF /* 'o' -> */, +/* pos 00c9: 56 */ 0xE3 /* 'c' -> */, +/* pos 00ca: 57 */ 0xEB /* 'k' -> */, +/* pos 00cb: 58 */ 0xE5 /* 'e' -> */, +/* pos 00cc: 59 */ 0xF4 /* 't' -> */, +/* pos 00cd: 60 */ 0xAD /* '-' -> */, +/* pos 00ce: 61 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x00E7 state 62) */, + 0x65 /* 'e' */, 0x20, 0x00 /* (to 0x00F1 state 70) */, + 0x6B /* 'k' */, 0x29, 0x00 /* (to 0x00FD state 81) */, + 0x70 /* 'p' */, 0x38, 0x00 /* (to 0x010F state 88) */, + 0x61 /* 'a' */, 0x3F, 0x00 /* (to 0x0119 state 97) */, + 0x6E /* 'n' */, 0x44, 0x00 /* (to 0x0121 state 104) */, + 0x76 /* 'v' */, 0x86, 0x01 /* (to 0x0266 state 284) */, + 0x6F /* 'o' */, 0x8C, 0x01 /* (to 0x026F state 292) */, + 0x08, /* fail */ +/* pos 00e7: 62 */ 0xF2 /* 'r' -> */, +/* pos 00e8: 63 */ 0xE1 /* 'a' -> */, +/* pos 00e9: 64 */ 0xE6 /* 'f' -> */, +/* pos 00ea: 65 */ 0xF4 /* 't' -> */, +/* pos 00eb: 66 */ 0xBA /* ':' -> */, +/* pos 00ec: 67 */ 0x00, 0x07 /* - terminal marker 7 - */, +/* pos 00ee: 68 */ 0x8A /* '.' -> */, +/* pos 00ef: 69 */ 0x00, 0x08 /* - terminal marker 8 - */, +/* pos 00f1: 70 */ 0xF8 /* 'x' -> */, +/* pos 00f2: 71 */ 0xF4 /* 't' -> */, +/* pos 00f3: 72 */ 0xE5 /* 'e' -> */, +/* pos 00f4: 73 */ 0xEE /* 'n' -> */, +/* pos 00f5: 74 */ 0xF3 /* 's' -> */, +/* pos 00f6: 75 */ 0xE9 /* 'i' -> */, +/* pos 00f7: 76 */ 0xEF /* 'o' -> */, +/* pos 00f8: 77 */ 0xEE /* 'n' -> */, +/* pos 00f9: 78 */ 0xF3 /* 's' -> */, +/* pos 00fa: 79 */ 0xBA /* ':' -> */, +/* pos 00fb: 80 */ 0x00, 0x09 /* - terminal marker 9 - */, +/* pos 00fd: 81 */ 0xE5 /* 'e' -> */, +/* pos 00fe: 82 */ 0xF9 /* 'y' -> */, +/* pos 00ff: 83 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0109 state 84) */, + 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x010C state 86) */, + 0x3A /* ':' */, 0x5F, 0x01 /* (to 0x0264 state 283) */, + 0x08, /* fail */ +/* pos 0109: 84 */ 0xBA /* ':' -> */, +/* pos 010a: 85 */ 0x00, 0x0A /* - terminal marker 10 - */, +/* pos 010c: 86 */ 0xBA /* ':' -> */, +/* pos 010d: 87 */ 0x00, 0x0B /* - terminal marker 11 - */, +/* pos 010f: 88 */ 0xF2 /* 'r' -> */, +/* pos 0110: 89 */ 0xEF /* 'o' -> */, +/* pos 0111: 90 */ 0xF4 /* 't' -> */, +/* pos 0112: 91 */ 0xEF /* 'o' -> */, +/* pos 0113: 92 */ 0xE3 /* 'c' -> */, +/* pos 0114: 93 */ 0xEF /* 'o' -> */, +/* pos 0115: 94 */ 0xEC /* 'l' -> */, +/* pos 0116: 95 */ 0xBA /* ':' -> */, +/* pos 0117: 96 */ 0x00, 0x0C /* - terminal marker 12 - */, +/* pos 0119: 97 */ 0xE3 /* 'c' -> */, +/* pos 011a: 98 */ 0xE3 /* 'c' -> */, +/* pos 011b: 99 */ 0xE5 /* 'e' -> */, +/* pos 011c: 100 */ 0xF0 /* 'p' -> */, +/* pos 011d: 101 */ 0xF4 /* 't' -> */, +/* pos 011e: 102 */ 0xBA /* ':' -> */, +/* pos 011f: 103 */ 0x00, 0x0D /* - terminal marker 13 - */, +/* pos 0121: 104 */ 0xEF /* 'o' -> */, +/* pos 0122: 105 */ 0xEE /* 'n' -> */, +/* pos 0123: 106 */ 0xE3 /* 'c' -> */, +/* pos 0124: 107 */ 0xE5 /* 'e' -> */, +/* pos 0125: 108 */ 0xBA /* ':' -> */, +/* pos 0126: 109 */ 0x00, 0x0E /* - terminal marker 14 - */, +/* pos 0128: 110 */ 0xF4 /* 't' -> */, +/* pos 0129: 111 */ 0xF0 /* 'p' -> */, +/* pos 012a: 112 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x0131 state 113) */, + 0x32 /* '2' */, 0x10, 0x00 /* (to 0x013D state 118) */, + 0x08, /* fail */ +/* pos 0131: 113 */ 0xB1 /* '1' -> */, +/* pos 0132: 114 */ 0xAE /* '.' -> */, +/* pos 0133: 115 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x013A state 116) */, + 0x30 /* '0' */, 0x1B, 0x03 /* (to 0x0451 state 660) */, + 0x08, /* fail */ +/* pos 013a: 116 */ 0xA0 /* ' ' -> */, +/* pos 013b: 117 */ 0x00, 0x0F /* - terminal marker 15 - */, +/* pos 013d: 118 */ 0xAD /* '-' -> */, +/* pos 013e: 119 */ 0xF3 /* 's' -> */, +/* pos 013f: 120 */ 0xE5 /* 'e' -> */, +/* pos 0140: 121 */ 0xF4 /* 't' -> */, +/* pos 0141: 122 */ 0xF4 /* 't' -> */, +/* pos 0142: 123 */ 0xE9 /* 'i' -> */, +/* pos 0143: 124 */ 0xEE /* 'n' -> */, +/* pos 0144: 125 */ 0xE7 /* 'g' -> */, +/* pos 0145: 126 */ 0xF3 /* 's' -> */, +/* pos 0146: 127 */ 0xBA /* ':' -> */, +/* pos 0147: 128 */ 0x00, 0x10 /* - terminal marker 16 - */, +/* pos 0149: 129 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x0156 state 130) */, + 0x75 /* 'u' */, 0xAC, 0x00 /* (to 0x01F8 state 230) */, + 0x67 /* 'g' */, 0x7D, 0x01 /* (to 0x02CC state 358) */, + 0x6C /* 'l' */, 0x7E, 0x01 /* (to 0x02D0 state 361) */, + 0x08, /* fail */ +/* pos 0156: 130 */ 0xE3 /* 'c' -> */, +/* pos 0157: 131 */ 0xE5 /* 'e' -> */, +/* pos 0158: 132 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x015F state 133) */, + 0x73 /* 's' */, 0x0E, 0x00 /* (to 0x0169 state 136) */, + 0x08, /* fail */ +/* pos 015f: 133 */ 0xF4 /* 't' -> */, +/* pos 0160: 134 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0167 state 135) */, + 0x2D /* '-' */, 0x59, 0x00 /* (to 0x01BC state 192) */, + 0x08, /* fail */ +/* pos 0167: 135 */ 0x00, 0x11 /* - terminal marker 17 - */, +/* pos 0169: 136 */ 0xF3 /* 's' -> */, +/* pos 016a: 137 */ 0xAD /* '-' -> */, +/* pos 016b: 138 */ 0xE3 /* 'c' -> */, +/* pos 016c: 139 */ 0xEF /* 'o' -> */, +/* pos 016d: 140 */ 0xEE /* 'n' -> */, +/* pos 016e: 141 */ 0xF4 /* 't' -> */, +/* pos 016f: 142 */ 0xF2 /* 'r' -> */, +/* pos 0170: 143 */ 0xEF /* 'o' -> */, +/* pos 0171: 144 */ 0xEC /* 'l' -> */, +/* pos 0172: 145 */ 0xAD /* '-' -> */, +/* pos 0173: 146 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x017A state 147) */, + 0x61 /* 'a' */, 0x48, 0x01 /* (to 0x02BE state 345) */, + 0x08, /* fail */ +/* pos 017a: 147 */ 0xE5 /* 'e' -> */, +/* pos 017b: 148 */ 0xF1 /* 'q' -> */, +/* pos 017c: 149 */ 0xF5 /* 'u' -> */, +/* pos 017d: 150 */ 0xE5 /* 'e' -> */, +/* pos 017e: 151 */ 0xF3 /* 's' -> */, +/* pos 017f: 152 */ 0xF4 /* 't' -> */, +/* pos 0180: 153 */ 0xAD /* '-' -> */, +/* pos 0181: 154 */ 0xE8 /* 'h' -> */, +/* pos 0182: 155 */ 0xE5 /* 'e' -> */, +/* pos 0183: 156 */ 0xE1 /* 'a' -> */, +/* pos 0184: 157 */ 0xE4 /* 'd' -> */, +/* pos 0185: 158 */ 0xE5 /* 'e' -> */, +/* pos 0186: 159 */ 0xF2 /* 'r' -> */, +/* pos 0187: 160 */ 0xF3 /* 's' -> */, +/* pos 0188: 161 */ 0xBA /* ':' -> */, +/* pos 0189: 162 */ 0x00, 0x12 /* - terminal marker 18 - */, +/* pos 018b: 163 */ 0xE6 /* 'f' -> */, +/* pos 018c: 164 */ 0xAD /* '-' -> */, +/* pos 018d: 165 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x019A state 166) */, + 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x01B0 state 181) */, + 0x72 /* 'r' */, 0x9E, 0x01 /* (to 0x0331 state 435) */, + 0x75 /* 'u' */, 0xA2, 0x01 /* (to 0x0338 state 441) */, + 0x08, /* fail */ +/* pos 019a: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x01A1 state 167) */, + 0x61 /* 'a' */, 0x8E, 0x01 /* (to 0x032B state 430) */, + 0x08, /* fail */ +/* pos 01a1: 167 */ 0xE4 /* 'd' -> */, +/* pos 01a2: 168 */ 0xE9 /* 'i' -> */, +/* pos 01a3: 169 */ 0xE6 /* 'f' -> */, +/* pos 01a4: 170 */ 0xE9 /* 'i' -> */, +/* pos 01a5: 171 */ 0xE5 /* 'e' -> */, +/* pos 01a6: 172 */ 0xE4 /* 'd' -> */, +/* pos 01a7: 173 */ 0xAD /* '-' -> */, +/* pos 01a8: 174 */ 0xF3 /* 's' -> */, +/* pos 01a9: 175 */ 0xE9 /* 'i' -> */, +/* pos 01aa: 176 */ 0xEE /* 'n' -> */, +/* pos 01ab: 177 */ 0xE3 /* 'c' -> */, +/* pos 01ac: 178 */ 0xE5 /* 'e' -> */, +/* pos 01ad: 179 */ 0xBA /* ':' -> */, +/* pos 01ae: 180 */ 0x00, 0x13 /* - terminal marker 19 - */, +/* pos 01b0: 181 */ 0xEF /* 'o' -> */, +/* pos 01b1: 182 */ 0xEE /* 'n' -> */, +/* pos 01b2: 183 */ 0xE5 /* 'e' -> */, +/* pos 01b3: 184 */ 0xAD /* '-' -> */, +/* pos 01b4: 185 */ 0xED /* 'm' -> */, +/* pos 01b5: 186 */ 0xE1 /* 'a' -> */, +/* pos 01b6: 187 */ 0xF4 /* 't' -> */, +/* pos 01b7: 188 */ 0xE3 /* 'c' -> */, +/* pos 01b8: 189 */ 0xE8 /* 'h' -> */, +/* pos 01b9: 190 */ 0xBA /* ':' -> */, +/* pos 01ba: 191 */ 0x00, 0x14 /* - terminal marker 20 - */, +/* pos 01bc: 192 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x01C9 state 193) */, + 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x01D3 state 202) */, + 0x63 /* 'c' */, 0xEB, 0x00 /* (to 0x02AD state 330) */, + 0x72 /* 'r' */, 0xF1, 0x00 /* (to 0x02B6 state 338) */, + 0x08, /* fail */ +/* pos 01c9: 193 */ 0xEE /* 'n' -> */, +/* pos 01ca: 194 */ 0xE3 /* 'c' -> */, +/* pos 01cb: 195 */ 0xEF /* 'o' -> */, +/* pos 01cc: 196 */ 0xE4 /* 'd' -> */, +/* pos 01cd: 197 */ 0xE9 /* 'i' -> */, +/* pos 01ce: 198 */ 0xEE /* 'n' -> */, +/* pos 01cf: 199 */ 0xE7 /* 'g' -> */, +/* pos 01d0: 200 */ 0xBA /* ':' -> */, +/* pos 01d1: 201 */ 0x00, 0x15 /* - terminal marker 21 - */, +/* pos 01d3: 202 */ 0xE1 /* 'a' -> */, +/* pos 01d4: 203 */ 0xEE /* 'n' -> */, +/* pos 01d5: 204 */ 0xE7 /* 'g' -> */, +/* pos 01d6: 205 */ 0xF5 /* 'u' -> */, +/* pos 01d7: 206 */ 0xE1 /* 'a' -> */, +/* pos 01d8: 207 */ 0xE7 /* 'g' -> */, +/* pos 01d9: 208 */ 0xE5 /* 'e' -> */, +/* pos 01da: 209 */ 0xBA /* ':' -> */, +/* pos 01db: 210 */ 0x00, 0x16 /* - terminal marker 22 - */, +/* pos 01dd: 211 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01E4 state 212) */, + 0x6F /* 'o' */, 0x9E, 0x01 /* (to 0x037E state 497) */, + 0x08, /* fail */ +/* pos 01e4: 212 */ 0xE7 /* 'g' -> */, +/* pos 01e5: 213 */ 0xED /* 'm' -> */, +/* pos 01e6: 214 */ 0xE1 /* 'a' -> */, +/* pos 01e7: 215 */ 0xBA /* ':' -> */, +/* pos 01e8: 216 */ 0x00, 0x17 /* - terminal marker 23 - */, +/* pos 01ea: 217 */ 0xE3 /* 'c' -> */, +/* pos 01eb: 218 */ 0xE8 /* 'h' -> */, +/* pos 01ec: 219 */ 0xE5 /* 'e' -> */, +/* pos 01ed: 220 */ 0xAD /* '-' -> */, +/* pos 01ee: 221 */ 0xE3 /* 'c' -> */, +/* pos 01ef: 222 */ 0xEF /* 'o' -> */, +/* pos 01f0: 223 */ 0xEE /* 'n' -> */, +/* pos 01f1: 224 */ 0xF4 /* 't' -> */, +/* pos 01f2: 225 */ 0xF2 /* 'r' -> */, +/* pos 01f3: 226 */ 0xEF /* 'o' -> */, +/* pos 01f4: 227 */ 0xEC /* 'l' -> */, +/* pos 01f5: 228 */ 0xBA /* ':' -> */, +/* pos 01f6: 229 */ 0x00, 0x18 /* - terminal marker 24 - */, +/* pos 01f8: 230 */ 0xF4 /* 't' -> */, +/* pos 01f9: 231 */ 0xE8 /* 'h' -> */, +/* pos 01fa: 232 */ 0xEF /* 'o' -> */, +/* pos 01fb: 233 */ 0xF2 /* 'r' -> */, +/* pos 01fc: 234 */ 0xE9 /* 'i' -> */, +/* pos 01fd: 235 */ 0xFA /* 'z' -> */, +/* pos 01fe: 236 */ 0xE1 /* 'a' -> */, +/* pos 01ff: 237 */ 0xF4 /* 't' -> */, +/* pos 0200: 238 */ 0xE9 /* 'i' -> */, +/* pos 0201: 239 */ 0xEF /* 'o' -> */, +/* pos 0202: 240 */ 0xEE /* 'n' -> */, +/* pos 0203: 241 */ 0xBA /* ':' -> */, +/* pos 0204: 242 */ 0x00, 0x19 /* - terminal marker 25 - */, +/* pos 0206: 243 */ 0xEB /* 'k' -> */, +/* pos 0207: 244 */ 0xE9 /* 'i' -> */, +/* pos 0208: 245 */ 0xE5 /* 'e' -> */, +/* pos 0209: 246 */ 0xBA /* ':' -> */, +/* pos 020a: 247 */ 0x00, 0x1A /* - terminal marker 26 - */, +/* pos 020c: 248 */ 0xE5 /* 'e' -> */, +/* pos 020d: 249 */ 0xEE /* 'n' -> */, +/* pos 020e: 250 */ 0xF4 /* 't' -> */, +/* pos 020f: 251 */ 0xAD /* '-' -> */, +/* pos 0210: 252 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0220 state 253) */, + 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0231 state 260) */, + 0x64 /* 'd' */, 0xC0, 0x00 /* (to 0x02D6 state 366) */, + 0x65 /* 'e' */, 0xCA, 0x00 /* (to 0x02E3 state 378) */, + 0x72 /* 'r' */, 0xE3, 0x00 /* (to 0x02FF state 403) */, + 0x08, /* fail */ +/* pos 0220: 253 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x022A state 254) */, + 0x61 /* 'a' */, 0xCA, 0x00 /* (to 0x02ED state 387) */, + 0x6F /* 'o' */, 0xD0, 0x00 /* (to 0x02F6 state 395) */, + 0x08, /* fail */ +/* pos 022a: 254 */ 0xEE /* 'n' -> */, +/* pos 022b: 255 */ 0xE7 /* 'g' -> */, +/* pos 022c: 256 */ 0xF4 /* 't' -> */, +/* pos 022d: 257 */ 0xE8 /* 'h' -> */, +/* pos 022e: 258 */ 0xBA /* ':' -> */, +/* pos 022f: 259 */ 0x00, 0x1B /* - terminal marker 27 - */, +/* pos 0231: 260 */ 0xF9 /* 'y' -> */, +/* pos 0232: 261 */ 0xF0 /* 'p' -> */, +/* pos 0233: 262 */ 0xE5 /* 'e' -> */, +/* pos 0234: 263 */ 0xBA /* ':' -> */, +/* pos 0235: 264 */ 0x00, 0x1C /* - terminal marker 28 - */, +/* pos 0237: 265 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x023E state 266) */, + 0x65 /* 'e' */, 0xF6, 0x01 /* (to 0x0430 state 637) */, + 0x08, /* fail */ +/* pos 023e: 266 */ 0xF4 /* 't' -> */, +/* pos 023f: 267 */ 0xE5 /* 'e' -> */, +/* pos 0240: 268 */ 0xBA /* ':' -> */, +/* pos 0241: 269 */ 0x00, 0x1D /* - terminal marker 29 - */, +/* pos 0243: 270 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x024A state 271) */, + 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0250 state 276) */, + 0x08, /* fail */ +/* pos 024a: 271 */ 0xEE /* 'n' -> */, +/* pos 024b: 272 */ 0xE7 /* 'g' -> */, +/* pos 024c: 273 */ 0xE5 /* 'e' -> */, +/* pos 024d: 274 */ 0xBA /* ':' -> */, +/* pos 024e: 275 */ 0x00, 0x1E /* - terminal marker 30 - */, +/* pos 0250: 276 */ 0x66 /* 'f' */, 0x07, 0x00 /* (to 0x0257 state 277) */, + 0x74 /* 't' */, 0x5A, 0x01 /* (to 0x03AD state 529) */, + 0x08, /* fail */ +/* pos 0257: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x025E state 278) */, + 0x72 /* 'r' */, 0x4D, 0x01 /* (to 0x03A7 state 524) */, + 0x08, /* fail */ +/* pos 025e: 278 */ 0xF2 /* 'r' -> */, +/* pos 025f: 279 */ 0xE5 /* 'e' -> */, +/* pos 0260: 280 */ 0xF2 /* 'r' -> */, +/* pos 0261: 281 */ 0xBA /* ':' -> */, +/* pos 0262: 282 */ 0x00, 0x1F /* - terminal marker 31 - */, +/* pos 0264: 283 */ 0x00, 0x20 /* - terminal marker 32 - */, +/* pos 0266: 284 */ 0xE5 /* 'e' -> */, +/* pos 0267: 285 */ 0xF2 /* 'r' -> */, +/* pos 0268: 286 */ 0xF3 /* 's' -> */, +/* pos 0269: 287 */ 0xE9 /* 'i' -> */, +/* pos 026a: 288 */ 0xEF /* 'o' -> */, +/* pos 026b: 289 */ 0xEE /* 'n' -> */, +/* pos 026c: 290 */ 0xBA /* ':' -> */, +/* pos 026d: 291 */ 0x00, 0x21 /* - terminal marker 33 - */, +/* pos 026f: 292 */ 0xF2 /* 'r' -> */, +/* pos 0270: 293 */ 0xE9 /* 'i' -> */, +/* pos 0271: 294 */ 0xE7 /* 'g' -> */, +/* pos 0272: 295 */ 0xE9 /* 'i' -> */, +/* pos 0273: 296 */ 0xEE /* 'n' -> */, +/* pos 0274: 297 */ 0xBA /* ':' -> */, +/* pos 0275: 298 */ 0x00, 0x22 /* - terminal marker 34 - */, +/* pos 0277: 299 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0284 state 300) */, + 0x6D /* 'm' */, 0x14, 0x00 /* (to 0x028E state 309) */, + 0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0295 state 315) */, + 0x73 /* 's' */, 0x1A, 0x00 /* (to 0x029A state 319) */, + 0x08, /* fail */ +/* pos 0284: 300 */ 0xF5 /* 'u' -> */, +/* pos 0285: 301 */ 0xF4 /* 't' -> */, +/* pos 0286: 302 */ 0xE8 /* 'h' -> */, +/* pos 0287: 303 */ 0xEF /* 'o' -> */, +/* pos 0288: 304 */ 0xF2 /* 'r' -> */, +/* pos 0289: 305 */ 0xE9 /* 'i' -> */, +/* pos 028a: 306 */ 0xF4 /* 't' -> */, +/* pos 028b: 307 */ 0xF9 /* 'y' -> */, +/* pos 028c: 308 */ 0x00, 0x23 /* - terminal marker 35 - */, +/* pos 028e: 309 */ 0xE5 /* 'e' -> */, +/* pos 028f: 310 */ 0xF4 /* 't' -> */, +/* pos 0290: 311 */ 0xE8 /* 'h' -> */, +/* pos 0291: 312 */ 0xEF /* 'o' -> */, +/* pos 0292: 313 */ 0xE4 /* 'd' -> */, +/* pos 0293: 314 */ 0x00, 0x24 /* - terminal marker 36 - */, +/* pos 0295: 315 */ 0xE1 /* 'a' -> */, +/* pos 0296: 316 */ 0xF4 /* 't' -> */, +/* pos 0297: 317 */ 0xE8 /* 'h' -> */, +/* pos 0298: 318 */ 0x00, 0x25 /* - terminal marker 37 - */, +/* pos 029a: 319 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x02A1 state 320) */, + 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x02A7 state 325) */, + 0x08, /* fail */ +/* pos 02a1: 320 */ 0xE8 /* 'h' -> */, +/* pos 02a2: 321 */ 0xE5 /* 'e' -> */, +/* pos 02a3: 322 */ 0xED /* 'm' -> */, +/* pos 02a4: 323 */ 0xE5 /* 'e' -> */, +/* pos 02a5: 324 */ 0x00, 0x26 /* - terminal marker 38 - */, +/* pos 02a7: 325 */ 0xE1 /* 'a' -> */, +/* pos 02a8: 326 */ 0xF4 /* 't' -> */, +/* pos 02a9: 327 */ 0xF5 /* 'u' -> */, +/* pos 02aa: 328 */ 0xF3 /* 's' -> */, +/* pos 02ab: 329 */ 0x00, 0x27 /* - terminal marker 39 - */, +/* pos 02ad: 330 */ 0xE8 /* 'h' -> */, +/* pos 02ae: 331 */ 0xE1 /* 'a' -> */, +/* pos 02af: 332 */ 0xF2 /* 'r' -> */, +/* pos 02b0: 333 */ 0xF3 /* 's' -> */, +/* pos 02b1: 334 */ 0xE5 /* 'e' -> */, +/* pos 02b2: 335 */ 0xF4 /* 't' -> */, +/* pos 02b3: 336 */ 0xBA /* ':' -> */, +/* pos 02b4: 337 */ 0x00, 0x28 /* - terminal marker 40 - */, +/* pos 02b6: 338 */ 0xE1 /* 'a' -> */, +/* pos 02b7: 339 */ 0xEE /* 'n' -> */, +/* pos 02b8: 340 */ 0xE7 /* 'g' -> */, +/* pos 02b9: 341 */ 0xE5 /* 'e' -> */, +/* pos 02ba: 342 */ 0xF3 /* 's' -> */, +/* pos 02bb: 343 */ 0xBA /* ':' -> */, +/* pos 02bc: 344 */ 0x00, 0x29 /* - terminal marker 41 - */, +/* pos 02be: 345 */ 0xEC /* 'l' -> */, +/* pos 02bf: 346 */ 0xEC /* 'l' -> */, +/* pos 02c0: 347 */ 0xEF /* 'o' -> */, +/* pos 02c1: 348 */ 0xF7 /* 'w' -> */, +/* pos 02c2: 349 */ 0xAD /* '-' -> */, +/* pos 02c3: 350 */ 0xEF /* 'o' -> */, +/* pos 02c4: 351 */ 0xF2 /* 'r' -> */, +/* pos 02c5: 352 */ 0xE9 /* 'i' -> */, +/* pos 02c6: 353 */ 0xE7 /* 'g' -> */, +/* pos 02c7: 354 */ 0xE9 /* 'i' -> */, +/* pos 02c8: 355 */ 0xEE /* 'n' -> */, +/* pos 02c9: 356 */ 0xBA /* ':' -> */, +/* pos 02ca: 357 */ 0x00, 0x2A /* - terminal marker 42 - */, +/* pos 02cc: 358 */ 0xE5 /* 'e' -> */, +/* pos 02cd: 359 */ 0xBA /* ':' -> */, +/* pos 02ce: 360 */ 0x00, 0x2B /* - terminal marker 43 - */, +/* pos 02d0: 361 */ 0xEC /* 'l' -> */, +/* pos 02d1: 362 */ 0xEF /* 'o' -> */, +/* pos 02d2: 363 */ 0xF7 /* 'w' -> */, +/* pos 02d3: 364 */ 0xBA /* ':' -> */, +/* pos 02d4: 365 */ 0x00, 0x2C /* - terminal marker 44 - */, +/* pos 02d6: 366 */ 0xE9 /* 'i' -> */, +/* pos 02d7: 367 */ 0xF3 /* 's' -> */, +/* pos 02d8: 368 */ 0xF0 /* 'p' -> */, +/* pos 02d9: 369 */ 0xEF /* 'o' -> */, +/* pos 02da: 370 */ 0xF3 /* 's' -> */, +/* pos 02db: 371 */ 0xE9 /* 'i' -> */, +/* pos 02dc: 372 */ 0xF4 /* 't' -> */, +/* pos 02dd: 373 */ 0xE9 /* 'i' -> */, +/* pos 02de: 374 */ 0xEF /* 'o' -> */, +/* pos 02df: 375 */ 0xEE /* 'n' -> */, +/* pos 02e0: 376 */ 0xBA /* ':' -> */, +/* pos 02e1: 377 */ 0x00, 0x2D /* - terminal marker 45 - */, +/* pos 02e3: 378 */ 0xEE /* 'n' -> */, +/* pos 02e4: 379 */ 0xE3 /* 'c' -> */, +/* pos 02e5: 380 */ 0xEF /* 'o' -> */, +/* pos 02e6: 381 */ 0xE4 /* 'd' -> */, +/* pos 02e7: 382 */ 0xE9 /* 'i' -> */, +/* pos 02e8: 383 */ 0xEE /* 'n' -> */, +/* pos 02e9: 384 */ 0xE7 /* 'g' -> */, +/* pos 02ea: 385 */ 0xBA /* ':' -> */, +/* pos 02eb: 386 */ 0x00, 0x2E /* - terminal marker 46 - */, +/* pos 02ed: 387 */ 0xEE /* 'n' -> */, +/* pos 02ee: 388 */ 0xE7 /* 'g' -> */, +/* pos 02ef: 389 */ 0xF5 /* 'u' -> */, +/* pos 02f0: 390 */ 0xE1 /* 'a' -> */, +/* pos 02f1: 391 */ 0xE7 /* 'g' -> */, +/* pos 02f2: 392 */ 0xE5 /* 'e' -> */, +/* pos 02f3: 393 */ 0xBA /* ':' -> */, +/* pos 02f4: 394 */ 0x00, 0x2F /* - terminal marker 47 - */, +/* pos 02f6: 395 */ 0xE3 /* 'c' -> */, +/* pos 02f7: 396 */ 0xE1 /* 'a' -> */, +/* pos 02f8: 397 */ 0xF4 /* 't' -> */, +/* pos 02f9: 398 */ 0xE9 /* 'i' -> */, +/* pos 02fa: 399 */ 0xEF /* 'o' -> */, +/* pos 02fb: 400 */ 0xEE /* 'n' -> */, +/* pos 02fc: 401 */ 0xBA /* ':' -> */, +/* pos 02fd: 402 */ 0x00, 0x30 /* - terminal marker 48 - */, +/* pos 02ff: 403 */ 0xE1 /* 'a' -> */, +/* pos 0300: 404 */ 0xEE /* 'n' -> */, +/* pos 0301: 405 */ 0xE7 /* 'g' -> */, +/* pos 0302: 406 */ 0xE5 /* 'e' -> */, +/* pos 0303: 407 */ 0xBA /* ':' -> */, +/* pos 0304: 408 */ 0x00, 0x31 /* - terminal marker 49 - */, +/* pos 0306: 409 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x030D state 410) */, + 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x0312 state 414) */, + 0x08, /* fail */ +/* pos 030d: 410 */ 0xE1 /* 'a' -> */, +/* pos 030e: 411 */ 0xE7 /* 'g' -> */, +/* pos 030f: 412 */ 0xBA /* ':' -> */, +/* pos 0310: 413 */ 0x00, 0x32 /* - terminal marker 50 - */, +/* pos 0312: 414 */ 0xF0 /* 'p' -> */, +/* pos 0313: 415 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x031A state 416) */, + 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x031F state 420) */, + 0x08, /* fail */ +/* pos 031a: 416 */ 0xE3 /* 'c' -> */, +/* pos 031b: 417 */ 0xF4 /* 't' -> */, +/* pos 031c: 418 */ 0xBA /* ':' -> */, +/* pos 031d: 419 */ 0x00, 0x33 /* - terminal marker 51 - */, +/* pos 031f: 420 */ 0xF2 /* 'r' -> */, +/* pos 0320: 421 */ 0xE5 /* 'e' -> */, +/* pos 0321: 422 */ 0xF3 /* 's' -> */, +/* pos 0322: 423 */ 0xBA /* ':' -> */, +/* pos 0323: 424 */ 0x00, 0x34 /* - terminal marker 52 - */, +/* pos 0325: 425 */ 0xF2 /* 'r' -> */, +/* pos 0326: 426 */ 0xEF /* 'o' -> */, +/* pos 0327: 427 */ 0xED /* 'm' -> */, +/* pos 0328: 428 */ 0xBA /* ':' -> */, +/* pos 0329: 429 */ 0x00, 0x35 /* - terminal marker 53 - */, +/* pos 032b: 430 */ 0xF4 /* 't' -> */, +/* pos 032c: 431 */ 0xE3 /* 'c' -> */, +/* pos 032d: 432 */ 0xE8 /* 'h' -> */, +/* pos 032e: 433 */ 0xBA /* ':' -> */, +/* pos 032f: 434 */ 0x00, 0x36 /* - terminal marker 54 - */, +/* pos 0331: 435 */ 0xE1 /* 'a' -> */, +/* pos 0332: 436 */ 0xEE /* 'n' -> */, +/* pos 0333: 437 */ 0xE7 /* 'g' -> */, +/* pos 0334: 438 */ 0xE5 /* 'e' -> */, +/* pos 0335: 439 */ 0xBA /* ':' -> */, +/* pos 0336: 440 */ 0x00, 0x37 /* - terminal marker 55 - */, +/* pos 0338: 441 */ 0xEE /* 'n' -> */, +/* pos 0339: 442 */ 0xED /* 'm' -> */, +/* pos 033a: 443 */ 0xEF /* 'o' -> */, +/* pos 033b: 444 */ 0xE4 /* 'd' -> */, +/* pos 033c: 445 */ 0xE9 /* 'i' -> */, +/* pos 033d: 446 */ 0xE6 /* 'f' -> */, +/* pos 033e: 447 */ 0xE9 /* 'i' -> */, +/* pos 033f: 448 */ 0xE5 /* 'e' -> */, +/* pos 0340: 449 */ 0xE4 /* 'd' -> */, +/* pos 0341: 450 */ 0xAD /* '-' -> */, +/* pos 0342: 451 */ 0xF3 /* 's' -> */, +/* pos 0343: 452 */ 0xE9 /* 'i' -> */, +/* pos 0344: 453 */ 0xEE /* 'n' -> */, +/* pos 0345: 454 */ 0xE3 /* 'c' -> */, +/* pos 0346: 455 */ 0xE5 /* 'e' -> */, +/* pos 0347: 456 */ 0xBA /* ':' -> */, +/* pos 0348: 457 */ 0x00, 0x38 /* - terminal marker 56 - */, +/* pos 034a: 458 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0354 state 459) */, + 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x0362 state 472) */, + 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0367 state 476) */, + 0x08, /* fail */ +/* pos 0354: 459 */ 0xF3 /* 's' -> */, +/* pos 0355: 460 */ 0xF4 /* 't' -> */, +/* pos 0356: 461 */ 0xAD /* '-' -> */, +/* pos 0357: 462 */ 0xED /* 'm' -> */, +/* pos 0358: 463 */ 0xEF /* 'o' -> */, +/* pos 0359: 464 */ 0xE4 /* 'd' -> */, +/* pos 035a: 465 */ 0xE9 /* 'i' -> */, +/* pos 035b: 466 */ 0xE6 /* 'f' -> */, +/* pos 035c: 467 */ 0xE9 /* 'i' -> */, +/* pos 035d: 468 */ 0xE5 /* 'e' -> */, +/* pos 035e: 469 */ 0xE4 /* 'd' -> */, +/* pos 035f: 470 */ 0xBA /* ':' -> */, +/* pos 0360: 471 */ 0x00, 0x39 /* - terminal marker 57 - */, +/* pos 0362: 472 */ 0xEE /* 'n' -> */, +/* pos 0363: 473 */ 0xEB /* 'k' -> */, +/* pos 0364: 474 */ 0xBA /* ':' -> */, +/* pos 0365: 475 */ 0x00, 0x3A /* - terminal marker 58 - */, +/* pos 0367: 476 */ 0xE3 /* 'c' -> */, +/* pos 0368: 477 */ 0xE1 /* 'a' -> */, +/* pos 0369: 478 */ 0xF4 /* 't' -> */, +/* pos 036a: 479 */ 0xE9 /* 'i' -> */, +/* pos 036b: 480 */ 0xEF /* 'o' -> */, +/* pos 036c: 481 */ 0xEE /* 'n' -> */, +/* pos 036d: 482 */ 0xBA /* ':' -> */, +/* pos 036e: 483 */ 0x00, 0x3B /* - terminal marker 59 - */, +/* pos 0370: 484 */ 0xE1 /* 'a' -> */, +/* pos 0371: 485 */ 0xF8 /* 'x' -> */, +/* pos 0372: 486 */ 0xAD /* '-' -> */, +/* pos 0373: 487 */ 0xE6 /* 'f' -> */, +/* pos 0374: 488 */ 0xEF /* 'o' -> */, +/* pos 0375: 489 */ 0xF2 /* 'r' -> */, +/* pos 0376: 490 */ 0xF7 /* 'w' -> */, +/* pos 0377: 491 */ 0xE1 /* 'a' -> */, +/* pos 0378: 492 */ 0xF2 /* 'r' -> */, +/* pos 0379: 493 */ 0xE4 /* 'd' -> */, +/* pos 037a: 494 */ 0xF3 /* 's' -> */, +/* pos 037b: 495 */ 0xBA /* ':' -> */, +/* pos 037c: 496 */ 0x00, 0x3C /* - terminal marker 60 - */, +/* pos 037e: 497 */ 0xF8 /* 'x' -> */, +/* pos 037f: 498 */ 0xF9 /* 'y' -> */, +/* pos 0380: 499 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0387 state 500) */, + 0x20 /* ' ' */, 0xBB, 0x00 /* (to 0x043E state 649) */, + 0x08, /* fail */ +/* pos 0387: 500 */ 0xE1 /* 'a' -> */, +/* pos 0388: 501 */ 0xF5 /* 'u' -> */, +/* pos 0389: 502 */ 0xF4 /* 't' -> */, +/* pos 038a: 503 */ 0xE8 /* 'h' -> */, +/* pos 038b: 504 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0392 state 505) */, + 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x039C state 514) */, + 0x08, /* fail */ +/* pos 0392: 505 */ 0xEE /* 'n' -> */, +/* pos 0393: 506 */ 0xF4 /* 't' -> */, +/* pos 0394: 507 */ 0xE9 /* 'i' -> */, +/* pos 0395: 508 */ 0xE3 /* 'c' -> */, +/* pos 0396: 509 */ 0xE1 /* 'a' -> */, +/* pos 0397: 510 */ 0xF4 /* 't' -> */, +/* pos 0398: 511 */ 0xE5 /* 'e' -> */, +/* pos 0399: 512 */ 0xBA /* ':' -> */, +/* pos 039a: 513 */ 0x00, 0x3D /* - terminal marker 61 - */, +/* pos 039c: 514 */ 0xF2 /* 'r' -> */, +/* pos 039d: 515 */ 0xE9 /* 'i' -> */, +/* pos 039e: 516 */ 0xFA /* 'z' -> */, +/* pos 039f: 517 */ 0xE1 /* 'a' -> */, +/* pos 03a0: 518 */ 0xF4 /* 't' -> */, +/* pos 03a1: 519 */ 0xE9 /* 'i' -> */, +/* pos 03a2: 520 */ 0xEF /* 'o' -> */, +/* pos 03a3: 521 */ 0xEE /* 'n' -> */, +/* pos 03a4: 522 */ 0xBA /* ':' -> */, +/* pos 03a5: 523 */ 0x00, 0x3E /* - terminal marker 62 - */, +/* pos 03a7: 524 */ 0xE5 /* 'e' -> */, +/* pos 03a8: 525 */ 0xF3 /* 's' -> */, +/* pos 03a9: 526 */ 0xE8 /* 'h' -> */, +/* pos 03aa: 527 */ 0xBA /* ':' -> */, +/* pos 03ab: 528 */ 0x00, 0x3F /* - terminal marker 63 - */, +/* pos 03ad: 529 */ 0xF2 /* 'r' -> */, +/* pos 03ae: 530 */ 0xF9 /* 'y' -> */, +/* pos 03af: 531 */ 0xAD /* '-' -> */, +/* pos 03b0: 532 */ 0xE1 /* 'a' -> */, +/* pos 03b1: 533 */ 0xE6 /* 'f' -> */, +/* pos 03b2: 534 */ 0xF4 /* 't' -> */, +/* pos 03b3: 535 */ 0xE5 /* 'e' -> */, +/* pos 03b4: 536 */ 0xF2 /* 'r' -> */, +/* pos 03b5: 537 */ 0xBA /* ':' -> */, +/* pos 03b6: 538 */ 0x00, 0x40 /* - terminal marker 64 - */, +/* pos 03b8: 539 */ 0xF6 /* 'v' -> */, +/* pos 03b9: 540 */ 0xE5 /* 'e' -> */, +/* pos 03ba: 541 */ 0xF2 /* 'r' -> */, +/* pos 03bb: 542 */ 0xBA /* ':' -> */, +/* pos 03bc: 543 */ 0x00, 0x41 /* - terminal marker 65 - */, +/* pos 03be: 544 */ 0xAD /* '-' -> */, +/* pos 03bf: 545 */ 0xE3 /* 'c' -> */, +/* pos 03c0: 546 */ 0xEF /* 'o' -> */, +/* pos 03c1: 547 */ 0xEF /* 'o' -> */, +/* pos 03c2: 548 */ 0xEB /* 'k' -> */, +/* pos 03c3: 549 */ 0xE9 /* 'i' -> */, +/* pos 03c4: 550 */ 0xE5 /* 'e' -> */, +/* pos 03c5: 551 */ 0xBA /* ':' -> */, +/* pos 03c6: 552 */ 0x00, 0x42 /* - terminal marker 66 - */, +/* pos 03c8: 553 */ 0xF2 /* 'r' -> */, +/* pos 03c9: 554 */ 0xE9 /* 'i' -> */, +/* pos 03ca: 555 */ 0xE3 /* 'c' -> */, +/* pos 03cb: 556 */ 0xF4 /* 't' -> */, +/* pos 03cc: 557 */ 0xAD /* '-' -> */, +/* pos 03cd: 558 */ 0xF4 /* 't' -> */, +/* pos 03ce: 559 */ 0xF2 /* 'r' -> */, +/* pos 03cf: 560 */ 0xE1 /* 'a' -> */, +/* pos 03d0: 561 */ 0xEE /* 'n' -> */, +/* pos 03d1: 562 */ 0xF3 /* 's' -> */, +/* pos 03d2: 563 */ 0xF0 /* 'p' -> */, +/* pos 03d3: 564 */ 0xEF /* 'o' -> */, +/* pos 03d4: 565 */ 0xF2 /* 'r' -> */, +/* pos 03d5: 566 */ 0xF4 /* 't' -> */, +/* pos 03d6: 567 */ 0xAD /* '-' -> */, +/* pos 03d7: 568 */ 0xF3 /* 's' -> */, +/* pos 03d8: 569 */ 0xE5 /* 'e' -> */, +/* pos 03d9: 570 */ 0xE3 /* 'c' -> */, +/* pos 03da: 571 */ 0xF5 /* 'u' -> */, +/* pos 03db: 572 */ 0xF2 /* 'r' -> */, +/* pos 03dc: 573 */ 0xE9 /* 'i' -> */, +/* pos 03dd: 574 */ 0xF4 /* 't' -> */, +/* pos 03de: 575 */ 0xF9 /* 'y' -> */, +/* pos 03df: 576 */ 0xBA /* ':' -> */, +/* pos 03e0: 577 */ 0x00, 0x43 /* - terminal marker 67 - */, +/* pos 03e2: 578 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x03E9 state 579) */, + 0x65 /* 'e' */, 0x84, 0x00 /* (to 0x0469 state 680) */, + 0x08, /* fail */ +/* pos 03e9: 579 */ 0xE1 /* 'a' -> */, +/* pos 03ea: 580 */ 0xEE /* 'n' -> */, +/* pos 03eb: 581 */ 0xF3 /* 's' -> */, +/* pos 03ec: 582 */ 0xE6 /* 'f' -> */, +/* pos 03ed: 583 */ 0xE5 /* 'e' -> */, +/* pos 03ee: 584 */ 0xF2 /* 'r' -> */, +/* pos 03ef: 585 */ 0xAD /* '-' -> */, +/* pos 03f0: 586 */ 0xE5 /* 'e' -> */, +/* pos 03f1: 587 */ 0xEE /* 'n' -> */, +/* pos 03f2: 588 */ 0xE3 /* 'c' -> */, +/* pos 03f3: 589 */ 0xEF /* 'o' -> */, +/* pos 03f4: 590 */ 0xE4 /* 'd' -> */, +/* pos 03f5: 591 */ 0xE9 /* 'i' -> */, +/* pos 03f6: 592 */ 0xEE /* 'n' -> */, +/* pos 03f7: 593 */ 0xE7 /* 'g' -> */, +/* pos 03f8: 594 */ 0xBA /* ':' -> */, +/* pos 03f9: 595 */ 0x00, 0x44 /* - terminal marker 68 - */, +/* pos 03fb: 596 */ 0xE5 /* 'e' -> */, +/* pos 03fc: 597 */ 0xF2 /* 'r' -> */, +/* pos 03fd: 598 */ 0xAD /* '-' -> */, +/* pos 03fe: 599 */ 0xE1 /* 'a' -> */, +/* pos 03ff: 600 */ 0xE7 /* 'g' -> */, +/* pos 0400: 601 */ 0xE5 /* 'e' -> */, +/* pos 0401: 602 */ 0xEE /* 'n' -> */, +/* pos 0402: 603 */ 0xF4 /* 't' -> */, +/* pos 0403: 604 */ 0xBA /* ':' -> */, +/* pos 0404: 605 */ 0x00, 0x45 /* - terminal marker 69 - */, +/* pos 0406: 606 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x040D state 607) */, + 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0412 state 611) */, + 0x08, /* fail */ +/* pos 040d: 607 */ 0xF2 /* 'r' -> */, +/* pos 040e: 608 */ 0xF9 /* 'y' -> */, +/* pos 040f: 609 */ 0xBA /* ':' -> */, +/* pos 0410: 610 */ 0x00, 0x46 /* - terminal marker 70 - */, +/* pos 0412: 611 */ 0xE1 /* 'a' -> */, +/* pos 0413: 612 */ 0xBA /* ':' -> */, +/* pos 0414: 613 */ 0x00, 0x47 /* - terminal marker 71 - */, +/* pos 0416: 614 */ 0xF7 /* 'w' -> */, +/* pos 0417: 615 */ 0xF7 /* 'w' -> */, +/* pos 0418: 616 */ 0xAD /* '-' -> */, +/* pos 0419: 617 */ 0xE1 /* 'a' -> */, +/* pos 041a: 618 */ 0xF5 /* 'u' -> */, +/* pos 041b: 619 */ 0xF4 /* 't' -> */, +/* pos 041c: 620 */ 0xE8 /* 'h' -> */, +/* pos 041d: 621 */ 0xE5 /* 'e' -> */, +/* pos 041e: 622 */ 0xEE /* 'n' -> */, +/* pos 041f: 623 */ 0xF4 /* 't' -> */, +/* pos 0420: 624 */ 0xE9 /* 'i' -> */, +/* pos 0421: 625 */ 0xE3 /* 'c' -> */, +/* pos 0422: 626 */ 0xE1 /* 'a' -> */, +/* pos 0423: 627 */ 0xF4 /* 't' -> */, +/* pos 0424: 628 */ 0xE5 /* 'e' -> */, +/* pos 0425: 629 */ 0xBA /* ':' -> */, +/* pos 0426: 630 */ 0x00, 0x48 /* - terminal marker 72 - */, +/* pos 0428: 631 */ 0xF4 /* 't' -> */, +/* pos 0429: 632 */ 0xE3 /* 'c' -> */, +/* pos 042a: 633 */ 0xE8 /* 'h' -> */, +/* pos 042b: 634 */ 0x00, 0x49 /* - terminal marker 73 - */, +/* pos 042d: 635 */ 0xF4 /* 't' -> */, +/* pos 042e: 636 */ 0x00, 0x4A /* - terminal marker 74 - */, +/* pos 0430: 637 */ 0xEC /* 'l' -> */, +/* pos 0431: 638 */ 0xE5 /* 'e' -> */, +/* pos 0432: 639 */ 0xF4 /* 't' -> */, +/* pos 0433: 640 */ 0xE5 /* 'e' -> */, +/* pos 0434: 641 */ 0x00, 0x4B /* - terminal marker 75 - */, +/* pos 0436: 642 */ 0xE9 /* 'i' -> */, +/* pos 0437: 643 */ 0xAD /* '-' -> */, +/* pos 0438: 644 */ 0xE1 /* 'a' -> */, +/* pos 0439: 645 */ 0xF2 /* 'r' -> */, +/* pos 043a: 646 */ 0xE7 /* 'g' -> */, +/* pos 043b: 647 */ 0xF3 /* 's' -> */, +/* pos 043c: 648 */ 0x00, 0x4C /* - terminal marker 76 - */, +/* pos 043e: 649 */ 0x00, 0x4D /* - terminal marker 77 - */, +/* pos 0440: 650 */ 0xAD /* '-' -> */, +/* pos 0441: 651 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0448 state 652) */, + 0x66 /* 'f' */, 0x10, 0x00 /* (to 0x0454 state 662) */, + 0x08, /* fail */ +/* pos 0448: 652 */ 0xE5 /* 'e' -> */, +/* pos 0449: 653 */ 0xE1 /* 'a' -> */, +/* pos 044a: 654 */ 0xEC /* 'l' -> */, +/* pos 044b: 655 */ 0xAD /* '-' -> */, +/* pos 044c: 656 */ 0xE9 /* 'i' -> */, +/* pos 044d: 657 */ 0xF0 /* 'p' -> */, +/* pos 044e: 658 */ 0xBA /* ':' -> */, +/* pos 044f: 659 */ 0x00, 0x4E /* - terminal marker 78 - */, +/* pos 0451: 660 */ 0xA0 /* ' ' -> */, +/* pos 0452: 661 */ 0x00, 0x4F /* - terminal marker 79 - */, +/* pos 0454: 662 */ 0xEF /* 'o' -> */, +/* pos 0455: 663 */ 0xF2 /* 'r' -> */, +/* pos 0456: 664 */ 0xF7 /* 'w' -> */, +/* pos 0457: 665 */ 0xE1 /* 'a' -> */, +/* pos 0458: 666 */ 0xF2 /* 'r' -> */, +/* pos 0459: 667 */ 0xE4 /* 'd' -> */, +/* pos 045a: 668 */ 0xE5 /* 'e' -> */, +/* pos 045b: 669 */ 0xE4 /* 'd' -> */, +/* pos 045c: 670 */ 0xAD /* '-' -> */, +/* pos 045d: 671 */ 0xE6 /* 'f' -> */, +/* pos 045e: 672 */ 0xEF /* 'o' -> */, +/* pos 045f: 673 */ 0xF2 /* 'r' -> */, +/* pos 0460: 674 */ 0x00, 0x50 /* - terminal marker 80 - */, +/* pos 0462: 675 */ 0x00, 0x51 /* - terminal marker 81 - */, +/* pos 0464: 676 */ 0xE1 /* 'a' -> */, +/* pos 0465: 677 */ 0xE4 /* 'd' -> */, +/* pos 0466: 678 */ 0xA0 /* ' ' -> */, +/* pos 0467: 679 */ 0x00, 0x52 /* - terminal marker 82 - */, +/* pos 0469: 680 */ 0xBA /* ':' -> */, +/* pos 046a: 681 */ 0x00, 0x53 /* - terminal marker 83 - */, +/* total size 1132 bytes */ diff -Nru libwebsockets-4.0.20/lib/lextable-strings.h libwebsockets-2.4.2/lib/lextable-strings.h --- libwebsockets-4.0.20/lib/lextable-strings.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/lextable-strings.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,104 @@ +/* set of parsable strings -- ALL LOWER CASE */ + +#if !defined(STORE_IN_ROM) +#define STORE_IN_ROM +#endif + +STORE_IN_ROM static const char * const set[] = { + "get ", + "post ", + "options ", + "host:", + "connection:", + "upgrade:", + "origin:", + "sec-websocket-draft:", + "\x0d\x0a", + + "sec-websocket-extensions:", + "sec-websocket-key1:", + "sec-websocket-key2:", + "sec-websocket-protocol:", + + "sec-websocket-accept:", + "sec-websocket-nonce:", + "http/1.1 ", + "http2-settings:", + + "accept:", + "access-control-request-headers:", + "if-modified-since:", + "if-none-match:", + "accept-encoding:", + "accept-language:", + "pragma:", + "cache-control:", + "authorization:", + "cookie:", + "content-length:", + "content-type:", + "date:", + "range:", + "referer:", + "sec-websocket-key:", + "sec-websocket-version:", + "sec-websocket-origin:", + + ":authority", + ":method", + ":path", + ":scheme", + ":status", + + "accept-charset:", + "accept-ranges:", + "access-control-allow-origin:", + "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:", + "max-forwards:", + "proxy-authenticate:", + "proxy-authorization:", + "refresh:", + "retry-after:", + "server:", + "set-cookie:", + "strict-transport-security:", + "transfer-encoding:", + "user-agent:", + "vary:", + "via:", + "www-authenticate:", + + "patch", + "put", + "delete", + + "uri-args", /* fake header used for uri-only storage */ + + "proxy ", + "x-real-ip:", + "http/1.0 ", + + "x-forwarded-for", + "connect ", + "head ", + "te:", /* http/2 wants it to reject it */ + + "", /* not matchable */ + +}; diff -Nru libwebsockets-4.0.20/lib/libwebsockets.c libwebsockets-2.4.2/lib/libwebsockets.c --- libwebsockets-4.0.20/lib/libwebsockets.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/libwebsockets.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,3051 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#ifdef LWS_HAVE_SYS_TYPES_H +#include +#endif + +#ifdef LWS_WITH_IPV6 +#if defined(WIN32) || defined(_WIN32) +#include +#else +#include +#endif +#endif + +int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; +static void (*lwsl_emit)(int level, const char *line) +#ifndef LWS_PLAT_OPTEE + = lwsl_emit_stderr +#endif + ; +#ifndef LWS_PLAT_OPTEE +static const char * const log_level_names[] = { + "ERR", + "WARN", + "NOTICE", + "INFO", + "DEBUG", + "PARSER", + "HEADER", + "EXTENSION", + "CLIENT", + "LATENCY", + "USER", + "?", + "?" +}; +#endif + +void +lws_free_wsi(struct lws *wsi) +{ + struct lws_context_per_thread *pt; + struct allocated_headers *ah; + + if (!wsi) + return; + + pt = &wsi->context->pt[(int)wsi->tsi]; + + /* + * Protocol user data may be allocated either internally by lws + * or by specified the user. We should only free what we allocated. + */ + if (wsi->protocol && wsi->protocol->per_session_data_size && + wsi->user_space && !wsi->user_space_externally_allocated) + lws_free(wsi->user_space); + + lws_free_set_NULL(wsi->rxflow_buffer); + lws_free_set_NULL(wsi->trunc_alloc); + + /* we may not have an ah, but may be on the waiting list... */ + lwsl_info("ah det due to close\n"); + /* we're closing, losing some rx is OK */ + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 0); + + if (wsi->vhost->lserv_wsi == wsi) + wsi->vhost->lserv_wsi = NULL; + + lws_pt_lock(pt); + ah = pt->ah_list; + while (ah) { + if (ah->in_use && ah->wsi == wsi) { + lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi); + ah->in_use = 0; + ah->wsi = NULL; + pt->ah_count_in_use--; + break; + } + ah = ah->next; + } + +#if defined(LWS_WITH_PEER_LIMITS) + lws_peer_track_wsi_close(wsi->context, wsi->peer); + wsi->peer = NULL; +#endif + +#if defined(LWS_WITH_HTTP2) + if (wsi->upgraded_to_http2 || wsi->http2_substream) { + lws_hpack_destroy_dynamic_header(wsi); + + if (wsi->u.h2.h2n) + lws_free_set_NULL(wsi->u.h2.h2n); + } +#endif + + lws_pt_unlock(pt); + + /* since we will destroy the wsi, make absolutely sure now */ + + lws_ssl_remove_wsi_from_buffered_list(wsi); + lws_remove_from_timeout_list(wsi); + + wsi->context->count_wsi_allocated--; + lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi, + wsi->context->count_wsi_allocated); + + lws_free(wsi); +} + +void +lws_remove_from_timeout_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + if (!wsi->timeout_list_prev) /* ie, not part of the list */ + return; + + lws_pt_lock(pt); + /* if we have a next guy, set his prev to our prev */ + if (wsi->timeout_list) + wsi->timeout_list->timeout_list_prev = wsi->timeout_list_prev; + /* set our prev guy to our next guy instead of us */ + *wsi->timeout_list_prev = wsi->timeout_list; + + /* we're out of the list, we should not point anywhere any more */ + wsi->timeout_list_prev = NULL; + wsi->timeout_list = NULL; + lws_pt_unlock(pt); +} + +LWS_VISIBLE void +lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + time_t now; + + if (secs == LWS_TO_KILL_SYNC) { + lws_remove_from_timeout_list(wsi); + lwsl_debug("synchronously killing %p\n", wsi); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return; + } + + lws_pt_lock(pt); + + time(&now); + + if (reason && !wsi->timeout_list_prev) { + /* our next guy is current first guy */ + wsi->timeout_list = pt->timeout_list; + /* if there is a next guy, set his prev ptr to our next ptr */ + if (wsi->timeout_list) + wsi->timeout_list->timeout_list_prev = &wsi->timeout_list; + /* our prev ptr is first ptr */ + wsi->timeout_list_prev = &pt->timeout_list; + /* set the first guy to be us */ + *wsi->timeout_list_prev = wsi; + } + + lwsl_debug("%s: %p: %d secs\n", __func__, wsi, secs); + wsi->pending_timeout_limit = now + secs; + wsi->pending_timeout = reason; + + lws_pt_unlock(pt); + + if (!reason) + lws_remove_from_timeout_list(wsi); +} + +static void +lws_remove_child_from_any_parent(struct lws *wsi) +{ + struct lws **pwsi; + int seen = 0; + + if (!wsi->parent) + return; + + /* detach ourselves from parent's child list */ + pwsi = &wsi->parent->child_list; + while (*pwsi) { + if (*pwsi == wsi) { + lwsl_info("%s: detach %p from parent %p\n", __func__, + wsi, wsi->parent); + + if (wsi->parent->protocol) + wsi->parent->protocol->callback(wsi, + LWS_CALLBACK_CHILD_CLOSING, + wsi->parent->user_space, wsi, 0); + + *pwsi = wsi->sibling_list; + seen = 1; + break; + } + pwsi = &(*pwsi)->sibling_list; + } + if (!seen) + lwsl_err("%s: failed to detach from parent\n", __func__); + + wsi->parent = NULL; +} + +int +lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p) +{ +// if (wsi->protocol == p) +// return 0; + const struct lws_protocols *vp = wsi->vhost->protocols, *vpo; + + if (wsi->protocol) + wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_DROP_PROTOCOL, + wsi->user_space, NULL, 0); + if (!wsi->user_space_externally_allocated) + lws_free_set_NULL(wsi->user_space); + + lws_same_vh_protocol_remove(wsi); + + wsi->protocol = p; + if (!p) + return 0; + + if (lws_ensure_user_space(wsi)) + return 1; + + if (p > vp && p < &vp[wsi->vhost->count_protocols]) + lws_same_vh_protocol_insert(wsi, p - vp); + else { + int n = wsi->vhost->count_protocols; + int hit = 0; + + vpo = vp; + + while (n--) { + if (p->name && vp->name && !strcmp(p->name, vp->name)) { + hit = 1; + lws_same_vh_protocol_insert(wsi, vp - vpo); + break; + } + vp++; + } + if (!hit) + lwsl_err("%s: %p is not in vhost '%s' protocols list\n", + __func__, p, wsi->vhost->name); + } + + if (wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_BIND_PROTOCOL, + wsi->user_space, NULL, 0)) + return 1; + + return 0; +} + +void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) +{ + struct lws_context_per_thread *pt; + struct lws *wsi1, *wsi2; + struct lws_context *context; + struct lws_tokens eff_buf; + int n, m, ret; + + lwsl_debug("%s: %p\n", __func__, wsi); + + if (!wsi) + return; + + lws_access_log(wsi); +#if defined(LWS_WITH_ESP8266) + if (wsi->premature_rx) + lws_free(wsi->premature_rx); + + if (wsi->pending_send_completion && !wsi->close_is_pending_send_completion) { + lwsl_notice("delaying close\n"); + wsi->close_is_pending_send_completion = 1; + return; + } +#endif + + /* we're closing, losing some rx is OK */ + lws_header_table_force_to_detachable_state(wsi); + + context = wsi->context; + pt = &context->pt[(int)wsi->tsi]; + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_CLOSE, 1); + + /* if we have children, close them first */ + if (wsi->child_list) { + wsi2 = wsi->child_list; + while (wsi2) { + wsi1 = wsi2->sibling_list; + wsi2->parent = NULL; + /* stop it doing shutdown processing */ + wsi2->socket_is_permanently_unusable = 1; + lws_close_free_wsi(wsi2, reason); + wsi2 = wsi1; + } + wsi->child_list = NULL; + } + +#if defined(LWS_WITH_HTTP2) + + if (wsi->u.h2.parent_wsi) { + lwsl_info(" wsi: %p, his parent %p: siblings:\n", wsi, wsi->u.h2.parent_wsi); + lws_start_foreach_llp(struct lws **, w, wsi->u.h2.parent_wsi->u.h2.child_list) { + lwsl_info(" \\---- child %p\n", *w); + } lws_end_foreach_llp(w, u.h2.sibling_list); + } + + if (wsi->upgraded_to_http2 || wsi->http2_substream) { + lwsl_info("closing %p: parent %p\n", wsi, wsi->u.h2.parent_wsi); + + if (wsi->u.h2.child_list) { + lwsl_info(" parent %p: closing children: list:\n", wsi); + lws_start_foreach_llp(struct lws **, w, wsi->u.h2.child_list) { + lwsl_info(" \\---- child %p\n", *w); + } lws_end_foreach_llp(w, u.h2.sibling_list); + /* trigger closing of all of our http2 children first */ + lws_start_foreach_llp(struct lws **, w, wsi->u.h2.child_list) { + lwsl_info(" closing child %p\n", *w); + /* disconnect from siblings */ + wsi2 = (*w)->u.h2.sibling_list; + (*w)->u.h2.sibling_list = NULL; + (*w)->socket_is_permanently_unusable = 1; + lws_close_free_wsi(*w, reason); + *w = wsi2; + continue; + } lws_end_foreach_llp(w, u.h2.sibling_list); + } + } + + if (wsi->upgraded_to_http2) { + /* remove pps */ + struct lws_h2_protocol_send *w = wsi->u.h2.h2n->pps, *w1; + while (w) { + w1 = wsi->u.h2.h2n->pps->next; + free(w); + w = w1; + } + wsi->u.h2.h2n->pps = NULL; + } + + if (wsi->http2_substream && wsi->u.h2.parent_wsi) { + lwsl_info(" %p: disentangling from siblings\n", wsi); + lws_start_foreach_llp(struct lws **, w, + wsi->u.h2.parent_wsi->u.h2.child_list) { + /* disconnect from siblings */ + if (*w == wsi) { + wsi2 = (*w)->u.h2.sibling_list; + (*w)->u.h2.sibling_list = NULL; + *w = wsi2; + lwsl_info(" %p disentangled from sibling %p\n", wsi, wsi2); + break; + } + } lws_end_foreach_llp(w, u.h2.sibling_list); + wsi->u.h2.parent_wsi->u.h2.child_count--; + wsi->u.h2.parent_wsi = NULL; + if (wsi->u.h2.pending_status_body) + lws_free_set_NULL(wsi->u.h2.pending_status_body); + } + + if (wsi->upgraded_to_http2 && wsi->u.h2.h2n && + wsi->u.h2.h2n->rx_scratch) + lws_free_set_NULL(wsi->u.h2.h2n->rx_scratch); +#endif + + if (wsi->mode == LWSCM_RAW_FILEDESC) { + lws_remove_child_from_any_parent(wsi); + remove_wsi_socket_from_fds(wsi); + wsi->protocol->callback(wsi, + LWS_CALLBACK_RAW_CLOSE_FILE, + wsi->user_space, NULL, 0); + goto async_close; + } + +#ifdef LWS_WITH_CGI + if (wsi->mode == LWSCM_CGI) { + /* we are not a network connection, but a handler for CGI io */ + if (wsi->parent && wsi->parent->cgi) { + + if (wsi->cgi_channel == LWS_STDOUT) + lws_cgi_remove_and_kill(wsi->parent); + + /* end the binding between us and master */ + wsi->parent->cgi->stdwsi[(int)wsi->cgi_channel] = NULL; + } + wsi->socket_is_permanently_unusable = 1; + + goto just_kill_connection; + } + + if (wsi->cgi) + lws_cgi_remove_and_kill(wsi); +#endif + +#if !defined(LWS_NO_CLIENT) + if (wsi->mode == LWSCM_HTTP_CLIENT || + wsi->mode == LWSCM_WSCL_WAITING_CONNECT || + wsi->mode == LWSCM_WSCL_WAITING_PROXY_REPLY || + wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE || + wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE2 || + wsi->mode == LWSCM_WSCL_WAITING_SSL || + wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY || + wsi->mode == LWSCM_WSCL_WAITING_EXTENSION_CONNECT || + wsi->mode == LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY || + wsi->mode == LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY || + wsi->mode == LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY) + if (wsi->u.hdr.stash) + lws_free_set_NULL(wsi->u.hdr.stash); +#endif + + if (wsi->mode == LWSCM_RAW) { + wsi->protocol->callback(wsi, + LWS_CALLBACK_RAW_CLOSE, wsi->user_space, NULL, 0); + wsi->socket_is_permanently_unusable = 1; + goto just_kill_connection; + } + + if ((wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED || + wsi->mode == LWSCM_HTTP2_SERVING) && + wsi->u.http.fop_fd != NULL) { + lws_vfs_file_close(&wsi->u.http.fop_fd); + wsi->vhost->protocols->callback(wsi, + LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + if (wsi->socket_is_permanently_unusable || + reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY || + wsi->state == LWSS_SHUTDOWN) + goto just_kill_connection; + + wsi->state_pre_close = wsi->state; + + switch (wsi->state_pre_close) { + case LWSS_DEAD_SOCKET: + return; + + /* we tried the polite way... */ + case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION: + case LWSS_AWAITING_CLOSE_ACK: + goto just_kill_connection; + + case LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE: + if (wsi->trunc_len) { + lws_callback_on_writable(wsi); + return; + } + lwsl_info("%p: end FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); + goto just_kill_connection; + default: + if (wsi->trunc_len) { + lwsl_info("%p: start FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); + wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE; + lws_set_timeout(wsi, PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5); + return; + } + break; + } + + if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT || + wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE) + goto just_kill_connection; + + if (!wsi->told_user_closed && + (wsi->mode == LWSCM_HTTP_SERVING || + wsi->mode == LWSCM_HTTP2_SERVING)) { + if (wsi->user_space) + wsi->vhost->protocols->callback(wsi, + LWS_CALLBACK_HTTP_DROP_PROTOCOL, + wsi->user_space, NULL, 0); + wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP, + wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + + /* + * are his extensions okay with him closing? Eg he might be a mux + * parent and just his ch1 aspect is closing? + */ + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) { + lwsl_ext("extension vetoed close\n"); + return; + } + + /* + * flush any tx pending from extensions, since we may send close packet + * if there are problems with send, just nuke the connection + */ + do { + ret = 0; + eff_buf.token = NULL; + eff_buf.token_len = 0; + + /* show every extension the new incoming data */ + + m = lws_ext_cb_active(wsi, + LWS_EXT_CB_FLUSH_PENDING_TX, &eff_buf, 0); + if (m < 0) { + lwsl_ext("Extension reports fatal error\n"); + goto just_kill_connection; + } + if (m) + /* + * at least one extension told us he has more + * to spill, so we will go around again after + */ + ret = 1; + + /* assuming they left us something to send, send it */ + + if (eff_buf.token_len) + if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len) != + eff_buf.token_len) { + lwsl_debug("close: ext spill failed\n"); + goto just_kill_connection; + } + } while (ret); + + /* + * signal we are closing, lws_write will + * add any necessary version-specific stuff. If the write fails, + * no worries we are closing anyway. If we didn't initiate this + * close, then our state has been changed to + * LWSS_RETURNED_CLOSE_ALREADY and we will skip this. + * + * Likewise if it's a second call to close this connection after we + * sent the close indication to the peer already, we are in state + * LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time. + */ + + if (wsi->state_pre_close == LWSS_ESTABLISHED && + (wsi->u.ws.close_in_ping_buffer_len || /* already a reason */ + (reason != LWS_CLOSE_STATUS_NOSTATUS && + (reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) { + lwsl_debug("sending close indication...\n"); + + /* if no prepared close reason, use 1000 and no aux data */ + if (!wsi->u.ws.close_in_ping_buffer_len) { + wsi->u.ws.close_in_ping_buffer_len = 2; + wsi->u.ws.ping_payload_buf[LWS_PRE] = + (reason >> 8) & 0xff; + wsi->u.ws.ping_payload_buf[LWS_PRE + 1] = + reason & 0xff; + } + +#if defined (LWS_WITH_ESP8266) + wsi->close_is_pending_send_completion = 1; +#endif + + lwsl_debug("waiting for chance to send close\n"); + wsi->waiting_to_send_close_frame = 1; + wsi->state = LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION; + lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 2); + lws_callback_on_writable(wsi); + + return; + } + +just_kill_connection: + + lws_remove_child_from_any_parent(wsi); + n = 0; + + if (!wsi->told_user_closed && wsi->user_space) { + lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi, + wsi->protocol->name); + wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_DROP_PROTOCOL, + wsi->user_space, NULL, 0); + } + + if ((wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY || + wsi->mode == LWSCM_WSCL_WAITING_CONNECT) && + !wsi->already_did_cce) { + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, NULL, 0); + } + + if (wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) { + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLOSED_CLIENT_HTTP, + wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + + +#if LWS_POSIX + /* + * Testing with ab shows that we have to stage the socket close when + * the system is under stress... shutdown any further TX, change the + * state to one that won't emit anything more, and wait with a timeout + * for the POLLIN to show a zero-size rx before coming back and doing + * the actual close. + */ + if (wsi->mode != LWSCM_RAW && + !(wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) && + wsi->state != LWSS_SHUTDOWN && + wsi->state != LWSS_CLIENT_UNCONNECTED && + reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY && + !wsi->socket_is_permanently_unusable) { +#ifdef LWS_OPENSSL_SUPPORT + if (lws_is_ssl(wsi) && wsi->ssl) { + n = SSL_shutdown(wsi->ssl); + /* + * If finished the SSL shutdown, then do socket + * shutdown, else need to retry SSL shutdown + */ + switch (n) { + case 0: + lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN); + break; + case 1: + n = shutdown(wsi->desc.sockfd, SHUT_WR); + break; + default: + if (SSL_want_read(wsi->ssl)) { + lws_change_pollfd(wsi, 0, LWS_POLLIN); + n = 0; + break; + } + if (SSL_want_write(wsi->ssl)) { + lws_change_pollfd(wsi, 0, LWS_POLLOUT); + n = 0; + break; + } + n = shutdown(wsi->desc.sockfd, SHUT_WR); + break; + } + } else +#endif + { + lwsl_info("%s: shutdown conn: %p (sock %d, state %d)\n", + __func__, wsi, (int)(long)wsi->desc.sockfd, + wsi->state); + if (!wsi->socket_is_permanently_unusable && + lws_sockfd_valid(wsi->desc.sockfd)) { + wsi->socket_is_permanently_unusable = 1; + n = shutdown(wsi->desc.sockfd, SHUT_WR); + } + } + if (n) + lwsl_debug("closing: shutdown (state %d) ret %d\n", + wsi->state, LWS_ERRNO); + + /* + * This causes problems on WINCE / ESP32 with disconnection + * when the events are half closing connection + */ +#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP32) + /* libuv: no event available to guarantee completion */ + if (!wsi->socket_is_permanently_unusable && + lws_sockfd_valid(wsi->desc.sockfd) && + !LWS_LIBUV_ENABLED(context)) { + lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN); + wsi->state = LWSS_SHUTDOWN; + lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH, + context->timeout_secs); + + return; + } +#endif + } +#endif + + lwsl_debug("%s: real just_kill_connection: %p (sockfd %d)\n", __func__, + wsi, wsi->desc.sockfd); + +#ifdef LWS_WITH_HTTP_PROXY + if (wsi->rw) { + lws_rewrite_destroy(wsi->rw); + wsi->rw = NULL; + } +#endif + /* + * we won't be servicing or receiving anything further from this guy + * delete socket from the internal poll list if still present + */ + lws_ssl_remove_wsi_from_buffered_list(wsi); + lws_remove_from_timeout_list(wsi); + + /* checking return redundant since we anyway close */ + if (wsi->desc.sockfd != LWS_SOCK_INVALID) + remove_wsi_socket_from_fds(wsi); + else + lws_same_vh_protocol_remove(wsi); + +#if defined(LWS_WITH_ESP8266) + espconn_disconnect(wsi->desc.sockfd); +#endif + + wsi->state = LWSS_DEAD_SOCKET; + + lws_free_set_NULL(wsi->rxflow_buffer); + if (wsi->state_pre_close == LWSS_ESTABLISHED || + wsi->mode == LWSCM_WS_SERVING || + wsi->mode == LWSCM_WS_CLIENT) { + + if (wsi->u.ws.rx_draining_ext) { + struct lws **w = &pt->rx_draining_ext_list; + + wsi->u.ws.rx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.rx_draining_ext_list; + break; + } + w = &((*w)->u.ws.rx_draining_ext_list); + } + wsi->u.ws.rx_draining_ext_list = NULL; + } + + if (wsi->u.ws.tx_draining_ext) { + struct lws **w = &pt->tx_draining_ext_list; + + wsi->u.ws.tx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.tx_draining_ext_list; + break; + } + w = &((*w)->u.ws.tx_draining_ext_list); + } + wsi->u.ws.tx_draining_ext_list = NULL; + } + lws_free_set_NULL(wsi->u.ws.rx_ubuf); + + if (wsi->trunc_alloc) + /* not going to be completed... nuke it */ + lws_free_set_NULL(wsi->trunc_alloc); + + wsi->u.ws.ping_payload_len = 0; + wsi->u.ws.ping_pending_flag = 0; + } + + /* tell the user it's all over for this guy */ + + if (!wsi->told_user_closed && + wsi->mode != LWSCM_RAW && wsi->protocol && + wsi->protocol->callback && + (wsi->state_pre_close == LWSS_ESTABLISHED || + wsi->state_pre_close == LWSS_HTTP2_ESTABLISHED || + wsi->state_pre_close == LWSS_HTTP_BODY || + wsi->state_pre_close == LWSS_HTTP || + wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK || + wsi->state_pre_close == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION || + wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE || + (wsi->mode == LWSCM_WS_CLIENT && wsi->state_pre_close == LWSS_HTTP) || + (wsi->mode == LWSCM_WS_SERVING && wsi->state_pre_close == LWSS_HTTP))) { + lwsl_debug("calling back CLOSED %d %d\n", wsi->mode, wsi->state); + wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED, + wsi->user_space, NULL, 0); + } else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) { + lwsl_debug("calling back CLOSED_HTTP\n"); + wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP, + wsi->user_space, NULL, 0 ); + } else + lwsl_debug("not calling back closed mode=%d state=%d\n", + wsi->mode, wsi->state_pre_close); + + /* deallocate any active extension contexts */ + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0) + lwsl_warn("extension destruction failed\n"); + /* + * inform all extensions in case they tracked this guy out of band + * even though not active on him specifically + */ + if (lws_ext_cb_all_exts(context, wsi, + LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0) + lwsl_warn("ext destroy wsi failed\n"); + +async_close: + wsi->socket_is_permanently_unusable = 1; + +#ifdef LWS_WITH_LIBUV + if (!wsi->parent_carries_io && + lws_sockfd_valid(wsi->desc.sockfd)) + if (LWS_LIBUV_ENABLED(context)) { + if (wsi->listener) { + lwsl_debug("%s: stop listener poll\n", __func__); + uv_poll_stop(&wsi->w_read.uv_watcher); + } + lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", + __func__, wsi); + /* + * libuv has to do his own close handle processing + * asynchronously + */ + lws_libuv_closehandle(wsi); + + return; + } +#endif + + lws_close_free_wsi_final(wsi); +} + +void +lws_close_free_wsi_final(struct lws *wsi) +{ + int n; + + if (lws_socket_is_valid(wsi->desc.sockfd) && !lws_ssl_close(wsi)) { +#if LWS_POSIX + n = compatible_close(wsi->desc.sockfd); + if (n) + lwsl_debug("closing: close ret %d\n", LWS_ERRNO); + +#else + compatible_close(wsi->desc.sockfd); + (void)n; +#endif + wsi->desc.sockfd = LWS_SOCK_INVALID; + } + + /* outermost destroy notification for wsi (user_space still intact) */ + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, + wsi->user_space, NULL, 0); + +#ifdef LWS_WITH_CGI + if (wsi->cgi) { + + for (n = 0; n < 3; n++) { + if (wsi->cgi->pipe_fds[n][!!(n == 0)] == 0) + lwsl_err("ZERO FD IN CGI CLOSE"); + + if (wsi->cgi->pipe_fds[n][!!(n == 0)] >= 0) + close(wsi->cgi->pipe_fds[n][!!(n == 0)]); + } + + lws_free(wsi->cgi); + } +#endif + + lws_free_wsi(wsi); +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len) +{ + int n = 0, sl = strlen(name); + + while (lws_hdr_copy_fragment(wsi, buf, len, + WSI_TOKEN_HTTP_URI_ARGS, n) >= 0) { + + if (!strncmp(buf, name, sl)) + return buf + sl; + + n++; + } + + return NULL; +} + +#if LWS_POSIX && !defined(LWS_WITH_ESP32) +LWS_VISIBLE int +interface_to_sa(struct lws_vhost *vh, const char *ifname, + struct sockaddr_in *addr, size_t addrlen) +{ + int ipv6 = 0; +#ifdef LWS_WITH_IPV6 + ipv6 = LWS_IPV6_ENABLED(vh); +#endif + (void)vh; + + return lws_interface_to_sa(ipv6, ifname, addr, addrlen); +} +#endif + +#ifndef LWS_PLAT_OPTEE +#if LWS_POSIX +static int +lws_get_addresses(struct lws_vhost *vh, void *ads, char *name, + int name_len, char *rip, int rip_len) +{ +#if LWS_POSIX + struct addrinfo ai, *res; + struct sockaddr_in addr4; + + rip[0] = '\0'; + name[0] = '\0'; + addr4.sin_family = AF_UNSPEC; + +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(vh)) { + if (!lws_plat_inet_ntop(AF_INET6, + &((struct sockaddr_in6 *)ads)->sin6_addr, + rip, rip_len)) { + lwsl_err("inet_ntop: %s", strerror(LWS_ERRNO)); + return -1; + } + + // Strip off the IPv4 to IPv6 header if one exists + if (strncmp(rip, "::ffff:", 7) == 0) + memmove(rip, rip + 7, strlen(rip) - 6); + + getnameinfo((struct sockaddr *)ads, + sizeof(struct sockaddr_in6), name, name_len, NULL, 0, 0); + + return 0; + } else +#endif + { + struct addrinfo *result; + + memset(&ai, 0, sizeof ai); + ai.ai_family = PF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + ai.ai_flags = AI_CANONNAME; +#if !defined(LWS_WITH_ESP32) + if (getnameinfo((struct sockaddr *)ads, + sizeof(struct sockaddr_in), + name, name_len, NULL, 0, 0)) + return -1; +#endif + + if (getaddrinfo(name, NULL, &ai, &result)) + return -1; + + res = result; + while (addr4.sin_family == AF_UNSPEC && res) { + switch (res->ai_family) { + case AF_INET: + addr4.sin_addr = + ((struct sockaddr_in *)res->ai_addr)->sin_addr; + addr4.sin_family = AF_INET; + break; + } + + res = res->ai_next; + } + freeaddrinfo(result); + } + + if (addr4.sin_family == AF_UNSPEC) + return -1; + + if (lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len) == NULL) + return -1; + + return 0; +#else + (void)vh; + (void)ads; + (void)name; + (void)name_len; + (void)rip; + (void)rip_len; + + return -1; +#endif +} +#endif + + +LWS_VISIBLE const char * +lws_get_peer_simple(struct lws *wsi, char *name, int namelen) +{ +#if LWS_POSIX + socklen_t len, olen; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; + int af = AF_INET; + void *p, *q; + +#if defined(LWS_WITH_HTTP2) + if (wsi->http2_substream) + wsi = wsi->u.h2.parent_wsi; +#endif + + if (wsi->parent_carries_io) + wsi = wsi->parent; + +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + len = sizeof(sin6); + p = &sin6; + af = AF_INET6; + q = &sin6.sin6_addr; + } else +#endif + { + len = sizeof(sin4); + p = &sin4; + q = &sin4.sin_addr; + } + + olen = len; + if (getpeername(wsi->desc.sockfd, p, &len) < 0 || len > olen) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + return NULL; + } + + return lws_plat_inet_ntop(af, q, name, namelen); +#else +#if defined(LWS_WITH_ESP8266) + return lws_plat_get_peer_simple(wsi, name, namelen); +#else + return NULL; +#endif +#endif +} +#endif + +LWS_VISIBLE void +lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, + int name_len, char *rip, int rip_len) +{ +#ifndef LWS_PLAT_OPTEE +#if LWS_POSIX + socklen_t len; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; + struct lws_context *context = wsi->context; + int ret = -1; + void *p; + + rip[0] = '\0'; + name[0] = '\0'; + + lws_latency_pre(context, wsi); + +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + len = sizeof(sin6); + p = &sin6; + } else +#endif + { + len = sizeof(sin4); + p = &sin4; + } + + if (getpeername(fd, p, &len) < 0) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + goto bail; + } + + ret = lws_get_addresses(wsi->vhost, p, name, name_len, rip, rip_len); + +bail: + lws_latency(context, wsi, "lws_get_peer_addresses", ret, 1); +#endif +#endif + (void)wsi; + (void)fd; + (void)name; + (void)name_len; + (void)rip; + (void)rip_len; + +} + +LWS_EXTERN void * +lws_vhost_user(struct lws_vhost *vhost) +{ + return vhost->user; +} + +LWS_EXTERN void * +lws_context_user(struct lws_context *context) +{ + return context->user_space; +} + +LWS_VISIBLE struct lws_vhost * +lws_vhost_get(struct lws *wsi) +{ + return wsi->vhost; +} + +LWS_VISIBLE struct lws_vhost * +lws_get_vhost(struct lws *wsi) +{ + return wsi->vhost; +} + +LWS_VISIBLE const struct lws_protocols * +lws_protocol_get(struct lws *wsi) +{ + return wsi->protocol; +} + +LWS_VISIBLE struct lws * +lws_get_network_wsi(struct lws *wsi) +{ + if (!wsi) + return NULL; + +#if defined(LWS_WITH_HTTP2) + if (!wsi->http2_substream) + return wsi; + + while (wsi->u.h2.parent_wsi) + wsi = wsi->u.h2.parent_wsi; +#endif + + return wsi; +} + +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name) +{ + int n; + + for (n = 0; n < vh->count_protocols; n++) + if (!strcmp(name, vh->protocols[n].name)) + return &vh->protocols[n]; + + return NULL; +} + +LWS_VISIBLE int +lws_callback_all_protocol(struct lws_context *context, + const struct lws_protocols *protocol, int reason) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + unsigned int n, m = context->count_threads; + struct lws *wsi; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->protocol == protocol) + protocol->callback(wsi, reason, wsi->user_space, + NULL, 0); + } + pt++; + } + + return 0; +} + +LWS_VISIBLE int +lws_callback_all_protocol_vhost_args(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason, + void *argp, size_t len) +{ + struct lws_context *context = vh->context; + struct lws_context_per_thread *pt = &context->pt[0]; + unsigned int n, m = context->count_threads; + struct lws *wsi; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->vhost == vh && (wsi->protocol == protocol || + !protocol)) + wsi->protocol->callback(wsi, reason, + wsi->user_space, argp, len); + } + pt++; + } + + return 0; +} + +LWS_VISIBLE int +lws_callback_all_protocol_vhost(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason) +{ + return lws_callback_all_protocol_vhost_args(vh, protocol, reason, NULL, 0); +} + +LWS_VISIBLE LWS_EXTERN int +lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len) +{ + int n; + + for (n = 0; n < wsi->vhost->count_protocols; n++) + if (wsi->vhost->protocols[n].callback(wsi, reason, NULL, in, len)) + return 1; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN void +lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops) +{ + context->fops = fops; +} + +LWS_VISIBLE LWS_EXTERN lws_filepos_t +lws_vfs_tell(lws_fop_fd_t fop_fd) +{ + return fop_fd->pos; +} + +LWS_VISIBLE LWS_EXTERN lws_filepos_t +lws_vfs_get_length(lws_fop_fd_t fop_fd) +{ + return fop_fd->len; +} + +LWS_VISIBLE LWS_EXTERN uint32_t +lws_vfs_get_mod_time(lws_fop_fd_t fop_fd) +{ + return fop_fd->mod_time; +} + +LWS_VISIBLE lws_fileofs_t +lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + lws_fileofs_t ofs; + + ofs = fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset - fop_fd->pos); + + return ofs; +} + + +LWS_VISIBLE lws_fileofs_t +lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, fop_fd->len + + fop_fd->pos + offset); +} + + +const struct lws_plat_file_ops * +lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, + const char **vpath) +{ + const struct lws_plat_file_ops *pf; + const char *p = vfs_path; + int n; + + *vpath = NULL; + + /* no non-platform fops, just use that */ + + if (!fops->next) + return fops; + + /* + * scan the vfs path looking for indications we are to be + * handled by a specific fops + */ + + while (p && *p) { + if (*p != '/') { + p++; + continue; + } + /* the first one is always platform fops, so skip */ + pf = fops->next; + while (pf) { + n = 0; + while (n < ARRAY_SIZE(pf->fi) && pf->fi[n].sig) { + if (p >= vfs_path + pf->fi[n].len) + if (!strncmp(p - (pf->fi[n].len - 1), + pf->fi[n].sig, + pf->fi[n].len - 1)) { + *vpath = p + 1; + return pf; + } + + n++; + } + pf = pf->next; + } + p++; + } + + return fops; +} + +LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT +lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path, + lws_fop_flags_t *flags) +{ + const char *vpath = ""; + const struct lws_plat_file_ops *selected; + + selected = lws_vfs_select_fops(fops, vfs_path, &vpath); + + return selected->LWS_FOP_OPEN(fops, vfs_path, vpath, flags); +} + + +/** + * lws_now_secs() - seconds since 1970-1-1 + * + */ +LWS_VISIBLE LWS_EXTERN unsigned long +lws_now_secs(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return tv.tv_sec; +} + + +#if LWS_POSIX + +LWS_VISIBLE int +lws_get_socket_fd(struct lws *wsi) +{ + if (!wsi) + return -1; + return wsi->desc.sockfd; +} + +#endif + +#ifdef LWS_LATENCY +void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completed) +{ + unsigned long long u; + char buf[256]; + + u = time_in_microseconds(); + + if (!action) { + wsi->latency_start = u; + if (!wsi->action_start) + wsi->action_start = u; + return; + } + if (completed) { + if (wsi->action_start == wsi->latency_start) + sprintf(buf, + "Completion first try lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, + (void *)wsi, ret, action); + else + sprintf(buf, + "Completion %lluus: lat %lluus: %p: ret %d: %s\n", + u - wsi->action_start, + u - wsi->latency_start, + (void *)wsi, ret, action); + wsi->action_start = 0; + } else + sprintf(buf, "lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, (void *)wsi, ret, action); + + if (u - wsi->latency_start > context->worst_latency) { + context->worst_latency = u - wsi->latency_start; + strcpy(context->worst_latency_info, buf); + } + lwsl_latency("%s", buf); +} +#endif + +LWS_VISIBLE int +lws_rx_flow_control(struct lws *wsi, int _enable) +{ + int en = _enable; + + lwsl_info("%s: %p 0x%x\n", __func__, wsi, _enable); + + if (!(_enable & LWS_RXFLOW_REASON_APPLIES)) { + /* + * convert user bool style to bitmap style... in user simple + * bool style _enable = 0 = flow control it, = 1 = allow rx + */ + en = LWS_RXFLOW_REASON_APPLIES | LWS_RXFLOW_REASON_USER_BOOL; + if (_enable & 1) + en |= LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT; + } + + /* any bit set in rxflow_bitmap DISABLEs rxflow control */ + if (en & LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT) + wsi->rxflow_bitmap &= ~(en & 0xff); + else + wsi->rxflow_bitmap |= en & 0xff; + + if ((LWS_RXFLOW_PENDING_CHANGE | (!wsi->rxflow_bitmap)) == + wsi->rxflow_change_to) + return 0; + + wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !wsi->rxflow_bitmap; + + lwsl_info("%s: 0x%p: bitmap 0x%x: en 0x%x, ch 0x%x\n", __func__, wsi, + wsi->rxflow_bitmap, en, wsi->rxflow_change_to); + + if (_enable & LWS_RXFLOW_REASON_FLAG_PROCESS_NOW || + !wsi->rxflow_will_be_applied) + return _lws_rx_flow_control(wsi); + + return 0; +} + +LWS_VISIBLE void +lws_rx_flow_allow_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol) +{ + const struct lws_context_per_thread *pt = &context->pt[0]; + struct lws *wsi; + unsigned int n, m = context->count_threads; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->protocol == protocol) + lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW); + } + pt++; + } +} + +LWS_VISIBLE extern const char * +lws_canonical_hostname(struct lws_context *context) +{ + return (const char *)context->canonical_hostname; +} + +int user_callback_handle_rxflow(lws_callback_function callback_function, + struct lws *wsi, + enum lws_callback_reasons reason, void *user, + void *in, size_t len) +{ + int n; + + wsi->rxflow_will_be_applied = 1; + n = callback_function(wsi, reason, user, in, len); + wsi->rxflow_will_be_applied = 0; + if (!n) + n = _lws_rx_flow_control(wsi); + + return n; +} + +#if defined(LWS_WITH_ESP8266) +#undef strchr +#define strchr ets_strchr +#endif + +LWS_VISIBLE int +lws_set_proxy(struct lws_vhost *vhost, const char *proxy) +{ +#if !defined(LWS_WITH_ESP8266) + char *p; + char authstring[96]; + + if (!proxy) + return -1; + + /* we have to deal with a possible redundant leading http:// */ + if (!strncmp(proxy, "http://", 7)) + proxy += 7; + + p = strchr(proxy, '@'); + if (p) { /* auth is around */ + + if ((unsigned int)(p - proxy) > sizeof(authstring) - 1) + goto auth_too_long; + + strncpy(authstring, proxy, p - proxy); + // null termination not needed on input + if (lws_b64_encode_string(authstring, (p - proxy), + vhost->proxy_basic_auth_token, + sizeof vhost->proxy_basic_auth_token) < 0) + goto auth_too_long; + + lwsl_info(" Proxy auth in use\n"); + + proxy = p + 1; + } else + vhost->proxy_basic_auth_token[0] = '\0'; + + strncpy(vhost->http_proxy_address, proxy, + sizeof(vhost->http_proxy_address) - 1); + vhost->http_proxy_address[ + sizeof(vhost->http_proxy_address) - 1] = '\0'; + + p = strchr(vhost->http_proxy_address, ':'); + if (!p && !vhost->http_proxy_port) { + lwsl_err("http_proxy needs to be ads:port\n"); + + return -1; + } else { + if (p) { + *p = '\0'; + vhost->http_proxy_port = atoi(p + 1); + } + } + + lwsl_info(" Proxy %s:%u\n", vhost->http_proxy_address, + vhost->http_proxy_port); + + return 0; + +auth_too_long: + lwsl_err("proxy auth too long\n"); +#endif + return -1; +} + +#if defined(LWS_WITH_SOCKS5) +LWS_VISIBLE int +lws_set_socks(struct lws_vhost *vhost, const char *socks) +{ +#if !defined(LWS_WITH_ESP8266) + char *p_at, *p_colon; + char user[96]; + char password[96]; + + if (!socks) + return -1; + + vhost->socks_user[0] = '\0'; + vhost->socks_password[0] = '\0'; + + p_at = strchr(socks, '@'); + if (p_at) { /* auth is around */ + if ((unsigned int)(p_at - socks) > (sizeof(user) + + sizeof(password) - 2)) { + lwsl_err("Socks auth too long\n"); + goto bail; + } + + p_colon = strchr(socks, ':'); + if (p_colon) { + if ((unsigned int)(p_colon - socks) > (sizeof(user) + - 1) ) { + lwsl_err("Socks user too long\n"); + goto bail; + } + if ((unsigned int)(p_at - p_colon) > (sizeof(password) + - 1) ) { + lwsl_err("Socks password too long\n"); + goto bail; + } + + strncpy(vhost->socks_user, socks, p_colon - socks); + strncpy(vhost->socks_password, p_colon + 1, + p_at - (p_colon + 1)); + } + + lwsl_info(" Socks auth, user: %s, password: %s\n", + vhost->socks_user, vhost->socks_password ); + + socks = p_at + 1; + } + + strncpy(vhost->socks_proxy_address, socks, + sizeof(vhost->socks_proxy_address) - 1); + vhost->socks_proxy_address[sizeof(vhost->socks_proxy_address) - 1] + = '\0'; + + p_colon = strchr(vhost->socks_proxy_address, ':'); + if (!p_colon && !vhost->socks_proxy_port) { + lwsl_err("socks_proxy needs to be address:port\n"); + return -1; + } else { + if (p_colon) { + *p_colon = '\0'; + vhost->socks_proxy_port = atoi(p_colon + 1); + } + } + + lwsl_info(" Socks %s:%u\n", vhost->socks_proxy_address, + vhost->socks_proxy_port); + + return 0; + +bail: +#endif + return -1; +} +#endif + +LWS_VISIBLE const struct lws_protocols * +lws_get_protocol(struct lws *wsi) +{ + return wsi->protocol; +} + +LWS_VISIBLE int +lws_is_final_fragment(struct lws *wsi) +{ + lwsl_info("%s: final %d, rx pk length %ld, draining %ld\n", __func__, + wsi->u.ws.final, (long)wsi->u.ws.rx_packet_length, + (long)wsi->u.ws.rx_draining_ext); + return wsi->u.ws.final && !wsi->u.ws.rx_packet_length && + !wsi->u.ws.rx_draining_ext; +} + +LWS_VISIBLE int +lws_is_first_fragment(struct lws *wsi) +{ + return wsi->u.ws.first_fragment; +} + +LWS_VISIBLE unsigned char +lws_get_reserved_bits(struct lws *wsi) +{ + return wsi->u.ws.rsv; +} + +int +lws_ensure_user_space(struct lws *wsi) +{ + if (!wsi->protocol) + return 0; + + /* allocate the per-connection user memory (if any) */ + + if (wsi->protocol->per_session_data_size && !wsi->user_space) { + wsi->user_space = lws_zalloc(wsi->protocol->per_session_data_size, "user space"); + if (wsi->user_space == NULL) { + lwsl_err("%s: OOM\n", __func__); + return 1; + } + } else + lwsl_debug("%s: %p protocol pss %lu, user_space=%p\n", __func__, + wsi, (long)wsi->protocol->per_session_data_size, + wsi->user_space); + return 0; +} + +LWS_VISIBLE void * +lws_adjust_protocol_psds(struct lws *wsi, size_t new_size) +{ + ((struct lws_protocols *)lws_get_protocol(wsi))->per_session_data_size = + new_size; + + if (lws_ensure_user_space(wsi)) + return NULL; + + return wsi->user_space; +} + +LWS_VISIBLE int +lwsl_timestamp(int level, char *p, int len) +{ +#ifndef LWS_PLAT_OPTEE + time_t o_now = time(NULL); + unsigned long long now; + struct tm *ptm = NULL; +#ifndef WIN32 + struct tm tm; +#endif + int n; + +#ifndef _WIN32_WCE +#ifdef WIN32 + ptm = localtime(&o_now); +#else + if (localtime_r(&o_now, &tm)) + ptm = &tm; +#endif +#endif + p[0] = '\0'; + for (n = 0; n < LLL_COUNT; n++) { + if (level != (1 << n)) + continue; + now = time_in_microseconds() / 100; + if (ptm) + n = lws_snprintf(p, len, + "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %s: ", + ptm->tm_year + 1900, + ptm->tm_mon + 1, + ptm->tm_mday, + ptm->tm_hour, + ptm->tm_min, + ptm->tm_sec, + (int)(now % 10000), log_level_names[n]); + else + n = lws_snprintf(p, len, "[%llu:%04d] %s: ", + (unsigned long long) now / 10000, + (int)(now % 10000), log_level_names[n]); + return n; + } +#endif + return 0; +} + +static const char * const colours[] = { + "[31;1m", /* LLL_ERR */ + "[36;1m", /* LLL_WARN */ + "[35;1m", /* LLL_NOTICE */ + "[32;1m", /* LLL_INFO */ + "[34;1m", /* LLL_DEBUG */ + "[33;1m", /* LLL_PARSER */ + "[33;1m", /* LLL_HEADER */ + "[33;1m", /* LLL_EXT */ + "[33;1m", /* LLL_CLIENT */ + "[33;1m", /* LLL_LATENCY */ + "[30;1m", /* LLL_USER */ +}; + +#ifndef LWS_PLAT_OPTEE +LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line) +{ +#if !defined(LWS_WITH_ESP8266) + char buf[50]; + static char tty; + int n, m = ARRAY_SIZE(colours) - 1; + + if (!tty) + tty = isatty(2) | 2; + + lwsl_timestamp(level, buf, sizeof(buf)); + + if (tty == 3) { + n = 1 << (ARRAY_SIZE(colours) - 1); + while (n) { + if (level & n) + break; + m--; + n >>= 1; + } + fprintf(stderr, "%c%s%s%s%c[0m", 27, colours[m], buf, line, 27); + } else + fprintf(stderr, "%s%s", buf, line); +#endif +} +#endif + +LWS_VISIBLE void _lws_logv(int filter, const char *format, va_list vl) +{ +#if defined(LWS_WITH_ESP8266) + char buf[128]; +#else + char buf[256]; +#endif + int n; + + if (!(log_level & filter)) + return; + + n = vsnprintf(buf, sizeof(buf) - 1, format, vl); + (void)n; +#if defined(LWS_WITH_ESP8266) + buf[sizeof(buf) - 1] = '\0'; +#else + /* vnsprintf returns what it would have written, even if truncated */ + if (n > sizeof(buf) - 1) + n = sizeof(buf) - 1; + if (n > 0) + buf[n] = '\0'; +#endif + + lwsl_emit(filter, buf); +} + +LWS_VISIBLE void _lws_log(int filter, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _lws_logv(filter, format, ap); + va_end(ap); +} + +LWS_VISIBLE void lws_set_log_level(int level, + void (*func)(int level, const char *line)) +{ + log_level = level; + if (func) + lwsl_emit = func; +} + +LWS_VISIBLE int lwsl_visible(int level) +{ + return log_level & level; +} + +LWS_VISIBLE void +lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len) +{ + unsigned char *buf = (unsigned char *)vbuf; + unsigned int n, m, start; + char line[80]; + char *p; + + if (!lwsl_visible(hexdump_level)) + return; + + _lws_log(hexdump_level, "\n"); + + for (n = 0; n < len;) { + start = n; + p = line; + + p += sprintf(p, "%04X: ", start); + + for (m = 0; m < 16 && n < len; m++) + p += sprintf(p, "%02X ", buf[n++]); + while (m++ < 16) + p += sprintf(p, " "); + + p += sprintf(p, " "); + + for (m = 0; m < 16 && (start + m) < len; m++) { + if (buf[start + m] >= ' ' && buf[start + m] < 127) + *p++ = buf[start + m]; + else + *p++ = '.'; + } + while (m++ < 16) + *p++ = ' '; + + *p++ = '\n'; + *p = '\0'; + _lws_log(hexdump_level, "%s", line); + (void)line; + } + + _lws_log(hexdump_level, "\n"); +} + +LWS_VISIBLE void +lwsl_hexdump(const void *vbuf, size_t len) +{ + lwsl_hexdump_level(LLL_DEBUG, vbuf, len); +} + +LWS_VISIBLE int +lws_is_ssl(struct lws *wsi) +{ +#ifdef LWS_OPENSSL_SUPPORT + return wsi->use_ssl; +#else + (void)wsi; + return 0; +#endif +} + +#ifdef LWS_OPENSSL_SUPPORT +LWS_VISIBLE SSL* +lws_get_ssl(struct lws *wsi) +{ + return wsi->ssl; +} +#endif + +LWS_VISIBLE int +lws_partial_buffered(struct lws *wsi) +{ + return !!wsi->trunc_len; +} + +LWS_VISIBLE size_t +lws_get_peer_write_allowance(struct lws *wsi) +{ +#ifdef LWS_WITH_HTTP2 + /* only if we are using HTTP2 on this connection */ + if (wsi->mode != LWSCM_HTTP2_SERVING) + return -1; + + return lws_h2_tx_cr_get(wsi); +#else + (void)wsi; + return -1; +#endif +} + +LWS_VISIBLE void +lws_union_transition(struct lws *wsi, enum connection_mode mode) +{ + lwsl_debug("%s: %p: mode %d\n", __func__, wsi, mode); + memset(&wsi->u, 0, sizeof(wsi->u)); + wsi->mode = mode; +} + +LWS_VISIBLE struct lws_plat_file_ops * +lws_get_fops(struct lws_context *context) +{ + return (struct lws_plat_file_ops *)context->fops; +} + +LWS_VISIBLE LWS_EXTERN struct lws_context * +lws_get_context(const struct lws *wsi) +{ + return wsi->context; +} + +LWS_VISIBLE LWS_EXTERN int +lws_get_count_threads(struct lws_context *context) +{ + return context->count_threads; +} + +LWS_VISIBLE LWS_EXTERN void * +lws_wsi_user(struct lws *wsi) +{ + return wsi->user_space; +} + +LWS_VISIBLE LWS_EXTERN void +lws_set_wsi_user(struct lws *wsi, void *data) +{ + if (wsi->user_space_externally_allocated) + wsi->user_space = data; + else + lwsl_err("%s: Cannot set internally-allocated user_space\n", + __func__); +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_parent(const struct lws *wsi) +{ + return wsi->parent; +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_child(const struct lws *wsi) +{ + return wsi->child_list; +} + +LWS_VISIBLE LWS_EXTERN void +lws_set_parent_carries_io(struct lws *wsi) +{ + wsi->parent_carries_io = 1; +} + +LWS_VISIBLE LWS_EXTERN void * +lws_get_opaque_parent_data(const struct lws *wsi) +{ + return wsi->opaque_parent_data; +} + +LWS_VISIBLE LWS_EXTERN void +lws_set_opaque_parent_data(struct lws *wsi, void *data) +{ + wsi->opaque_parent_data = data; +} + +LWS_VISIBLE LWS_EXTERN int +lws_get_child_pending_on_writable(const struct lws *wsi) +{ + return wsi->parent_pending_cb_on_writable; +} + +LWS_VISIBLE LWS_EXTERN void +lws_clear_child_pending_on_writable(struct lws *wsi) +{ + wsi->parent_pending_cb_on_writable = 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_get_close_length(struct lws *wsi) +{ + return wsi->u.ws.close_in_ping_buffer_len; +} + +LWS_VISIBLE LWS_EXTERN unsigned char * +lws_get_close_payload(struct lws *wsi) +{ + return &wsi->u.ws.ping_payload_buf[LWS_PRE]; +} + +LWS_VISIBLE LWS_EXTERN void +lws_close_reason(struct lws *wsi, enum lws_close_status status, + unsigned char *buf, size_t len) +{ + unsigned char *p, *start; + int budget = sizeof(wsi->u.ws.ping_payload_buf) - LWS_PRE; + + assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT); + + start = p = &wsi->u.ws.ping_payload_buf[LWS_PRE]; + + *p++ = (((int)status) >> 8) & 0xff; + *p++ = ((int)status) & 0xff; + + if (buf) + while (len-- && p < start + budget) + *p++ = *buf++; + + wsi->u.ws.close_in_ping_buffer_len = p - start; +} + +LWS_EXTERN int +_lws_rx_flow_control(struct lws *wsi) +{ + struct lws *wsic = wsi->child_list; + + /* if he has children, do those if they were changed */ + while (wsic) { + if (wsic->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE) + _lws_rx_flow_control(wsic); + + wsic = wsic->sibling_list; + } + + /* there is no pending change */ + if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) + return 0; + + /* stuff is still buffered, not ready to really accept new input */ + if (wsi->rxflow_buffer) { + /* get ourselves called back to deal with stashed buffer */ + lws_callback_on_writable(wsi); + return 0; + } + + /* pending is cleared, we can change rxflow state */ + + wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE; + + lwsl_info("rxflow: wsi %p change_to %d\n", wsi, + wsi->rxflow_change_to & LWS_RXFLOW_ALLOW); + + /* adjust the pollfd for this wsi */ + + if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) { + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lwsl_info("%s: fail\n", __func__); + return -1; + } + } else + if (lws_change_pollfd(wsi, LWS_POLLIN, 0)) + return -1; + + return 0; +} + +LWS_EXTERN int +lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len) +{ + static const unsigned char e0f4[] = { + 0xa0 | ((2 - 1) << 2) | 1, /* e0 */ + 0x80 | ((4 - 1) << 2) | 1, /* e1 */ + 0x80 | ((4 - 1) << 2) | 1, /* e2 */ + 0x80 | ((4 - 1) << 2) | 1, /* e3 */ + 0x80 | ((4 - 1) << 2) | 1, /* e4 */ + 0x80 | ((4 - 1) << 2) | 1, /* e5 */ + 0x80 | ((4 - 1) << 2) | 1, /* e6 */ + 0x80 | ((4 - 1) << 2) | 1, /* e7 */ + 0x80 | ((4 - 1) << 2) | 1, /* e8 */ + 0x80 | ((4 - 1) << 2) | 1, /* e9 */ + 0x80 | ((4 - 1) << 2) | 1, /* ea */ + 0x80 | ((4 - 1) << 2) | 1, /* eb */ + 0x80 | ((4 - 1) << 2) | 1, /* ec */ + 0x80 | ((2 - 1) << 2) | 1, /* ed */ + 0x80 | ((4 - 1) << 2) | 1, /* ee */ + 0x80 | ((4 - 1) << 2) | 1, /* ef */ + 0x90 | ((3 - 1) << 2) | 2, /* f0 */ + 0x80 | ((4 - 1) << 2) | 2, /* f1 */ + 0x80 | ((4 - 1) << 2) | 2, /* f2 */ + 0x80 | ((4 - 1) << 2) | 2, /* f3 */ + 0x80 | ((1 - 1) << 2) | 2, /* f4 */ + + 0, /* s0 */ + 0x80 | ((4 - 1) << 2) | 0, /* s2 */ + 0x80 | ((4 - 1) << 2) | 1, /* s3 */ + }; + unsigned char s = *state; + + while (len--) { + unsigned char c = *buf++; + + if (!s) { + if (c >= 0x80) { + if (c < 0xc2 || c > 0xf4) + return 1; + if (c < 0xe0) + s = 0x80 | ((4 - 1) << 2); + else + s = e0f4[c - 0xe0]; + } + } else { + if (c < (s & 0xf0) || + c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30)) + return 1; + s = e0f4[21 + (s & 3)]; + } + } + + *state = s; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_parse_uri(char *p, const char **prot, const char **ads, int *port, + const char **path) +{ + const char *end; + static const char *slash = "/"; + + /* cut up the location into address, port and path */ + *prot = p; + while (*p && (*p != ':' || p[1] != '/' || p[2] != '/')) + p++; + if (!*p) { + end = p; + p = (char *)*prot; + *prot = end; + } else { + *p = '\0'; + p += 3; + } + *ads = p; + if (!strcmp(*prot, "http") || !strcmp(*prot, "ws")) + *port = 80; + else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss")) + *port = 443; + + if (*p == '[') + { + ++(*ads); + while (*p && *p != ']') + p++; + if (*p) + *p++ = '\0'; + } + else + { + while (*p && *p != ':' && *p != '/') + p++; + } + if (*p == ':') { + *p++ = '\0'; + *port = atoi(p); + while (*p && *p != '/') + p++; + } + *path = slash; + if (*p) { + *p++ = '\0'; + if (*p) + *path = p; + } + + return 0; +} + +#ifdef LWS_NO_EXTENSIONS + +/* we need to provide dummy callbacks for internal exts + * so user code runs when faced with a lib compiled with + * extensions disabled. + */ + +int +lws_extension_callback_pm_deflate(struct lws_context *context, + const struct lws_extension *ext, + struct lws *wsi, + enum lws_extension_callback_reasons reason, + void *user, void *in, size_t len) +{ + (void)context; + (void)ext; + (void)wsi; + (void)reason; + (void)user; + (void)in; + (void)len; + + return 0; +} +#endif + +LWS_EXTERN int +lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, + const char *iface) +{ +#if LWS_POSIX +#ifdef LWS_WITH_UNIX_SOCK + struct sockaddr_un serv_unix; +#endif +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 serv_addr6; +#endif + struct sockaddr_in serv_addr4; +#ifndef LWS_PLAT_OPTEE + socklen_t len = sizeof(struct sockaddr_storage); +#endif + int n; + struct sockaddr_storage sin; + struct sockaddr *v; + +#ifdef LWS_WITH_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vhost)) { + v = (struct sockaddr *)&serv_unix; + n = sizeof(struct sockaddr_un); + bzero((char *) &serv_unix, sizeof(serv_unix)); + serv_unix.sun_family = AF_UNIX; + if (sizeof(serv_unix.sun_path) <= strlen(iface)) { + lwsl_err("\"%s\" too long for UNIX domain socket\n", + iface); + return -1; + } + strcpy(serv_unix.sun_path, iface); + if (serv_unix.sun_path[0] == '@') + serv_unix.sun_path[0] = '\0'; + + } else +#endif +#if defined(LWS_WITH_IPV6) && !defined(LWS_WITH_ESP32) + if (LWS_IPV6_ENABLED(vhost)) { + v = (struct sockaddr *)&serv_addr6; + n = sizeof(struct sockaddr_in6); + bzero((char *) &serv_addr6, sizeof(serv_addr6)); + if (iface) { + if (interface_to_sa(vhost, iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find if %s\n", iface); + return -1; + } + serv_addr6.sin6_scope_id = lws_get_addr_scope(iface); + } + + serv_addr6.sin6_family = AF_INET6; + serv_addr6.sin6_port = htons(port); + } else +#endif + { + v = (struct sockaddr *)&serv_addr4; + n = sizeof(serv_addr4); + bzero((char *) &serv_addr4, sizeof(serv_addr4)); + serv_addr4.sin_addr.s_addr = INADDR_ANY; + serv_addr4.sin_family = AF_INET; +#if !defined(LWS_WITH_ESP32) + + if (iface && + interface_to_sa(vhost, iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find interface %s\n", iface); + return -1; + } +#endif + serv_addr4.sin_port = htons(port); + } /* ipv4 */ + + n = bind(sockfd, v, n); +#ifdef LWS_WITH_UNIX_SOCK + if (n < 0 && LWS_UNIX_SOCK_ENABLED(vhost)) { + lwsl_err("ERROR on binding fd %d to \"%s\" (%d %d)\n", + sockfd, iface, n, LWS_ERRNO); + return -1; + } else +#endif + if (n < 0) { + lwsl_err("ERROR on binding fd %d to port %d (%d %d)\n", + sockfd, port, n, LWS_ERRNO); + return -1; + } + +#ifndef LWS_PLAT_OPTEE + if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) + lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO)); + else +#endif +#if defined(LWS_WITH_IPV6) + port = (sin.ss_family == AF_INET6) ? + ntohs(((struct sockaddr_in6 *) &sin)->sin6_port) : + ntohs(((struct sockaddr_in *) &sin)->sin_port); +#else + { + struct sockaddr_in sain; + memcpy(&sain, &sin, sizeof(sain)); + port = ntohs(sain.sin_port); + } +#endif +#endif + + return port; +} + +#if defined(LWS_WITH_IPV6) +LWS_EXTERN unsigned long +lws_get_addr_scope(const char *ipaddr) +{ + unsigned long scope = 0; + +#ifndef WIN32 + struct ifaddrs *addrs, *addr; + char ip[NI_MAXHOST]; + unsigned int i; + + getifaddrs(&addrs); + for (addr = addrs; addr; addr = addr->ifa_next) { + if (!addr->ifa_addr || + addr->ifa_addr->sa_family != AF_INET6) + continue; + + getnameinfo(addr->ifa_addr, + sizeof(struct sockaddr_in6), + ip, sizeof(ip), + NULL, 0, NI_NUMERICHOST); + + i = 0; + while (ip[i]) + if (ip[i++] == '%') { + ip[i - 1] = '\0'; + break; + } + + if (!strcmp(ip, ipaddr)) { + scope = if_nametoindex(addr->ifa_name); + break; + } + } + freeifaddrs(addrs); +#else + PIP_ADAPTER_ADDRESSES adapter, addrs = NULL; + PIP_ADAPTER_UNICAST_ADDRESS addr; + ULONG size = 0; + DWORD ret; + struct sockaddr_in6 *sockaddr; + char ip[NI_MAXHOST]; + unsigned int i; + int found = 0; + + for (i = 0; i < 5; i++) + { + ret = GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, + NULL, addrs, &size); + if ((ret == NO_ERROR) || (ret == ERROR_NO_DATA)) { + break; + } else if (ret == ERROR_BUFFER_OVERFLOW) + { + if (addrs) + free(addrs); + addrs = (IP_ADAPTER_ADDRESSES *)malloc(size); + } else + { + if (addrs) + { + free(addrs); + addrs = NULL; + } + lwsl_err("Failed to get IPv6 address table (%d)", ret); + break; + } + } + + if ((ret == NO_ERROR) && (addrs)) { + adapter = addrs; + while (adapter && !found) { + addr = adapter->FirstUnicastAddress; + while (addr && !found) { + if (addr->Address.lpSockaddr->sa_family == AF_INET6) { + sockaddr = (struct sockaddr_in6 *) + (addr->Address.lpSockaddr); + + lws_plat_inet_ntop(sockaddr->sin6_family, + &sockaddr->sin6_addr, + ip, sizeof(ip)); + + if (!strcmp(ip, ipaddr)) { + scope = sockaddr->sin6_scope_id; + found = 1; + break; + } + } + addr = addr->Next; + } + adapter = adapter->Next; + } + } + if (addrs) + free(addrs); +#endif + + return scope; +} +#endif + +LWS_EXTERN void +lws_restart_ws_ping_pong_timer(struct lws *wsi) +{ + if (!wsi->context->ws_ping_pong_interval) + return; + if (wsi->state != LWSS_ESTABLISHED) + return; + + wsi->u.ws.time_next_ping_check = (time_t)lws_now_secs() + + wsi->context->ws_ping_pong_interval; +} + +static const char *hex = "0123456789ABCDEF"; + +LWS_VISIBLE LWS_EXTERN const char * +lws_sql_purify(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + while (*p && len-- > 2) { + if (*p == '\'') { + *q++ = '\''; + *q++ = '\''; + len --; + p++; + } else + *q++ = *p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_json_purify(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + if (!p) { + escaped[0] = '\0'; + return escaped; + } + + while (*p && len-- > 6) { + if (*p == '\"' || *p == '\\' || *p < 0x20) { + *q++ = '\\'; + *q++ = 'u'; + *q++ = '0'; + *q++ = '0'; + *q++ = hex[((*p) >> 4) & 15]; + *q++ = hex[(*p) & 15]; + len -= 5; + p++; + } else + *q++ = *p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_urlencode(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + while (*p && len-- > 3) { + if (*p == ' ') { + *q++ = '+'; + p++; + continue; + } + if ((*p >= '0' && *p <= '9') || + (*p >= 'A' && *p <= 'Z') || + (*p >= 'a' && *p <= 'z')) { + *q++ = *p++; + continue; + } + *q++ = '%'; + *q++ = hex[(*p >> 4) & 0xf]; + *q++ = hex[*p & 0xf]; + + len -= 2; + p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN int +lws_urldecode(char *string, const char *escaped, int len) +{ + int state = 0, n; + char sum = 0; + + while (*escaped && len) { + switch (state) { + case 0: + if (*escaped == '%') { + state++; + escaped++; + continue; + } + if (*escaped == '+') { + escaped++; + *string++ = ' '; + len--; + continue; + } + *string++ = *escaped++; + len--; + break; + case 1: + n = char_to_hex(*escaped); + if (n < 0) + return -1; + escaped++; + sum = n << 4; + state++; + break; + + case 2: + n = char_to_hex(*escaped); + if (n < 0) + return -1; + escaped++; + *string++ = sum | n; + len--; + state = 0; + break; + } + + } + *string = '\0'; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_finalize_startup(struct lws_context *context) +{ + struct lws_context_creation_info info; + + info.uid = context->uid; + info.gid = context->gid; + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + memcpy(info.caps, context->caps, sizeof(info.caps)); + info.count_caps = context->count_caps; +#endif + + if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lws_plat_drop_app_privileges(&info); + + return 0; +} + +int +lws_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int n; + + if (!size) + return 0; + + va_start(ap, format); + n = vsnprintf(str, size, format, ap); + va_end(ap); + + if (n >= (int)size) + return size; + + return n; +} + + +LWS_VISIBLE LWS_EXTERN int +lws_is_cgi(struct lws *wsi) { +#ifdef LWS_WITH_CGI + return !!wsi->cgi; +#else + return 0; +#endif +} + + + +#ifdef LWS_NO_EXTENSIONS +LWS_EXTERN int +lws_set_extension_option(struct lws *wsi, const char *ext_name, + const char *opt_name, const char *opt_val) +{ + return -1; +} +#endif + + +void +lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs) +{ + const struct lws_vhost *vh = ctx->vhost_list; + + while (vh) { + + cs->rx += vh->conn_stats.rx; + cs->tx += vh->conn_stats.tx; + cs->h1_conn += vh->conn_stats.h1_conn; + cs->h1_trans += vh->conn_stats.h1_trans; + cs->h2_trans += vh->conn_stats.h2_trans; + cs->ws_upg += vh->conn_stats.ws_upg; + cs->h2_upg += vh->conn_stats.h2_upg; + cs->h2_alpn += vh->conn_stats.h2_alpn; + cs->h2_subs += vh->conn_stats.h2_subs; + cs->rejected += vh->conn_stats.rejected; + + vh = vh->vhost_next; + } +} + +#ifdef LWS_WITH_SERVER_STATUS + +LWS_EXTERN int +lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len) +{ + static const char * const prots[] = { + "http://", + "https://", + "file://", + "cgi://", + ">http://", + ">https://", + "callback://" + }; + char *orig = buf, *end = buf + len - 1, first = 1; + int n = 0; + + if (len < 100) + return 0; + + buf += lws_snprintf(buf, end - buf, + "{\n \"name\":\"%s\",\n" + " \"port\":\"%d\",\n" + " \"use_ssl\":\"%d\",\n" + " \"sts\":\"%d\",\n" + " \"rx\":\"%llu\",\n" + " \"tx\":\"%llu\",\n" + " \"h1_conn\":\"%lu\",\n" + " \"h1_trans\":\"%lu\",\n" + " \"h2_trans\":\"%lu\",\n" + " \"ws_upg\":\"%lu\",\n" + " \"rejected\":\"%lu\",\n" + " \"h2_upg\":\"%lu\",\n" + " \"h2_alpn\":\"%lu\",\n" + " \"h2_subs\":\"%lu\"" + , + vh->name, vh->listen_port, +#ifdef LWS_OPENSSL_SUPPORT + vh->use_ssl, +#else + 0, +#endif + !!(vh->options & LWS_SERVER_OPTION_STS), + vh->conn_stats.rx, vh->conn_stats.tx, + vh->conn_stats.h1_conn, + vh->conn_stats.h1_trans, + vh->conn_stats.h2_trans, + vh->conn_stats.ws_upg, + vh->conn_stats.rejected, + vh->conn_stats.h2_upg, + vh->conn_stats.h2_alpn, + vh->conn_stats.h2_subs + ); + + if (vh->mount_list) { + const struct lws_http_mount *m = vh->mount_list; + + buf += lws_snprintf(buf, end - buf, ",\n \"mounts\":["); + while (m) { + if (!first) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n \"mountpoint\":\"%s\",\n" + " \"origin\":\"%s%s\",\n" + " \"cache_max_age\":\"%d\",\n" + " \"cache_reuse\":\"%d\",\n" + " \"cache_revalidate\":\"%d\",\n" + " \"cache_intermediaries\":\"%d\"\n" + , + m->mountpoint, + prots[m->origin_protocol], + m->origin, + m->cache_max_age, + m->cache_reusable, + m->cache_revalidate, + m->cache_intermediaries); + if (m->def) + buf += lws_snprintf(buf, end - buf, + ",\n \"default\":\"%s\"", + m->def); + buf += lws_snprintf(buf, end - buf, "\n }"); + first = 0; + m = m->mount_next; + } + buf += lws_snprintf(buf, end - buf, "\n ]"); + } + + if (vh->protocols) { + n = 0; + first = 1; + + buf += lws_snprintf(buf, end - buf, ",\n \"ws-protocols\":["); + while (n < vh->count_protocols) { + if (!first) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n \"%s\":{\n" + " \"status\":\"ok\"\n }\n }" + , + vh->protocols[n].name); + first = 0; + n++; + } + buf += lws_snprintf(buf, end - buf, "\n ]"); + } + + buf += lws_snprintf(buf, end - buf, "\n}"); + + return buf - orig; +} + + +LWS_EXTERN LWS_VISIBLE int +lws_json_dump_context(const struct lws_context *context, char *buf, int len, + int hide_vhosts) +{ + char *orig = buf, *end = buf + len - 1, first = 1; + const struct lws_vhost *vh = context->vhost_list; + const struct lws_context_per_thread *pt; + time_t t = time(NULL); + int n, listening = 0, cgi_count = 0; + struct lws_conn_stats cs; + double d = 0; +#ifdef LWS_WITH_CGI + struct lws_cgi * const *pcgi; +#endif + +#ifdef LWS_WITH_LIBUV + uv_uptime(&d); +#endif + + buf += lws_snprintf(buf, end - buf, "{ " + "\"version\":\"%s\",\n" + "\"uptime\":\"%ld\",\n", + lws_get_library_version(), + (long)d); + +#ifdef LWS_HAVE_GETLOADAVG + { + double d[3]; + int m; + + m = getloadavg(d, 3); + for (n = 0; n < m; n++) { + buf += lws_snprintf(buf, end - buf, + "\"l%d\":\"%.2f\",\n", + n + 1, d[n]); + } + } +#endif + + buf += lws_snprintf(buf, end - buf, "\"contexts\":[\n"); + + buf += lws_snprintf(buf, end - buf, "{ " + "\"context_uptime\":\"%ld\",\n" + "\"cgi_spawned\":\"%d\",\n" + "\"pt_fd_max\":\"%d\",\n" + "\"ah_pool_max\":\"%d\",\n" + "\"deprecated\":\"%d\",\n" + "\"wsi_alive\":\"%d\",\n", + (unsigned long)(t - context->time_up), + context->count_cgi_spawned, + context->fd_limit_per_thread, + context->max_http_header_pool, + context->deprecated, + context->count_wsi_allocated); + + buf += lws_snprintf(buf, end - buf, "\"pt\":[\n "); + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + if (n) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n" + " \"fds_count\":\"%d\",\n" + " \"ah_pool_inuse\":\"%d\",\n" + " \"ah_wait_list\":\"%d\"\n" + " }", + pt->fds_count, + pt->ah_count_in_use, + pt->ah_wait_list_length); + } + + buf += lws_snprintf(buf, end - buf, "]"); + + buf += lws_snprintf(buf, end - buf, ", \"vhosts\":[\n "); + + first = 1; + vh = context->vhost_list; + listening = 0; + cs = context->conn_stats; + lws_sum_stats(context, &cs); + while (vh) { + + if (!hide_vhosts) { + if (!first) + if(buf != end) + *buf++ = ','; + buf += lws_json_dump_vhost(vh, buf, end - buf); + first = 0; + } + if (vh->lserv_wsi) + listening++; + vh = vh->vhost_next; + } + + buf += lws_snprintf(buf, end - buf, + "],\n\"listen_wsi\":\"%d\",\n" + " \"rx\":\"%llu\",\n" + " \"tx\":\"%llu\",\n" + " \"h1_conn\":\"%lu\",\n" + " \"h1_trans\":\"%lu\",\n" + " \"h2_trans\":\"%lu\",\n" + " \"ws_upg\":\"%lu\",\n" + " \"rejected\":\"%lu\",\n" + " \"h2_alpn\":\"%lu\",\n" + " \"h2_subs\":\"%lu\",\n" + " \"h2_upg\":\"%lu\"", + listening, cs.rx, cs.tx, + cs.h1_conn, + cs.h1_trans, + cs.h2_trans, + cs.ws_upg, + cs.rejected, + cs.h2_alpn, + cs.h2_subs, + cs.h2_upg); + +#ifdef LWS_WITH_CGI + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + pcgi = &pt->cgi_list; + + while (*pcgi) { + pcgi = &(*pcgi)->cgi_list; + + cgi_count++; + } + } +#endif + buf += lws_snprintf(buf, end - buf, ",\n \"cgi_alive\":\"%d\"\n ", + cgi_count); + + buf += lws_snprintf(buf, end - buf, "}"); + + + buf += lws_snprintf(buf, end - buf, "]}\n "); + + return buf - orig; +} + +#endif + +#if defined(LWS_WITH_STATS) + +LWS_VISIBLE LWS_EXTERN uint64_t +lws_stats_get(struct lws_context *context, int index) +{ + if (index >= LWSSTATS_SIZE) + return 0; + + return context->lws_stats[index]; +} + +LWS_VISIBLE LWS_EXTERN void +lws_stats_log_dump(struct lws_context *context) +{ + struct lws_vhost *v = context->vhost_list; + int n, m; + + (void)m; + + if (!context->updated) + return; + + context->updated = 0; + + lwsl_notice("\n"); + lwsl_notice("LWS internal statistics dump ----->\n"); + lwsl_notice("LWSSTATS_C_CONNECTIONS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_CONNECTIONS)); + lwsl_notice("LWSSTATS_C_API_CLOSE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_CLOSE)); + lwsl_notice("LWSSTATS_C_API_READ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_READ)); + lwsl_notice("LWSSTATS_C_API_LWS_WRITE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_LWS_WRITE)); + lwsl_notice("LWSSTATS_C_API_WRITE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_WRITE)); + lwsl_notice("LWSSTATS_C_WRITE_PARTIALS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITE_PARTIALS)); + lwsl_notice("LWSSTATS_C_WRITEABLE_CB_REQ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB_REQ)); + lwsl_notice("LWSSTATS_C_WRITEABLE_CB_EFF_REQ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB_EFF_REQ)); + lwsl_notice("LWSSTATS_C_WRITEABLE_CB: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB)); + lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN)); + lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_FAILED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_FAILED)); + lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)); + lwsl_notice("LWSSTATS_C_SSL_CONNS_HAD_RX: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNS_HAD_RX)); + lwsl_notice("LWSSTATS_C_PEER_LIMIT_AH_DENIED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_PEER_LIMIT_AH_DENIED)); + lwsl_notice("LWSSTATS_C_PEER_LIMIT_WSI_DENIED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_PEER_LIMIT_WSI_DENIED)); + + lwsl_notice("LWSSTATS_C_TIMEOUTS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_TIMEOUTS)); + lwsl_notice("LWSSTATS_C_SERVICE_ENTRY: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SERVICE_ENTRY)); + lwsl_notice("LWSSTATS_B_READ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_B_READ)); + lwsl_notice("LWSSTATS_B_WRITE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_B_WRITE)); + lwsl_notice("LWSSTATS_B_PARTIALS_ACCEPTED_PARTS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS)); + lwsl_notice("LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY: %8llums\n", (unsigned long long)lws_stats_get(context, LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) / 1000); + if (lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)) + lwsl_notice(" Avg accept delay: %8llums\n", + (unsigned long long)(lws_stats_get(context, LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) / + lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)) / 1000); + lwsl_notice("LWSSTATS_MS_SSL_RX_DELAY: %8llums\n", (unsigned long long)lws_stats_get(context, LWSSTATS_MS_SSL_RX_DELAY) / 1000); + if (lws_stats_get(context, LWSSTATS_C_SSL_CONNS_HAD_RX)) + lwsl_notice(" Avg accept-rx delay: %8llums\n", + (unsigned long long)(lws_stats_get(context, LWSSTATS_MS_SSL_RX_DELAY) / + lws_stats_get(context, LWSSTATS_C_SSL_CONNS_HAD_RX)) / 1000); + + lwsl_notice("LWSSTATS_MS_WRITABLE_DELAY: %8lluus\n", + (unsigned long long)lws_stats_get(context, LWSSTATS_MS_WRITABLE_DELAY)); + lwsl_notice("LWSSTATS_MS_WORST_WRITABLE_DELAY: %8lluus\n", + (unsigned long long)lws_stats_get(context, LWSSTATS_MS_WORST_WRITABLE_DELAY)); + if (lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB)) + lwsl_notice(" Avg writable delay: %8lluus\n", + (unsigned long long)(lws_stats_get(context, LWSSTATS_MS_WRITABLE_DELAY) / + lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB))); + lwsl_notice("Simultaneous SSL restriction: %8d/%d/%d\n", context->simultaneous_ssl, + context->simultaneous_ssl_restriction, context->ssl_gate_accepts); + + lwsl_notice("Live wsi: %8d\n", context->count_wsi_allocated); + + context->updated = 1; + + while (v) { + if (v->lserv_wsi) { + + struct lws_context_per_thread *pt = &context->pt[(int)v->lserv_wsi->tsi]; + struct lws_pollfd *pfd; + + pfd = &pt->fds[v->lserv_wsi->position_in_fds_table]; + + lwsl_notice(" Listen port %d actual POLLIN: %d\n", + v->listen_port, (int)pfd->events & LWS_POLLIN); + } + + v = v->vhost_next; + } + + for (n = 0; n < context->count_threads; n++) { + struct lws_context_per_thread *pt = &context->pt[n]; + struct lws *wl; + int m = 0; + + lwsl_notice("PT %d\n", n + 1); + + lws_pt_lock(pt); + + lwsl_notice(" AH in use / max: %d / %d\n", + pt->ah_count_in_use, + context->max_http_header_pool); + + wl = pt->ah_wait_list; + while (wl) { + m++; + wl = wl->u.hdr.ah_wait_list; + } + + lwsl_notice(" AH wait list count / actual: %d / %d\n", + pt->ah_wait_list_length, m); + + lws_pt_unlock(pt); + } + +#if defined(LWS_WITH_PEER_LIMITS) + m = 0; + for (n = 0; n < (int)context->pl_hash_elements; n++) { + lws_start_foreach_llp(struct lws_peer **, peer, + context->pl_hash_table[n]) { + m++; + } lws_end_foreach_llp(peer, next); + } + + lwsl_notice(" Peers: total active %d\n", m); + if (m > 10) { + m = 10; + lwsl_notice(" (showing 10 peers only)\n"); + } + + if (m) { + for (n = 0; n < (int)context->pl_hash_elements; n++) { + char buf[72]; + + lws_start_foreach_llp(struct lws_peer **, peer, context->pl_hash_table[n]) { + struct lws_peer *df = *peer; + + if (!lws_plat_inet_ntop(df->af, df->addr, buf, + sizeof(buf) - 1)) + strcpy(buf, "unknown"); + + lwsl_notice(" peer %s: count wsi: %d, count ah: %d\n", + buf, df->count_wsi, df->count_ah); + + if (!--m) + break; + } lws_end_foreach_llp(peer, next); + } + } +#endif + + lwsl_notice("\n"); +} + +void +lws_stats_atomic_bump(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t bump) +{ + lws_pt_lock(pt); + context->lws_stats[index] += bump; + if (index != LWSSTATS_C_SERVICE_ENTRY) + context->updated = 1; + lws_pt_unlock(pt); +} + +void +lws_stats_atomic_max(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t val) +{ + lws_pt_lock(pt); + if (val > context->lws_stats[index]) { + context->lws_stats[index] = val; + context->updated = 1; + } + lws_pt_unlock(pt); +} + +#endif + diff -Nru libwebsockets-4.0.20/lib/libwebsockets.h libwebsockets-2.4.2/lib/libwebsockets.h --- libwebsockets-4.0.20/lib/libwebsockets.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/libwebsockets.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,5787 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2016 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/** @file */ + +#ifndef LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C +#define LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C + +#ifdef __cplusplus +#include +#include +# +extern "C" { +#else +#include +#endif + +#include "lws_config.h" + +/* + * CARE: everything using cmake defines needs to be below here + */ + +#if defined(LWS_WITH_ESP8266) +struct sockaddr_in; +#define LWS_POSIX 0 +#else +#define LWS_POSIX 1 +#endif + +#if defined(LWS_HAS_INTPTR_T) +#include +#define lws_intptr_t intptr_t +#else +typedef unsigned long long lws_intptr_t; +#endif + +#if defined(WIN32) || defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#include +#include +#ifndef _WIN32_WCE +#include +#else +#define _O_RDONLY 0x0000 +#define O_RDONLY _O_RDONLY +#endif + +// Visual studio older than 2015 and WIN_CE has only _stricmp +#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_WIN32_WCE) +#define strcasecmp _stricmp +#elif !defined(__MINGW32__) +#define strcasecmp stricmp +#endif +#define getdtablesize() 30000 + +#define LWS_INLINE __inline +#define LWS_VISIBLE +#define LWS_WARN_UNUSED_RESULT +#define LWS_WARN_DEPRECATED +#define LWS_FORMAT(string_index) + +#ifdef LWS_DLL +#ifdef LWS_INTERNAL +#define LWS_EXTERN extern __declspec(dllexport) +#else +#define LWS_EXTERN extern __declspec(dllimport) +#endif +#else +#define LWS_EXTERN +#endif + +#define LWS_INVALID_FILE INVALID_HANDLE_VALUE +#define LWS_O_RDONLY _O_RDONLY +#define LWS_O_WRONLY _O_WRONLY +#define LWS_O_CREAT _O_CREAT +#define LWS_O_TRUNC _O_TRUNC + +#if !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER < 1900) /* Visual Studio 2015 already defines this in */ +#define lws_snprintf _snprintf +#endif + +#ifndef __func__ +#define __func__ __FUNCTION__ +#endif + +#if !defined(__MINGW32__) &&(!defined(_MSC_VER) || _MSC_VER < 1900) && !defined(snprintf) +#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__) +#endif + +#else /* NOT WIN32 */ +#include +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) +#include +#endif + +#if defined(__NetBSD__) || defined(__FreeBSD__) +#include +#endif + +#define LWS_INLINE inline +#define LWS_O_RDONLY O_RDONLY +#define LWS_O_WRONLY O_WRONLY +#define LWS_O_CREAT O_CREAT +#define LWS_O_TRUNC O_TRUNC + +#if !defined(LWS_WITH_ESP8266) && !defined(OPTEE_TA) && !defined(LWS_WITH_ESP32) +#include +#include +#define LWS_INVALID_FILE -1 +#else +#define getdtablesize() (30) +#if defined(LWS_WITH_ESP32) +#define LWS_INVALID_FILE NULL +#else +#define LWS_INVALID_FILE NULL +#endif +#endif + +#if defined(__GNUC__) + +/* warn_unused_result attribute only supported by GCC 3.4 or later */ +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define LWS_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define LWS_WARN_UNUSED_RESULT +#endif + +#define LWS_VISIBLE __attribute__((visibility("default"))) +#define LWS_WARN_DEPRECATED __attribute__ ((deprecated)) +#define LWS_FORMAT(string_index) __attribute__ ((format(printf, string_index, string_index+1))) +#else +#define LWS_VISIBLE +#define LWS_WARN_UNUSED_RESULT +#define LWS_WARN_DEPRECATED +#define LWS_FORMAT(string_index) +#endif + +#if defined(__ANDROID__) +#include +#define getdtablesize() sysconf(_SC_OPEN_MAX) +#endif + +#endif + +#ifdef LWS_WITH_LIBEV +#include +#endif /* LWS_WITH_LIBEV */ +#ifdef LWS_WITH_LIBUV +#include +#ifdef LWS_HAVE_UV_VERSION_H +#include +#endif +#endif /* LWS_WITH_LIBUV */ +#ifdef LWS_WITH_LIBEVENT +#include +#endif /* LWS_WITH_LIBEVENT */ + +#ifndef LWS_EXTERN +#define LWS_EXTERN extern +#endif + +#ifdef _WIN32 +#define random rand +#else +#if !defined(OPTEE_TA) +#include +#include +#endif +#endif + +#ifdef LWS_OPENSSL_SUPPORT + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL +#include +#include +#else +#include +#include +#endif /* not USE_OLD_CYASSL */ +#else +#if defined(LWS_WITH_MBEDTLS) +#if defined(LWS_WITH_ESP32) +/* this filepath is passed to us but without quotes or <> */ +#undef MBEDTLS_CONFIG_FILE +#define MBEDTLS_CONFIG_FILE +#endif +#include +#endif +#include +#if !defined(LWS_WITH_MBEDTLS) +#include +#endif +#endif /* not USE_WOLFSSL */ +#endif + + +#define CONTEXT_PORT_NO_LISTEN -1 +#define CONTEXT_PORT_NO_LISTEN_SERVER -2 + +/** \defgroup log Logging + * + * ##Logging + * + * Lws provides flexible and filterable logging facilities, which can be + * used inside lws and in user code. + * + * Log categories may be individually filtered bitwise, and directed to built-in + * sinks for syslog-compatible logging, or a user-defined function. + */ +///@{ + +enum lws_log_levels { + LLL_ERR = 1 << 0, + LLL_WARN = 1 << 1, + LLL_NOTICE = 1 << 2, + LLL_INFO = 1 << 3, + LLL_DEBUG = 1 << 4, + LLL_PARSER = 1 << 5, + LLL_HEADER = 1 << 6, + LLL_EXT = 1 << 7, + LLL_CLIENT = 1 << 8, + LLL_LATENCY = 1 << 9, + LLL_USER = 1 << 10, + + LLL_COUNT = 11 /* set to count of valid flags */ +}; + +LWS_VISIBLE LWS_EXTERN void _lws_log(int filter, const char *format, ...) LWS_FORMAT(2); +LWS_VISIBLE LWS_EXTERN void _lws_logv(int filter, const char *format, va_list vl); +/** + * lwsl_timestamp: generate logging timestamp string + * + * \param level: logging level + * \param p: char * buffer to take timestamp + * \param len: length of p + * + * returns length written in p + */ +LWS_VISIBLE LWS_EXTERN int +lwsl_timestamp(int level, char *p, int len); + +/* these guys are unconditionally included */ + +#define lwsl_err(...) _lws_log(LLL_ERR, __VA_ARGS__) +#define lwsl_user(...) _lws_log(LLL_USER, __VA_ARGS__) + +#if !defined(LWS_WITH_NO_LOGS) +/* notice and warn are usually included by being compiled in */ +#define lwsl_warn(...) _lws_log(LLL_WARN, __VA_ARGS__) +#define lwsl_notice(...) _lws_log(LLL_NOTICE, __VA_ARGS__) +#endif +/* + * weaker logging can be deselected by telling CMake to build in RELEASE mode + * that gets rid of the overhead of checking while keeping _warn and _err + * active + */ + +#if defined(LWS_WITH_ESP8266) +#undef _DEBUG +#endif + +#ifdef _DEBUG +#if defined(LWS_WITH_NO_LOGS) +/* notice, warn and log are always compiled in */ +#define lwsl_warn(...) _lws_log(LLL_WARN, __VA_ARGS__) +#define lwsl_notice(...) _lws_log(LLL_NOTICE, __VA_ARGS__) +#endif +#define lwsl_info(...) _lws_log(LLL_INFO, __VA_ARGS__) +#define lwsl_debug(...) _lws_log(LLL_DEBUG, __VA_ARGS__) +#define lwsl_parser(...) _lws_log(LLL_PARSER, __VA_ARGS__) +#define lwsl_header(...) _lws_log(LLL_HEADER, __VA_ARGS__) +#define lwsl_ext(...) _lws_log(LLL_EXT, __VA_ARGS__) +#define lwsl_client(...) _lws_log(LLL_CLIENT, __VA_ARGS__) +#define lwsl_latency(...) _lws_log(LLL_LATENCY, __VA_ARGS__) + +#else /* no debug */ +#if defined(LWS_WITH_NO_LOGS) +#define lwsl_warn(...) do {} while(0) +#define lwsl_notice(...) do {} while(0) +#endif +#define lwsl_info(...) do {} while(0) +#define lwsl_debug(...) do {} while(0) +#define lwsl_parser(...) do {} while(0) +#define lwsl_header(...) do {} while(0) +#define lwsl_ext(...) do {} while(0) +#define lwsl_client(...) do {} while(0) +#define lwsl_latency(...) do {} while(0) + +#endif + +/** + * lwsl_hexdump() - helper to hexdump a buffer + * + * \param level: one of LLL_ constants + * \param buf: buffer start to dump + * \param len: length of buffer to dump + * + * If \p level is visible, does a nice hexdump -C style dump of \p buf for + * \p len bytes. This can be extremely convenient while debugging. + */ +LWS_VISIBLE LWS_EXTERN void +lwsl_hexdump_level(int level, const void *vbuf, size_t len); + +/** + * lwsl_hexdump() - helper to hexdump a buffer (DEBUG builds only) + * + * \param buf: buffer start to dump + * \param len: length of buffer to dump + * + * Calls through to lwsl_hexdump_level(LLL_DEBUG, ... for compatability. + * It's better to use lwsl_hexdump_level(level, ... directly so you can control + * the visibility. + */ +LWS_VISIBLE LWS_EXTERN void +lwsl_hexdump(const void *buf, size_t len); + +/** + * lws_is_be() - returns nonzero if the platform is Big Endian + */ +static LWS_INLINE int lws_is_be(void) { + const int probe = ~0xff; + + return *(const char *)&probe; +} + +/** + * lws_set_log_level() - Set the logging bitfield + * \param level: OR together the LLL_ debug contexts you want output from + * \param log_emit_function: NULL to leave it as it is, or a user-supplied + * function to perform log string emission instead of + * the default stderr one. + * + * log level defaults to "err", "warn" and "notice" contexts enabled and + * emission on stderr. If stderr is a tty (according to isatty()) then + * the output is coloured according to the log level using ANSI escapes. + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_log_level(int level, + void (*log_emit_function)(int level, const char *line)); + +/** + * lwsl_emit_syslog() - helper log emit function writes to system log + * + * \param level: one of LLL_ log level indexes + * \param line: log string + * + * You use this by passing the function pointer to lws_set_log_level(), to set + * it as the log emit function, it is not called directly. + */ +LWS_VISIBLE LWS_EXTERN void +lwsl_emit_syslog(int level, const char *line); + +/** + * lwsl_visible() - returns true if the log level should be printed + * + * \param level: one of LLL_ log level indexes + * + * This is useful if you have to do work to generate the log content, you + * can skip the work if the log level used to print it is not actually + * enabled at runtime. + */ +LWS_VISIBLE LWS_EXTERN int +lwsl_visible(int level); + +///@} + + +#include + +#ifndef lws_container_of +#define lws_container_of(P,T,M) ((T *)((char *)(P) - offsetof(T, M))) +#endif + + +struct lws; +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +/* api change list for user code to test against */ + +#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_ARG + +/* the struct lws_protocols has the id field present */ +#define LWS_FEATURE_PROTOCOLS_HAS_ID_FIELD + +/* you can call lws_get_peer_write_allowance */ +#define LWS_FEATURE_PROTOCOLS_HAS_PEER_WRITE_ALLOWANCE + +/* extra parameter introduced in 917f43ab821 */ +#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_LEN + +/* File operations stuff exists */ +#define LWS_FEATURE_FOPS + + +#if defined(_WIN32) +typedef SOCKET lws_sockfd_type; +typedef HANDLE lws_filefd_type; +#define lws_sockfd_valid(sfd) (!!sfd) +struct lws_pollfd { + lws_sockfd_type fd; /**< file descriptor */ + SHORT events; /**< which events to respond to */ + SHORT revents; /**< which events happened */ +}; +#define LWS_POLLHUP (FD_CLOSE) +#define LWS_POLLIN (FD_READ | FD_ACCEPT) +#define LWS_POLLOUT (FD_WRITE) +#else + + +#if defined(LWS_WITH_ESP8266) + +#include +#include + +typedef struct espconn * lws_sockfd_type; +typedef void * lws_filefd_type; +#define lws_sockfd_valid(sfd) (!!sfd) +struct pollfd { + lws_sockfd_type fd; /**< fd related to */ + short events; /**< which POLL... events to respond to */ + short revents; /**< which POLL... events occurred */ +}; +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +struct lws_vhost; + +lws_sockfd_type esp8266_create_tcp_listen_socket(struct lws_vhost *vh); +void esp8266_tcp_stream_accept(lws_sockfd_type fd, struct lws *wsi); + +#include +#include +#include "ets_sys.h" + +int ets_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3); +#define snprintf ets_snprintf + +typedef os_timer_t uv_timer_t; +typedef void uv_cb_t(uv_timer_t *); + +void os_timer_disarm(void *); +void os_timer_setfn(os_timer_t *, os_timer_func_t *, void *); + +void ets_timer_arm_new(os_timer_t *, int, int, int); + +//void os_timer_arm(os_timer_t *, int, int); + +#define UV_VERSION_MAJOR 1 + +#define lws_uv_getloop(a, b) (NULL) + +static inline void uv_timer_init(void *l, uv_timer_t *t) +{ + (void)l; + memset(t, 0, sizeof(*t)); + os_timer_disarm(t); +} + +static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep) +{ + os_timer_setfn(t, (os_timer_func_t *)cb, t); + /* ms, repeat */ + os_timer_arm(t, first, !!rep); +} + +static inline void uv_timer_stop(uv_timer_t *t) +{ + os_timer_disarm(t); +} + +#else +#if defined(LWS_WITH_ESP32) + +typedef int lws_sockfd_type; +typedef int lws_filefd_type; +#define lws_sockfd_valid(sfd) (sfd >= 0) +struct pollfd { + lws_sockfd_type fd; /**< fd related to */ + short events; /**< which POLL... events to respond to */ + short revents; /**< which POLL... events occurred */ +}; +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +#include +#include +#include +#include "esp_wifi.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_event_loop.h" +#include "nvs.h" +#include "driver/gpio.h" +#include "esp_spi_flash.h" +#include "freertos/timers.h" + +#if !defined(CONFIG_FREERTOS_HZ) +#define CONFIG_FREERTOS_HZ 100 +#endif + +typedef TimerHandle_t uv_timer_t; +typedef void uv_cb_t(uv_timer_t *); +typedef void * uv_handle_t; + +struct timer_mapping { + uv_cb_t *cb; + uv_timer_t *t; +}; + +#define UV_VERSION_MAJOR 1 + +#define lws_uv_getloop(a, b) (NULL) + +static inline void uv_timer_init(void *l, uv_timer_t *t) +{ + (void)l; + *t = NULL; +} + +extern void esp32_uvtimer_cb(TimerHandle_t t); + +static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep) +{ + struct timer_mapping *tm = (struct timer_mapping *)malloc(sizeof(*tm)); + + if (!tm) + return; + + tm->t = t; + tm->cb = cb; + + *t = xTimerCreate("x", pdMS_TO_TICKS(first), !!rep, tm, + (TimerCallbackFunction_t)esp32_uvtimer_cb); + xTimerStart(*t, 0); +} + +static inline void uv_timer_stop(uv_timer_t *t) +{ + xTimerStop(*t, 0); +} + +static inline void uv_close(uv_handle_t *h, void *v) +{ + free(pvTimerGetTimerID((uv_timer_t)h)); + xTimerDelete(*(uv_timer_t *)h, 0); +} + +/* ESP32 helper declarations */ + +#include +#include + +#define LWS_PLUGIN_STATIC +#define LWS_MAGIC_REBOOT_TYPE_ADS 0x50001ffc +#define LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY 0xb00bcafe +#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY 0xfaceb00b +#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON 0xf0cedfac + + +/* user code provides these */ + +extern void +lws_esp32_identify_physical_device(void); + +/* lws-plat-esp32 provides these */ + +typedef void (*lws_cb_scan_done)(uint16_t count, wifi_ap_record_t *recs, void *arg); + +enum genled_state { + LWSESP32_GENLED__INIT, + LWSESP32_GENLED__LOST_NETWORK, + LWSESP32_GENLED__NO_NETWORK, + LWSESP32_GENLED__CONN_AP, + LWSESP32_GENLED__GOT_IP, + LWSESP32_GENLED__OK, +}; + +struct lws_group_member { + struct lws_group_member *next; + uint64_t last_seen; + char model[16]; + char role[16]; + char host[32]; + char mac[20]; + int width, height; + struct ip4_addr addr; + struct ip6_addr addrv6; + uint8_t flags; +}; + +#define LWS_SYSTEM_GROUP_MEMBER_ADD 1 +#define LWS_SYSTEM_GROUP_MEMBER_CHANGE 2 +#define LWS_SYSTEM_GROUP_MEMBER_REMOVE 3 + +#define LWS_GROUP_FLAG_SELF 1 + +struct lws_esp32 { + char sta_ip[16]; + char sta_mask[16]; + char sta_gw[16]; + char serial[16]; + char opts[16]; + char model[16]; + char group[16]; + char role[16]; + char ssid[4][16]; + char password[4][32]; + char active_ssid[32]; + char access_pw[16]; + char hostname[32]; + char mac[20]; + mdns_server_t *mdns; + char region; + char inet; + char conn_ap; + + enum genled_state genled; + uint64_t genled_t; + + lws_cb_scan_done scan_consumer; + void *scan_consumer_arg; + struct lws_group_member *first; + int extant_group_members; +}; + +struct lws_esp32_image { + uint32_t romfs; + uint32_t romfs_len; + uint32_t json; + uint32_t json_len; +}; + +extern struct lws_esp32 lws_esp32; +struct lws_vhost; + +extern esp_err_t +lws_esp32_event_passthru(void *ctx, system_event_t *event); +extern void +lws_esp32_wlan_config(void); +extern void +lws_esp32_wlan_start_ap(void); +extern void +lws_esp32_wlan_start_station(void); +struct lws_context_creation_info; +extern void +lws_esp32_set_creation_defaults(struct lws_context_creation_info *info); +extern struct lws_context * +lws_esp32_init(struct lws_context_creation_info *, struct lws_vhost **pvh); +extern int +lws_esp32_wlan_nvs_get(int retry); +extern esp_err_t +lws_nvs_set_str(nvs_handle handle, const char* key, const char* value); +extern void +lws_esp32_restart_guided(uint32_t type); +extern const esp_partition_t * +lws_esp_ota_get_boot_partition(void); +extern int +lws_esp32_get_image_info(const esp_partition_t *part, struct lws_esp32_image *i, char *json, int json_len); +extern int +lws_esp32_leds_network_indication(void); + +extern uint32_t lws_esp32_get_reboot_type(void); +extern uint16_t lws_esp32_sine_interp(int n); + +/* required in external code by esp32 plat (may just return if no leds) */ +extern void lws_esp32_leds_timer_cb(TimerHandle_t th); +#else +typedef int lws_sockfd_type; +typedef int lws_filefd_type; +#define lws_sockfd_valid(sfd) (sfd >= 0) +#endif +#endif + +#define lws_pollfd pollfd +#define LWS_POLLHUP (POLLHUP|POLLERR) +#define LWS_POLLIN (POLLIN) +#define LWS_POLLOUT (POLLOUT) +#endif + + +#if (defined(WIN32) || defined(_WIN32)) && !defined(__MINGW32__) +/* ... */ +#define ssize_t SSIZE_T +#endif + +#if defined(WIN32) && defined(LWS_HAVE__STAT32I64) +#include +#include +#endif + +#if defined(LWS_HAVE_STDINT_H) +#include +#else +#if defined(WIN32) || defined(_WIN32) +/* !!! >:-[ */ +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +#else +typedef unsigned int uint32_t; +typedef unsigned short uint16_t; +typedef unsigned char uint8_t; +#endif +#endif + +typedef unsigned long long lws_filepos_t; +typedef long long lws_fileofs_t; +typedef uint32_t lws_fop_flags_t; + +/** struct lws_pollargs - argument structure for all external poll related calls + * passed in via 'in' */ +struct lws_pollargs { + lws_sockfd_type fd; /**< applicable socket descriptor */ + int events; /**< the new event mask */ + int prev_events; /**< the previous event mask */ +}; + +struct lws_tokens; +struct lws_token_limits; + +/*! \defgroup wsclose Websocket Close + * + * ##Websocket close frame control + * + * When we close a ws connection, we can send a reason code and a short + * UTF-8 description back with the close packet. + */ +///@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +/** enum lws_close_status - RFC6455 close status codes */ +enum lws_close_status { + LWS_CLOSE_STATUS_NOSTATUS = 0, + LWS_CLOSE_STATUS_NORMAL = 1000, + /**< 1000 indicates a normal closure, meaning that the purpose for + which the connection was established has been fulfilled. */ + LWS_CLOSE_STATUS_GOINGAWAY = 1001, + /**< 1001 indicates that an endpoint is "going away", such as a server + going down or a browser having navigated away from a page. */ + LWS_CLOSE_STATUS_PROTOCOL_ERR = 1002, + /**< 1002 indicates that an endpoint is terminating the connection due + to a protocol error. */ + LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE = 1003, + /**< 1003 indicates that an endpoint is terminating the connection + because it has received a type of data it cannot accept (e.g., an + endpoint that understands only text data MAY send this if it + receives a binary message). */ + LWS_CLOSE_STATUS_RESERVED = 1004, + /**< Reserved. The specific meaning might be defined in the future. */ + LWS_CLOSE_STATUS_NO_STATUS = 1005, + /**< 1005 is a reserved value and MUST NOT be set as a status code in a + Close control frame by an endpoint. It is designated for use in + applications expecting a status code to indicate that no status + code was actually present. */ + LWS_CLOSE_STATUS_ABNORMAL_CLOSE = 1006, + /**< 1006 is a reserved value and MUST NOT be set as a status code in a + Close control frame by an endpoint. It is designated for use in + applications expecting a status code to indicate that the + connection was closed abnormally, e.g., without sending or + receiving a Close control frame. */ + LWS_CLOSE_STATUS_INVALID_PAYLOAD = 1007, + /**< 1007 indicates that an endpoint is terminating the connection + because it has received data within a message that was not + consistent with the type of the message (e.g., non-UTF-8 [RFC3629] + data within a text message). */ + LWS_CLOSE_STATUS_POLICY_VIOLATION = 1008, + /**< 1008 indicates that an endpoint is terminating the connection + because it has received a message that violates its policy. This + is a generic status code that can be returned when there is no + other more suitable status code (e.g., 1003 or 1009) or if there + is a need to hide specific details about the policy. */ + LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE = 1009, + /**< 1009 indicates that an endpoint is terminating the connection + because it has received a message that is too big for it to + process. */ + LWS_CLOSE_STATUS_EXTENSION_REQUIRED = 1010, + /**< 1010 indicates that an endpoint (client) is terminating the + connection because it has expected the server to negotiate one or + more extension, but the server didn't return them in the response + message of the WebSocket handshake. The list of extensions that + are needed SHOULD appear in the /reason/ part of the Close frame. + Note that this status code is not used by the server, because it + can fail the WebSocket handshake instead */ + LWS_CLOSE_STATUS_UNEXPECTED_CONDITION = 1011, + /**< 1011 indicates that a server is terminating the connection because + it encountered an unexpected condition that prevented it from + fulfilling the request. */ + LWS_CLOSE_STATUS_TLS_FAILURE = 1015, + /**< 1015 is a reserved value and MUST NOT be set as a status code in a + Close control frame by an endpoint. It is designated for use in + applications expecting a status code to indicate that the + connection was closed due to a failure to perform a TLS handshake + (e.g., the server certificate can't be verified). */ + + /****** add new things just above ---^ ******/ + + LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY = 9999, +}; + +/** + * lws_close_reason - Set reason and aux data to send with Close packet + * If you are going to return nonzero from the callback + * requesting the connection to close, you can optionally + * call this to set the reason the peer will be told if + * possible. + * + * \param wsi: The websocket connection to set the close reason on + * \param status: A valid close status from websocket standard + * \param buf: NULL or buffer containing up to 124 bytes of auxiliary data + * \param len: Length of data in \param buf to send + */ +LWS_VISIBLE LWS_EXTERN void +lws_close_reason(struct lws *wsi, enum lws_close_status status, + unsigned char *buf, size_t len); + +///@} + +struct lws; +struct lws_context; +/* needed even with extensions disabled for create context */ +struct lws_extension; + +/*! \defgroup lwsmeta lws-meta + * + * ##lws-meta protocol + * + * The protocol wraps other muxed connections inside one tcp connection. + * + * Commands are assigned from 0x41 up (so they are valid unicode) + */ +///@{ + +enum lws_meta_commands { + LWS_META_CMD_OPEN_SUBCHANNEL = 'A', + /**< Client requests to open new subchannel + */ + LWS_META_CMD_OPEN_RESULT, + /**< Result of client request to open new subchannel */ + LWS_META_CMD_CLOSE_NOTIFY, + /**< Notification of subchannel closure */ + LWS_META_CMD_CLOSE_RQ, + /**< client requests to close a subchannel */ + LWS_META_CMD_WRITE, + /**< connection writes something to specific channel index */ + + /****** add new things just above ---^ ******/ +}; + +/* channel numbers are transported offset by 0x20 so they are valid unicode */ + +#define LWS_META_TRANSPORT_OFFSET 0x20 + +///@} + +/*! \defgroup usercb User Callback + * + * ##User protocol callback + * + * The protocol callback is the primary way lws interacts with + * user code. For one of a list of a few dozen reasons the callback gets + * called at some event to be handled. + * + * All of the events can be ignored, returning 0 is taken as "OK" and returning + * nonzero in most cases indicates that the connection should be closed. + */ +///@{ + +struct lws_ssl_info { + int where; + int ret; +}; + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +/** enum lws_callback_reasons - reason you're getting a protocol callback */ +enum lws_callback_reasons { + LWS_CALLBACK_ESTABLISHED = 0, + /**< (VH) after the server completes a handshake with an incoming + * client. If you built the library with ssl support, in is a + * pointer to the ssl struct associated with the connection or NULL.*/ + LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 1, + /**< the request client connection has been unable to complete a + * handshake with the remote server. If in is non-NULL, you can + * find an error string of length len where it points to + * + * Diagnostic strings that may be returned include + * + * "getaddrinfo (ipv6) failed" + * "unknown address family" + * "getaddrinfo (ipv4) failed" + * "set socket opts failed" + * "insert wsi failed" + * "lws_ssl_client_connect1 failed" + * "lws_ssl_client_connect2 failed" + * "Peer hung up" + * "read failed" + * "HS: URI missing" + * "HS: Redirect code but no Location" + * "HS: URI did not parse" + * "HS: Redirect failed" + * "HS: Server did not return 200" + * "HS: OOM" + * "HS: disallowed by client filter" + * "HS: disallowed at ESTABLISHED" + * "HS: ACCEPT missing" + * "HS: ws upgrade response not 101" + * "HS: UPGRADE missing" + * "HS: Upgrade to something other than websocket" + * "HS: CONNECTION missing" + * "HS: UPGRADE malformed" + * "HS: PROTOCOL malformed" + * "HS: Cannot match protocol" + * "HS: EXT: list too big" + * "HS: EXT: failed setting defaults" + * "HS: EXT: failed parsing defaults" + * "HS: EXT: failed parsing options" + * "HS: EXT: Rejects server options" + * "HS: EXT: unknown ext" + * "HS: Accept hash wrong" + * "HS: Rejected by filter cb" + * "HS: OOM" + * "HS: SO_SNDBUF failed" + * "HS: Rejected at CLIENT_ESTABLISHED" + */ + LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH = 2, + /**< this is the last chance for the client user code to examine the + * http headers and decide to reject the connection. If the + * content in the headers is interesting to the + * client (url, etc) it needs to copy it out at + * this point since it will be destroyed before + * the CLIENT_ESTABLISHED call */ + LWS_CALLBACK_CLIENT_ESTABLISHED = 3, + /**< after your client connection completed + * a handshake with the remote server */ + LWS_CALLBACK_CLOSED = 4, + /**< when the websocket session ends */ + LWS_CALLBACK_CLOSED_HTTP = 5, + /**< when a HTTP (non-websocket) session ends */ + LWS_CALLBACK_RECEIVE = 6, + /**< data has appeared for this server endpoint from a + * remote client, it can be found at *in and is + * len bytes long */ + LWS_CALLBACK_RECEIVE_PONG = 7, + /**< servers receive PONG packets with this callback reason */ + LWS_CALLBACK_CLIENT_RECEIVE = 8, + /**< data has appeared from the server for the client connection, it + * can be found at *in and is len bytes long */ + LWS_CALLBACK_CLIENT_RECEIVE_PONG = 9, + /**< clients receive PONG packets with this callback reason */ + LWS_CALLBACK_CLIENT_WRITEABLE = 10, + /**< If you call lws_callback_on_writable() on a connection, you will + * get one of these callbacks coming when the connection socket + * is able to accept another write packet without blocking. + * If it already was able to take another packet without blocking, + * you'll get this callback at the next call to the service loop + * function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE + * and servers get LWS_CALLBACK_SERVER_WRITEABLE. */ + LWS_CALLBACK_SERVER_WRITEABLE = 11, + /**< See LWS_CALLBACK_CLIENT_WRITEABLE */ + LWS_CALLBACK_HTTP = 12, + /**< an http request has come from a client that is not + * asking to upgrade the connection to a websocket + * one. This is a chance to serve http content, + * for example, to send a script to the client + * which will then open the websockets connection. + * in points to the URI path requested and + * lws_serve_http_file() makes it very + * simple to send back a file to the client. + * Normally after sending the file you are done + * with the http connection, since the rest of the + * activity will come by websockets from the script + * that was delivered by http, so you will want to + * return 1; to close and free up the connection. */ + LWS_CALLBACK_HTTP_BODY = 13, + /**< the next len bytes data from the http + * request body HTTP connection is now available in in. */ + LWS_CALLBACK_HTTP_BODY_COMPLETION = 14, + /**< the expected amount of http request body has been delivered */ + LWS_CALLBACK_HTTP_FILE_COMPLETION = 15, + /**< a file requested to be sent down http link has completed. */ + LWS_CALLBACK_HTTP_WRITEABLE = 16, + /**< you can write more down the http protocol link now. */ + LWS_CALLBACK_FILTER_NETWORK_CONNECTION = 17, + /**< called when a client connects to + * the server at network level; the connection is accepted but then + * passed to this callback to decide whether to hang up immediately + * or not, based on the client IP. in contains the connection + * socket's descriptor. Since the client connection information is + * not available yet, wsi still pointing to the main server socket. + * Return non-zero to terminate the connection before sending or + * receiving anything. Because this happens immediately after the + * network connection from the client, there's no websocket protocol + * selected yet so this callback is issued only to protocol 0. */ + LWS_CALLBACK_FILTER_HTTP_CONNECTION = 18, + /**< called when the request has + * been received and parsed from the client, but the response is + * not sent yet. Return non-zero to disallow the connection. + * user is a pointer to the connection user space allocation, + * in is the URI, eg, "/" + * In your handler you can use the public APIs + * lws_hdr_total_length() / lws_hdr_copy() to access all of the + * headers using the header enums lws_token_indexes from + * libwebsockets.h to check for and read the supported header + * presence and content before deciding to allow the http + * connection to proceed or to kill the connection. */ + LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED = 19, + /**< A new client just had + * been connected, accepted, and instantiated into the pool. This + * callback allows setting any relevant property to it. Because this + * happens immediately after the instantiation of a new client, + * there's no websocket protocol selected yet so this callback is + * issued only to protocol 0. Only wsi is defined, pointing to the + * new client, and the return value is ignored. */ + LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION = 20, + /**< called when the handshake has + * been received and parsed from the client, but the response is + * not sent yet. Return non-zero to disallow the connection. + * user is a pointer to the connection user space allocation, + * in is the requested protocol name + * In your handler you can use the public APIs + * lws_hdr_total_length() / lws_hdr_copy() to access all of the + * headers using the header enums lws_token_indexes from + * libwebsockets.h to check for and read the supported header + * presence and content before deciding to allow the handshake + * to proceed or to kill the connection. */ + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS = 21, + /**< if configured for + * including OpenSSL support, this callback allows your user code + * to perform extra SSL_CTX_load_verify_locations() or similar + * calls to direct OpenSSL where to find certificates the client + * can use to confirm the remote server identity. user is the + * OpenSSL SSL_CTX* */ + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22, + /**< if configured for + * including OpenSSL support, this callback allows your user code + * to load extra certificates into the server which allow it to + * verify the validity of certificates returned by clients. user + * is the server's OpenSSL SSL_CTX* */ + LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23, + /**< if the libwebsockets vhost was created with the option + * LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT, then this + * callback is generated during OpenSSL verification of the cert + * sent from the client. It is sent to protocol[0] callback as + * no protocol has been negotiated on the connection yet. + * Notice that the libwebsockets context and wsi are both NULL + * during this callback. See + * http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html + * to understand more detail about the OpenSSL callback that + * generates this libwebsockets callback and the meanings of the + * arguments passed. In this callback, user is the x509_ctx, + * in is the ssl pointer and len is preverify_ok + * Notice that this callback maintains libwebsocket return + * conventions, return 0 to mean the cert is OK or 1 to fail it. + * This also means that if you don't handle this callback then + * the default callback action of returning 0 allows the client + * certificates. */ + LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER = 24, + /**< this callback happens + * when a client handshake is being compiled. user is NULL, + * in is a char **, it's pointing to a char * which holds the + * next location in the header buffer where you can add + * headers, and len is the remaining space in the header buffer, + * which is typically some hundreds of bytes. So, to add a canned + * cookie, your handler code might look similar to: + * + * char **p = (char **)in; + * + * if (len < 100) + * return 1; + * + * *p += sprintf(*p, "Cookie: a=b\x0d\x0a"); + * + * return 0; + * + * Notice if you add anything, you just have to take care about + * the CRLF on the line you added. Obviously this callback is + * optional, if you don't handle it everything is fine. + * + * Notice the callback is coming to protocols[0] all the time, + * because there is no specific protocol negotiated yet. */ + LWS_CALLBACK_CONFIRM_EXTENSION_OKAY = 25, + /**< When the server handshake code + * sees that it does support a requested extension, before + * accepting the extension by additing to the list sent back to + * the client it gives this callback just to check that it's okay + * to use that extension. It calls back to the requested protocol + * and with in being the extension name, len is 0 and user is + * valid. Note though at this time the ESTABLISHED callback hasn't + * happened yet so if you initialize user content there, user + * content during this callback might not be useful for anything. */ + LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED = 26, + /**< When a client + * connection is being prepared to start a handshake to a server, + * each supported extension is checked with protocols[0] callback + * with this reason, giving the user code a chance to suppress the + * claim to support that extension by returning non-zero. If + * unhandled, by default 0 will be returned and the extension + * support included in the header to the server. Notice this + * callback comes to protocols[0]. */ + LWS_CALLBACK_PROTOCOL_INIT = 27, + /**< One-time call per protocol, per-vhost using it, so it can + * do initial setup / allocations etc */ + LWS_CALLBACK_PROTOCOL_DESTROY = 28, + /**< One-time call per protocol, per-vhost using it, indicating + * this protocol won't get used at all after this callback, the + * vhost is getting destroyed. Take the opportunity to + * deallocate everything that was allocated by the protocol. */ + LWS_CALLBACK_WSI_CREATE = 29, + /**< outermost (earliest) wsi create notification to protocols[0] */ + LWS_CALLBACK_WSI_DESTROY = 30, + /**< outermost (latest) wsi destroy notification to protocols[0] */ + LWS_CALLBACK_GET_THREAD_ID = 31, + /**< lws can accept callback when writable requests from other + * threads, if you implement this callback and return an opaque + * current thread ID integer. */ + + /* external poll() management support */ + LWS_CALLBACK_ADD_POLL_FD = 32, + /**< lws normally deals with its poll() or other event loop + * internally, but in the case you are integrating with another + * server you will need to have lws sockets share a + * polling array with the other server. This and the other + * POLL_FD related callbacks let you put your specialized + * poll array interface code in the callback for protocol 0, the + * first protocol you support, usually the HTTP protocol in the + * serving case. + * This callback happens when a socket needs to be + * added to the polling loop: in points to a struct + * lws_pollargs; the fd member of the struct is the file + * descriptor, and events contains the active events + * + * If you are using the internal lws polling / event loop + * you can just ignore these callbacks. */ + LWS_CALLBACK_DEL_POLL_FD = 33, + /**< This callback happens when a socket descriptor + * needs to be removed from an external polling array. in is + * again the struct lws_pollargs containing the fd member + * to be removed. If you are using the internal polling + * loop, you can just ignore it. */ + LWS_CALLBACK_CHANGE_MODE_POLL_FD = 34, + /**< This callback happens when lws wants to modify the events for + * a connection. + * in is the struct lws_pollargs with the fd to change. + * The new event mask is in events member and the old mask is in + * the prev_events member. + * If you are using the internal polling loop, you can just ignore + * it. */ + LWS_CALLBACK_LOCK_POLL = 35, + /**< These allow the external poll changes driven + * by lws to participate in an external thread locking + * scheme around the changes, so the whole thing is threadsafe. + * These are called around three activities in the library, + * - inserting a new wsi in the wsi / fd table (len=1) + * - deleting a wsi from the wsi / fd table (len=1) + * - changing a wsi's POLLIN/OUT state (len=0) + * Locking and unlocking external synchronization objects when + * len == 1 allows external threads to be synchronized against + * wsi lifecycle changes if it acquires the same lock for the + * duration of wsi dereference from the other thread context. */ + LWS_CALLBACK_UNLOCK_POLL = 36, + /**< See LWS_CALLBACK_LOCK_POLL, ignore if using lws internal poll */ + + LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY = 37, + /**< if configured for including OpenSSL support but no private key + * file has been specified (ssl_private_key_filepath is NULL), this is + * called to allow the user to set the private key directly via + * libopenssl and perform further operations if required; this might be + * useful in situations where the private key is not directly accessible + * by the OS, for example if it is stored on a smartcard. + * user is the server's OpenSSL SSL_CTX* */ + LWS_CALLBACK_WS_PEER_INITIATED_CLOSE = 38, + /**< The peer has sent an unsolicited Close WS packet. in and + * len are the optional close code (first 2 bytes, network + * order) and the optional additional information which is not + * defined in the standard, and may be a string or non-human- readable data. + * If you return 0 lws will echo the close and then close the + * connection. If you return nonzero lws will just close the + * connection. */ + + LWS_CALLBACK_WS_EXT_DEFAULTS = 39, + /**< Gives client connections an opportunity to adjust negotiated + * extension defaults. `user` is the extension name that was + * negotiated (eg, "permessage-deflate"). `in` points to a + * buffer and `len` is the buffer size. The user callback can + * set the buffer to a string describing options the extension + * should parse. Or just ignore for defaults. */ + + LWS_CALLBACK_CGI = 40, + /**< CGI: CGI IO events on stdin / out / err are sent here on + * protocols[0]. The provided `lws_callback_http_dummy()` + * handles this and the callback should be directed there if + * you use CGI. */ + LWS_CALLBACK_CGI_TERMINATED = 41, + /**< CGI: The related CGI process ended, this is called before + * the wsi is closed. Used to, eg, terminate chunking. + * The provided `lws_callback_http_dummy()` + * handles this and the callback should be directed there if + * you use CGI. The child PID that terminated is in len. */ + LWS_CALLBACK_CGI_STDIN_DATA = 42, + /**< CGI: Data is, to be sent to the CGI process stdin, eg from + * a POST body. The provided `lws_callback_http_dummy()` + * handles this and the callback should be directed there if + * you use CGI. */ + LWS_CALLBACK_CGI_STDIN_COMPLETED = 43, + /**< CGI: no more stdin is coming. The provided + * `lws_callback_http_dummy()` handles this and the callback + * should be directed there if you use CGI. */ + LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44, + /**< The HTTP client connection has succeeded, and is now + * connected to the server */ + LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45, + /**< The HTTP client connection is closing */ + LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46, + /**< This simply indicates data was received on the HTTP client + * connection. It does NOT drain or provide the data. + * This exists to neatly allow a proxying type situation, + * where this incoming data will go out on another connection. + * If the outgoing connection stalls, we should stall processing + * the incoming data. So a handler for this in that case should + * simply set a flag to indicate there is incoming data ready + * and ask for a writeable callback on the outgoing connection. + * In the writable callback he can check the flag and then get + * and drain the waiting incoming data using lws_http_client_read(). + * This will use callbacks to LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ + * to get and drain the incoming data, where it should be sent + * back out on the outgoing connection. */ + LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47, + /**< The client transaction completed... at the moment this + * is the same as closing since transaction pipelining on + * client side is not yet supported. */ + LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48, + /**< This is generated by lws_http_client_read() used to drain + * incoming data. In the case the incoming data was chunked, + * it will be split into multiple smaller callbacks for each + * chunk block, removing the chunk headers. If not chunked, + * it will appear all in one callback. */ + LWS_CALLBACK_HTTP_BIND_PROTOCOL = 49, + /**< By default, all HTTP handling is done in protocols[0]. + * However you can bind different protocols (by name) to + * different parts of the URL space using callback mounts. This + * callback occurs in the new protocol when a wsi is bound + * to that protocol. Any protocol allocation related to the + * http transaction processing should be created then. + * These specific callbacks are necessary because with HTTP/1.1, + * a single connection may perform at series of different + * transactions at different URLs, thus the lifetime of the + * protocol bind is just for one transaction, not connection. */ + LWS_CALLBACK_HTTP_DROP_PROTOCOL = 50, + /**< This is called when a transaction is unbound from a protocol. + * It indicates the connection completed its transaction and may + * do something different now. Any protocol allocation related + * to the http transaction processing should be destroyed. */ + LWS_CALLBACK_CHECK_ACCESS_RIGHTS = 51, + /**< This gives the user code a chance to forbid an http access. + * `in` points to a `struct lws_process_html_args`, which + * describes the URL, and a bit mask describing the type of + * authentication required. If the callback returns nonzero, + * the transaction ends with HTTP_STATUS_UNAUTHORIZED. */ + LWS_CALLBACK_PROCESS_HTML = 52, + /**< This gives your user code a chance to mangle outgoing + * HTML. `in` points to a `struct lws_process_html_args` + * which describes the buffer containing outgoing HTML. + * The buffer may grow up to `.max_len` (currently +128 + * bytes per buffer). + * */ + LWS_CALLBACK_ADD_HEADERS = 53, + /**< This gives your user code a chance to add headers to a + * transaction bound to your protocol. `in` points to a + * `struct lws_process_html_args` describing a buffer and length + * you can add headers into using the normal lws apis. + * + * Only `args->p` and `args->len` are valid, and `args->p` should + * be moved on by the amount of bytes written, if any. Eg + * + * case LWS_CALLBACK_ADD_HEADERS: + * + * struct lws_process_html_args *args = + * (struct lws_process_html_args *)in; + * + * if (lws_add_http_header_by_name(wsi, + * (unsigned char *)"set-cookie:", + * (unsigned char *)cookie, cookie_len, + * (unsigned char **)&args->p, + * (unsigned char *)args->p + args->max_len)) + * return 1; + * + * break; + */ + LWS_CALLBACK_SESSION_INFO = 54, + /**< This is only generated by user code using generic sessions. + * It's used to get a `struct lws_session_info` filled in by + * generic sessions with information about the logged-in user. + * See the messageboard sample for an example of how to use. */ + + LWS_CALLBACK_GS_EVENT = 55, + /**< Indicates an event happened to the Generic Sessions session. + * `in` contains a `struct lws_gs_event_args` describing the event. */ + LWS_CALLBACK_HTTP_PMO = 56, + /**< per-mount options for this connection, called before + * the normal LWS_CALLBACK_HTTP when the mount has per-mount + * options. + */ + LWS_CALLBACK_CLIENT_HTTP_WRITEABLE = 57, + /**< when doing an HTTP type client connection, you can call + * lws_client_http_body_pending(wsi, 1) from + * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to get these callbacks + * sending the HTTP headers. + * + * From this callback, when you have sent everything, you should let + * lws know by calling lws_client_http_body_pending(wsi, 0) + */ + LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION = 58, + /**< Similar to LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION + * this callback is called during OpenSSL verification of the cert + * sent from the server to the client. It is sent to protocol[0] + * callback as no protocol has been negotiated on the connection yet. + * Notice that the wsi is set because lws_client_connect_via_info was + * successful. + * + * See http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html + * to understand more detail about the OpenSSL callback that + * generates this libwebsockets callback and the meanings of the + * arguments passed. In this callback, user is the x509_ctx, + * in is the ssl pointer and len is preverify_ok. + * + * THIS IS NOT RECOMMENDED BUT if a cert validation error shall be + * overruled and cert shall be accepted as ok, + * X509_STORE_CTX_set_error((X509_STORE_CTX*)user, X509_V_OK); must be + * called and return value must be 0 to mean the cert is OK; + * returning 1 will fail the cert in any case. + * + * This also means that if you don't handle this callback then + * the default callback action of returning 0 will not accept the + * certificate in case of a validation error decided by the SSL lib. + * + * This is expected and secure behaviour when validating certificates. + * + * Note: LCCSCF_ALLOW_SELFSIGNED and + * LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK still work without this + * callback being implemented. + */ + LWS_CALLBACK_RAW_RX = 59, + /**< RAW mode connection RX */ + LWS_CALLBACK_RAW_CLOSE = 60, + /**< RAW mode connection is closing */ + LWS_CALLBACK_RAW_WRITEABLE = 61, + /**< RAW mode connection may be written */ + LWS_CALLBACK_RAW_ADOPT = 62, + /**< RAW mode connection was adopted (equivalent to 'wsi created') */ + LWS_CALLBACK_RAW_ADOPT_FILE = 63, + /**< RAW mode file was adopted (equivalent to 'wsi created') */ + LWS_CALLBACK_RAW_RX_FILE = 64, + /**< RAW mode file has something to read */ + LWS_CALLBACK_RAW_WRITEABLE_FILE = 65, + /**< RAW mode file is writeable */ + LWS_CALLBACK_RAW_CLOSE_FILE = 66, + /**< RAW mode wsi that adopted a file is closing */ + LWS_CALLBACK_SSL_INFO = 67, + /**< SSL connections only. An event you registered an + * interest in at the vhost has occurred on a connection + * using the vhost. in is a pointer to a + * struct lws_ssl_info containing information about the + * event*/ + LWS_CALLBACK_CHILD_WRITE_VIA_PARENT = 68, + /**< Child has been marked with parent_carries_io attribute, so + * lws_write directs the to this callback at the parent, + * in is a struct lws_write_passthru containing the args + * the lws_write() was called with. + */ + LWS_CALLBACK_CHILD_CLOSING = 69, + /**< Sent to parent to notify them a child is closing / being + * destroyed. in is the child wsi. + */ + LWS_CALLBACK_CGI_PROCESS_ATTACH = 70, + /**< CGI: Sent when the CGI process is spawned for the wsi. The + * len parameter is the PID of the child process */ + + /****** add new things just above ---^ ******/ + + LWS_CALLBACK_USER = 1000, + /**< user code can use any including above without fear of clashes */ +}; + + + +/** + * typedef lws_callback_function() - User server actions + * \param wsi: Opaque websocket instance pointer + * \param reason: The reason for the call + * \param user: Pointer to per-session user data allocated by library + * \param in: Pointer used for some callback reasons + * \param len: Length set for some callback reasons + * + * This callback is the way the user controls what is served. All the + * protocol detail is hidden and handled by the library. + * + * For each connection / session there is user data allocated that is + * pointed to by "user". You set the size of this user data area when + * the library is initialized with lws_create_server. + */ +typedef int +lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len); + +#define LWS_CB_REASON_AUX_BF__CGI 1 +#define LWS_CB_REASON_AUX_BF__PROXY 2 +#define LWS_CB_REASON_AUX_BF__CGI_CHUNK_END 4 +#define LWS_CB_REASON_AUX_BF__CGI_HEADERS 8 +///@} + +/*! \defgroup generic hash + * ## Generic Hash related functions + * + * Lws provides generic hash / digest accessors that abstract the ones + * provided by whatever OpenSSL library you are linking against. + * + * It lets you use the same code if you build against mbedtls or OpenSSL + * for example. + */ +///@{ + +#ifdef LWS_OPENSSL_SUPPORT + +#if defined(LWS_WITH_MBEDTLS) +#include +#include +#include +#endif + +#define LWS_GENHASH_TYPE_SHA1 0 +#define LWS_GENHASH_TYPE_SHA256 1 +#define LWS_GENHASH_TYPE_SHA512 2 + +struct lws_genhash_ctx { + uint8_t type; +#if defined(LWS_WITH_MBEDTLS) + union { + mbedtls_sha1_context sha1; + mbedtls_sha256_context sha256; + mbedtls_sha512_context sha512; + } u; +#else + const EVP_MD *evp_type; + EVP_MD_CTX *mdctx; +#endif +}; + +/** lws_genhash_size() - get hash size in bytes + * + * \param type: one of LWS_GENHASH_TYPE_... + * + * Returns number of bytes in this type of hash + */ +LWS_VISIBLE LWS_EXTERN size_t LWS_WARN_UNUSED_RESULT +lws_genhash_size(int type); + +/** lws_genhash_init() - prepare your struct lws_genhash_ctx for use + * + * \param ctx: your struct lws_genhash_ctx + * \param type: one of LWS_GENHASH_TYPE_... + * + * Initializes the hash context for the type you requested + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_genhash_init(struct lws_genhash_ctx *ctx, int type); + +/** lws_genhash_update() - digest len bytes of the buffer starting at in + * + * \param ctx: your struct lws_genhash_ctx + * \param in: start of the bytes to digest + * \param len: count of bytes to digest + * + * Updates the state of your hash context to reflect digesting len bytes from in + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len); + +/** lws_genhash_destroy() - copy out the result digest and destroy the ctx + * + * \param ctx: your struct lws_genhash_ctx + * \param result: NULL, or where to copy the result hash + * + * Finalizes the hash and copies out the digest. Destroys any allocations such + * that ctx can safely go out of scope after calling this. + * + * NULL result is supported so that you can destroy the ctx cleanly on error + * conditions, where there is no valid result. + */ +LWS_VISIBLE LWS_EXTERN int +lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result); + +#endif + +///@} + +/*! \defgroup extensions Extension related functions + * ##Extension releated functions + * + * Ws defines optional extensions, lws provides the ability to implement these + * in user code if so desired. + * + * We provide one extensions permessage-deflate. + */ +///@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum lws_extension_callback_reasons { + LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT = 0, + LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT = 1, + LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT = 2, + LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT = 3, + LWS_EXT_CB_CONSTRUCT = 4, + LWS_EXT_CB_CLIENT_CONSTRUCT = 5, + LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE = 6, + LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION = 7, + LWS_EXT_CB_DESTROY = 8, + LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING = 9, + LWS_EXT_CB_ANY_WSI_ESTABLISHED = 10, + LWS_EXT_CB_PACKET_RX_PREPARSE = 11, + LWS_EXT_CB_PACKET_TX_PRESEND = 12, + LWS_EXT_CB_PACKET_TX_DO_SEND = 13, + LWS_EXT_CB_HANDSHAKE_REPLY_TX = 14, + LWS_EXT_CB_FLUSH_PENDING_TX = 15, + LWS_EXT_CB_EXTENDED_PAYLOAD_RX = 16, + LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION = 17, + LWS_EXT_CB_1HZ = 18, + LWS_EXT_CB_REQUEST_ON_WRITEABLE = 19, + LWS_EXT_CB_IS_WRITEABLE = 20, + LWS_EXT_CB_PAYLOAD_TX = 21, + LWS_EXT_CB_PAYLOAD_RX = 22, + LWS_EXT_CB_OPTION_DEFAULT = 23, + LWS_EXT_CB_OPTION_SET = 24, + LWS_EXT_CB_OPTION_CONFIRM = 25, + LWS_EXT_CB_NAMED_OPTION_SET = 26, + + /****** add new things just above ---^ ******/ +}; + +/** enum lws_ext_options_types */ +enum lws_ext_options_types { + EXTARG_NONE, /**< does not take an argument */ + EXTARG_DEC, /**< requires a decimal argument */ + EXTARG_OPT_DEC /**< may have an optional decimal argument */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** struct lws_ext_options - Option arguments to the extension. These are + * used in the negotiation at ws upgrade time. + * The helper function lws_ext_parse_options() + * uses these to generate callbacks */ +struct lws_ext_options { + const char *name; /**< Option name, eg, "server_no_context_takeover" */ + enum lws_ext_options_types type; /**< What kind of args the option can take */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** struct lws_ext_option_arg */ +struct lws_ext_option_arg { + const char *option_name; /**< may be NULL, option_index used then */ + int option_index; /**< argument ordinal to use if option_name missing */ + const char *start; /**< value */ + int len; /**< length of value */ +}; + +/** + * typedef lws_extension_callback_function() - Hooks to allow extensions to operate + * \param context: Websockets context + * \param ext: This extension + * \param wsi: Opaque websocket instance pointer + * \param reason: The reason for the call + * \param user: Pointer to ptr to per-session user data allocated by library + * \param in: Pointer used for some callback reasons + * \param len: Length set for some callback reasons + * + * Each extension that is active on a particular connection receives + * callbacks during the connection lifetime to allow the extension to + * operate on websocket data and manage itself. + * + * Libwebsockets takes care of allocating and freeing "user" memory for + * each active extension on each connection. That is what is pointed to + * by the user parameter. + * + * LWS_EXT_CB_CONSTRUCT: called when the server has decided to + * select this extension from the list provided by the client, + * just before the server will send back the handshake accepting + * the connection with this extension active. This gives the + * extension a chance to initialize its connection context found + * in user. + * + * LWS_EXT_CB_CLIENT_CONSTRUCT: same as LWS_EXT_CB_CONSTRUCT + * but called when client is instantiating this extension. Some + * extensions will work the same on client and server side and then + * you can just merge handlers for both CONSTRUCTS. + * + * LWS_EXT_CB_DESTROY: called when the connection the extension was + * being used on is about to be closed and deallocated. It's the + * last chance for the extension to deallocate anything it has + * allocated in the user data (pointed to by user) before the + * user data is deleted. This same callback is used whether you + * are in client or server instantiation context. + * + * LWS_EXT_CB_PACKET_RX_PREPARSE: when this extension was active on + * a connection, and a packet of data arrived at the connection, + * it is passed to this callback to give the extension a chance to + * change the data, eg, decompress it. user is pointing to the + * extension's private connection context data, in is pointing + * to an lws_tokens struct, it consists of a char * pointer called + * token, and an int called token_len. At entry, these are + * set to point to the received buffer and set to the content + * length. If the extension will grow the content, it should use + * a new buffer allocated in its private user context data and + * set the pointed-to lws_tokens members to point to its buffer. + * + * LWS_EXT_CB_PACKET_TX_PRESEND: this works the same way as + * LWS_EXT_CB_PACKET_RX_PREPARSE above, except it gives the + * extension a chance to change websocket data just before it will + * be sent out. Using the same lws_token pointer scheme in in, + * the extension can change the buffer and the length to be + * transmitted how it likes. Again if it wants to grow the + * buffer safely, it should copy the data into its own buffer and + * set the lws_tokens token pointer to it. + * + * LWS_EXT_CB_ARGS_VALIDATE: + */ +typedef int +lws_extension_callback_function(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_extension - An extension we support */ +struct lws_extension { + const char *name; /**< Formal extension name, eg, "permessage-deflate" */ + lws_extension_callback_function *callback; /**< Service callback */ + const char *client_offer; /**< String containing exts and options client offers */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** + * lws_set_extension_option(): set extension option if possible + * + * \param wsi: websocket connection + * \param ext_name: name of ext, like "permessage-deflate" + * \param opt_name: name of option, like "rx_buf_size" + * \param opt_val: value to set option to + */ +LWS_VISIBLE LWS_EXTERN int +lws_set_extension_option(struct lws *wsi, const char *ext_name, + const char *opt_name, const char *opt_val); + +#ifndef LWS_NO_EXTENSIONS +/* lws_get_internal_extensions() - DEPRECATED + * + * \Deprecated There is no longer a set internal extensions table. The table is provided + * by user code along with application-specific settings. See the test + * client and server for how to do. + */ +static LWS_INLINE LWS_WARN_DEPRECATED const struct lws_extension * +lws_get_internal_extensions(void) { return NULL; } + +/** + * lws_ext_parse_options() - deal with parsing negotiated extension options + * + * \param ext: related extension struct + * \param wsi: websocket connection + * \param ext_user: per-connection extension private data + * \param opts: list of supported options + * \param o: option string to parse + * \param len: length + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi, + void *ext_user, const struct lws_ext_options *opts, + const char *o, int len); +#endif + +/** lws_extension_callback_pm_deflate() - extension for RFC7692 + * + * \param context: lws context + * \param ext: related lws_extension struct + * \param wsi: websocket connection + * \param reason: incoming callback reason + * \param user: per-connection extension private data + * \param in: pointer parameter + * \param len: length parameter + * + * Built-in callback implementing RFC7692 permessage-deflate + */ +LWS_EXTERN +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); + +/* + * The internal exts are part of the public abi + * If we add more extensions, publish the callback here ------v + */ +///@} + +/*! \defgroup Protocols-and-Plugins Protocols and Plugins + * \ingroup lwsapi + * + * ##Protocol and protocol plugin -related apis + * + * Protocols bind ws protocol names to a custom callback specific to that + * protocol implementaion. + * + * A list of protocols can be passed in at context creation time, but it is + * also legal to leave that NULL and add the protocols and their callback code + * using plugins. + * + * Plugins are much preferable compared to cut and pasting code into an + * application each time, since they can be used standalone. + */ +///@{ +/** struct lws_protocols - List of protocols and handlers client or server + * supports. */ + +struct lws_protocols { + const char *name; + /**< Protocol name that must match the one given in the client + * Javascript new WebSocket(url, 'protocol') name. */ + lws_callback_function *callback; + /**< The service callback used for this protocol. It allows the + * service action for an entire protocol to be encapsulated in + * the protocol-specific callback */ + size_t per_session_data_size; + /**< Each new connection using this protocol gets + * this much memory allocated on connection establishment and + * freed on connection takedown. A pointer to this per-connection + * allocation is passed into the callback in the 'user' parameter */ + size_t rx_buffer_size; + /**< lws allocates this much space for rx data and informs callback + * when something came. Due to rx flow control, the callback may not + * be able to consume it all without having to return to the event + * loop. That is supported in lws. + * + * If .tx_packet_size is 0, this also controls how much may be sent at once + * for backwards compatibility. + */ + unsigned int id; + /**< ignored by lws, but useful to contain user information bound + * to the selected protocol. For example if this protocol was + * called "myprotocol-v2", you might set id to 2, and the user + * code that acts differently according to the version can do so by + * switch (wsi->protocol->id), user code might use some bits as + * capability flags based on selected protocol version, etc. */ + void *user; /**< ignored by lws, but user code can pass a pointer + here it can later access from the protocol callback */ + size_t tx_packet_size; + /**< 0 indicates restrict send() size to .rx_buffer_size for backwards- + * compatibility. + * If greater than zero, a single send() is restricted to this amount + * and any remainder is buffered by lws and sent afterwards also in + * these size chunks. Since that is expensive, it's preferable + * to restrict one fragment you are trying to send to match this + * size. + */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +struct lws_vhost; + +/** + * lws_vhost_name_to_protocol() - get vhost's protocol object from its name + * + * \param vh: vhost to search + * \param name: protocol name + * + * Returns NULL or a pointer to the vhost's protocol of the requested name + */ +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name); + +/** + * lws_get_protocol() - Returns a protocol pointer from a websocket + * connection. + * \param wsi: pointer to struct websocket you want to know the protocol of + * + * + * Some apis can act on all live connections of a given protocol, + * this is how you can get a pointer to the active protocol if needed. + */ +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_get_protocol(struct lws *wsi); + +/** lws_protocol_get() - deprecated: use lws_get_protocol */ +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_protocol_get(struct lws *wsi) LWS_WARN_DEPRECATED; + +/** + * lws_protocol_vh_priv_zalloc() - Allocate and zero down a protocol's per-vhost + * storage + * \param vhost: vhost the instance is related to + * \param prot: protocol the instance is related to + * \param size: bytes to allocate + * + * Protocols often find it useful to allocate a per-vhost struct, this is a + * helper to be called in the per-vhost init LWS_CALLBACK_PROTOCOL_INIT + */ +LWS_VISIBLE LWS_EXTERN void * +lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, const struct lws_protocols *prot, + int size); + +/** + * lws_protocol_vh_priv_get() - retreive a protocol's per-vhost storage + * + * \param vhost: vhost the instance is related to + * \param prot: protocol the instance is related to + * + * Recover a pointer to the allocated per-vhost storage for the protocol created + * by lws_protocol_vh_priv_zalloc() earlier + */ +LWS_VISIBLE LWS_EXTERN void * +lws_protocol_vh_priv_get(struct lws_vhost *vhost, const struct lws_protocols *prot); + +/** + * lws_adjust_protocol_psds - change a vhost protocol's per session data size + * + * \param wsi: a connection with the protocol to change + * \param new_size: the new size of the per session data size for the protocol + * + * Returns user_space for the wsi, after allocating + * + * This should not be used except to initalize a vhost protocol's per session + * data size one time, before any connections are accepted. + * + * Sometimes the protocol wraps another protocol and needs to discover and set + * its per session data size at runtime. + */ +LWS_VISIBLE LWS_EXTERN void * +lws_adjust_protocol_psds(struct lws *wsi, size_t new_size); + +/** + * lws_finalize_startup() - drop initial process privileges + * + * \param context: lws context + * + * This is called after the end of the vhost protocol initializations, but + * you may choose to call it earlier + */ +LWS_VISIBLE LWS_EXTERN int +lws_finalize_startup(struct lws_context *context); + +LWS_VISIBLE LWS_EXTERN int +lws_protocol_init(struct lws_context *context); + +#ifdef LWS_WITH_PLUGINS + +/* PLUGINS implies LIBUV */ + +#define LWS_PLUGIN_API_MAGIC 180 + +/** struct lws_plugin_capability - how a plugin introduces itself to lws */ +struct lws_plugin_capability { + unsigned int api_magic; /**< caller fills this in, plugin fills rest */ + const struct lws_protocols *protocols; /**< array of supported protocols provided by plugin */ + int count_protocols; /**< how many protocols */ + const struct lws_extension *extensions; /**< array of extensions provided by plugin */ + int count_extensions; /**< how many extensions */ +}; + +typedef int (*lws_plugin_init_func)(struct lws_context *, + struct lws_plugin_capability *); +typedef int (*lws_plugin_destroy_func)(struct lws_context *); + +/** struct lws_plugin */ +struct lws_plugin { + struct lws_plugin *list; /**< linked list */ +#if (UV_VERSION_MAJOR > 0) + uv_lib_t lib; /**< shared library pointer */ +#else + void *l; /**< so we can compile on ancient libuv */ +#endif + char name[64]; /**< name of the plugin */ + struct lws_plugin_capability caps; /**< plugin capabilities */ +}; + +#endif + +///@} + + +/*! \defgroup generic-sessions plugin: generic-sessions + * \ingroup Protocols-and-Plugins + * + * ##Plugin Generic-sessions related + * + * generic-sessions plugin provides a reusable, generic session and login / + * register / forgot password framework including email verification. + */ +///@{ + +#define LWSGS_EMAIL_CONTENT_SIZE 16384 +/**< Maximum size of email we might send */ + +/* SHA-1 binary and hexified versions */ +/** typedef struct lwsgw_hash_bin */ +typedef struct { unsigned char bin[20]; /**< binary representation of hash */} lwsgw_hash_bin; +/** typedef struct lwsgw_hash */ +typedef struct { char id[41]; /**< ascii hex representation of hash */ } lwsgw_hash; + +/** enum lwsgs_auth_bits */ +enum lwsgs_auth_bits { + LWSGS_AUTH_LOGGED_IN = 1, /**< user is logged in as somebody */ + LWSGS_AUTH_ADMIN = 2, /**< logged in as the admin user */ + LWSGS_AUTH_VERIFIED = 4, /**< user has verified his email */ + LWSGS_AUTH_FORGOT_FLOW = 8, /**< he just completed "forgot password" flow */ +}; + +/** struct lws_session_info - information about user session status */ +struct lws_session_info { + char username[32]; /**< username logged in as, or empty string */ + char email[100]; /**< email address associated with login, or empty string */ + char ip[72]; /**< ip address session was started from */ + unsigned int mask; /**< access rights mask associated with session + * see enum lwsgs_auth_bits */ + char session[42]; /**< session id string, usable as opaque uid when not logged in */ +}; + +/** enum lws_gs_event */ +enum lws_gs_event { + LWSGSE_CREATED, /**< a new user was created */ + LWSGSE_DELETED /**< an existing user was deleted */ +}; + +/** struct lws_gs_event_args */ +struct lws_gs_event_args { + enum lws_gs_event event; /**< which event happened */ + const char *username; /**< which username the event happened to */ + const char *email; /**< the email address of that user */ +}; + +///@} + + +/*! \defgroup context-and-vhost context and vhost related functions + * ##Context and Vhost releated functions + * \ingroup lwsapi + * + * + * LWS requires that there is one context, in which you may define multiple + * vhosts. Each vhost is a virtual host, with either its own listen port + * or sharing an existing one. Each vhost has its own SSL context that can + * be set up individually or left disabled. + * + * If you don't care about multiple "site" support, you can ignore it and + * lws will create a single default vhost at context creation time. + */ +///@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ + +/** enum lws_context_options - context and vhost options */ +enum lws_context_options { + LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT = (1 << 1) | + (1 << 12), + /**< (VH) Don't allow the connection unless the client has a + * client cert that we recognize; provides + * LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT */ + LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME = (1 << 2), + /**< (CTX) Don't try to get the server's hostname */ + LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT = (1 << 3) | + (1 << 12), + /**< (VH) Allow non-SSL (plaintext) connections on the same + * port as SSL is listening... undermines the security of SSL; + * provides LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT */ + LWS_SERVER_OPTION_LIBEV = (1 << 4), + /**< (CTX) Use libev event loop */ + LWS_SERVER_OPTION_DISABLE_IPV6 = (1 << 5), + /**< (VH) Disable IPV6 support */ + LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS = (1 << 6), + /**< (VH) Don't load OS CA certs, you will need to load your + * own CA cert(s) */ + LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED = (1 << 7), + /**< (VH) Accept connections with no valid Cert (eg, selfsigned) */ + LWS_SERVER_OPTION_VALIDATE_UTF8 = (1 << 8), + /**< (VH) Check UT-8 correctness */ + LWS_SERVER_OPTION_SSL_ECDH = (1 << 9) | + (1 << 12), + /**< (VH) initialize ECDH ciphers */ + LWS_SERVER_OPTION_LIBUV = (1 << 10), + /**< (CTX) Use libuv event loop */ + LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS = (1 << 11) | + (1 << 12), + /**< (VH) Use http redirect to force http to https + * (deprecated: use mount redirection) */ + LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT = (1 << 12), + /**< (CTX) Initialize the SSL library at all */ + LWS_SERVER_OPTION_EXPLICIT_VHOSTS = (1 << 13), + /**< (CTX) Only create the context when calling context + * create api, implies user code will create its own vhosts */ + LWS_SERVER_OPTION_UNIX_SOCK = (1 << 14), + /**< (VH) Use Unix socket */ + LWS_SERVER_OPTION_STS = (1 << 15), + /**< (VH) Send Strict Transport Security header, making + * clients subsequently go to https even if user asked for http */ + LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY = (1 << 16), + /**< (VH) Enable LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE to take effect */ + LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE = (1 << 17), + /**< (VH) if set, only ipv6 allowed on the vhost */ + LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN = (1 << 18), + /**< (CTX) Libuv only: Do not spin on SIGSEGV / SIGFPE. A segfault + * normally makes the lib spin so you can attach a debugger to it + * even if it happened without a debugger in place. You can disable + * that by giving this option. + */ + LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN = (1 << 19), + /**< For backwards-compatibility reasons, by default + * lws prepends "http://" to the origin you give in the client + * connection info struct. If you give this flag when you create + * the context, only the string you give in the client connect + * info for .origin (if any) will be used directly. + */ + LWS_SERVER_OPTION_FALLBACK_TO_RAW = (1 << 20), + /**< (VH) if invalid http is coming in the first line, */ + LWS_SERVER_OPTION_LIBEVENT = (1 << 21), + /**< (CTX) Use libevent event loop */ + LWS_SERVER_OPTION_ONLY_RAW = (1 << 22), + /**< (VH) All connections to this vhost / port are RAW as soon as + * the connection is accepted, no HTTP is going to be coming. + */ + LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE = (1 << 23), + /**< (VH) Set to allow multiple listen sockets on one interface + + * address + port. The default is to strictly allow only one + * listen socket at a time. This is automatically selected if you + * have multiple service threads. + */ + LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX = (1 << 24), + /**< (VH) Force setting up the vhost SSL_CTX, even though the user + * code doesn't explicitly provide a cert in the info struct. It + * implies the user code is going to provide a cert at the + * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS callback, which + * provides the vhost SSL_CTX * in the user parameter. + */ + + /****** add new things just above ---^ ******/ +}; + +#define lws_check_opt(c, f) (((c) & (f)) == (f)) + +struct lws_plat_file_ops; + +/** struct lws_context_creation_info - parameters to create context and /or vhost with + * + * This is also used to create vhosts.... if LWS_SERVER_OPTION_EXPLICIT_VHOSTS + * is not given, then for backwards compatibility one vhost is created at + * context-creation time using the info from this struct. + * + * If LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, then no vhosts are created + * at the same time as the context, they are expected to be created afterwards. + */ +struct lws_context_creation_info { + int port; + /**< VHOST: Port to listen on. Use CONTEXT_PORT_NO_LISTEN to suppress + * listening for a client. Use CONTEXT_PORT_NO_LISTEN_SERVER if you are + * writing a server but you are using \ref sock-adopt instead of the + * built-in listener */ + const char *iface; + /**< VHOST: NULL to bind the listen socket to all interfaces, or the + * interface name, eg, "eth2" + * If options specifies LWS_SERVER_OPTION_UNIX_SOCK, this member is + * the pathname of a UNIX domain socket. you can use the UNIX domain + * sockets in abstract namespace, by prepending an at symbol to the + * socket name. */ + const struct lws_protocols *protocols; + /**< VHOST: Array of structures listing supported protocols and a protocol- + * specific callback for each one. The list is ended with an + * entry that has a NULL callback pointer. */ + const struct lws_extension *extensions; + /**< VHOST: NULL or array of lws_extension structs listing the + * extensions this context supports. */ + const struct lws_token_limits *token_limits; + /**< CONTEXT: NULL or struct lws_token_limits pointer which is initialized + * with a token length limit for each possible WSI_TOKEN_ */ + const char *ssl_private_key_password; + /**< VHOST: NULL or the passphrase needed for the private key. (For + * backwards compatibility, this can also be used to pass the client + * cert passphrase when setting up a vhost client SSL context, but it is + * preferred to use .client_ssl_private_key_password for that.) */ + const char *ssl_cert_filepath; + /**< VHOST: If libwebsockets was compiled to use ssl, and you want + * to listen using SSL, set to the filepath to fetch the + * server cert from, otherwise NULL for unencrypted. (For backwards + * compatibility, this can also be used to pass the client certificate + * when setting up a vhost client SSL context, but it is preferred to + * use .client_ssl_cert_filepath for that.) */ + const char *ssl_private_key_filepath; + /**< VHOST: filepath to private key if wanting SSL mode; + * if this is set to NULL but ssl_cert_filepath is set, the + * OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY callback is called + * to allow setting of the private key directly via openSSL + * library calls. (For backwards compatibility, this can also be used + * to pass the client cert private key filepath when setting up a + * vhost client SSL context, but it is preferred to use + * .client_ssl_private_key_filepath for that.) */ + const char *ssl_ca_filepath; + /**< VHOST: CA certificate filepath or NULL. (For backwards + * compatibility, this can also be used to pass the client CA + * filepath when setting up a vhost client SSL context, + * but it is preferred to use .client_ssl_ca_filepath for that.) */ + const char *ssl_cipher_list; + /**< VHOST: List of valid ciphers to use (eg, + * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL" + * or you can leave it as NULL to get "DEFAULT" (For backwards + * compatibility, this can also be used to pass the client cipher + * list when setting up a vhost client SSL context, + * but it is preferred to use .client_ssl_cipher_list for that.)*/ + const char *http_proxy_address; + /**< VHOST: If non-NULL, attempts to proxy via the given address. + * If proxy auth is required, use format "username:password\@server:port" */ + unsigned int http_proxy_port; + /**< VHOST: If http_proxy_address was non-NULL, uses this port */ + int gid; + /**< CONTEXT: group id to change to after setting listen socket, or -1. */ + int uid; + /**< CONTEXT: user id to change to after setting listen socket, or -1. */ + unsigned int options; + /**< VHOST + CONTEXT: 0, or LWS_SERVER_OPTION_... bitfields */ + void *user; + /**< VHOST + CONTEXT: optional user pointer that will be associated + * with the context when creating the context (and can be retrieved by + * lws_context_user(context), or with the vhost when creating the vhost + * (and can be retrieved by lws_vhost_user(vhost)). You will need to + * use LWS_SERVER_OPTION_EXPLICIT_VHOSTS and create the vhost separately + * if you care about giving the context and vhost different user pointer + * values. + */ + int ka_time; + /**< CONTEXT: 0 for no TCP keepalive, otherwise apply this keepalive + * timeout to all libwebsocket sockets, client or server */ + int ka_probes; + /**< CONTEXT: if ka_time was nonzero, after the timeout expires how many + * times to try to get a response from the peer before giving up + * and killing the connection */ + int ka_interval; + /**< CONTEXT: if ka_time was nonzero, how long to wait before each ka_probes + * attempt */ +#ifdef LWS_OPENSSL_SUPPORT + SSL_CTX *provided_client_ssl_ctx; + /**< CONTEXT: If non-null, swap out libwebsockets ssl + * implementation for the one provided by provided_ssl_ctx. + * Libwebsockets no longer is responsible for freeing the context + * if this option is selected. */ +#else /* maintain structure layout either way */ + void *provided_client_ssl_ctx; /**< dummy if ssl disabled */ +#endif + + short max_http_header_data; + /**< CONTEXT: The max amount of header payload that can be handled + * in an http request (unrecognized header payload is dropped) */ + short max_http_header_pool; + /**< CONTEXT: The max number of connections with http headers that + * can be processed simultaneously (the corresponding memory is + * allocated for the lifetime of the context). If the pool is + * busy new incoming connections must wait for accept until one + * becomes free. */ + + unsigned int count_threads; + /**< CONTEXT: how many contexts to create in an array, 0 = 1 */ + unsigned int fd_limit_per_thread; + /**< CONTEXT: nonzero means restrict each service thread to this + * many fds, 0 means the default which is divide the process fd + * limit by the number of threads. */ + unsigned int timeout_secs; + /**< VHOST: various processes involving network roundtrips in the + * library are protected from hanging forever by timeouts. If + * nonzero, this member lets you set the timeout used in seconds. + * Otherwise a default timeout is used. */ + const char *ecdh_curve; + /**< VHOST: if NULL, defaults to initializing server with "prime256v1" */ + const char *vhost_name; + /**< VHOST: name of vhost, must match external DNS name used to + * access the site, like "warmcat.com" as it's used to match + * Host: header and / or SNI name for SSL. */ + const char * const *plugin_dirs; + /**< CONTEXT: NULL, or NULL-terminated array of directories to + * scan for lws protocol plugins at context creation time */ + const struct lws_protocol_vhost_options *pvo; + /**< VHOST: pointer to optional linked list of per-vhost + * options made accessible to protocols */ + int keepalive_timeout; + /**< VHOST: (default = 0 = 60s) seconds to allow remote + * client to hold on to an idle HTTP/1.1 connection */ + const char *log_filepath; + /**< VHOST: filepath to append logs to... this is opened before + * any dropping of initial privileges */ + const struct lws_http_mount *mounts; + /**< VHOST: optional linked list of mounts for this vhost */ + const char *server_string; + /**< CONTEXT: string used in HTTP headers to identify server + * software, if NULL, "libwebsockets". */ + unsigned int pt_serv_buf_size; + /**< CONTEXT: 0 = default of 4096. This buffer is used by + * various service related features including file serving, it + * defines the max chunk of file that can be sent at once. + * At the risk of lws having to buffer failed large sends, it + * can be increased to, eg, 128KiB to improve throughput. */ + unsigned int max_http_header_data2; + /**< CONTEXT: if max_http_header_data is 0 and this + * is nonzero, this will be used in place of the default. It's + * like this for compatibility with the original short version, + * this is unsigned int length. */ + long ssl_options_set; + /**< VHOST: Any bits set here will be set as SSL options */ + long ssl_options_clear; + /**< VHOST: Any bits set here will be cleared as SSL options */ + unsigned short ws_ping_pong_interval; + /**< CONTEXT: 0 for none, else interval in seconds between sending + * PINGs on idle websocket connections. When the PING is sent, + * the PONG must come within the normal timeout_secs timeout period + * or the connection will be dropped. + * Any RX or TX traffic on the connection restarts the interval timer, + * so a connection which always sends or receives something at intervals + * less than the interval given here will never send PINGs / expect + * PONGs. Conversely as soon as the ws connection is established, an + * idle connection will do the PING / PONG roundtrip as soon as + * ws_ping_pong_interval seconds has passed without traffic + */ + const struct lws_protocol_vhost_options *headers; + /**< VHOST: pointer to optional linked list of per-vhost + * canned headers that are added to server responses */ + + const struct lws_protocol_vhost_options *reject_service_keywords; + /**< CONTEXT: Optional list of keywords and rejection codes + text. + * + * The keywords are checked for existing in the user agent string. + * + * Eg, "badrobot" "404 Not Found" + */ + void *external_baggage_free_on_destroy; + /**< CONTEXT: NULL, or pointer to something externally malloc'd, that + * should be freed when the context is destroyed. This allows you to + * automatically sync the freeing action to the context destruction + * action, so there is no need for an external free() if the context + * succeeded to create. + */ + + const char *client_ssl_private_key_password; + /**< VHOST: Client SSL context init: NULL or the passphrase needed + * for the private key */ + const char *client_ssl_cert_filepath; + /**< VHOST: Client SSL context init:T he certificate the client + * should present to the peer on connection */ + const char *client_ssl_private_key_filepath; + /**< VHOST: Client SSL context init: filepath to client private key + * if this is set to NULL but client_ssl_cert_filepath is set, you + * can handle the LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS + * callback of protocols[0] to allow setting of the private key directly + * via openSSL library calls */ + const char *client_ssl_ca_filepath; + /**< VHOST: Client SSL context init: CA certificate filepath or NULL */ + const char *client_ssl_cipher_list; + /**< VHOST: Client SSL context init: List of valid ciphers to use (eg, + * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL" + * or you can leave it as NULL to get "DEFAULT" */ + + const struct lws_plat_file_ops *fops; + /**< CONTEXT: NULL, or pointer to an array of fops structs, terminated + * by a sentinel with NULL .open. + * + * If NULL, lws provides just the platform file operations struct for + * backwards compatibility. + */ + int simultaneous_ssl_restriction; + /**< CONTEXT: 0 (no limit) or limit of simultaneous SSL sessions possible.*/ + const char *socks_proxy_address; + /**< VHOST: If non-NULL, attempts to proxy via the given address. + * If proxy auth is required, use format "username:password\@server:port" */ + unsigned int socks_proxy_port; + /**< VHOST: If socks_proxy_address was non-NULL, uses this port */ +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + cap_value_t caps[4]; + /**< CONTEXT: array holding Linux capabilities you want to + * continue to be available to the server after it transitions + * to a noprivileged user. Usually none are needed but for, eg, + * .bind_iface, CAP_NET_RAW is required. This gives you a way + * to still have the capability but drop root. + */ + char count_caps; + /**< CONTEXT: count of Linux capabilities in .caps[]. 0 means + * no capabilities will be inherited from root (the default) */ +#endif + int bind_iface; + /**< VHOST: nonzero to strictly bind sockets to the interface name in + * .iface (eg, "eth2"), using SO_BIND_TO_DEVICE. + * + * Requires SO_BINDTODEVICE support from your OS and CAP_NET_RAW + * capability. + * + * Notice that common things like access network interface IP from + * your local machine use your lo / loopback interface and will be + * disallowed by this. + */ + int ssl_info_event_mask; + /**< VHOST: mask of ssl events to be reported on LWS_CALLBACK_SSL_INFO + * callback for connections on this vhost. The mask values are of + * the form SSL_CB_ALERT, defined in openssl/ssl.h. The default of + * 0 means no info events will be reported. + */ + unsigned int timeout_secs_ah_idle; + /**< VHOST: seconds to allow a client to hold an ah without using it. + * 0 defaults to 10s. */ + unsigned short ip_limit_ah; + /**< CONTEXT: max number of ah a single IP may use simultaneously + * 0 is no limit. This is a soft limit: if the limit is + * reached, connections from that IP will wait in the ah + * waiting list and not be able to acquire an ah until + * a connection belonging to the IP relinquishes one it + * already has. + */ + unsigned short ip_limit_wsi; + /**< CONTEXT: max number of wsi a single IP may use simultaneously. + * 0 is no limit. This is a hard limit, connections from + * the same IP will simply be dropped once it acquires the + * amount of simultaneous wsi / accepted connections + * given here. + */ + uint32_t http2_settings[7]; + /**< CONTEXT: after context creation http2_settings[1] thru [6] have + * been set to the lws platform default values. + * VHOST: if http2_settings[0] is nonzero, the values given in + * http2_settings[1]..[6] are used instead of the lws + * platform default values. + * Just leave all at 0 if you don't care. + */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility + * + * The below is to ensure later library versions with new + * members added above will see 0 (default) even if the app + * was not built against the newer headers. + */ + + void *_unused[8]; /**< dummy */ +}; + +/** + * lws_create_context() - Create the websocket handler + * \param info: pointer to struct with parameters + * + * This function creates the listening socket (if serving) and takes care + * of all initialization in one step. + * + * If option LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, no vhost is + * created; you're expected to create your own vhosts afterwards using + * lws_create_vhost(). Otherwise a vhost named "default" is also created + * using the information in the vhost-related members, for compatibility. + * + * After initialization, it returns a struct lws_context * that + * represents this server. After calling, user code needs to take care + * of calling lws_service() with the context pointer to get the + * server's sockets serviced. This must be done in the same process + * context as the initialization call. + * + * The protocol callback functions are called for a handful of events + * including http requests coming in, websocket connections becoming + * established, and data arriving; it's also called periodically to allow + * async transmission. + * + * HTTP requests are sent always to the FIRST protocol in protocol, since + * at that time websocket protocol has not been negotiated. Other + * protocols after the first one never see any HTTP callback activity. + * + * The server created is a simple http server by default; part of the + * websocket standard is upgrading this http connection to a websocket one. + * + * This allows the same server to provide files like scripts and favicon / + * images or whatever over http and dynamic data over websockets all in + * one place; they're all handled in the user callback. + */ +LWS_VISIBLE LWS_EXTERN struct lws_context * +lws_create_context(struct lws_context_creation_info *info); + +/** + * lws_context_destroy() - Destroy the websocket context + * \param context: Websocket context + * + * This function closes any active connections and then frees the + * context. After calling this, any further use of the context is + * undefined. + */ +LWS_VISIBLE LWS_EXTERN void +lws_context_destroy(struct lws_context *context); + +LWS_VISIBLE LWS_EXTERN void +lws_context_destroy2(struct lws_context *context); + +typedef int (*lws_reload_func)(void); + +/** + * lws_context_deprecate() - Deprecate the websocket context + * + * \param context: Websocket context + * \param cb: Callback notified when old context listen sockets are closed + * + * This function is used on an existing context before superceding it + * with a new context. + * + * It closes any listen sockets in the context, so new connections are + * not possible. + * + * And it marks the context to be deleted when the number of active + * connections into it falls to zero. + * + * Otherwise if you attach the deprecated context to the replacement + * context when it has been created using lws_context_attach_deprecated() + * both any deprecated and the new context will service their connections. + * + * This is aimed at allowing seamless configuration reloads. + * + * The callback cb will be called after the listen sockets are actually + * closed and may be reopened. In the callback the new context should be + * configured and created. (With libuv, socket close happens async after + * more loop events). + */ +LWS_VISIBLE LWS_EXTERN void +lws_context_deprecate(struct lws_context *context, lws_reload_func cb); + +LWS_VISIBLE LWS_EXTERN int +lws_context_is_deprecated(struct lws_context *context); + +/** + * lws_set_proxy() - Setups proxy to lws_context. + * \param vhost: pointer to struct lws_vhost you want set proxy for + * \param proxy: pointer to c string containing proxy in format address:port + * + * Returns 0 if proxy string was parsed and proxy was setup. + * Returns -1 if proxy is NULL or has incorrect format. + * + * This is only required if your OS does not provide the http_proxy + * environment variable (eg, OSX) + * + * IMPORTANT! You should call this function right after creation of the + * lws_context and before call to connect. If you call this + * function after connect behavior is undefined. + * This function will override proxy settings made on lws_context + * creation with genenv() call. + */ +LWS_VISIBLE LWS_EXTERN int +lws_set_proxy(struct lws_vhost *vhost, const char *proxy); + +/** + * lws_set_socks() - Setup socks to lws_context. + * \param vhost: pointer to struct lws_vhost you want set socks for + * \param socks: pointer to c string containing socks in format address:port + * + * Returns 0 if socks string was parsed and socks was setup. + * Returns -1 if socks is NULL or has incorrect format. + * + * This is only required if your OS does not provide the socks_proxy + * environment variable (eg, OSX) + * + * IMPORTANT! You should call this function right after creation of the + * lws_context and before call to connect. If you call this + * function after connect behavior is undefined. + * This function will override proxy settings made on lws_context + * creation with genenv() call. + */ +LWS_VISIBLE LWS_EXTERN int +lws_set_socks(struct lws_vhost *vhost, const char *socks); + +struct lws_vhost; + +/** + * lws_create_vhost() - Create a vhost (virtual server context) + * \param context: pointer to result of lws_create_context() + * \param info: pointer to struct with parameters + * + * This function creates a virtual server (vhost) using the vhost-related + * members of the info struct. You can create many vhosts inside one context + * if you created the context with the option LWS_SERVER_OPTION_EXPLICIT_VHOSTS + */ +LWS_VISIBLE LWS_EXTERN struct lws_vhost * +lws_create_vhost(struct lws_context *context, + struct lws_context_creation_info *info); + +/** + * lws_vhost_destroy() - Destroy a vhost (virtual server context) + * + * \param vh: pointer to result of lws_create_vhost() + * + * This function destroys a vhost. Normally, if you just want to exit, + * then lws_destroy_context() will take care of everything. If you want + * to destroy an individual vhost and all connections and allocations, you + * can do it with this. + * + * If the vhost has a listen sockets shared by other vhosts, it will be given + * to one of the vhosts sharing it rather than closed. + */ +LWS_VISIBLE LWS_EXTERN void +lws_vhost_destroy(struct lws_vhost *vh); + +/** + * lwsws_get_config_globals() - Parse a JSON server config file + * \param info: pointer to struct with parameters + * \param d: filepath of the config file + * \param config_strings: storage for the config strings extracted from JSON, + * the pointer is incremented as strings are stored + * \param len: pointer to the remaining length left in config_strings + * the value is decremented as strings are stored + * + * This function prepares a n lws_context_creation_info struct with global + * settings from a file d. + * + * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled + */ +LWS_VISIBLE LWS_EXTERN int +lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d, + char **config_strings, int *len); + +/** + * lwsws_get_config_vhosts() - Create vhosts from a JSON server config file + * \param context: pointer to result of lws_create_context() + * \param info: pointer to struct with parameters + * \param d: filepath of the config file + * \param config_strings: storage for the config strings extracted from JSON, + * the pointer is incremented as strings are stored + * \param len: pointer to the remaining length left in config_strings + * the value is decremented as strings are stored + * + * This function creates vhosts into a context according to the settings in + *JSON files found in directory d. + * + * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled + */ +LWS_VISIBLE LWS_EXTERN int +lwsws_get_config_vhosts(struct lws_context *context, + struct lws_context_creation_info *info, const char *d, + char **config_strings, int *len); + +/** lws_vhost_get() - \deprecated deprecated: use lws_get_vhost() */ +LWS_VISIBLE LWS_EXTERN struct lws_vhost * +lws_vhost_get(struct lws *wsi) LWS_WARN_DEPRECATED; + +/** + * lws_get_vhost() - return the vhost a wsi belongs to + * + * \param wsi: which connection + */ +LWS_VISIBLE LWS_EXTERN struct lws_vhost * +lws_get_vhost(struct lws *wsi); + +/** + * lws_json_dump_vhost() - describe vhost state and stats in JSON + * + * \param vh: the vhost + * \param buf: buffer to fill with JSON + * \param len: max length of buf + */ +LWS_VISIBLE LWS_EXTERN int +lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len); + +/** + * lws_json_dump_context() - describe context state and stats in JSON + * + * \param context: the context + * \param buf: buffer to fill with JSON + * \param len: max length of buf + * \param hide_vhosts: nonzero to not provide per-vhost mount etc information + * + * Generates a JSON description of vhost state into buf + */ +LWS_VISIBLE LWS_EXTERN int +lws_json_dump_context(const struct lws_context *context, char *buf, int len, + int hide_vhosts); + +/** + * lws_vhost_user() - get the user data associated with the vhost + * \param vhost: Websocket vhost + * + * This returns the optional user pointer that can be attached to + * a vhost when it was created. Lws never dereferences this pointer, it only + * sets it when the vhost is created, and returns it using this api. + */ +LWS_VISIBLE LWS_EXTERN void * +lws_vhost_user(struct lws_vhost *vhost); + +/** + * lws_context_user() - get the user data associated with the context + * \param context: Websocket context + * + * This returns the optional user allocation that can be attached to + * the context the sockets live in at context_create time. It's a way + * to let all sockets serviced in the same context share data without + * using globals statics in the user code. + */ +LWS_VISIBLE LWS_EXTERN void * +lws_context_user(struct lws_context *context); + +/*! \defgroup vhost-mounts Vhost mounts and options + * \ingroup context-and-vhost-creation + * + * ##Vhost mounts and options + */ +///@{ +/** struct lws_protocol_vhost_options - linked list of per-vhost protocol + * name=value options + * + * This provides a general way to attach a linked-list of name=value pairs, + * which can also have an optional child link-list using the options member. + */ +struct lws_protocol_vhost_options { + const struct lws_protocol_vhost_options *next; /**< linked list */ + const struct lws_protocol_vhost_options *options; /**< child linked-list of more options for this node */ + const char *name; /**< name of name=value pair */ + const char *value; /**< value of name=value pair */ +}; + +/** enum lws_mount_protocols + * This specifies the mount protocol for a mountpoint, whether it is to be + * served from a filesystem, or it is a cgi etc. + */ +enum lws_mount_protocols { + LWSMPRO_HTTP = 0, /**< http reverse proxy */ + LWSMPRO_HTTPS = 1, /**< https reverse proxy */ + LWSMPRO_FILE = 2, /**< serve from filesystem directory */ + LWSMPRO_CGI = 3, /**< pass to CGI to handle */ + LWSMPRO_REDIR_HTTP = 4, /**< redirect to http:// url */ + LWSMPRO_REDIR_HTTPS = 5, /**< redirect to https:// url */ + LWSMPRO_CALLBACK = 6, /**< hand by named protocol's callback */ +}; + +/** struct lws_http_mount + * + * arguments for mounting something in a vhost's url namespace + */ +struct lws_http_mount { + const struct lws_http_mount *mount_next; + /**< pointer to next struct lws_http_mount */ + const char *mountpoint; + /**< mountpoint in http pathspace, eg, "/" */ + const char *origin; + /**< path to be mounted, eg, "/var/www/warmcat.com" */ + const char *def; + /**< default target, eg, "index.html" */ + const char *protocol; + /**<"protocol-name" to handle mount */ + + const struct lws_protocol_vhost_options *cgienv; + /**< optional linked-list of cgi options. These are created + * as environment variables for the cgi process + */ + const struct lws_protocol_vhost_options *extra_mimetypes; + /**< optional linked-list of mimetype mappings */ + const struct lws_protocol_vhost_options *interpret; + /**< optional linked-list of files to be interpreted */ + + int cgi_timeout; + /**< seconds cgi is allowed to live, if cgi://mount type */ + int cache_max_age; + /**< max-age for reuse of client cache of files, seconds */ + unsigned int auth_mask; + /**< bits set here must be set for authorized client session */ + + unsigned int cache_reusable:1; /**< set if client cache may reuse this */ + unsigned int cache_revalidate:1; /**< set if client cache should revalidate on use */ + unsigned int cache_intermediaries:1; /**< set if intermediaries are allowed to cache */ + + unsigned char origin_protocol; /**< one of enum lws_mount_protocols */ + unsigned char mountpoint_len; /**< length of mountpoint string */ + + const char *basic_auth_login_file; + /**revents will be zeroed now. + * + * If the socket is foreign to lws, it leaves revents alone. So you can + * see if you should service yourself by checking the pollfd revents + * after letting lws try to service it. + * + * You should also call this with pollfd = NULL to just allow the + * once-per-second global timeout checks; if less than a second since the last + * check it returns immediately then. + */ +LWS_VISIBLE LWS_EXTERN int +lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd); + +/** + * lws_service_fd_tsi() - Service polled socket in specific service thread + * \param context: Websocket context + * \param pollfd: The pollfd entry describing the socket fd and which events + * happened. + * \param tsi: thread service index + * + * Same as lws_service_fd() but used with multiple service threads + */ +LWS_VISIBLE LWS_EXTERN int +lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, + int tsi); + +/** + * lws_service_adjust_timeout() - Check for any connection needing forced service + * \param context: Websocket context + * \param timeout_ms: The original poll timeout value. You can just set this + * to 1 if you don't really have a poll timeout. + * \param tsi: thread service index + * + * Under some conditions connections may need service even though there is no + * pending network action on them, this is "forced service". For default + * poll() and libuv / libev, the library takes care of calling this and + * dealing with it for you. But for external poll() integration, you need + * access to the apis. + * + * If anybody needs "forced service", returned timeout is zero. In that case, + * you can call lws_service_tsi() with a timeout of -1 to only service + * guys who need forced service. + */ +LWS_VISIBLE LWS_EXTERN int +lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi); + +/* Backwards compatibility */ +#define lws_plat_service_tsi lws_service_tsi + +LWS_VISIBLE LWS_EXTERN int +lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); + +///@} + +/*! \defgroup http HTTP + + Modules related to handling HTTP +*/ +//@{ + +/*! \defgroup httpft HTTP File transfer + * \ingroup http + + APIs for sending local files in response to HTTP requests +*/ +//@{ + +/** + * lws_get_mimetype() - Determine mimetype to use from filename + * + * \param file: filename + * \param m: NULL, or mount context + * + * This uses a canned list of known filetypes first, if no match and m is + * non-NULL, then tries a list of per-mount file suffix to mimtype mappings. + * + * Returns either NULL or a pointer to the mimetype matching the file. + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_get_mimetype(const char *file, const struct lws_http_mount *m); + +/** + * lws_serve_http_file() - Send a file back to the client using http + * \param wsi: Websocket instance (available from user callback) + * \param file: The file to issue over http + * \param content_type: The http content type, eg, text/html + * \param other_headers: NULL or pointer to header string + * \param other_headers_len: length of the other headers if non-NULL + * + * This function is intended to be called from the callback in response + * to http requests from the client. It allows the callback to issue + * local files down the http link in a single step. + * + * Returning <0 indicates error and the wsi should be closed. Returning + * >0 indicates the file was completely sent and + * lws_http_transaction_completed() called on the wsi (and close if != 0) + * ==0 indicates the file transfer is started and needs more service later, + * the wsi should be left alone. + */ +LWS_VISIBLE LWS_EXTERN int +lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, + const char *other_headers, int other_headers_len); + +LWS_VISIBLE LWS_EXTERN int +lws_serve_http_file_fragment(struct lws *wsi); +//@} + + +enum http_status { + HTTP_STATUS_CONTINUE = 100, + + HTTP_STATUS_OK = 200, + HTTP_STATUS_NO_CONTENT = 204, + HTTP_STATUS_PARTIAL_CONTENT = 206, + + HTTP_STATUS_MOVED_PERMANENTLY = 301, + HTTP_STATUS_FOUND = 302, + HTTP_STATUS_SEE_OTHER = 303, + HTTP_STATUS_NOT_MODIFIED = 304, + + HTTP_STATUS_BAD_REQUEST = 400, + HTTP_STATUS_UNAUTHORIZED, + HTTP_STATUS_PAYMENT_REQUIRED, + HTTP_STATUS_FORBIDDEN, + HTTP_STATUS_NOT_FOUND, + HTTP_STATUS_METHOD_NOT_ALLOWED, + HTTP_STATUS_NOT_ACCEPTABLE, + HTTP_STATUS_PROXY_AUTH_REQUIRED, + HTTP_STATUS_REQUEST_TIMEOUT, + HTTP_STATUS_CONFLICT, + HTTP_STATUS_GONE, + HTTP_STATUS_LENGTH_REQUIRED, + HTTP_STATUS_PRECONDITION_FAILED, + HTTP_STATUS_REQ_ENTITY_TOO_LARGE, + HTTP_STATUS_REQ_URI_TOO_LONG, + HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, + HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, + HTTP_STATUS_EXPECTATION_FAILED, + + HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, + HTTP_STATUS_NOT_IMPLEMENTED, + HTTP_STATUS_BAD_GATEWAY, + HTTP_STATUS_SERVICE_UNAVAILABLE, + HTTP_STATUS_GATEWAY_TIMEOUT, + HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED, +}; +/*! \defgroup html-chunked-substitution HTML Chunked Substitution + * \ingroup http + * + * ##HTML chunked Substitution + * + * APIs for receiving chunks of text, replacing a set of variable names via + * a callback, and then prepending and appending HTML chunked encoding + * headers. + */ +//@{ + +struct lws_process_html_args { + char *p; /**< pointer to the buffer containing the data */ + int len; /**< length of the original data at p */ + int max_len; /**< maximum length we can grow the data to */ + int final; /**< set if this is the last chunk of the file */ +}; + +typedef const char *(*lws_process_html_state_cb)(void *data, int index); + +struct lws_process_html_state { + char *start; /**< pointer to start of match */ + char swallow[16]; /**< matched character buffer */ + int pos; /**< position in match */ + void *data; /**< opaque pointer */ + const char * const *vars; /**< list of variable names */ + int count_vars; /**< count of variable names */ + + lws_process_html_state_cb replace; /**< called on match to perform substitution */ +}; + +/*! lws_chunked_html_process() - generic chunked substitution + * \param args: buffer to process using chunked encoding + * \param s: current processing state + */ +LWS_VISIBLE LWS_EXTERN int +lws_chunked_html_process(struct lws_process_html_args *args, + struct lws_process_html_state *s); +//@} + +/** \defgroup HTTP-headers-read HTTP headers: read + * \ingroup http + * + * ##HTTP header releated functions + * + * In lws the client http headers are temporarily stored in a pool, only for the + * duration of the http part of the handshake. It's because in most cases, + * the header content is ignored for the whole rest of the connection lifetime + * and would then just be taking up space needlessly. + * + * During LWS_CALLBACK_HTTP when the URI path is delivered is the last time + * the http headers are still allocated, you can use these apis then to + * look at and copy out interesting header content (cookies, etc) + * + * Notice that the header total length reported does not include a terminating + * '\0', however you must allocate for it when using the _copy apis. So the + * length reported for a header containing "123" is 3, but you must provide + * a buffer of length 4 so that "123\0" may be copied into it, or the copy + * will fail with a nonzero return code. + * + * In the special case of URL arguments, like ?x=1&y=2, the arguments are + * stored in a token named for the method, eg, WSI_TOKEN_GET_URI if it + * was a GET or WSI_TOKEN_POST_URI if POST. You can check the total + * length to confirm the method. + * + * For URL arguments, each argument is stored urldecoded in a "fragment", so + * you can use the fragment-aware api lws_hdr_copy_fragment() to access each + * argument in turn: the fragments contain urldecoded strings like x=1 or y=2. + * + * As a convenience, lws has an api that will find the fragment with a + * given name= part, lws_get_urlarg_by_name(). + */ +///@{ + +/** struct lws_tokens + * you need these to look at headers that have been parsed if using the + * LWS_CALLBACK_FILTER_CONNECTION callback. If a header from the enum + * list below is absent, .token = NULL and token_len = 0. Otherwise .token + * points to .token_len chars containing that header content. + */ +struct lws_tokens { + char *token; /**< pointer to start of the token */ + int token_len; /**< length of the token's value */ +}; + +/* enum lws_token_indexes + * these have to be kept in sync with lextable.h / minilex.c + * + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum lws_token_indexes { + WSI_TOKEN_GET_URI = 0, + WSI_TOKEN_POST_URI = 1, + WSI_TOKEN_OPTIONS_URI = 2, + WSI_TOKEN_HOST = 3, + WSI_TOKEN_CONNECTION = 4, + WSI_TOKEN_UPGRADE = 5, + WSI_TOKEN_ORIGIN = 6, + WSI_TOKEN_DRAFT = 7, + WSI_TOKEN_CHALLENGE = 8, + WSI_TOKEN_EXTENSIONS = 9, + WSI_TOKEN_KEY1 = 10, + WSI_TOKEN_KEY2 = 11, + WSI_TOKEN_PROTOCOL = 12, + WSI_TOKEN_ACCEPT = 13, + WSI_TOKEN_NONCE = 14, + WSI_TOKEN_HTTP = 15, + WSI_TOKEN_HTTP2_SETTINGS = 16, + WSI_TOKEN_HTTP_ACCEPT = 17, + WSI_TOKEN_HTTP_AC_REQUEST_HEADERS = 18, + WSI_TOKEN_HTTP_IF_MODIFIED_SINCE = 19, + WSI_TOKEN_HTTP_IF_NONE_MATCH = 20, + WSI_TOKEN_HTTP_ACCEPT_ENCODING = 21, + WSI_TOKEN_HTTP_ACCEPT_LANGUAGE = 22, + WSI_TOKEN_HTTP_PRAGMA = 23, + WSI_TOKEN_HTTP_CACHE_CONTROL = 24, + WSI_TOKEN_HTTP_AUTHORIZATION = 25, + WSI_TOKEN_HTTP_COOKIE = 26, + WSI_TOKEN_HTTP_CONTENT_LENGTH = 27, + WSI_TOKEN_HTTP_CONTENT_TYPE = 28, + WSI_TOKEN_HTTP_DATE = 29, + WSI_TOKEN_HTTP_RANGE = 30, + WSI_TOKEN_HTTP_REFERER = 31, + WSI_TOKEN_KEY = 32, + WSI_TOKEN_VERSION = 33, + WSI_TOKEN_SWORIGIN = 34, + + WSI_TOKEN_HTTP_COLON_AUTHORITY = 35, + WSI_TOKEN_HTTP_COLON_METHOD = 36, + WSI_TOKEN_HTTP_COLON_PATH = 37, + WSI_TOKEN_HTTP_COLON_SCHEME = 38, + WSI_TOKEN_HTTP_COLON_STATUS = 39, + + WSI_TOKEN_HTTP_ACCEPT_CHARSET = 40, + WSI_TOKEN_HTTP_ACCEPT_RANGES = 41, + WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN = 42, + WSI_TOKEN_HTTP_AGE = 43, + WSI_TOKEN_HTTP_ALLOW = 44, + WSI_TOKEN_HTTP_CONTENT_DISPOSITION = 45, + WSI_TOKEN_HTTP_CONTENT_ENCODING = 46, + WSI_TOKEN_HTTP_CONTENT_LANGUAGE = 47, + WSI_TOKEN_HTTP_CONTENT_LOCATION = 48, + WSI_TOKEN_HTTP_CONTENT_RANGE = 49, + WSI_TOKEN_HTTP_ETAG = 50, + WSI_TOKEN_HTTP_EXPECT = 51, + WSI_TOKEN_HTTP_EXPIRES = 52, + WSI_TOKEN_HTTP_FROM = 53, + WSI_TOKEN_HTTP_IF_MATCH = 54, + WSI_TOKEN_HTTP_IF_RANGE = 55, + WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE = 56, + WSI_TOKEN_HTTP_LAST_MODIFIED = 57, + WSI_TOKEN_HTTP_LINK = 58, + WSI_TOKEN_HTTP_LOCATION = 59, + WSI_TOKEN_HTTP_MAX_FORWARDS = 60, + WSI_TOKEN_HTTP_PROXY_AUTHENTICATE = 61, + WSI_TOKEN_HTTP_PROXY_AUTHORIZATION = 62, + WSI_TOKEN_HTTP_REFRESH = 63, + WSI_TOKEN_HTTP_RETRY_AFTER = 64, + WSI_TOKEN_HTTP_SERVER = 65, + WSI_TOKEN_HTTP_SET_COOKIE = 66, + WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY = 67, + WSI_TOKEN_HTTP_TRANSFER_ENCODING = 68, + WSI_TOKEN_HTTP_USER_AGENT = 69, + WSI_TOKEN_HTTP_VARY = 70, + WSI_TOKEN_HTTP_VIA = 71, + WSI_TOKEN_HTTP_WWW_AUTHENTICATE = 72, + + WSI_TOKEN_PATCH_URI = 73, + WSI_TOKEN_PUT_URI = 74, + WSI_TOKEN_DELETE_URI = 75, + + WSI_TOKEN_HTTP_URI_ARGS = 76, + WSI_TOKEN_PROXY = 77, + WSI_TOKEN_HTTP_X_REAL_IP = 78, + WSI_TOKEN_HTTP1_0 = 79, + WSI_TOKEN_X_FORWARDED_FOR = 80, + WSI_TOKEN_CONNECT = 81, + WSI_TOKEN_HEAD_URI = 82, + WSI_TOKEN_TE = 83, + /****** add new things just above ---^ ******/ + + /* use token storage to stash these internally, not for + * user use */ + + _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, + _WSI_TOKEN_CLIENT_PEER_ADDRESS, + _WSI_TOKEN_CLIENT_URI, + _WSI_TOKEN_CLIENT_HOST, + _WSI_TOKEN_CLIENT_ORIGIN, + _WSI_TOKEN_CLIENT_METHOD, + _WSI_TOKEN_CLIENT_IFACE, + + /* always last real token index*/ + WSI_TOKEN_COUNT, + + /* parser state additions, no storage associated */ + WSI_TOKEN_NAME_PART, + WSI_TOKEN_SKIPPING, + WSI_TOKEN_SKIPPING_SAW_CR, + WSI_PARSING_COMPLETE, + WSI_INIT_TOKEN_MUXURL, +}; + +struct lws_token_limits { + unsigned short token_limit[WSI_TOKEN_COUNT]; /**< max chars for this token */ +}; + +/** + * lws_token_to_string() - returns a textual representation of a hdr token index + * + * \param token: token index + */ +LWS_VISIBLE LWS_EXTERN const unsigned char * +lws_token_to_string(enum lws_token_indexes token); + +/** + * lws_hdr_total_length: report length of all fragments of a header totalled up + * The returned length does not include the space for a + * terminating '\0' + * + * \param wsi: websocket connection + * \param h: which header index we are interested in + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h); + +/** + * lws_hdr_fragment_length: report length of a single fragment of a header + * The returned length does not include the space for a + * terminating '\0' + * + * \param wsi: websocket connection + * \param h: which header index we are interested in + * \param frag_idx: which fragment of h we want to get the length of + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx); + +/** + * lws_hdr_copy() - copy a single fragment of the given header to a buffer + * The buffer length len must include space for an additional + * terminating '\0', or it will fail returning -1. + * + * \param wsi: websocket connection + * \param dest: destination buffer + * \param len: length of destination buffer + * \param h: which header index we are interested in + * + * copies the whole, aggregated header, even if it was delivered in + * several actual headers piece by piece + */ +LWS_VISIBLE LWS_EXTERN int +lws_hdr_copy(struct lws *wsi, char *dest, int len, enum lws_token_indexes h); + +/** + * lws_hdr_copy_fragment() - copy a single fragment of the given header to a buffer + * The buffer length len must include space for an additional + * terminating '\0', or it will fail returning -1. + * If the requested fragment index is not present, it fails + * returning -1. + * + * \param wsi: websocket connection + * \param dest: destination buffer + * \param len: length of destination buffer + * \param h: which header index we are interested in + * \param frag_idx: which fragment of h we want to copy + * + * Normally this is only useful + * to parse URI arguments like ?x=1&y=2, token index WSI_TOKEN_HTTP_URI_ARGS + * fragment 0 will contain "x=1" and fragment 1 "y=2" + */ +LWS_VISIBLE LWS_EXTERN int +lws_hdr_copy_fragment(struct lws *wsi, char *dest, int len, + enum lws_token_indexes h, int frag_idx); + +/** + * lws_get_urlarg_by_name() - return pointer to arg value if present + * \param wsi: the connection to check + * \param name: the arg name, like "token=" + * \param buf: the buffer to receive the urlarg (including the name= part) + * \param len: the length of the buffer to receive the urlarg + * + * Returns NULL if not found or a pointer inside buf to just after the + * name= part. + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len); +///@} + +/*! \defgroup HTTP-headers-create HTTP headers: create + * + * ## HTTP headers: Create + * + * These apis allow you to create HTTP response headers in a way compatible with + * both HTTP/1.x and HTTP/2. + * + * They each append to a buffer taking care about the buffer end, which is + * passed in as a pointer. When data is written to the buffer, the current + * position p is updated accordingly. + * + * All of these apis are LWS_WARN_UNUSED_RESULT as they can run out of space + * and fail with nonzero return. + */ +///@{ + +#define LWSAHH_CODE_MASK ((1 << 16) - 1) +#define LWSAHH_FLAG_NO_SERVER_NAME (1 << 30) + +/** + * lws_add_http_header_status() - add the HTTP response status code + * + * \param wsi: the connection to check + * \param code: an HTTP code like 200, 404 etc (see enum http_status) + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Adds the initial response code, so should be called first. + * + * Code may additionally take OR'd flags: + * + * LWSAHH_FLAG_NO_SERVER_NAME: don't apply server name header this time + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_add_http_header_status(struct lws *wsi, + unsigned int code, unsigned char **p, + unsigned char *end); +/** + * lws_add_http_header_by_name() - append named header and value + * + * \param wsi: the connection to check + * \param name: the hdr name, like "my-header" + * \param value: the value after the = for this header + * \param length: the length of the value + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Appends name: value to the headers + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +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); +/** + * lws_add_http_header_by_token() - append given header and value + * + * \param wsi: the connection to check + * \param token: the token index for the hdr + * \param value: the value after the = for this header + * \param length: the length of the value + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Appends name=value to the headers, but is able to take advantage of better + * HTTP/2 coding mechanisms where possible. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +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); +/** + * lws_add_http_header_content_length() - append content-length helper + * + * \param wsi: the connection to check + * \param content_length: the content length to use + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Appends content-length: content_length to the headers + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_add_http_header_content_length(struct lws *wsi, + lws_filepos_t content_length, + unsigned char **p, unsigned char *end); +/** + * lws_finalize_http_header() - terminate header block + * + * \param wsi: the connection to check + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Indicates no more headers will be added + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_finalize_http_header(struct lws *wsi, unsigned char **p, + unsigned char *end); +///@} + +/** \defgroup form-parsing Form Parsing + * \ingroup http + * ##POSTed form parsing functions + * + * These lws_spa (stateful post arguments) apis let you parse and urldecode + * POSTed form arguments, both using simple urlencoded and multipart transfer + * encoding. + * + * It's capable of handling file uploads as well a named input parsing, + * and the apis are the same for both form upload styles. + * + * You feed it a list of parameter names and it creates pointers to the + * urldecoded arguments: file upload parameters pass the file data in chunks to + * a user-supplied callback as they come. + * + * Since it's stateful, it handles the incoming data needing more than one + * POST_BODY callback and has no limit on uploaded file size. + */ +///@{ + +/** enum lws_spa_fileupload_states */ +enum lws_spa_fileupload_states { + LWS_UFS_CONTENT, + /**< a chunk of file content has arrived */ + LWS_UFS_FINAL_CONTENT, + /**< the last chunk (possibly zero length) of file content has arrived */ + LWS_UFS_OPEN + /**< a new file is starting to arrive */ +}; + +/** + * lws_spa_fileupload_cb() - callback to receive file upload data + * + * \param data: opt_data pointer set in lws_spa_create + * \param name: name of the form field being uploaded + * \param filename: original filename from client + * \param buf: start of data to receive + * \param len: length of data to receive + * \param state: information about how this call relates to file + * + * Notice name and filename shouldn't be trusted, as they are passed from + * HTTP provided by the client. + */ +typedef int (*lws_spa_fileupload_cb)(void *data, const char *name, + const char *filename, char *buf, int len, + enum lws_spa_fileupload_states state); + +/** struct lws_spa - opaque urldecode parser capable of handling multipart + * and file uploads */ +struct lws_spa; + +/** + * lws_spa_create() - create urldecode parser + * + * \param wsi: lws connection (used to find Content Type) + * \param param_names: array of form parameter names, like "username" + * \param count_params: count of param_names + * \param max_storage: total amount of form parameter values we can store + * \param opt_cb: NULL, or callback to receive file upload data. + * \param opt_data: NULL, or user pointer provided to opt_cb. + * + * Creates a urldecode parser and initializes it. + * + * opt_cb can be NULL if you just want normal name=value parsing, however + * if one or more entries in your form are bulk data (file transfer), you + * can provide this callback and filter on the name callback parameter to + * treat that urldecoded data separately. The callback should return -1 + * in case of fatal error, and 0 if OK. + */ +LWS_VISIBLE LWS_EXTERN 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_process() - parses a chunk of input data + * + * \param spa: the parser object previously created + * \param in: incoming, urlencoded data + * \param len: count of bytes valid at \param in + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_process(struct lws_spa *spa, const char *in, int len); + +/** + * lws_spa_finalize() - indicate incoming data completed + * + * \param spa: the parser object previously created + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_finalize(struct lws_spa *spa); + +/** + * lws_spa_get_length() - return length of parameter value + * + * \param spa: the parser object previously created + * \param n: parameter ordinal to return length of value for + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_get_length(struct lws_spa *spa, int n); + +/** + * lws_spa_get_string() - return pointer to parameter value + * \param spa: the parser object previously created + * \param n: parameter ordinal to return pointer to value for + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_spa_get_string(struct lws_spa *spa, int n); + +/** + * lws_spa_destroy() - destroy parser object + * + * \param spa: the parser object previously created + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_destroy(struct lws_spa *spa); +///@} + +/*! \defgroup urlendec Urlencode and Urldecode + * \ingroup http + * + * ##HTML chunked Substitution + * + * APIs for receiving chunks of text, replacing a set of variable names via + * a callback, and then prepending and appending HTML chunked encoding + * headers. + */ +//@{ + +/** + * lws_urlencode() - like strncpy but with urlencoding + * + * \param escaped: output buffer + * \param string: input buffer ('/0' terminated) + * \param len: output buffer max length + * + * Because urlencoding expands the output string, it's not + * possible to do it in-place, ie, with escaped == string + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_urlencode(char *escaped, const char *string, int len); + +/* + * URLDECODE 1 / 2 + * + * This simple urldecode only operates until the first '\0' and requires the + * data to exist all at once + */ +/** + * lws_urldecode() - like strncpy but with urldecoding + * + * \param string: output buffer + * \param escaped: input buffer ('\0' terminated) + * \param len: output buffer max length + * + * This is only useful for '\0' terminated strings + * + * Since urldecoding only shrinks the output string, it is possible to + * do it in-place, ie, string == escaped + * + * Returns 0 if completed OK or nonzero for urldecode violation (non-hex chars + * where hex required, etc) + */ +LWS_VISIBLE LWS_EXTERN int +lws_urldecode(char *string, const char *escaped, int len); +///@} +/** + * lws_return_http_status() - Return simple http status + * \param wsi: Websocket instance (available from user callback) + * \param code: Status index, eg, 404 + * \param html_body: User-readable HTML description < 1KB, or NULL + * + * Helper to report HTTP errors back to the client cleanly and + * consistently + */ +LWS_VISIBLE LWS_EXTERN int +lws_return_http_status(struct lws *wsi, unsigned int code, + const char *html_body); + +/** + * lws_http_redirect() - write http redirect into buffer + * + * \param wsi: websocket connection + * \param code: HTTP response code (eg, 301) + * \param loc: where to redirect to + * \param len: length of loc + * \param p: pointer current position in buffer (updated as we write) + * \param end: pointer to end of buffer + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len, + unsigned char **p, unsigned char *end); + +/** + * lws_http_transaction_completed() - wait for new http transaction or close + * \param wsi: websocket connection + * + * Returns 1 if the HTTP connection must close now + * Returns 0 and resets connection to wait for new HTTP header / + * transaction if possible + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed(struct lws *wsi); +///@} + +/*! \defgroup pur Sanitize / purify SQL and JSON helpers + * + * ##Sanitize / purify SQL and JSON helpers + * + * APIs for escaping untrusted JSON and SQL safely before use + */ +//@{ + +/** + * lws_sql_purify() - like strncpy but with escaping for sql quotes + * + * \param escaped: output buffer + * \param string: input buffer ('/0' terminated) + * \param len: output buffer max length + * + * Because escaping expands the output string, it's not + * possible to do it in-place, ie, with escaped == string + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_sql_purify(char *escaped, const char *string, int len); + +/** + * lws_json_purify() - like strncpy but with escaping for json chars + * + * \param escaped: output buffer + * \param string: input buffer ('/0' terminated) + * \param len: output buffer max length + * + * Because escaping expands the output string, it's not + * possible to do it in-place, ie, with escaped == string + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_json_purify(char *escaped, const char *string, int len); +///@} + +/*! \defgroup ev libev helpers + * + * ##libev helpers + * + * APIs specific to libev event loop itegration + */ +///@{ + +#ifdef LWS_WITH_LIBEV +typedef void (lws_ev_signal_cb_t)(EV_P_ struct ev_signal *w, int revents); + +LWS_VISIBLE LWS_EXTERN int +lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint, + lws_ev_signal_cb_t *cb); + +LWS_VISIBLE LWS_EXTERN int +lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents); +#endif /* LWS_WITH_LIBEV */ + +///@} + +/*! \defgroup uv libuv helpers + * + * ##libuv helpers + * + * APIs specific to libuv event loop itegration + */ +///@{ +#ifdef LWS_WITH_LIBUV +LWS_VISIBLE LWS_EXTERN int +lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint, + uv_signal_cb cb); + +LWS_VISIBLE LWS_EXTERN void +lws_libuv_run(const struct lws_context *context, int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_libuv_stop(struct lws_context *context); + +LWS_VISIBLE LWS_EXTERN void +lws_libuv_stop_without_kill(const struct lws_context *context, int tsi); + +LWS_VISIBLE LWS_EXTERN int +lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi); + +LWS_VISIBLE LWS_EXTERN uv_loop_t * +lws_uv_getloop(struct lws_context *context, int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_uv_sigint_cb(uv_signal_t *watcher, int signum); + +LWS_VISIBLE LWS_EXTERN void +lws_close_all_handles_in_loop(uv_loop_t *loop); +#endif /* LWS_WITH_LIBUV */ +///@} + +/*! \defgroup event libevent helpers + * + * ##libevent helpers + * + * APIs specific to libevent event loop itegration + */ +///@{ + +#ifdef LWS_WITH_LIBEVENT +typedef void (lws_event_signal_cb_t) (evutil_socket_t sock_fd, short revents, + void *ctx); + +LWS_VISIBLE LWS_EXTERN int +lws_event_sigint_cfg(struct lws_context *context, int use_event_sigint, + lws_event_signal_cb_t cb); + +LWS_VISIBLE LWS_EXTERN int +lws_event_initloop(struct lws_context *context, struct event_base *loop, + int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, + void *ctx); +#endif /* LWS_WITH_LIBEVENT */ + +///@} + +/*! \defgroup timeout Connection timeouts + + APIs related to setting connection timeouts +*/ +//@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum pending_timeout { + NO_PENDING_TIMEOUT = 0, + PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE = 1, + PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE = 2, + PENDING_TIMEOUT_ESTABLISH_WITH_SERVER = 3, + PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE = 4, + PENDING_TIMEOUT_AWAITING_PING = 5, + PENDING_TIMEOUT_CLOSE_ACK = 6, + PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE = 7, + PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE = 8, + PENDING_TIMEOUT_SSL_ACCEPT = 9, + PENDING_TIMEOUT_HTTP_CONTENT = 10, + PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND = 11, + PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE = 12, + PENDING_TIMEOUT_SHUTDOWN_FLUSH = 13, + PENDING_TIMEOUT_CGI = 14, + PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE = 15, + PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING = 16, + PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG = 17, + PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD = 18, + PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY = 19, + PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY = 20, + PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY = 21, + PENDING_TIMEOUT_KILLED_BY_SSL_INFO = 22, + PENDING_TIMEOUT_KILLED_BY_PARENT = 23, + PENDING_TIMEOUT_CLOSE_SEND = 24, + PENDING_TIMEOUT_HOLDING_AH = 25, + + /****** add new things just above ---^ ******/ + + PENDING_TIMEOUT_USER_REASON_BASE = 1000 +}; + +#define LWS_TO_KILL_ASYNC -1 +/**< If LWS_TO_KILL_ASYNC is given as the timeout sec in a lws_set_timeout() + * call, then the connection is marked to be killed at the next timeout + * check. This is how you should force-close the wsi being serviced if + * you are doing it outside the callback (where you should close by nonzero + * return). + */ +#define LWS_TO_KILL_SYNC -2 +/**< If LWS_TO_KILL_SYNC is given as the timeout sec in a lws_set_timeout() + * call, then the connection is closed before returning (which may delete + * the wsi). This should only be used where the wsi being closed is not the + * wsi currently being serviced. + */ +/** + * lws_set_timeout() - marks the wsi as subject to a timeout + * + * You will not need this unless you are doing something special + * + * \param wsi: Websocket connection instance + * \param reason: timeout reason + * \param secs: how many seconds. You may set to LWS_TO_KILL_ASYNC to + * force the connection to timeout at the next opportunity, or + * LWS_TO_KILL_SYNC to close it synchronously if you know the + * wsi is not the one currently being serviced. + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs); +///@} + +/*! \defgroup sending-data Sending data + + APIs related to writing data on a connection +*/ +//@{ +#if !defined(LWS_SIZEOFPTR) +#define LWS_SIZEOFPTR (sizeof (void *)) +#endif + +#if defined(__x86_64__) +#define _LWS_PAD_SIZE 16 /* Intel recommended for best performance */ +#else +#define _LWS_PAD_SIZE LWS_SIZEOFPTR /* Size of a pointer on the target arch */ +#endif +#define _LWS_PAD(n) (((n) % _LWS_PAD_SIZE) ? \ + ((n) + (_LWS_PAD_SIZE - ((n) % _LWS_PAD_SIZE))) : (n)) +/* last 2 is for lws-meta */ +#define LWS_PRE _LWS_PAD(4 + 10 + 2) +/* used prior to 1.7 and retained for backward compatibility */ +#define LWS_SEND_BUFFER_PRE_PADDING LWS_PRE +#define LWS_SEND_BUFFER_POST_PADDING 0 + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum lws_write_protocol { + LWS_WRITE_TEXT = 0, + /**< Send a ws TEXT message,the pointer must have LWS_PRE valid + * memory behind it. The receiver expects only valid utf-8 in the + * payload */ + LWS_WRITE_BINARY = 1, + /**< Send a ws BINARY message, the pointer must have LWS_PRE valid + * memory behind it. Any sequence of bytes is valid */ + LWS_WRITE_CONTINUATION = 2, + /**< Continue a previous ws message, the pointer must have LWS_PRE valid + * memory behind it */ + LWS_WRITE_HTTP = 3, + /**< Send HTTP content */ + + /* LWS_WRITE_CLOSE is handled by lws_close_reason() */ + LWS_WRITE_PING = 5, + LWS_WRITE_PONG = 6, + + /* Same as write_http but we know this write ends the transaction */ + LWS_WRITE_HTTP_FINAL = 7, + + /* HTTP2 */ + + LWS_WRITE_HTTP_HEADERS = 8, + /**< Send http headers (http2 encodes this payload and LWS_WRITE_HTTP + * payload differently, http 1.x links also handle this correctly. so + * to be compatible with both in the future,header response part should + * be sent using this regardless of http version expected) + */ + LWS_WRITE_HTTP_HEADERS_CONTINUATION = 9, + /**< Continuation of http/2 headers + */ + + /****** add new things just above ---^ ******/ + + /* flags */ + + LWS_WRITE_NO_FIN = 0x40, + /**< This part of the message is not the end of the message */ + + LWS_WRITE_H2_STREAM_END = 0x80, + /**< Flag indicates this packet should go out with STREAM_END if h2 + * STREAM_END is allowed on DATA or HEADERS. + */ + + LWS_WRITE_CLIENT_IGNORE_XOR_MASK = 0x80 + /**< client packet payload goes out on wire unmunged + * only useful for security tests since normal servers cannot + * decode the content if used */ +}; + +/* used with LWS_CALLBACK_CHILD_WRITE_VIA_PARENT */ + +struct lws_write_passthru { + struct lws *wsi; + unsigned char *buf; + size_t len; + enum lws_write_protocol wp; +}; + + +/** + * lws_write() - Apply protocol then write data to client + * \param wsi: Websocket instance (available from user callback) + * \param buf: The data to send. For data being sent on a websocket + * connection (ie, not default http), this buffer MUST have + * LWS_PRE bytes valid BEFORE the pointer. + * This is so the protocol header data can be added in-situ. + * \param len: Count of the data bytes in the payload starting from buf + * \param protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one + * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate + * data on a websockets connection. Remember to allow the extra + * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT + * are used. + * + * This function provides the way to issue data back to the client + * for both http and websocket protocols. + * + * IMPORTANT NOTICE! + * + * When sending with websocket protocol + * + * LWS_WRITE_TEXT, + * LWS_WRITE_BINARY, + * LWS_WRITE_CONTINUATION, + * LWS_WRITE_PING, + * LWS_WRITE_PONG + * + * the send buffer has to have LWS_PRE bytes valid BEFORE + * the buffer pointer you pass to lws_write(). + * + * This allows us to add protocol info before and after the data, and send as + * one packet on the network without payload copying, for maximum efficiency. + * + * So for example you need this kind of code to use lws_write with a + * 128-byte payload + * + * char buf[LWS_PRE + 128]; + * + * // fill your part of the buffer... for example here it's all zeros + * memset(&buf[LWS_PRE], 0, 128); + * + * lws_write(wsi, &buf[LWS_PRE], 128, LWS_WRITE_TEXT); + * + * When sending HTTP, with + * + * LWS_WRITE_HTTP, + * LWS_WRITE_HTTP_HEADERS + * LWS_WRITE_HTTP_FINAL + * + * there is no protocol data prepended, and don't need to take care about the + * LWS_PRE bytes valid before the buffer pointer. + * + * LWS_PRE is at least the frame nonce + 2 header + 8 length + * LWS_SEND_BUFFER_POST_PADDING is deprecated, it's now 0 and can be left off. + * The example apps no longer use it. + * + * Pad LWS_PRE to the CPU word size, so that word references + * to the address immediately after the padding won't cause an unaligned access + * error. Sometimes for performance reasons the recommended padding is even + * larger than sizeof(void *). + * + * In the case of sending using websocket protocol, be sure to allocate + * valid storage before and after buf as explained above. This scheme + * allows maximum efficiency of sending data and protocol in a single + * packet while not burdening the user code with any protocol knowledge. + * + * Return may be -1 for a fatal error needing connection close, or the + * number of bytes sent. + * + * Truncated Writes + * ================ + * + * The OS may not accept everything you asked to write on the connection. + * + * Posix defines POLLOUT indication from poll() to show that the connection + * will accept more write data, but it doesn't specifiy how much. It may just + * accept one byte of whatever you wanted to send. + * + * LWS will buffer the remainder automatically, and send it out autonomously. + * + * During that time, WRITABLE callbacks will be suppressed. + * + * This is to handle corner cases where unexpectedly the OS refuses what we + * usually expect it to accept. You should try to send in chunks that are + * almost always accepted in order to avoid the inefficiency of the buffering. + */ +LWS_VISIBLE LWS_EXTERN int +lws_write(struct lws *wsi, unsigned char *buf, size_t len, + enum lws_write_protocol protocol); + +/* helper for case where buffer may be const */ +#define lws_write_http(wsi, buf, len) \ + lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP) +///@} + +/** \defgroup callback-when-writeable Callback when writeable + * + * ##Callback When Writeable + * + * lws can only write data on a connection when it is able to accept more + * data without blocking. + * + * So a basic requirement is we should only use the lws_write() apis when the + * connection we want to write on says that he can accept more data. + * + * When lws cannot complete your send at the time, it will buffer the data + * and send it in the background, suppressing any further WRITEABLE callbacks + * on that connection until it completes. So it is important to write new + * things in a new writeable callback. + * + * These apis reflect the various ways we can indicate we would like to be + * called back when one or more connections is writeable. + */ +///@{ + +/** + * lws_callback_on_writable() - Request a callback when this socket + * becomes able to be written to without + * blocking + * + * \param wsi: Websocket connection instance to get callback for + * + * - Which: only this wsi + * - When: when the individual connection becomes writeable + * - What: LWS_CALLBACK_*_WRITEABLE + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_on_writable(struct lws *wsi); + +/** + * lws_callback_on_writable_all_protocol() - Request a callback for all + * connections using the given protocol when it + * becomes possible to write to each socket without + * blocking in turn. + * + * \param context: lws_context + * \param protocol: Protocol whose connections will get callbacks + * + * - Which: connections using this protocol on ANY VHOST + * - When: when the individual connection becomes writeable + * - What: LWS_CALLBACK_*_WRITEABLE + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_on_writable_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol); + +/** + * lws_callback_on_writable_all_protocol_vhost() - Request a callback for + * all connections on same vhost using the given protocol + * when it becomes possible to write to each socket without + * blocking in turn. + * + * \param vhost: Only consider connections on this lws_vhost + * \param protocol: Protocol whose connections will get callbacks + * + * - Which: connections using this protocol on GIVEN VHOST ONLY + * - When: when the individual connection becomes writeable + * - What: LWS_CALLBACK_*_WRITEABLE + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost, + const struct lws_protocols *protocol); + +/** + * lws_callback_all_protocol() - Callback all connections using + * the given protocol with the given reason + * + * \param context: lws_context + * \param protocol: Protocol whose connections will get callbacks + * \param reason: Callback reason index + * + * - Which: connections using this protocol on ALL VHOSTS + * - When: before returning + * - What: reason + * + * This isn't normally what you want... normally any update of connection- + * specific information can wait until a network-related callback like rx, + * writable, or close. + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_all_protocol(struct lws_context *context, + const struct lws_protocols *protocol, int reason); + +/** + * lws_callback_all_protocol_vhost() - Callback all connections using + * the given protocol with the given reason. This is + * deprecated since v2.4: use lws_callback_all_protocol_vhost_args + * + * \param vh: Vhost whose connections will get callbacks + * \param protocol: Which protocol to match. NULL means all. + * \param reason: Callback reason index + * + * - Which: connections using this protocol on GIVEN VHOST ONLY + * - When: now + * - What: reason + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_all_protocol_vhost(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason) +LWS_WARN_DEPRECATED; + +/** + * lws_callback_all_protocol_vhost_args() - Callback all connections using + * the given protocol with the given reason and args + * + * \param vh: Vhost whose connections will get callbacks + * \param protocol: Which protocol to match. NULL means all. + * \param reason: Callback reason index + * \param argp: Callback "in" parameter + * \param len: Callback "len" parameter + * + * - Which: connections using this protocol on GIVEN VHOST ONLY + * - When: now + * - What: reason + */ +LWS_VISIBLE int +lws_callback_all_protocol_vhost_args(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason, + void *argp, size_t len); + +/** + * lws_callback_vhost_protocols() - Callback all protocols enabled on a vhost + * with the given reason + * + * \param wsi: wsi whose vhost will get callbacks + * \param reason: Callback reason index + * \param in: in argument to callback + * \param len: len argument to callback + * + * - Which: connections using this protocol on same VHOST as wsi ONLY + * - When: now + * - What: reason + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len); + +LWS_VISIBLE LWS_EXTERN int +lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len); + +/** + * lws_get_socket_fd() - returns the socket file descriptor + * + * You will not need this unless you are doing something special + * + * \param wsi: Websocket connection instance + */ +LWS_VISIBLE LWS_EXTERN int +lws_get_socket_fd(struct lws *wsi); + +/** + * lws_get_peer_write_allowance() - get the amount of data writeable to peer + * if known + * + * \param wsi: Websocket connection instance + * + * if the protocol does not have any guidance, returns -1. Currently only + * http2 connections get send window information from this API. But your code + * should use it so it can work properly with any protocol. + * + * If nonzero return is the amount of payload data the peer or intermediary has + * reported it has buffer space for. That has NO relationship with the amount + * of buffer space your OS can accept on this connection for a write action. + * + * This number represents the maximum you could send to the peer or intermediary + * on this connection right now without the protocol complaining. + * + * lws manages accounting for send window updates and payload writes + * automatically, so this number reflects the situation at the peer or + * intermediary dynamically. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_get_peer_write_allowance(struct lws *wsi); +///@} + +enum { + /* + * Flags for enable and disable rxflow with reason bitmap and with + * backwards-compatible single bool + */ + LWS_RXFLOW_REASON_USER_BOOL = (1 << 0), + LWS_RXFLOW_REASON_HTTP_RXBUFFER = (1 << 6), + LWS_RXFLOW_REASON_H2_PPS_PENDING = (1 << 7), + + LWS_RXFLOW_REASON_APPLIES = (1 << 14), + LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT = (1 << 13), + LWS_RXFLOW_REASON_APPLIES_ENABLE = LWS_RXFLOW_REASON_APPLIES | + LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT, + LWS_RXFLOW_REASON_APPLIES_DISABLE = LWS_RXFLOW_REASON_APPLIES, + LWS_RXFLOW_REASON_FLAG_PROCESS_NOW = (1 << 12), + +}; + +/** + * lws_rx_flow_control() - Enable and disable socket servicing for + * received packets. + * + * If the output side of a server process becomes choked, this allows flow + * control for the input side. + * + * \param wsi: Websocket connection instance to get callback for + * \param enable: 0 = disable read servicing for this connection, 1 = enable + * + * If you need more than one additive reason for rxflow control, you can give + * iLWS_RXFLOW_REASON_APPLIES_ENABLE or _DISABLE together with one or more of + * b5..b0 set to idicate which bits to enable or disable. If any bits are + * enabled, rx on the connection is suppressed. + * + * LWS_RXFLOW_REASON_FLAG_PROCESS_NOW flag may also be given to force any change + * in rxflowbstatus to benapplied immediately, this should be used when you are + * changing a wsi flow control state from outside a callback on that wsi. + */ +LWS_VISIBLE LWS_EXTERN int +lws_rx_flow_control(struct lws *wsi, int enable); + +/** + * lws_rx_flow_allow_all_protocol() - Allow all connections with this protocol to receive + * + * When the user server code realizes it can accept more input, it can + * call this to have the RX flow restriction removed from all connections using + * the given protocol. + * \param context: lws_context + * \param protocol: all connections using this protocol will be allowed to receive + */ +LWS_VISIBLE LWS_EXTERN void +lws_rx_flow_allow_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol); + +/** + * lws_remaining_packet_payload() - Bytes to come before "overall" + * rx packet is complete + * \param wsi: Websocket instance (available from user callback) + * + * This function is intended to be called from the callback if the + * user code is interested in "complete packets" from the client. + * libwebsockets just passes through payload as it comes and issues a buffer + * additionally when it hits a built-in limit. The LWS_CALLBACK_RECEIVE + * callback handler can use this API to find out if the buffer it has just + * been given is the last piece of a "complete packet" from the client -- + * when that is the case lws_remaining_packet_payload() will return + * 0. + * + * Many protocols won't care becuse their packets are always small. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_remaining_packet_payload(struct lws *wsi); + + +/** \defgroup sock-adopt Socket adoption helpers + * ##Socket adoption helpers + * + * When integrating with an external app with its own event loop, these can + * be used to accept connections from someone else's listening socket. + * + * When using lws own event loop, these are not needed. + */ +///@{ + +/** + * lws_adopt_socket() - adopt foreign socket as if listen socket accepted it + * for the default vhost of context. + * + * \param context: lws context + * \param accept_fd: fd of already-accepted socket to adopt + * + * Either returns new wsi bound to accept_fd, or closes accept_fd and + * returns NULL, having cleaned up any new wsi pieces. + * + * LWS adopts the socket in http serving mode, it's ready to accept an upgrade + * to ws or just serve http. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd); +/** + * lws_adopt_socket_vhost() - adopt foreign socket as if listen socket accepted it + * for vhost + * + * \param vh: lws vhost + * \param accept_fd: fd of already-accepted socket to adopt + * + * Either returns new wsi bound to accept_fd, or closes accept_fd and + * returns NULL, having cleaned up any new wsi pieces. + * + * LWS adopts the socket in http serving mode, it's ready to accept an upgrade + * to ws or just serve http. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); + +typedef enum { + LWS_ADOPT_RAW_FILE_DESC = 0, /* convenience constant */ + LWS_ADOPT_HTTP = 1, /* flag: absent implies RAW */ + LWS_ADOPT_SOCKET = 2, /* flag: absent implies file descr */ + LWS_ADOPT_ALLOW_SSL = 4, /* flag: if set requires LWS_ADOPT_SOCKET */ + LWS_ADOPT_WS_PARENTIO = 8, /* flag: ws mode parent handles IO + * if given must be only flag + * wsi put directly into ws mode + */ +} lws_adoption_type; + +typedef union { + lws_sockfd_type sockfd; + lws_filefd_type filefd; +} lws_sock_file_fd_type; + +/* +* lws_adopt_descriptor_vhost() - adopt foreign socket or file descriptor +* if socket descriptor, should already have been accepted from listen socket +* +* \param vhost: lws vhost +* \param type: OR-ed combinations of lws_adoption_type flags +* \param fd: union with either .sockfd or .filefd set +* \param vh_prot_name: NULL or vh protocol name to bind raw connection to +* \param parent: NULL or struct lws to attach new_wsi to as a child +* +* Either returns new wsi bound to accept_fd, or closes accept_fd and +* returns NULL, having cleaned up any new wsi pieces. +* +* If LWS_ADOPT_SOCKET is set, LWS adopts the socket in http serving mode, it's +* ready to accept an upgrade to ws or just serve http. +* +* parent may be NULL, if given it should be an existing wsi that will become the +* parent of the new wsi created by this call. +*/ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, + lws_sock_file_fd_type fd, const char *vh_prot_name, + struct lws *parent); + +/** + * lws_adopt_socket_readbuf() - adopt foreign socket and first rx as if listen socket accepted it + * for the default vhost of context. + * \param context: lws context + * \param accept_fd: fd of already-accepted socket to adopt + * \param readbuf: NULL or pointer to data that must be drained before reading from + * accept_fd + * \param len: The length of the data held at \param readbuf + * + * Either returns new wsi bound to accept_fd, or closes accept_fd and + * returns NULL, having cleaned up any new wsi pieces. + * + * LWS adopts the socket in http serving mode, it's ready to accept an upgrade + * to ws or just serve http. + * + * If your external code did not already read from the socket, you can use + * lws_adopt_socket() instead. + * + * This api is guaranteed to use the data at \param readbuf first, before reading from + * the socket. + * + * readbuf is limited to the size of the ah rx buf, currently 2048 bytes. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, + const char *readbuf, size_t len); +/** + * lws_adopt_socket_vhost_readbuf() - adopt foreign socket and first rx as if listen socket + * accepted it for vhost. + * \param vhost: lws vhost + * \param accept_fd: fd of already-accepted socket to adopt + * \param readbuf: NULL or pointer to data that must be drained before reading from + * accept_fd + * \param len: The length of the data held at \param readbuf + * + * Either returns new wsi bound to accept_fd, or closes accept_fd and + * returns NULL, having cleaned up any new wsi pieces. + * + * LWS adopts the socket in http serving mode, it's ready to accept an upgrade + * to ws or just serve http. + * + * If your external code did not already read from the socket, you can use + * lws_adopt_socket() instead. + * + * This api is guaranteed to use the data at \param readbuf first, before reading from + * the socket. + * + * readbuf is limited to the size of the ah rx buf, currently 2048 bytes. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, lws_sockfd_type accept_fd, + const char *readbuf, size_t len); +///@} + +/** \defgroup net Network related helper APIs + * ##Network related helper APIs + * + * These wrap miscellaneous useful network-related functions + */ +///@{ + +/** + * lws_canonical_hostname() - returns this host's hostname + * + * This is typically used by client code to fill in the host parameter + * when making a client connection. You can only call it after the context + * has been created. + * + * \param context: Websocket context + */ +LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT +lws_canonical_hostname(struct lws_context *context); + +/** + * lws_get_peer_addresses() - Get client address information + * \param wsi: Local struct lws associated with + * \param fd: Connection socket descriptor + * \param name: Buffer to take client address name + * \param name_len: Length of client address name buffer + * \param rip: Buffer to take client address IP dotted quad + * \param rip_len: Length of client address IP buffer + * + * This function fills in name and rip with the name and IP of + * the client connected with socket descriptor fd. Names may be + * truncated if there is not enough room. If either cannot be + * determined, they will be returned as valid zero-length strings. + */ +LWS_VISIBLE LWS_EXTERN void +lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, + int name_len, char *rip, int rip_len); + +/** + * lws_get_peer_simple() - Get client address information without RDNS + * + * \param wsi: Local struct lws associated with + * \param name: Buffer to take client address name + * \param namelen: Length of client address name buffer + * + * This provides a 123.123.123.123 type IP address in name from the + * peer that has connected to wsi + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_get_peer_simple(struct lws *wsi, char *name, int namelen); +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) +/** + * lws_interface_to_sa() - Convert interface name or IP to sockaddr struct + * + * \param ipv6: Allow IPV6 addresses + * \param ifname: Interface name or IP + * \param addr: struct sockaddr_in * to be written + * \param addrlen: Length of addr + * + * This converts a textual network interface name to a sockaddr usable by + * other network functions + */ +LWS_VISIBLE LWS_EXTERN int +lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, + size_t addrlen); +///@} +#endif + +/** \defgroup misc Miscellaneous APIs +* ##Miscellaneous APIs +* +* Various APIs outside of other categories +*/ +///@{ + +/** + * lws_start_foreach_ll(): linkedlist iterator helper start + * + * \param type: type of iteration, eg, struct xyz * + * \param it: iterator var name to create + * \param start: start of list + * + * This helper creates an iterator and starts a while (it) { + * loop. The iterator runs through the linked list starting at start and + * ends when it gets a NULL. + * The while loop should be terminated using lws_start_foreach_ll(). + */ +#define lws_start_foreach_ll(type, it, start)\ +{ \ + type it = start; \ + while (it) { + +/** + * lws_end_foreach_ll(): linkedlist iterator helper end + * + * \param it: same iterator var name given when starting + * \param nxt: member name in the iterator pointing to next list element + * + * This helper is the partner for lws_start_foreach_ll() that ends the + * while loop. + */ + +#define lws_end_foreach_ll(it, nxt) \ + it = it->nxt; \ + } \ +} + +/** + * lws_start_foreach_llp(): linkedlist pointer iterator helper start + * + * \param type: type of iteration, eg, struct xyz ** + * \param it: iterator var name to create + * \param start: start of list + * + * This helper creates an iterator and starts a while (it) { + * loop. The iterator runs through the linked list starting at the + * address of start and ends when it gets a NULL. + * The while loop should be terminated using lws_start_foreach_llp(). + * + * This helper variant iterates using a pointer to the previous linked-list + * element. That allows you to easily delete list members by rewriting the + * previous pointer to the element's next pointer. + */ +#define lws_start_foreach_llp(type, it, start)\ +{ \ + type it = &(start); \ + while (*(it)) { + +/** + * lws_end_foreach_llp(): linkedlist pointer iterator helper end + * + * \param it: same iterator var name given when starting + * \param nxt: member name in the iterator pointing to next list element + * + * This helper is the partner for lws_start_foreach_llp() that ends the + * while loop. + */ + +#define lws_end_foreach_llp(it, nxt) \ + it = &(*(it))->nxt; \ + } \ +} + +/** + * lws_snprintf(): snprintf that truncates the returned length too + * + * \param str: destination buffer + * \param size: bytes left in destination buffer + * \param format: format string + * \param ...: args for format + * + * This lets you correctly truncate buffers by concatenating lengths, if you + * reach the limit the reported length doesn't exceed the limit. + */ +LWS_VISIBLE LWS_EXTERN int +lws_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3); + +/** + * lws_get_random(): fill a buffer with platform random data + * + * \param context: the lws context + * \param buf: buffer to fill + * \param len: how much to fill + * + * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if + * it's interested to see if the frame it's dealing with was sent in binary + * mode. + */ +LWS_VISIBLE LWS_EXTERN int +lws_get_random(struct lws_context *context, void *buf, int len); +/** + * lws_daemonize(): make current process run in the background + * + * \param _lock_path: the filepath to write the lock file + * + * Spawn lws as a background process, taking care of various things + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_daemonize(const char *_lock_path); +/** + * lws_get_library_version(): return string describing the version of lws + * + * On unix, also includes the git describe + */ +LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT +lws_get_library_version(void); + +/** + * lws_wsi_user() - get the user data associated with the connection + * \param wsi: lws connection + * + * Not normally needed since it's passed into the callback + */ +LWS_VISIBLE LWS_EXTERN void * +lws_wsi_user(struct lws *wsi); + +/** + * lws_wsi_set_user() - set the user data associated with the client connection + * \param wsi: lws connection + * \param user: user data + * + * By default lws allocates this and it's not legal to externally set it + * yourself. However client connections may have it set externally when the + * connection is created... if so, this api can be used to modify it at + * runtime additionally. + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_wsi_user(struct lws *wsi, void *user); + +/** + * lws_parse_uri: cut up prot:/ads:port/path into pieces + * Notice it does so by dropping '\0' into input string + * and the leading / on the path is consequently lost + * + * \param p: incoming uri string.. will get written to + * \param prot: result pointer for protocol part (https://) + * \param ads: result pointer for address part + * \param port: result pointer for port part + * \param path: result pointer for path part + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse_uri(char *p, const char **prot, const char **ads, int *port, + const char **path); + +/** + * lws_now_secs(): return seconds since 1970-1-1 + */ +LWS_VISIBLE LWS_EXTERN unsigned long +lws_now_secs(void); + +/** + * lws_get_context - Allow getting lws_context from a Websocket connection + * instance + * + * With this function, users can access context in the callback function. + * Otherwise users may have to declare context as a global variable. + * + * \param wsi: Websocket connection instance + */ +LWS_VISIBLE LWS_EXTERN struct lws_context * LWS_WARN_UNUSED_RESULT +lws_get_context(const struct lws *wsi); + +/** + * lws_get_count_threads(): how many service threads the context uses + * + * \param context: the lws context + * + * By default this is always 1, if you asked for more than lws can handle it + * will clip the number of threads. So you can use this to find out how many + * threads are actually in use. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_get_count_threads(struct lws_context *context); + +/** + * lws_get_parent() - get parent wsi or NULL + * \param wsi: lws connection + * + * Specialized wsi like cgi stdin/out/err are associated to a parent wsi, + * this allows you to get their parent. + */ +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_get_parent(const struct lws *wsi); + +/** + * lws_get_child() - get child wsi or NULL + * \param wsi: lws connection + * + * Allows you to find a related wsi from the parent wsi. + */ +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_get_child(const struct lws *wsi); + +/** + * lws_parent_carries_io() - mark wsi as needing to send messages via parent + * + * \param wsi: child lws connection + */ + +LWS_VISIBLE LWS_EXTERN void +lws_set_parent_carries_io(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN void * +lws_get_opaque_parent_data(const struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN void +lws_set_opaque_parent_data(struct lws *wsi, void *data); + +LWS_VISIBLE LWS_EXTERN int +lws_get_child_pending_on_writable(const struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN void +lws_clear_child_pending_on_writable(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN int +lws_get_close_length(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN unsigned char * +lws_get_close_payload(struct lws *wsi); + +/** + * lws_get_network_wsi() - Returns wsi that has the tcp connection for this wsi + * + * \param wsi: wsi you have + * + * Returns wsi that has the tcp connection (which may be the incoming wsi) + * + * HTTP/1 connections will always return the incoming wsi + * HTTP/2 connections may return a different wsi that has the tcp connection + */ +LWS_VISIBLE LWS_EXTERN +struct lws *lws_get_network_wsi(struct lws *wsi); + +/* + * \deprecated DEPRECATED Note: this is not normally needed as a user api. + * It's provided in case it is + * useful when integrating with other app poll loop service code. + */ +LWS_VISIBLE LWS_EXTERN int +lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len); + +/** + * lws_set_allocator() - custom allocator support + * + * \param realloc + * + * Allows you to replace the allocator (and deallocator) used by lws + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_allocator(void *(*realloc)(void *ptr, size_t size, const char *reason)); +///@} + +/** \defgroup wsstatus Websocket status APIs + * ##Websocket connection status APIs + * + * These provide information about ws connection or message status + */ +///@{ +/** + * lws_send_pipe_choked() - tests if socket is writable or not + * \param wsi: lws connection + * + * Allows you to check if you can write more on the socket + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_send_pipe_choked(struct lws *wsi); + +/** + * lws_is_final_fragment() - tests if last part of ws message + * + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN int +lws_is_final_fragment(struct lws *wsi); + +/** + * lws_is_first_fragment() - tests if first part of ws message + * + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN int +lws_is_first_fragment(struct lws *wsi); + +/** + * lws_get_reserved_bits() - access reserved bits of ws frame + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN unsigned char +lws_get_reserved_bits(struct lws *wsi); + +/** + * lws_partial_buffered() - find out if lws buffered the last write + * \param wsi: websocket connection to check + * + * Returns 1 if you cannot use lws_write because the last + * write on this connection is still buffered, and can't be cleared without + * returning to the service loop and waiting for the connection to be + * writeable again. + * + * If you will try to do >1 lws_write call inside a single + * WRITEABLE callback, you must check this after every write and bail if + * set, ask for a new writeable callback and continue writing from there. + * + * This is never set at the start of a writeable callback, but any write + * may set it. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_partial_buffered(struct lws *wsi); + +/** + * lws_frame_is_binary(): true if the current frame was sent in binary mode + * + * \param wsi: the connection we are inquiring about + * + * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if + * it's interested to see if the frame it's dealing with was sent in binary + * mode. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_frame_is_binary(struct lws *wsi); + +/** + * lws_is_ssl() - Find out if connection is using SSL + * \param wsi: websocket connection to check + * + * Returns 0 if the connection is not using SSL, 1 if using SSL and + * using verified cert, and 2 if using SSL but the cert was not + * checked (appears for client wsi told to skip check on connection) + */ +LWS_VISIBLE LWS_EXTERN int +lws_is_ssl(struct lws *wsi); +/** + * lws_is_cgi() - find out if this wsi is running a cgi process + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN int +lws_is_cgi(struct lws *wsi); + +#ifdef LWS_OPENSSL_SUPPORT +/** + * lws_get_ssl() - Return wsi's SSL context structure + * \param wsi: websocket connection + * + * Returns pointer to the SSL library's context structure + */ +LWS_VISIBLE LWS_EXTERN SSL* +lws_get_ssl(struct lws *wsi); +#endif +///@} + +/** \defgroup lws_ring LWS Ringbuffer APIs + * ##lws_ring: generic ringbuffer struct + * + * Provides an abstract ringbuffer api supporting one head and one or an + * unlimited number of tails. + * + * All of the members are opaque and manipulated by lws_ring_...() apis. + * + * The lws_ring and its buffer is allocated at runtime on the heap, using + * + * - lws_ring_create() + * - lws_ring_destroy() + * + * It may contain any type, the size of the "element" stored in the ring + * buffer and the number of elements is given at creation time. + * + * When you create the ringbuffer, you can optionally provide an element + * destroy callback that frees any allocations inside the element. This is then + * automatically called for elements with no tail behind them, ie, elements + * which don't have any pending consumer are auto-freed. + * + * Whole elements may be inserted into the ringbuffer and removed from it, using + * + * - lws_ring_insert() + * - lws_ring_consume() + * + * You can find out how many whole elements are free or waiting using + * + * - lws_ring_get_count_free_elements() + * - lws_ring_get_count_waiting_elements() + * + * In addition there are special purpose optional byte-centric apis + * + * - lws_ring_next_linear_insert_range() + * - lws_ring_bump_head() + * + * which let you, eg, read() directly into the ringbuffer without needing + * an intermediate bounce buffer. + * + * The accessors understand that the ring wraps, and optimizes insertion and + * consumption into one or two memcpy()s depending on if the head or tail + * wraps. + * + * lws_ring only supports a single head, but optionally multiple tails with + * an API to inform it when the "oldest" tail has moved on. You can give + * NULL where-ever an api asks for a tail pointer, and it will use an internal + * single tail pointer for convenience. + * + * The "oldest tail", which is the only tail if you give it NULL instead of + * some other tail, is used to track which elements in the ringbuffer are + * still unread by anyone. + * + * - lws_ring_update_oldest_tail() + */ +///@{ +struct lws_ring; + +/** + * lws_ring_create(): create a new ringbuffer + * + * \param element_len: the size in bytes of one element in the ringbuffer + * \param count: the number of elements the ringbuffer can contain + * \param destroy_element: NULL, or callback to be called for each element + * that is removed from the ringbuffer due to the + * oldest tail moving beyond it + * + * Creates the ringbuffer and allocates the storage. Returns the new + * lws_ring *, or NULL if the allocation failed. + * + * If non-NULL, destroy_element will get called back for every element that is + * retired from the ringbuffer after the oldest tail has gone past it, and for + * any element still left in the ringbuffer when it is destroyed. It replaces + * all other element destruction code in your user code. + */ +LWS_VISIBLE LWS_EXTERN struct lws_ring * +lws_ring_create(size_t element_len, size_t count, + void (*destroy_element)(void *element)); + +/** + * lws_ring_destroy(): destroy a previously created ringbuffer + * + * \param ring: the struct lws_ring to destroy + * + * Destroys the ringbuffer allocation and the struct lws_ring itself. + */ +LWS_VISIBLE LWS_EXTERN void +lws_ring_destroy(struct lws_ring *ring); + +/** + * lws_ring_get_count_free_elements(): return how many elements can fit + * in the free space + * + * \param ring: the struct lws_ring to report on + * + * Returns how much room is left in the ringbuffer for whole element insertion. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_ring_get_count_free_elements(struct lws_ring *ring); + +/** + * lws_ring_get_count_waiting_elements(): return how many elements can be consumed + * + * \param ring: the struct lws_ring to report on + * \param tail: a pointer to the tail struct to use, or NULL for single tail + * + * Returns how many elements are waiting to be consumed from the perspective + * of the tail pointer given. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail); + +/** + * lws_ring_insert(): attempt to insert up to max_count elements from src + * + * \param ring: the struct lws_ring to report on + * \param src: the array of elements to be inserted + * \param max_count: the number of available elements at src + * + * Attempts to insert as many of the elements at src as possible, up to the + * maximum max_count. Returns the number of elements actually inserted. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count); + +/** + * lws_ring_consume(): attempt to copy out and remove up to max_count elements + * to src + * + * \param ring: the struct lws_ring to report on + * \param tail: a pointer to the tail struct to use, or NULL for single tail + * \param dest: the array of elements to be inserted. or NULL for no copy + * \param max_count: the number of available elements at src + * + * Attempts to copy out as many waiting elements as possible into dest, from + * the perspective of the given tail, up to max_count. If dest is NULL, the + * copying out is not done but the elements are logically consumed as usual. + * NULL dest is useful in combination with lws_ring_get_element(), where you + * can use the element direct from the ringbuffer and then call this with NULL + * dest to logically consume it. + * + * Increments the tail position according to how many elements could be + * consumed. + * + * Returns the number of elements consumed. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest, + size_t max_count); + +/** + * lws_ring_get_element(): get a pointer to the next waiting element for tail + * + * \param ring: the struct lws_ring to report on + * \param tail: a pointer to the tail struct to use, or NULL for single tail + * + * Points to the next element that tail would consume, directly in the + * ringbuffer. This lets you write() or otherwise use the element without + * having to copy it out somewhere first. + * + * After calling this, you must call lws_ring_consume(ring, &tail, NULL, 1) + * which will logically consume the element you used up and increment your + * tail (tail may also be NULL there if you use a single tail). + * + * Returns NULL if no waiting element, or a const void * pointing to it. + */ +LWS_VISIBLE LWS_EXTERN const void * +lws_ring_get_element(struct lws_ring *ring, uint32_t *tail); + +/** + * lws_ring_update_oldest_tail(): free up elements older than tail for reuse + * + * \param ring: the struct lws_ring to report on + * \param tail: a pointer to the tail struct to use, or NULL for single tail + * + * If you are using multiple tails, you must use this API to inform the + * lws_ring when none of the tails still need elements in the fifo any more, + * by updating it when the "oldest" tail has moved on. + */ +LWS_VISIBLE LWS_EXTERN void +lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail); + +/** + * lws_ring_get_oldest_tail(): get current oldest available data index + * + * \param ring: the struct lws_ring to report on + * + * If you are initializing a new ringbuffer consumer, you can set its tail to + * this to start it from the oldest ringbuffer entry still available. + */ +LWS_VISIBLE LWS_EXTERN uint32_t +lws_ring_get_oldest_tail(struct lws_ring *ring); + +/** + * lws_ring_next_linear_insert_range(): used to write directly into the ring + * + * \param ring: the struct lws_ring to report on + * \param start: pointer to a void * set to the start of the next ringbuffer area + * \param bytes: pointer to a size_t set to the max length you may use from *start + * + * This provides a low-level, bytewise access directly into the ringbuffer + * allowing direct insertion of data without having to use a bounce buffer. + * + * The api reports the position and length of the next linear range that can + * be written in the ringbuffer, ie, up to the point it would wrap, and sets + * *start and *bytes accordingly. You can then, eg, directly read() into + * *start for up to *bytes, and use lws_ring_bump_head() to update the lws_ring + * with what you have done. + * + * Returns nonzero if no insertion is currently possible. + */ +LWS_VISIBLE LWS_EXTERN int +lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start, + size_t *bytes); + +/** + * lws_ring_bump_head(): used to write directly into the ring + * + * \param ring: the struct lws_ring to operate on + * \param bytes: the number of bytes you inserted at the current head + */ +LWS_VISIBLE LWS_EXTERN void +lws_ring_bump_head(struct lws_ring *ring, size_t bytes); +///@} + +/** \defgroup sha SHA and B64 helpers + * ##SHA and B64 helpers + * + * These provide SHA-1 and B64 helper apis + */ +///@{ +#ifdef LWS_SHA1_USE_OPENSSL_NAME +#define lws_SHA1 SHA1 +#else +/** + * lws_SHA1(): make a SHA-1 digest of a buffer + * + * \param d: incoming buffer + * \param n: length of incoming buffer + * \param md: buffer for message digest (must be >= 20 bytes) + * + * Reduces any size buffer into a 20-byte SHA-1 hash. + */ +LWS_VISIBLE LWS_EXTERN unsigned char * +lws_SHA1(const unsigned char *d, size_t n, unsigned char *md); +#endif +/** + * lws_b64_encode_string(): encode a string into base 64 + * + * \param in: incoming buffer + * \param in_len: length of incoming buffer + * \param out: result buffer + * \param out_size: length of result buffer + * + * Encodes a string using b64 + */ +LWS_VISIBLE LWS_EXTERN int +lws_b64_encode_string(const char *in, int in_len, char *out, int out_size); +/** + * lws_b64_decode_string(): decode a string from base 64 + * + * \param in: incoming buffer + * \param out: result buffer + * \param out_size: length of result buffer + * + * Decodes a string using b64 + */ +LWS_VISIBLE LWS_EXTERN int +lws_b64_decode_string(const char *in, char *out, int out_size); +///@} + + +/*! \defgroup cgi cgi handling + * + * ##CGI handling + * + * These functions allow low-level control over stdin/out/err of the cgi. + * + * However for most cases, binding the cgi to http in and out, the default + * lws implementation already does the right thing. + */ + +enum lws_enum_stdinouterr { + LWS_STDIN = 0, + LWS_STDOUT = 1, + LWS_STDERR = 2, +}; + +enum lws_cgi_hdr_state { + LCHS_HEADER, + LCHS_CR1, + LCHS_LF1, + LCHS_CR2, + LCHS_LF2, + LHCS_RESPONSE, + LHCS_DUMP_HEADERS, + LHCS_PAYLOAD, + LCHS_SINGLE_0A, +}; + +struct lws_cgi_args { + struct lws **stdwsi; /**< get fd with lws_get_socket_fd() */ + enum lws_enum_stdinouterr ch; /**< channel index */ + unsigned char *data; /**< for messages with payload */ + enum lws_cgi_hdr_state hdr_state; /**< track where we are in cgi headers */ + int len; /**< length */ +}; + +#ifdef LWS_WITH_CGI +/** + * lws_cgi: spawn network-connected cgi process + * + * \param wsi: connection to own the process + * \param exec_array: array of "exec-name" "arg1" ... "argn" NULL + * \param script_uri_path_len: how many chars on the left of the uri are the + * path to the cgi, or -1 to spawn without URL-related env vars + * \param timeout_secs: seconds script should be allowed to run + * \param mp_cgienv: pvo list with per-vhost cgi options to put in env + */ +LWS_VISIBLE LWS_EXTERN int +lws_cgi(struct lws *wsi, const char * const *exec_array, + int script_uri_path_len, int timeout_secs, + const struct lws_protocol_vhost_options *mp_cgienv); + +/** + * lws_cgi_write_split_stdout_headers: write cgi output accounting for header part + * + * \param wsi: connection to own the process + */ +LWS_VISIBLE LWS_EXTERN int +lws_cgi_write_split_stdout_headers(struct lws *wsi); + +/** + * lws_cgi_kill: terminate cgi process associated with wsi + * + * \param wsi: connection to own the process + */ +LWS_VISIBLE LWS_EXTERN int +lws_cgi_kill(struct lws *wsi); + +/** + * lws_cgi_get_stdwsi: get wsi for stdin, stdout, or stderr + * + * \param wsi: parent wsi that has cgi + * \param ch: which of LWS_STDIN, LWS_STDOUT or LWS_STDERR + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch); + +#endif +///@} + + +/*! \defgroup fops file operation wrapping + * + * ##File operation wrapping + * + * Use these helper functions if you want to access a file from the perspective + * of a specific wsi, which is usually the case. If you just want contextless + * file access, use the fops callbacks directly with NULL wsi instead of these + * helpers. + * + * If so, then it calls the platform handler or user overrides where present + * (as defined in info->fops) + * + * The advantage from all this is user code can be portable for file operations + * without having to deal with differences between platforms. + */ +//@{ + +/** struct lws_plat_file_ops - Platform-specific file operations + * + * These provide platform-agnostic ways to deal with filesystem access in the + * library and in the user code. + */ + +#if defined(LWS_WITH_ESP32) +/* sdk preprocessor defs? compiler issue? gets confused with member names */ +#define LWS_FOP_OPEN _open +#define LWS_FOP_CLOSE _close +#define LWS_FOP_SEEK_CUR _seek_cur +#define LWS_FOP_READ _read +#define LWS_FOP_WRITE _write +#else +#define LWS_FOP_OPEN open +#define LWS_FOP_CLOSE close +#define LWS_FOP_SEEK_CUR seek_cur +#define LWS_FOP_READ read +#define LWS_FOP_WRITE write +#endif + +#define LWS_FOP_FLAGS_MASK ((1 << 23) - 1) +#define LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP (1 << 24) +#define LWS_FOP_FLAG_COMPR_IS_GZIP (1 << 25) +#define LWS_FOP_FLAG_MOD_TIME_VALID (1 << 26) +#define LWS_FOP_FLAG_VIRTUAL (1 << 27) + +struct lws_plat_file_ops; + +struct lws_fop_fd { + lws_filefd_type fd; + /**< real file descriptor related to the file... */ + const struct lws_plat_file_ops *fops; + /**< fops that apply to this fop_fd */ + void *filesystem_priv; + /**< ignored by lws; owned by the fops handlers */ + lws_filepos_t pos; + /**< generic "position in file" */ + lws_filepos_t len; + /**< generic "length of file" */ + lws_fop_flags_t flags; + /**< copy of the returned flags */ + uint32_t mod_time; + /**< optional "modification time of file", only valid if .open() + * set the LWS_FOP_FLAG_MOD_TIME_VALID flag */ +}; +typedef struct lws_fop_fd *lws_fop_fd_t; + +struct lws_fops_index { + const char *sig; /* NULL or vfs signature, eg, ".zip/" */ + uint8_t len; /* length of above string */ +}; + +struct lws_plat_file_ops { + lws_fop_fd_t (*LWS_FOP_OPEN)(const struct lws_plat_file_ops *fops, + const char *filename, const char *vpath, + lws_fop_flags_t *flags); + /**< Open file (always binary access if plat supports it) + * vpath may be NULL, or if the fops understands it, the point at which + * the filename's virtual part starts. + * *flags & LWS_FOP_FLAGS_MASK should be set to O_RDONLY or O_RDWR. + * If the file may be gzip-compressed, + * LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP is set. If it actually is + * gzip-compressed, then the open handler should OR + * LWS_FOP_FLAG_COMPR_IS_GZIP on to *flags before returning. + */ + int (*LWS_FOP_CLOSE)(lws_fop_fd_t *fop_fd); + /**< close file AND set the pointer to NULL */ + lws_fileofs_t (*LWS_FOP_SEEK_CUR)(lws_fop_fd_t fop_fd, + lws_fileofs_t offset_from_cur_pos); + /**< seek from current position */ + int (*LWS_FOP_READ)(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len); + /**< Read from file, on exit *amount is set to amount actually read */ + int (*LWS_FOP_WRITE)(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len); + /**< Write to file, on exit *amount is set to amount actually written */ + + struct lws_fops_index fi[3]; + /**< vfs path signatures implying use of this fops */ + + const struct lws_plat_file_ops *next; + /**< NULL or next fops in list */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** + * lws_get_fops() - get current file ops + * + * \param context: context + */ +LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops * LWS_WARN_UNUSED_RESULT +lws_get_fops(struct lws_context *context); +LWS_VISIBLE LWS_EXTERN void +lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops); +/** + * lws_vfs_tell() - get current file position + * + * \param fop_fd: fop_fd we are asking about + */ +LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT +lws_vfs_tell(lws_fop_fd_t fop_fd); +/** + * lws_vfs_get_length() - get current file total length in bytes + * + * \param fop_fd: fop_fd we are asking about + */ +LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT +lws_vfs_get_length(lws_fop_fd_t fop_fd); +/** + * lws_vfs_get_mod_time() - get time file last modified + * + * \param fop_fd: fop_fd we are asking about + */ +LWS_VISIBLE LWS_EXTERN uint32_t LWS_WARN_UNUSED_RESULT +lws_vfs_get_mod_time(lws_fop_fd_t fop_fd); +/** + * lws_vfs_file_seek_set() - seek relative to start of file + * + * \param fop_fd: fop_fd we are seeking in + * \param offset: offset from start of file + */ +LWS_VISIBLE LWS_EXTERN lws_fileofs_t +lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset); +/** + * lws_vfs_file_seek_end() - seek relative to end of file + * + * \param fop_fd: fop_fd we are seeking in + * \param offset: offset from start of file + */ +LWS_VISIBLE LWS_EXTERN lws_fileofs_t +lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset); + +extern struct lws_plat_file_ops fops_zip; + +/** + * lws_plat_file_open() - open vfs filepath + * + * \param fops: file ops struct that applies to this descriptor + * \param vfs_path: filename to open + * \param flags: pointer to open flags + * + * The vfs_path is scanned for known fops signatures, and the open directed + * to any matching fops open. + * + * User code should use this api to perform vfs opens. + * + * returns semi-opaque handle + */ +LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT +lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path, + lws_fop_flags_t *flags); + +/** + * lws_plat_file_close() - close file + * + * \param fop_fd: file handle to close + */ +static LWS_INLINE int +lws_vfs_file_close(lws_fop_fd_t *fop_fd) +{ + return (*fop_fd)->fops->LWS_FOP_CLOSE(fop_fd); +} + +/** + * lws_plat_file_seek_cur() - close file + * + * + * \param fop_fd: file handle + * \param offset: position to seek to + */ +static LWS_INLINE lws_fileofs_t +lws_vfs_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset); +} +/** + * lws_plat_file_read() - read from file + * + * \param fop_fd: file handle + * \param amount: how much to read (rewritten by call) + * \param buf: buffer to write to + * \param len: max length + */ +static LWS_INLINE int LWS_WARN_UNUSED_RESULT +lws_vfs_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + return fop_fd->fops->LWS_FOP_READ(fop_fd, amount, buf, len); +} +/** + * lws_plat_file_write() - write from file + * + * \param fop_fd: file handle + * \param amount: how much to write (rewritten by call) + * \param buf: buffer to read from + * \param len: max length + */ +static LWS_INLINE int LWS_WARN_UNUSED_RESULT +lws_vfs_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + return fop_fd->fops->LWS_FOP_WRITE(fop_fd, amount, buf, len); +} + +/* these are the platform file operations implementations... they can + * be called directly and used in fops arrays + */ + +LWS_VISIBLE LWS_EXTERN lws_fop_fd_t +_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, + const char *vpath, lws_fop_flags_t *flags); +LWS_VISIBLE LWS_EXTERN int +_lws_plat_file_close(lws_fop_fd_t *fop_fd); +LWS_VISIBLE LWS_EXTERN lws_fileofs_t +_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset); +LWS_VISIBLE LWS_EXTERN int +_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len); +LWS_VISIBLE LWS_EXTERN int +_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len); + +LWS_VISIBLE LWS_EXTERN int +lws_alloc_vfs_file(struct lws_context *context, const char *filename, + uint8_t **buf, lws_filepos_t *amount); +//@} + +/** \defgroup smtp SMTP related functions + * ##SMTP related functions + * \ingroup lwsapi + * + * These apis let you communicate with a local SMTP server to send email from + * lws. It handles all the SMTP sequencing and protocol actions. + * + * Your system should have postfix, sendmail or another MTA listening on port + * 25 and able to send email using the "mail" commandline app. Usually distro + * MTAs are configured for this by default. + * + * It runs via its own libuv events if initialized (which requires giving it + * a libuv loop to attach to). + * + * It operates using three callbacks, on_next() queries if there is a new email + * to send, on_get_body() asks for the body of the email, and on_sent() is + * called after the email is successfully sent. + * + * To use it + * + * - create an lws_email struct + * + * - initialize data, loop, the email_* strings, max_content_size and + * the callbacks + * + * - call lws_email_init() + * + * When you have at least one email to send, call lws_email_check() to + * schedule starting to send it. + */ +//@{ +#ifdef LWS_WITH_SMTP + +/** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */ +enum lwsgs_smtp_states { + LGSSMTP_IDLE, /**< awaiting new email */ + LGSSMTP_CONNECTING, /**< opening tcp connection to MTA */ + LGSSMTP_CONNECTED, /**< tcp connection to MTA is connected */ + LGSSMTP_SENT_HELO, /**< sent the HELO */ + LGSSMTP_SENT_FROM, /**< sent FROM */ + LGSSMTP_SENT_TO, /**< sent TO */ + LGSSMTP_SENT_DATA, /**< sent DATA request */ + LGSSMTP_SENT_BODY, /**< sent the email body */ + LGSSMTP_SENT_QUIT, /**< sent the session quit */ +}; + +/** struct lws_email - abstract context for performing SMTP operations */ +struct lws_email { + void *data; + /**< opaque pointer set by user code and available to the callbacks */ + uv_loop_t *loop; + /**< the libuv loop we will work on */ + + char email_smtp_ip[32]; /**< Fill before init, eg, "127.0.0.1" */ + char email_helo[32]; /**< Fill before init, eg, "myserver.com" */ + char email_from[100]; /**< Fill before init or on_next */ + char email_to[100]; /**< Fill before init or on_next */ + + unsigned int max_content_size; + /**< largest possible email body size */ + + /* Fill all the callbacks before init */ + + int (*on_next)(struct lws_email *email); + /**< (Fill in before calling lws_email_init) + * called when idle, 0 = another email to send, nonzero is idle. + * If you return 0, all of the email_* char arrays must be set + * to something useful. */ + int (*on_sent)(struct lws_email *email); + /**< (Fill in before calling lws_email_init) + * called when transfer of the email to the SMTP server was + * successful, your callback would remove the current email + * from its queue */ + int (*on_get_body)(struct lws_email *email, char *buf, int len); + /**< (Fill in before calling lws_email_init) + * called when the body part of the queued email is about to be + * sent to the SMTP server. */ + + + /* private things */ + uv_timer_t timeout_email; /**< private */ + enum lwsgs_smtp_states estate; /**< private */ + uv_connect_t email_connect_req; /**< private */ + uv_tcp_t email_client; /**< private */ + time_t email_connect_started; /**< private */ + char email_buf[256]; /**< private */ + char *content; /**< private */ +}; + +/** + * lws_email_init() - Initialize a struct lws_email + * + * \param email: struct lws_email to init + * \param loop: libuv loop to use + * \param max_content: max email content size + * + * Prepares a struct lws_email for use ending SMTP + */ +LWS_VISIBLE LWS_EXTERN int +lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content); + +/** + * lws_email_check() - Request check for new email + * + * \param email: struct lws_email context to check + * + * Schedules a check for new emails in 1s... call this when you have queued an + * email for send. + */ +LWS_VISIBLE LWS_EXTERN void +lws_email_check(struct lws_email *email); +/** + * lws_email_destroy() - stop using the struct lws_email + * + * \param email: the struct lws_email context + * + * Stop sending email using email and free allocations + */ +LWS_VISIBLE LWS_EXTERN void +lws_email_destroy(struct lws_email *email); + +#endif +//@} + +/* + * Stats are all uint64_t numbers that start at 0. + * Index names here have the convention + * + * _C_ counter + * _B_ byte count + * _MS_ millisecond count + */ + +enum { + LWSSTATS_C_CONNECTIONS, /**< count incoming connections */ + LWSSTATS_C_API_CLOSE, /**< count calls to close api */ + LWSSTATS_C_API_READ, /**< count calls to read from socket api */ + LWSSTATS_C_API_LWS_WRITE, /**< count calls to lws_write API */ + LWSSTATS_C_API_WRITE, /**< count calls to write API */ + LWSSTATS_C_WRITE_PARTIALS, /**< count of partial writes */ + LWSSTATS_C_WRITEABLE_CB_REQ, /**< count of writable callback requests */ + LWSSTATS_C_WRITEABLE_CB_EFF_REQ, /**< count of effective writable callback requests */ + LWSSTATS_C_WRITEABLE_CB, /**< count of writable callbacks */ + LWSSTATS_C_SSL_CONNECTIONS_FAILED, /**< count of failed SSL connections */ + LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, /**< count of accepted SSL connections */ + LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, /**< count of SSL_accept() attempts */ + LWSSTATS_C_SSL_CONNS_HAD_RX, /**< count of accepted SSL conns that have had some RX */ + LWSSTATS_C_TIMEOUTS, /**< count of timed-out connections */ + LWSSTATS_C_SERVICE_ENTRY, /**< count of entries to lws service loop */ + LWSSTATS_B_READ, /**< aggregate bytes read */ + LWSSTATS_B_WRITE, /**< aggregate bytes written */ + LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, /**< aggreate of size of accepted write data from new partials */ + LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY, /**< aggregate delay in accepting connection */ + LWSSTATS_MS_WRITABLE_DELAY, /**< aggregate delay between asking for writable and getting cb */ + LWSSTATS_MS_WORST_WRITABLE_DELAY, /**< single worst delay between asking for writable and getting cb */ + LWSSTATS_MS_SSL_RX_DELAY, /**< aggregate delay between ssl accept complete and first RX */ + LWSSTATS_C_PEER_LIMIT_AH_DENIED, /**< number of times we would have given an ah but for the peer limit */ + LWSSTATS_C_PEER_LIMIT_WSI_DENIED, /**< number of times we would have given a wsi but for the peer limit */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ + LWSSTATS_SIZE +}; + +#if defined(LWS_WITH_STATS) + +LWS_VISIBLE LWS_EXTERN uint64_t +lws_stats_get(struct lws_context *context, int index); +LWS_VISIBLE LWS_EXTERN void +lws_stats_log_dump(struct lws_context *context); +#else +static LWS_INLINE uint64_t +lws_stats_get(struct lws_context *context, int index) { return 0; } +static LWS_INLINE void +lws_stats_log_dump(struct lws_context *context) { } +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl3.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl3.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl3.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl3.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,44 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL3_H_ +#define _SSL3_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +# define SSL3_AD_CLOSE_NOTIFY 0 +# define SSL3_AD_UNEXPECTED_MESSAGE 10/* fatal */ +# define SSL3_AD_BAD_RECORD_MAC 20/* fatal */ +# define SSL3_AD_DECOMPRESSION_FAILURE 30/* fatal */ +# define SSL3_AD_HANDSHAKE_FAILURE 40/* fatal */ +# define SSL3_AD_NO_CERTIFICATE 41 +# define SSL3_AD_BAD_CERTIFICATE 42 +# define SSL3_AD_UNSUPPORTED_CERTIFICATE 43 +# define SSL3_AD_CERTIFICATE_REVOKED 44 +# define SSL3_AD_CERTIFICATE_EXPIRED 45 +# define SSL3_AD_CERTIFICATE_UNKNOWN 46 +# define SSL3_AD_ILLEGAL_PARAMETER 47/* fatal */ + +# define SSL3_AL_WARNING 1 +# define SSL3_AL_FATAL 2 + +#define SSL3_VERSION 0x0300 + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_cert.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_cert.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_cert.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_cert.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,55 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_CERT_H_ +#define _SSL_CERT_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "ssl_types.h" + +/** + * @brief create a certification object include private key object according to input certification + * + * @param ic - input certification point + * + * @return certification object point + */ +CERT *__ssl_cert_new(CERT *ic); + +/** + * @brief create a certification object include private key object + * + * @param none + * + * @return certification object point + */ +CERT* ssl_cert_new(void); + +/** + * @brief free a certification object + * + * @param cert - certification object point + * + * @return none + */ +void ssl_cert_free(CERT *cert); + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_code.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_code.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_code.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_code.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,124 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_CODE_H_ +#define _SSL_CODE_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "ssl3.h" +#include "tls1.h" +#include "x509_vfy.h" + +/* Used in SSL_set_shutdown()/SSL_get_shutdown(); */ +# define SSL_SENT_SHUTDOWN 1 +# define SSL_RECEIVED_SHUTDOWN 2 + +# define SSL_VERIFY_NONE 0x00 +# define SSL_VERIFY_PEER 0x01 +# define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02 +# define SSL_VERIFY_CLIENT_ONCE 0x04 + +/* + * The following 3 states are kept in ssl->rlayer.rstate when reads fail, you + * should not need these + */ +# define SSL_ST_READ_HEADER 0xF0 +# define SSL_ST_READ_BODY 0xF1 +# define SSL_ST_READ_DONE 0xF2 + +# define SSL_NOTHING 1 +# define SSL_WRITING 2 +# define SSL_READING 3 +# define SSL_X509_LOOKUP 4 +# define SSL_ASYNC_PAUSED 5 +# define SSL_ASYNC_NO_JOBS 6 + + +# define SSL_ERROR_NONE 0 +# define SSL_ERROR_SSL 1 +# define SSL_ERROR_WANT_READ 2 +# define SSL_ERROR_WANT_WRITE 3 +# define SSL_ERROR_WANT_X509_LOOKUP 4 +# define SSL_ERROR_SYSCALL 5/* look at error stack/return value/errno */ +# define SSL_ERROR_ZERO_RETURN 6 +# define SSL_ERROR_WANT_CONNECT 7 +# define SSL_ERROR_WANT_ACCEPT 8 +# define SSL_ERROR_WANT_ASYNC 9 +# define SSL_ERROR_WANT_ASYNC_JOB 10 + +/* Message flow states */ +typedef enum { + /* No handshake in progress */ + MSG_FLOW_UNINITED, + /* A permanent error with this connection */ + MSG_FLOW_ERROR, + /* We are about to renegotiate */ + MSG_FLOW_RENEGOTIATE, + /* We are reading messages */ + MSG_FLOW_READING, + /* We are writing messages */ + MSG_FLOW_WRITING, + /* Handshake has finished */ + MSG_FLOW_FINISHED +} MSG_FLOW_STATE; + +/* SSL subsystem states */ +typedef enum { + TLS_ST_BEFORE, + TLS_ST_OK, + DTLS_ST_CR_HELLO_VERIFY_REQUEST, + TLS_ST_CR_SRVR_HELLO, + TLS_ST_CR_CERT, + TLS_ST_CR_CERT_STATUS, + TLS_ST_CR_KEY_EXCH, + TLS_ST_CR_CERT_REQ, + TLS_ST_CR_SRVR_DONE, + TLS_ST_CR_SESSION_TICKET, + TLS_ST_CR_CHANGE, + TLS_ST_CR_FINISHED, + TLS_ST_CW_CLNT_HELLO, + TLS_ST_CW_CERT, + TLS_ST_CW_KEY_EXCH, + TLS_ST_CW_CERT_VRFY, + TLS_ST_CW_CHANGE, + TLS_ST_CW_NEXT_PROTO, + TLS_ST_CW_FINISHED, + TLS_ST_SW_HELLO_REQ, + TLS_ST_SR_CLNT_HELLO, + DTLS_ST_SW_HELLO_VERIFY_REQUEST, + TLS_ST_SW_SRVR_HELLO, + TLS_ST_SW_CERT, + TLS_ST_SW_KEY_EXCH, + TLS_ST_SW_CERT_REQ, + TLS_ST_SW_SRVR_DONE, + TLS_ST_SR_CERT, + TLS_ST_SR_KEY_EXCH, + TLS_ST_SR_CERT_VRFY, + TLS_ST_SR_NEXT_PROTO, + TLS_ST_SR_CHANGE, + TLS_ST_SR_FINISHED, + TLS_ST_SW_SESSION_TICKET, + TLS_ST_SW_CERT_STATUS, + TLS_ST_SW_CHANGE, + TLS_ST_SW_FINISHED +} OSSL_HANDSHAKE_STATE; + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_dbg.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_dbg.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_dbg.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_dbg.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,190 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_DEBUG_H_ +#define _SSL_DEBUG_H_ + +#include "platform/ssl_port.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef CONFIG_OPENSSL_DEBUG_LEVEL + #define SSL_DEBUG_LEVEL CONFIG_OPENSSL_DEBUG_LEVEL +#else + #define SSL_DEBUG_LEVEL 0 +#endif + +#define SSL_DEBUG_ON (SSL_DEBUG_LEVEL + 1) +#define SSL_DEBUG_OFF (SSL_DEBUG_LEVEL - 1) + +#ifdef CONFIG_OPENSSL_DEBUG + #ifndef SSL_DEBUG_LOG + #error "SSL_DEBUG_LOG is not defined" + #endif + + #ifndef SSL_DEBUG_FL + #define SSL_DEBUG_FL "\n" + #endif + + #define SSL_SHOW_LOCATION() \ + SSL_DEBUG_LOG("SSL assert : %s %d\n", \ + __FILE__, __LINE__) + + #define SSL_DEBUG(level, fmt, ...) \ + { \ + if (level > SSL_DEBUG_LEVEL) { \ + SSL_DEBUG_LOG(fmt SSL_DEBUG_FL, ##__VA_ARGS__); \ + } \ + } +#else /* CONFIG_OPENSSL_DEBUG */ + #define SSL_SHOW_LOCATION() + + #define SSL_DEBUG(level, fmt, ...) +#endif /* CONFIG_OPENSSL_DEBUG */ + +/** + * OpenSSL assert function + * + * if select "CONFIG_OPENSSL_ASSERT_DEBUG", SSL_ASSERT* will show error file name and line + * if select "CONFIG_OPENSSL_ASSERT_EXIT", SSL_ASSERT* will just return error code. + * if select "CONFIG_OPENSSL_ASSERT_DEBUG_EXIT" SSL_ASSERT* will show error file name and line, + * then return error code. + * if select "CONFIG_OPENSSL_ASSERT_DEBUG_BLOCK", SSL_ASSERT* will show error file name and line, + * then block here with "while (1)" + * + * SSL_ASSERT1 may will return "-1", so function's return argument is integer. + * SSL_ASSERT2 may will return "NULL", so function's return argument is a point. + * SSL_ASSERT2 may will return nothing, so function's return argument is "void". + */ +#if defined(CONFIG_OPENSSL_ASSERT_DEBUG) + #define SSL_ASSERT1(s) \ + { \ + if (!(s)) { \ + SSL_SHOW_LOCATION(); \ + } \ + } + + #define SSL_ASSERT2(s) \ + { \ + if (!(s)) { \ + SSL_SHOW_LOCATION(); \ + } \ + } + + #define SSL_ASSERT3(s) \ + { \ + if (!(s)) { \ + SSL_SHOW_LOCATION(); \ + } \ + } +#elif defined(CONFIG_OPENSSL_ASSERT_EXIT) + #define SSL_ASSERT1(s) \ + { \ + if (!(s)) { \ + return -1; \ + } \ + } + + #define SSL_ASSERT2(s) \ + { \ + if (!(s)) { \ + return NULL; \ + } \ + } + + #define SSL_ASSERT3(s) \ + { \ + if (!(s)) { \ + return ; \ + } \ + } +#elif defined(CONFIG_OPENSSL_ASSERT_DEBUG_EXIT) + #define SSL_ASSERT1(s) \ + { \ + if (!(s)) { \ + SSL_SHOW_LOCATION(); \ + return -1; \ + } \ + } + + #define SSL_ASSERT2(s) \ + { \ + if (!(s)) { \ + SSL_SHOW_LOCATION(); \ + return NULL; \ + } \ + } + + #define SSL_ASSERT3(s) \ + { \ + if (!(s)) { \ + SSL_SHOW_LOCATION(); \ + return ; \ + } \ + } +#elif defined(CONFIG_OPENSSL_ASSERT_DEBUG_BLOCK) + #define SSL_ASSERT1(s) \ + { \ + if (!(s)) { \ + SSL_SHOW_LOCATION(); \ + while (1); \ + } \ + } + + #define SSL_ASSERT2(s) \ + { \ + if (!(s)) { \ + SSL_SHOW_LOCATION(); \ + while (1); \ + } \ + } + + #define SSL_ASSERT3(s) \ + { \ + if (!(s)) { \ + SSL_SHOW_LOCATION(); \ + while (1); \ + } \ + } +#else + #define SSL_ASSERT1(s) + #define SSL_ASSERT2(s) + #define SSL_ASSERT3(s) +#endif + +#define SSL_PLATFORM_DEBUG_LEVEL SSL_DEBUG_OFF +#define SSL_PLATFORM_ERROR_LEVEL SSL_DEBUG_ON + +#define SSL_CERT_DEBUG_LEVEL SSL_DEBUG_OFF +#define SSL_CERT_ERROR_LEVEL SSL_DEBUG_ON + +#define SSL_PKEY_DEBUG_LEVEL SSL_DEBUG_OFF +#define SSL_PKEY_ERROR_LEVEL SSL_DEBUG_ON + +#define SSL_X509_DEBUG_LEVEL SSL_DEBUG_OFF +#define SSL_X509_ERROR_LEVEL SSL_DEBUG_ON + +#define SSL_LIB_DEBUG_LEVEL SSL_DEBUG_OFF +#define SSL_LIB_ERROR_LEVEL SSL_DEBUG_ON + +#define SSL_STACK_DEBUG_LEVEL SSL_DEBUG_OFF +#define SSL_STACK_ERROR_LEVEL SSL_DEBUG_ON + +#ifdef __cplusplus + } +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_lib.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_lib.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_lib.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_lib.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,30 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_LIB_H_ +#define _SSL_LIB_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "ssl_types.h" + + void _ssl_set_alpn_list(const SSL *ssl); + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_methods.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_methods.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_methods.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_methods.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,121 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_METHODS_H_ +#define _SSL_METHODS_H_ + +#include "ssl_types.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * TLS method function implement + */ +#define IMPLEMENT_TLS_METHOD_FUNC(func_name, \ + new, free, \ + handshake, shutdown, clear, \ + read, send, pending, \ + set_fd, get_fd, \ + set_bufflen, \ + get_verify_result, \ + get_state) \ + static const SSL_METHOD_FUNC func_name LOCAL_ATRR = { \ + new, \ + free, \ + handshake, \ + shutdown, \ + clear, \ + read, \ + send, \ + pending, \ + set_fd, \ + get_fd, \ + set_bufflen, \ + get_verify_result, \ + get_state \ + }; + +#define IMPLEMENT_TLS_METHOD(ver, mode, fun, func_name) \ + const SSL_METHOD* func_name(void) { \ + static const SSL_METHOD func_name##_data LOCAL_ATRR = { \ + ver, \ + mode, \ + &(fun), \ + }; \ + return &func_name##_data; \ + } + +#define IMPLEMENT_SSL_METHOD(ver, mode, fun, func_name) \ + const SSL_METHOD* func_name(void) { \ + static const SSL_METHOD func_name##_data LOCAL_ATRR = { \ + ver, \ + mode, \ + &(fun), \ + }; \ + return &func_name##_data; \ + } + +#define IMPLEMENT_X509_METHOD(func_name, \ + new, \ + free, \ + load, \ + show_info) \ + const X509_METHOD* func_name(void) { \ + static const X509_METHOD func_name##_data LOCAL_ATRR = { \ + new, \ + free, \ + load, \ + show_info \ + }; \ + return &func_name##_data; \ + } + +#define IMPLEMENT_PKEY_METHOD(func_name, \ + new, \ + free, \ + load) \ + const PKEY_METHOD* func_name(void) { \ + static const PKEY_METHOD func_name##_data LOCAL_ATRR = { \ + new, \ + free, \ + load \ + }; \ + return &func_name##_data; \ + } + +/** + * @brief get X509 object method + * + * @param none + * + * @return X509 object method point + */ +const X509_METHOD* X509_method(void); + +/** + * @brief get private key object method + * + * @param none + * + * @return private key object method point + */ +const PKEY_METHOD* EVP_PKEY_method(void); + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_pkey.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_pkey.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_pkey.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_pkey.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,86 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_PKEY_H_ +#define _SSL_PKEY_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "ssl_types.h" + +/** + * @brief create a private key object according to input private key + * + * @param ipk - input private key point + * + * @return new private key object point + */ +EVP_PKEY* __EVP_PKEY_new(EVP_PKEY *ipk); + +/** + * @brief create a private key object + * + * @param none + * + * @return private key object point + */ +EVP_PKEY* EVP_PKEY_new(void); + +/** + * @brief load a character key context into system context. If '*a' is pointed to the + * private key, then load key into it. Or create a new private key object + * + * @param type - private key type + * @param a - a point pointed to a private key point + * @param pp - a point pointed to the key context memory point + * @param length - key bytes + * + * @return private key object point + */ +EVP_PKEY* d2i_PrivateKey(int type, + EVP_PKEY **a, + const unsigned char **pp, + long length); + +/** + * @brief free a private key object + * + * @param pkey - private key object point + * + * @return none + */ +void EVP_PKEY_free(EVP_PKEY *x); + +/** + * @brief load private key into the SSL + * + * @param type - private key type + * @param ssl - SSL point + * @param len - data bytes + * @param d - data point + * + * @return result + * 0 : failed + * 1 : OK + */ + int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const unsigned char *d, long len); + + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_stack.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_stack.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_stack.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_stack.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,52 @@ +#ifndef _SSL_STACK_H_ +#define _SSL_STACK_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "ssl_types.h" + +#define STACK_OF(type) struct stack_st_##type + +#define SKM_DEFINE_STACK_OF(t1, t2, t3) \ + STACK_OF(t1); \ + static ossl_inline STACK_OF(t1) *sk_##t1##_new_null(void) \ + { \ + return (STACK_OF(t1) *)OPENSSL_sk_new_null(); \ + } \ + +#define DEFINE_STACK_OF(t) SKM_DEFINE_STACK_OF(t, t, t) + +/** + * @brief create a openssl stack object + * + * @param c - stack function + * + * @return openssl stack object point + */ +OPENSSL_STACK* OPENSSL_sk_new(OPENSSL_sk_compfunc c); + +/** + * @brief create a NULL function openssl stack object + * + * @param none + * + * @return openssl stack object point + */ +OPENSSL_STACK *OPENSSL_sk_new_null(void); + +/** + * @brief free openssl stack object + * + * @param openssl stack object point + * + * @return none + */ +void OPENSSL_sk_free(OPENSSL_STACK *stack); + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_types.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_types.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_types.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_types.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,297 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_TYPES_H_ +#define _SSL_TYPES_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include +#if defined(LWS_WITH_ESP32) +#undef MBEDTLS_CONFIG_FILE +#define MBEDTLS_CONFIG_FILE +#endif + +#include "ssl_code.h" + +typedef void SSL_CIPHER; + +typedef void X509_STORE_CTX; +typedef void X509_STORE; + +typedef void RSA; + +typedef void STACK; +typedef void BIO; + +#define ossl_inline inline + +#define SSL_METHOD_CALL(f, s, ...) s->method->func->ssl_##f(s, ##__VA_ARGS__) +#define X509_METHOD_CALL(f, x, ...) x->method->x509_##f(x, ##__VA_ARGS__) +#define EVP_PKEY_METHOD_CALL(f, k, ...) k->method->pkey_##f(k, ##__VA_ARGS__) + +typedef int (*OPENSSL_sk_compfunc)(const void *, const void *); + +struct stack_st; +typedef struct stack_st OPENSSL_STACK; + +struct ssl_method_st; +typedef struct ssl_method_st SSL_METHOD; + +struct ssl_method_func_st; +typedef struct ssl_method_func_st SSL_METHOD_FUNC; + +struct record_layer_st; +typedef struct record_layer_st RECORD_LAYER; + +struct ossl_statem_st; +typedef struct ossl_statem_st OSSL_STATEM; + +struct ssl_session_st; +typedef struct ssl_session_st SSL_SESSION; + +struct ssl_ctx_st; +typedef struct ssl_ctx_st SSL_CTX; + +struct ssl_st; +typedef struct ssl_st SSL; + +struct cert_st; +typedef struct cert_st CERT; + +struct x509_st; +typedef struct x509_st X509; + +struct X509_VERIFY_PARAM_st; +typedef struct X509_VERIFY_PARAM_st X509_VERIFY_PARAM; + +struct evp_pkey_st; +typedef struct evp_pkey_st EVP_PKEY; + +struct x509_method_st; +typedef struct x509_method_st X509_METHOD; + +struct pkey_method_st; +typedef struct pkey_method_st PKEY_METHOD; + +struct stack_st { + + char **data; + + int num_alloc; + + OPENSSL_sk_compfunc c; +}; + +struct evp_pkey_st { + + void *pkey_pm; + + const PKEY_METHOD *method; +}; + +struct x509_st { + + /* X509 certification platform private point */ + void *x509_pm; + + const X509_METHOD *method; +}; + +struct cert_st { + + int sec_level; + + X509 *x509; + + EVP_PKEY *pkey; + +}; + +struct ossl_statem_st { + + MSG_FLOW_STATE state; + + int hand_state; +}; + +struct record_layer_st { + + int rstate; + + int read_ahead; +}; + +struct ssl_session_st { + + long timeout; + + long time; + + X509 *peer; +}; + +struct X509_VERIFY_PARAM_st { + + int depth; + +}; + +typedef int (*next_proto_cb)(SSL *ssl, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg); + +struct ssl_ctx_st +{ + int version; + + int references; + + unsigned long options; + + const SSL_METHOD *method; + + CERT *cert; + + X509 *client_CA; + + const char **alpn_protos; + + next_proto_cb alpn_cb; + + int verify_mode; + + int (*default_verify_callback) (int ok, X509_STORE_CTX *ctx); + + long session_timeout; + + int read_ahead; + + int read_buffer_len; + + X509_VERIFY_PARAM param; +}; + +struct ssl_st +{ + /* protocol version(one of SSL3.0, TLS1.0, etc.) */ + int version; + + unsigned long options; + + /* shut things down(0x01 : sent, 0x02 : received) */ + int shutdown; + + CERT *cert; + + X509 *client_CA; + + SSL_CTX *ctx; + + const SSL_METHOD *method; + + RECORD_LAYER rlayer; + + /* where we are */ + OSSL_STATEM statem; + + SSL_SESSION *session; + + int verify_mode; + + int (*verify_callback) (int ok, X509_STORE_CTX *ctx); + + int rwstate; + int interrupted_remaining_write; + + long verify_result; + + X509_VERIFY_PARAM param; + + int err; + + void (*info_callback) (const SSL *ssl, int type, int val); + + /* SSL low-level system arch point */ + void *ssl_pm; +}; + +struct ssl_method_st { + /* protocol version(one of SSL3.0, TLS1.0, etc.) */ + int version; + + /* SSL mode(client(0) , server(1), not known(-1)) */ + int endpoint; + + const SSL_METHOD_FUNC *func; +}; + +struct ssl_method_func_st { + + int (*ssl_new)(SSL *ssl); + + void (*ssl_free)(SSL *ssl); + + int (*ssl_handshake)(SSL *ssl); + + int (*ssl_shutdown)(SSL *ssl); + + int (*ssl_clear)(SSL *ssl); + + int (*ssl_read)(SSL *ssl, void *buffer, int len); + + int (*ssl_send)(SSL *ssl, const void *buffer, int len); + + int (*ssl_pending)(const SSL *ssl); + + void (*ssl_set_fd)(SSL *ssl, int fd, int mode); + + int (*ssl_get_fd)(const SSL *ssl, int mode); + + void (*ssl_set_bufflen)(SSL *ssl, int len); + + long (*ssl_get_verify_result)(const SSL *ssl); + + OSSL_HANDSHAKE_STATE (*ssl_get_state)(const SSL *ssl); +}; + +struct x509_method_st { + + int (*x509_new)(X509 *x, X509 *m_x); + + void (*x509_free)(X509 *x); + + int (*x509_load)(X509 *x, const unsigned char *buf, int len); + + int (*x509_show_info)(X509 *x); +}; + +struct pkey_method_st { + + int (*pkey_new)(EVP_PKEY *pkey, EVP_PKEY *m_pkey); + + void (*pkey_free)(EVP_PKEY *pkey); + + int (*pkey_load)(EVP_PKEY *pkey, const unsigned char *buf, int len); +}; + +#define OPENSSL_NPN_NEGOTIATED 1 + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_x509.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_x509.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/ssl_x509.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/ssl_x509.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,110 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_X509_H_ +#define _SSL_X509_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "ssl_types.h" +#include "ssl_stack.h" + +DEFINE_STACK_OF(X509_NAME) + +/** + * @brief create a X509 certification object according to input X509 certification + * + * @param ix - input X509 certification point + * + * @return new X509 certification object point + */ +X509* __X509_new(X509 *ix); + +/** + * @brief create a X509 certification object + * + * @param none + * + * @return X509 certification object point + */ +X509* X509_new(void); + +/** + * @brief load a character certification context into system context. If '*cert' is pointed to the + * certification, then load certification into it. Or create a new X509 certification object + * + * @param cert - a point pointed to X509 certification + * @param buffer - a point pointed to the certification context memory point + * @param length - certification bytes + * + * @return X509 certification object point + */ +X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); + +/** + * @brief free a X509 certification object + * + * @param x - X509 certification object point + * + * @return none + */ +void X509_free(X509 *x); + +/** + * @brief set SSL context client CA certification + * + * @param ctx - SSL context point + * @param x - X509 certification point + * + * @return result + * 0 : failed + * 1 : OK + */ +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); + +/** + * @brief add CA client certification into the SSL + * + * @param ssl - SSL point + * @param x - X509 certification point + * + * @return result + * 0 : failed + * 1 : OK + */ +int SSL_add_client_CA(SSL *ssl, X509 *x); + +/** + * @brief load certification into the SSL + * + * @param ssl - SSL point + * @param len - data bytes + * @param d - data point + * + * @return result + * 0 : failed + * 1 : OK + * + */ +int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d); + +const char *X509_verify_cert_error_string(long n); + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/tls1.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/tls1.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/tls1.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/tls1.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,58 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _TLS1_H_ +#define _TLS1_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +# define TLS1_AD_DECRYPTION_FAILED 21 +# define TLS1_AD_RECORD_OVERFLOW 22 +# define TLS1_AD_UNKNOWN_CA 48/* fatal */ +# define TLS1_AD_ACCESS_DENIED 49/* fatal */ +# define TLS1_AD_DECODE_ERROR 50/* fatal */ +# define TLS1_AD_DECRYPT_ERROR 51 +# define TLS1_AD_EXPORT_RESTRICTION 60/* fatal */ +# define TLS1_AD_PROTOCOL_VERSION 70/* fatal */ +# define TLS1_AD_INSUFFICIENT_SECURITY 71/* fatal */ +# define TLS1_AD_INTERNAL_ERROR 80/* fatal */ +# define TLS1_AD_INAPPROPRIATE_FALLBACK 86/* fatal */ +# define TLS1_AD_USER_CANCELLED 90 +# define TLS1_AD_NO_RENEGOTIATION 100 +/* codes 110-114 are from RFC3546 */ +# define TLS1_AD_UNSUPPORTED_EXTENSION 110 +# define TLS1_AD_CERTIFICATE_UNOBTAINABLE 111 +# define TLS1_AD_UNRECOGNIZED_NAME 112 +# define TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE 113 +# define TLS1_AD_BAD_CERTIFICATE_HASH_VALUE 114 +# define TLS1_AD_UNKNOWN_PSK_IDENTITY 115/* fatal */ +# define TLS1_AD_NO_APPLICATION_PROTOCOL 120 /* fatal */ + +/* Special value for method supporting multiple versions */ +#define TLS_ANY_VERSION 0x10000 + +#define TLS1_VERSION 0x0301 +#define TLS1_1_VERSION 0x0302 +#define TLS1_2_VERSION 0x0303 + +#define SSL_TLSEXT_ERR_OK 0 +#define SSL_TLSEXT_ERR_NOACK 3 + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/x509_vfy.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/x509_vfy.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/internal/x509_vfy.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/internal/x509_vfy.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,116 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _X509_VFY_H_ +#define _X509_VFY_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#define X509_V_OK 0 +#define X509_V_ERR_UNSPECIFIED 1 +#define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT 2 +#define X509_V_ERR_UNABLE_TO_GET_CRL 3 +#define X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE 4 +#define X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE 5 +#define X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY 6 +#define X509_V_ERR_CERT_SIGNATURE_FAILURE 7 +#define X509_V_ERR_CRL_SIGNATURE_FAILURE 8 +#define X509_V_ERR_CERT_NOT_YET_VALID 9 +#define X509_V_ERR_CERT_HAS_EXPIRED 10 +#define X509_V_ERR_CRL_NOT_YET_VALID 11 +#define X509_V_ERR_CRL_HAS_EXPIRED 12 +#define X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD 13 +#define X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD 14 +#define X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD 15 +#define X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD 16 +#define X509_V_ERR_OUT_OF_MEM 17 +#define X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT 18 +#define X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN 19 +#define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY 20 +#define X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE 21 +#define X509_V_ERR_CERT_CHAIN_TOO_LONG 22 +#define X509_V_ERR_CERT_REVOKED 23 +#define X509_V_ERR_INVALID_CA 24 +#define X509_V_ERR_PATH_LENGTH_EXCEEDED 25 +#define X509_V_ERR_INVALID_PURPOSE 26 +#define X509_V_ERR_CERT_UNTRUSTED 27 +#define X509_V_ERR_CERT_REJECTED 28 +/* These are 'informational' when looking for issuer cert */ +#define X509_V_ERR_SUBJECT_ISSUER_MISMATCH 29 +#define X509_V_ERR_AKID_SKID_MISMATCH 30 +#define X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH 31 +#define X509_V_ERR_KEYUSAGE_NO_CERTSIGN 32 +#define X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER 33 +#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34 +#define X509_V_ERR_KEYUSAGE_NO_CRL_SIGN 35 +#define X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION 36 +#define X509_V_ERR_INVALID_NON_CA 37 +#define X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED 38 +#define X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE 39 +#define X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED 40 +#define X509_V_ERR_INVALID_EXTENSION 41 +#define X509_V_ERR_INVALID_POLICY_EXTENSION 42 +#define X509_V_ERR_NO_EXPLICIT_POLICY 43 +#define X509_V_ERR_DIFFERENT_CRL_SCOPE 44 +#define X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE 45 +#define X509_V_ERR_UNNESTED_RESOURCE 46 +#define X509_V_ERR_PERMITTED_VIOLATION 47 +#define X509_V_ERR_EXCLUDED_VIOLATION 48 +#define X509_V_ERR_SUBTREE_MINMAX 49 +/* The application is not happy */ +#define X509_V_ERR_APPLICATION_VERIFICATION 50 +#define X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE 51 +#define X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX 52 +#define X509_V_ERR_UNSUPPORTED_NAME_SYNTAX 53 +#define X509_V_ERR_CRL_PATH_VALIDATION_ERROR 54 +/* Another issuer check debug option */ +#define X509_V_ERR_PATH_LOOP 55 +/* Suite B mode algorithm violation */ +#define X509_V_ERR_SUITE_B_INVALID_VERSION 56 +#define X509_V_ERR_SUITE_B_INVALID_ALGORITHM 57 +#define X509_V_ERR_SUITE_B_INVALID_CURVE 58 +#define X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM 59 +#define X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED 60 +#define X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 61 +/* Host, email and IP check errors */ +#define X509_V_ERR_HOSTNAME_MISMATCH 62 +#define X509_V_ERR_EMAIL_MISMATCH 63 +#define X509_V_ERR_IP_ADDRESS_MISMATCH 64 +/* DANE TLSA errors */ +#define X509_V_ERR_DANE_NO_MATCH 65 +/* security level errors */ +#define X509_V_ERR_EE_KEY_TOO_SMALL 66 +#define X509_V_ERR_CA_KEY_TOO_SMALL 67 +#define X509_V_ERR_CA_MD_TOO_WEAK 68 +/* Caller error */ +#define X509_V_ERR_INVALID_CALL 69 +/* Issuer lookup error */ +#define X509_V_ERR_STORE_LOOKUP 70 +/* Certificate transparency */ +#define X509_V_ERR_NO_VALID_SCTS 71 + +#define X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION 72 + +typedef void X509_STORE_CTX; +int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx); +int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx); + + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/openssl/ssl.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/openssl/ssl.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/openssl/ssl.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/openssl/ssl.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1816 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_H_ +#define _SSL_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include +#include "internal/ssl_x509.h" +#include "internal/ssl_pkey.h" + +/* +{ +*/ + +#define SSL_CB_ALERT 0x4000 + +#define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT (1 << 0) +#define X509_CHECK_FLAG_NO_WILDCARDS (1 << 1) +#define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS (1 << 2) +#define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS (1 << 3) +#define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS (1 << 4) + +/** + * @brief create a SSL context + * + * @param method - the SSL context method point + * + * @return the context point + */ +SSL_CTX* SSL_CTX_new(const SSL_METHOD *method); + +/** + * @brief free a SSL context + * + * @param method - the SSL context point + * + * @return none + */ +void SSL_CTX_free(SSL_CTX *ctx); + +/** + * @brief create a SSL + * + * @param ctx - the SSL context point + * + * @return the SSL point + */ +SSL* SSL_new(SSL_CTX *ctx); + +/** + * @brief free the SSL + * + * @param ssl - the SSL point + * + * @return none + */ +void SSL_free(SSL *ssl); + +/** + * @brief connect to the remote SSL server + * + * @param ssl - the SSL point + * + * @return result + * 1 : OK + * -1 : failed + */ +int SSL_connect(SSL *ssl); + +/** + * @brief accept the remote connection + * + * @param ssl - the SSL point + * + * @return result + * 1 : OK + * -1 : failed + */ +int SSL_accept(SSL *ssl); + +/** + * @brief read data from to remote + * + * @param ssl - the SSL point which has been connected + * @param buffer - the received data buffer point + * @param len - the received data length + * + * @return result + * > 0 : OK, and return received data bytes + * = 0 : connection is closed + * < 0 : an error catch + */ +int SSL_read(SSL *ssl, void *buffer, int len); + +/** + * @brief send the data to remote + * + * @param ssl - the SSL point which has been connected + * @param buffer - the send data buffer point + * @param len - the send data length + * + * @return result + * > 0 : OK, and return sent data bytes + * = 0 : connection is closed + * < 0 : an error catch + */ +int SSL_write(SSL *ssl, const void *buffer, int len); + +/** + * @brief get the verifying result of the SSL certification + * + * @param ssl - the SSL point + * + * @return the result of verifying + */ +long SSL_get_verify_result(const SSL *ssl); + +/** + * @brief shutdown the connection + * + * @param ssl - the SSL point + * + * @return result + * 1 : OK + * 0 : shutdown is not finished + * -1 : an error catch + */ +int SSL_shutdown(SSL *ssl); + +/** + * @brief bind the socket file description into the SSL + * + * @param ssl - the SSL point + * @param fd - socket handle + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_set_fd(SSL *ssl, int fd); + +/** + * @brief These functions load the private key into the SSL_CTX or SSL object + * + * @param ctx - the SSL context point + * @param pkey - private key object point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); + +/** + * @brief These functions load the certification into the SSL_CTX or SSL object + * + * @param ctx - the SSL context point + * @param pkey - certification object point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); + +/** + * @brief create the target SSL context client method + * + * @param none + * + * @return the SSLV2.3 version SSL context client method + */ +const SSL_METHOD* SSLv23_client_method(void); + +/** + * @brief create the target SSL context client method + * + * @param none + * + * @return the TLSV1.0 version SSL context client method + */ +const SSL_METHOD* TLSv1_client_method(void); + +/** + * @brief create the target SSL context client method + * + * @param none + * + * @return the SSLV1.0 version SSL context client method + */ +const SSL_METHOD* SSLv3_client_method(void); + +/** + * @brief create the target SSL context client method + * + * @param none + * + * @return the TLSV1.1 version SSL context client method + */ +const SSL_METHOD* TLSv1_1_client_method(void); + +/** + * @brief create the target SSL context client method + * + * @param none + * + * @return the TLSV1.2 version SSL context client method + */ +const SSL_METHOD* TLSv1_2_client_method(void); + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLS any version SSL context client method + */ +const SSL_METHOD* TLS_client_method(void); + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the SSLV2.3 version SSL context server method + */ +const SSL_METHOD* SSLv23_server_method(void); + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLSV1.1 version SSL context server method + */ +const SSL_METHOD* TLSv1_1_server_method(void); + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLSV1.2 version SSL context server method + */ +const SSL_METHOD* TLSv1_2_server_method(void); + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLSV1.0 version SSL context server method + */ +const SSL_METHOD* TLSv1_server_method(void); + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the SSLV3.0 version SSL context server method + */ +const SSL_METHOD* SSLv3_server_method(void); + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLS any version SSL context server method + */ +const SSL_METHOD* TLS_server_method(void); + + +/** + * @brief set the SSL context ALPN select callback function + * + * @param ctx - SSL context point + * @param cb - ALPN select callback function + * @param arg - ALPN select callback function entry private data point + * + * @return none + */ +void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, + int (*cb) (SSL *ssl, + const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg), + void *arg); + + +/** + * @brief set the SSL context ALPN select protocol + * + * @param ctx - SSL context point + * @param protos - ALPN protocol name + * @param protos_len - ALPN protocol name bytes + * + * @return result + * 0 : OK + * 1 : failed + */ +int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char *protos, unsigned int protos_len); + +/** + * @brief set the SSL context next ALPN select callback function + * + * @param ctx - SSL context point + * @param cb - ALPN select callback function + * @param arg - ALPN select callback function entry private data point + * + * @return none + */ +void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, + int (*cb) (SSL *ssl, + unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg), + void *arg); + +void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, + unsigned int *len); + +void _ssl_set_alpn_list(const SSL *ssl); + +/** + * @brief get SSL error code + * + * @param ssl - SSL point + * @param ret_code - SSL return code + * + * @return SSL error number + */ +int SSL_get_error(const SSL *ssl, int ret_code); + +/** + * @brief clear the SSL error code + * + * @param none + * + * @return none + */ +void ERR_clear_error(void); + +/** + * @brief get the current SSL error code + * + * @param none + * + * @return current SSL error number + */ +int ERR_get_error(void); + +/** + * @brief register the SSL error strings + * + * @param none + * + * @return none + */ +void ERR_load_SSL_strings(void); + +/** + * @brief initialize the SSL library + * + * @param none + * + * @return none + */ +void SSL_library_init(void); + +/** + * @brief generates a human-readable string representing the error code e + * and store it into the "ret" point memory + * + * @param e - error code + * @param ret - memory point to store the string + * + * @return the result string point + */ +char *ERR_error_string(unsigned long e, char *ret); + +/** + * @brief add the SSL context option + * + * @param ctx - SSL context point + * @param opt - new SSL context option + * + * @return the SSL context option + */ +unsigned long SSL_CTX_set_options(SSL_CTX *ctx, unsigned long opt); + +/** + * @brief add the SSL context mode + * + * @param ctx - SSL context point + * @param mod - new SSL context mod + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_mode(SSL_CTX *ctx, int mod); + +/* +} +*/ + +/** + * @brief perform the SSL handshake + * + * @param ssl - SSL point + * + * @return result + * 1 : OK + * 0 : failed + * -1 : a error catch + */ +int SSL_do_handshake(SSL *ssl); + +/** + * @brief get the SSL current version + * + * @param ssl - SSL point + * + * @return the version string + */ +const char *SSL_get_version(const SSL *ssl); + +/** + * @brief set the SSL context version + * + * @param ctx - SSL context point + * @param meth - SSL method point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); + +/** + * @brief get the bytes numbers which are to be read + * + * @param ssl - SSL point + * + * @return bytes number + */ +int SSL_pending(const SSL *ssl); + +/** + * @brief check if SSL want nothing + * + * @param ssl - SSL point + * + * @return result + * 0 : false + * 1 : true + */ +int SSL_want_nothing(const SSL *ssl); + +/** + * @brief check if SSL want to read + * + * @param ssl - SSL point + * + * @return result + * 0 : false + * 1 : true + */ +int SSL_want_read(const SSL *ssl); + +/** + * @brief check if SSL want to write + * + * @param ssl - SSL point + * + * @return result + * 0 : false + * 1 : true + */ +int SSL_want_write(const SSL *ssl); + +/** + * @brief get the SSL context current method + * + * @param ctx - SSL context point + * + * @return the SSL context current method + */ +const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx); + +/** + * @brief get the SSL current method + * + * @param ssl - SSL point + * + * @return the SSL current method + */ +const SSL_METHOD *SSL_get_ssl_method(SSL *ssl); + +/** + * @brief set the SSL method + * + * @param ssl - SSL point + * @param meth - SSL method point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method); + +/** + * @brief add CA client certification into the SSL + * + * @param ssl - SSL point + * @param x - CA certification point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_add_client_CA(SSL *ssl, X509 *x); + +/** + * @brief add CA client certification into the SSL context + * + * @param ctx - SSL context point + * @param x - CA certification point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); + +/** + * @brief set the SSL CA certification list + * + * @param ssl - SSL point + * @param name_list - CA certification list + * + * @return none + */ +void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list); + +/** + * @brief set the SSL context CA certification list + * + * @param ctx - SSL context point + * @param name_list - CA certification list + * + * @return none + */ +void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list); + +/** + * @briefget the SSL CA certification list + * + * @param ssl - SSL point + * + * @return CA certification list + */ +STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl); + +/** + * @brief get the SSL context CA certification list + * + * @param ctx - SSL context point + * + * @return CA certification list + */ +STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx); + +/** + * @brief get the SSL certification point + * + * @param ssl - SSL point + * + * @return SSL certification point + */ +X509 *SSL_get_certificate(const SSL *ssl); + +/** + * @brief get the SSL private key point + * + * @param ssl - SSL point + * + * @return SSL private key point + */ +EVP_PKEY *SSL_get_privatekey(const SSL *ssl); + +/** + * @brief set the SSL information callback function + * + * @param ssl - SSL point + * @param cb - information callback function + * + * @return none + */ +void SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int val)); + +/** + * @brief get the SSL state + * + * @param ssl - SSL point + * + * @return SSL state + */ +OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); + +/** + * @brief set the SSL context read buffer length + * + * @param ctx - SSL context point + * @param len - read buffer length + * + * @return none + */ +void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); + +/** + * @brief set the SSL read buffer length + * + * @param ssl - SSL point + * @param len - read buffer length + * + * @return none + */ +void SSL_set_default_read_buffer_len(SSL *ssl, size_t len); + +/** + * @brief set the SSL security level + * + * @param ssl - SSL point + * @param level - security level + * + * @return none + */ +void SSL_set_security_level(SSL *ssl, int level); + +/** + * @brief get the SSL security level + * + * @param ssl - SSL point + * + * @return security level + */ +int SSL_get_security_level(const SSL *ssl); + +/** + * @brief get the SSL verifying mode of the SSL context + * + * @param ctx - SSL context point + * + * @return verifying mode + */ +int SSL_CTX_get_verify_mode(const SSL_CTX *ctx); + +/** + * @brief get the SSL verifying depth of the SSL context + * + * @param ctx - SSL context point + * + * @return verifying depth + */ +int SSL_CTX_get_verify_depth(const SSL_CTX *ctx); + +/** + * @brief set the SSL context verifying of the SSL context + * + * @param ctx - SSL context point + * @param mode - verifying mode + * @param verify_callback - verifying callback function + * + * @return none + */ +void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); + +/** + * @brief set the SSL verifying of the SSL context + * + * @param ctx - SSL point + * @param mode - verifying mode + * @param verify_callback - verifying callback function + * + * @return none + */ +void SSL_set_verify(SSL *s, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); + +/** + * @brief set the SSL verify depth of the SSL context + * + * @param ctx - SSL context point + * @param depth - verifying depth + * + * @return none + */ +void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth); + +/** + * @brief certification verifying callback function + * + * @param preverify_ok - verifying result + * @param x509_ctx - X509 certification point + * + * @return verifying result + */ +int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx); + +/** + * @brief set the session timeout time + * + * @param ctx - SSL context point + * @param t - new session timeout time + * + * @return old session timeout time + */ +long SSL_CTX_set_timeout(SSL_CTX *ctx, long t); + +/** + * @brief get the session timeout time + * + * @param ctx - SSL context point + * + * @return current session timeout time + */ +long SSL_CTX_get_timeout(const SSL_CTX *ctx); + +/** + * @brief set the SSL context cipher through the list string + * + * @param ctx - SSL context point + * @param str - cipher controller list string + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str); + +/** + * @brief set the SSL cipher through the list string + * + * @param ssl - SSL point + * @param str - cipher controller list string + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_set_cipher_list(SSL *ssl, const char *str); + +/** + * @brief get the SSL cipher list string + * + * @param ssl - SSL point + * + * @return cipher controller list string + */ +const char *SSL_get_cipher_list(const SSL *ssl, int n); + +/** + * @brief get the SSL cipher + * + * @param ssl - SSL point + * + * @return current cipher + */ +const SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl); + +/** + * @brief get the SSL cipher string + * + * @param ssl - SSL point + * + * @return cipher string + */ +const char *SSL_get_cipher(const SSL *ssl); + +/** + * @brief get the SSL context object X509 certification storage + * + * @param ctx - SSL context point + * + * @return x509 certification storage + */ +X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *ctx); + +/** + * @brief set the SSL context object X509 certification store + * + * @param ctx - SSL context point + * @param store - X509 certification store + * + * @return none + */ +void SSL_CTX_set_cert_store(SSL_CTX *ctx, X509_STORE *store); + +/** + * @brief get the SSL specifical statement + * + * @param ssl - SSL point + * + * @return specifical statement + */ +int SSL_want(const SSL *ssl); + +/** + * @brief check if the SSL is SSL_X509_LOOKUP state + * + * @param ssl - SSL point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_want_x509_lookup(const SSL *ssl); + +/** + * @brief reset the SSL + * + * @param ssl - SSL point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_clear(SSL *ssl); + +/** + * @brief get the socket handle of the SSL + * + * @param ssl - SSL point + * + * @return result + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_fd(const SSL *ssl); + +/** + * @brief get the read only socket handle of the SSL + * + * @param ssl - SSL point + * + * @return result + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_rfd(const SSL *ssl); + +/** + * @brief get the write only socket handle of the SSL + * + * @param ssl - SSL point + * + * @return result + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_wfd(const SSL *ssl); + +/** + * @brief set the SSL if we can read as many as data + * + * @param ssl - SSL point + * @param yes - enable the function + * + * @return none + */ +void SSL_set_read_ahead(SSL *s, int yes); + +/** + * @brief set the SSL context if we can read as many as data + * + * @param ctx - SSL context point + * @param yes - enbale the function + * + * @return none + */ +void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes); + +/** + * @brief get the SSL ahead signal if we can read as many as data + * + * @param ssl - SSL point + * + * @return SSL context ahead signal + */ +int SSL_get_read_ahead(const SSL *ssl); + +/** + * @brief get the SSL context ahead signal if we can read as many as data + * + * @param ctx - SSL context point + * + * @return SSL context ahead signal + */ +long SSL_CTX_get_read_ahead(SSL_CTX *ctx); + +/** + * @brief check if some data can be read + * + * @param ssl - SSL point + * + * @return + * 1 : there are bytes to be read + * 0 : no data + */ +int SSL_has_pending(const SSL *ssl); + +/** + * @brief load the X509 certification into SSL context + * + * @param ctx - SSL context point + * @param x - X509 certification point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x);//loads the certificate x into ctx + +/** + * @brief load the ASN1 certification into SSL context + * + * @param ctx - SSL context point + * @param len - certification length + * @param d - data point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); + +/** + * @brief load the certification file into SSL context + * + * @param ctx - SSL context point + * @param file - certification file name + * @param type - certification encoding type + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type); + +/** + * @brief load the certification chain file into SSL context + * + * @param ctx - SSL context point + * @param file - certification chain file name + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file); + + +/** + * @brief load the ASN1 private key into SSL context + * + * @param ctx - SSL context point + * @param d - data point + * @param len - private key length + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d, long len);//adds the private key of type pk stored at memory location d (length len) to ctx + +/** + * @brief load the private key file into SSL context + * + * @param ctx - SSL context point + * @param file - private key file name + * @param type - private key encoding type + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type); + +/** + * @brief load the RSA private key into SSL context + * + * @param ctx - SSL context point + * @param x - RSA private key point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa); + +/** + * @brief load the RSA ASN1 private key into SSL context + * + * @param ctx - SSL context point + * @param d - data point + * @param len - RSA private key length + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); + +/** + * @brief load the RSA private key file into SSL context + * + * @param ctx - SSL context point + * @param file - RSA private key file name + * @param type - private key encoding type + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type); + + +/** + * @brief check if the private key and certification is matched + * + * @param ctx - SSL context point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_check_private_key(const SSL_CTX *ctx); + +/** + * @brief set the SSL context server information + * + * @param ctx - SSL context point + * @param serverinfo - server information string + * @param serverinfo_length - server information length + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, size_t serverinfo_length); + +/** + * @brief load the SSL context server infomation file into SSL context + * + * @param ctx - SSL context point + * @param file - server information file + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file); + +/** + * @brief SSL select next function + * + * @param out - point of output data point + * @param outlen - output data length + * @param in - input data + * @param inlen - input data length + * @param client - client data point + * @param client_len -client data length + * + * @return NPN state + * OPENSSL_NPN_UNSUPPORTED : not support + * OPENSSL_NPN_NEGOTIATED : negotiated + * OPENSSL_NPN_NO_OVERLAP : no overlap + */ +int SSL_select_next_proto(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + const unsigned char *client, unsigned int client_len); + +/** + * @brief load the extra certification chain into the SSL context + * + * @param ctx - SSL context point + * @param x509 - X509 certification + * + * @return result + * 1 : OK + * 0 : failed + */ +long SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *); + +/** + * @brief control the SSL context + * + * @param ctx - SSL context point + * @param cmd - command + * @param larg - parameter length + * @param parg - parameter point + * + * @return result + * 1 : OK + * 0 : failed + */ +long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, char *parg); + +/** + * @brief get the SSL context cipher + * + * @param ctx - SSL context point + * + * @return SSL context cipher + */ +STACK *SSL_CTX_get_ciphers(const SSL_CTX *ctx); + +/** + * @brief check if the SSL context can read as many as data + * + * @param ctx - SSL context point + * + * @return result + * 1 : OK + * 0 : failed + */ +long SSL_CTX_get_default_read_ahead(SSL_CTX *ctx); + +/** + * @brief get the SSL context extra data + * + * @param ctx - SSL context point + * @param idx - index + * + * @return data point + */ +char *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx); + +/** + * @brief get the SSL context quiet shutdown option + * + * @param ctx - SSL context point + * + * @return quiet shutdown option + */ +int SSL_CTX_get_quiet_shutdown(const SSL_CTX *ctx); + +/** + * @brief load the SSL context CA file + * + * @param ctx - SSL context point + * @param CAfile - CA certification file + * @param CApath - CA certification file path + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath); + +/** + * @brief add SSL context reference count by '1' + * + * @param ctx - SSL context point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_up_ref(SSL_CTX *ctx); + +/** + * @brief set SSL context application private data + * + * @param ctx - SSL context point + * @param arg - private data + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_app_data(SSL_CTX *ctx, void *arg); + +/** + * @brief set SSL context client certification callback function + * + * @param ctx - SSL context point + * @param cb - callback function + * + * @return none + */ +void SSL_CTX_set_client_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, X509 **x509, EVP_PKEY **pkey)); + +/** + * @brief set the SSL context if we can read as many as data + * + * @param ctx - SSL context point + * @param m - enable the fuction + * + * @return none + */ +void SSL_CTX_set_default_read_ahead(SSL_CTX *ctx, int m); + +/** + * @brief set SSL context default verifying path + * + * @param ctx - SSL context point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); + +/** + * @brief set SSL context default verifying directory + * + * @param ctx - SSL context point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx); + +/** + * @brief set SSL context default verifying file + * + * @param ctx - SSL context point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_default_verify_file(SSL_CTX *ctx); + +/** + * @brief set SSL context extra data + * + * @param ctx - SSL context point + * @param idx - data index + * @param arg - data point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_ex_data(SSL_CTX *s, int idx, char *arg); + +/** + * @brief clear the SSL context option bit of "op" + * + * @param ctx - SSL context point + * @param op - option + * + * @return SSL context option + */ +unsigned long SSL_CTX_clear_options(SSL_CTX *ctx, unsigned long op); + +/** + * @brief get the SSL context option + * + * @param ctx - SSL context point + * @param op - option + * + * @return SSL context option + */ +unsigned long SSL_CTX_get_options(SSL_CTX *ctx); + +/** + * @brief set the SSL context quiet shutdown mode + * + * @param ctx - SSL context point + * @param mode - mode + * + * @return none + */ +void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode); + +/** + * @brief get the SSL context X509 certification + * + * @param ctx - SSL context point + * + * @return X509 certification + */ +X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx); + +/** + * @brief get the SSL context private key + * + * @param ctx - SSL context point + * + * @return private key + */ +EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx); + +/** + * @brief set SSL context PSK identity hint + * + * @param ctx - SSL context point + * @param hint - PSK identity hint + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *hint); + +/** + * @brief set SSL context PSK server callback function + * + * @param ctx - SSL context point + * @param callback - callback function + * + * @return none + */ +void SSL_CTX_set_psk_server_callback(SSL_CTX *ctx, + unsigned int (*callback)(SSL *ssl, + const char *identity, + unsigned char *psk, + int max_psk_len)); +/** + * @brief get alert description string + * + * @param value - alert value + * + * @return alert description string + */ +const char *SSL_alert_desc_string(int value); + +/** + * @brief get alert description long string + * + * @param value - alert value + * + * @return alert description long string + */ +const char *SSL_alert_desc_string_long(int value); + +/** + * @brief get alert type string + * + * @param value - alert value + * + * @return alert type string + */ +const char *SSL_alert_type_string(int value); + +/** + * @brief get alert type long string + * + * @param value - alert value + * + * @return alert type long string + */ +const char *SSL_alert_type_string_long(int value); + +/** + * @brief get SSL context of the SSL + * + * @param ssl - SSL point + * + * @return SSL context + */ +SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); + +/** + * @brief get SSL application data + * + * @param ssl - SSL point + * + * @return application data + */ +char *SSL_get_app_data(SSL *ssl); + +/** + * @brief get SSL cipher bits + * + * @param ssl - SSL point + * @param alg_bits - algorithm bits + * + * @return strength bits + */ +int SSL_get_cipher_bits(const SSL *ssl, int *alg_bits); + +/** + * @brief get SSL cipher name + * + * @param ssl - SSL point + * + * @return SSL cipher name + */ +char *SSL_get_cipher_name(const SSL *ssl); + +/** + * @brief get SSL cipher version + * + * @param ssl - SSL point + * + * @return SSL cipher version + */ +char *SSL_get_cipher_version(const SSL *ssl); + +/** + * @brief get SSL extra data + * + * @param ssl - SSL point + * @param idx - data index + * + * @return extra data + */ +char *SSL_get_ex_data(const SSL *ssl, int idx); + +/** + * @brief get index of the SSL extra data X509 storage context + * + * @param none + * + * @return data index + */ +int SSL_get_ex_data_X509_STORE_CTX_idx(void); + +/** + * @brief get peer certification chain + * + * @param ssl - SSL point + * + * @return certification chain + */ +STACK *SSL_get_peer_cert_chain(const SSL *ssl); + +/** + * @brief get peer certification + * + * @param ssl - SSL point + * + * @return certification + */ +X509 *SSL_get_peer_certificate(const SSL *ssl); + +/** + * @brief get SSL quiet shutdown mode + * + * @param ssl - SSL point + * + * @return quiet shutdown mode + */ +int SSL_get_quiet_shutdown(const SSL *ssl); + +/** + * @brief get SSL read only IO handle + * + * @param ssl - SSL point + * + * @return IO handle + */ +BIO *SSL_get_rbio(const SSL *ssl); + +/** + * @brief get SSL shared ciphers + * + * @param ssl - SSL point + * @param buf - buffer to store the ciphers + * @param len - buffer len + * + * @return shared ciphers + */ +char *SSL_get_shared_ciphers(const SSL *ssl, char *buf, int len); + +/** + * @brief get SSL shutdown mode + * + * @param ssl - SSL point + * + * @return shutdown mode + */ +int SSL_get_shutdown(const SSL *ssl); + +/** + * @brief get SSL session time + * + * @param ssl - SSL point + * + * @return session time + */ +long SSL_get_time(const SSL *ssl); + +/** + * @brief get SSL session timeout time + * + * @param ssl - SSL point + * + * @return session timeout time + */ +long SSL_get_timeout(const SSL *ssl); + +/** + * @brief get SSL verifying mode + * + * @param ssl - SSL point + * + * @return verifying mode + */ +int SSL_get_verify_mode(const SSL *ssl); + +/** + * @brief get SSL verify parameters + * + * @param ssl - SSL point + * + * @return verify parameters + */ +X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl); + +/** + * @brief set expected hostname the peer cert CN should have + * + * @param param - verify parameters from SSL_get0_param() + * + * @param name - the expected hostname + * + * @param namelen - the length of the hostname, or 0 if NUL terminated + * + * @return verify parameters + */ +int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, + const char *name, size_t namelen); + +/** + * @brief set parameters for X509 host verify action + * + * @param param -verify parameters from SSL_get0_param() + * + * @param flags - bitfield of X509_CHECK_FLAG_... parameters to set + * + * @return 1 for success, 0 for failure + */ +int X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param, + unsigned long flags); + +/** + * @brief clear parameters for X509 host verify action + * + * @param param -verify parameters from SSL_get0_param() + * + * @param flags - bitfield of X509_CHECK_FLAG_... parameters to clear + * + * @return 1 for success, 0 for failure + */ +int X509_VERIFY_PARAM_clear_hostflags(X509_VERIFY_PARAM *param, + unsigned long flags); + +/** + * @brief get SSL write only IO handle + * + * @param ssl - SSL point + * + * @return IO handle + */ +BIO *SSL_get_wbio(const SSL *ssl); + +/** + * @brief load SSL client CA certification file + * + * @param file - file name + * + * @return certification loading object + */ +STACK *SSL_load_client_CA_file(const char *file); + +/** + * @brief add SSL reference by '1' + * + * @param ssl - SSL point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_up_ref(SSL *ssl); + +/** + * @brief read and put data into buf, but not clear the SSL low-level storage + * + * @param ssl - SSL point + * @param buf - storage buffer point + * @param num - data bytes + * + * @return result + * > 0 : OK, and return read bytes + * = 0 : connect is closed + * < 0 : a error catch + */ +int SSL_peek(SSL *ssl, void *buf, int num); + +/** + * @brief make SSL renegotiate + * + * @param ssl - SSL point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_renegotiate(SSL *ssl); + +/** + * @brief get the state string where SSL is reading + * + * @param ssl - SSL point + * + * @return state string + */ +const char *SSL_rstate_string(SSL *ssl); + +/** + * @brief get the statement long string where SSL is reading + * + * @param ssl - SSL point + * + * @return statement long string + */ +const char *SSL_rstate_string_long(SSL *ssl); + +/** + * @brief set SSL accept statement + * + * @param ssl - SSL point + * + * @return none + */ +void SSL_set_accept_state(SSL *ssl); + +/** + * @brief set SSL application data + * + * @param ssl - SSL point + * @param arg - SSL application data point + * + * @return none + */ +void SSL_set_app_data(SSL *ssl, char *arg); + +/** + * @brief set SSL BIO + * + * @param ssl - SSL point + * @param rbio - read only IO + * @param wbio - write only IO + * + * @return none + */ +void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio); + +/** + * @brief clear SSL option + * + * @param ssl - SSL point + * @param op - clear option + * + * @return SSL option + */ +unsigned long SSL_clear_options(SSL *ssl, unsigned long op); + +/** + * @brief get SSL option + * + * @param ssl - SSL point + * + * @return SSL option + */ +unsigned long SSL_get_options(SSL *ssl); + +/** + * @brief clear SSL option + * + * @param ssl - SSL point + * @param op - setting option + * + * @return SSL option + */ +unsigned long SSL_set_options(SSL *ssl, unsigned long op); + +/** + * @brief set SSL quiet shutdown mode + * + * @param ssl - SSL point + * @param mode - quiet shutdown mode + * + * @return none + */ +void SSL_set_quiet_shutdown(SSL *ssl, int mode); + +/** + * @brief set SSL shutdown mode + * + * @param ssl - SSL point + * @param mode - shutdown mode + * + * @return none + */ +void SSL_set_shutdown(SSL *ssl, int mode); + +/** + * @brief set SSL session time + * + * @param ssl - SSL point + * @param t - session time + * + * @return session time + */ +void SSL_set_time(SSL *ssl, long t); + +/** + * @brief set SSL session timeout time + * + * @param ssl - SSL point + * @param t - session timeout time + * + * @return session timeout time + */ +void SSL_set_timeout(SSL *ssl, long t); + +/** + * @brief get SSL statement string + * + * @param ssl - SSL point + * + * @return SSL statement string + */ +char *SSL_state_string(const SSL *ssl); + +/** + * @brief get SSL statement long string + * + * @param ssl - SSL point + * + * @return SSL statement long string + */ +char *SSL_state_string_long(const SSL *ssl); + +/** + * @brief get SSL renegotiation count + * + * @param ssl - SSL point + * + * @return renegotiation count + */ +long SSL_total_renegotiations(SSL *ssl); + +/** + * @brief get SSL version + * + * @param ssl - SSL point + * + * @return SSL version + */ +int SSL_version(const SSL *ssl); + +/** + * @brief set SSL PSK identity hint + * + * @param ssl - SSL point + * @param hint - identity hint + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_use_psk_identity_hint(SSL *ssl, const char *hint); + +/** + * @brief get SSL PSK identity hint + * + * @param ssl - SSL point + * + * @return identity hint + */ +const char *SSL_get_psk_identity_hint(SSL *ssl); + +/** + * @brief get SSL PSK identity + * + * @param ssl - SSL point + * + * @return identity + */ +const char *SSL_get_psk_identity(SSL *ssl); + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/platform/ssl_pm.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/platform/ssl_pm.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/platform/ssl_pm.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/platform/ssl_pm.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,61 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_PM_H_ +#define _SSL_PM_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include +#include "ssl_types.h" +#include "ssl_port.h" + +#define LOCAL_ATRR + +int ssl_pm_new(SSL *ssl); +void ssl_pm_free(SSL *ssl); + +int ssl_pm_handshake(SSL *ssl); +int ssl_pm_shutdown(SSL *ssl); +int ssl_pm_clear(SSL *ssl); + +int ssl_pm_read(SSL *ssl, void *buffer, int len); +int ssl_pm_send(SSL *ssl, const void *buffer, int len); +int ssl_pm_pending(const SSL *ssl); + +void ssl_pm_set_fd(SSL *ssl, int fd, int mode); +int ssl_pm_get_fd(const SSL *ssl, int mode); + +OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl); + +void ssl_pm_set_bufflen(SSL *ssl, int len); + +int x509_pm_show_info(X509 *x); +int x509_pm_new(X509 *x, X509 *m_x); +void x509_pm_free(X509 *x); +int x509_pm_load(X509 *x, const unsigned char *buffer, int len); + +int pkey_pm_new(EVP_PKEY *pk, EVP_PKEY *m_pk); +void pkey_pm_free(EVP_PKEY *pk); +int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len); + +long ssl_pm_get_verify_result(const SSL *ssl); + +#ifdef __cplusplus + } +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/include/platform/ssl_port.h libwebsockets-2.4.2/lib/mbedtls_wrapper/include/platform/ssl_port.h --- libwebsockets-4.0.20/lib/mbedtls_wrapper/include/platform/ssl_port.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/include/platform/ssl_port.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,47 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_PORT_H_ +#define _SSL_PORT_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +/* +#include "esp_types.h" +#include "esp_log.h" +*/ +#include "string.h" +#include "malloc.h" + +void *ssl_mem_zalloc(size_t size); + +#define ssl_mem_malloc malloc +#define ssl_mem_free free + +#define ssl_memcpy memcpy +#define ssl_strlen strlen + +#define ssl_speed_up_enter() +#define ssl_speed_up_exit() + +#define SSL_DEBUG_FL +#define SSL_DEBUG_LOG(fmt, ...) ESP_LOGI("openssl", fmt, ##__VA_ARGS__) + +#ifdef __cplusplus + } +#endif + +#endif diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_cert.c libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_cert.c --- libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_cert.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_cert.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,87 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_cert.h" +#include "ssl_pkey.h" +#include "ssl_x509.h" +#include "ssl_dbg.h" +#include "ssl_port.h" + +/** + * @brief create a certification object according to input certification + */ +CERT *__ssl_cert_new(CERT *ic) +{ + CERT *cert; + + X509 *ix; + EVP_PKEY *ipk; + + cert = ssl_mem_zalloc(sizeof(CERT)); + if (!cert) { + SSL_DEBUG(SSL_CERT_ERROR_LEVEL, "no enough memory > (cert)"); + goto no_mem; + } + + if (ic) { + ipk = ic->pkey; + ix = ic->x509; + } else { + ipk = NULL; + ix = NULL; + } + + cert->pkey = __EVP_PKEY_new(ipk); + if (!cert->pkey) { + SSL_DEBUG(SSL_CERT_ERROR_LEVEL, "__EVP_PKEY_new() return NULL"); + goto pkey_err; + } + + cert->x509 = __X509_new(ix); + if (!cert->x509) { + SSL_DEBUG(SSL_CERT_ERROR_LEVEL, "__X509_new() return NULL"); + goto x509_err; + } + + return cert; + +x509_err: + EVP_PKEY_free(cert->pkey); +pkey_err: + ssl_mem_free(cert); +no_mem: + return NULL; +} + +/** + * @brief create a certification object include private key object + */ +CERT *ssl_cert_new(void) +{ + return __ssl_cert_new(NULL); +} + +/** + * @brief free a certification object + */ +void ssl_cert_free(CERT *cert) +{ + SSL_ASSERT3(cert); + + X509_free(cert->x509); + + EVP_PKEY_free(cert->pkey); + + ssl_mem_free(cert); +} diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_lib.c libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_lib.c --- libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_lib.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_lib.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1679 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_lib.h" +#include "ssl_pkey.h" +#include "ssl_x509.h" +#include "ssl_cert.h" +#include "ssl_dbg.h" +#include "ssl_port.h" + +#define SSL_SEND_DATA_MAX_LENGTH 1460 + +/** + * @brief create a new SSL session object + */ +static SSL_SESSION* SSL_SESSION_new(void) +{ + SSL_SESSION *session; + + session = ssl_mem_zalloc(sizeof(SSL_SESSION)); + if (!session) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "no enough memory > (session)"); + goto failed1; + } + + session->peer = X509_new(); + if (!session->peer) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "X509_new() return NULL"); + goto failed2; + } + + return session; + +failed2: + ssl_mem_free(session); +failed1: + return NULL; +} + +/** + * @brief free a new SSL session object + */ +static void SSL_SESSION_free(SSL_SESSION *session) +{ + X509_free(session->peer); + ssl_mem_free(session); +} + +/** + * @brief Discover whether the current connection is in the error state + */ +int ossl_statem_in_error(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + if (ssl->statem.state == MSG_FLOW_ERROR) + return 1; + + return 0; +} + +/** + * @brief get the SSL specifical statement + */ +int SSL_want(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + return ssl->rwstate; +} + +/** + * @brief check if SSL want nothing + */ +int SSL_want_nothing(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + if (ssl->err) + return 1; + + return (SSL_want(ssl) == SSL_NOTHING); +} + +/** + * @brief check if SSL want to read + */ +int SSL_want_read(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + if (ssl->err) + return 0; + + return (SSL_want(ssl) == SSL_READING); +} + +/** + * @brief check if SSL want to write + */ +int SSL_want_write(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + if (ssl->err) + return 0; + + return (SSL_want(ssl) == SSL_WRITING); +} + +/** + * @brief check if SSL want to lookup X509 certification + */ +int SSL_want_x509_lookup(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + return (SSL_want(ssl) == SSL_WRITING); +} + +/** + * @brief get SSL error code + */ +int SSL_get_error(const SSL *ssl, int ret_code) +{ + int ret = SSL_ERROR_SYSCALL; + + SSL_ASSERT1(ssl); + + if (ret_code > 0) + ret = SSL_ERROR_NONE; + else if (ret_code < 0) + { + if (ssl->err == SSL_ERROR_WANT_READ || SSL_want_read(ssl)) + ret = SSL_ERROR_WANT_READ; + else if (ssl->err == SSL_ERROR_WANT_WRITE || SSL_want_write(ssl)) + ret = SSL_ERROR_WANT_WRITE; + else + ret = SSL_ERROR_SYSCALL; //unknown + } + else // ret_code == 0 + { + if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) + ret = SSL_ERROR_ZERO_RETURN; + else + ret = SSL_ERROR_SYSCALL; + } + + return ret; +} + +/** + * @brief get the SSL state + */ +OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl) +{ + OSSL_HANDSHAKE_STATE state; + + SSL_ASSERT1(ssl); + + state = SSL_METHOD_CALL(get_state, ssl); + + return state; +} + +/** + * @brief create a SSL context + */ +SSL_CTX* SSL_CTX_new(const SSL_METHOD *method) +{ + SSL_CTX *ctx; + CERT *cert; + X509 *client_ca; + + if (!method) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "no no_method"); + return NULL; + } + + client_ca = X509_new(); + if (!client_ca) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "X509_new() return NULL"); + goto failed1; + } + + cert = ssl_cert_new(); + if (!cert) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "ssl_cert_new() return NULL"); + goto failed2; + } + + ctx = (SSL_CTX *)ssl_mem_zalloc(sizeof(SSL_CTX)); + if (!ctx) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "no enough memory > (ctx)"); + goto failed3; + } + + ctx->method = method; + ctx->client_CA = client_ca; + ctx->cert = cert; + + ctx->version = method->version; + + return ctx; + +failed3: + ssl_cert_free(cert); +failed2: + X509_free(client_ca); +failed1: + return NULL; +} + +/** + * @brief free a SSL context + */ +void SSL_CTX_free(SSL_CTX* ctx) +{ + SSL_ASSERT3(ctx); + + ssl_cert_free(ctx->cert); + + X509_free(ctx->client_CA); + + if (ctx->alpn_protos) + ssl_mem_free(ctx->alpn_protos); + + ssl_mem_free(ctx); +} + +/** + * @brief set the SSL context version + */ +int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth) +{ + SSL_ASSERT1(ctx); + SSL_ASSERT1(meth); + + ctx->method = meth; + + ctx->version = meth->version; + + return 1; +} + +/** + * @brief get the SSL context current method + */ +const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx) +{ + SSL_ASSERT2(ctx); + + return ctx->method; +} + +/** + * @brief create a SSL + */ +SSL *SSL_new(SSL_CTX *ctx) +{ + int ret = 0; + SSL *ssl; + + if (!ctx) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "no ctx"); + return NULL; + } + + ssl = (SSL *)ssl_mem_zalloc(sizeof(SSL)); + if (!ssl) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "no enough memory > (ssl)"); + goto failed1; + } + + ssl->session = SSL_SESSION_new(); + if (!ssl->session) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_SESSION_new() return NULL"); + goto failed2; + } + + ssl->cert = __ssl_cert_new(ctx->cert); + if (!ssl->cert) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "__ssl_cert_new() return NULL"); + goto failed3; + } + + ssl->client_CA = __X509_new(ctx->client_CA); + if (!ssl->client_CA) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "__X509_new() return NULL"); + goto failed4; + } + + ssl->ctx = ctx; + ssl->method = ctx->method; + + ssl->version = ctx->version; + ssl->options = ctx->options; + + ssl->verify_mode = ctx->verify_mode; + + ret = SSL_METHOD_CALL(new, ssl); + if (ret) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_METHOD_CALL(new) return %d", ret); + goto failed5; + } + + _ssl_set_alpn_list(ssl); + + ssl->rwstate = SSL_NOTHING; + + return ssl; + +failed5: + X509_free(ssl->client_CA); +failed4: + ssl_cert_free(ssl->cert); +failed3: + SSL_SESSION_free(ssl->session); +failed2: + ssl_mem_free(ssl); +failed1: + return NULL; +} + +/** + * @brief free the SSL + */ +void SSL_free(SSL *ssl) +{ + SSL_ASSERT3(ssl); + + SSL_METHOD_CALL(free, ssl); + + X509_free(ssl->client_CA); + + ssl_cert_free(ssl->cert); + + SSL_SESSION_free(ssl->session); + + ssl_mem_free(ssl); +} + +/** + * @brief perform the SSL handshake + */ +int SSL_do_handshake(SSL *ssl) +{ + int ret; + + SSL_ASSERT1(ssl); + + ret = SSL_METHOD_CALL(handshake, ssl); + + return ret; +} + +/** + * @brief connect to the remote SSL server + */ +int SSL_connect(SSL *ssl) +{ + SSL_ASSERT1(ssl); + + return SSL_do_handshake(ssl); +} + +/** + * @brief accept the remote connection + */ +int SSL_accept(SSL *ssl) +{ + SSL_ASSERT1(ssl); + + return SSL_do_handshake(ssl); +} + +/** + * @brief shutdown the connection + */ +int SSL_shutdown(SSL *ssl) +{ + int ret; + + SSL_ASSERT1(ssl); + + if (SSL_get_state(ssl) != TLS_ST_OK) return 1; + + ret = SSL_METHOD_CALL(shutdown, ssl); + + return ret; +} + +/** + * @brief reset the SSL + */ +int SSL_clear(SSL *ssl) +{ + int ret; + + SSL_ASSERT1(ssl); + + ret = SSL_shutdown(ssl); + if (1 != ret) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_shutdown return %d", ret); + goto failed1; + } + + SSL_METHOD_CALL(free, ssl); + + ret = SSL_METHOD_CALL(new, ssl); + if (!ret) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_METHOD_CALL(new) return %d", ret); + goto failed1; + } + + return 1; + +failed1: + return ret; +} + +/** + * @brief read data from to remote + */ +int SSL_read(SSL *ssl, void *buffer, int len) +{ + int ret; + + SSL_ASSERT1(ssl); + SSL_ASSERT1(buffer); + SSL_ASSERT1(len); + + ssl->rwstate = SSL_READING; + + ret = SSL_METHOD_CALL(read, ssl, buffer, len); + + if (ret == len) + ssl->rwstate = SSL_NOTHING; + + return ret; +} + +/** + * @brief send the data to remote + */ +int SSL_write(SSL *ssl, const void *buffer, int len) +{ + int ret; + int send_bytes, bytes; + const unsigned char *pbuf; + + SSL_ASSERT1(ssl); + SSL_ASSERT1(buffer); + SSL_ASSERT1(len); + + ssl->rwstate = SSL_WRITING; + + send_bytes = len; + pbuf = (const unsigned char *)buffer; + + do { + if (send_bytes > SSL_SEND_DATA_MAX_LENGTH) + bytes = SSL_SEND_DATA_MAX_LENGTH; + else + bytes = send_bytes; + + if (ssl->interrupted_remaining_write) { + bytes = ssl->interrupted_remaining_write; + ssl->interrupted_remaining_write = 0; + } + + ret = SSL_METHOD_CALL(send, ssl, pbuf, bytes); + //printf("%s: ssl_pm said %d for %d requested (cum %d)\n", __func__, ret, bytes, len -send_bytes); + /* the return is a NEGATIVE OpenSSL error code, or the length sent */ + if (ret > 0) { + pbuf += ret; + send_bytes -= ret; + } else + ssl->interrupted_remaining_write = bytes; + } while (ret > 0 && send_bytes && ret == bytes); + + if (ret >= 0) { + ret = len - send_bytes; + if (!ret) + ssl->rwstate = SSL_NOTHING; + } else { + if (send_bytes == len) + ret = -1; + else + ret = len - send_bytes; + } + + return ret; +} + +/** + * @brief get SSL context of the SSL + */ +SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) +{ + SSL_ASSERT2(ssl); + + return ssl->ctx; +} + +/** + * @brief get the SSL current method + */ +const SSL_METHOD *SSL_get_ssl_method(SSL *ssl) +{ + SSL_ASSERT2(ssl); + + return ssl->method; +} + +/** + * @brief set the SSL method + */ +int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method) +{ + int ret; + + SSL_ASSERT1(ssl); + SSL_ASSERT1(method); + + if (ssl->version != method->version) { + + ret = SSL_shutdown(ssl); + if (1 != ret) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_shutdown return %d", ret); + goto failed1; + } + + SSL_METHOD_CALL(free, ssl); + + ssl->method = method; + + ret = SSL_METHOD_CALL(new, ssl); + if (!ret) { + SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_METHOD_CALL(new) return %d", ret); + goto failed1; + } + } else { + ssl->method = method; + } + + + return 1; + +failed1: + return ret; +} + +/** + * @brief get SSL shutdown mode + */ +int SSL_get_shutdown(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + return ssl->shutdown; +} + +/** + * @brief set SSL shutdown mode + */ +void SSL_set_shutdown(SSL *ssl, int mode) +{ + SSL_ASSERT3(ssl); + + ssl->shutdown = mode; +} + + +/** + * @brief get the number of the bytes to be read + */ +int SSL_pending(const SSL *ssl) +{ + int ret; + + SSL_ASSERT1(ssl); + + ret = SSL_METHOD_CALL(pending, ssl); + + return ret; +} + +/** + * @brief check if some data can be read + */ +int SSL_has_pending(const SSL *ssl) +{ + int ret; + + SSL_ASSERT1(ssl); + + if (SSL_pending(ssl)) + ret = 1; + else + ret = 0; + + return ret; +} + +/** + * @brief clear the SSL context option bit of "op" + */ +unsigned long SSL_CTX_clear_options(SSL_CTX *ctx, unsigned long op) +{ + SSL_ASSERT1(ctx); + + return ctx->options &= ~op; +} + +/** + * @brief get the SSL context option + */ +unsigned long SSL_CTX_get_options(SSL_CTX *ctx) +{ + SSL_ASSERT1(ctx); + + return ctx->options; +} + +/** + * @brief set the option of the SSL context + */ +unsigned long SSL_CTX_set_options(SSL_CTX *ctx, unsigned long opt) +{ + SSL_ASSERT1(ctx); + + return ctx->options |= opt; +} + +/** + * @brief clear SSL option + */ +unsigned long SSL_clear_options(SSL *ssl, unsigned long op) +{ + SSL_ASSERT1(ssl); + + return ssl->options & ~op; +} + +/** + * @brief get SSL option + */ +unsigned long SSL_get_options(SSL *ssl) +{ + SSL_ASSERT1(ssl); + + return ssl->options; +} + +/** + * @brief clear SSL option + */ +unsigned long SSL_set_options(SSL *ssl, unsigned long op) +{ + SSL_ASSERT1(ssl); + + return ssl->options |= op; +} + +/** + * @brief get the socket handle of the SSL + */ +int SSL_get_fd(const SSL *ssl) +{ + int ret; + + SSL_ASSERT1(ssl); + + ret = SSL_METHOD_CALL(get_fd, ssl, 0); + + return ret; +} + +/** + * @brief get the read only socket handle of the SSL + */ +int SSL_get_rfd(const SSL *ssl) +{ + int ret; + + SSL_ASSERT1(ssl); + + ret = SSL_METHOD_CALL(get_fd, ssl, 0); + + return ret; +} + +/** + * @brief get the write only socket handle of the SSL + */ +int SSL_get_wfd(const SSL *ssl) +{ + int ret; + + SSL_ASSERT1(ssl); + + ret = SSL_METHOD_CALL(get_fd, ssl, 0); + + return ret; +} + +/** + * @brief bind the socket file description into the SSL + */ +int SSL_set_fd(SSL *ssl, int fd) +{ + SSL_ASSERT1(ssl); + SSL_ASSERT1(fd >= 0); + + SSL_METHOD_CALL(set_fd, ssl, fd, 0); + + return 1; +} + +/** + * @brief bind the read only socket file description into the SSL + */ +int SSL_set_rfd(SSL *ssl, int fd) +{ + SSL_ASSERT1(ssl); + SSL_ASSERT1(fd >= 0); + + SSL_METHOD_CALL(set_fd, ssl, fd, 0); + + return 1; +} + +/** + * @brief bind the write only socket file description into the SSL + */ +int SSL_set_wfd(SSL *ssl, int fd) +{ + SSL_ASSERT1(ssl); + SSL_ASSERT1(fd >= 0); + + SSL_METHOD_CALL(set_fd, ssl, fd, 0); + + return 1; +} + +/** + * @brief get SSL version + */ +int SSL_version(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + return ssl->version; +} + +/** + * @brief get the SSL version string + */ +static const char* ssl_protocol_to_string(int version) +{ + const char *str; + + if (version == TLS1_2_VERSION) + str = "TLSv1.2"; + else if (version == TLS1_1_VERSION) + str = "TLSv1.1"; + else if (version == TLS1_VERSION) + str = "TLSv1"; + else if (version == SSL3_VERSION) + str = "SSLv3"; + else + str = "unknown"; + + return str; +} + +/** + * @brief get the SSL current version + */ +const char *SSL_get_version(const SSL *ssl) +{ + SSL_ASSERT2(ssl); + + return ssl_protocol_to_string(SSL_version(ssl)); +} + +/** + * @brief get alert description string + */ +const char* SSL_alert_desc_string(int value) +{ + const char *str; + + switch (value & 0xff) + { + case SSL3_AD_CLOSE_NOTIFY: + str = "CN"; + break; + case SSL3_AD_UNEXPECTED_MESSAGE: + str = "UM"; + break; + case SSL3_AD_BAD_RECORD_MAC: + str = "BM"; + break; + case SSL3_AD_DECOMPRESSION_FAILURE: + str = "DF"; + break; + case SSL3_AD_HANDSHAKE_FAILURE: + str = "HF"; + break; + case SSL3_AD_NO_CERTIFICATE: + str = "NC"; + break; + case SSL3_AD_BAD_CERTIFICATE: + str = "BC"; + break; + case SSL3_AD_UNSUPPORTED_CERTIFICATE: + str = "UC"; + break; + case SSL3_AD_CERTIFICATE_REVOKED: + str = "CR"; + break; + case SSL3_AD_CERTIFICATE_EXPIRED: + str = "CE"; + break; + case SSL3_AD_CERTIFICATE_UNKNOWN: + str = "CU"; + break; + case SSL3_AD_ILLEGAL_PARAMETER: + str = "IP"; + break; + case TLS1_AD_DECRYPTION_FAILED: + str = "DC"; + break; + case TLS1_AD_RECORD_OVERFLOW: + str = "RO"; + break; + case TLS1_AD_UNKNOWN_CA: + str = "CA"; + break; + case TLS1_AD_ACCESS_DENIED: + str = "AD"; + break; + case TLS1_AD_DECODE_ERROR: + str = "DE"; + break; + case TLS1_AD_DECRYPT_ERROR: + str = "CY"; + break; + case TLS1_AD_EXPORT_RESTRICTION: + str = "ER"; + break; + case TLS1_AD_PROTOCOL_VERSION: + str = "PV"; + break; + case TLS1_AD_INSUFFICIENT_SECURITY: + str = "IS"; + break; + case TLS1_AD_INTERNAL_ERROR: + str = "IE"; + break; + case TLS1_AD_USER_CANCELLED: + str = "US"; + break; + case TLS1_AD_NO_RENEGOTIATION: + str = "NR"; + break; + case TLS1_AD_UNSUPPORTED_EXTENSION: + str = "UE"; + break; + case TLS1_AD_CERTIFICATE_UNOBTAINABLE: + str = "CO"; + break; + case TLS1_AD_UNRECOGNIZED_NAME: + str = "UN"; + break; + case TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE: + str = "BR"; + break; + case TLS1_AD_BAD_CERTIFICATE_HASH_VALUE: + str = "BH"; + break; + case TLS1_AD_UNKNOWN_PSK_IDENTITY: + str = "UP"; + break; + default: + str = "UK"; + break; + } + + return str; +} + +/** + * @brief get alert description long string + */ +const char* SSL_alert_desc_string_long(int value) +{ + const char *str; + + switch (value & 0xff) + { + case SSL3_AD_CLOSE_NOTIFY: + str = "close notify"; + break; + case SSL3_AD_UNEXPECTED_MESSAGE: + str = "unexpected_message"; + break; + case SSL3_AD_BAD_RECORD_MAC: + str = "bad record mac"; + break; + case SSL3_AD_DECOMPRESSION_FAILURE: + str = "decompression failure"; + break; + case SSL3_AD_HANDSHAKE_FAILURE: + str = "handshake failure"; + break; + case SSL3_AD_NO_CERTIFICATE: + str = "no certificate"; + break; + case SSL3_AD_BAD_CERTIFICATE: + str = "bad certificate"; + break; + case SSL3_AD_UNSUPPORTED_CERTIFICATE: + str = "unsupported certificate"; + break; + case SSL3_AD_CERTIFICATE_REVOKED: + str = "certificate revoked"; + break; + case SSL3_AD_CERTIFICATE_EXPIRED: + str = "certificate expired"; + break; + case SSL3_AD_CERTIFICATE_UNKNOWN: + str = "certificate unknown"; + break; + case SSL3_AD_ILLEGAL_PARAMETER: + str = "illegal parameter"; + break; + case TLS1_AD_DECRYPTION_FAILED: + str = "decryption failed"; + break; + case TLS1_AD_RECORD_OVERFLOW: + str = "record overflow"; + break; + case TLS1_AD_UNKNOWN_CA: + str = "unknown CA"; + break; + case TLS1_AD_ACCESS_DENIED: + str = "access denied"; + break; + case TLS1_AD_DECODE_ERROR: + str = "decode error"; + break; + case TLS1_AD_DECRYPT_ERROR: + str = "decrypt error"; + break; + case TLS1_AD_EXPORT_RESTRICTION: + str = "export restriction"; + break; + case TLS1_AD_PROTOCOL_VERSION: + str = "protocol version"; + break; + case TLS1_AD_INSUFFICIENT_SECURITY: + str = "insufficient security"; + break; + case TLS1_AD_INTERNAL_ERROR: + str = "internal error"; + break; + case TLS1_AD_USER_CANCELLED: + str = "user canceled"; + break; + case TLS1_AD_NO_RENEGOTIATION: + str = "no renegotiation"; + break; + case TLS1_AD_UNSUPPORTED_EXTENSION: + str = "unsupported extension"; + break; + case TLS1_AD_CERTIFICATE_UNOBTAINABLE: + str = "certificate unobtainable"; + break; + case TLS1_AD_UNRECOGNIZED_NAME: + str = "unrecognized name"; + break; + case TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE: + str = "bad certificate status response"; + break; + case TLS1_AD_BAD_CERTIFICATE_HASH_VALUE: + str = "bad certificate hash value"; + break; + case TLS1_AD_UNKNOWN_PSK_IDENTITY: + str = "unknown PSK identity"; + break; + default: + str = "unknown"; + break; + } + + return str; +} + +/** + * @brief get alert type string + */ +const char *SSL_alert_type_string(int value) +{ + const char *str; + + switch (value >> 8) + { + case SSL3_AL_WARNING: + str = "W"; + break; + case SSL3_AL_FATAL: + str = "F"; + break; + default: + str = "U"; + break; + } + + return str; +} + +/** + * @brief get alert type long string + */ +const char *SSL_alert_type_string_long(int value) +{ + const char *str; + + switch (value >> 8) + { + case SSL3_AL_WARNING: + str = "warning"; + break; + case SSL3_AL_FATAL: + str = "fatal"; + break; + default: + str = "unknown"; + break; + } + + return str; +} + +/** + * @brief get the state string where SSL is reading + */ +const char *SSL_rstate_string(SSL *ssl) +{ + const char *str; + + SSL_ASSERT2(ssl); + + switch (ssl->rlayer.rstate) + { + case SSL_ST_READ_HEADER: + str = "RH"; + break; + case SSL_ST_READ_BODY: + str = "RB"; + break; + case SSL_ST_READ_DONE: + str = "RD"; + break; + default: + str = "unknown"; + break; + } + + return str; +} + +/** + * @brief get the statement long string where SSL is reading + */ +const char *SSL_rstate_string_long(SSL *ssl) +{ + const char *str = "unknown"; + + SSL_ASSERT2(ssl); + + switch (ssl->rlayer.rstate) + { + case SSL_ST_READ_HEADER: + str = "read header"; + break; + case SSL_ST_READ_BODY: + str = "read body"; + break; + case SSL_ST_READ_DONE: + str = "read done"; + break; + default: + break; + } + + return str; +} + +/** + * @brief get SSL statement string + */ +char *SSL_state_string(const SSL *ssl) +{ + char *str = "UNKWN "; + + SSL_ASSERT2(ssl); + + if (ossl_statem_in_error(ssl)) + str = "SSLERR"; + else + { + switch (SSL_get_state(ssl)) + { + case TLS_ST_BEFORE: + str = "PINIT "; + break; + case TLS_ST_OK: + str = "SSLOK "; + break; + case TLS_ST_CW_CLNT_HELLO: + str = "TWCH"; + break; + case TLS_ST_CR_SRVR_HELLO: + str = "TRSH"; + break; + case TLS_ST_CR_CERT: + str = "TRSC"; + break; + case TLS_ST_CR_KEY_EXCH: + str = "TRSKE"; + break; + case TLS_ST_CR_CERT_REQ: + str = "TRCR"; + break; + case TLS_ST_CR_SRVR_DONE: + str = "TRSD"; + break; + case TLS_ST_CW_CERT: + str = "TWCC"; + break; + case TLS_ST_CW_KEY_EXCH: + str = "TWCKE"; + break; + case TLS_ST_CW_CERT_VRFY: + str = "TWCV"; + break; + case TLS_ST_SW_CHANGE: + case TLS_ST_CW_CHANGE: + str = "TWCCS"; + break; + case TLS_ST_SW_FINISHED: + case TLS_ST_CW_FINISHED: + str = "TWFIN"; + break; + case TLS_ST_SR_CHANGE: + case TLS_ST_CR_CHANGE: + str = "TRCCS"; + break; + case TLS_ST_SR_FINISHED: + case TLS_ST_CR_FINISHED: + str = "TRFIN"; + break; + case TLS_ST_SW_HELLO_REQ: + str = "TWHR"; + break; + case TLS_ST_SR_CLNT_HELLO: + str = "TRCH"; + break; + case TLS_ST_SW_SRVR_HELLO: + str = "TWSH"; + break; + case TLS_ST_SW_CERT: + str = "TWSC"; + break; + case TLS_ST_SW_KEY_EXCH: + str = "TWSKE"; + break; + case TLS_ST_SW_CERT_REQ: + str = "TWCR"; + break; + case TLS_ST_SW_SRVR_DONE: + str = "TWSD"; + break; + case TLS_ST_SR_CERT: + str = "TRCC"; + break; + case TLS_ST_SR_KEY_EXCH: + str = "TRCKE"; + break; + case TLS_ST_SR_CERT_VRFY: + str = "TRCV"; + break; + case DTLS_ST_CR_HELLO_VERIFY_REQUEST: + str = "DRCHV"; + break; + case DTLS_ST_SW_HELLO_VERIFY_REQUEST: + str = "DWCHV"; + break; + default: + break; + } + } + + return str; +} + +/** + * @brief get SSL statement long string + */ +char *SSL_state_string_long(const SSL *ssl) +{ + char *str = "UNKWN "; + + SSL_ASSERT2(ssl); + + if (ossl_statem_in_error(ssl)) + str = "SSLERR"; + else + { + switch (SSL_get_state(ssl)) + { + case TLS_ST_BEFORE: + str = "before SSL initialization"; + break; + case TLS_ST_OK: + str = "SSL negotiation finished successfully"; + break; + case TLS_ST_CW_CLNT_HELLO: + str = "SSLv3/TLS write client hello"; + break; + case TLS_ST_CR_SRVR_HELLO: + str = "SSLv3/TLS read server hello"; + break; + case TLS_ST_CR_CERT: + str = "SSLv3/TLS read server certificate"; + break; + case TLS_ST_CR_KEY_EXCH: + str = "SSLv3/TLS read server key exchange"; + break; + case TLS_ST_CR_CERT_REQ: + str = "SSLv3/TLS read server certificate request"; + break; + case TLS_ST_CR_SESSION_TICKET: + str = "SSLv3/TLS read server session ticket"; + break; + case TLS_ST_CR_SRVR_DONE: + str = "SSLv3/TLS read server done"; + break; + case TLS_ST_CW_CERT: + str = "SSLv3/TLS write client certificate"; + break; + case TLS_ST_CW_KEY_EXCH: + str = "SSLv3/TLS write client key exchange"; + break; + case TLS_ST_CW_CERT_VRFY: + str = "SSLv3/TLS write certificate verify"; + break; + case TLS_ST_CW_CHANGE: + case TLS_ST_SW_CHANGE: + str = "SSLv3/TLS write change cipher spec"; + break; + case TLS_ST_CW_FINISHED: + case TLS_ST_SW_FINISHED: + str = "SSLv3/TLS write finished"; + break; + case TLS_ST_CR_CHANGE: + case TLS_ST_SR_CHANGE: + str = "SSLv3/TLS read change cipher spec"; + break; + case TLS_ST_CR_FINISHED: + case TLS_ST_SR_FINISHED: + str = "SSLv3/TLS read finished"; + break; + case TLS_ST_SR_CLNT_HELLO: + str = "SSLv3/TLS read client hello"; + break; + case TLS_ST_SW_HELLO_REQ: + str = "SSLv3/TLS write hello request"; + break; + case TLS_ST_SW_SRVR_HELLO: + str = "SSLv3/TLS write server hello"; + break; + case TLS_ST_SW_CERT: + str = "SSLv3/TLS write certificate"; + break; + case TLS_ST_SW_KEY_EXCH: + str = "SSLv3/TLS write key exchange"; + break; + case TLS_ST_SW_CERT_REQ: + str = "SSLv3/TLS write certificate request"; + break; + case TLS_ST_SW_SESSION_TICKET: + str = "SSLv3/TLS write session ticket"; + break; + case TLS_ST_SW_SRVR_DONE: + str = "SSLv3/TLS write server done"; + break; + case TLS_ST_SR_CERT: + str = "SSLv3/TLS read client certificate"; + break; + case TLS_ST_SR_KEY_EXCH: + str = "SSLv3/TLS read client key exchange"; + break; + case TLS_ST_SR_CERT_VRFY: + str = "SSLv3/TLS read certificate verify"; + break; + case DTLS_ST_CR_HELLO_VERIFY_REQUEST: + str = "DTLS1 read hello verify request"; + break; + case DTLS_ST_SW_HELLO_VERIFY_REQUEST: + str = "DTLS1 write hello verify request"; + break; + default: + break; + } + } + + return str; +} + +/** + * @brief set the SSL context read buffer length + */ +void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len) +{ + SSL_ASSERT3(ctx); + + ctx->read_buffer_len = len; +} + +/** + * @brief set the SSL read buffer length + */ +void SSL_set_default_read_buffer_len(SSL *ssl, size_t len) +{ + SSL_ASSERT3(ssl); + SSL_ASSERT3(len); + + SSL_METHOD_CALL(set_bufflen, ssl, len); +} + +/** + * @brief set the SSL information callback function + */ +void SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int val)) +{ + SSL_ASSERT3(ssl); + + ssl->info_callback = cb; +} + +/** + * @brief add SSL context reference count by '1' + */ +int SSL_CTX_up_ref(SSL_CTX *ctx) +{ + SSL_ASSERT1(ctx); + + /** + * no support multi-thread SSL here + */ + ctx->references++; + + return 1; +} + +/** + * @brief set the SSL security level + */ +void SSL_set_security_level(SSL *ssl, int level) +{ + SSL_ASSERT3(ssl); + + ssl->cert->sec_level = level; +} + +/** + * @brief get the SSL security level + */ +int SSL_get_security_level(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + return ssl->cert->sec_level; +} + +/** + * @brief get the SSL verifying mode of the SSL context + */ +int SSL_CTX_get_verify_mode(const SSL_CTX *ctx) +{ + SSL_ASSERT1(ctx); + + return ctx->verify_mode; +} + +/** + * @brief set the session timeout time + */ +long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) +{ + long l; + + SSL_ASSERT1(ctx); + + l = ctx->session_timeout; + ctx->session_timeout = t; + + return l; +} + +/** + * @brief get the session timeout time + */ +long SSL_CTX_get_timeout(const SSL_CTX *ctx) +{ + SSL_ASSERT1(ctx); + + return ctx->session_timeout; +} + +/** + * @brief set the SSL if we can read as many as data + */ +void SSL_set_read_ahead(SSL *ssl, int yes) +{ + SSL_ASSERT3(ssl); + + ssl->rlayer.read_ahead = yes; +} + +/** + * @brief set the SSL context if we can read as many as data + */ +void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) +{ + SSL_ASSERT3(ctx); + + ctx->read_ahead = yes; +} + +/** + * @brief get the SSL ahead signal if we can read as many as data + */ +int SSL_get_read_ahead(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + return ssl->rlayer.read_ahead; +} + +/** + * @brief get the SSL context ahead signal if we can read as many as data + */ +long SSL_CTX_get_read_ahead(SSL_CTX *ctx) +{ + SSL_ASSERT1(ctx); + + return ctx->read_ahead; +} + +/** + * @brief check if the SSL context can read as many as data + */ +long SSL_CTX_get_default_read_ahead(SSL_CTX *ctx) +{ + SSL_ASSERT1(ctx); + + return ctx->read_ahead; +} + +/** + * @brief set SSL session time + */ +long SSL_set_time(SSL *ssl, long t) +{ + SSL_ASSERT1(ssl); + + ssl->session->time = t; + + return t; +} + +/** + * @brief set SSL session timeout time + */ +long SSL_set_timeout(SSL *ssl, long t) +{ + SSL_ASSERT1(ssl); + + ssl->session->timeout = t; + + return t; +} + +/** + * @brief get the verifying result of the SSL certification + */ +long SSL_get_verify_result(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + return SSL_METHOD_CALL(get_verify_result, ssl); +} + +/** + * @brief get the SSL verifying depth of the SSL context + */ +int SSL_CTX_get_verify_depth(const SSL_CTX *ctx) +{ + SSL_ASSERT1(ctx); + + return ctx->param.depth; +} + +/** + * @brief set the SSL verify depth of the SSL context + */ +void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) +{ + SSL_ASSERT3(ctx); + + ctx->param.depth = depth; +} + +/** + * @brief get the SSL verifying depth of the SSL + */ +int SSL_get_verify_depth(const SSL *ssl) +{ + SSL_ASSERT1(ssl); + + return ssl->param.depth; +} + +/** + * @brief set the SSL verify depth of the SSL + */ +void SSL_set_verify_depth(SSL *ssl, int depth) +{ + SSL_ASSERT3(ssl); + + ssl->param.depth = depth; +} + +/** + * @brief set the SSL context verifying of the SSL context + */ +void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) +{ + SSL_ASSERT3(ctx); + + ctx->verify_mode = mode; + ctx->default_verify_callback = verify_callback; +} + +/** + * @brief set the SSL verifying of the SSL context + */ +void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) +{ + SSL_ASSERT3(ssl); + + ssl->verify_mode = mode; + ssl->verify_callback = verify_callback; +} + +void ERR_error_string_n(unsigned long e, char *buf, size_t len) +{ + strncpy(buf, "unknown", len); +} + +void ERR_free_strings(void) +{ +} + +char *ERR_error_string(unsigned long e, char *buf) +{ + if (buf) { + strcpy(buf, "unknown"); + } + + return "unknown"; +} + +void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) +{ + return NULL; +} + +/* + * Openssl wants the valid protocol names supplied like this: + * + * (unsigned char *)"\x02h2\x08http/1.1", 6 + 9 + * + * Mbedtls wants this: + * + * Pointer to a NULL-terminated list of supported protocols, in decreasing + * preference order. The pointer to the list is recorded by the library for + * later reference as required, so the lifetime of the table must be at least + * as long as the lifetime of the SSL configuration structure. + * + * So accept the OpenSSL style and convert to mbedtls style + */ + +struct alpn_ctx { + unsigned char *data; + unsigned short len; +}; + +void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg) +{ + struct alpn_ctx *ac = arg; + unsigned char *p = ac->data, *q; + unsigned char len; + int count = 0; + + /* find out how many entries he gave us */ + + len = *p++; + while (p - ac->data < ac->len) { + if (len--) { + p++; + continue; + } + count++; + len = *p++; + if (!len) + break; + } + + if (!count) + return; + + /* allocate space for count + 1 pointers and the data afterwards */ + + ctx->alpn_protos = ssl_mem_zalloc((count + 1) * sizeof(char *) + ac->len + 1); + if (!ctx->alpn_protos) + return; + + /* convert to mbedtls format */ + + q = (unsigned char *)ctx->alpn_protos + (count + 1) * sizeof(char *); + p = ac->data; + count = 0; + + len = *p++; + ctx->alpn_protos[count] = (char *)q; + while (p - ac->data < ac->len) { + if (len--) { + *q++ = *p++; + continue; + } + *q++ = '\0'; + count++; + len = *p++; + ctx->alpn_protos[count] = (char *)q; + if (!len) + break; + } + ctx->alpn_protos[count] = NULL; /* last pointer ends list with NULL */ + + ctx->alpn_cb = cb; +} diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_methods.c libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_methods.c --- libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_methods.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_methods.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,81 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_methods.h" +#include "ssl_pm.h" + +/** + * TLS method function collection + */ +IMPLEMENT_TLS_METHOD_FUNC(TLS_method_func, + ssl_pm_new, ssl_pm_free, + ssl_pm_handshake, ssl_pm_shutdown, ssl_pm_clear, + ssl_pm_read, ssl_pm_send, ssl_pm_pending, + ssl_pm_set_fd, ssl_pm_get_fd, + ssl_pm_set_bufflen, + ssl_pm_get_verify_result, + ssl_pm_get_state); + +/** + * TLS or SSL client method collection + */ +IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, 0, TLS_method_func, TLS_client_method); + +IMPLEMENT_TLS_METHOD(TLS1_2_VERSION, 0, TLS_method_func, TLSv1_2_client_method); + +IMPLEMENT_TLS_METHOD(TLS1_1_VERSION, 0, TLS_method_func, TLSv1_1_client_method); + +IMPLEMENT_TLS_METHOD(TLS1_VERSION, 0, TLS_method_func, TLSv1_client_method); + +IMPLEMENT_SSL_METHOD(SSL3_VERSION, 0, TLS_method_func, SSLv3_client_method); + +/** + * TLS or SSL server method collection + */ +IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, 1, TLS_method_func, TLS_server_method); + +IMPLEMENT_TLS_METHOD(TLS1_1_VERSION, 1, TLS_method_func, TLSv1_1_server_method); + +IMPLEMENT_TLS_METHOD(TLS1_2_VERSION, 1, TLS_method_func, TLSv1_2_server_method); + +IMPLEMENT_TLS_METHOD(TLS1_VERSION, 0, TLS_method_func, TLSv1_server_method); + +IMPLEMENT_SSL_METHOD(SSL3_VERSION, 1, TLS_method_func, SSLv3_server_method); + +/** + * TLS or SSL method collection + */ +IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, -1, TLS_method_func, TLS_method); + +IMPLEMENT_SSL_METHOD(TLS1_2_VERSION, -1, TLS_method_func, TLSv1_2_method); + +IMPLEMENT_SSL_METHOD(TLS1_1_VERSION, -1, TLS_method_func, TLSv1_1_method); + +IMPLEMENT_SSL_METHOD(TLS1_VERSION, -1, TLS_method_func, TLSv1_method); + +IMPLEMENT_SSL_METHOD(SSL3_VERSION, -1, TLS_method_func, SSLv3_method); + +/** + * @brief get X509 object method + */ +IMPLEMENT_X509_METHOD(X509_method, + x509_pm_new, x509_pm_free, + x509_pm_load, x509_pm_show_info); + +/** + * @brief get private key object method + */ +IMPLEMENT_PKEY_METHOD(EVP_PKEY_method, + pkey_pm_new, pkey_pm_free, + pkey_pm_load); diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_pkey.c libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_pkey.c --- libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_pkey.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_pkey.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,239 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_pkey.h" +#include "ssl_methods.h" +#include "ssl_dbg.h" +#include "ssl_port.h" + +/** + * @brief create a private key object according to input private key + */ +EVP_PKEY* __EVP_PKEY_new(EVP_PKEY *ipk) +{ + int ret; + EVP_PKEY *pkey; + + pkey = ssl_mem_zalloc(sizeof(EVP_PKEY)); + if (!pkey) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "no enough memory > (pkey)"); + goto no_mem; + } + + if (ipk) { + pkey->method = ipk->method; + } else { + pkey->method = EVP_PKEY_method(); + } + + ret = EVP_PKEY_METHOD_CALL(new, pkey, ipk); + if (ret) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "EVP_PKEY_METHOD_CALL(new) return %d", ret); + goto failed; + } + + return pkey; + +failed: + ssl_mem_free(pkey); +no_mem: + return NULL; +} + +/** + * @brief create a private key object + */ +EVP_PKEY* EVP_PKEY_new(void) +{ + return __EVP_PKEY_new(NULL); +} + +/** + * @brief free a private key object + */ +void EVP_PKEY_free(EVP_PKEY *pkey) +{ + SSL_ASSERT3(pkey); + + EVP_PKEY_METHOD_CALL(free, pkey); + + ssl_mem_free(pkey); +} + +/** + * @brief load a character key context into system context. If '*a' is pointed to the + * private key, then load key into it. Or create a new private key object + */ +EVP_PKEY *d2i_PrivateKey(int type, + EVP_PKEY **a, + const unsigned char **pp, + long length) +{ + int m = 0; + int ret; + EVP_PKEY *pkey; + + SSL_ASSERT2(pp); + SSL_ASSERT2(*pp); + SSL_ASSERT2(length); + + if (a && *a) { + pkey = *a; + } else { + pkey = EVP_PKEY_new();; + if (!pkey) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "EVP_PKEY_new() return NULL"); + goto failed1; + } + + m = 1; + } + + ret = EVP_PKEY_METHOD_CALL(load, pkey, *pp, length); + if (ret) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "EVP_PKEY_METHOD_CALL(load) return %d", ret); + goto failed2; + } + + if (a) + *a = pkey; + + return pkey; + +failed2: + if (m) + EVP_PKEY_free(pkey); +failed1: + return NULL; +} + +/** + * @brief set the SSL context private key + */ +int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) +{ + SSL_ASSERT1(ctx); + SSL_ASSERT1(pkey); + + if (ctx->cert->pkey == pkey) + return 1; + + if (ctx->cert->pkey) + EVP_PKEY_free(ctx->cert->pkey); + + ctx->cert->pkey = pkey; + + return 1; +} + +/** + * @brief set the SSL private key + */ +int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) +{ + SSL_ASSERT1(ssl); + SSL_ASSERT1(pkey); + + if (ssl->cert->pkey == pkey) + return 1; + + if (ssl->cert->pkey) + EVP_PKEY_free(ssl->cert->pkey); + + ssl->cert->pkey = pkey; + + return 1; +} + +/** + * @brief load private key into the SSL context + */ +int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, + const unsigned char *d, long len) +{ + int ret; + EVP_PKEY *pk; + + pk = d2i_PrivateKey(0, NULL, &d, len); + if (!pk) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_PrivateKey() return NULL"); + goto failed1; + } + + ret = SSL_CTX_use_PrivateKey(ctx, pk); + if (!ret) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "SSL_CTX_use_PrivateKey() return %d", ret); + goto failed2; + } + + return 1; + +failed2: + EVP_PKEY_free(pk); +failed1: + return 0; +} + +/** + * @brief load private key into the SSL + */ +int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, + const unsigned char *d, long len) +{ + int ret; + EVP_PKEY *pk; + + pk = d2i_PrivateKey(0, NULL, &d, len); + if (!pk) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_PrivateKey() return NULL"); + goto failed1; + } + + ret = SSL_use_PrivateKey(ssl, pk); + if (!ret) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "SSL_use_PrivateKey() return %d", ret); + goto failed2; + } + + return 1; + +failed2: + EVP_PKEY_free(pk); +failed1: + return 0; +} + +/** + * @brief load the private key file into SSL context + */ +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) +{ + return 0; +} + +/** + * @brief load the private key file into SSL + */ +int SSL_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) +{ + return 0; +} + +/** + * @brief load the RSA ASN1 private key into SSL context + */ +int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len) +{ + return SSL_CTX_use_PrivateKey_ASN1(0, ctx, d, len); +} diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_stack.c libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_stack.c --- libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_stack.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_stack.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,74 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_stack.h" +#include "ssl_dbg.h" +#include "ssl_port.h" + +#ifndef CONFIG_MIN_NODES + #define MIN_NODES 4 +#else + #define MIN_NODES CONFIG_MIN_NODES +#endif + +/** + * @brief create a openssl stack object + */ +OPENSSL_STACK* OPENSSL_sk_new(OPENSSL_sk_compfunc c) +{ + OPENSSL_STACK *stack; + char **data; + + stack = ssl_mem_zalloc(sizeof(OPENSSL_STACK)); + if (!stack) { + SSL_DEBUG(SSL_STACK_ERROR_LEVEL, "no enough memory > (stack)"); + goto no_mem1; + } + + data = ssl_mem_zalloc(sizeof(*data) * MIN_NODES); + if (!data) { + SSL_DEBUG(SSL_STACK_ERROR_LEVEL, "no enough memory > (data)"); + goto no_mem2; + } + + stack->data = data; + stack->num_alloc = MIN_NODES; + stack->c = c; + + return stack; + +no_mem2: + ssl_mem_free(stack); +no_mem1: + return NULL; +} + +/** + * @brief create a NULL function openssl stack object + */ +OPENSSL_STACK *OPENSSL_sk_new_null(void) +{ + return OPENSSL_sk_new((OPENSSL_sk_compfunc)NULL); +} + +/** + * @brief free openssl stack object + */ +void OPENSSL_sk_free(OPENSSL_STACK *stack) +{ + SSL_ASSERT3(stack); + + ssl_mem_free(stack->data); + ssl_mem_free(stack); +} diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_x509.c libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_x509.c --- libwebsockets-4.0.20/lib/mbedtls_wrapper/library/ssl_x509.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/library/ssl_x509.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,330 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_x509.h" +#include "ssl_methods.h" +#include "ssl_dbg.h" +#include "ssl_port.h" + +/** + * @brief show X509 certification information + */ +int __X509_show_info(X509 *x) +{ + return X509_METHOD_CALL(show_info, x); +} + +/** + * @brief create a X509 certification object according to input X509 certification + */ +X509* __X509_new(X509 *ix) +{ + int ret; + X509 *x; + + x = ssl_mem_zalloc(sizeof(X509)); + if (!x) { + SSL_DEBUG(SSL_X509_ERROR_LEVEL, "no enough memory > (x)"); + goto no_mem; + } + + if (ix) + x->method = ix->method; + else + x->method = X509_method(); + + ret = X509_METHOD_CALL(new, x, ix); + if (ret) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "X509_METHOD_CALL(new) return %d", ret); + goto failed; + } + + return x; + +failed: + ssl_mem_free(x); +no_mem: + return NULL; +} + +/** + * @brief create a X509 certification object + */ +X509* X509_new(void) +{ + return __X509_new(NULL); +} + +/** + * @brief free a X509 certification object + */ +void X509_free(X509 *x) +{ + SSL_ASSERT3(x); + + X509_METHOD_CALL(free, x); + + ssl_mem_free(x); +}; + +/** + * @brief load a character certification context into system context. If '*cert' is pointed to the + * certification, then load certification into it. Or create a new X509 certification object + */ +X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) +{ + int m = 0; + int ret; + X509 *x; + + SSL_ASSERT2(buffer); + SSL_ASSERT2(len); + + if (cert && *cert) { + x = *cert; + } else { + x = X509_new(); + if (!x) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "X509_new() return NULL"); + goto failed1; + } + m = 1; + } + + ret = X509_METHOD_CALL(load, x, buffer, len); + if (ret) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "X509_METHOD_CALL(load) return %d", ret); + goto failed2; + } + + return x; + +failed2: + if (m) + X509_free(x); +failed1: + return NULL; +} + +/** + * @brief return SSL X509 verify parameters + */ + +X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl) +{ + return &ssl->param; +} + +/** + * @brief set X509 host verification flags + */ + +int X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param, + unsigned long flags) +{ + /* flags not supported yet */ + return 0; +} + +/** + * @brief clear X509 host verification flags + */ + +int X509_VERIFY_PARAM_clear_hostflags(X509_VERIFY_PARAM *param, + unsigned long flags) +{ + /* flags not supported yet */ + return 0; +} + +/** + * @brief set SSL context client CA certification + */ +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) +{ + SSL_ASSERT1(ctx); + SSL_ASSERT1(x); + + if (ctx->client_CA == x) + return 1; + + X509_free(ctx->client_CA); + + ctx->client_CA = x; + + return 1; +} + +/** + * @brief add CA client certification into the SSL + */ +int SSL_add_client_CA(SSL *ssl, X509 *x) +{ + SSL_ASSERT1(ssl); + SSL_ASSERT1(x); + + if (ssl->client_CA == x) + return 1; + + X509_free(ssl->client_CA); + + ssl->client_CA = x; + + return 1; +} + +/** + * @brief set the SSL context certification + */ +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) +{ + SSL_ASSERT1(ctx); + SSL_ASSERT1(x); + + if (ctx->cert->x509 == x) + return 1; + + X509_free(ctx->cert->x509); + + ctx->cert->x509 = x; + + return 1; +} + +/** + * @brief set the SSL certification + */ +int SSL_use_certificate(SSL *ssl, X509 *x) +{ + SSL_ASSERT1(ssl); + SSL_ASSERT1(x); + + if (ssl->cert->x509 == x) + return 1; + + X509_free(ssl->cert->x509); + + ssl->cert->x509 = x; + + return 1; +} + +/** + * @brief get the SSL certification point + */ +X509 *SSL_get_certificate(const SSL *ssl) +{ + SSL_ASSERT2(ssl); + + return ssl->cert->x509; +} + +/** + * @brief load certification into the SSL context + */ +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, + const unsigned char *d) +{ + int ret; + X509 *x; + + x = d2i_X509(NULL, d, len); + if (!x) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_X509() return NULL"); + goto failed1; + } + + ret = SSL_CTX_use_certificate(ctx, x); + if (!ret) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "SSL_CTX_use_certificate() return %d", ret); + goto failed2; + } + + return 1; + +failed2: + X509_free(x); +failed1: + return 0; +} + +/** + * @brief load certification into the SSL + */ +int SSL_use_certificate_ASN1(SSL *ssl, int len, + const unsigned char *d) +{ + int ret; + X509 *x; + + x = d2i_X509(NULL, d, len); + if (!x) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_X509() return NULL"); + goto failed1; + } + + ret = SSL_use_certificate(ssl, x); + if (!ret) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "SSL_use_certificate() return %d", ret); + goto failed2; + } + + return 1; + +failed2: + X509_free(x); +failed1: + return 0; +} + +/** + * @brief load the certification file into SSL context + */ +int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) +{ + return 0; +} + +/** + * @brief load the certification file into SSL + */ +int SSL_use_certificate_file(SSL *ssl, const char *file, int type) +{ + return 0; +} + +/** + * @brief get peer certification + */ +X509 *SSL_get_peer_certificate(const SSL *ssl) +{ + SSL_ASSERT2(ssl); + + return ssl->session->peer; +} + +int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx) +{ + return X509_V_ERR_UNSPECIFIED; +} + +int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx) +{ + return 0; +} + +const char *X509_verify_cert_error_string(long n) +{ + return "unknown"; +} diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/platform/ssl_pm.c libwebsockets-2.4.2/lib/mbedtls_wrapper/platform/ssl_pm.c --- libwebsockets-4.0.20/lib/mbedtls_wrapper/platform/ssl_pm.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/platform/ssl_pm.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,771 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_pm.h" +#include "ssl_port.h" +#include "ssl_dbg.h" + +/* mbedtls include */ +#include "mbedtls/platform.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/debug.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/certs.h" + +#define X509_INFO_STRING_LENGTH 8192 + +struct ssl_pm +{ + /* local socket file description */ + mbedtls_net_context fd; + /* remote client socket file description */ + mbedtls_net_context cl_fd; + + mbedtls_ssl_config conf; + + mbedtls_ctr_drbg_context ctr_drbg; + + mbedtls_ssl_context ssl; + + mbedtls_entropy_context entropy; +}; + +struct x509_pm +{ + mbedtls_x509_crt *x509_crt; + + mbedtls_x509_crt *ex_crt; +}; + +struct pkey_pm +{ + mbedtls_pk_context *pkey; + + mbedtls_pk_context *ex_pkey; +}; + +unsigned int max_content_len; + +/*********************************************************************************************/ +/************************************ SSL arch interface *************************************/ + +#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG + +/* mbedtls debug level */ +#define MBEDTLS_DEBUG_LEVEL 4 + +/** + * @brief mbedtls debug function + */ +static void ssl_platform_debug(void *ctx, int level, + const char *file, int line, + const char *str) +{ + /* Shorten 'file' from the whole file path to just the filename + + This is a bit wasteful because the macros are compiled in with + the full _FILE_ path in each case. + */ + char *file_sep = rindex(file, '/'); + if(file_sep) + file = file_sep + 1; + + SSL_DEBUG(SSL_DEBUG_ON, "%s:%d %s", file, line, str); +} +#endif + +/** + * @brief create SSL low-level object + */ +int ssl_pm_new(SSL *ssl) +{ + struct ssl_pm *ssl_pm; + int ret; + + const unsigned char pers[] = "OpenSSL PM"; + size_t pers_len = sizeof(pers); + + int endpoint; + int version; + + const SSL_METHOD *method = ssl->method; + + ssl_pm = ssl_mem_zalloc(sizeof(struct ssl_pm)); + if (!ssl_pm) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (ssl_pm)"); + goto no_mem; + } + + if (!ssl->ctx->read_buffer_len) + ssl->ctx->read_buffer_len = 2048; + + max_content_len = ssl->ctx->read_buffer_len; + // printf("ssl->ctx->read_buffer_len = %d ++++++++++++++++++++\n", ssl->ctx->read_buffer_len); + + mbedtls_net_init(&ssl_pm->fd); + mbedtls_net_init(&ssl_pm->cl_fd); + + mbedtls_ssl_config_init(&ssl_pm->conf); + mbedtls_ctr_drbg_init(&ssl_pm->ctr_drbg); + mbedtls_entropy_init(&ssl_pm->entropy); + mbedtls_ssl_init(&ssl_pm->ssl); + + ret = mbedtls_ctr_drbg_seed(&ssl_pm->ctr_drbg, mbedtls_entropy_func, &ssl_pm->entropy, pers, pers_len); + if (ret) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ctr_drbg_seed() return -0x%x", -ret); + goto mbedtls_err1; + } + + if (method->endpoint) { + endpoint = MBEDTLS_SSL_IS_SERVER; + } else { + endpoint = MBEDTLS_SSL_IS_CLIENT; + } + ret = mbedtls_ssl_config_defaults(&ssl_pm->conf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + if (ret) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_config_defaults() return -0x%x", -ret); + goto mbedtls_err2; + } + + if (TLS_ANY_VERSION != ssl->version) { + if (TLS1_2_VERSION == ssl->version) + version = MBEDTLS_SSL_MINOR_VERSION_3; + else if (TLS1_1_VERSION == ssl->version) + version = MBEDTLS_SSL_MINOR_VERSION_2; + else if (TLS1_VERSION == ssl->version) + version = MBEDTLS_SSL_MINOR_VERSION_1; + else + version = MBEDTLS_SSL_MINOR_VERSION_0; + + mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); + mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); + } else { + mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); + mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); + } + + mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); + +#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG + mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL); + mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL); +#else + mbedtls_ssl_conf_dbg(&ssl_pm->conf, NULL, NULL); +#endif + + ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf); + if (ret) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_setup() return -0x%x", -ret); + goto mbedtls_err2; + } + + mbedtls_ssl_set_bio(&ssl_pm->ssl, &ssl_pm->fd, mbedtls_net_send, mbedtls_net_recv, NULL); + + ssl->ssl_pm = ssl_pm; + + return 0; + +mbedtls_err2: + mbedtls_ssl_config_free(&ssl_pm->conf); + mbedtls_ctr_drbg_free(&ssl_pm->ctr_drbg); +mbedtls_err1: + mbedtls_entropy_free(&ssl_pm->entropy); + ssl_mem_free(ssl_pm); +no_mem: + return -1; +} + +/** + * @brief free SSL low-level object + */ +void ssl_pm_free(SSL *ssl) +{ + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + mbedtls_ctr_drbg_free(&ssl_pm->ctr_drbg); + mbedtls_entropy_free(&ssl_pm->entropy); + mbedtls_ssl_config_free(&ssl_pm->conf); + mbedtls_ssl_free(&ssl_pm->ssl); + + ssl_mem_free(ssl_pm); + ssl->ssl_pm = NULL; +} + +/** + * @brief reload SSL low-level certification object + */ +static int ssl_pm_reload_crt(SSL *ssl) +{ + int ret; + int mode; + struct ssl_pm *ssl_pm = ssl->ssl_pm; + struct x509_pm *ca_pm = (struct x509_pm *)ssl->client_CA->x509_pm; + + struct pkey_pm *pkey_pm = (struct pkey_pm *)ssl->cert->pkey->pkey_pm; + struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm; + + if (ssl->verify_mode == SSL_VERIFY_PEER) + mode = MBEDTLS_SSL_VERIFY_REQUIRED; + else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT) + mode = MBEDTLS_SSL_VERIFY_OPTIONAL; + else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE) + mode = MBEDTLS_SSL_VERIFY_UNSET; + else + mode = MBEDTLS_SSL_VERIFY_NONE; + + mbedtls_ssl_conf_authmode(&ssl_pm->conf, mode); + + if (ca_pm->x509_crt) { + mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->x509_crt, NULL); + } else if (ca_pm->ex_crt) { + mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->ex_crt, NULL); + } + + if (crt_pm->x509_crt && pkey_pm->pkey) { + ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, crt_pm->x509_crt, pkey_pm->pkey); + } else if (crt_pm->ex_crt && pkey_pm->ex_pkey) { + ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, crt_pm->ex_crt, pkey_pm->ex_pkey); + } else { + ret = 0; + } + + if (ret) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_conf_own_cert() return -0x%x", -ret); + ret = -1; + } + + return ret; +} + +/* + * Perform the mbedtls SSL handshake instead of mbedtls_ssl_handshake. + * We can add debug here. + */ +static int mbedtls_handshake( mbedtls_ssl_context *ssl ) +{ + int ret = 0; + + while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) { + ret = mbedtls_ssl_handshake_step(ssl); + + SSL_DEBUG(SSL_PLATFORM_DEBUG_LEVEL, "ssl ret %d state %d", ret, ssl->state); + + if (ret != 0) + break; + } + + return ret; +} + +int ssl_pm_handshake(SSL *ssl) +{ + int ret; + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + ret = ssl_pm_reload_crt(ssl); + if (ret) + return 0; + + if (ssl_pm->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) { + ssl_speed_up_enter(); + + /* mbedtls return codes + * 0 = successful, or MBEDTLS_ERR_SSL_WANT_READ/WRITE + * anything else = death + */ + ret = mbedtls_handshake(&ssl_pm->ssl); + ssl_speed_up_exit(); + } else + ret = 0; + + /* + * OpenSSL return codes: + * 0 = did not complete, but may be retried + * 1 = successfully completed + * <0 = death + */ + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_handshake() return -0x%x", -ret); + return 0; /* OpenSSL: did not complete but may be retried */ + } + + if (ret == 0) { /* successful */ + struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm; + + x509_pm->ex_crt = (mbedtls_x509_crt *)mbedtls_ssl_get_peer_cert(&ssl_pm->ssl); + return 1; /* openssl successful */ + } + + /* it's had it */ + + ssl->err = SSL_ERROR_SYSCALL; + + return -1; /* openssl death */ +} + +int ssl_pm_shutdown(SSL *ssl) +{ + int ret; + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + ret = mbedtls_ssl_close_notify(&ssl_pm->ssl); + if (ret) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_close_notify() return -0x%x", -ret); + if (ret == MBEDTLS_ERR_NET_CONN_RESET) + ssl->err = SSL_ERROR_SYSCALL; + ret = -1; /* OpenSSL: "Call SSL_get_error with the return value to find the reason */ + } else { + struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm; + + x509_pm->ex_crt = NULL; + ret = 1; /* OpenSSL: "The shutdown was successfully completed" + ...0 means retry */ + } + + return ret; +} + +int ssl_pm_clear(SSL *ssl) +{ + return ssl_pm_shutdown(ssl); +} + + +int ssl_pm_read(SSL *ssl, void *buffer, int len) +{ + int ret; + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + ret = mbedtls_ssl_read(&ssl_pm->ssl, buffer, len); + if (ret < 0) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_read() return -0x%x", -ret); + if (ret == MBEDTLS_ERR_NET_CONN_RESET) + ssl->err = SSL_ERROR_SYSCALL; + ret = -1; + } + + return ret; +} + +/* + * This returns -1, or the length sent. + * If -1, then you need to find out if the error was + * fatal or recoverable using SSL_get_error() + */ +int ssl_pm_send(SSL *ssl, const void *buffer, int len) +{ + int ret; + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + ret = mbedtls_ssl_write(&ssl_pm->ssl, buffer, len); + /* + * We can get a positive number, which may be less than len... that + * much was sent successfully and you can call again to send more. + * + * We can get a negative mbedtls error code... if WANT_WRITE or WANT_READ, + * it's nonfatal and means it should be retried as-is. If something else, + * it's fatal actually. + * + * If this function returns something other than a positive value or + * MBEDTLS_ERR_SSL_WANT_READ/WRITE, the ssl context becomes unusable, and + * you should either free it or call mbedtls_ssl_session_reset() on it + * before re-using it for a new connection; the current connection must + * be closed. + * + * When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ, it must be + * called later with the same arguments, until it returns a positive value. + */ + + if (ret < 0) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret); + switch (ret) { + case MBEDTLS_ERR_NET_CONN_RESET: + ssl->err = SSL_ERROR_SYSCALL; + break; + case MBEDTLS_ERR_SSL_WANT_WRITE: + ssl->err = SSL_ERROR_WANT_WRITE; + break; + case MBEDTLS_ERR_SSL_WANT_READ: + ssl->err = SSL_ERROR_WANT_READ; + break; + default: + break; + } + + ret = -1; + } + + return ret; +} + +int ssl_pm_pending(const SSL *ssl) +{ + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + return mbedtls_ssl_get_bytes_avail(&ssl_pm->ssl); +} + +void ssl_pm_set_fd(SSL *ssl, int fd, int mode) +{ + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + ssl_pm->fd.fd = fd; +} + +int ssl_pm_get_fd(const SSL *ssl, int mode) +{ + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + return ssl_pm->fd.fd; +} + +OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl) +{ + OSSL_HANDSHAKE_STATE state; + + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + switch (ssl_pm->ssl.state) + { + case MBEDTLS_SSL_CLIENT_HELLO: + state = TLS_ST_CW_CLNT_HELLO; + break; + case MBEDTLS_SSL_SERVER_HELLO: + state = TLS_ST_SW_SRVR_HELLO; + break; + case MBEDTLS_SSL_SERVER_CERTIFICATE: + state = TLS_ST_SW_CERT; + break; + case MBEDTLS_SSL_SERVER_HELLO_DONE: + state = TLS_ST_SW_SRVR_DONE; + break; + case MBEDTLS_SSL_CLIENT_KEY_EXCHANGE: + state = TLS_ST_CW_KEY_EXCH; + break; + case MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC: + state = TLS_ST_CW_CHANGE; + break; + case MBEDTLS_SSL_CLIENT_FINISHED: + state = TLS_ST_CW_FINISHED; + break; + case MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC: + state = TLS_ST_SW_CHANGE; + break; + case MBEDTLS_SSL_SERVER_FINISHED: + state = TLS_ST_SW_FINISHED; + break; + case MBEDTLS_SSL_CLIENT_CERTIFICATE: + state = TLS_ST_CW_CERT; + break; + case MBEDTLS_SSL_SERVER_KEY_EXCHANGE: + state = TLS_ST_SR_KEY_EXCH; + break; + case MBEDTLS_SSL_SERVER_NEW_SESSION_TICKET: + state = TLS_ST_SW_SESSION_TICKET; + break; + case MBEDTLS_SSL_SERVER_HELLO_VERIFY_REQUEST_SENT: + state = TLS_ST_SW_CERT_REQ; + break; + case MBEDTLS_SSL_HANDSHAKE_OVER: + state = TLS_ST_OK; + break; + default : + state = TLS_ST_BEFORE; + break; + } + + return state; +} + +int x509_pm_show_info(X509 *x) +{ + int ret; + char *buf; + mbedtls_x509_crt *x509_crt; + struct x509_pm *x509_pm = x->x509_pm; + + if (x509_pm->x509_crt) + x509_crt = x509_pm->x509_crt; + else if (x509_pm->ex_crt) + x509_crt = x509_pm->ex_crt; + else + x509_crt = NULL; + + if (!x509_crt) + return -1; + + buf = ssl_mem_malloc(X509_INFO_STRING_LENGTH); + if (!buf) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (buf)"); + goto no_mem; + } + + ret = mbedtls_x509_crt_info(buf, X509_INFO_STRING_LENGTH - 1, "", x509_crt); + if (ret <= 0) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_x509_crt_info() return -0x%x", -ret); + goto mbedtls_err1; + } + + buf[ret] = 0; + + ssl_mem_free(buf); + + SSL_DEBUG(SSL_DEBUG_ON, "%s", buf); + + return 0; + +mbedtls_err1: + ssl_mem_free(buf); +no_mem: + return -1; +} + +int x509_pm_new(X509 *x, X509 *m_x) +{ + struct x509_pm *x509_pm; + + x509_pm = ssl_mem_zalloc(sizeof(struct x509_pm)); + if (!x509_pm) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (x509_pm)"); + goto failed1; + } + + x->x509_pm = x509_pm; + + if (m_x) { + struct x509_pm *m_x509_pm = (struct x509_pm *)m_x->x509_pm; + + x509_pm->ex_crt = m_x509_pm->x509_crt; + } + + return 0; + +failed1: + return -1; +} + +void x509_pm_free(X509 *x) +{ + struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; + + if (x509_pm->x509_crt) { + mbedtls_x509_crt_free(x509_pm->x509_crt); + + ssl_mem_free(x509_pm->x509_crt); + x509_pm->x509_crt = NULL; + } + + ssl_mem_free(x->x509_pm); + x->x509_pm = NULL; +} + +int x509_pm_load(X509 *x, const unsigned char *buffer, int len) +{ + int ret; + unsigned char *load_buf; + struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; + + if (x509_pm->x509_crt) + mbedtls_x509_crt_free(x509_pm->x509_crt); + + if (!x509_pm->x509_crt) { + x509_pm->x509_crt = ssl_mem_malloc(sizeof(mbedtls_x509_crt)); + if (!x509_pm->x509_crt) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (x509_pm->x509_crt)"); + goto no_mem; + } + } + + load_buf = ssl_mem_malloc(len + 1); + if (!load_buf) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)"); + goto failed; + } + + ssl_memcpy(load_buf, buffer, len); + load_buf[len] = '\0'; + + mbedtls_x509_crt_init(x509_pm->x509_crt); + + ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1); + ssl_mem_free(load_buf); + + if (ret) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_x509_crt_parse return -0x%x", -ret); + goto failed; + } + + return 0; + +failed: + mbedtls_x509_crt_free(x509_pm->x509_crt); + ssl_mem_free(x509_pm->x509_crt); + x509_pm->x509_crt = NULL; +no_mem: + return -1; +} + +int pkey_pm_new(EVP_PKEY *pk, EVP_PKEY *m_pkey) +{ + struct pkey_pm *pkey_pm; + + pkey_pm = ssl_mem_zalloc(sizeof(struct pkey_pm)); + if (!pkey_pm) + return -1; + + pk->pkey_pm = pkey_pm; + + if (m_pkey) { + struct pkey_pm *m_pkey_pm = (struct pkey_pm *)m_pkey->pkey_pm; + + pkey_pm->ex_pkey = m_pkey_pm->pkey; + } + + return 0; +} + +void pkey_pm_free(EVP_PKEY *pk) +{ + struct pkey_pm *pkey_pm = (struct pkey_pm *)pk->pkey_pm; + + if (pkey_pm->pkey) { + mbedtls_pk_free(pkey_pm->pkey); + + ssl_mem_free(pkey_pm->pkey); + pkey_pm->pkey = NULL; + } + + ssl_mem_free(pk->pkey_pm); + pk->pkey_pm = NULL; +} + +int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) +{ + int ret; + unsigned char *load_buf; + struct pkey_pm *pkey_pm = (struct pkey_pm *)pk->pkey_pm; + + if (pkey_pm->pkey) + mbedtls_pk_free(pkey_pm->pkey); + + if (!pkey_pm->pkey) { + pkey_pm->pkey = ssl_mem_malloc(sizeof(mbedtls_pk_context)); + if (!pkey_pm->pkey) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (pkey_pm->pkey)"); + goto no_mem; + } + } + + load_buf = ssl_mem_malloc(len + 1); + if (!load_buf) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)"); + goto failed; + } + + ssl_memcpy(load_buf, buffer, len); + load_buf[len] = '\0'; + + mbedtls_pk_init(pkey_pm->pkey); + + ret = mbedtls_pk_parse_key(pkey_pm->pkey, load_buf, len + 1, NULL, 0); + ssl_mem_free(load_buf); + + if (ret) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_pk_parse_key return -0x%x", -ret); + goto failed; + } + + return 0; + +failed: + mbedtls_pk_free(pkey_pm->pkey); + ssl_mem_free(pkey_pm->pkey); + pkey_pm->pkey = NULL; +no_mem: + return -1; +} + + + +void ssl_pm_set_bufflen(SSL *ssl, int len) +{ + max_content_len = len; +} + +long ssl_pm_get_verify_result(const SSL *ssl) +{ + uint32_t ret; + long verify_result; + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl); + if (ret) { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret); + verify_result = X509_V_ERR_UNSPECIFIED; + } else + verify_result = X509_V_OK; + + return verify_result; +} + +/** + * @brief set expected hostname on peer cert CN + */ + +int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, + const char *name, size_t namelen) +{ + SSL *ssl = (SSL *)((char *)param - offsetof(SSL, param)); + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + char *name_cstr = NULL; + + if (namelen) { + name_cstr = malloc(namelen + 1); + if (!name_cstr) + return 0; + memcpy(name_cstr, name, namelen); + name_cstr[namelen] = '\0'; + name = name_cstr; + } + + mbedtls_ssl_set_hostname(&ssl_pm->ssl, name); + + if (namelen) + free(name_cstr); + + return 1; +} + +void _ssl_set_alpn_list(const SSL *ssl) +{ + if (!ssl->ctx->alpn_protos) + return; + if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->ctx->alpn_protos)) + fprintf(stderr, "mbedtls_ssl_conf_alpn_protocols failed\n"); +} + +void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, + unsigned int *len) +{ + const char *alp = mbedtls_ssl_get_alpn_protocol(&((struct ssl_pm *)(ssl->ssl_pm))->ssl); + + *data = (const unsigned char *)alp; + if (alp) + *len = strlen(alp); + else + *len = 0; +} + diff -Nru libwebsockets-4.0.20/lib/mbedtls_wrapper/platform/ssl_port.c libwebsockets-2.4.2/lib/mbedtls_wrapper/platform/ssl_port.c --- libwebsockets-4.0.20/lib/mbedtls_wrapper/platform/ssl_port.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/mbedtls_wrapper/platform/ssl_port.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,29 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_port.h" + +/*********************************************************************************************/ +/********************************* SSL general interface *************************************/ + +void *ssl_mem_zalloc(size_t size) +{ + void *p = malloc(size); + + if (p) + memset(p, 0, size); + + return p; +} + diff -Nru libwebsockets-4.0.20/lib/minilex.c libwebsockets-2.4.2/lib/minilex.c --- libwebsockets-4.0.20/lib/minilex.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/minilex.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,272 @@ +/* + * minilex.c + * + * High efficiency lexical state parser + * + * Copyright (C)2011-2014 Andy Green + * + * Licensed under LGPL2 + * + * Usage: gcc minilex.c -o minilex && ./minilex > lextable.h + * + * Run it twice to test parsing on the generated table on stderr + */ + +#include +#include +#include + +#include "lextable-strings.h" + +/* + * 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[] = { + #include "lextable.h" +}; + +#define PARALLEL 30 + +struct state { + char c[PARALLEL]; + int state[PARALLEL]; + int count; + int bytepos; + + int real_pos; +}; + +struct state state[1000]; +int next = 1; + +#define FAIL_CHAR 0x08 + +int lextable_decode(int pos, char c) +{ + while (1) { + if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */ + if ((lextable[pos] & 0x7f) != c) + return -1; + /* fall thru */ + pos++; + if (lextable[pos] == FAIL_CHAR) + return -1; + return pos; + } else { /* b7 = 0, end or 3-byte */ + if (lextable[pos] < FAIL_CHAR) /* terminal marker */ + return pos; + + if (lextable[pos] == c) /* goto */ + return pos + (lextable[pos + 1]) + + (lextable[pos + 2] << 8); + /* fall thru goto */ + pos += 3; + /* continue */ + } + } +} + +int main(void) +{ + int n = 0; + int m = 0; + int prev; + char c; + int walk; + int saw; + int y; + int j; + int pos = 0; + + while (n < sizeof(set) / sizeof(set[0])) { + + m = 0; + walk = 0; + prev = 0; + + if (set[n][0] == '\0') { + n++; + continue; + } + + while (set[n][m]) { + + saw = 0; + for (y = 0; y < state[walk].count; y++) + if (state[walk].c[y] == set[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] = set[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); + + /* + * Try to parse every legal input string + */ + + for (n = 0; n < sizeof(set) / sizeof(set[0]); n++) { + walk = 0; + m = 0; + y = -1; + + if (set[n][0] == '\0') + continue; + + fprintf(stderr, " trying '%s'\n", set[n]); + + while (set[n][m]) { + walk = lextable_decode(walk, set[n][m]); + if (walk < 0) { + fprintf(stderr, "failed\n"); + return 3; + } + + if (lextable[walk] < FAIL_CHAR) { + y = (lextable[walk] << 8) + lextable[walk + 1]; + break; + } + m++; + } + + if (y != n) { + fprintf(stderr, "decode failed %d\n", y); + return 4; + } + } + + fprintf(stderr, "All decode OK\n"); + + return 0; +} diff -Nru libwebsockets-4.0.20/lib/misc/base64-decode.c libwebsockets-2.4.2/lib/misc/base64-decode.c --- libwebsockets-4.0.20/lib/misc/base64-decode.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/base64-decode.c 2018-03-08 10:28:37.000000000 +0000 @@ -3,7 +3,7 @@ * * http://base64.sourceforge.net/b64.c * - * already with MIT license, which is retained. + * with the following license: * * LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc. * @@ -33,31 +33,31 @@ * Bob Trower 08/04/01 -- Create Version 0.00.00B * * I cleaned it up quite a bit to match the (linux kernel) style of the rest - * of libwebsockets + * of libwebsockets; this version is under LGPL2.1 + SLE like the rest of lws + * since he explicitly allows sublicensing, but I give the URL above so you can + * get the original with Bob's super-liberal terms directly if you prefer. */ -#include - #include #include -#include "private-lib-core.h" +#include "private-libwebsockets.h" -static const char encode_orig[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +static const char encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char encode_url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz0123456789-_"; static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW" "$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; -static int -_lws_b64_encode_string(const char *encode, const char *in, int in_len, - char *out, int out_size) +LWS_VISIBLE int +lws_b64_encode_string(const char *in, int in_len, char *out, int out_size) { unsigned char triple[3]; - int i, done = 0; + int i; + int len; + int line = 0; + int done = 0; while (in_len) { - int len = 0; + len = 0; for (i = 0; i < 3; i++) { if (in_len) { triple[i] = *in++; @@ -71,13 +71,14 @@ return -1; *out++ = encode[triple[0] >> 2]; - *out++ = encode[(((triple[0] & 0x03) << 4) & 0x30) | - (((triple[1] & 0xf0) >> 4) & 0x0f)]; - *out++ = (len > 1 ? encode[(((triple[1] & 0x0f) << 2) & 0x3c) | - (((triple[2] & 0xc0) >> 6) & 3)] : '='); + *out++ = encode[((triple[0] & 0x03) << 4) | + ((triple[1] & 0xf0) >> 4)]; + *out++ = (len > 1 ? encode[((triple[1] & 0x0f) << 2) | + ((triple[2] & 0xc0) >> 6)] : '='); *out++ = (len > 2 ? encode[triple[2] & 0x3f] : '='); done += 4; + line += 4; } if (done + 1 >= out_size) @@ -88,62 +89,41 @@ return done; } -int -lws_b64_encode_string(const char *in, int in_len, char *out, int out_size) -{ - return _lws_b64_encode_string(encode_orig, in, in_len, out, out_size); -} - -int -lws_b64_encode_string_url(const char *in, int in_len, char *out, int out_size) -{ - return _lws_b64_encode_string(encode_url, in, in_len, out, out_size); -} - - -void -lws_b64_decode_state_init(struct lws_b64state *state) -{ - memset(state, 0, sizeof(*state)); -} +/* + * returns length of decoded string in out, or -1 if out was too small + * according to out_size + */ -int -lws_b64_decode_stateful(struct lws_b64state *s, const char *in, size_t *in_len, - uint8_t *out, size_t *out_size, int final) +LWS_VISIBLE int +lws_b64_decode_string(const char *in, char *out, int out_size) { - const char *orig_in = in, *end_in = in + *in_len; - uint8_t *orig_out = out, *end_out = out + *out_size; + int len, i, c = 0, done = 0; + unsigned char v, quad[4]; - while (in < end_in && *in && out + 4 < end_out) { + while (*in) { - for (; s->i < 4 && in < end_in && *in; s->i++) { - uint8_t v; + len = 0; + for (i = 0; i < 4 && *in; i++) { v = 0; - s->c = 0; - while (in < end_in && *in && !v) { - s->c = v = *in++; - /* support the url base64 variant too */ - if (v == '-') - s->c = v = '+'; - if (v == '_') - s->c = v = '/'; + c = 0; + while (*in && !v) { + c = v = *in++; v = (v < 43 || v > 122) ? 0 : decode[v - 43]; if (v) v = (v == '$') ? 0 : v - 61; } - if (s->c) { - s->len++; + if (c) { + len++; if (v) - s->quad[s->i] = v - 1; + quad[i] = v - 1; } else - s->quad[s->i] = 0; + quad[i] = 0; } - if (s->i != 4 && !final) - continue; - - s->i = 0; + if (out_size < (done + len - 1)) + /* out buffer is too small */ + return -1; /* * "The '==' sequence indicates that the last group contained @@ -151,96 +131,53 @@ * bytes." (wikipedia) */ - if ((in >= end_in || !*in) && s->c == '=') - s->len--; + if (!*in && c == '=') + len--; - if (s->len >= 2) - *out++ = s->quad[0] << 2 | s->quad[1] >> 4; - if (s->len >= 3) - *out++ = s->quad[1] << 4 | s->quad[2] >> 2; - if (s->len >= 4) - *out++ = ((s->quad[2] << 6) & 0xc0) | s->quad[3]; + if (len >= 2) + *out++ = quad[0] << 2 | quad[1] >> 4; + if (len >= 3) + *out++ = quad[1] << 4 | quad[2] >> 2; + if (len >= 4) + *out++ = ((quad[2] << 6) & 0xc0) | quad[3]; - s->done += s->len - 1; - s->len = 0; + done += len - 1; } - *out = '\0'; - *in_len = in - orig_in; - *out_size = out - orig_out; - - return 0; -} - - -/* - * returns length of decoded string in out, or -1 if out was too small - * according to out_size - * - * Only reads up to in_len chars, otherwise if in_len is -1 on entry reads until - * the first NUL in the input. - */ - -static size_t -_lws_b64_decode_string(const char *in, int in_len, char *out, int out_size) -{ - struct lws_b64state state; - size_t il = (size_t)in_len, ol = out_size; - - if (in_len == -1) - il = strlen(in); - - lws_b64_decode_state_init(&state); - lws_b64_decode_stateful(&state, in, &il, (uint8_t *)out, &ol, 1); - - if (!il) - return 0; - - return ol; -} + if (done + 1 >= out_size) + return -1; -int -lws_b64_decode_string(const char *in, char *out, int out_size) -{ - return (int)_lws_b64_decode_string(in, -1, out, out_size); -} + *out = '\0'; -int -lws_b64_decode_string_len(const char *in, int in_len, char *out, int out_size) -{ - return (int)_lws_b64_decode_string(in, in_len, out, out_size); + return done; } #if 0 -static const char * const plaintext[] = { - "any carnal pleasure.", - "any carnal pleasure", - "any carnal pleasur", - "any carnal pleasu", - "any carnal pleas", - "Admin:kloikloi" -}; -static const char * const coded[] = { - "YW55IGNhcm5hbCBwbGVhc3VyZS4=", - "YW55IGNhcm5hbCBwbGVhc3VyZQ==", - "YW55IGNhcm5hbCBwbGVhc3Vy", - "YW55IGNhcm5hbCBwbGVhc3U=", - "YW55IGNhcm5hbCBwbGVhcw==", - "QWRtaW46a2xvaWtsb2k=" -}; - int lws_b64_selftest(void) { char buf[64]; unsigned int n, r = 0; unsigned int test; - - lwsl_notice("%s\n", __func__); - /* examples from https://en.wikipedia.org/wiki/Base64 */ + static const char * const plaintext[] = { + "any carnal pleasure.", + "any carnal pleasure", + "any carnal pleasur", + "any carnal pleasu", + "any carnal pleas", + "Admin:kloikloi" + }; + static const char * const coded[] = { + "YW55IGNhcm5hbCBwbGVhc3VyZS4=", + "YW55IGNhcm5hbCBwbGVhc3VyZQ==", + "YW55IGNhcm5hbCBwbGVhc3Vy", + "YW55IGNhcm5hbCBwbGVhc3U=", + "YW55IGNhcm5hbCBwbGVhcw==", + "QWRtaW46a2xvaWtsb2k=" + }; - for (test = 0; test < (int)LWS_ARRAY_SIZE(plaintext); test++) { + for (test = 0; test < sizeof plaintext / sizeof(plaintext[0]); test++) { buf[sizeof(buf) - 1] = '\0'; n = lws_b64_encode_string(plaintext[test], @@ -254,20 +191,15 @@ buf[sizeof(buf) - 1] = '\0'; n = lws_b64_decode_string(coded[test], buf, sizeof buf); if (n != strlen(plaintext[test]) || - strcmp(buf, plaintext[test])) { + strcmp(buf, plaintext[test])) { lwsl_err("Failed lws_b64 decode selftest " - "%d result '%s' / '%s', %d / %zu\n", - test, buf, plaintext[test], n, - strlen(plaintext[test])); - lwsl_hexdump_err(buf, n); + "%d result '%s' / '%s', %d / %d\n", + test, buf, plaintext[test], n, strlen(plaintext[test])); r = -1; } } - if (!r) - lwsl_notice("Base 64 selftests passed\n"); - else - lwsl_notice("Base64 selftests failed\n"); + lwsl_notice("Base 64 selftests passed\n"); return r; } diff -Nru libwebsockets-4.0.20/lib/misc/daemonize.c libwebsockets-2.4.2/lib/misc/daemonize.c --- libwebsockets-4.0.20/lib/misc/daemonize.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/daemonize.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,236 +0,0 @@ -/* - * This code is mainly taken from Doug Potter's page - * - * http://www-theorie.physik.unizh.ch/~dpotter/howto/daemonize - * - * I contacted him 2007-04-16 about the license for the original code, - * he replied it is Public Domain. Use the URL above to get the original - * Public Domain version if you want it. - * - * This version is MIT like the rest of libwebsockets and is - * Copyright (c)2006 - 2013 Andy Green - * - * - * You're much better advised to use systemd to daemonize stuff without needing - * this kind of support in the app itself. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "private-lib-core.h" - -pid_t pid_daemon; -static char *lock_path; - -pid_t get_daemonize_pid() -{ - return pid_daemon; -} - -static void -child_handler(int signum) -{ - int len, sent, fd; - char sz[20]; - - switch (signum) { - - case SIGALRM: /* timed out daemonizing */ - exit(0); - break; - - case SIGUSR1: /* positive confirmation we daemonized well */ - - if (!lock_path) - exit(0); - - /* Create the lock file as the current user */ - - fd = lws_open(lock_path, O_TRUNC | O_RDWR | O_CREAT, 0640); - if (fd < 0) { - fprintf(stderr, - "unable to create lock file %s, code=%d (%s)\n", - lock_path, errno, strerror(errno)); - exit(0); - } - len = sprintf(sz, "%u", (unsigned int)pid_daemon); - sent = write(fd, sz, len); - if (sent != len) - fprintf(stderr, - "unable to write pid to lock file %s, code=%d (%s)\n", - lock_path, errno, strerror(errno)); - - close(fd); - - exit(0); - //!!(sent == len)); - - case SIGCHLD: /* daemonization failed */ - exit(0); - break; - } -} - -static void lws_daemon_closing(int sigact) -{ - if (getpid() == pid_daemon) - if (lock_path) { - unlink(lock_path); - lws_free_set_NULL(lock_path); - } - - kill(getpid(), SIGKILL); -} - -/* - * You just need to call this from your main(), when it - * returns you are all set "in the background" decoupled - * from the console you were started from. - * - * The process context you called from has been terminated then. - */ - -int -lws_daemonize(const char *_lock_path) -{ - struct sigaction act; - pid_t sid, parent; - - /* already a daemon */ -// if (getppid() == 1) -// return 1; - - if (_lock_path) { - int n; - - int fd = lws_open(_lock_path, O_RDONLY); - if (fd >= 0) { - char buf[10]; - - n = read(fd, buf, sizeof(buf)); - close(fd); - if (n) { - int ret; - n = atoi(buf); - ret = kill(n, 0); - if (ret >= 0) { - fprintf(stderr, - "Daemon already running pid %d\n", - n); - exit(1); - } - fprintf(stderr, - "Removing stale lock %s from dead pid %d\n", - _lock_path, n); - unlink(lock_path); - } - } - - n = strlen(_lock_path) + 1; - lock_path = lws_malloc(n, "daemonize lock"); - if (!lock_path) { - fprintf(stderr, "Out of mem in lws_daemonize\n"); - return 1; - } - strcpy(lock_path, _lock_path); - } - - /* Trap signals that we expect to receive */ - signal(SIGCHLD, child_handler); /* died */ - signal(SIGUSR1, child_handler); /* was happy */ - signal(SIGALRM, child_handler); /* timeout daemonizing */ - - /* Fork off the parent process */ - pid_daemon = fork(); - if ((int)pid_daemon < 0) { - fprintf(stderr, "unable to fork daemon, code=%d (%s)", - errno, strerror(errno)); - exit(9); - } - - /* If we got a good PID, then we can exit the parent process. */ - if (pid_daemon > 0) { - - /* - * Wait for confirmation signal from the child via - * SIGCHILD / USR1, or for two seconds to elapse - * (SIGALRM). pause() should not return. - */ - alarm(2); - - pause(); - /* should not be reachable */ - exit(1); - } - - /* At this point we are executing as the child process */ - parent = getppid(); - pid_daemon = getpid(); - - /* Cancel certain signals */ - signal(SIGCHLD, SIG_DFL); /* A child process dies */ - signal(SIGTSTP, SIG_IGN); /* Various TTY signals */ - signal(SIGTTOU, SIG_IGN); - signal(SIGTTIN, SIG_IGN); - signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */ - - /* Change the file mode mask */ - umask(0); - - /* Create a new SID for the child process */ - sid = setsid(); - if (sid < 0) { - fprintf(stderr, - "unable to create a new session, code %d (%s)", - errno, strerror(errno)); - exit(2); - } - - /* - * Change the current working directory. This prevents the current - * directory from being locked; hence not being able to remove it. - */ - if (chdir("/tmp") < 0) { - fprintf(stderr, - "unable to change directory to %s, code %d (%s)", - "/", errno, strerror(errno)); - exit(3); - } - - /* Redirect standard files to /dev/null */ - if (!freopen("/dev/null", "r", stdin)) - fprintf(stderr, "unable to freopen() stdin, code %d (%s)", - errno, strerror(errno)); - - if (!freopen("/dev/null", "w", stdout)) - fprintf(stderr, "unable to freopen() stdout, code %d (%s)", - errno, strerror(errno)); - - if (!freopen("/dev/null", "w", stderr)) - fprintf(stderr, "unable to freopen() stderr, code %d (%s)", - errno, strerror(errno)); - - /* Tell the parent process that we are A-okay */ - kill(parent, SIGUSR1); - - act.sa_handler = lws_daemon_closing; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - - sigaction(SIGTERM, &act, NULL); - - /* return to continue what is now "the daemon" */ - - return 0; -} - diff -Nru libwebsockets-4.0.20/lib/misc/dir.c libwebsockets-2.4.2/lib/misc/dir.c --- libwebsockets-4.0.20/lib/misc/dir.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/dir.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,190 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(NO_GNU_SOURCE_THIS_TIME) -#define NO_GNU_SOURCE_THIS_TIME -#endif -#if !defined(_DARWIN_C_SOURCE) -#define _DARWIN_C_SOURCE -#endif - -#include -#include "private-lib-core.h" -#include -#include - -#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0 - -int -lws_dir(const char *dirpath, void *user, lws_dir_callback_function cb) -{ - struct lws_dir_entry lde; - uv_dirent_t dent; - uv_fs_t req; - int ret = 1, ir; - uv_loop_t loop; - - ir = uv_loop_init(&loop); - if (ir) { - lwsl_err("%s: loop init failed %d\n", __func__, ir); - return 1; - } - - ir = uv_fs_scandir(&loop, &req, dirpath, 0, NULL); - if (ir < 0) { - lwsl_err("Scandir on %s failed, errno %d\n", dirpath, LWS_ERRNO); - ret = 2; - goto bail; - } - - while (uv_fs_scandir_next(&req, &dent) != UV_EOF) { - lde.name = dent.name; - lde.type = (int)dent.type; - if (cb(dirpath, user, &lde)) - goto bail1; - } - - ret = 0; - -bail1: - uv_fs_req_cleanup(&req); -bail: - while (uv_loop_close(&loop)) - ; - - return ret; -} - -#else - -#if !defined(_WIN32) && !defined(LWS_PLAT_FREERTOS) - -#include - -static int filter(const struct dirent *ent) -{ - if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) - return 0; - - return 1; -} - -int -lws_dir(const char *dirpath, void *user, lws_dir_callback_function cb) -{ - struct lws_dir_entry lde; - struct dirent **namelist; - int n, i, ret = 1; - - n = scandir((char *)dirpath, &namelist, filter, alphasort); - if (n < 0) { - lwsl_err("Scandir on '%s' failed, errno %d\n", dirpath, LWS_ERRNO); - return 1; - } - - for (i = 0; i < n; i++) { - if (strchr(namelist[i]->d_name, '~')) - goto skip; - lde.name = namelist[i]->d_name; - - /* - * some filesystems don't report this (ZFS) and tell that - * files are LDOT_UNKNOWN - */ - -#if defined(__sun) - struct stat s; - stat(namelist[i]->d_name, &s); - switch (s.st_mode) { - case S_IFBLK: - lde.type = LDOT_BLOCK; - break; - case S_IFCHR: - lde.type = LDOT_CHAR; - break; - case S_IFDIR: - lde.type = LDOT_DIR; - break; - case S_IFIFO: - lde.type = LDOT_FIFO; - break; - case S_IFLNK: - lde.type = LDOT_LINK; - break; - case S_IFREG: - lde.type = LDOT_FILE; - break; - default: - lde.type = LDOT_UNKNOWN; - break; - } -#else - switch (namelist[i]->d_type) { - case DT_BLK: - lde.type = LDOT_BLOCK; - break; - case DT_CHR: - lde.type = LDOT_CHAR; - break; - case DT_DIR: - lde.type = LDOT_DIR; - break; - case DT_FIFO: - lde.type = LDOT_FIFO; - break; - case DT_LNK: - lde.type = LDOT_LINK; - break; - case DT_REG: - lde.type = LDOT_FILE; - break; - case DT_SOCK: - lde.type = LDOTT_SOCKET; - break; - default: - lde.type = LDOT_UNKNOWN; - break; - } -#endif - if (cb(dirpath, user, &lde)) { - while (i++ < n) - free(namelist[i]); - goto bail; - } -skip: - free(namelist[i]); - } - - ret = 0; - -bail: - free(namelist); - - return ret; -} - -#else -#error "If you want lws_dir on windows, you need libuv" -#endif -#endif diff -Nru libwebsockets-4.0.20/lib/misc/diskcache.c libwebsockets-2.4.2/lib/misc/diskcache.c --- libwebsockets-4.0.20/lib/misc/diskcache.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/diskcache.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,490 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#include - -#include -#include "private-lib-core.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#if defined(__APPLE__) -#include -/* Travis OSX does not have DT_REG... */ -#if !defined(DT_REG) -#define DT_REG 8 -#endif -#endif - -struct file_entry { - lws_list_ptr sorted; - lws_list_ptr prev; - char name[64]; - time_t modified; - size_t size; -}; - -struct lws_diskcache_scan { - struct file_entry *batch; - const char *cache_dir_base; - lws_list_ptr head; - time_t last_scan_completed; - uint64_t agg_size; - uint64_t cache_size_limit; - uint64_t avg_size; - uint64_t cache_tries; - uint64_t cache_hits; - int cache_subdir; - int batch_in_use; - int agg_file_count; - int secs_waiting; -}; - -#define KIB (1024) -#define MIB (KIB * KIB) - -#define lp_to_fe(p, _n) lws_list_ptr_container(p, struct file_entry, _n) - -static const char *hex = "0123456789abcdef"; - -#define BATCH_COUNT 128 - -static int -fe_modified_sort(lws_list_ptr a, lws_list_ptr b) -{ - struct file_entry *p1 = lp_to_fe(a, sorted), *p2 = lp_to_fe(b, sorted); - - return p2->modified - p1->modified; -} - -struct lws_diskcache_scan * -lws_diskcache_create(const char *cache_dir_base, uint64_t cache_size_limit) -{ - struct lws_diskcache_scan *lds = lws_malloc(sizeof(*lds), "cachescan"); - - if (!lds) - return NULL; - - memset(lds, 0, sizeof(*lds)); - - lds->cache_dir_base = cache_dir_base; - lds->cache_size_limit = cache_size_limit; - - return lds; -} - -void -lws_diskcache_destroy(struct lws_diskcache_scan **lds) -{ - if ((*lds)->batch) - lws_free((*lds)->batch); - lws_free(*lds); - *lds = NULL; -} - -int -lws_diskcache_prepare(const char *cache_base_dir, int mode, int uid) -{ - char dir[256]; - int n, m; - - (void)mkdir(cache_base_dir, mode); - if (chown(cache_base_dir, uid, -1)) - lwsl_err("%s: %s: unable to chown %d\n", __func__, - cache_base_dir, uid); - - for (n = 0; n < 16; n++) { - lws_snprintf(dir, sizeof(dir), "%s/%c", cache_base_dir, hex[n]); - (void)mkdir(dir, mode); - if (chown(dir, uid, -1)) - lwsl_err("%s: %s: unable to chown %d\n", __func__, - dir, uid); - for (m = 0; m < 16; m++) { - lws_snprintf(dir, sizeof(dir), "%s/%c/%c", - cache_base_dir, hex[n], hex[m]); - (void)mkdir(dir, mode); - if (chown(dir, uid, -1)) - lwsl_err("%s: %s: unable to chown %d\n", - __func__, dir, uid); - } - } - - return 0; -} - -/* copies and then truncates the incoming name, and renames the file at the - * untruncated path to have the new truncated name */ - -int -lws_diskcache_finalize_name(char *cache) -{ - char ren[256], *p; - - strncpy(ren, cache, sizeof(ren) - 1); - ren[sizeof(ren) - 1] = '\0'; - p = strchr(cache, '~'); - if (p) { - *p = '\0'; - if (rename(ren, cache)) { - lwsl_err("%s: problem renaming %s to %s\n", __func__, - ren, cache); - return 1; - } - - return 0; - } - - return 1; -} - -int -lws_diskcache_query(struct lws_diskcache_scan *lds, int is_bot, - const char *hash_hex, int *_fd, char *cache, int cache_len, - size_t *extant_cache_len) -{ - struct stat s; - int n; - - /* caching is disabled? */ - if (!lds->cache_dir_base) - return LWS_DISKCACHE_QUERY_NO_CACHE; - - if (!is_bot) - lds->cache_tries++; - - n = lws_snprintf(cache, cache_len, "%s/%c/%c/%s", lds->cache_dir_base, - hash_hex[0], hash_hex[1], hash_hex); - - lwsl_info("%s: job cache %s\n", __func__, cache); - - *_fd = open(cache, O_RDONLY); - if (*_fd >= 0) { - int fd; - - if (!is_bot) - lds->cache_hits++; - - if (fstat(*_fd, &s)) { - close(*_fd); - - return LWS_DISKCACHE_QUERY_NO_CACHE; - } - - *extant_cache_len = (size_t)s.st_size; - - /* "touch" the hit cache file so it's last for LRU now */ - fd = open(cache, O_RDWR); - if (fd >= 0) - close(fd); - - return LWS_DISKCACHE_QUERY_EXISTS; - } - - /* bots are too random to pollute the cache with their antics */ - if (is_bot) - return LWS_DISKCACHE_QUERY_NO_CACHE; - - /* let's create it first with a unique temp name */ - - lws_snprintf(cache + n, cache_len - n, "~%d-%p", (int)getpid(), - extant_cache_len); - - *_fd = open(cache, O_RDWR | O_CREAT | O_TRUNC, 0600); - if (*_fd < 0) { - /* well... ok... we will proceed without cache then... */ - lwsl_notice("%s: Problem creating cache %s: errno %d\n", - __func__, cache, errno); - return LWS_DISKCACHE_QUERY_NO_CACHE; - } - - return LWS_DISKCACHE_QUERY_CREATING; -} - -int -lws_diskcache_secs_to_idle(struct lws_diskcache_scan *lds) -{ - return lds->secs_waiting; -} - -/* - * The goal is to collect the oldest BATCH_COUNT filepaths and filesizes from - * the dirs under the cache dir. Since we don't need or want a full list of - * files in there in memory at once, we restrict the linked-list size to - * BATCH_COUNT entries, and once it is full, simply ignore any further files - * that are newer than the newest one on that list. Files older than the - * newest guy already on the list evict the newest guy already on the list - * and are sorted into the correct order. In this way no matter the number - * of files to be processed the memory requirement is fixed at BATCH_COUNT - * struct file_entry-s. - * - * The oldest subset of BATCH_COUNT files are sorted into the cd->batch - * allocation in more recent -> least recent order. - * - * We want to track the total size of all files we saw as well, so we know if - * we need to actually do anything yet to restrict how much space it's taking - * up. - * - * And we want to do those things statefully and incrementally instead of one - * big atomic operation, since the user may want a huge cache, so we look in - * one cache dir at a time and track state in the repodir struct. - * - * When we have seen everything, we add the doubly-linked prev pointers and then - * if we are over the limit, start deleting up to BATCH_COUNT files working back - * from the end. - */ - -int -lws_diskcache_trim(struct lws_diskcache_scan *lds) -{ - size_t cache_size_limit = lds->cache_size_limit; - char dirpath[132], filepath[132 + 32]; - lws_list_ptr lp, op = NULL; - int files_trimmed = 0; - struct file_entry *p; - int fd, n, ret = -1; - size_t trimmed = 0; - struct dirent *de; - struct stat s; - DIR *dir; - - if (!lds->cache_subdir) { - - if (lds->last_scan_completed + lds->secs_waiting > time(NULL)) - return 0; - - lds->batch = lws_malloc(sizeof(struct file_entry) * - BATCH_COUNT, "cache_trim"); - if (!lds->batch) { - lwsl_err("%s: OOM\n", __func__); - - return 1; - } - lds->agg_size = 0; - lds->head = NULL; - lds->batch_in_use = 0; - lds->agg_file_count = 0; - } - - lws_snprintf(dirpath, sizeof(dirpath), "%s/%c/%c", - lds->cache_dir_base, hex[(lds->cache_subdir >> 4) & 15], - hex[lds->cache_subdir & 15]); - - dir = opendir(dirpath); - if (!dir) { - lwsl_err("Unable to walk repo dir '%s'\n", - lds->cache_dir_base); - return -1; - } - - do { - de = readdir(dir); - if (!de) - break; - - if (de->d_type != DT_REG) - continue; - - lds->agg_file_count++; - - lws_snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, - de->d_name); - - fd = open(filepath, O_RDONLY); - if (fd < 0) { - lwsl_err("%s: cannot open %s\n", __func__, filepath); - - continue; - } - - n = fstat(fd, &s); - close(fd); - if (n) { - lwsl_notice("%s: cannot stat %s\n", __func__, filepath); - continue; - } - - lds->agg_size += s.st_size; - - if (lds->batch_in_use == BATCH_COUNT) { - /* - * once we filled up the batch with candidates, we don't - * need to consider any files newer than the newest guy - * on the list... - */ - if (lp_to_fe(lds->head, sorted)->modified < s.st_mtime) - continue; - - /* - * ... and if we find an older file later, we know it - * will be replacing the newest guy on the list, so use - * that directly... - */ - p = lds->head; - lds->head = p->sorted; - } else - /* we are still accepting anything to fill the batch */ - - p = &lds->batch[lds->batch_in_use++]; - - p->sorted = NULL; - strncpy(p->name, de->d_name, sizeof(p->name) - 1); - p->name[sizeof(p->name) - 1] = '\0'; - p->modified = s.st_mtime; - p->size = s.st_size; - - lws_list_ptr_insert(&lds->head, &p->sorted, fe_modified_sort); - } while (de); - - ret = 0; - - lds->cache_subdir++; - if (lds->cache_subdir != 0x100) - goto done; - - /* we completed the whole scan... */ - - /* if really no guidence, then 256MiB */ - if (!cache_size_limit) - cache_size_limit = 256 * 1024 * 1024; - - if (lds->agg_size > cache_size_limit) { - - /* apply prev pointers to make the list doubly-linked */ - - lp = lds->head; - while (lp) { - p = lp_to_fe(lp, sorted); - - p->prev = op; - op = &p->prev; - lp = p->sorted; - } - - /* - * reverse the list (start from tail, now traverse using - * .prev)... it's oldest-first now... - */ - - lp = op; - - while (lp && lds->agg_size > cache_size_limit) { - p = lp_to_fe(lp, prev); - - lws_snprintf(filepath, sizeof(filepath), "%s/%c/%c/%s", - lds->cache_dir_base, p->name[0], - p->name[1], p->name); - - if (!unlink(filepath)) { - lds->agg_size -= p->size; - trimmed += p->size; - files_trimmed++; - } else - lwsl_notice("%s: Failed to unlink %s\n", - __func__, filepath); - - lp = p->prev; - } - - if (files_trimmed) - lwsl_notice("%s: %s: trimmed %d files totalling " - "%lldKib, leaving %lldMiB\n", __func__, - lds->cache_dir_base, files_trimmed, - ((unsigned long long)trimmed) / KIB, - ((unsigned long long)lds->agg_size) / MIB); - } - - if (lds->agg_size && lds->agg_file_count) - lds->avg_size = lds->agg_size / lds->agg_file_count; - - /* - * estimate how long we can go before scanning again... default we need - * to start again immediately - */ - - lds->last_scan_completed = time(NULL); - lds->secs_waiting = 1; - - if (lds->agg_size < cache_size_limit) { - uint64_t avg = 4096, capacity, projected; - - /* let's use 80% of the real average for margin */ - if (lds->agg_size && lds->agg_file_count) - avg = ((lds->agg_size * 8) / lds->agg_file_count) / 10; - - /* - * if we collected BATCH_COUNT files of the average size, - * how much can we clean up in 256s? - */ - - capacity = avg * BATCH_COUNT; - - /* - * if the cache grew by 10%, would we hit the limit even then? - */ - projected = (lds->agg_size * 11) / 10; - if (projected < cache_size_limit) - /* no... */ - lds->secs_waiting = (256 / 2) * ((cache_size_limit - - projected) / capacity); - - /* - * large waits imply we may not have enough info yet, so - * check once an hour at least. - */ - - if (lds->secs_waiting > 3600) - lds->secs_waiting = 3600; - } else - lds->secs_waiting = 0; - - lwsl_info("%s: cache %s: %lldKiB / %lldKiB, next scan %ds\n", __func__, - lds->cache_dir_base, - (unsigned long long)lds->agg_size / KIB, - (unsigned long long)cache_size_limit / KIB, - lds->secs_waiting); - - lws_free(lds->batch); - lds->batch = NULL; - - lds->cache_subdir = 0; - -done: - closedir(dir); - - return ret; -} diff -Nru libwebsockets-4.0.20/lib/misc/fsmount.c libwebsockets-2.4.2/lib/misc/fsmount.c --- libwebsockets-4.0.20/lib/misc/fsmount.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/fsmount.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,156 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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, sublicefsme, and/or - * sell copies of the Software, and to permit persofsm to whom the Software is - * furnished to do so, subject to the following conditiofsm: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portiofsm 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. - * - * Mount and unmount overlayfs mountpoints (linux only) - */ - -#include "private-lib-core.h" -#include - -#include - -#include -#include - -#include -#include -#include -#include - -static int -rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) -{ - char path[384]; - - if (!strcmp(lde->name, ".") || !strcmp(lde->name, "..")) - return 0; - - lws_snprintf(path, sizeof(path), "%s/%s", dirpath, lde->name); - - if (lde->type == LDOT_DIR) { - lws_dir(path, NULL, rm_rf_cb); - rmdir(path); - } else - unlink(path); - - return 0; -} - -int -lws_fsmount_mount(struct lws_fsmount *fsm) -{ - struct libmnt_context *ctx; - char opts[512], c; - int n, m; - - /* - * For robustness, there are a couple of sticky situations caused by - * previous mounts not cleaning up... 1) still mounted on the mountpoint - * and 2) junk in the session dir from the dead session. - * - * For 1), do a gratuitous umount attempts until it feels nothing to - * umount... - */ - - c = fsm->mp[0]; - while (!lws_fsmount_unmount(fsm)) - fsm->mp[0] = c; - fsm->mp[0] = c; - - /* - * ... for 2), generate the session dir basepath and destroy everything - * in it... it's less dangerous than it sounds because there are - * hardcoded unusual dir names in the base path, so it can't go wild - * even if the overlay path is empty or / - */ - - lws_snprintf(opts, sizeof(opts), "%s/overlays/%s/session", - fsm->overlay_path, fsm->ovname); - lwsl_info("%s: emptying session dir %s\n", __func__, opts); - lws_dir(opts, NULL, rm_rf_cb); - - /* - * Piece together the options for the overlay mount... - */ - - n = lws_snprintf(opts, sizeof(opts), "lowerdir="); - for (m = LWS_ARRAY_SIZE(fsm->layers) - 1; m >= 0; m--) - if (fsm->layers[m]) { - if (n != 9) - opts[n++] = ':'; - - n += lws_snprintf(&opts[n], sizeof(opts) - n, - "%s/%s/%s", fsm->layers_path, - fsm->distro, fsm->layers[m]); - } - - n += lws_snprintf(&opts[n], sizeof(opts) - n, - ",upperdir=%s/overlays/%s/session", - fsm->overlay_path, fsm->ovname); - - n += lws_snprintf(&opts[n], sizeof(opts) - n, - ",workdir=%s/overlays/%s/work", - fsm->overlay_path, fsm->ovname); - - ctx = mnt_new_context(); - if (!ctx) - return 1; - - mnt_context_set_fstype(ctx, "overlay"); - mnt_context_set_options(ctx, opts); - mnt_context_set_mflags(ctx, MS_NOATIME /* |MS_NOEXEC */); - mnt_context_set_target(ctx, fsm->mp); - mnt_context_set_source(ctx, "none"); - - lwsl_notice("%s: mount opts %s\n", __func__, opts); - puts(opts); - - m = mnt_context_mount(ctx); - lwsl_notice("%s: mountpoint %s: %d\n", __func__, fsm->mp, m); - - mnt_free_context(ctx); - - return m; -} - -int -lws_fsmount_unmount(struct lws_fsmount *fsm) -{ - struct libmnt_context *ctx; - int m; - - lwsl_notice("%s: %s\n", __func__, fsm->mp); - - ctx = mnt_new_context(); - if (!ctx) - return 1; - - mnt_context_set_target(ctx, fsm->mp); - - m = mnt_context_umount(ctx); - mnt_free_context(ctx); - - fsm->mp[0] = '\0'; - - return m; -} diff -Nru libwebsockets-4.0.20/lib/misc/fts/private-lib-misc-fts.h libwebsockets-2.4.2/lib/misc/fts/private-lib-misc-fts.h --- libwebsockets-4.0.20/lib/misc/fts/private-lib-misc-fts.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/fts/private-lib-misc-fts.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -#include - -/* if you need > 2GB trie files */ -//typedef off_t jg2_file_offset; -typedef uint32_t jg2_file_offset; - -struct lws_fts_file { - int fd; - jg2_file_offset root, flen, filepath_table; - int max_direct_hits; - int max_completion_hits; - int filepaths; -}; - - - -#define TRIE_FILE_HDR_SIZE 20 -#define MAX_VLI 5 - -#define LWS_FTS_LINES_PER_CHUNK 200 - -int -rq32(unsigned char *b, uint32_t *d); diff -Nru libwebsockets-4.0.20/lib/misc/fts/README.md libwebsockets-2.4.2/lib/misc/fts/README.md --- libwebsockets-4.0.20/lib/misc/fts/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/fts/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,315 +0,0 @@ -# LWS Full Text Search - -## Introduction - -![lwsac flow](/doc-assets/lws-fts.svg) - -The general approach is to scan one or more UTF-8 input text "files" (they may -only exist in memory) and create an in-memory optimized trie for every token in -the file. - -This can then be serialized out to disk in the form of a single index file (no -matter how many input files were involved or how large they were). - -The implementation is designed to be modest on memory and cpu for both index -creation and querying, and suitable for weak machines with some kind of random -access storage. For searching only memory to hold results is required, the -actual searches and autocomplete suggestions are done very rapidly by seeking -around structures in the on-disk index file. - -Function|Related Link ----|--- -Public API|[include/libwebsockets/lws-fts.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-fts.h) -CI test app|[minimal-examples/api-tests/api-test-fts](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-fts) -Demo minimal example|[minimal-examples/http-server/minimal-http-server-fulltext-search](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/http-server/minimal-http-server-fulltext-search) -Live Demo|[https://libwebsockets.org/ftsdemo/](https://libwebsockets.org/ftsdemo/) - -## Query API overview - -Searching returns a potentially very large lwsac allocated object, with contents -and max size controlled by the members of a struct lws_fts_search_params passed -to the search function. Three kinds of result are possible: - -### Autocomplete suggestions - -These are useful to provide lists of extant results in -realtime as the user types characters that constrain the search. So if the -user has typed 'len', any hits for 'len' itself are reported along with -'length', and whatever else is in the index beginning 'len'.. The results are -selected using and are accompanied by an aggregated count of results down that -path, and the results so the "most likely" results already measured by potential -hits appear first. - -These results are in a linked-list headed by `result.autocomplete_head` and -each is in a `struct lws_fts_result_autocomplete`. - -They're enabled in the search results by giving the flag - `LWSFTS_F_QUERY_AUTOCOMPLETE` in the search parameter flags. - -### Filepath results - -Simply a list of input files containing the search term with some statistics, -one file is mentioned in a `struct lws_fts_result_filepath` result struct. - -This would be useful for creating a selection UI to "drill down" to individual -files when there are many with matches. - -This is enabled by the `LWSFTS_F_QUERY_FILES` search flag. - -### Filepath and line results - -Same as the file path list, but for each filepath, information on the line -numbers and input file offset where the line starts are provided. - -This is enabled by `LWSFTS_F_QUERY_FILE_LINES`... if you additionally give -`LWSFTS_F_QUERY_QUOTE_LINE` flag then the contents of each hit line from the -input file are also provided. - -## Result format inside the lwsac - -A `struct lws_fts_result` at the start of the lwsac contains heads for linked- -lists of autocomplete and filepath results inside the lwsac. - -For autocomplete suggestions, the string itself is immediately after the -`struct lws_fts_result_autocomplete` in memory. For filepath results, after -each `struct lws_fts_result_filepath` is - - - match information depending on the flags given to the search - - the filepath string - -You can always skip the line number table to get the filepath string by adding -.matches_length to the address of the byte after the struct. - -The matches information is either - - - 0 bytes per match - - - 2x int32_t per match (8 bytes) if `LWSFTS_F_QUERY_FILE_LINES` given... the - first is the native-endian line number of the match, the second is the - byte offset in the original file where that line starts - - - 2 x int32_t as above plus a const char * if `LWSFTS_F_QUERY_QUOTE_LINE` is - also given... this points to a NUL terminated string also stored in the - results lwsac that contains up to 255 chars of the line from the original - file. In some cases, the original file was either virtual (you are indexing - a git revision) or is not stored with the index, in that case you can't - usefully use `LWSFTS_F_QUERY_QUOTE_LINE`. - -To facilitate interpreting what is stored per match, the original search flags -that created the result are stored in the `struct lws_fts_result`. - -## Indexing In-memory and serialized to file - -When creating the trie, in-memory structs are used with various optimization -schemes trading off memory usage for speed. While in-memory, it's possible to -add more indexed filepaths to the single index. Once the trie is complete in -terms of having indexed everything, it is serialized to disk. - -These contain many additional housekeeping pointers and trie entries which can -be optimized out. Most in-memory values must be held literally in large types, -whereas most of the values in the serialized file use smaller VLI which use -more or less bytes according to the value. So the peak memory requirements for -large tries are much bigger than the size of the serialized trie file that is -output. - -For the linux kernel at 4.14 and default indexing whitelist on a 2.8GHz AMD -threadripper (using one thread), the stats are: - -Name|Value ----|--- -Files indexed|52932 -Input corpus size|694MiB -Indexing cpu time|50.1s (>1000 files / sec; 13.8MBytes/sec) -Peak alloc|78MiB -Serialization time|202ms -Trie File size|347MiB - -To index libwebsockets master under the same conditions: - -Name|Value ----|--- -Files indexed|489 -Input corpus size|3MiB -Indexing time|123ms -Peak alloc|3MiB -Serialization time|1ms -Trie File size|1.4MiB - - -Once it's generated, querying the trie file is very inexpensive, even when there -are lots of results. - - - trie entry child lists are kept sorted by the character they map to. This - allows discovering there is no match as soon as a character later in the - order than the one being matched is seen - - - for the root trie, in addition to the linked-list child + sibling entries, - a 256-entry pointer table is associated with the root trie, allowing one- - step lookup. But as the table is 2KiB, it's too expensive to use on all - trie entries - -## Structure on disk - -All explicit multibyte numbers are stored in Network (MSB-first) byte order. - - - file header - - filepath line number tables - - filepath information - - filepath map table - - tries, trie instances (hits), trie child tables - -### VLI coding - -VLI (Variable Length Integer) coding works like this - -[b7 EON] [b6 .. b0 DATA] - -If EON = 0, then DATA represents the Least-significant 7 bits of the number. -if EON = 1, DATA represents More-significant 7-bits that should be shifted -left until the byte with EON = 0 is found to terminate the number. - -The VLI used is predicated around 32-bit unsigned integers - -Examples: - - - 0x30 = 48 - - 0x81 30 = 176 - - 0x81 0x80 0x00 = 16384 - -Bytes | Range ----|--- -1|<= 127 -2|<= 16K - 1 -3|<= 2M -1 -4|<= 256M - 1 -5|<= 4G - 1 - -The coding is very efficient if there's a high probabilty the number being -stored is not large. So it's great for line numbers for example, where most -files have less that 16K lines and the VLI for the line number fits in 2 bytes, -but if you meet a huge file, the VLI coding can also handle it. - -All numbers except a few in the headers that are actually written after the -following data are stored using VLI for space- efficiency without limiting -capability. The numbers that are fixed up after the fact have to have a fixed -size and can't use VLI. - -### File header - -The first byte of the file header where the magic is, is "fileoffset" 0. All -the stored "fileoffset"s are relative to that. - -The header has a fixed size of 16 bytes. - -size|function ----|--- -32-bits|Magic 0xCA7A5F75 -32-bits|Fileoffset to root trie entry -32-bits|Size of the trie file when it was created (to detect truncation) -32-bits|Fileoffset to the filepath map -32-bits|Number of filepaths - -### Filepath line tables - -Immediately after the file header are the line length tables. - -As the input files are parsed, line length tables are written for each file... -at that time the rest of the parser data is held in memory so nothing else is -in the file yet. These allow you to map logical line numbers in the file to -file offsets space- and time- efficiently without having to walk through the -file contents. - -The line information is cut into blocks, allowing quick skipping over the VLI -data that doesn't contain the line you want just by following the 8-byte header -part. - -Once you find the block with your line, you have to iteratively add the VLIs -until you hit the one you want. - -For normal text files with average line length below 128, the VLIs will -typically be a single byte. So a block of 200 line lengths is typically -208 bytes long. - -There is a final linetable chunk consisting of all zeros to indicate the end -of the filepath line chunk series for a filepath. - -size|function ----|--- -16-bit|length of this chunk itself in bytes -16-bit|count of lines covered in this chunk -32-bit|count of bytes in the input file this chunk covers -VLI...|for each line in the chunk, the number of bytes in the line - - -### Filepaths - -The single trie in the file may contain information from multiple files, for -example one trie may cover all files in a directory. The "Filepaths" are -listed after the line tables, and referred to by index thereafter. - -For each filepath, one after the other: - -size|function ----|--- -VLI|fileoffset of the start of this filepath's line table -VLI|count of lines in the file -VLI|length of filepath in bytes -...|the filepath (with no NUL) - -### Filepath map - -To facilitate rapid filepath lookup, there's a filepath map table with a 32-bit -fileoffset per filepath. This is the way to convert filepath indexes to -information on the filepath like its name, etc - -size|function ----|--- -32-bit...|fileoffset to filepath table for each filepath - -### Trie entries - -Immediately after that, the trie entries are dumped, for each one a header: - -#### Trie entry header - -size|function ----|--- -VLI|Fileoffset of first file table in this trie entry instance list -VLI|number of child trie entries this trie entry has -VLI|number of instances this trie entry has - -The child list follows immediately after this header - -#### Trie entry instance file - -For each file that has instances of this symbol: - -size|function ----|--- -VLI|Fileoffset of next file table in this trie entry instance list -VLI|filepath index -VLI|count of line number instances following - -#### Trie entry file line number table - -Then for the file mentioned above, a list of all line numbers in the file with -the symbol in them, in ascending order. As a VLI, the median size per entry -will typically be ~15.9 bits due to the probability of line numbers below 16K. - -size|function ----|--- -VLI|line number -... - -#### Trie entry child table - -For each child node - -size|function ----|--- -VLI|file offset of child -VLI|instance count belonging directly to this child -VLI|aggregated number of instances down all descendent paths of child -VLI|aggregated number of children down all descendent paths of child -VLI|match string length -...|the match string diff -Nru libwebsockets-4.0.20/lib/misc/fts/trie.c libwebsockets-2.4.2/lib/misc/fts/trie.c --- libwebsockets-4.0.20/lib/misc/fts/trie.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/fts/trie.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1372 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 functions allow - * - * - collecting a concordance of strings from one or more files (eg, a - * directory of files) into a single in-memory, lac-backed trie; - * - * - to optimize and serialize the in-memory trie to an fd; - * - * - to very quickly report any instances of a string in any of the files - * indexed by the trie, by a seeking around a serialized trie fd, without - * having to load it all in memory - */ - -#include "private-lib-core.h" -#include "private-lib-misc-fts.h" - -#include -#include -#include -#include -#include -#include - -struct lws_fts_entry; - -/* notice these are stored in t->lwsac_input_head which has input file scope */ - -struct lws_fts_filepath { - struct lws_fts_filepath *next; - struct lws_fts_filepath *prev; - char filepath[256]; - jg2_file_offset ofs; - jg2_file_offset line_table_ofs; - int filepath_len; - int file_index; - int total_lines; - int priority; -}; - -/* notice these are stored in t->lwsac_input_head which has input file scope */ - -struct lws_fts_lines { - struct lws_fts_lines *lines_next; - /* - * amount of line numbers needs to meet average count for best - * efficiency. - * - * Line numbers are stored in VLI format since if we don't, around half - * the total lac allocation consists of struct lws_fts_lines... - * size chosen to maintain 8-byte struct alignment - */ - uint8_t vli[119]; - char count; -}; - -/* this represents the instances of a symbol inside a given filepath */ - -struct lws_fts_instance_file { - /* linked-list of tifs generated for current file */ - struct lws_fts_instance_file *inst_file_next; - struct lws_fts_entry *owner; - struct lws_fts_lines *lines_list, *lines_tail; - uint32_t file_index; - uint32_t total; - - /* - * optimization for the common case there's only 1 - ~3 matches, so we - * don't have to allocate any lws_fts_lines struct - * - * Using 8 bytes total for this maintains 8-byte struct alignment... - */ - - uint8_t vli[7]; - char count; -}; - -/* - * this is the main trie in-memory allocation object - */ - -struct lws_fts_entry { - struct lws_fts_entry *parent; - - struct lws_fts_entry *child_list; - struct lws_fts_entry *sibling; - - /* - * care... this points to content in t->lwsac_input_head, it goes - * out of scope when the input file being indexed completes - */ - struct lws_fts_instance_file *inst_file_list; - - jg2_file_offset ofs_last_inst_file; - - char *suffix; /* suffix string or NULL if one char (in .c) */ - jg2_file_offset ofs; - uint32_t child_count; - uint32_t instance_count; - uint32_t agg_inst_count; - uint32_t agg_child_count; - uint32_t suffix_len; - unsigned char c; -}; - -/* there's only one of these per trie file */ - -struct lws_fts { - struct lwsac *lwsac_head; - struct lwsac *lwsac_input_head; - struct lws_fts_entry *root; - struct lws_fts_filepath *filepath_list; - struct lws_fts_filepath *fp; - - struct lws_fts_entry *parser; - struct lws_fts_entry *root_lookup[256]; - - /* - * head of linked-list of tifs generated for current file - * care... this points to content in t->lwsac_input_head - */ - struct lws_fts_instance_file *tif_list; - - jg2_file_offset c; /* length of output file so far */ - - uint64_t agg_trie_creation_us; - uint64_t agg_raw_input; - uint64_t worst_lwsac_input_size; - int last_file_index; - int chars_in_line; - jg2_file_offset last_block_len_ofs; - int line_number; - int lines_in_unsealed_linetable; - int next_file_index; - int count_entries; - - int fd; - unsigned int agg_pos; - unsigned int str_match_pos; - - unsigned char aggregate; - unsigned char agg[128]; -}; - -/* since the kernel case allocates >300MB, no point keeping this too low */ - -#define TRIE_LWSAC_BLOCK_SIZE (1024 * 1024) - -#define spill(margin, force) \ - if (bp && ((uint32_t)bp >= (sizeof(buf) - (margin)) || (force))) { \ - if (write(t->fd, buf, bp) != bp) { \ - lwsl_err("%s: write %d failed (%d)\n", __func__, \ - bp, errno); \ - return 1; \ - } \ - t->c += bp; \ - bp = 0; \ - } - -static int -g32(unsigned char *b, uint32_t d) -{ - *b++ = (d >> 24) & 0xff; - *b++ = (d >> 16) & 0xff; - *b++ = (d >> 8) & 0xff; - *b = d & 0xff; - - return 4; -} - -static int -g16(unsigned char *b, int d) -{ - *b++ = (d >> 8) & 0xff; - *b = d & 0xff; - - return 2; -} - -static int -wq32(unsigned char *b, uint32_t d) -{ - unsigned char *ob = b; - - if (d > (1 << 28) - 1) - *b++ = ((d >> 28) | 0x80) & 0xff; - - if (d > (1 << 21) - 1) - *b++ = ((d >> 21) | 0x80) & 0xff; - - if (d > (1 << 14) - 1) - *b++ = ((d >> 14) | 0x80) & 0xff; - - if (d > (1 << 7) - 1) - *b++ = ((d >> 7) | 0x80) & 0xff; - - *b++ = d & 0x7f; - - return (int)(b - ob); -} - - -/* read a VLI, return the number of bytes used */ - -int -rq32(unsigned char *b, uint32_t *d) -{ - unsigned char *ob = b; - uint32_t t = 0; - - t = *b & 0x7f; - if (*(b++) & 0x80) { - t = (t << 7) | (*b & 0x7f); - if (*(b++) & 0x80) { - t = (t << 7) | (*b & 0x7f); - if (*(b++) & 0x80) { - t = (t << 7) | (*b & 0x7f); - if (*(b++) & 0x80) { - t = (t << 7) | (*b & 0x7f); - b++; - } - } - } - } - - *d = t; - - return (int)(b - ob); -} - -struct lws_fts * -lws_fts_create(int fd) -{ - struct lws_fts *t; - struct lwsac *lwsac_head = NULL; - unsigned char buf[TRIE_FILE_HDR_SIZE]; - - t = lwsac_use(&lwsac_head, sizeof(*t), TRIE_LWSAC_BLOCK_SIZE); - if (!t) - return NULL; - - memset(t, 0, sizeof(*t)); - - t->fd = fd; - t->lwsac_head = lwsac_head; - t->root = lwsac_use(&lwsac_head, sizeof(*t->root), - TRIE_LWSAC_BLOCK_SIZE); - if (!t->root) - goto unwind; - - memset(t->root, 0, sizeof(*t->root)); - t->parser = t->root; - t->last_file_index = -1; - t->line_number = 1; - t->filepath_list = NULL; - - memset(t->root_lookup, 0, sizeof(*t->root_lookup)); - - /* write the header */ - - buf[0] = 0xca; - buf[1] = 0x7a; - buf[2] = 0x5f; - buf[3] = 0x75; - - /* (these are filled in with correct data at the end) */ - - /* file offset to root trie entry */ - g32(&buf[4], 0); - /* file length when it was created */ - g32(&buf[8], 0); - /* fileoffset to the filepath table */ - g32(&buf[0xc], 0); - /* count of filepaths */ - g32(&buf[0x10], 0); - - if (write(t->fd, buf, TRIE_FILE_HDR_SIZE) != TRIE_FILE_HDR_SIZE) { - lwsl_err("%s: trie header write failed\n", __func__); - goto unwind; - } - - t->c = TRIE_FILE_HDR_SIZE; - - return t; - -unwind: - lwsac_free(&lwsac_head); - - return NULL; -} - -void -lws_fts_destroy(struct lws_fts **trie) -{ - struct lwsac *lwsac_head = (*trie)->lwsac_head; - lwsac_free(&(*trie)->lwsac_input_head); - lwsac_free(&lwsac_head); - *trie = NULL; -} - -int -lws_fts_file_index(struct lws_fts *t, const char *filepath, int filepath_len, - int priority) -{ - struct lws_fts_filepath *fp = t->filepath_list; -#if 0 - while (fp) { - if (fp->filepath_len == filepath_len && - !strcmp(fp->filepath, filepath)) - return fp->file_index; - - fp = fp->next; - } -#endif - fp = lwsac_use(&t->lwsac_head, sizeof(*fp), TRIE_LWSAC_BLOCK_SIZE); - if (!fp) - return -1; - - fp->next = t->filepath_list; - t->filepath_list = fp; - strncpy(fp->filepath, filepath, sizeof(fp->filepath) - 1); - fp->filepath[sizeof(fp->filepath) - 1] = '\0'; - fp->filepath_len = filepath_len; - fp->file_index = t->next_file_index++; - fp->line_table_ofs = t->c; - fp->priority = priority; - fp->total_lines = 0; - t->fp = fp; - - return fp->file_index; -} - -static struct lws_fts_entry * -lws_fts_entry_child_add(struct lws_fts *t, unsigned char c, - struct lws_fts_entry *parent) -{ - struct lws_fts_entry *e, **pe; - - e = lwsac_use(&t->lwsac_head, sizeof(*e), TRIE_LWSAC_BLOCK_SIZE); - if (!e) - return NULL; - - memset(e, 0, sizeof(*e)); - - e->c = c; - parent->child_count++; - e->parent = parent; - t->count_entries++; - - /* keep the parent child list in ascending sort order for c */ - - pe = &parent->child_list; - while (*pe) { - assert((*pe)->parent == parent); - if ((*pe)->c > c) { - /* add it before */ - e->sibling = *pe; - *pe = e; - break; - } - pe = &(*pe)->sibling; - } - - if (!*pe) { - /* add it at the end */ - e->sibling = NULL; - *pe = e; - } - - return e; -} - -static int -finalize_per_input(struct lws_fts *t) -{ - struct lws_fts_instance_file *tif; - unsigned char buf[8192]; - uint64_t lwsac_input_size; - jg2_file_offset temp; - int bp = 0; - - bp += g16(&buf[bp], 0); - bp += g16(&buf[bp], 0); - bp += g32(&buf[bp], 0); - if (write(t->fd, buf, bp) != bp) - return 1; - t->c += bp; - bp = 0; - - /* - * Write the generated file index + instances (if any) - * - * Notice the next same-parent file instance fileoffset list is - * backwards, so it does not require seeks to fill in. The first - * entry has 0 but the second entry points to the first entry (whose - * fileoffset is known). - * - * After all the file instance structs are finalized, - * .ofs_last_inst_file contains the fileoffset of that child's tif - * list head in the file. - * - * The file instances are written to disk in the order that the files - * were indexed, along with their prev pointers inline. - */ - - tif = t->tif_list; - while (tif) { - struct lws_fts_lines *i; - - spill((3 * MAX_VLI) + tif->count, 0); - - temp = tif->owner->ofs_last_inst_file; - if (tif->total) - tif->owner->ofs_last_inst_file = t->c + bp; - - assert(!temp || (temp > TRIE_FILE_HDR_SIZE && temp < t->c)); - - /* fileoffset of prev instance file for this entry, or 0 */ - bp += wq32(&buf[bp], temp); - bp += wq32(&buf[bp], tif->file_index); - bp += wq32(&buf[bp], tif->total); - - /* remove any pointers into this disposable lac footprint */ - tif->owner->inst_file_list = NULL; - - memcpy(&buf[bp], &tif->vli, tif->count); - bp += tif->count; - - i = tif->lines_list; - while (i) { - spill(i->count, 0); - memcpy(&buf[bp], &i->vli, i->count); - bp += i->count; - - i = i->lines_next; - } - - tif = tif->inst_file_next; - } - - spill(0, 1); - - assert(lseek(t->fd, 0, SEEK_END) == (off_t)t->c); - - if (t->lwsac_input_head) { - lwsac_input_size = lwsac_total_alloc(t->lwsac_input_head); - if (lwsac_input_size > t->worst_lwsac_input_size) - t->worst_lwsac_input_size = lwsac_input_size; - } - - /* - * those per-file allocations are all on a separate lac so we can - * free it cleanly afterwards - */ - lwsac_free(&t->lwsac_input_head); - - /* and lose the pointer into the deallocated lac */ - t->tif_list = NULL; - - return 0; -} - -/* - * 0 = punctuation, whitespace, brackets etc - * 1 = character inside symbol set - * 2 = upper-case character inside symbol set - */ - -static char classify[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 1, //1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -}; - -#if 0 -static const char * -name_entry(struct lws_fts_entry *e1, char *s, int len) -{ - struct lws_fts_entry *e2; - int n = len; - - s[--n] = '\0'; - - e2 = e1; - while (e2) { - if (e2->suffix) { - if ((int)e2->suffix_len < n) { - n -= e2->suffix_len; - memcpy(&s[n], e2->suffix, e2->suffix_len); - } - } else { - n--; - s[n] = e2->c; - } - - e2 = e2->parent; - } - - return &s[n + 1]; -} -#endif - -/* - * as we parse the input, we create a line length table for the file index. - * Only the file header has been written before we start doing this. - */ - -int -lws_fts_fill(struct lws_fts *t, uint32_t file_index, const char *buf, - size_t len) -{ - unsigned long long tf = lws_now_usecs(); - unsigned char c, linetable[256], vlibuf[8]; - struct lws_fts_entry *e, *e1, *dcl; - struct lws_fts_instance_file *tif; - int bp = 0, sline, chars, m; - char *osuff, skipline = 0; - struct lws_fts_lines *tl; - unsigned int olen, n; - off_t lbh; - - if ((int)file_index != t->last_file_index) { - if (t->last_file_index >= 0) - finalize_per_input(t); - t->last_file_index = file_index; - t->line_number = 1; - t->chars_in_line = 0; - t->lines_in_unsealed_linetable = 0; - } - - t->agg_raw_input += len; - -resume: - - chars = 0; - lbh = t->c; - sline = t->line_number; - bp += g16(&linetable[bp], 0); - bp += g16(&linetable[bp], 0); - bp += g32(&linetable[bp], 0); - - while (len) { - char go_around = 0; - - if (t->lines_in_unsealed_linetable >= LWS_FTS_LINES_PER_CHUNK) - break; - - len--; - - c = (unsigned char)*buf++; - t->chars_in_line++; - if (c == '\n') { - skipline = 0; - t->filepath_list->total_lines++; - t->lines_in_unsealed_linetable++; - t->line_number++; - - bp += wq32(&linetable[bp], t->chars_in_line); - if ((unsigned int)bp > sizeof(linetable) - 6) { - if (write(t->fd, linetable, bp) != bp) { - lwsl_err("%s: linetable write failed\n", - __func__); - return 1; - } - t->c += bp; - bp = 0; - // assert(lseek(t->fd, 0, SEEK_END) == t->c); - } - - chars += t->chars_in_line; - t->chars_in_line = 0; - - /* - * Detect overlength lines and skip them (eg, BASE64 - * in css etc) - */ - - if (len > 200) { - n = 0; - m = 0; - while (n < 200 && m < 80 && buf[n] != '\n') { - if (buf[n] == ' ' || buf[n] == '\t') - m = 0; - n++; - m++; - } - - /* 80 lines no whitespace, or >=200-char line */ - - if (m == 80 || n == 200) - skipline = 1; - } - - goto seal; - } - if (skipline) - continue; - - m = classify[(int)c]; - if (!m) - goto seal; - if (m == 2) - c += 'a' - 'A'; - - if (t->aggregate) { - - /* - * We created a trie entry for an earlier char in this - * symbol already. So we know at the moment, any - * further chars in the symbol are the only children. - * - * Aggregate them and add them as a string suffix to - * the trie symbol at the end (when we know how much to - * allocate). - */ - - if (t->agg_pos < sizeof(t->agg) - 1) - /* symbol is not too long to stash */ - t->agg[t->agg_pos++] = c; - - continue; - } - - if (t->str_match_pos) { - go_around = 1; - goto seal; - } - - /* zeroth-iteration child matching */ - - if (t->parser == t->root) { - e = t->root_lookup[(int)c]; - if (e) { - t->parser = e; - continue; - } - } else { - - /* look for the char amongst the children */ - - e = t->parser->child_list; - while (e) { - - /* since they're alpha ordered... */ - if (e->c > c) { - e = NULL; - break; - } - if (e->c == c) { - t->parser = e; - - if (e->suffix) - t->str_match_pos = 1; - - break; - } - - e = e->sibling; - } - - if (e) - continue; - } - - /* - * we are blazing a new trail, add a new child representing - * the whole suffix that couldn't be matched until now. - */ - - e = lws_fts_entry_child_add(t, c, t->parser); - if (!e) { - lwsl_err("%s: lws_fts_entry_child_add failed\n", - __func__); - return 1; - } - - /* if it's the root node, keep the root_lookup table in sync */ - - if (t->parser == t->root) - t->root_lookup[(int)c] = e; - - /* follow the new path */ - t->parser = e; - - { - struct lws_fts_entry **pe = &e->child_list; - while (*pe) { - assert((*pe)->parent == e); - - pe = &(*pe)->sibling; - } - } - - /* - * If there are any more symbol characters coming, just - * create a suffix string on t->parser instead of what must - * currently be single-child nodes, since we just created e - * as a child with a single character due to no existing match - * on that single character... so if no match on 'h' with this - * guy's parent, we created e that matches on the single char - * 'h'. If the symbol continues ... 'a' 'p' 'p' 'y', then - * instead of creating singleton child nodes under e, - * modify e to match on the whole string suffix "happy". - * - * If later "hoppy" appears, we will remove the suffix on e, - * so it reverts to a char match for 'h', add singleton children - * for 'a' and 'o', and attach a "ppy" suffix child to each of - * those. - * - * We want to do this so we don't have to allocate trie entries - * for every char in the string to save memory and consequently - * time. - * - * Don't try this optimization if the parent is the root node... - * it's not compatible with it's root_lookup table and it's - * highly likely children off the root entry are going to have - * to be fragmented. - */ - - if (e->parent != t->root) { - t->aggregate = 1; - t->agg_pos = 0; - } - - continue; - -seal: - if (t->str_match_pos) { - - /* - * We're partway through matching an elaborated string - * on a child, not just a character. String matches - * only exist when we met a child entry that only had - * one path until now... so we had an 'h', and the - * only child had a string "hello". - * - * We are following the right path and will not need - * to back up, but we may find as we go we have the - * first instance of a second child path, eg, "help". - * - * When we get to the 'p', we have to split what was - * the only string option "hello" into "hel" and then - * two child entries, for "lo" and 'p'. - */ - - if (c == t->parser->suffix[t->str_match_pos++]) { - if (t->str_match_pos < t->parser->suffix_len) - continue; - - /* - * We simply matched everything, continue - * parsing normally from this trie entry. - */ - - t->str_match_pos = 0; - continue; - } - - /* - * So... we hit a mismatch somewhere... it means we - * have to split this string entry. - * - * We know the first char actually matched in order to - * start down this road. So for the current trie entry, - * we need to truncate his suffix at the char before - * this mismatched one, where we diverged (if the - * second char, simply remove the suffix string from the - * current trie entry to turn it back to a 1-char match) - * - * The original entry, which becomes the lhs post-split, - * is t->parser. - */ - - olen = t->parser->suffix_len; - osuff = t->parser->suffix; - - if (t->str_match_pos == 2) - t->parser->suffix = NULL; - else - t->parser->suffix_len = t->str_match_pos - 1; - - /* - * Then we need to create a new child trie entry that - * represents the remainder of the original string - * path that we didn't match. For the "hello" / - * "help" case, this guy will have "lo". - * - * Any instances or children (not siblings...) that were - * attached to the original trie entry must be detached - * first and then migrate to this new guy that completes - * the original string. - */ - - dcl = t->parser->child_list; - m = t->parser->child_count; - - t->parser->child_list = NULL; - t->parser->child_count = 0; - - e = lws_fts_entry_child_add(t, - osuff[t->str_match_pos - 1], t->parser); - if (!e) { - lwsl_err("%s: lws_fts_entry_child_add fail1\n", - __func__); - return 1; - } - - e->child_list = dcl; - e->child_count = m; - /* - * any children we took over must point to us as the - * parent now they appear on our child list - */ - e1 = e->child_list; - while (e1) { - e1->parent = e; - e1 = e1->sibling; - } - - /* - * We detached any children, gave them to the new guy - * and replaced them with just our new guy - */ - t->parser->child_count = 1; - t->parser->child_list = e; - - /* - * any instances that belonged to the original entry we - * are splitting now must be reassigned to the end - * part - */ - - e->inst_file_list = t->parser->inst_file_list; - if (e->inst_file_list) - e->inst_file_list->owner = e; - t->parser->inst_file_list = NULL; - e->instance_count = t->parser->instance_count; - t->parser->instance_count = 0; - - e->ofs_last_inst_file = t->parser->ofs_last_inst_file; - t->parser->ofs_last_inst_file = 0; - - if (t->str_match_pos != olen) { - /* we diverged partway */ - e->suffix = &osuff[t->str_match_pos - 1]; - e->suffix_len = olen - (t->str_match_pos - 1); - } - - /* - * if the current char is a terminal, skip creating a - * new way forward. - */ - - if (classify[(int)c]) { - - /* - * Lastly we need to create a new child trie - * entry that represents the new way forward - * from the point that we diverged. For the - * "hello" / "help" case, this guy will start - * as a child of "hel" with the single - * character match 'p'. - * - * Since he becomes the current parser context, - * more symbol characters may be coming to make - * him into, eg, "helping", in which case he - * will acquire a suffix eventually of "ping" - * via the aggregation stuff - */ - - e = lws_fts_entry_child_add(t, c, t->parser); - if (!e) { - lwsl_err("%s: child_add fail2\n", - __func__); - return 1; - } - } - - /* go on following this path */ - t->parser = e; - - t->aggregate = 1; - t->agg_pos = 0; - - t->str_match_pos = 0; - - if (go_around) - continue; - - /* this is intended to be a seal */ - } - - - /* end of token */ - - if (t->aggregate && t->agg_pos) { - - /* if nothing in agg[]: leave as single char match */ - - /* otherwise copy out the symbol aggregation */ - t->parser->suffix = lwsac_use(&t->lwsac_head, - t->agg_pos + 1, - TRIE_LWSAC_BLOCK_SIZE); - if (!t->parser->suffix) { - lwsl_err("%s: lac for suffix failed\n", - __func__); - return 1; - } - - /* add the first char at the beginning */ - *t->parser->suffix = t->parser->c; - /* and then add the agg buffer stuff */ - memcpy(t->parser->suffix + 1, t->agg, t->agg_pos); - t->parser->suffix_len = t->agg_pos + 1; - } - t->aggregate = 0; - - if (t->parser == t->root) /* multiple terminal chars */ - continue; - - if (!t->parser->inst_file_list || - t->parser->inst_file_list->file_index != file_index) { - tif = lwsac_use(&t->lwsac_input_head, sizeof(*tif), - TRIE_LWSAC_BLOCK_SIZE); - if (!tif) { - lwsl_err("%s: lac for tif failed\n", - __func__); - return 1; - } - - tif->file_index = file_index; - tif->owner = t->parser; - tif->lines_list = NULL; - tif->lines_tail = NULL; - tif->total = 0; - tif->count = 0; - tif->inst_file_next = t->tif_list; - t->tif_list = tif; - - t->parser->inst_file_list = tif; - } - - /* - * A naive allocation strategy for this leads to 50% of the - * total inmem lac allocation being for line numbers... - * - * It's mainly solved by only holding the instance and line - * number tables for the duration of a file being input, as soon - * as one input file is finished it is written to disk. - * - * For the common case of 1 - ~3 matches the line number are - * stored in a small VLI array inside the filepath inst. If the - * next one won't fit, it allocates a line number struct with - * more vli space and continues chaining those if needed. - */ - - n = wq32(vlibuf, t->line_number); - tif = t->parser->inst_file_list; - - if (!tif->lines_list) { - /* we are still trying to use the file inst vli */ - if (LWS_ARRAY_SIZE(tif->vli) - tif->count >= n) { - tif->count += wq32(tif->vli + tif->count, - t->line_number); - goto after; - } - /* we are going to have to allocate */ - } - - /* can we add to an existing line numbers struct? */ - if (tif->lines_tail && - LWS_ARRAY_SIZE(tif->lines_tail->vli) - - tif->lines_tail->count >= n) { - tif->lines_tail->count += wq32(tif->lines_tail->vli + - tif->lines_tail->count, - t->line_number); - goto after; - } - - /* either no existing line numbers struct at tail, or full */ - - /* have to create a(nother) line numbers struct */ - tl = lwsac_use(&t->lwsac_input_head, sizeof(*tl), - TRIE_LWSAC_BLOCK_SIZE); - if (!tl) { - lwsl_err("%s: lac for tl failed\n", __func__); - return 1; - } - tl->lines_next = NULL; - if (tif->lines_tail) - tif->lines_tail->lines_next = tl; - - tif->lines_tail = tl; - if (!tif->lines_list) - tif->lines_list = tl; - - tl->count = wq32(tl->vli, t->line_number); -after: - tif->total++; -#if 0 - { - char s[128]; - const char *ne = name_entry(t->parser, s, sizeof(s)); - - if (!strcmp(ne, "describ")) { - lwsl_err(" %s %d\n", ne, t->str_match_pos); - write(1, buf - 10, 20); - } - } -#endif - t->parser->instance_count++; - t->parser = t->root; - t->str_match_pos = 0; - } - - /* seal off the line length table block */ - - if (bp) { - if (write(t->fd, linetable, bp) != bp) - return 1; - t->c += bp; - bp = 0; - } - - if (lseek(t->fd, lbh, SEEK_SET) < 0) { - lwsl_err("%s: seek to 0x%llx failed\n", __func__, - (unsigned long long)lbh); - return 1; - } - - g16(linetable, t->c - lbh); - g16(linetable + 2, t->line_number - sline); - g32(linetable + 4, chars); - if (write(t->fd, linetable, 8) != 8) { - lwsl_err("%s: write linetable header failed\n", __func__); - return 1; - } - - assert(lseek(t->fd, 0, SEEK_END) == (off_t)t->c); - - if (lseek(t->fd, t->c, SEEK_SET) < 0) { - lwsl_err("%s: end seek failed\n", __func__); - return 1; - } - - bp = 0; - - if (len) { - t->lines_in_unsealed_linetable = 0; - goto resume; - } - - /* dump the collected per-input instance and line data, and free it */ - - t->agg_trie_creation_us += lws_now_usecs() - tf; - - return 0; -} - -/* refer to ./README.md */ - -int -lws_fts_serialize(struct lws_fts *t) -{ - struct lws_fts_filepath *fp = t->filepath_list, *ofp; - unsigned long long tf = lws_now_usecs(); - struct lws_fts_entry *e, *e1, *s[256]; - unsigned char buf[8192], stasis; - int n, bp, sp = 0, do_parent; - - (void)tf; - finalize_per_input(t); - - /* - * Compute aggregated instance counts (parents should know the total - * number of instances below each child path) - * - * - * If we have - * - * (root) -> (c1) -> (c2) - * -> (c3) - * - * we need to visit the nodes in the order - * - * c2, c1, c3, root - */ - - sp = 0; - s[0] = t->root; - do_parent = 0; - while (sp >= 0) { - int n; - - /* aggregate in every antecedent */ - - for (n = 0; n <= sp; n++) { - s[n]->agg_inst_count += s[sp]->instance_count; - s[n]->agg_child_count += s[sp]->child_count; - } - - /* handle any children before the parent */ - - if (s[sp]->child_list) { - if (sp + 1 == LWS_ARRAY_SIZE(s)) { - lwsl_err("Stack too deep\n"); - - goto bail; - } - - s[sp + 1] = s[sp]->child_list; - sp++; - continue; - } - - do { - if (s[sp]->sibling) { - s[sp] = s[sp]->sibling; - break; - } else - sp--; - } while (sp >= 0); - } - - /* dump the filepaths and set prev */ - - fp = t->filepath_list; - ofp = NULL; - bp = 0; - while (fp) { - - fp->ofs = t->c + bp; - n = (int)strlen(fp->filepath); - spill(15 + n, 0); - - bp += wq32(&buf[bp], fp->line_table_ofs); - bp += wq32(&buf[bp], fp->total_lines); - bp += wq32(&buf[bp], n); - memcpy(&buf[bp], fp->filepath, n); - bp += n; - - fp->prev = ofp; - ofp = fp; - fp = fp->next; - } - - spill(0, 1); - - /* record the fileoffset of the filepath map and filepath count */ - - if (lseek(t->fd, 0xc, SEEK_SET) < 0) - goto bail_seek; - - g32(buf, t->c + bp); - g32(buf + 4, t->next_file_index); - if (write(t->fd, buf, 8) != 8) - goto bail; - - if (lseek(t->fd, t->c + bp, SEEK_SET) < 0) - goto bail_seek; - - /* dump the filepath map, starting from index 0, which is at the tail */ - - fp = ofp; - bp = 0; - while (fp) { - spill(5, 0); - g32(buf + bp, fp->ofs); - bp += 4; - fp = fp->prev; - } - spill(0, 1); - - /* - * The trie entries in reverse order... because of the reversal, we have - * always written children first, and marked them with their file offset - * before we come to refer to them. - */ - - bp = 0; - sp = 0; - s[0] = t->root; - do_parent = 0; - while (s[sp]) { - - /* handle any children before the parent */ - - if (!do_parent && s[sp]->child_list) { - - if (sp + 1 == LWS_ARRAY_SIZE(s)) { - lwsl_err("Stack too deep\n"); - - goto bail; - } - - s[sp + 1] = s[sp]->child_list; - sp++; - continue; - } - - /* leaf nodes with no children */ - - e = s[sp]; - e->ofs = t->c + bp; - - /* write the trie entry header */ - - spill((3 * MAX_VLI), 0); - - bp += wq32(&buf[bp], e->ofs_last_inst_file); - bp += wq32(&buf[bp], e->child_count); - bp += wq32(&buf[bp], e->instance_count); - bp += wq32(&buf[bp], e->agg_inst_count); - - /* sort the children in order of highest aggregate hits first */ - - do { - struct lws_fts_entry **pe, *te1, *te2; - - stasis = 1; - - /* bubble sort keeps going until nothing changed */ - - pe = &e->child_list; - while (*pe) { - - te1 = *pe; - te2 = te1->sibling; - - if (te2 && te1->agg_inst_count < - te2->agg_inst_count) { - stasis = 0; - - *pe = te2; - te1->sibling = te2->sibling; - te2->sibling = te1; - } - - pe = &(*pe)->sibling; - } - - } while (!stasis); - - /* write the children */ - - e1 = e->child_list; - while (e1) { - spill((5 * MAX_VLI) + e1->suffix_len + 1, 0); - - bp += wq32(&buf[bp], e1->ofs); - bp += wq32(&buf[bp], e1->instance_count); - bp += wq32(&buf[bp], e1->agg_inst_count); - bp += wq32(&buf[bp], e1->agg_child_count); - - if (e1->suffix) { /* string */ - bp += wq32(&buf[bp], e1->suffix_len); - memmove(&buf[bp], e1->suffix, e1->suffix_len); - bp += e1->suffix_len; - } else { /* char */ - bp += wq32(&buf[bp], 1); - buf[bp++] = e1->c; - } -#if 0 - if (e1->suffix && e1->suffix_len == 3 && - !memcmp(e1->suffix, "cri", 3)) { - struct lws_fts_entry *e2; - - e2 = e1; - while (e2){ - if (e2->suffix) - lwsl_notice("%s\n", e2->suffix); - else - lwsl_notice("%c\n", e2->c); - - e2 = e2->parent; - } - - lwsl_err("*** %c CRI inst %d ch %d\n", e1->parent->c, - e1->instance_count, e1->child_count); - } -#endif - e1 = e1->sibling; - } - - /* if there are siblings, do those next */ - - if (do_parent) { - do_parent = 0; - sp--; - } - - if (s[sp]->sibling) - s[sp] = s[sp]->sibling; - else { - /* if there are no siblings, do the parent */ - do_parent = 1; - s[sp] = s[sp]->parent; - } - } - - spill(0, 1); - - assert(lseek(t->fd, 0, SEEK_END) == (off_t)t->c); - - /* drop the correct root trie offset + file length into the header */ - - if (lseek(t->fd, 4, SEEK_SET) < 0) { - lwsl_err("%s: unable to seek\n", __func__); - - goto bail; - } - - g32(buf, t->root->ofs); - g32(buf + 4, t->c); - if (write(t->fd, buf, 0x8) != 0x8) - goto bail; - - lwsl_notice("%s: index %d files (%uMiB) cpu time %dms, " - "alloc: %dKiB + %dKiB, " - "serialize: %dms, file: %dKiB\n", __func__, - t->next_file_index, - (int)(t->agg_raw_input / (1024 * 1024)), - (int)(t->agg_trie_creation_us / 1000), - (int)(lwsac_total_alloc(t->lwsac_head) / 1024), - (int)(t->worst_lwsac_input_size / 1024), - (int)((lws_now_usecs() - tf) / 1000), - (int)(t->c / 1024)); - - return 0; - -bail_seek: - lwsl_err("%s: problem seekings\n", __func__); - -bail: - return 1; -} - - diff -Nru libwebsockets-4.0.20/lib/misc/fts/trie-fd.c libwebsockets-2.4.2/lib/misc/fts/trie-fd.c --- libwebsockets-4.0.20/lib/misc/fts/trie-fd.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/fts/trie-fd.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1004 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-misc-fts.h" - -#include -#include -#include -#include -#include -#include - -#define AC_COUNT_STASHED_CHILDREN 8 - -struct ch { - jg2_file_offset ofs; - char name[64]; - int inst; - int child_agg; - int name_length; - int effpos; - int descendents; -}; - -struct wac { - struct ch ch[AC_COUNT_STASHED_CHILDREN]; - - jg2_file_offset self; - jg2_file_offset tifs; - int child_count; - int child; - - int agg; - int desc; - char done_children; - char once; -}; - -struct linetable { - struct linetable *next; - - int chunk_line_number_start; - int chunk_line_number_count; - - off_t chunk_filepos_start; - - off_t vli_ofs_in_index; -}; - -static uint32_t -b32(unsigned char *b) -{ - return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; -} - -static uint16_t -b16(unsigned char *b) -{ - return (b[0] << 8) | b[1]; -} - -static int -lws_fts_filepath(struct lws_fts_file *jtf, int filepath_index, char *result, - size_t len, uint32_t *ofs_linetable, uint32_t *lines) -{ - unsigned char buf[256 + 15]; - uint32_t flen; - int ra, bp = 0; - size_t m; - off_t o; - - if (filepath_index > jtf->filepaths) - return 1; - - if (lseek(jtf->fd, jtf->filepath_table + (4 * filepath_index), - SEEK_SET) < 0) { - lwsl_err("%s: unable to seek\n", __func__); - - return 1; - } - - ra = read(jtf->fd, buf, 4); - if (ra < 0) - return 1; - - o = (unsigned int)b32(buf); - if (lseek(jtf->fd, o, SEEK_SET) < 0) { - lwsl_err("%s: unable to seek\n", __func__); - - return 1; - } - - ra = read(jtf->fd, buf, sizeof(buf)); - if (ra < 0) - return 1; - - if (ofs_linetable) - bp += rq32(&buf[bp], ofs_linetable); - else - bp += rq32(&buf[bp], &flen); - if (lines) - bp += rq32(&buf[bp], lines); - else - bp += rq32(&buf[bp], &flen); - bp += rq32(&buf[bp], &flen); - - m = flen; - if (len - 1 < m) - m = flen - 1; - - strncpy(result, (char *)&buf[bp], m); - result[m] = '\0'; - result[len - 1] = '\0'; - - return 0; -} - -/* - * returns -1 for fail or fd open on the trie file. - * - * *root is set to the position of the root trie entry. - * *flen is set to the length of the whole file - */ - -int -lws_fts_adopt(struct lws_fts_file *jtf) -{ - unsigned char buf[256]; - off_t ot; - - if (read(jtf->fd, buf, TRIE_FILE_HDR_SIZE) != TRIE_FILE_HDR_SIZE) { - lwsl_err("%s: unable to read file header\n", __func__); - goto bail; - } - - if (buf[0] != 0xca || buf[1] != 0x7a || - buf[2] != 0x5f || buf[3] != 0x75) { - lwsl_err("%s: bad magic %02X %02X %02X %02X\n", __func__, - buf[0], buf[1], buf[2], buf[3]); - goto bail; - } - - jtf->root = b32(&buf[4]); - - ot = lseek(jtf->fd, 0, SEEK_END); - if (ot < 0) { - lwsl_err("%s: unable to seek\n", __func__); - - goto bail; - } - jtf->flen = ot; - - if (jtf->flen != b32(&buf[8])) { - lwsl_err("%s: file size doesn't match expected\n", __func__); - - goto bail; - } - - jtf->filepath_table = b32(&buf[12]); - jtf->filepaths = b32(&buf[16]); - - return jtf->fd; - -bail: - return -1; -} - -struct lws_fts_file * -lws_fts_open(const char *filepath) -{ - struct lws_fts_file *jtf; - - jtf = lws_malloc(sizeof(*jtf), "fts open"); - if (!jtf) - goto bail1; - - jtf->fd = open(filepath, O_RDONLY); - if (jtf->fd < 0) { - lwsl_err("%s: unable to open %s\n", __func__, filepath); - goto bail2; - } - - if (lws_fts_adopt(jtf) < 0) - goto bail3; - - return jtf; - -bail3: - close(jtf->fd); -bail2: - lws_free(jtf); -bail1: - return NULL; -} - -void -lws_fts_close(struct lws_fts_file *jtf) -{ - close(jtf->fd); - lws_free(jtf); -} - -#define grab(_pos, _size) { \ - bp = 0; \ - if (lseek(jtf->fd, _pos, SEEK_SET) < 0) { \ - lwsl_err("%s: unable to seek\n", __func__); \ -\ - goto bail; \ - } \ -\ - ra = read(jtf->fd, buf, _size); \ - if (ra < 0) \ - goto bail; \ -} - -static struct linetable * -lws_fts_cache_chunktable(struct lws_fts_file *jtf, uint32_t ofs_linetable, - struct lwsac **linetable_head) -{ - struct linetable *lt, *first = NULL, **prev = NULL; - unsigned char buf[8]; - int line = 1, bp, ra; - off_t cfs = 0; - - *linetable_head = NULL; - - do { - grab(ofs_linetable, sizeof(buf)); - - lt = lwsac_use(linetable_head, sizeof(*lt), 0); - if (!lt) - goto bail; - if (!first) - first = lt; - - lt->next = NULL; - if (prev) - *prev = lt; - prev = <->next; - - lt->chunk_line_number_start = line; - lt->chunk_line_number_count = b16(&buf[bp + 2]); - lt->vli_ofs_in_index = ofs_linetable + 8; - lt->chunk_filepos_start = cfs; - - line += lt->chunk_line_number_count; - - cfs += b32(&buf[bp + 4]); - ofs_linetable += b16(&buf[bp]); - - } while (b16(&buf[bp])); - - return first; - -bail: - lwsac_free(linetable_head); - - return NULL; -} - -static int -lws_fts_getfileoffset(struct lws_fts_file *jtf, struct linetable *ltstart, - int line, off_t *_ofs) -{ - struct linetable *lt = ltstart; - unsigned char buf[LWS_FTS_LINES_PER_CHUNK * 5]; - uint32_t ll; - off_t ofs; - int bp, ra; - - /* first figure out which chunk */ - - do { - if (line >= lt->chunk_line_number_start && - line < lt->chunk_line_number_start + - lt->chunk_line_number_count) - break; - - lt = lt->next; - } while (lt); - - if (!lt) - goto bail; - - /* we know it's in this chunk */ - - ofs = lt->chunk_filepos_start; - line -= lt->chunk_line_number_start; - - grab(lt->vli_ofs_in_index, sizeof(buf)); - - bp = 0; - while (line) { - bp += rq32(&buf[bp], &ll); - ofs += ll; - line--; - } - - /* we know the offset it is at in the original file */ - - *_ofs = ofs; - - return 0; - -bail: - lwsl_info("%s: bail %d\n", __func__, line); - - return 1; -} - -static int -ac_record(struct lws_fts_file *jtf, struct lwsac **results_head, - const char *needle, int pos, struct wac *s, int sp, - uint32_t instances, uint32_t agg_instances, uint32_t children, - struct lws_fts_result_autocomplete ***ppac) -{ - struct lws_fts_result_autocomplete *ac; - int n, m; - char *p; - - if (!instances && !agg_instances) - return 1; - - m = pos; - for (n = 1; n <= sp; n++) - m += s[n].ch[s[n].child - 1].name_length; - - ac = lwsac_use(results_head, sizeof(*ac) + m + 1, 0); - if (!ac) - return -1; - - p = (char *)(ac + 1); - - **ppac = ac; - ac->next = NULL; - *ppac = &ac->next; - ac->instances = instances; - ac->agg_instances = agg_instances; - ac->ac_length = m; - ac->has_children = !!children; - ac->elided = 0; - - memcpy(p, needle, pos); - p += pos; - - for (n = 1; n <= sp; n++) { - int w = s[n].child - 1; - - memcpy(p, s[n].ch[w].name, s[n].ch[w].name_length); - p += s[n].ch[w].name_length; - } - p = (char *)(ac + 1); - p[m] = '\0'; - - /* - * deduct this child's instance weight from his antecdents to track - * relative path attractiveness dynamically, after we already used its - * best results (children are sorted best-first) - */ - for (n = sp; n >= 0; n--) { - s[n].ch[s[n].child - 1].child_agg -= instances; - s[n].agg -= instances; - } - - return 0; -} - -struct lws_fts_result * -lws_fts_search(struct lws_fts_file *jtf, struct lws_fts_search_params *ftsp) -{ - uint32_t children, instances, co, sl, agg, slt, chunk, - fileofs_tif_start, desc, agg_instances; - int pos = 0, n, m, nl, bp, base = 0, ra, palm, budget, sp, ofd = -1; - unsigned long long tf = lws_now_usecs(); - struct lws_fts_result_autocomplete **pac = NULL; - char stasis, nac = 0, credible, needle[32]; - struct lws_fts_result_filepath *fp; - struct lws_fts_result *result; - unsigned char buf[4096]; - off_t o, child_ofs; - struct wac s[128]; - - ftsp->results_head = NULL; - - if (!ftsp->needle) - return NULL; - - nl = (int)strlen(ftsp->needle); - if ((size_t)nl > sizeof(needle) - 2) - return NULL; - - result = lwsac_use(&ftsp->results_head, sizeof(*result), 0); - if (!result) - return NULL; - - /* start with no results... */ - - result->autocomplete_head = NULL; - pac = &result->autocomplete_head; - result->filepath_head = NULL; - result->duration_ms = 0; - result->effective_flags = ftsp->flags; - - palm = 0; - - for (n = 0; n < nl; n++) - needle[n] = tolower(ftsp->needle[n]); - needle[nl] = '\0'; - - o = jtf->root; - do { - bp = 0; - base = 0; - - grab(o, sizeof(buf)); - - child_ofs = o + bp; - bp += rq32(&buf[bp], &fileofs_tif_start); - bp += rq32(&buf[bp], &children); - bp += rq32(&buf[bp], &instances); - bp += rq32(&buf[bp], &agg_instances); - palm = pos; - - /* the children follow here */ - - if (pos == nl) { - - nac = 0; - if (!fileofs_tif_start) - /* - * we matched, but there are no instances of - * this, it's actually an intermediate - */ - - goto autocomp; - - /* we leave with bp positioned at the instance list */ - - o = fileofs_tif_start; - grab(o, sizeof(buf)); - break; - } - - if (ra - bp < 1024) { - - /* - * We don't have enough. So reload the buffer starting - * at where we got to. - */ - - base += bp; - grab(o + base, sizeof(buf)); - } - - /* gets set if any child COULD match needle if it went on */ - - credible = 0; - for (n = 0; (uint32_t)n < children; n++) { - uint32_t inst; - - bp += rq32(&buf[bp], &co); - bp += rq32(&buf[bp], &inst); - bp += rq32(&buf[bp], &agg); - bp += rq32(&buf[bp], &desc); - bp += rq32(&buf[bp], &sl); - - if (sl > (uint32_t)(nl - pos)) { - - /* - * it can't be a match because it's longer than - * our needle string (but that leaves it as a - * perfectly fine autocomplete candidate) - */ - size_t g = nl - pos; - - /* - * "credible" means at least one child matches - * all the chars in needle up to as many as it - * has. If not "credible" this path cannot - * match. - */ - if (!strncmp((char *)&buf[bp], &needle[pos], g)) - credible = 1; - else - /* - * deflate the parent agg using the - * knowledge this child is not on the - * path shown by the remainder of needle - */ - agg_instances -= agg; - - nac = 0; - bp += sl; - slt = 0; - pos = palm; - goto ensure; - } - - /* the comparison string potentially has huge length */ - - slt = sl; - while (slt) { - - /* - * the strategy is to compare whatever we have - * lying around, then bring in more if it didn't - * fail to match yet. That way we don't bring - * in anything we could already have known was - * not needed due to a match fail. - */ - - chunk = ra - bp; - if (chunk > slt) - chunk = slt; - - if ((chunk == 1 && needle[pos] != buf[bp]) || - (chunk != 1 && - memcmp(&needle[pos], &buf[bp], chunk))) { - - /* - * it doesn't match... so nothing can - * autocomplete this... - */ - bp += slt; - slt = 0; - nac = 1; - goto ensure; - } - - slt -= chunk; - pos += chunk; - bp += chunk; - - /* so far, it matches */ - - if (!slt) { - /* we matched the whole thing */ - o = co; - if (!co) - goto bail; - n = (int)children; - credible = 1; - } - -ensure: - /* - * do we have at least buf more to match, or the - * remainder of the string, whichever is less? - * - * bp may exceed sizeof(buf) on no match path - */ - chunk = sizeof(buf); - if (slt < chunk) - chunk = slt; - - if (ra - bp >= (int)chunk) - continue; - - /* - * We don't have enough. So reload buf starting - * at where we got to. - */ - base += bp; - grab(o + base, sizeof(buf)); - - } /* while we are still comparing */ - - } /* for each child */ - - if ((uint32_t)n == children) { - if (!credible) - goto bail; - - nac = 0; - goto autocomp; - } - } while(1); - - result->duration_ms = (int)((lws_now_usecs() - tf) / 1000); - - if (!instances && !children) - return result; - - /* the match list may easily exceed one read buffer load ... */ - - o += bp; - - /* - * Only do the file match list if it was requested in the search flags - */ - - if (!(ftsp->flags & LWSFTS_F_QUERY_FILES)) - goto autocomp; - - do { - uint32_t fi, tot, line, ro, ofs_linetable, lines, fplen, - *u, _o; - struct lwsac *lt_head = NULL; - struct linetable *ltst; - char path[256], *pp; - int footprint; - off_t fo; - - ofd = -1; - grab(o, sizeof(buf)); - - ro = o; - bp += rq32(&buf[bp], &_o); - o = _o; - - assert(!o || o > TRIE_FILE_HDR_SIZE); - - bp += rq32(&buf[bp], &fi); - bp += rq32(&buf[bp], &tot); - - if (lws_fts_filepath(jtf, fi, path, sizeof(path) - 1, - &ofs_linetable, &lines)) { - lwsl_err("can't get filepath index %d\n", fi); - goto bail; - } - - if (ftsp->only_filepath && strcmp(path, ftsp->only_filepath)) - continue; - - ltst = lws_fts_cache_chunktable(jtf, ofs_linetable, <_head); - if (!ltst) - goto bail; - - if (ftsp->flags & LWSFTS_F_QUERY_QUOTE_LINE) { - ofd = open(path, O_RDONLY); - if (ofd < 0) { - lwsac_free(<_head); - goto bail; - } - } - - fplen = (int)strlen(path); - footprint = sizeof(*fp) + fplen + 1; - if (ftsp->flags & LWSFTS_F_QUERY_FILE_LINES) { - /* line number and offset in file */ - footprint += 2 * sizeof(uint32_t) * tot; - - if (ftsp->flags & LWSFTS_F_QUERY_QUOTE_LINE) - /* pointer to quote string */ - footprint += sizeof(void *) * tot; - } - - fp = lwsac_use(&ftsp->results_head, footprint, 0); - if (!fp) { - lwsac_free(<_head); - goto bail; - } - - fp->filepath_length = fplen; - fp->lines_in_file = lines; - fp->matches = tot; - fp->matches_length = footprint - sizeof(*fp) - (fplen + 1); - fp->next = result->filepath_head; - result->filepath_head = fp; - - /* line table first so it can be aligned */ - - u = (uint32_t*)(fp + 1); - - if (ftsp->flags & LWSFTS_F_QUERY_FILE_LINES) { - - /* for each line number */ - - for (n = 0; (uint32_t)n < tot; n++) { - - unsigned char lbuf[256], *p; - char ebuf[384]; - const char **v; - int m; - - if ((ra - bp) < 8) { - base += bp; - grab(ro + base, sizeof(buf)); - } - - bp += rq32(&buf[bp], &line); - *u++ = line; - - if (lws_fts_getfileoffset(jtf, ltst, line, &fo)) - continue; - - *u++ = (uint32_t)fo; - - if (!(ftsp->flags & LWSFTS_F_QUERY_QUOTE_LINE)) - continue; - - if (lseek(ofd, fo, SEEK_SET) < 0) - continue; - - m = read(ofd, lbuf, sizeof(lbuf) - 1); - if (m < 0) - continue; - lbuf[sizeof(lbuf) - 1] = '\0'; - - p = (unsigned char *)strchr((char *)lbuf, '\n'); - if (p) - m = lws_ptr_diff(p, lbuf); - lbuf[m] = '\0'; - p = (unsigned char *)strchr((char *)lbuf, '\r'); - if (p) - m = lws_ptr_diff(p, lbuf); - lbuf[m] = '\0'; - - lws_json_purify(ebuf, (const char *)lbuf, - sizeof(ebuf) - 1, NULL); - m = (int)strlen(ebuf); - - p = lwsac_use(&ftsp->results_head, m + 1, 0); - if (!p) { - lwsac_free(<_head); - goto bail; - } - - memcpy(p, ebuf, m); - p[m] = '\0'; - v = (const char **)u; - *v = (const char *)p; - u += sizeof(const char *) / sizeof(uint32_t); - } - } - - pp = ((char *)&fp[1]) + fp->matches_length; - memcpy(pp, path, fplen); - pp[fplen] = '\0'; - - if (ofd >= 0) { - close(ofd); - ofd = -1; - } - - lwsac_free(<_head); - - if (ftsp->only_filepath) - break; - - } while (o); - - /* sort the instance file list by results density */ - - do { - struct lws_fts_result_filepath **prf, *rf1, *rf2; - - stasis = 1; - - /* bubble sort keeps going until nothing changed */ - - prf = &result->filepath_head; - while (*prf) { - - rf1 = *prf; - rf2 = rf1->next; - - if (rf2 && rf1->lines_in_file && rf2->lines_in_file && - ((rf1->matches * 1000) / rf1->lines_in_file) < - ((rf2->matches * 1000) / rf2->lines_in_file)) { - stasis = 0; - - *prf = rf2; - rf1->next = rf2->next; - rf2->next = rf1; - } - - prf = &(*prf)->next; - } - - } while (!stasis); - -autocomp: - - if (!(ftsp->flags & LWSFTS_F_QUERY_AUTOCOMPLETE) || nac) - return result; - - /* - * autocomplete (ie, the descendent paths that yield the most hits) - * - * We actually need to spider the earliest terminal descendents from - * the child we definitely got past, and present the first n terminal - * strings. The descendents are already sorted in order of highest - * aggregated hits in their descendents first, so simply collecting n - * earliest leaf children is enough. - * - * The leaf children may be quite deep down in a stack however. So we - * have to go through all the walking motions collecting and retaining - * child into for when we come back up the walk. - * - * We can completely ignore file instances for this, we just need the - * earliest children. And we can restrict how many children we stash - * in each stack level to eg, 5. - * - * child_ofs comes in pointing at the start of the trie entry that is - * to be the starting point for making suggestions. - */ - - budget = ftsp->max_autocomplete; - base = 0; - bp = 0; - pac = &result->autocomplete_head; - sp = 0; - if (pos > (int)sizeof(s[sp].ch[0].name) - 1) - pos = (int)sizeof(s[sp].ch[0].name) - 1; - - memset(&s[sp], 0, sizeof(s[sp])); - - s[sp].child = 1; - s[sp].tifs = fileofs_tif_start; - s[sp].self = child_ofs; - s[sp].ch[0].effpos = pos; - - if (pos == nl) - n = ac_record(jtf, &ftsp->results_head, needle, pos, s, 0, - instances, agg_instances, children, &pac); - - while (sp >= 0 && budget) { - int nobump = 0; - struct ch *tch = &s[sp].ch[s[sp].child - 1]; - - grab(child_ofs, sizeof(buf)); - - bp += rq32(&buf[bp], &fileofs_tif_start); - bp += rq32(&buf[bp], &children); - bp += rq32(&buf[bp], &instances); - bp += rq32(&buf[bp], &agg_instances); - - if (sp > 0 && s[sp - 1].done_children && - tch->effpos + tch->name_length >= nl && - tch->inst && fileofs_tif_start) { - n = ac_record(jtf, &ftsp->results_head, needle, pos, s, - sp, tch->inst, tch->child_agg, - tch->descendents, &pac); - if (n < 0) - goto bail; - if (!n) - if (--budget == 0) - break; - } - - if (!s[sp].done_children && children) { - s[sp].done_children = 1; - sp++; - memset(&s[sp], 0, sizeof(s[sp])); - s[sp].tifs = fileofs_tif_start; - s[sp].self = child_ofs; - - for (n = 0; n < (int)children && s[sp].child_count < - (int)LWS_ARRAY_SIZE(s[0].ch); n++) { - uint32_t slen, cho, agg, inst; - int i = s[sp].child_count; - struct ch *ch = &s[sp].ch[i]; - size_t max; - - bp += rq32(&buf[bp], &cho); - bp += rq32(&buf[bp], &inst); - bp += rq32(&buf[bp], &agg); - bp += rq32(&buf[bp], &desc); - bp += rq32(&buf[bp], &slen); - - max = slen; - if (max > sizeof(ch->name) - 1) - max = sizeof(ch->name) - 1; - - strncpy(ch->name, (char *)&buf[bp], max); - bp += slen; - - ch->name_length = (int)max; - ch->name[sizeof(ch->name) - 1] = '\0'; - ch->inst = inst; - ch->effpos = - s[sp - 1].ch[s[sp - 1].child - 1].effpos; - - ch->child_agg = agg; - ch->descendents = desc; - - /* - * if we have more needle chars than we matched - * to get this far, we can only allow potential - * matches that are consistent with the - * additional unmatched character(s)... - */ - - m = nl - ch->effpos; - if (m > ch->name_length) - m = ch->name_length; - - if (m > 0 && - strncmp(&needle[ch->effpos], ch->name, m)) - continue; - - ch->effpos += m; - s[sp].ch[s[sp].child_count++].ofs = cho; - } - - } - - while (sp >= 0 && s[sp].child >= s[sp].child_count) { - s[sp].done_children = 0; - sp--; - } - - /* - * Compare parent remaining agg vs parent's next siblings' still - * intact original agg... if the next sibling has more, abandon - * the parent path and go with the sibling... this keeps the - * autocomplete results related to popularity. - */ - - nobump = 0; - n = sp - 1; - while (n >= 0) { - struct lws_fts_result_autocomplete *ac = - (struct lws_fts_result_autocomplete *)pac; - - if (s[n].child < s[n].child_count && - s[n].ch[s[n].child - 1].child_agg < - s[n].ch[s[n].child].child_agg) { - - if (pac) - /* - * mark the autocomplete result that - * there were more children down his - * path that we skipped in these results - */ - ac->elided = 1; - - for (m = n; m < sp + 1; m++) - s[m].done_children = 0; - sp = n; - child_ofs = s[sp].ch[s[sp].child++].ofs; - nobump = 1; - } - - n--; - } - - if (nobump || sp < 0) - continue; - - child_ofs = s[sp].ch[s[sp].child++].ofs; - } - - /* let's do a final sort into agg order */ - - do { - struct lws_fts_result_autocomplete *ac1, *ac2; - - stasis = 1; - - /* bubble sort keeps going until nothing changed */ - - pac = &result->autocomplete_head; - while (*pac) { - - ac1 = *pac; - ac2 = ac1->next; - - if (ac2 && ac1->instances < ac2->instances) { - stasis = 0; - - *pac = ac2; - ac1->next = ac2->next; - ac2->next = ac1; - } - - pac = &(*pac)->next; - } - - } while (!stasis); - - return result; - -bail: - if (ofd >= 0) - close(ofd); - - lwsl_info("%s: search ended up at bail\n", __func__); - - return result; -} diff -Nru libwebsockets-4.0.20/lib/misc/getifaddrs.c libwebsockets-2.4.2/lib/misc/getifaddrs.c --- libwebsockets-4.0.20/lib/misc/getifaddrs.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/getifaddrs.c 2018-03-08 10:28:37.000000000 +0000 @@ -43,9 +43,7 @@ #include #include #include - -#include -#include "private-lib-core.h" +#include "private-libwebsockets.h" #ifdef LWS_HAVE_SYS_SOCKIO_H #include diff -Nru libwebsockets-4.0.20/lib/misc/lejp.c libwebsockets-2.4.2/lib/misc/lejp.c --- libwebsockets-4.0.20/lib/misc/lejp.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/lejp.c 2018-03-08 10:28:37.000000000 +0000 @@ -1,58 +1,15 @@ /* - * libwebsockets - small server side websockets and web server implementation + * Lightweight Embedded JSON Parser * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. + * Copyright (C) 2013 Andy Green + * This code is licensed under LGPL 2.1 + * http://www.gnu.org/licenses/lgpl-2.1.html */ -#include -#include "private-lib-core.h" #include -#include +#include "lejp.h" -static const char * const parser_errs[] = { - "", - "", - "No opening '{'", - "Expected closing '}'", - "Expected '\"'", - "String underrun", - "Illegal unescaped control char", - "Illegal escape format", - "Illegal hex number", - "Expected ':'", - "Illegal value start", - "Digit required after decimal point", - "Bad number format", - "Bad exponent format", - "Unknown token", - "Too many ']'", - "Mismatched ']'", - "Expected ']'", - "JSON nesting limit exceeded", - "Nesting tracking used up", - "Number too long", - "Comma or block end expected", - "Unknown", - "Parser callback errored (see earlier error)", -}; +#include /** * lejp_construct - prepare a struct lejp_ctx for use @@ -61,7 +18,7 @@ * \param callback: your user callback which will received parsed tokens * \param user: optional user data pointer untouched by lejp * \param paths: your array of name elements you are interested in - * \param count_paths: LWS_ARRAY_SIZE() of @paths + * \param count_paths: ARRAY_SIZE() of @paths * * Prepares your context struct for use with lejp */ @@ -77,20 +34,15 @@ ctx->st[0].b = 0; ctx->sp = 0; ctx->ipos = 0; + ctx->ppos = 0; ctx->path_match = 0; - ctx->path_stride = 0; ctx->path[0] = '\0'; + ctx->callback = callback; ctx->user = user; + ctx->paths = paths; + ctx->count_paths = count_paths; ctx->line = 1; - - ctx->pst_sp = 0; - ctx->pst[0].callback = callback; - ctx->pst[0].paths = paths; - ctx->pst[0].count_paths = count_paths; - ctx->pst[0].user = NULL; - ctx->pst[0].ppos = 0; - - ctx->pst[0].callback(ctx, LEJPCB_CONSTRUCTED); + ctx->callback(ctx, LEJPCB_CONSTRUCTED); } /** @@ -107,7 +59,7 @@ lejp_destruct(struct lejp_ctx *ctx) { /* no allocations... just let callback know what it happening */ - ctx->pst[0].callback(ctx, LEJPCB_DESTRUCTED); + ctx->callback(ctx, LEJPCB_DESTRUCTED); } /** @@ -136,30 +88,23 @@ lejp_change_callback(struct lejp_ctx *ctx, signed char (*callback)(struct lejp_ctx *ctx, char reason)) { - ctx->pst[0].callback(ctx, LEJPCB_DESTRUCTED); - ctx->pst[0].callback = callback; - ctx->pst[0].callback(ctx, LEJPCB_CONSTRUCTED); - ctx->pst[0].callback(ctx, LEJPCB_START); + ctx->callback(ctx, LEJPCB_DESTRUCTED); + ctx->callback = callback; + ctx->callback(ctx, LEJPCB_CONSTRUCTED); + ctx->callback(ctx, LEJPCB_START); } -void +static void lejp_check_path_match(struct lejp_ctx *ctx) { const char *p, *q; int n; - size_t s = sizeof(char *); - - if (ctx->path_stride) - s = ctx->path_stride; /* we only need to check if a match is not active */ - for (n = 0; !ctx->path_match && - n < ctx->pst[ctx->pst_sp].count_paths; n++) { + for (n = 0; !ctx->path_match && n < ctx->count_paths; n++) { ctx->wildcount = 0; p = ctx->path; - - q = *((char **)(((char *)ctx->pst[ctx->pst_sp].paths) + (n * s))); - + q = ctx->paths[n]; while (*p && *q) { if (*q != '*') { if (*p != *q) @@ -168,7 +113,7 @@ q++; continue; } - ctx->wild[ctx->wildcount++] = lws_ptr_diff(p, ctx->path); + ctx->wild[ctx->wildcount++] = p - ctx->path; q++; /* * if * has something after it, match to . @@ -185,7 +130,7 @@ continue; ctx->path_match = n + 1; - ctx->path_match_len = ctx->pst[ctx->pst_sp].ppos; + ctx->path_match_len = ctx->ppos; return; } @@ -203,8 +148,7 @@ n = ctx->wild[wildcard]; - while (--len && n < ctx->pst[ctx->pst_sp].ppos && - (n == ctx->wild[wildcard] || ctx->path[n] != '.')) + while (--len && n < ctx->ppos && (n == ctx->wild[wildcard] || ctx->path[n] != '.')) *dest++ = ctx->path[n++]; *dest = '\0'; @@ -237,11 +181,12 @@ static const char esc_tran[] = "\"\\/\b\f\n\r\t"; static const char tokens[] = "rue alse ull "; - if (!ctx->sp && !ctx->pst[ctx->pst_sp].ppos) - ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_START); + if (!ctx->sp && !ctx->ppos) + ctx->callback(ctx, LEJPCB_START); while (len--) { c = *json++; + s = ctx->st[ctx->sp].s; /* skip whitespace unless we should care */ @@ -267,7 +212,7 @@ ret = LEJP_REJECT_IDLE_NO_BRACE; goto reject; } - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_OBJECT_START)) { + if (ctx->callback(ctx, LEJPCB_OBJECT_START)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -293,13 +238,13 @@ case LEJP_MP_STRING: if (c == '\"') { - if (!ctx->sp) { /* JSON can't end on quote */ + if (!ctx->sp) { ret = LEJP_REJECT_MP_STRING_UNDERRUN; goto reject; } if (ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) { ctx->buf[ctx->npos] = '\0'; - if (ctx->pst[ctx->pst_sp].callback(ctx, + if (ctx->callback(ctx, LEJPCB_VAL_STR_END) < 0) { ret = LEJP_REJECT_CALLBACK; goto reject; @@ -406,17 +351,17 @@ goto reject; } ctx->st[ctx->sp].s = LEJP_MP_VALUE; - ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + ctx->path[ctx->ppos] = '\0'; lejp_check_path_match(ctx); - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_PAIR_NAME)) { + if (ctx->callback(ctx, LEJPCB_PAIR_NAME)) { ret = LEJP_REJECT_CALLBACK; goto reject; } break; case LEJP_MP_VALUE: - if (c == '-' || (c >= '0' && c <= '9')) { + if (c >= '0' && c <= '9') { ctx->npos = 0; ctx->dcount = 0; ctx->f = 0; @@ -430,7 +375,7 @@ c = LEJP_MP_STRING; ctx->npos = 0; ctx->buf[0] = '\0'; - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_STR_START)) { + if (ctx->callback(ctx, LEJPCB_VAL_STR_START)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -441,7 +386,7 @@ ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; c = LEJP_MEMBERS; lejp_check_path_match(ctx); - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_OBJECT_START)) { + if (ctx->callback(ctx, LEJPCB_OBJECT_START)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -452,46 +397,20 @@ /* push */ ctx->st[ctx->sp].s = LEJP_MP_ARRAY_END; c = LEJP_MP_VALUE; - ctx->path[ctx->pst[ctx->pst_sp].ppos++] = '['; - ctx->path[ctx->pst[ctx->pst_sp].ppos++] = ']'; - ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_ARRAY_START)) { + ctx->path[ctx->ppos++] = '['; + ctx->path[ctx->ppos++] = ']'; + ctx->path[ctx->ppos] = '\0'; + if (ctx->callback(ctx, LEJPCB_ARRAY_START)) { ret = LEJP_REJECT_CALLBACK; goto reject; } ctx->i[ctx->ipos++] = 0; - if (ctx->ipos > LWS_ARRAY_SIZE(ctx->i)) { + if (ctx->ipos > ARRAY_SIZE(ctx->i)) { ret = LEJP_REJECT_MP_DELIM_ISTACK; goto reject; } goto add_stack_level; - case ']': - /* pop */ - if (!ctx->sp) { /* JSON can't end on ] */ - ret = LEJP_REJECT_MP_C_OR_E_UNDERF; - goto reject; - } - ctx->sp--; - if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) { - ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY; - goto reject; - } - /* drop the path [n] bit */ - if (ctx->sp) { - ctx->pst[ctx->pst_sp].ppos = ctx->st[ctx->sp - 1].p; - ctx->ipos = ctx->st[ctx->sp - 1].i; - } - ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; - if (ctx->path_match && - ctx->pst[ctx->pst_sp].ppos <= ctx->path_match_len) - /* - * we shrank the path to be - * smaller than the matching point - */ - ctx->path_match = 0; - goto array_end; - case 't': /* true */ ctx->uni = 0; ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; @@ -518,7 +437,7 @@ goto append_npos; } - if (ctx->dcount < 20 && c >= '0' && c <= '9') { + if (ctx->dcount < 10 && c >= '0' && c <= '9') { if (ctx->f & LEJP_SEEN_POINT) ctx->f |= LEJP_SEEN_POST_POINT; ctx->dcount++; @@ -559,12 +478,12 @@ ctx->buf[ctx->npos] = '\0'; if (ctx->f & LEJP_SEEN_POINT) { - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_NUM_FLOAT)) { + if (ctx->callback(ctx, LEJPCB_VAL_NUM_FLOAT)) { ret = LEJP_REJECT_CALLBACK; goto reject; } } else { - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_NUM_INT)) { + if (ctx->callback(ctx, LEJPCB_VAL_NUM_INT)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -595,7 +514,7 @@ case 3: ctx->buf[0] = '1'; ctx->buf[1] = '\0'; - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_TRUE)) { + if (ctx->callback(ctx, LEJPCB_VAL_TRUE)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -603,14 +522,14 @@ case 8: ctx->buf[0] = '0'; ctx->buf[1] = '\0'; - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_FALSE)) { + if (ctx->callback(ctx, LEJPCB_VAL_FALSE)) { ret = LEJP_REJECT_CALLBACK; goto reject; } break; case 12: ctx->buf[0] = '\0'; - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_NULL)) { + if (ctx->callback(ctx, LEJPCB_VAL_NULL)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -620,12 +539,12 @@ break; case LEJP_MP_COMMA_OR_END: - ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + ctx->path[ctx->ppos] = '\0'; if (c == ',') { /* increment this stack level's index */ ctx->st[ctx->sp].s = LEJP_M_P; if (!ctx->sp) { - ctx->pst[ctx->pst_sp].ppos = 0; + ctx->ppos = 0; /* * since we came back to root level, * no path can still match @@ -633,10 +552,10 @@ ctx->path_match = 0; break; } - ctx->pst[ctx->pst_sp].ppos = ctx->st[ctx->sp - 1].p; - ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->path[ctx->ppos] = '\0'; if (ctx->path_match && - ctx->pst[ctx->pst_sp].ppos <= ctx->path_match_len) + ctx->ppos <= ctx->path_match_len) /* * we shrank the path to be * smaller than the matching point @@ -652,7 +571,7 @@ break; } if (c == ']') { - if (!ctx->sp) { /* JSON can't end on ] */ + if (!ctx->sp) { ret = LEJP_REJECT_MP_C_OR_E_UNDERF; goto reject; } @@ -663,13 +582,11 @@ goto reject; } /* drop the path [n] bit */ - if (ctx->sp) { - ctx->pst[ctx->pst_sp].ppos = ctx->st[ctx->sp - 1].p; - ctx->ipos = ctx->st[ctx->sp - 1].i; - } - ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->ipos = ctx->st[ctx->sp - 1].i; + ctx->path[ctx->ppos] = '\0'; if (ctx->path_match && - ctx->pst[ctx->pst_sp].ppos <= ctx->path_match_len) + ctx->ppos <= ctx->path_match_len) /* * we shrank the path to be * smaller than the matching point @@ -680,40 +597,30 @@ goto redo_character; } if (c == '}') { - if (!ctx->sp) { + if (ctx->sp == 0) { lejp_check_path_match(ctx); - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_OBJECT_END)) { + if (ctx->callback(ctx, LEJPCB_OBJECT_END)) { ret = LEJP_REJECT_CALLBACK; goto reject; } - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_COMPLETE)) - goto reject; - else - /* done, return unused amount */ - return len; + ctx->callback(ctx, LEJPCB_COMPLETE); + /* done, return unused amount */ + return len; } - /* pop */ - ctx->sp--; - if (ctx->sp) { - ctx->pst[ctx->pst_sp].ppos = - ctx->st[ctx->sp].p; - ctx->ipos = ctx->st[ctx->sp].i; - } - ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->ipos = ctx->st[ctx->sp - 1].i; + ctx->path[ctx->ppos] = '\0'; if (ctx->path_match && - ctx->pst[ctx->pst_sp].ppos <= - ctx->path_match_len) + ctx->ppos <= ctx->path_match_len) /* * we shrank the path to be * smaller than the matching point */ ctx->path_match = 0; - lejp_check_path_match(ctx); - if (ctx->pst[ctx->pst_sp].callback(ctx, - LEJPCB_OBJECT_END)) { + if (ctx->callback(ctx, LEJPCB_OBJECT_END)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -724,16 +631,15 @@ goto reject; case LEJP_MP_ARRAY_END: -array_end: - ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + ctx->path[ctx->ppos] = '\0'; if (c == ',') { /* increment this stack level's index */ if (ctx->ipos) ctx->i[ctx->ipos - 1]++; ctx->st[ctx->sp].s = LEJP_MP_VALUE; if (ctx->sp) - ctx->pst[ctx->pst_sp].ppos = ctx->st[ctx->sp - 1].p; - ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->path[ctx->ppos] = '\0'; break; } if (c != ']') { @@ -742,7 +648,7 @@ } ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; - ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_ARRAY_END); + ctx->callback(ctx, LEJPCB_ARRAY_END); break; } @@ -753,7 +659,7 @@ /* assemble the string value into chunks */ ctx->buf[ctx->npos++] = c; if (ctx->npos == sizeof(ctx->buf) - 1) { - if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_STR_CHUNK)) { + if (ctx->callback(ctx, LEJPCB_VAL_STR_CHUNK)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -762,23 +668,22 @@ continue; } /* name part of name:value pair */ - ctx->path[ctx->pst[ctx->pst_sp].ppos++] = c; + ctx->path[ctx->ppos++] = c; continue; add_stack_level: /* push on to the object stack */ - if (ctx->pst[ctx->pst_sp].ppos && - ctx->st[ctx->sp].s != LEJP_MP_COMMA_OR_END && - ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) - ctx->path[ctx->pst[ctx->pst_sp].ppos++] = '.'; + if (ctx->ppos && ctx->st[ctx->sp].s != LEJP_MP_COMMA_OR_END && + ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) + ctx->path[ctx->ppos++] = '.'; - ctx->st[ctx->sp].p = ctx->pst[ctx->pst_sp].ppos; + ctx->st[ctx->sp].p = ctx->ppos; ctx->st[ctx->sp].i = ctx->ipos; - if (++ctx->sp == LWS_ARRAY_SIZE(ctx->st)) { + if (++ctx->sp == ARRAY_SIZE(ctx->st)) { ret = LEJP_REJECT_STACK_OVERFLOW; goto reject; } - ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + ctx->path[ctx->ppos] = '\0'; ctx->st[ctx->sp].s = c; ctx->st[ctx->sp].b = 0; continue; @@ -799,65 +704,6 @@ return LEJP_CONTINUE; reject: - ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_FAILED); + ctx->callback(ctx, LEJPCB_FAILED); return ret; } - -int -lejp_parser_push(struct lejp_ctx *ctx, void *user, const char * const *paths, - unsigned char paths_count, lejp_callback lejp_cb) -{ - struct _lejp_parsing_stack *p; - - if (ctx->pst_sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH) - return -1; - - lejp_check_path_match(ctx); - - ctx->pst[ctx->pst_sp].path_match = ctx->path_match; - ctx->pst_sp++; - - p = &ctx->pst[ctx->pst_sp]; - p->user = user; - p->callback = lejp_cb; - p->paths = paths; - p->count_paths = paths_count; - p->ppos = 0; - - ctx->path_match = 0; - lejp_check_path_match(ctx); - - lwsl_debug("%s: pushed parser stack to %d (path %s)\n", __func__, - ctx->pst_sp, ctx->path); - - return 0; -} - -int -lejp_parser_pop(struct lejp_ctx *ctx) -{ - if (!ctx->pst_sp) - return -1; - - ctx->pst_sp--; - lwsl_debug("%s: popped parser stack to %d\n", __func__, ctx->pst_sp); - - ctx->path_match = 0; /* force it to check */ - lejp_check_path_match(ctx); - - return 0; -} - -const char * -lejp_error_to_string(int e) -{ - if (e > 0) - e = 0; - else - e = -e; - - if (e >= (int)LWS_ARRAY_SIZE(parser_errs)) - return "Unknown error"; - - return parser_errs[e]; -} diff -Nru libwebsockets-4.0.20/lib/misc/lejp.h libwebsockets-2.4.2/lib/misc/lejp.h --- libwebsockets-4.0.20/lib/misc/lejp.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/lejp.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,232 @@ +#include "libwebsockets.h" +struct lejp_ctx; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0])) +#endif +#define LEJP_FLAG_WS_KEEP 64 +#define LEJP_FLAG_WS_COMMENTLINE 32 + +enum lejp_states { + LEJP_IDLE = 0, + LEJP_MEMBERS = 1, + LEJP_M_P = 2, + LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3, + LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4, + LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5, + LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6, + LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7, + LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8, + LEJP_MP_DELIM = 9, + LEJP_MP_VALUE = 10, + LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11, + LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12, + LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13, + LEJP_MP_COMMA_OR_END = 14, + LEJP_MP_ARRAY_END = 15, +}; + +enum lejp_reasons { + LEJP_CONTINUE = -1, + LEJP_REJECT_IDLE_NO_BRACE = -2, + LEJP_REJECT_MEMBERS_NO_CLOSE = -3, + LEJP_REJECT_MP_NO_OPEN_QUOTE = -4, + LEJP_REJECT_MP_STRING_UNDERRUN = -5, + LEJP_REJECT_MP_ILLEGAL_CTRL = -6, + LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7, + LEJP_REJECT_ILLEGAL_HEX = -8, + LEJP_REJECT_MP_DELIM_MISSING_COLON = -9, + LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10, + LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11, + LEJP_REJECT_MP_VAL_NUM_FORMAT = -12, + LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13, + LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14, + LEJP_REJECT_MP_C_OR_E_UNDERF = -15, + LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16, + LEJP_REJECT_MP_ARRAY_END_MISSING = -17, + LEJP_REJECT_STACK_OVERFLOW = -18, + LEJP_REJECT_MP_DELIM_ISTACK = -19, + LEJP_REJECT_NUM_TOO_LONG = -20, + LEJP_REJECT_MP_C_OR_E_NEITHER = -21, + LEJP_REJECT_UNKNOWN = -22, + LEJP_REJECT_CALLBACK = -23 +}; + +#define LEJP_FLAG_CB_IS_VALUE 64 + +enum lejp_callbacks { + LEJPCB_CONSTRUCTED = 0, + LEJPCB_DESTRUCTED = 1, + + LEJPCB_START = 2, + LEJPCB_COMPLETE = 3, + LEJPCB_FAILED = 4, + + LEJPCB_PAIR_NAME = 5, + + LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6, + LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7, + LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8, + LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9, + LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10, + LEJPCB_VAL_STR_START = 11, /* notice handle separately */ + LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12, + LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13, + + LEJPCB_ARRAY_START = 14, + LEJPCB_ARRAY_END = 15, + + LEJPCB_OBJECT_START = 16, + LEJPCB_OBJECT_END = 17 +}; + +/** + * _lejp_callback() - User parser actions + * \param ctx: LEJP context + * \param reason: Callback reason + * + * Your user callback is associated with the context at construction time, + * and receives calls as the parsing progresses. + * + * All of the callbacks may be ignored and just return 0. + * + * The reasons it might get called, found in @reason, are: + * + * LEJPCB_CONSTRUCTED: The context was just constructed... you might want to + * perform one-time allocation for the life of the context. + * + * LEJPCB_DESTRUCTED: The context is being destructed... if you made any + * allocations at construction-time, you can free them now + * + * LEJPCB_START: Parsing is beginning at the first byte of input + * + * LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or + * positive return code from lejp_parse indicating the + * amount of unused bytes left in the input buffer + * + * LEJPCB_FAILED: Parsing failed. You'll get a negative error code + * returned from lejp_parse + * + * LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed, + * this callback occurs. You can find the new name at + * the end of ctx->path[] + * + * LEJPCB_VAL_TRUE: The "true" value appeared + * + * LEJPCB_VAL_FALSE: The "false" value appeared + * + * LEJPCB_VAL_NULL: The "null" value appeared + * + * LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf + * + * LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf + * + * LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet + * + * LEJPCB_VAL_STR_CHUNK: We parsed LEJP_STRING_CHUNK -1 bytes of string data in + * ctx->buf, which is as much as we can buffer, so we are + * spilling it. If all your strings are less than + * LEJP_STRING_CHUNK - 1 bytes, you will never see this + * callback. + * + * LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the + * string is in ctx->buf. + * + * LEJPCB_ARRAY_START: An array started + * + * LEJPCB_ARRAY_END: An array ended + * + * LEJPCB_OBJECT_START: An object started + * + * LEJPCB_OBJECT_END: An object ended + */ +LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason); + +typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason); + +#ifndef LEJP_MAX_DEPTH +#define LEJP_MAX_DEPTH 12 +#endif +#ifndef LEJP_MAX_INDEX_DEPTH +#define LEJP_MAX_INDEX_DEPTH 5 +#endif +#ifndef LEJP_MAX_PATH +#define LEJP_MAX_PATH 128 +#endif +#ifndef LEJP_STRING_CHUNK +/* must be >= 30 to assemble floats */ +#define LEJP_STRING_CHUNK 255 +#endif + +enum num_flags { + LEJP_SEEN_MINUS = (1 << 0), + LEJP_SEEN_POINT = (1 << 1), + LEJP_SEEN_POST_POINT = (1 << 2), + LEJP_SEEN_EXP = (1 << 3) +}; + +struct _lejp_stack { + char s; /* lejp_state stack*/ + char p; /* path length */ + char i; /* index array length */ + char b; /* user bitfield */ +}; + +struct lejp_ctx { + + /* sorted by type for most compact alignment + * + * pointers + */ + + signed char (*callback)(struct lejp_ctx *ctx, char reason); + void *user; + const char * const *paths; + + /* arrays */ + + struct _lejp_stack st[LEJP_MAX_DEPTH]; + unsigned short i[LEJP_MAX_INDEX_DEPTH]; /* index array */ + unsigned short wild[LEJP_MAX_INDEX_DEPTH]; /* index array */ + char path[LEJP_MAX_PATH]; + char buf[LEJP_STRING_CHUNK]; + + /* int */ + + unsigned int line; + + /* short */ + + unsigned short uni; + + /* char */ + + unsigned char npos; + unsigned char dcount; + unsigned char f; + unsigned char sp; /* stack head */ + unsigned char ipos; /* index stack depth */ + unsigned char ppos; + unsigned char count_paths; + unsigned char path_match; + unsigned char path_match_len; + unsigned char wildcount; +}; + +LWS_VISIBLE LWS_EXTERN void +lejp_construct(struct lejp_ctx *ctx, + signed char (*callback)(struct lejp_ctx *ctx, char reason), + void *user, const char * const *paths, unsigned char paths_count); + +LWS_VISIBLE LWS_EXTERN void +lejp_destruct(struct lejp_ctx *ctx); + +LWS_VISIBLE LWS_EXTERN int +lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len); + +LWS_VISIBLE LWS_EXTERN void +lejp_change_callback(struct lejp_ctx *ctx, + signed char (*callback)(struct lejp_ctx *ctx, char reason)); + +LWS_VISIBLE LWS_EXTERN int +lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len); diff -Nru libwebsockets-4.0.20/lib/misc/lwsac/cached-file.c libwebsockets-2.4.2/lib/misc/lwsac/cached-file.c --- libwebsockets-4.0.20/lib/misc/lwsac/cached-file.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/lwsac/cached-file.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,218 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT) - -#include "private-lib-core.h" -#include "private-lib-misc-lwsac.h" - -/* - * Helper for caching a file in memory in a lac, but also to check at intervals - * no less than 5s if the file is still fresh. - * - * Set *cache to NULL the first time before calling. - * - * You should call this each time before using the cache... if it's - * - * - less than 5s since the last freshness check, and - * - the file is already in memory - * - * it just returns with *cache left alone; this costs very little. You should - * call `lwsac_use_cached_file_start()` and `lwsac_use_cached_file_end()` - * to lock the cache against deletion while you are using it. - * - * If it's - * - * - at least 5s since the last freshness check, and - * - the file timestamp has changed - * - * then - * - * - the file is reloaded into a new lac and *cache set to that - * - * - the old cache lac, if any, is detached (so it will be freed when its - * reference count reaches zero, or immediately if nobody has it) - * - * Note the call can fail due to OOM or filesystem issue at any time. - * - * - * After the LAC header there is stored a `struct cached_file_info` and then - * the raw file contents. * - * - * [LAC header] - * [struct cached_file_info] - * [file contents] <--- *cache is set to here - * - * The api returns a lwsac_cached_file_t type offset to point to the file - * contents. Helpers for reference counting and freeing are also provided - * that take that type and know how to correct it back to operate on the LAC. - */ - -#define cache_file_to_lac(c) ((struct lwsac *)((char *)c - \ - sizeof(struct cached_file_info) - \ - sizeof(struct lwsac_head) - \ - sizeof(struct lwsac))) - -void -lwsac_use_cached_file_start(lwsac_cached_file_t cache) -{ - struct lwsac *lac = cache_file_to_lac(cache); - struct lwsac_head *lachead = (struct lwsac_head *)&lac->head[1]; - - lachead->refcount++; - // lwsl_debug("%s: html refcount: %d\n", __func__, lachead->refcount); -} - -void -lwsac_use_cached_file_end(lwsac_cached_file_t *cache) -{ - struct lwsac *lac; - struct lwsac_head *lachead; - - if (!cache || !*cache) - return; - - lac = cache_file_to_lac(*cache); - lachead = (struct lwsac_head *)&lac->head[1]; - - if (!lachead->refcount) - lwsl_err("%s: html refcount zero on entry\n", __func__); - - if (lachead->refcount && !--lachead->refcount && lachead->detached) { - *cache = NULL; /* not usable any more */ - lwsac_free(&lac); - } -} - -void -lwsac_use_cached_file_detach(lwsac_cached_file_t *cache) -{ - struct lwsac *lac = cache_file_to_lac(*cache); - struct lwsac_head *lachead = NULL; - - if (lac) { - lachead = (struct lwsac_head *)&lac->head[1]; - - lachead->detached = 1; - if (lachead->refcount) - return; - } - - *cache = NULL; - lwsac_free(&lac); -} - -int -lwsac_cached_file(const char *filepath, lwsac_cached_file_t *cache, size_t *len) -{ - struct cached_file_info *info = NULL; - lwsac_cached_file_t old = *cache; - struct lwsac *lac = NULL; - time_t t = time(NULL); - unsigned char *a; - struct stat s; - size_t all; - ssize_t rd; - int fd; - - if (old) { /* we already have a cached copy of it */ - - info = (struct cached_file_info *)((*cache) - sizeof(*info)); - - if (t - info->last_confirm < 5) - /* we checked it as fresh less than 5s ago, use old */ - return 0; - } - - /* - * ...it's been 5s, we should check again on the filesystem - * that the file hasn't changed - */ - - fd = open(filepath, O_RDONLY); - if (fd < 0) { - lwsl_err("%s: cannot open %s\n", __func__, filepath); - - return 1; - } - - if (fstat(fd, &s)) { - lwsl_err("%s: cannot stat %s\n", __func__, filepath); - - goto bail; - } - - if (old && s.st_mtime == info->s.st_mtime) { - /* it still seems to be the same as our cached one */ - info->last_confirm = t; - - close(fd); - - return 0; - } - - /* - * we either didn't cache it yet, or it has changed since we cached - * it... reload in a new lac and then detach the old lac. - */ - - all = sizeof(*info) + s.st_size + 2; - - info = lwsac_use(&lac, all, all); - if (!info) - goto bail; - - info->s = s; - info->last_confirm = t; - - a = (unsigned char *)(info + 1); - - *len = s.st_size; - a[s.st_size] = '\0'; - - rd = read(fd, a, s.st_size); - if (rd != s.st_size) { - lwsl_err("%s: cannot read %s (%d)\n", __func__, filepath, - (int)rd); - goto bail1; - } - - close(fd); - - *cache = (lwsac_cached_file_t)a; - if (old) - lwsac_use_cached_file_detach(&old); - - return 0; - -bail1: - lwsac_free(&lac); - -bail: - close(fd); - - return 1; -} - -#endif diff -Nru libwebsockets-4.0.20/lib/misc/lwsac/lwsac.c libwebsockets-2.4.2/lib/misc/lwsac/lwsac.c --- libwebsockets-4.0.20/lib/misc/lwsac/lwsac.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/lwsac/lwsac.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,358 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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 "private-lib-misc-lwsac.h" - -void -lws_list_ptr_insert(lws_list_ptr *head, lws_list_ptr *add, - lws_list_ptr_sort_func_t sort_func) -{ - while (sort_func && *head) { - if (sort_func(add, *head) <= 0) - break; - - head = *head; - } - - *add = *head; - *head = add; -} - -size_t -lwsac_align(size_t length) -{ - size_t align = sizeof(int *); - - if (length & (align - 1)) - length += align - (length & (align - 1)); - - return length; -} - -size_t -lwsac_sizeof(int first) -{ - return sizeof(struct lwsac) + (first ? sizeof(struct lwsac_head) : 0); -} - -size_t -lwsac_get_tail_pos(struct lwsac *lac) -{ - return lac->ofs; -} - -struct lwsac * -lwsac_get_next(struct lwsac *lac) -{ - return lac->next; -} - -int -lwsac_extend(struct lwsac *head, int amount) -{ - struct lwsac_head *lachead; - struct lwsac *bf; - - assert(head); - lachead = (struct lwsac_head *)&head[1]; - - bf = lachead->curr; - assert(bf); - - if (bf->alloc_size - bf->ofs < lwsac_align(amount)) - return 1; - - /* memset so constant folding never sees uninitialized data */ - - memset(((uint8_t *)bf) + bf->ofs, 0, lwsac_align(amount)); - bf->ofs += lwsac_align(amount); - - return 0; -} - -static void * -_lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size, char backfill) -{ - struct lwsac_head *lachead = NULL; - size_t ofs, alloc, al, hp; - struct lwsac *bf = *head; - - if (bf) - lachead = (struct lwsac_head *)&bf[1]; - - al = lwsac_align(ensure); - - /* backfill into earlier chunks if that is allowed */ - - if (backfill) - /* - * check if anything can take it, from the start - */ - while (bf) { - if (bf->alloc_size - bf->ofs >= ensure) - goto do_use; - - bf = bf->next; - } - else { - /* - * If there's a current chunk, just check if he can take it - */ - if (lachead && lachead->curr) { - bf = lachead->curr; - if (bf->alloc_size - bf->ofs >= ensure) - goto do_use; - } - } - - /* nothing can currently take it... so we must allocate */ - - hp = sizeof(*bf); /* always need the normal header part... */ - if (!*head) - hp += sizeof(struct lwsac_head); - - if (!chunk_size) - alloc = LWSAC_CHUNK_SIZE + hp; - else - alloc = chunk_size + hp; - - /* - * If we get asked for something outside our expectation, - * increase the allocation to meet it - */ - - if (al >= alloc - hp) - alloc = al + hp; - - lwsl_debug("%s: alloc %d for %d\n", __func__, (int)alloc, (int)ensure); - bf = malloc(alloc); - if (!bf) { - lwsl_err("%s: OOM trying to alloc %llud\n", __func__, - (unsigned long long)alloc); - return NULL; - } - - /* - * belabouring the point... ofs is aligned to the platform's - * generic struct alignment at the start then - */ - bf->ofs = sizeof(*bf); - - if (!*head) { - /* - * We are the first, head, entry... - */ - *head = bf; - /* - * ... allocate for the special head block - */ - bf->ofs += sizeof(*lachead); - lachead = (struct lwsac_head *)&bf[1]; - memset(lachead, 0, sizeof(*lachead)); - } else - if (lachead->curr) - lachead->curr->next = bf; - - lachead->curr = bf; - bf->head = *head; - bf->next = NULL; - bf->alloc_size = alloc; - - lachead->total_alloc_size += alloc; - lachead->total_blocks++; - -do_use: - - ofs = bf->ofs; - - if (al > ensure) - /* zero down the alignment padding part */ - memset((char *)bf + ofs + ensure, 0, al - ensure); - - bf->ofs += al; - if (bf->ofs >= bf->alloc_size) - bf->ofs = bf->alloc_size; - - return (char *)bf + ofs; -} - -void * -lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size) -{ - return _lwsac_use(head, ensure, chunk_size, 0); -} - -void * -lwsac_use_backfill(struct lwsac **head, size_t ensure, size_t chunk_size) -{ - return _lwsac_use(head, ensure, chunk_size, 1); -} - -uint8_t * -lwsac_scan_extant(struct lwsac *head, uint8_t *find, size_t len, int nul) -{ - while (head) { - uint8_t *pos = (uint8_t *)&head[1], - *end = ((uint8_t *)head) + head->ofs - len; - - if (head->ofs - sizeof(*head) >= len) - while (pos < end) { - if (*pos == *find && (!nul || !pos[len]) && - pos[len - 1] == find[len - 1] && - !memcmp(pos, find, len)) - /* found the blob */ - return pos; - pos++; - } - - head = head->next; - } - - return NULL; -} - -uint64_t -lwsac_total_overhead(struct lwsac *head) -{ - uint64_t overhead = 0; - - while (head) { - overhead += (head->alloc_size - head->ofs) + sizeof(*head); - - head = head->next; - } - - return overhead; -} - -void * -lwsac_use_zero(struct lwsac **head, size_t ensure, size_t chunk_size) -{ - void *p = lwsac_use(head, ensure, chunk_size); - - if (p) - memset(p, 0, ensure); - - return p; -} - -void -lwsac_free(struct lwsac **head) -{ - struct lwsac *it = *head; - - *head = NULL; - lwsl_debug("%s: head %p\n", __func__, *head); - - while (it) { - struct lwsac *tmp = it->next; - - free(it); - it = tmp; - } -} - -void -lwsac_info(struct lwsac *head) -{ -#if _LWS_ENABLED_LOGS & LLL_DEBUG - struct lwsac_head *lachead; - - if (!head) { - lwsl_debug("%s: empty\n", __func__); - return; - } - - lachead = (struct lwsac_head *)&head[1]; - - lwsl_debug("%s: lac %p: %dKiB in %d blocks\n", __func__, head, - (int)(lachead->total_alloc_size >> 10), lachead->total_blocks); -#endif -} - -uint64_t -lwsac_total_alloc(struct lwsac *head) -{ - struct lwsac_head *lachead; - - if (!head) - return 0; - - lachead = (struct lwsac_head *)&head[1]; - return lachead->total_alloc_size; -} - -void -lwsac_reference(struct lwsac *head) -{ - struct lwsac_head *lachead = (struct lwsac_head *)&head[1]; - - lachead->refcount++; - lwsl_debug("%s: head %p: (det %d) refcount -> %d\n", - __func__, head, lachead->detached, lachead->refcount); -} - -void -lwsac_unreference(struct lwsac **head) -{ - struct lwsac_head *lachead; - - if (!(*head)) - return; - - lachead = (struct lwsac_head *)&(*head)[1]; - - if (!lachead->refcount) - lwsl_warn("%s: refcount going below zero\n", __func__); - - lachead->refcount--; - - lwsl_debug("%s: head %p: (det %d) refcount -> %d\n", - __func__, *head, lachead->detached, lachead->refcount); - - if (lachead->detached && !lachead->refcount) { - lwsl_debug("%s: head %p: FREED\n", __func__, *head); - lwsac_free(head); - } -} - -void -lwsac_detach(struct lwsac **head) -{ - struct lwsac_head *lachead; - - if (!(*head)) - return; - - lachead = (struct lwsac_head *)&(*head)[1]; - - lachead->detached = 1; - if (!lachead->refcount) { - lwsl_debug("%s: head %p: FREED\n", __func__, *head); - lwsac_free(head); - } else - lwsl_debug("%s: head %p: refcount %d: Marked as detached\n", - __func__, *head, lachead->refcount); -} diff -Nru libwebsockets-4.0.20/lib/misc/lwsac/private-lib-misc-lwsac.h libwebsockets-2.4.2/lib/misc/lwsac/private-lib-misc-lwsac.h --- libwebsockets-4.0.20/lib/misc/lwsac/private-lib-misc-lwsac.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/lwsac/private-lib-misc-lwsac.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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_PRIVATE_LIB_MISC_LWSAC_H__) -#define __LWS_PRIVATE_LIB_MISC_LWSAC_H__ - -#if !defined(LWS_PLAT_OPTEE) -#include -#endif - -/* under page size of 4096 to allow overhead */ -#define LWSAC_CHUNK_SIZE 4000 - -/* - * the chunk list members all point back to the head themselves so the list - * can be detached from the formal head and free itself when its reference - * count reaches zero. - */ - -/* - * One of these per chunk - */ - -struct lwsac { - struct lwsac *next; - struct lwsac *head; /* pointer back to the first chunk */ - size_t alloc_size; /* alloc size of the whole chunk */ - size_t ofs; /* next writeable position inside chunk */ -}; - -/* - * One of these per lwsac, at start of first chunk - */ - -struct lwsac_head { - struct lwsac *curr; - size_t total_alloc_size; - int refcount; - int total_blocks; - char detached; /* if our refcount gets to zero, free the chunk list */ -}; - -#if !defined(LWS_PLAT_OPTEE) -struct cached_file_info { - struct stat s; - time_t last_confirm; -}; -#endif -#endif diff -Nru libwebsockets-4.0.20/lib/misc/lwsac/README.md libwebsockets-2.4.2/lib/misc/lwsac/README.md --- libwebsockets-4.0.20/lib/misc/lwsac/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/lwsac/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,148 +0,0 @@ -## LWS Allocated Chunks - -![lwsac flow](/doc-assets/lwsac.svg) - -These apis provide a way to manage a linked-list of allocated chunks... - -[ HEAD alloc ] -> [ next alloc ] -> [ next alloc ] -> [ curr alloc ] - -... and sub-allocate trivially inside the chunks. These sub-allocations are -not tracked by lwsac at all, there is a "used" high-water mark for each chunk -that's simply advanced by the amount sub-allocated. If the allocation size -matches the platform pointer alignment, there is zero overhead to sub-allocate -(otherwise the allocation is padded to the next platform pointer alignment -automatically). - -If you have an unknown amount of relatively little things to allocate, including -strings or other unstructured data, lwsac is significantly more efficient than -individual allocations using malloc or so. - -[lwsac full public api](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-lwsac.h) - -## lwsac_use() api - -``` -/** - * lwsac_use - allocate / use some memory from a lwsac - * - * \param head: pointer to the lwsac list object - * \param ensure: the number of bytes we want to use - * \param chunk_size: 0, or the size of the chunk to (over)allocate if - * what we want won't fit in the current tail chunk. If - * 0, the default value of 4000 is used. If ensure is - * larger, it is used instead. - * - * This also serves to init the lwsac if *head is NULL. Basically it does - * whatever is necessary to return you a pointer to ensure bytes of memory - * reserved for the caller. - * - * Returns NULL if OOM. - */ -LWS_VISIBLE LWS_EXTERN void * -lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size); -``` - -When you make an sub-allocation using `lwsac_use()`, you can either -set the `chunk_size` arg to zero, defaulting to 4000, or a specific chunk size. -In the event the requested sub-allocation exceeds the chunk size, the chunk -size is increated to match it automatically for this allocation only. - -Subsequent `lwsac_use()` calls will advance internal pointers to use up the -remaining space inside the current chunk if possible; if not enough remaining -space it is skipped, a new allocation is chained on and the request pointed to -there. - -Lwsac does not store information about sub-allocations. There is really zero -overhead for individual sub-allocations (unless their size is not -pointer-aligned, in which case the actual amount sub-allocated is rounded up to -the next pointer alignment automatically). For structs, which are pointer- -aligned naturally, and a chunk size relatively large for the sub-allocation -size, lwsac is extremely efficient even for huge numbers of small allocations. - -This makes lwsac very effective when the total amount of allocation needed is -not known at the start and may be large... it will simply add on chunks to cope -with whatever happens. - -## lwsac_free() api - -``` -/** - * lwsac_free - deallocate all chunks in the lwsac and set head NULL - * - * \param head: pointer to the lwsac list object - * - * This deallocates all chunks in the lwsac, then sets *head to NULL. All - * lwsac_use() pointers are invalidated in one hit without individual frees. - */ -LWS_VISIBLE LWS_EXTERN void -lwsac_free(struct lwsac **head); -``` - -When you are finished with the lwsac, you simply free the chain of allocated -chunks using lwsac_free() on the lwsac head. There's no tracking or individual -destruction of suballocations - the whole chain of chunks the suballocations -live in are freed and invalidated all together. - -If the structs stored in the lwsac allocated things **outside** the lwsac, then the -user must unwind through them and perform the frees. But the idea of lwsac is -things stored in the lwsac also suballocate into the lwsac, and point into the -lwsac if they need to, avoiding any need to visit them during destroy. It's -like clearing up after a kids' party by gathering up a disposable tablecloth: -no matter what was left on the table, it's all gone in one step. - -## `lws_list_ptr` helpers - -``` -/* sort may be NULL if you don't care about order */ -LWS_VISIBLE LWS_EXTERN void -lws_list_ptr_insert(lws_list_ptr *phead, lws_list_ptr *add, - lws_list_ptr_sort_func_t sort); -``` - -A common pattern needed with sub-allocated structs is they are on one or more -linked-list. To make that simple to do cleanly, `lws_list...` apis are provided -along with a generic insertion function that can take a sort callback. These -allow a struct to participate on multiple linked-lists simultaneously. - -## common const string and blob folding - -In some cases the input to be stored in the lwsac may repeat the same tokens -multiple times... if the pattern is to store the string or blob in the lwsac -and then point to it, you can make use of a helper api - -``` -uint8_t * -lwsac_scan_extant(struct lwsac *head, uint8_t *find, size_t len, int nul); -``` - -This lets you check in all previous used parts of the lwsac for the same -string or blob, plus optionally a terminal NUL afterwards. If not found, -it returns `NULL` and you can copy it into the lwsac as usual. If it is -found, a pointer is returned, and you can use this directly without copying -the string or blob in again. - -## optimizations to minimize overhead - -If the lwsac will persist in the system for some time, it's desirable to reduce -the memory needed as overhead. Overhead is created - - - once per chunk... in addition to the malloc overhead, there's an lwsac - chunk header of 2 x pointers and 2 x size_t - - - at the unused part at the end that was allocated but not used - -A good strategy is to make the initial allocation reflect the minimum expected -size of the overall lwsac in one hit. Then use a chunk size that is a tradeoff -between the number of chunks that might be needed and the fact that on average, -you can expect to waste half a chunk. For example if the storage is typically -between 4K - 6K, you could allocate 4K or 4.5K for the first chunk and then fill -in using 256 or 512 byte chunks. - -You can measure the overhead in an lwsac using `lwsac_total_overhead()`. - -The lwsac apis look first in the unused part of previous chunks, if any, and -will place new allocations there preferentially if they fit. This helps for the -case lwsac was forced to allocate a new chunk because you asked for something -large, while there was actually significant free space left in the old chunk, -just not enough for that particular allocation. Subsequent lwsac use can then -"backfill" smaller things there to make best use of allocated space. diff -Nru libwebsockets-4.0.20/lib/misc/lws-genhash.c libwebsockets-2.4.2/lib/misc/lws-genhash.c --- libwebsockets-4.0.20/lib/misc/lws-genhash.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/lws-genhash.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,159 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * lws_genhash provides a hash abstraction api in lws that works the same + * whether you are using openssl or mbedtls hash functions underneath. + */ +#include "libwebsockets.h" + +#if defined(LWS_WITH_MBEDTLS) +#include + +#if (MBEDTLS_VERSION_NUMBER >= 0x02070000) +#define MBA(fn) fn##_ret +#else +#define MBA(fn) fn +#endif +#endif + +size_t +lws_genhash_size(int type) +{ + switch(type) { + case LWS_GENHASH_TYPE_SHA1: + return 20; + case LWS_GENHASH_TYPE_SHA256: + return 32; + case LWS_GENHASH_TYPE_SHA512: + return 64; + } + + return 0; +} + +int +lws_genhash_init(struct lws_genhash_ctx *ctx, int type) +{ + ctx->type = type; + +#if defined(LWS_WITH_MBEDTLS) + switch (ctx->type) { + case LWS_GENHASH_TYPE_SHA1: + mbedtls_sha1_init(&ctx->u.sha1); + MBA(mbedtls_sha1_starts)(&ctx->u.sha1); + break; + case LWS_GENHASH_TYPE_SHA256: + mbedtls_sha256_init(&ctx->u.sha256); + MBA(mbedtls_sha256_starts)(&ctx->u.sha256, 0); + break; + case LWS_GENHASH_TYPE_SHA512: + mbedtls_sha512_init(&ctx->u.sha512); + MBA(mbedtls_sha512_starts)(&ctx->u.sha512, 0); + break; + default: + return 1; + } +#else + ctx->mdctx = EVP_MD_CTX_create(); + if (!ctx->mdctx) + return 1; + + switch (ctx->type) { + case LWS_GENHASH_TYPE_SHA1: + ctx->evp_type = EVP_sha1(); + break; + case LWS_GENHASH_TYPE_SHA256: + ctx->evp_type = EVP_sha256(); + break; + case LWS_GENHASH_TYPE_SHA512: + ctx->evp_type = EVP_sha512(); + break; + default: + return 1; + } + + if (EVP_DigestInit_ex(ctx->mdctx, ctx->evp_type, NULL) != 1) { + EVP_MD_CTX_destroy(ctx->mdctx); + + return 1; + } + +#endif + return 0; +} + +int +lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len) +{ +#if defined(LWS_WITH_MBEDTLS) + switch (ctx->type) { + case LWS_GENHASH_TYPE_SHA1: + MBA(mbedtls_sha1_update)(&ctx->u.sha1, in, len); + break; + case LWS_GENHASH_TYPE_SHA256: + MBA(mbedtls_sha256_update)(&ctx->u.sha256, in, len); + break; + case LWS_GENHASH_TYPE_SHA512: + MBA(mbedtls_sha512_update)(&ctx->u.sha512, in, len); + break; + } +#else + return EVP_DigestUpdate(ctx->mdctx, in, len) != 1; +#endif + + return 0; +} + +int +lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result) +{ +#if defined(LWS_WITH_MBEDTLS) + switch (ctx->type) { + case LWS_GENHASH_TYPE_SHA1: + MBA(mbedtls_sha1_finish)(&ctx->u.sha1, result); + mbedtls_sha1_free(&ctx->u.sha1); + break; + case LWS_GENHASH_TYPE_SHA256: + MBA(mbedtls_sha256_finish)(&ctx->u.sha256, result); + mbedtls_sha256_free(&ctx->u.sha256); + break; + case LWS_GENHASH_TYPE_SHA512: + MBA(mbedtls_sha512_finish)(&ctx->u.sha512, result); + mbedtls_sha512_free(&ctx->u.sha512); + break; + } + + return 0; +#else + unsigned int len; + int ret = 0; + + if (result) + ret = EVP_DigestFinal_ex(ctx->mdctx, result, &len) != 1; + + (void)len; + + EVP_MD_CTX_destroy(ctx->mdctx); + + return ret; +#endif +} + + diff -Nru libwebsockets-4.0.20/lib/misc/lws-ring.c libwebsockets-2.4.2/lib/misc/lws-ring.c --- libwebsockets-4.0.20/lib/misc/lws-ring.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/lws-ring.c 2018-03-08 10:28:37.000000000 +0000 @@ -1,31 +1,27 @@ /* - * libwebsockets - small server side websockets and web server implementation + * libwebsockets - lws-ring multi-tail abstract ringbuffer api * - * Copyright (C) 2010 - 2019 Andy Green + * Copyright (C) 2017 Andy Green * - * 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: + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * 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 should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA */ -#include -#include "private-lib-core.h" +#include "private-libwebsockets.h" -struct lws_ring * +LWS_VISIBLE LWS_EXTERN struct lws_ring * lws_ring_create(size_t element_len, size_t count, void (*destroy_element)(void *)) { @@ -34,8 +30,8 @@ if (!ring) return NULL; - ring->buflen = (uint32_t)(count * element_len); - ring->element_len = (uint32_t)element_len; + ring->buflen = count * element_len; + ring->element_len = element_len; ring->head = 0; ring->oldest_tail = 0; ring->destroy_element = destroy_element; @@ -50,7 +46,7 @@ return ring; } -void +LWS_VISIBLE LWS_EXTERN void lws_ring_destroy(struct lws_ring *ring) { if (ring->destroy_element) @@ -67,7 +63,7 @@ lws_free(ring); } -size_t +LWS_VISIBLE LWS_EXTERN size_t lws_ring_get_count_free_elements(struct lws_ring *ring) { int f; @@ -97,7 +93,7 @@ return f / ring->element_len; } -size_t +LWS_VISIBLE LWS_EXTERN size_t lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail) { int f; @@ -123,14 +119,14 @@ return f / ring->element_len; } -int +LWS_VISIBLE LWS_EXTERN int lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start, size_t *bytes) { int n; /* n is how many bytes the whole fifo can take */ - n = (int)(lws_ring_get_count_free_elements(ring) * ring->element_len); + n = lws_ring_get_count_free_elements(ring) * ring->element_len; if (!n) return 1; @@ -148,24 +144,24 @@ return 0; } -void +LWS_VISIBLE LWS_EXTERN void lws_ring_bump_head(struct lws_ring *ring, size_t bytes) { - ring->head = (ring->head + (uint32_t)bytes) % ring->buflen; + ring->head = (ring->head + bytes) % ring->buflen; } -size_t +LWS_VISIBLE LWS_EXTERN size_t lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count) { const uint8_t *osrc = src; int m, n; /* n is how many bytes the whole fifo can take */ - n = (int)(lws_ring_get_count_free_elements(ring) * ring->element_len); + n = lws_ring_get_count_free_elements(ring) * ring->element_len; /* restrict n to how much we want to insert */ - if ((uint32_t)n > max_count * ring->element_len) - n = (int)(max_count * ring->element_len); + if ((size_t)n > max_count * ring->element_len) + n = max_count * ring->element_len; /* * n is legal to insert, but as an optimization we can cut the @@ -195,7 +191,7 @@ return (((uint8_t *)src + n) - osrc) / ring->element_len; } -size_t +LWS_VISIBLE LWS_EXTERN size_t lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest, size_t max_count) { @@ -210,12 +206,11 @@ } /* n is how many bytes the whole fifo has for us */ - n = (int)(lws_ring_get_count_waiting_elements(ring, tail) * - ring->element_len); + n = lws_ring_get_count_waiting_elements(ring, tail) * ring->element_len; /* restrict n to how much we want to insert */ if ((size_t)n > max_count * ring->element_len) - n = (int)(max_count * ring->element_len); + n = max_count * ring->element_len; if (!dest) { *tail = ((*tail) + n) % ring->buflen; @@ -251,7 +246,7 @@ return (((uint8_t *)dest + n) - odest) / ring->element_len; } -const void * +LWS_VISIBLE LWS_EXTERN const void * lws_ring_get_element(struct lws_ring *ring, uint32_t *tail) { if (!tail) @@ -263,7 +258,7 @@ return ((uint8_t *)ring->buf) + *tail; } -void +LWS_VISIBLE LWS_EXTERN void lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail) { if (!ring->destroy_element) { @@ -278,21 +273,8 @@ } } -uint32_t +LWS_VISIBLE LWS_EXTERN uint32_t lws_ring_get_oldest_tail(struct lws_ring *ring) { return ring->oldest_tail; } - -void -lws_ring_dump(struct lws_ring *ring, uint32_t *tail) -{ - if (tail == NULL) - tail = &ring->oldest_tail; - lwsl_notice("ring %p: buflen %u, elem_len %u, head %u, oldest_tail %u\n" - " free_elems: %u; for tail %u, waiting elements: %u\n", - ring, (int)ring->buflen, (int)ring->element_len, - (int)ring->head, (int)ring->oldest_tail, - (int)lws_ring_get_count_free_elements(ring), (int)*tail, - (int)lws_ring_get_count_waiting_elements(ring, tail)); -} diff -Nru libwebsockets-4.0.20/lib/misc/lws-struct-lejp.c libwebsockets-2.4.2/lib/misc/lws-struct-lejp.c --- libwebsockets-4.0.20/lib/misc/lws-struct-lejp.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/lws-struct-lejp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,796 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 -#include - -#include - -signed char -lws_struct_schema_only_lejp_cb(struct lejp_ctx *ctx, char reason) -{ - lws_struct_args_t *a = (lws_struct_args_t *)ctx->user; - const lws_struct_map_t *map = a->map_st[ctx->pst_sp]; - size_t n = a->map_entries_st[ctx->pst_sp]; - lejp_callback cb = map->lejp_cb; - - if (reason != LEJPCB_VAL_STR_END || ctx->path_match != 1) - return 0; - - while (n--) { - if (strcmp(ctx->buf, map->colname)) { - map++; - continue; - } - - a->dest = lwsac_use_zero(&a->ac, map->aux, a->ac_block_size); - if (!a->dest) { - lwsl_err("%s: OOT\n", __func__); - - return 1; - } - a->dest_len = map->aux; - if (!ctx->pst_sp) - a->top_schema_index = (int)(map - a->map_st[ctx->pst_sp]); - - if (!cb) - cb = lws_struct_default_lejp_cb; - - lejp_parser_push(ctx, a->dest, &map->child_map[0].colname, - (uint8_t)map->child_map_size, cb); - a->map_st[ctx->pst_sp] = map->child_map; - a->map_entries_st[ctx->pst_sp] = map->child_map_size; - - return 0; - } - - lwsl_notice("%s: unknown schema %s\n", __func__, ctx->buf); - - return 1; -} - -static int -lws_struct_lejp_push(struct lejp_ctx *ctx, lws_struct_args_t *args, - const lws_struct_map_t *map, uint8_t *ch) -{ - lejp_callback cb = map->lejp_cb; - - if (!cb) - cb = lws_struct_default_lejp_cb; - - lejp_parser_push(ctx, ch, (const char * const*)map->child_map, - (uint8_t)map->child_map_size, cb); - - args->map_st[ctx->pst_sp] = map->child_map; - args->map_entries_st[ctx->pst_sp] = map->child_map_size; - - return 0; -} - -signed char -lws_struct_default_lejp_cb(struct lejp_ctx *ctx, char reason) -{ - lws_struct_args_t *args = (lws_struct_args_t *)ctx->user; - const lws_struct_map_t *map, *pmap = NULL; - uint8_t *ch; - size_t n; - char *u; - - if (reason == LEJPCB_ARRAY_END) { - lejp_parser_pop(ctx); - - return 0; - } - - if (reason == LEJPCB_ARRAY_START) { - map = &args->map_st[ctx->pst_sp][ctx->path_match - 1]; - - if (map->type == LSMT_LIST) - lws_struct_lejp_push(ctx, args, map, NULL); - - return 0; - } - - if (ctx->pst_sp) - pmap = &args->map_st[ctx->pst_sp - 1] - [ctx->pst[ctx->pst_sp - 1].path_match - 1]; - map = &args->map_st[ctx->pst_sp][ctx->path_match - 1]; - n = args->map_entries_st[ctx->pst_sp]; - - if (reason == LEJPCB_OBJECT_START) { - - if (map->type != LSMT_CHILD_PTR) { - ctx->pst[ctx->pst_sp].user = NULL; - - return 0; - } - pmap = map; - - lws_struct_lejp_push(ctx, args, map, NULL); - map = &args->map_st[ctx->pst_sp][ctx->path_match - 1]; - n = args->map_entries_st[ctx->pst_sp]; - } - - if (reason == LEJPCB_OBJECT_END && pmap && pmap->type == LSMT_CHILD_PTR) - lejp_parser_pop(ctx); - - if (map->type == LSMT_SCHEMA) { - - while (n--) { - if (strcmp(map->colname, ctx->buf)) { - map++; - continue; - } - - /* instantiate the correct toplevel object */ - - ch = lwsac_use_zero(&args->ac, map->aux, - args->ac_block_size); - if (!ch) { - lwsl_err("OOM\n"); - - return 1; - } - - lws_struct_lejp_push(ctx, args, map, ch); - - return 0; - } - lwsl_notice("%s: unknown schema\n", __func__); - - goto cleanup; - } - - if (!ctx->pst[ctx->pst_sp].user) { - struct lws_dll2_owner *owner; - struct lws_dll2 *list; - - /* create list item object if none already */ - - if (!ctx->path_match || !pmap) - return 0; - - map = &args->map_st[ctx->pst_sp - 1][ctx->path_match - 1]; - n = args->map_entries_st[ctx->pst_sp - 1]; - - if (pmap->type != LSMT_LIST && pmap->type != LSMT_CHILD_PTR) - return 1; - - /* we need to create a child or array item object */ - - owner = (struct lws_dll2_owner *) - (((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs); - - assert(pmap->aux); - - /* instantiate one of the child objects */ - - ctx->pst[ctx->pst_sp].user = lwsac_use_zero(&args->ac, - pmap->aux, args->ac_block_size); - if (!ctx->pst[ctx->pst_sp].user) { - lwsl_err("OOM\n"); - - return 1; - } - lwsl_notice("%s: created child object size %d\n", __func__, - (int)pmap->aux); - - if (pmap->type == LSMT_LIST) { - list = (struct lws_dll2 *)((char *)ctx->pst[ctx->pst_sp].user + - map->ofs_clist); - - lws_dll2_add_tail(list, owner); - } - } - - if (!ctx->path_match) - return 0; - - if (reason == LEJPCB_VAL_STR_CHUNK) { - lejp_collation_t *coll; - - /* don't cache stuff we are going to ignore */ - - if (map->type == LSMT_STRING_CHAR_ARRAY && - args->chunks_length >= map->aux) - return 0; - - coll = lwsac_use_zero(&args->ac_chunks, sizeof(*coll), - sizeof(*coll)); - if (!coll) { - lwsl_err("%s: OOT\n", __func__); - - return 1; - } - coll->chunks.prev = NULL; - coll->chunks.next = NULL; - coll->chunks.owner = NULL; - - coll->len = ctx->npos; - lws_dll2_add_tail(&coll->chunks, &args->chunks_owner); - - memcpy(coll->buf, ctx->buf, ctx->npos); - - args->chunks_length += ctx->npos; - - return 0; - } - - if (reason != LEJPCB_VAL_STR_END && reason != LEJPCB_VAL_NUM_INT && - reason != LEJPCB_VAL_TRUE && reason != LEJPCB_VAL_FALSE) - return 0; - - /* this is the end of the string */ - - if (ctx->pst[ctx->pst_sp].user && pmap && pmap->type == LSMT_CHILD_PTR) { - void **pp = (void **) - (((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs); - - *pp = ctx->pst[ctx->pst_sp].user; - } - - u = (char *)ctx->pst[ctx->pst_sp].user; - if (!u) - u = (char *)ctx->pst[ctx->pst_sp - 1].user; - - { - char **pp, *s; - size_t lim, b; - long long li; - - switch (map->type) { - case LSMT_SIGNED: - if (map->aux == sizeof(signed char)) { - signed char *pc; - pc = (signed char *)(u + map->ofs); - *pc = atoi(ctx->buf); - break; - } - if (map->aux == sizeof(int)) { - int *pi; - pi = (int *)(u + map->ofs); - *pi = atoi(ctx->buf); - break; - } - if (map->aux == sizeof(long)) { - long *pl; - pl = (long *)(u + map->ofs); - *pl = atol(ctx->buf); - } else { - long long *pll; - pll = (long long *)(u + map->ofs); - *pll = atoll(ctx->buf); - } - break; - - case LSMT_UNSIGNED: - if (map->aux == sizeof(unsigned char)) { - unsigned char *pc; - pc = (unsigned char *)(u + map->ofs); - *pc = atoi(ctx->buf); - break; - } - if (map->aux == sizeof(unsigned int)) { - unsigned int *pi; - pi = (unsigned int *)(u + map->ofs); - *pi = atoi(ctx->buf); - break; - } - if (map->aux == sizeof(unsigned long)) { - unsigned long *pl; - pl = (unsigned long *)(u + map->ofs); - *pl = atol(ctx->buf); - } else { - unsigned long long *pll; - pll = (unsigned long long *)(u + map->ofs); - *pll = atoll(ctx->buf); - } - break; - - case LSMT_BOOLEAN: - li = reason == LEJPCB_VAL_TRUE; - if (map->aux == sizeof(char)) { - char *pc; - pc = (char *)(u + map->ofs); - *pc = (char)li; - break; - } - if (map->aux == sizeof(int)) { - int *pi; - pi = (int *)(u + map->ofs); - *pi = (int)li; - } else { - uint64_t *p64; - p64 = (uint64_t *)(u + map->ofs); - *p64 = li; - } - break; - - case LSMT_STRING_CHAR_ARRAY: - s = (char *)(u + map->ofs); - lim = map->aux - 1; - goto chunk_copy; - - case LSMT_STRING_PTR: - pp = (char **)(u + map->ofs); - lim = args->chunks_length + ctx->npos; - s = lwsac_use(&args->ac, lim + 1, args->ac_block_size); - if (!s) - goto cleanup; - *pp = s; - -chunk_copy: - s[lim] = '\0'; - /* copy up to lim from the string chunk ac first */ - lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1, - args->chunks_owner.head) { - lejp_collation_t *coll = (lejp_collation_t *)p; - - if (lim) { - b = coll->len; - if (b > lim) - b = lim; - memcpy(s, coll->buf, b); - s += b; - lim -= b; - } - } lws_end_foreach_dll_safe(p, p1); - - lwsac_free(&args->ac_chunks); - args->chunks_owner.count = 0; - args->chunks_owner.head = NULL; - args->chunks_owner.tail = NULL; - - if (lim) { - b = ctx->npos; - if (b > lim) - b = lim; - memcpy(s, ctx->buf, b); - s[b] = '\0'; - } - break; - default: - break; - } - } - - if (args->cb) - args->cb(args->dest, args->cb_arg); - - return 0; - -cleanup: - lwsl_notice("%s: cleanup\n", __func__); - lwsac_free(&args->ac_chunks); - args->chunks_owner.count = 0; - args->chunks_owner.head = NULL; - args->chunks_owner.tail = NULL; - - return 1; -} - -static const char * schema[] = { "schema" }; - -int -lws_struct_json_init_parse(struct lejp_ctx *ctx, lejp_callback cb, void *user) -{ - if (!cb) - cb = lws_struct_schema_only_lejp_cb; - lejp_construct(ctx, cb, user, schema, 1); - - ctx->path_stride = sizeof(lws_struct_map_t); - - return 0; -} - -lws_struct_serialize_t * -lws_struct_json_serialize_create(const lws_struct_map_t *map, - size_t map_entries, int flags, - const void *ptoplevel) -{ - lws_struct_serialize_t *js = lws_zalloc(sizeof(*js), __func__); - lws_struct_serialize_st_t *j; - - if (!js) - return NULL; - - js->flags = flags; - - j = &js->st[0]; - j->map = map; - j->map_entries = map_entries; - j->obj = ptoplevel; - j->idt = 0; - - return js; -} - -void -lws_struct_json_serialize_destroy(lws_struct_serialize_t **pjs) -{ - if (!*pjs) - return; - - lws_free(*pjs); - - *pjs = NULL; -} - -static void -lws_struct_pretty(lws_struct_serialize_t *js, uint8_t **pbuf, size_t *plen) -{ - if (js->flags & LSSERJ_FLAG_PRETTY) { - int n; - - *(*pbuf)++ = '\n'; - (*plen)--; - for (n = 0; n < js->st[js->sp].idt; n++) { - *(*pbuf)++ = ' '; - (*plen)--; - } - } -} - -lws_struct_json_serialize_result_t -lws_struct_json_serialize(lws_struct_serialize_t *js, uint8_t *buf, - size_t len, size_t *written) -{ - lws_struct_serialize_st_t *j; - const lws_struct_map_t *map; - size_t budget = 0, olen = len, m; - struct lws_dll2_owner *o; - unsigned long long uli; - const char *q; - const void *p; - char dbuf[72]; - long long li; - int n, used; - - *written = 0; - *buf = '\0'; - - while (len > sizeof(dbuf) + 20) { - j = &js->st[js->sp]; - map = &j->map[j->map_entry]; - q = j->obj + map->ofs; - - /* early check if the entry should be elided */ - - switch (map->type) { - case LSMT_STRING_PTR: - case LSMT_CHILD_PTR: - q = (char *)*(char **)q; - if (!q) - goto up; - break; - - case LSMT_LIST: - o = (struct lws_dll2_owner *)q; - p = j->dllpos = lws_dll2_get_head(o); - if (!p) - goto up; - break; - - default: - break; - } - - if (j->subsequent) { - *buf++ = ','; - len--; - lws_struct_pretty(js, &buf, &len); - } - j->subsequent = 1; - - if (map->type != LSMT_SCHEMA && !js->offset) { - n = lws_snprintf((char *)buf, len, "\"%s\":", - map->colname); - buf += n; - len -= n; - if (js->flags & LSSERJ_FLAG_PRETTY) { - *buf++ = ' '; - len--; - } - } - - switch (map->type) { - case LSMT_BOOLEAN: - case LSMT_UNSIGNED: - if (map->aux == sizeof(char)) { - uli = *(unsigned char *)q; - } else { - if (map->aux == sizeof(int)) { - uli = *(unsigned int *)q; - } else { - if (map->aux == sizeof(long)) - uli = *(unsigned long *)q; - else - uli = *(unsigned long long *)q; - } - } - q = dbuf; - - if (map->type == LSMT_BOOLEAN) { - budget = lws_snprintf(dbuf, sizeof(dbuf), - "%s", uli ? "true" : "false"); - } else - budget = lws_snprintf(dbuf, sizeof(dbuf), - "%llu", uli); - break; - - case LSMT_SIGNED: - if (map->aux == sizeof(signed char)) { - li = (long long)*(signed char *)q; - } else { - if (map->aux == sizeof(int)) { - li = (long long)*(int *)q; - } else { - if (map->aux == sizeof(long)) - li = (long long)*(long *)q; - else - li = *(long long *)q; - } - } - q = dbuf; - budget = lws_snprintf(dbuf, sizeof(dbuf), "%lld", li); - break; - - case LSMT_STRING_CHAR_ARRAY: - case LSMT_STRING_PTR: - if (!js->offset) { - *buf++ = '\"'; - len--; - } - break; - - case LSMT_LIST: - *buf++ = '['; - len--; - if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH) - return LSJS_RESULT_ERROR; - - /* add a stack level to handle parsing array members */ - - o = (struct lws_dll2_owner *)q; - p = j->dllpos = lws_dll2_get_head(o); - - if (!j->dllpos) { - *buf++ = ']'; - len--; - goto up; - } - - n = j->idt; - j = &js->st[++js->sp]; - j->idt = n + 2; - j->map = map->child_map; - j->map_entries = map->child_map_size; - j->size = map->aux; - j->subsequent = 0; - j->map_entry = 0; - lws_struct_pretty(js, &buf, &len); - *buf++ = '{'; - len--; - lws_struct_pretty(js, &buf, &len); - if (p) - j->obj = ((char *)p) - j->map->ofs_clist; - else - j->obj = NULL; - continue; - - case LSMT_CHILD_PTR: - - if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH) - return LSJS_RESULT_ERROR; - - /* add a stack level to handle parsing child members */ - - n = j->idt; - j = &js->st[++js->sp]; - j->idt = n + 2; - j->map = map->child_map; - j->map_entries = map->child_map_size; - j->size = map->aux; - j->subsequent = 0; - j->map_entry = 0; - *buf++ = '{'; - len--; - lws_struct_pretty(js, &buf, &len); - j->obj = q; - - continue; - - case LSMT_SCHEMA: - q = dbuf; - *buf++ = '{'; - len--; - j = &js->st[++js->sp]; - lws_struct_pretty(js, &buf, &len); - budget = lws_snprintf(dbuf, 15, "\"schema\":"); - if (js->flags & LSSERJ_FLAG_PRETTY) - dbuf[budget++] = ' '; - - budget += lws_snprintf(dbuf + budget, - sizeof(dbuf) - budget, - "\"%s\"", map->colname); - - - if (js->sp != 1) - return LSJS_RESULT_ERROR; - j->map = map->child_map; - j->map_entries = map->child_map_size; - j->size = map->aux; - j->subsequent = 0; - j->map_entry = 0; - j->obj = js->st[js->sp - 1].obj; - j->dllpos = NULL; - /* we're actually at the same level */ - j->subsequent = 1; - j->idt = 1; - break; - } - - switch (map->type) { - case LSMT_STRING_CHAR_ARRAY: - case LSMT_STRING_PTR: - /* - * This is a bit tricky... we have to escape the string - * which may 6x its length depending on what the - * contents are. - * - * We offset the unescaped string starting point first - */ - - q += js->offset; - budget = strlen(q); /* how much unescaped is left */ - - /* - * This is going to escape as much as it can fit, and - * let us know the amount of input that was consumed - * in "used". - */ - - lws_json_purify((char *)buf, q, len, &used); - m = strlen((const char *)buf); - buf += m; - len -= m; - js->remaining = budget - used; - js->offset = used; - if (!js->remaining) - js->offset = 0; - - break; - default: - q += js->offset; - budget -= js->remaining; - - if (budget > len) { - js->remaining = budget - len; - js->offset = len; - budget = len; - } else { - js->remaining = 0; - js->offset = 0; - } - - memcpy(buf, q, budget); - buf += budget; - *buf = '\0'; - len -= budget; - break; - } - - - - switch (map->type) { - case LSMT_STRING_CHAR_ARRAY: - case LSMT_STRING_PTR: - *buf++ = '\"'; - len--; - break; - case LSMT_SCHEMA: - continue; - default: - break; - } - - if (js->remaining) - continue; -up: - if (++j->map_entry < j->map_entries) - continue; - - if (!js->sp) - continue; - js->sp--; - if (!js->sp) { - lws_struct_pretty(js, &buf, &len); - *buf++ = '}'; - len--; - lws_struct_pretty(js, &buf, &len); - break; - } - js->offset = 0; - j = &js->st[js->sp]; - map = &j->map[j->map_entry]; - - if (map->type == LSMT_CHILD_PTR) { - lws_struct_pretty(js, &buf, &len); - *buf++ = '}'; - len--; - - /* we have done the singular child pointer */ - - js->offset = 0; - goto up; - } - - if (map->type != LSMT_LIST) - continue; - /* - * we are coming back up to an array map, it means we should - * advance to the next array member if there is one - */ - - lws_struct_pretty(js, &buf, &len); - *buf++ = '}'; - len--; - - p = j->dllpos = j->dllpos->next; - if (j->dllpos) { - /* - * there was another item in the array to do... let's - * move on to that and do it - */ - *buf++ = ','; - len--; - lws_struct_pretty(js, &buf, &len); - js->offset = 0; - j = &js->st[++js->sp]; - j->map_entry = 0; - map = &j->map[j->map_entry]; - - *buf++ = '{'; - len--; - lws_struct_pretty(js, &buf, &len); - - j->subsequent = 0; - j->obj = ((char *)p) - j->map->ofs_clist; - continue; - } - - /* there are no further items in the array */ - - js->offset = 0; - lws_struct_pretty(js, &buf, &len); - *buf++ = ']'; - len--; - goto up; - } - - *written = olen - len; - *buf = '\0'; /* convenience, a NUL after the official end */ - - return LSJS_RESULT_FINISH; -} diff -Nru libwebsockets-4.0.20/lib/misc/lws-struct-sqlite.c libwebsockets-2.4.2/lib/misc/lws-struct-sqlite.c --- libwebsockets-4.0.20/lib/misc/lws-struct-sqlite.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/lws-struct-sqlite.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,484 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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 -#include - -#include - -/* - * we get one of these per matching result from the query - */ - -static int -lws_struct_sq3_deser_cb(void *priv, int cols, char **cv, char **cn) -{ - lws_struct_args_t *a = (lws_struct_args_t *)priv; - char *u = lwsac_use_zero(&a->ac, a->dest_len, a->ac_block_size); - lws_dll2_owner_t *o = (lws_dll2_owner_t *)a->cb_arg; - const lws_struct_map_t *map = a->map_st[0]; - int n, mems = a->map_entries_st[0]; - long long li; - size_t lim; - char **pp; - char *s; - - if (!u) { - lwsl_err("OOM\n"); - - return 1; - } - - lws_dll2_add_tail((lws_dll2_t *)((char *)u + a->toplevel_dll2_ofs), o); - - while (mems--) { - for (n = 0; n < cols; n++) { - if (!cv[n] || strcmp(cn[n], map->colname)) - continue; - - switch (map->type) { - case LSMT_SIGNED: - if (map->aux == sizeof(signed char)) { - signed char *pc; - pc = (signed char *)(u + map->ofs); - *pc = atoi(cv[n]); - break; - } - if (map->aux == sizeof(short)) { - short *ps; - ps = (short *)(u + map->ofs); - *ps = atoi(cv[n]); - break; - } - if (map->aux == sizeof(int)) { - int *pi; - pi = (int *)(u + map->ofs); - *pi = atoi(cv[n]); - break; - } - if (map->aux == sizeof(long)) { - long *pl; - pl = (long *)(u + map->ofs); - *pl = atol(cv[n]); - break; - } - { - long long *pll; - pll = (long long *)(u + map->ofs); - *pll = atoll(cv[n]); - } - break; - - case LSMT_UNSIGNED: - if (map->aux == sizeof(unsigned char)) { - unsigned char *pc; - pc = (unsigned char *)(u + map->ofs); - *pc = atoi(cv[n]); - break; - } - if (map->aux == sizeof(unsigned short)) { - unsigned short *ps; - ps = (unsigned short *)(u + map->ofs); - *ps = atoi(cv[n]); - break; - } - if (map->aux == sizeof(unsigned int)) { - unsigned int *pi; - pi = (unsigned int *)(u + map->ofs); - *pi = atoi(cv[n]); - break; - } - if (map->aux == sizeof(unsigned long)) { - unsigned long *pl; - pl = (unsigned long *)(u + map->ofs); - *pl = atol(cv[n]); - break; - } - { - unsigned long long *pll; - pll = (unsigned long long *)(u + map->ofs); - *pll = atoll(cv[n]); - } - break; - - case LSMT_BOOLEAN: - li = 0; - if (!strcmp(cv[n], "true") || - !strcmp(cv[n], "TRUE") || cv[n][0] == '1') - li = 1; - if (map->aux == sizeof(char)) { - char *pc; - pc = (char *)(u + map->ofs); - *pc = (char)li; - break; - } - if (map->aux == sizeof(int)) { - int *pi; - pi = (int *)(u + map->ofs); - *pi = (int)li; - } else { - uint64_t *p64; - p64 = (uint64_t *)(u + map->ofs); - *p64 = li; - } - break; - - case LSMT_STRING_CHAR_ARRAY: - s = (char *)(u + map->ofs); - lim = map->aux; - lws_strncpy(s, cv[n], lim); - break; - - case LSMT_STRING_PTR: - pp = (char **)(u + map->ofs); - lim = strlen(cv[n]); - s = lwsac_use(&a->ac, lim + 1, a->ac_block_size); - if (!s) - return 1; - *pp = s; - memcpy(s, cv[n], lim); - s[lim] = '\0'; - break; - default: - break; - } - } - map++; - } - - return 0; -} - -/* - * Call this with an LSM_SCHEMA map, its colname is the table name and its - * type information describes the toplevel type. Schema is dereferenced and - * put in args before the actual sq3 query, which is given the child map. - */ - -int -lws_struct_sq3_deserialize(sqlite3 *pdb, const char *filter, const char *order, - const lws_struct_map_t *schema, lws_dll2_owner_t *o, - struct lwsac **ac, int start, int _limit) -{ - char s[250], where[250]; - lws_struct_args_t a; - int limit = _limit < 0 ? -_limit : _limit; - - memset(&a, 0, sizeof(a)); - a.cb_arg = o; /* lws_dll2_owner tracking query result objects */ - a.map_st[0] = schema->child_map; - a.map_entries_st[0] = schema->child_map_size; - a.dest_len = schema->aux; /* size of toplevel object to allocate */ - a.toplevel_dll2_ofs = schema->ofs; - - lws_dll2_owner_clear(o); - - where[0] = '\0'; - lws_snprintf(where, sizeof(where), " where _lws_idx >= %llu %s", - (unsigned long long)start, filter ? filter : ""); - - lws_snprintf(s, sizeof(s) - 1, "select * " - "from %s %s order by _lws_idx%s %slimit %d;", - schema->colname, where, order ? order : "", - _limit < 0 ? "desc " : "", limit); - - if (sqlite3_exec(pdb, s, lws_struct_sq3_deser_cb, &a, NULL) != SQLITE_OK) { - lwsl_err("%s: %s: fail\n", __func__, sqlite3_errmsg(pdb)); - lwsac_free(&a.ac); - return -1; - } - - *ac = a.ac; - - return 0; -} - -/* - * This takes a struct and turns it into an sqlite3 UPDATE, using the given - * schema... which has one LSM_SCHEMA_DLL2 entry wrapping the actual schema - */ - -static int -_lws_struct_sq3_ser_one(sqlite3 *pdb, const lws_struct_map_t *schema, - uint32_t idx, void *st) -{ - const lws_struct_map_t *map = schema->child_map; - int n, m, pk = 0, nentries = schema->child_map_size; - size_t sql_est = 46 + strlen(schema->colname) + 1; - /* "insert into (_lws_idx, ) values (00000001,);" ... - * plus the table name */ - uint8_t *stb = (uint8_t *)st; - const char *p; - char *sql; - - /* - * Figure out an estimate for the length of the populated sqlite - * command, and then malloc it up - */ - - for (n = 0; n < nentries; n++) { - sql_est += strlen(map[n].colname) + 2; - switch (map[n].type) { - case LSMT_SIGNED: - case LSMT_UNSIGNED: - case LSMT_BOOLEAN: - - switch (map[n].aux) { - case 1: - sql_est += 3 + 2; - break; - case 2: - sql_est += 5 + 2; - break; - case 4: - sql_est += 10 + 2; - break; - case 8: - sql_est += 20 + 2; - break; - } - - if (map[n].type == LSMT_SIGNED) - sql_est++; /* minus sign */ - - break; - case LSMT_STRING_CHAR_ARRAY: - sql_est += lws_sql_purify_len((const char *)st + - map[n].ofs) + 2; - break; - - case LSMT_STRING_PTR: - p = *((const char * const *)&stb[map[n].ofs]); - sql_est += (p ? lws_sql_purify_len(p) : 0) + 2; - break; - - default: - lwsl_err("%s: unsupported type\n", __func__); - assert(0); - break; - } - } - - sql = malloc(sql_est); - if (!sql) - return -1; - - m = lws_snprintf(sql, sql_est, "insert into %s(_lws_idx, ", - schema->colname); - - /* - * First explicit integer type is primary key autoincrement, should - * not be specified - */ - - for (n = 0; n < nentries; n++) { - if (!pk && map[n].type == LSMT_UNSIGNED) { - pk = 1; - continue; - } - m += lws_snprintf(sql + m, sql_est - m, - n == nentries - 1 ? "%s" : "%s, ", - map[n].colname); - } - - m += lws_snprintf(sql + m, sql_est - m, ") values(%u, ", idx); - - pk = 0; - for (n = 0; n < nentries; n++) { - uint64_t uu64; - size_t q; - - if (!pk && map[n].type == LSMT_UNSIGNED) { - pk = 1; - continue; - } - - switch (map[n].type) { - case LSMT_SIGNED: - case LSMT_UNSIGNED: - case LSMT_BOOLEAN: - - uu64 = 0; - for (q = 0; q < map[n].aux; q++) - uu64 |= ((uint64_t)stb[map[n].ofs + q] << - (q << 3)); - - if (map[n].type == LSMT_SIGNED) - m += lws_snprintf(sql + m, sql_est - m, "%lld", - (long long)(int64_t)uu64); - else - m += lws_snprintf(sql + m, sql_est - m, "%llu", - (unsigned long long)uu64); - break; - - case LSMT_STRING_CHAR_ARRAY: - sql[m++] = '\''; - lws_sql_purify(sql + m, (const char *)&stb[map[n].ofs], - sql_est - m - 4); - m += strlen(sql + m); - sql[m++] = '\''; - break; - case LSMT_STRING_PTR: - p = *((const char * const *)&stb[map[n].ofs]); - sql[m++] = '\''; - if (p) { - lws_sql_purify(sql + m, p, sql_est - m - 4); - m += strlen(sql + m); - } - sql[m++] = '\''; - break; - default: - lwsl_err("%s: unsupported type\n", __func__); - assert(0); - break; - } - - if (n != nentries - 1) { - if (sql_est - m < 6) - return -1; - sql[m++] = ','; - sql[m++] = ' '; - } - } - - lws_snprintf(sql + m, sql_est - m, ");"); - - n = sqlite3_exec(pdb, sql, NULL, NULL, NULL); - free(sql); - if (n != SQLITE_OK) { - lwsl_err("%s: %s: fail\n", __func__, sqlite3_errmsg(pdb)); - return -1; - } - - return 0; -} - -int -lws_struct_sq3_serialize(sqlite3 *pdb, const lws_struct_map_t *schema, - lws_dll2_owner_t *owner, uint32_t manual_idx) -{ - uint32_t idx = manual_idx; - - lws_start_foreach_dll(struct lws_dll2 *, p, owner->head) { - void *item = (void *)((uint8_t *)p - schema->ofs_clist); - if (_lws_struct_sq3_ser_one(pdb, schema, idx++, item)) - return 1; - - } lws_end_foreach_dll(p); - - return 0; -} - -int -lws_struct_sq3_create_table(sqlite3 *pdb, const lws_struct_map_t *schema) -{ - const lws_struct_map_t *map = schema->child_map; - int map_size = schema->child_map_size, subsequent = 0; - char s[2048], *p = s, *end = &s[sizeof(s) - 1], - *pri = " primary key autoincrement", *use; - - p += lws_snprintf(p, end - p, - "create table if not exists %s (_lws_idx integer, ", - schema->colname); - - while (map_size--) { - if (map->type > LSMT_STRING_PTR) { - map++; - continue; - } - if (subsequent && (end - p) > 4) { - *p++ = ','; - *p++ = ' '; - } - subsequent = 1; - if (map->type < LSMT_STRING_CHAR_ARRAY) { - use = ""; - if (map->colname[0] != '_') /* _lws_idx is not primary key */ - use = pri; - p += lws_snprintf(p, end - p, "%s integer%s", - map->colname, use); - if (map->colname[0] != '_') - pri = ""; - } else - p += lws_snprintf(p, end - p, "%s varchar", - map->colname); - - map++; - } - - p += lws_snprintf(p, end - p, ");"); - - if (sqlite3_exec(pdb, s, NULL, NULL, NULL) != SQLITE_OK) { - lwsl_err("%s: %s: fail\n", __func__, sqlite3_errmsg(pdb)); - - return -1; - } - - return 0; -} - -int -lws_struct_sq3_open(struct lws_context *context, const char *sqlite3_path, - sqlite3 **pdb) -{ -#if !defined(WIN32) - int uid = 0, gid = 0; -#endif - - if (sqlite3_open_v2(sqlite3_path, pdb, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, - NULL) != SQLITE_OK) { - lwsl_err("%s: Unable to open db %s: %s\n", - __func__, sqlite3_path, sqlite3_errmsg(*pdb)); - - return 1; - } - -#if !defined(WIN32) - lws_get_effective_uid_gid(context, &uid, &gid); - if (uid) - chown(sqlite3_path, uid, gid); - chmod(sqlite3_path, 0600); - - lwsl_notice("%s: created %s owned by %u:%u mode 0600\n", __func__, - sqlite3_path, (unsigned int)uid, (unsigned int)gid); -#else - lwsl_notice("%s: created %s\n", __func__, sqlite3_path); -#endif - sqlite3_extended_result_codes(*pdb, 1); - - return 0; -} - -int -lws_struct_sq3_close(sqlite3 **pdb) -{ - if (!*pdb) - return 0; - - sqlite3_close(*pdb); - *pdb = NULL; - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/misc/peer-limits.c libwebsockets-2.4.2/lib/misc/peer-limits.c --- libwebsockets-4.0.20/lib/misc/peer-limits.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/peer-limits.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,294 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 -#include "private-lib-core.h" - -/* requires context->lock */ -static void -__lws_peer_remove_from_peer_wait_list(struct lws_context *context, - struct lws_peer *peer) -{ - struct lws_peer *df; - - lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) { - if (*p == peer) { - df = *p; - - *p = df->peer_wait_list; - df->peer_wait_list = NULL; - - return; - } - } lws_end_foreach_llp(p, peer_wait_list); -} - -/* requires context->lock */ -static void -__lws_peer_add_to_peer_wait_list(struct lws_context *context, - struct lws_peer *peer) -{ - __lws_peer_remove_from_peer_wait_list(context, peer); - - peer->peer_wait_list = context->peer_wait_list; - context->peer_wait_list = peer; -} - - -struct lws_peer * -lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd) -{ - struct lws_context *context = vhost->context; - socklen_t rlen = 0; - void *q; - uint8_t *q8; - struct lws_peer *peer; - uint32_t hash = 0; - int n, af = AF_INET; - struct sockaddr_storage addr; - - if (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK) - return NULL; - -#ifdef LWS_WITH_IPV6 - if (LWS_IPV6_ENABLED(vhost)) { - af = AF_INET6; - } -#endif - rlen = sizeof(addr); - if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen)) - /* eg, udp doesn't have to have a peer */ - return NULL; - -#ifdef LWS_WITH_IPV6 - if (af == AF_INET) -#endif - { - struct sockaddr_in *s = (struct sockaddr_in *)&addr; - q = &s->sin_addr; - rlen = sizeof(s->sin_addr); - } -#ifdef LWS_WITH_IPV6 - else { - struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; - q = &s->sin6_addr; - rlen = sizeof(s->sin6_addr); - } -#endif - - q8 = q; - for (n = 0; n < (int)rlen; n++) - hash = (((hash << 4) | (hash >> 28)) * n) ^ q8[n]; - - hash = hash % context->pl_hash_elements; - - lws_context_lock(context, "peer search"); /* <======================= */ - - lws_start_foreach_ll(struct lws_peer *, peerx, - context->pl_hash_table[hash]) { - if (peerx->af == af && !memcmp(q, peerx->addr, rlen)) { - lws_context_unlock(context); /* === */ - return peerx; - } - } lws_end_foreach_ll(peerx, next); - - lwsl_info("%s: creating new peer\n", __func__); - - peer = lws_zalloc(sizeof(*peer), "peer"); - if (!peer) { - lws_context_unlock(context); /* === */ - lwsl_err("%s: OOM for new peer\n", __func__); - return NULL; - } - - context->count_peers++; - peer->next = context->pl_hash_table[hash]; - peer->hash = hash; - peer->af = af; - context->pl_hash_table[hash] = peer; - memcpy(peer->addr, q, rlen); - time(&peer->time_created); - /* - * On creation, the peer has no wsi attached, so is created on the - * wait list. When a wsi is added it is removed from the wait list. - */ - time(&peer->time_closed_all); - __lws_peer_add_to_peer_wait_list(context, peer); - - lws_context_unlock(context); /* ====================================> */ - - return peer; -} - -/* requires context->lock */ -static int -__lws_peer_destroy(struct lws_context *context, struct lws_peer *peer) -{ - lws_start_foreach_llp(struct lws_peer **, p, - context->pl_hash_table[peer->hash]) { - if (*p == peer) { - struct lws_peer *df = *p; - *p = df->next; - lws_free(df); - context->count_peers--; - - return 0; - } - } lws_end_foreach_llp(p, next); - - return 1; -} - -void -lws_peer_cull_peer_wait_list(struct lws_context *context) -{ - struct lws_peer *df; - time_t t; - - time(&t); - - if (context->next_cull && t < context->next_cull) - return; - - lws_context_lock(context, "peer cull"); /* <========================= */ - - context->next_cull = t + 5; - - lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) { - if (t - (*p)->time_closed_all > 10) { - df = *p; - - /* remove us from the peer wait list */ - *p = df->peer_wait_list; - df->peer_wait_list = NULL; - - __lws_peer_destroy(context, df); - continue; /* we already point to next, if any */ - } - } lws_end_foreach_llp(p, peer_wait_list); - - lws_context_unlock(context); /* ====================================> */ -} - -void -lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer, - struct lws *wsi) -{ - if (!peer) - return; - - lws_context_lock(context, "peer add"); /* <========================== */ - - peer->count_wsi++; - wsi->peer = peer; - __lws_peer_remove_from_peer_wait_list(context, peer); - - lws_context_unlock(context); /* ====================================> */ -} - -void -lws_peer_dump_from_wsi(struct lws *wsi) -{ - struct lws_peer *peer; - - if (!wsi || !wsi->peer) - return; - - peer = wsi->peer; - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - lwsl_notice("%s: wsi %p: created %llu: wsi: %d/%d, ah %d/%d\n", - __func__, - wsi, (unsigned long long)peer->time_created, - peer->count_wsi, peer->total_wsi, - peer->http.count_ah, peer->http.total_ah); -#else - lwsl_notice("%s: wsi %p: created %llu: wsi: %d/%d\n", __func__, - wsi, (unsigned long long)peer->time_created, - peer->count_wsi, peer->total_wsi); -#endif -} - -void -lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer) -{ - if (!peer) - return; - - lws_context_lock(context, "peer wsi close"); /* <==================== */ - - assert(peer->count_wsi); - peer->count_wsi--; - - if (!peer->count_wsi -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - && !peer->http.count_ah -#endif - ) { - /* - * in order that we can accumulate peer activity correctly - * allowing for periods when the peer has no connections, - * we don't synchronously destroy the peer when his last - * wsi closes. Instead we mark the time his last wsi - * closed and add him to a peer_wait_list to be reaped - * later if no further activity is coming. - */ - time(&peer->time_closed_all); - __lws_peer_add_to_peer_wait_list(context, peer); - } - - lws_context_unlock(context); /* ====================================> */ -} - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) -int -lws_peer_confirm_ah_attach_ok(struct lws_context *context, - struct lws_peer *peer) -{ - if (!peer) - return 0; - - if (context->ip_limit_ah && - peer->http.count_ah >= context->ip_limit_ah) { - lwsl_info("peer reached ah limit %d, deferring\n", - context->ip_limit_ah); - - return 1; - } - - return 0; -} - -void -lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer) -{ - if (!peer) - return; - - lws_context_lock(context, "peer ah detach"); /* <==================== */ - assert(peer->http.count_ah); - peer->http.count_ah--; - lws_context_unlock(context); /* ====================================> */ -} -#endif diff -Nru libwebsockets-4.0.20/lib/misc/romfs.c libwebsockets-2.4.2/lib/misc/romfs.c --- libwebsockets-4.0.20/lib/misc/romfs.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/romfs.c 2018-03-08 10:28:37.000000000 +0000 @@ -31,14 +31,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include #include "romfs.h" -#if defined(LWS_WITH_ESP32) #include "esp_spi_flash.h" -#endif #define RFS_STRING_MAX 96 @@ -49,13 +46,11 @@ static void set_cache(romfs_inode_t inode, size_t len) { -#if defined(LWS_WITH_ESP32) spi_flash_read((uint32_t)inode, cache, len); -#endif } static uint32_t -untohl(const u32_be_t be) +ntohl(const u32_be_t be) { return ((be >> 24) & 0xff) | ((be >> 16) & 0xff) << 8 | @@ -96,7 +91,7 @@ cr->magic2 != 0x2d736631) return 0; - return untohl(cr->size); + return ntohl(cr->size); } static romfs_inode_t @@ -117,23 +112,22 @@ { set_cache(i, sizeof(*i)); return (romfs_inode_t)((const uint8_t *)romfs + - untohl(ci->dir_start)); + ntohl(ci->dir_start)); } static romfs_inode_t romfs_lookup(romfs_t romfs, romfs_inode_t start, const char *path) { romfs_inode_t level, i = start, i_in; - const char *p, *cp; + const char *p, *n, *cp; uint32_t next_be; if (start == (romfs_inode_t)romfs) i = skip_and_pad((romfs_inode_t)romfs); level = i; while (i != (romfs_inode_t)romfs) { - const char *n = ((const char *)i) + sizeof(*i); - p = path; + n = ((const char *)i) + sizeof(*i); i_in = i; set_cache(i, sizeof(*i)); @@ -142,27 +136,23 @@ cp = (const char *)cache; set_cache((romfs_inode_t)n, RFS_STRING_MAX); - while (*p && *p != '/' && *cp && *p == *cp && - (p - path) < RFS_STRING_MAX) { + while (*p && *p != '/' && *cp && *p == *cp && (p - path) < RFS_STRING_MAX) { p++; n++; cp++; } - while (*p == '/' && p[1] == '/') - p++; - if (!*cp && (!*p || *p == '/') && - (untohl(next_be) & 7) == RFST_HARDLINK) { + (ntohl(next_be) & 7) == RFST_HARDLINK) { set_cache(i, sizeof(*i)); return (romfs_inode_t) ((const uint8_t *)romfs + - (untohl(ci->dir_start) & ~15)); + (ntohl(ci->dir_start) & ~15)); } if (!*p && !*cp) { set_cache(i, sizeof(*i)); - if ((untohl(ci->next) & 7) == RFST_SYMLINK) { + if ((ntohl(ci->next) & 7) == RFST_SYMLINK) { i = romfs_symlink(romfs, level, i); continue; } @@ -172,12 +162,9 @@ if (!*p && *cp == '/') return NULL; - while (*p == '/' && p[1] == '/') - p++; - if (*p == '/' && !*cp) { set_cache(i, sizeof(*i)); - switch (untohl(ci->next) & 7) { + switch (ntohl(ci->next) & 7) { case RFST_SYMLINK: i = romfs_symlink(romfs, level, i); if (!i) @@ -203,11 +190,11 @@ } set_cache(i, sizeof(*i)); - if (!(untohl(ci->next) & ~15)) + if (!(ntohl(ci->next) & ~15)) return NULL; i = (romfs_inode_t)((const uint8_t *)romfs + - (untohl(ci->next) & ~15)); + (ntohl(ci->next) & ~15)); if (i == i_in) return NULL; } @@ -229,9 +216,9 @@ return NULL; set_cache(i, sizeof(*i)); - *len = untohl(ci->size); + *len = ntohl(ci->size); if (csum) - *csum = untohl(ci->checksum); + *csum = ntohl(ci->checksum); return (void *)skip_and_pad(i); } diff -Nru libwebsockets-4.0.20/lib/misc/sha-1.c libwebsockets-2.4.2/lib/misc/sha-1.c --- libwebsockets-4.0.20/lib/misc/sha-1.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/sha-1.c 2018-03-08 10:28:37.000000000 +0000 @@ -32,8 +32,7 @@ * implemented by Jun-ichiro itojun Itoh */ -#include -#include "private-lib-core.h" +#include "private-libwebsockets.h" #ifdef LWS_HAVE_SYS_TYPES_H #include @@ -184,7 +183,7 @@ H(3) = H(3) + d; H(4) = H(4) + e; - memset(&ctxt->m.b8[0], 0, 64); + bzero(&ctxt->m.b8[0], 64); } /*------------------------------------------------------------*/ @@ -192,7 +191,7 @@ static void _sha1_init(struct sha1_ctxt *ctxt) { - memset(ctxt, 0, sizeof(struct sha1_ctxt)); + bzero(ctxt, sizeof(struct sha1_ctxt)); H(0) = 0x67452301; H(1) = 0xefcdab89; H(2) = 0x98badcfe; @@ -211,14 +210,14 @@ padstart = COUNT % 64; padlen = 64 - padstart; if (padlen < 8) { - memset(&ctxt->m.b8[padstart], 0, padlen); + bzero(&ctxt->m.b8[padstart], padlen); COUNT += (unsigned char)padlen; COUNT %= 64; sha1_step(ctxt); padstart = COUNT % 64; /* should be 0 */ padlen = 64 - padstart; /* should be 64 */ } - memset(&ctxt->m.b8[padstart], 0, padlen - 8); + bzero(&ctxt->m.b8[padstart], padlen - 8); COUNT += ((unsigned char)padlen - 8); COUNT %= 64; #if BYTE_ORDER == BIG_ENDIAN @@ -237,14 +236,18 @@ void sha1_loop(struct sha1_ctxt *ctxt, const unsigned char *input, size_t len) { + size_t gaplen; + size_t gapstart; size_t off; + size_t copysiz; off = 0; while (off < len) { - size_t gapstart = COUNT % 64, gaplen = 64 - gapstart, - copysiz = (gaplen < len - off) ? gaplen : len - off; + gapstart = COUNT % 64; + gaplen = 64 - gapstart; + copysiz = (gaplen < len - off) ? gaplen : len - off; memcpy(&ctxt->m.b8[gapstart], &input[off], copysiz); COUNT += (unsigned char)copysiz; COUNT %= 64; @@ -282,7 +285,7 @@ * This should look and work like the libcrypto implementation */ -unsigned char * +LWS_VISIBLE unsigned char * lws_SHA1(const unsigned char *d, size_t n, unsigned char *md) { struct sha1_ctxt ctx; diff -Nru libwebsockets-4.0.20/lib/misc/smtp.c libwebsockets-2.4.2/lib/misc/smtp.c --- libwebsockets-4.0.20/lib/misc/smtp.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/smtp.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,242 @@ +/* + * SMTP support for libwebsockets + * + * Copyright (C) 2016-2017 Andy Green + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +static unsigned int +lwsgs_now_secs(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return tv.tv_sec; +} + +static void +ccb(uv_handle_t* handle) +{ + +} + +static void +alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) +{ + struct lws_email *email = (struct lws_email *)handle->data; + + *buf = uv_buf_init(email->email_buf, sizeof(email->email_buf) - 1); +} + +static void +on_write_end(uv_write_t *req, int status) { + lwsl_notice("%s\n", __func__); + if (status == -1) { + fprintf(stderr, "error on_write_end"); + return; + } +} + +static void +lwsgs_email_read(struct uv_stream_s *s, ssize_t nread, const uv_buf_t *buf) +{ + struct lws_email *email = (struct lws_email *)s->data; + static const short retcodes[] = { + 0, /* idle */ + 0, /* connecting */ + 220, /* connected */ + 250, /* helo */ + 250, /* from */ + 250, /* to */ + 354, /* data */ + 250, /* body */ + 221, /* quit */ + }; + uv_write_t write_req; + uv_buf_t wbuf; + int n; + + if (nread >= 0) + email->email_buf[nread] = '\0'; + lwsl_notice("%s: %s\n", __func__, buf->base); + if (nread == -1) { + lwsl_err("%s: failed\n", __func__); + return; + } + + n = atoi(buf->base); + if (n != retcodes[email->estate]) { + lwsl_err("%s: bad response from server\n", __func__); + goto close_conn; + } + + switch (email->estate) { + case LGSSMTP_CONNECTED: + n = sprintf(email->content, "HELO %s\n", email->email_helo); + email->estate = LGSSMTP_SENT_HELO; + break; + case LGSSMTP_SENT_HELO: + n = sprintf(email->content, "MAIL FROM: <%s>\n", + email->email_from); + email->estate = LGSSMTP_SENT_FROM; + break; + case LGSSMTP_SENT_FROM: + n = sprintf(email->content, "RCPT TO: <%s>\n", email->email_to); + email->estate = LGSSMTP_SENT_TO; + break; + case LGSSMTP_SENT_TO: + n = sprintf(email->content, "DATA\n"); + email->estate = LGSSMTP_SENT_DATA; + break; + case LGSSMTP_SENT_DATA: + if (email->on_get_body(email, email->content, + email->max_content_size)) + return; + n = strlen(email->content); + email->estate = LGSSMTP_SENT_BODY; + break; + case LGSSMTP_SENT_BODY: + n = sprintf(email->content, "quit\n"); + email->estate = LGSSMTP_SENT_QUIT; + break; + case LGSSMTP_SENT_QUIT: + lwsl_notice("%s: done\n", __func__); + email->on_sent(email); + email->estate = LGSSMTP_IDLE; + goto close_conn; + default: + return; + } + + puts(email->content); + wbuf = uv_buf_init(email->content, n); + uv_write(&write_req, s, &wbuf, 1, on_write_end); + + return; + +close_conn: + + uv_close((uv_handle_t *)s, ccb); +} + +static void +lwsgs_email_on_connect(uv_connect_t *req, int status) +{ + struct lws_email *email = (struct lws_email *)req->data; + + lwsl_notice("%s\n", __func__); + + if (status == -1) { + lwsl_err("%s: failed\n", __func__); + return; + } + + uv_read_start(req->handle, alloc_buffer, lwsgs_email_read); + email->estate = LGSSMTP_CONNECTED; +} + + +static void +uv_timeout_cb_email(uv_timer_t *w +#if UV_VERSION_MAJOR == 0 + , int status +#endif +) +{ + struct lws_email *email = lws_container_of(w, struct lws_email, + timeout_email); + time_t now = lwsgs_now_secs(); + struct sockaddr_in req_addr; + + switch (email->estate) { + case LGSSMTP_IDLE: + + if (email->on_next(email)) + break; + + email->estate = LGSSMTP_CONNECTING; + + uv_tcp_init(email->loop, &email->email_client); + if (uv_ip4_addr(email->email_smtp_ip, 25, &req_addr)) { + lwsl_err("Unable to convert mailserver ads\n"); + return; + } + + lwsl_notice("LGSSMTP_IDLE: connecting\n"); + + email->email_connect_started = now; + email->email_connect_req.data = email; + email->email_client.data = email; + uv_tcp_connect(&email->email_connect_req, &email->email_client, + (struct sockaddr *)&req_addr, + lwsgs_email_on_connect); + + uv_timer_start(&email->timeout_email, + uv_timeout_cb_email, 5000, 0); + + break; + + case LGSSMTP_CONNECTING: + if (email->email_connect_started - now > 5) { + lwsl_err("mail session timed out\n"); + /* !!! kill the connection */ + uv_close((uv_handle_t *) &email->email_connect_req, ccb); + email->estate = LGSSMTP_IDLE; + } + break; + + default: + break; + } +} + +LWS_VISIBLE LWS_EXTERN int +lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content) +{ + email->content = lws_malloc(max_content, "email content"); + if (!email->content) + return 1; + + email->max_content_size = max_content; + uv_timer_init(loop, &email->timeout_email); + + email->loop = loop; + + /* trigger him one time in a bit */ + uv_timer_start(&email->timeout_email, uv_timeout_cb_email, 2000, 0); + + return 0; +} + +LWS_VISIBLE LWS_EXTERN void +lws_email_check(struct lws_email *email) +{ + uv_timer_start(&email->timeout_email, uv_timeout_cb_email, 1000, 0); +} + +LWS_VISIBLE LWS_EXTERN void +lws_email_destroy(struct lws_email *email) +{ + if (email->content) + lws_free_set_NULL(email->content); + + uv_timer_stop(&email->timeout_email); + uv_close((uv_handle_t *)&email->timeout_email, NULL); +} diff -Nru libwebsockets-4.0.20/lib/misc/spawn.c libwebsockets-2.4.2/lib/misc/spawn.c --- libwebsockets-4.0.20/lib/misc/spawn.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/spawn.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,511 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif - -#include "private-lib-core.h" -#include - -void -lws_spawn_timeout(struct lws_sorted_usec_list *sul) -{ - struct lws_spawn_piped *lsp = lws_container_of(sul, - struct lws_spawn_piped, sul); - - lwsl_warn("%s: spawn exceeded timeout, killing\n", __func__); - - lws_spawn_piped_kill_child_process(lsp); -} - -static struct lws * -lws_create_basic_wsi(struct lws_context *context, int tsi, - const struct lws_role_ops *ops) -{ - struct lws *new_wsi; - - if (!context->vhost_list) - return NULL; - - if ((unsigned int)context->pt[tsi].fds_count == - context->fd_limit_per_thread - 1) { - lwsl_err("no space for new conn\n"); - return NULL; - } - - new_wsi = lws_zalloc(sizeof(*new_wsi), "new wsi"); - if (new_wsi == NULL) { - lwsl_err("Out of memory for new connection\n"); - return NULL; - } - - new_wsi->tsi = tsi; - new_wsi->context = context; - new_wsi->pending_timeout = NO_PENDING_TIMEOUT; - new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; - - /* initialize the instance struct */ - - lws_role_transition(new_wsi, 0, LRS_ESTABLISHED, ops); - - new_wsi->hdr_parsing_completed = 0; - new_wsi->position_in_fds_table = LWS_NO_FDS_POS; - - /* - * these can only be set once the protocol is known - * we set an unestablished connection's protocol pointer - * to the start of the defauly vhost supported list, so it can look - * for matching ones during the handshake - */ - - new_wsi->user_space = NULL; - new_wsi->desc.sockfd = LWS_SOCK_INVALID; - context->count_wsi_allocated++; - - return new_wsi; -} - -void -lws_spawn_piped_destroy(struct lws_spawn_piped **_lsp) -{ - struct lws_spawn_piped *lsp = *_lsp; - int n; - - if (!lsp) - return; - - for (n = 0; n < 3; n++) { - if (lsp->pipe_fds[n][!!(n == 0)] == 0) - lwsl_err("ZERO FD IN CGI CLOSE"); - - if (lsp->pipe_fds[n][!!(n == 0)] >= 0) { - close(lsp->pipe_fds[n][!!(n == 0)]); - lsp->pipe_fds[n][!!(n == 0)] = LWS_SOCK_INVALID; - } - } - - lws_dll2_remove(&lsp->dll); - - lws_sul_schedule(lsp->info.vh->context, lsp->info.tsi, &lsp->sul, - NULL, LWS_SET_TIMER_USEC_CANCEL); - - lws_free_set_NULL((*_lsp)); -} - -int -lws_spawn_reap(struct lws_spawn_piped *lsp) -{ - long hz = sysconf(_SC_CLK_TCK); /* accounting Hz */ - void *opaque = lsp->info.opaque; - lsp_cb_t cb = lsp->info.reap_cb; - struct lws_spawn_piped temp; - struct tms tms; - int n; - - if (lsp->child_pid < 1) - return 0; - - /* check if exited, do not reap yet */ - - memset(&lsp->si, 0, sizeof(lsp->si)); - n = waitid(P_PID, lsp->child_pid, &lsp->si, WEXITED | WNOHANG | WNOWAIT); - if (n < 0) { - lwsl_info("%s: child %d still running\n", __func__, lsp->child_pid); - return 0; - } - - if (!lsp->si.si_code) - return 0; - - /* his process has exited... */ - - if (!lsp->reaped) { - /* mark the earliest time we knew he had gone */ - lsp->reaped = lws_now_usecs(); - - /* - * Switch the timeout to restrict the amount of grace time - * to drain stdwsi - */ - - lws_sul_schedule(lsp->info.vh->context, lsp->info.tsi, - &lsp->sul, lws_spawn_timeout, - 5 * LWS_US_PER_SEC); - } - - /* - * Stage finalizing our reaction to the process going down until the - * stdwsi flushed whatever is in flight and all noticed they were - * closed. For that reason, each stdwsi close must call lws_spawn_reap - * to check if that was the last one and we can proceed with the reap. - */ - - if (!lsp->ungraceful && lsp->pipes_alive) { - lwsl_debug("%s: stdwsi alive, not reaping\n", __func__); - return 0; - } - - /* we reached the reap point, no need for timeout wait */ - - lws_sul_schedule(lsp->info.vh->context, lsp->info.tsi, &lsp->sul, NULL, - LWS_SET_TIMER_USEC_CANCEL); - - /* - * All the stdwsi went down, nothing more is coming... it's over - * Collect the final information and then reap the dead process - */ - - if (times(&tms) != (clock_t) -1) { - /* - * Cpu accounting in us - */ - lsp->accounting[0] = ((uint64_t)tms.tms_cstime * 1000000) / hz; - lsp->accounting[1] = ((uint64_t)tms.tms_cutime * 1000000) / hz; - lsp->accounting[2] = ((uint64_t)tms.tms_stime * 1000000) / hz; - lsp->accounting[3] = ((uint64_t)tms.tms_utime * 1000000) / hz; - } - - temp = *lsp; - waitid(P_PID, lsp->child_pid, &lsp->si, WEXITED | WNOHANG); - lsp->child_pid = -1; - - /* destroy the lsp itself first (it's freed and plsp set NULL */ - - if (lsp->info.plsp) - lws_spawn_piped_destroy(lsp->info.plsp); - - /* then do the parent callback informing it's destroyed */ - - if (cb) - cb(opaque, temp.accounting, &temp.si, - temp.we_killed_him_timeout | - (temp.we_killed_him_spew << 1)); - - return 1; /* was reaped */ -} - -int -lws_spawn_piped_kill_child_process(struct lws_spawn_piped *lsp) -{ - int status, n; - - if (lsp->child_pid <= 0) - return 1; - - lsp->ungraceful = 1; /* don't wait for flushing, just kill it */ - - if (lws_spawn_reap(lsp)) - /* that may have invalidated lsp */ - return 0; - - /* kill the process group */ - n = kill(-lsp->child_pid, SIGTERM); - lwsl_debug("%s: SIGTERM child PID %d says %d (errno %d)\n", __func__, - lsp->child_pid, n, errno); - if (n < 0) { - /* - * hum seen errno=3 when process is listed in ps, - * it seems we don't always retain process grouping - * - * Direct these fallback attempt to the exact child - */ - n = kill(lsp->child_pid, SIGTERM); - if (n < 0) { - n = kill(lsp->child_pid, SIGPIPE); - if (n < 0) { - n = kill(lsp->child_pid, SIGKILL); - if (n < 0) - lwsl_info("%s: SIGKILL PID %d " - "failed errno %d " - "(maybe zombie)\n", __func__, - lsp->child_pid, errno); - } - } - } - - /* He could be unkillable because he's a zombie */ - - n = 1; - while (n > 0) { - n = waitpid(-lsp->child_pid, &status, WNOHANG); - if (n > 0) - lwsl_debug("%s: reaped PID %d\n", __func__, n); - if (n <= 0) { - n = waitpid(lsp->child_pid, &status, WNOHANG); - if (n > 0) - lwsl_debug("%s: reaped PID %d\n", __func__, n); - } - } - - lws_spawn_reap(lsp); - /* that may have invalidated lsp */ - - return 0; -} - -/* - * Deals with spawning a subprocess and executing it securely with stdin/out/err - * diverted into pipes - */ - -struct lws_spawn_piped * -lws_spawn_piped(const struct lws_spawn_piped_info *i) -{ - const struct lws_protocols *pcol = i->vh->context->vhost_list->protocols; - struct lws_context *context = i->vh->context; - struct lws_spawn_piped *lsp; - const char *wd; - int n, m; - - if (i->protocol_name) - pcol = lws_vhost_name_to_protocol(i->vh, i->protocol_name); - if (!pcol) { - lwsl_err("%s: unknown protocol %s\n", __func__, - i->protocol_name ? i->protocol_name : "default"); - - return NULL; - } - - lsp = lws_zalloc(sizeof(*lsp), __func__); - if (!lsp) - return NULL; - - /* wholesale take a copy of info */ - lsp->info = *i; - - /* - * Prepare the stdin / out / err pipes - */ - - for (n = 0; n < 3; n++) { - lsp->pipe_fds[n][0] = -1; - lsp->pipe_fds[n][1] = -1; - } - - /* create pipes for [stdin|stdout] and [stderr] */ - - for (n = 0; n < 3; n++) - if (pipe(lsp->pipe_fds[n]) == -1) - goto bail1; - - /* create wsis for each stdin/out/err fd */ - - for (n = 0; n < 3; n++) { - lsp->stdwsi[n] = lws_create_basic_wsi(i->vh->context, i->tsi, - i->ops ? i->ops : &role_ops_raw_file); - if (!lsp->stdwsi[n]) { - lwsl_err("%s: unable to create lsp stdwsi\n", __func__); - goto bail2; - } - lsp->stdwsi[n]->lsp_channel = n; - lws_vhost_bind_wsi(i->vh, lsp->stdwsi[n]); - lsp->stdwsi[n]->protocol = pcol; - lsp->stdwsi[n]->opaque_user_data = i->opaque; - - lwsl_debug("%s: lsp stdwsi %p: pipe idx %d -> fd %d / %d\n", __func__, - lsp->stdwsi[n], n, lsp->pipe_fds[n][!!(n == 0)], - lsp->pipe_fds[n][!(n == 0)]); - - /* read side is 0, stdin we want the write side, others read */ - - lsp->stdwsi[n]->desc.sockfd = lsp->pipe_fds[n][!!(n == 0)]; - if (fcntl(lsp->pipe_fds[n][!!(n == 0)], F_SETFL, O_NONBLOCK) < 0) { - lwsl_err("%s: setting NONBLOCK failed\n", __func__); - goto bail2; - } - } - - for (n = 0; n < 3; n++) { - if (context->event_loop_ops->sock_accept) - if (context->event_loop_ops->sock_accept(lsp->stdwsi[n])) - goto bail3; - - if (__insert_wsi_socket_into_fds(context, lsp->stdwsi[n])) - goto bail3; - if (i->opt_parent) { - lsp->stdwsi[n]->parent = i->opt_parent; - lsp->stdwsi[n]->sibling_list = i->opt_parent->child_list; - i->opt_parent->child_list = lsp->stdwsi[n]; - } - } - - if (lws_change_pollfd(lsp->stdwsi[LWS_STDIN], LWS_POLLIN, LWS_POLLOUT)) - goto bail3; - if (lws_change_pollfd(lsp->stdwsi[LWS_STDOUT], LWS_POLLOUT, LWS_POLLIN)) - goto bail3; - if (lws_change_pollfd(lsp->stdwsi[LWS_STDERR], LWS_POLLOUT, LWS_POLLIN)) - goto bail3; - - lwsl_debug("%s: fds in %d, out %d, err %d\n", __func__, - lsp->stdwsi[LWS_STDIN]->desc.sockfd, - lsp->stdwsi[LWS_STDOUT]->desc.sockfd, - lsp->stdwsi[LWS_STDERR]->desc.sockfd); - - /* we are ready with the redirection pipes... run the thing */ -#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE) - lsp->child_pid = fork(); -#else - lsp->child_pid = vfork(); -#endif - if (lsp->child_pid < 0) { - lwsl_err("%s: fork failed, errno %d", __func__, errno); - goto bail3; - } - -#if defined(__linux__) - prctl(PR_SET_PDEATHSIG, SIGTERM); -#endif - - if (lsp->info.disable_ctrlc) - /* stops non-daemonized main processess getting SIGINT - * from TTY */ - setpgrp(); - - if (lsp->child_pid) { - - /* we are the parent process */ - - lwsl_info("%s: lsp %p spawned PID %d\n", __func__, lsp, - lsp->child_pid); - - lws_sul_schedule(context, i->tsi, &lsp->sul, lws_spawn_timeout, - i->timeout_us ? i->timeout_us : - 300 * LWS_US_PER_SEC); - - /* - * close: stdin:r, stdout:w, stderr:w - * hide from other forks: stdin:w, stdout:r, stderr:r - */ - for (n = 0; n < 3; n++) { - lws_plat_apply_FD_CLOEXEC(lsp->pipe_fds[n][!!(n == 0)]); - close(lsp->pipe_fds[n][!(n == 0)]); - } - - lsp->pipes_alive = 3; - lsp->created = lws_now_usecs(); - - if (i->owner) - lws_dll2_add_head(&lsp->dll, i->owner); - - if (i->timeout_us) - lws_sul_schedule(context, i->tsi, &lsp->sul, - lws_spawn_timeout, i->timeout_us); - - return lsp; - } - - /* - * We are the forked process, redirect and kill inherited things. - * - * Because of vfork(), we cannot do anything that changes pages in - * the parent environment. Stuff that changes kernel state for the - * process is OK. Stuff that happens after the execvpe() is OK. - */ - - if (i->chroot_path && chroot(i->chroot_path)) { - lwsl_err("%s: child chroot %s failed, errno %d\n", - __func__, i->chroot_path, errno); - - exit(2); - } - - /* cwd: somewhere we can at least read things and enter it */ - - wd = i->wd; - if (!wd) - wd = "/tmp"; - if (chdir(wd)) - lwsl_notice("%s: Failed to cd to %s\n", __func__, wd); - - for (m = 0; m < 3; m++) { - if (dup2(lsp->pipe_fds[m][!(m == 0)], m) < 0) { - lwsl_err("%s: stdin dup2 failed\n", __func__); - goto bail3; - } - close(lsp->pipe_fds[m][0]); - close(lsp->pipe_fds[m][1]); - } - - // lwsl_notice("%s: child cd %s, exec %s\n", __func__, wd, i->exec_array[0]); - -#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE) -#if defined(__linux__) - m = 0; - while (i->env_array[m]){ - char *p = strchr(i->env_array[m], '='); - *p++ = '\0'; - setenv(i->env_array[m], p, 1); - m++; - } -#endif - execvp(i->exec_array[0], (char * const *)&i->exec_array[0]); -#else - execvpe(i->exec_array[0], (char * const *)&i->exec_array[0], - &i->env_array[0]); -#endif - - lwsl_err("%s: child exec of %s failed %d\n", __func__, i->exec_array[0], - LWS_ERRNO); - - _exit(1); - -bail3: - - while (--n >= 0) - __remove_wsi_socket_from_fds(lsp->stdwsi[n]); -bail2: - for (n = 0; n < 3; n++) - if (lsp->stdwsi[n]) - __lws_free_wsi(lsp->stdwsi[n]); - -bail1: - for (n = 0; n < 3; n++) { - if (lsp->pipe_fds[n][0] >= 0) - close(lsp->pipe_fds[n][0]); - if (lsp->pipe_fds[n][1] >= 0) - close(lsp->pipe_fds[n][1]); - } - - lws_free(lsp); - - lwsl_err("%s: failed\n", __func__); - - return NULL; -} - -void -lws_spawn_stdwsi_closed(struct lws_spawn_piped *lsp) -{ - assert(lsp); - lsp->pipes_alive--; - lwsl_debug("%s: pipes alive %d\n", __func__, lsp->pipes_alive); - lws_spawn_reap(lsp); -} - -int -lws_spawn_get_stdfd(struct lws *wsi) -{ - return wsi->lsp_channel; -} diff -Nru libwebsockets-4.0.20/lib/misc/threadpool/README.md libwebsockets-2.4.2/lib/misc/threadpool/README.md --- libwebsockets-4.0.20/lib/misc/threadpool/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/threadpool/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,182 +0,0 @@ -## Threadpool - -### Overview - -![overview](/doc-assets/threadpool.svg) - -An api that lets you create a pool of worker threads, and a queue of tasks that -are bound to a wsi. Tasks in their own thread synchronize communication to the -lws service thread of the wsi via `LWS_CALLBACK_SERVER_WRITEABLE` and friends. - -Tasks can produce some output, then return that they want to "sync" with the -service thread. That causes a `LWS_CALLBACK_SERVER_WRITEABLE` in the service -thread context, where the output can be consumed, and the task told to continue, -or completed tasks be reaped. - -ALL of the details related to thread synchronization and an associated wsi in -the lws service thread context are handled by the threadpool api, without needing -any pthreads in user code. - -### Example - -https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/ws-server/minimal-ws-server-threadpool - -### Lifecycle considerations - -#### Tasks vs wsi - -Although all tasks start out as being associated to a wsi, in fact the lifetime -of a task and that of the wsi are not necessarily linked. - -You may start a long task, eg, that runs atomically in its thread for 30s, and -at any time the client may close the connection, eg, close a browser window. - -There are arrangements that a task can "check in" periodically with lws to see -if it has been asked to stop, allowing the task lifetime to be related to the -wsi lifetime somewhat, but some tasks are going to be atomic and longlived. - -For that reason, at wsi close an ongoing task can detach from the wsi and -continue until it ends or understands it has been asked to stop. To make -that work, the task is created with a `cleanup` callback that performs any -freeing independent of still having a wsi around to do it... the task takes over -responsibility to free the user pointer on destruction when the task is created. - -![Threadpool States](/doc-assets/threadpool-states.svg) - -#### Reaping completed tasks - -Once created, although tasks may run asynchronously, the task itself does not -get destroyed on completion but added to a "done queue". Only when the lws -service thread context queries the task state with `lws_threadpool_task_status()` -may the task be reaped and memory freed. - -This is analogous to unix processes and `wait()`. - -If a task became detached from its wsi, then joining the done queue is enough -to get the task reaped, since there's nobody left any more to synchronize the -reaping with. - -### User interface - -The api is declared at https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-threadpool.h - -#### Threadpool creation / destruction - -The threadpool should be created at program or vhost init using -`lws_threadpool_create()` and destroyed on exit or vhost destruction using -first `lws_threadpool_finish()` and then `lws_threadpool_destroy()`. - -Threadpools should be named, varargs are provided on the create function -to facilite eg, naming the threadpool by the vhost it's associated with. - -Threadpool creation takes an args struct with the following members: - -Member|function ----|--- -threads|The maxiumum number of independent threads in the pool -max_queue_depth|The maximum number of tasks allowed to wait for a place in the pool - -#### Task creation / destruction - -Tasks are created and queued using `lws_threadpool_enqueue()`, this takes an -args struct with the following members - -Member|function ----|--- -wsi|The wsi the task is initially associated with -user|An opaque user-private pointer used for communication with the lws service thread and private state / data -task|A pointer to the function that will run in the pool thread -cleanup|A pointer to a function that will clean up finished or stopped tasks (perhaps freeing user) - -Tasks also should have a name, the creation function again provides varargs -to simplify naming the task with string elements related to who started it -and why. - -#### The task function itself - -The task function receives the task user pointer and the task state. The -possible task states are - -State|Meaning ----|--- -LWS_TP_STATUS_QUEUED|Task is still waiting for a pool thread -LWS_TP_STATUS_RUNNING|Task is supposed to do its work -LWS_TP_STATUS_SYNCING|Task is blocked waiting for sync from lws service thread -LWS_TP_STATUS_STOPPING|Task has been asked to stop but didn't stop yet -LWS_TP_STATUS_FINISHED|Task has reported it has completed -LWS_TP_STATUS_STOPPED|Task has aborted - -The task function will only be told `LWS_TP_STATUS_RUNNING` or -`LWS_TP_STATUS_STOPPING` in its status argument... RUNNING means continue with the -user task and STOPPING means clean up and return `LWS_TP_RETURN_STOPPED`. - -If possible every 100ms or so the task should return `LWS_TP_RETURN_CHECKING_IN` -to allow lws to inform it reasonably quickly that it has been asked to stop -(eg, because the related wsi has closed), or if it can continue. If not -possible, it's okay but eg exiting the application may experience delays -until the running task finishes, and since the wsi may have gone, the work -is wasted. - -The task function may return one of - -Return|Meaning ----|--- -LWS_TP_RETURN_CHECKING_IN|Still wants to run, but confirming nobody asked him to stop. Will be called again immediately with `LWS_TP_STATUS_RUNNING` or `LWS_TP_STATUS_STOPPING` -LWS_TP_RETURN_SYNC|Task wants to trigger a WRITABLE callback and block until lws service thread restarts it with `lws_threadpool_task_sync()` -LWS_TP_RETURN_FINISHED|Task has finished, successfully as far as it goes -LWS_TP_RETURN_STOPPED|Task has finished, aborting in response to a request to stop - -The SYNC or CHECKING_IN return may also have a flag `LWS_TP_RETURN_FLAG_OUTLIVE` -applied to it, which indicates to threadpool that this task wishes to remain -unstopped after the wsi closes. This is useful in the case where the task -understands it will take a long time to complete, and wants to return a -complete status and maybe close the connection, perhaps with a token identifying -the task. The task can then be monitored separately by using the token. - -#### Synchronizing - -The task can choose to "SYNC" with the lws service thread, in other words -cause a WRITABLE callback on the associated wsi in the lws service thread -context and block itself until it hears back from there via -`lws_threadpool_task_sync()` to resume the task. - -This is typically used when, eg, the task has filled its buffer, or ringbuffer, -and needs to pause operations until what's done has been sent and some buffer -space is open again. - -In the WRITABLE callback, in lws service thread context, the buffer can be -sent with `lws_write()` and then `lws_threadpool_task_sync()` to allow the task -to fill another buffer and continue that way. - -If the WRITABLE callback determines that the task should stop, it can just call -`lws_threadpool_task_sync()` with the second argument as 1, to force the task -to stop immediately after it resumes. - -#### The cleanup function - -When a finished task is reaped, or a task that become detached from its initial -wsi completes or is stopped, it calls the `.cleanup` function defined in the -task creation args struct to free anything related to the user pointer. - -With threadpool, responsibility for freeing allocations used by the task belongs -strictly with the task, via the `.cleanup` function, once the task has been -enqueued. That's different from a typical non-threadpool protocol where the -wsi lifecycle controls deallocation. This reflects the fact that the task -may outlive the wsi. - -#### Protecting against WRITABLE and / or SYNC duplication - -Care should be taken than data prepared by the task thread in the user priv -memory should only be sent once. For example, after sending data from a user -priv buffer of a given length stored in the priv, zero down the length. - -Task execution and the SYNC writable callbacks are mutually exclusive, so there -is no danger of collision between the task thread and the lws service thread if -the reason for the callback is a SYNC operation from the task thread. - -### Thread overcommit - -If the tasks running on the threads are ultimately network-bound for all or some -of their processing (via the SYNC with the WRITEABLE callback), it's possible -to overcommit the number of threads in the pool compared to the number of -threads the processor has in hardware to get better occupancy in the CPU. diff -Nru libwebsockets-4.0.20/lib/misc/threadpool/threadpool.c libwebsockets-2.4.2/lib/misc/threadpool/threadpool.c --- libwebsockets-4.0.20/lib/misc/threadpool/threadpool.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/misc/threadpool/threadpool.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1019 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif - -#include - -#include "private-lib-core.h" - -#include -#include - -struct lws_threadpool; - -struct lws_threadpool_task { - struct lws_threadpool_task *task_queue_next; - - struct lws_threadpool *tp; - char name[32]; - struct lws_threadpool_task_args args; - - lws_usec_t created; - lws_usec_t acquired; - lws_usec_t done; - lws_usec_t entered_state; - - lws_usec_t acc_running; - lws_usec_t acc_syncing; - - pthread_cond_t wake_idle; - - enum lws_threadpool_task_status status; - - int late_sync_retries; - - char wanted_writeable_cb; - char outlive; -}; - -struct lws_pool { - struct lws_threadpool *tp; - pthread_t thread; - pthread_mutex_t lock; /* part of task wake_idle */ - struct lws_threadpool_task *task; - lws_usec_t acquired; - int worker_index; -}; - -struct lws_threadpool { - pthread_mutex_t lock; /* protects all pool lists */ - pthread_cond_t wake_idle; - struct lws_pool *pool_list; - - struct lws_context *context; - struct lws_threadpool *tp_list; /* context list of threadpools */ - - struct lws_threadpool_task *task_queue_head; - struct lws_threadpool_task *task_done_head; - - char name[32]; - - int threads_in_pool; - int queue_depth; - int done_queue_depth; - int max_queue_depth; - int running_tasks; - - unsigned int destroying:1; -}; - -static int -ms_delta(lws_usec_t now, lws_usec_t then) -{ - return (int)((now - then) / 1000); -} - -static void -us_accrue(lws_usec_t *acc, lws_usec_t then) -{ - lws_usec_t now = lws_now_usecs(); - - *acc += now - then; -} - -static int -pc_delta(lws_usec_t now, lws_usec_t then, lws_usec_t us) -{ - lws_usec_t delta = (now - then) + 1; - - return (int)((us * 100) / delta); -} - -static void -__lws_threadpool_task_dump(struct lws_threadpool_task *task, char *buf, int len) -{ - lws_usec_t now = lws_now_usecs(); - char *end = buf + len - 1; - int syncms = 0, runms = 0; - - if (!task->acquired) { - buf += lws_snprintf(buf, end - buf, - "task: %s, QUEUED queued: %dms", - task->name, ms_delta(now, task->created)); - - return; - } - - if (task->acc_running) - runms = task->acc_running; - - if (task->acc_syncing) - syncms = task->acc_syncing; - - if (!task->done) { - buf += lws_snprintf(buf, end - buf, - "task: %s, ONGOING state %d (%dms) alive: %dms " - "(queued %dms, acquired: %dms, " - "run: %d%%, sync: %d%%)", task->name, task->status, - ms_delta(now, task->entered_state), - ms_delta(now, task->created), - ms_delta(task->acquired, task->created), - ms_delta(now, task->acquired), - pc_delta(now, task->acquired, runms), - pc_delta(now, task->acquired, syncms)); - - return; - } - - lws_snprintf(buf, end - buf, - "task: %s, DONE state %d lived: %dms " - "(queued %dms, on thread: %dms, " - "ran: %d%%, synced: %d%%)", task->name, task->status, - ms_delta(task->done, task->created), - ms_delta(task->acquired, task->created), - ms_delta(task->done, task->acquired), - pc_delta(task->done, task->acquired, runms), - pc_delta(task->done, task->acquired, syncms)); -} - -void -lws_threadpool_dump(struct lws_threadpool *tp) -{ -#if defined(_DEBUG) - struct lws_threadpool_task **c; - char buf[160]; - int n, count; - - pthread_mutex_lock(&tp->lock); /* ======================== tpool lock */ - - lwsl_thread("%s: tp: %s, Queued: %d, Run: %d, Done: %d\n", __func__, - tp->name, tp->queue_depth, tp->running_tasks, - tp->done_queue_depth); - - count = 0; - c = &tp->task_queue_head; - while (*c) { - struct lws_threadpool_task *task = *c; - __lws_threadpool_task_dump(task, buf, sizeof(buf)); - lwsl_thread(" - %s\n", buf); - count++; - - c = &(*c)->task_queue_next; - } - - if (count != tp->queue_depth) - lwsl_err("%s: tp says queue depth %d, but actually %d\n", - __func__, tp->queue_depth, count); - - count = 0; - for (n = 0; n < tp->threads_in_pool; n++) { - struct lws_pool *pool = &tp->pool_list[n]; - struct lws_threadpool_task *task = pool->task; - - if (task) { - __lws_threadpool_task_dump(task, buf, sizeof(buf)); - lwsl_thread(" - worker %d: %s\n", n, buf); - count++; - } - } - - if (count != tp->running_tasks) - lwsl_err("%s: tp says %d running_tasks, but actually %d\n", - __func__, tp->running_tasks, count); - - count = 0; - c = &tp->task_done_head; - while (*c) { - struct lws_threadpool_task *task = *c; - __lws_threadpool_task_dump(task, buf, sizeof(buf)); - lwsl_thread(" - %s\n", buf); - count++; - - c = &(*c)->task_queue_next; - } - - if (count != tp->done_queue_depth) - lwsl_err("%s: tp says done_queue_depth %d, but actually %d\n", - __func__, tp->done_queue_depth, count); - - pthread_mutex_unlock(&tp->lock); /* --------------- tp unlock */ -#endif -} - -static void -state_transition(struct lws_threadpool_task *task, - enum lws_threadpool_task_status status) -{ - task->entered_state = lws_now_usecs(); - task->status = status; -} - -static void -lws_threadpool_task_cleanup_destroy(struct lws_threadpool_task *task) -{ - if (task->args.cleanup) - task->args.cleanup(task->args.wsi, task->args.user); - - if (task->args.wsi) - task->args.wsi->tp_task = NULL; - - lwsl_thread("%s: tp %p: cleaned finished task for wsi %p\n", - __func__, task->tp, task->args.wsi); - - lws_free(task); -} - -static void -__lws_threadpool_reap(struct lws_threadpool_task *task) -{ - struct lws_threadpool_task **c, *t = NULL; - struct lws_threadpool *tp = task->tp; - - /* remove the task from the done queue */ - - c = &tp->task_done_head; - - while (*c) { - if ((*c) == task) { - t = *c; - *c = t->task_queue_next; - t->task_queue_next = NULL; - tp->done_queue_depth--; - - lwsl_thread("%s: tp %s: reaped task wsi %p\n", __func__, - tp->name, task->args.wsi); - - break; - } - c = &(*c)->task_queue_next; - } - - if (!t) - lwsl_err("%s: task %p not in done queue\n", __func__, task); - - /* call the task's cleanup and delete the task itself */ - - lws_threadpool_task_cleanup_destroy(task); -} - -/* - * this gets called from each tsi service context after the service was - * cancelled... we need to ask for the writable callback from the matching - * tsi context for any wsis bound to a worked thread that need it - */ - -int -lws_threadpool_tsi_context(struct lws_context *context, int tsi) -{ - struct lws_threadpool_task **c, *task = NULL; - struct lws_threadpool *tp; - struct lws *wsi; - - lws_context_lock(context, __func__); - - tp = context->tp_list_head; - while (tp) { - int n; - - /* for the running (syncing...) tasks... */ - - for (n = 0; n < tp->threads_in_pool; n++) { - struct lws_pool *pool = &tp->pool_list[n]; - - task = pool->task; - if (!task) - continue; - - wsi = task->args.wsi; - if (!wsi || wsi->tsi != tsi || - !task->wanted_writeable_cb) - continue; - - task->wanted_writeable_cb = 0; - lws_memory_barrier(); - - /* - * finally... we can ask for the callback on - * writable from the correct service thread - * context - */ - - lws_callback_on_writable(wsi); - } - - /* for the done tasks... */ - - c = &tp->task_done_head; - - while (*c) { - task = *c; - wsi = task->args.wsi; - - if (wsi && wsi->tsi == tsi && - task->wanted_writeable_cb) { - - task->wanted_writeable_cb = 0; - lws_memory_barrier(); - - /* - * finally... we can ask for the callback on - * writable from the correct service thread - * context - */ - - lws_callback_on_writable(wsi); - } - - c = &task->task_queue_next; - } - - tp = tp->tp_list; - } - - lws_context_unlock(context); - - return 0; -} - -static int -lws_threadpool_worker_sync(struct lws_pool *pool, - struct lws_threadpool_task *task) -{ - enum lws_threadpool_task_status temp; - struct timespec abstime; - struct lws *wsi; - int tries = 15; - - /* block until writable acknowledges */ - lwsl_debug("%s: %p: LWS_TP_RETURN_SYNC in\n", __func__, task); - pthread_mutex_lock(&pool->lock); /* ======================= pool lock */ - - lwsl_info("%s: %s: task %p (%s): syncing with wsi %p\n", __func__, - pool->tp->name, task, task->name, task->args.wsi); - - temp = task->status; - state_transition(task, LWS_TP_STATUS_SYNCING); - while (tries--) { - wsi = task->args.wsi; - - /* - * if the wsi is no longer attached to this task, there is - * nothing we can sync to usefully. Since the work wants to - * sync, it means we should react to the situation by telling - * the task it can't continue usefully by stopping it. - */ - - if (!wsi) { - lwsl_thread("%s: %s: task %p (%s): No longer bound to any " - "wsi to sync to\n", __func__, pool->tp->name, - task, task->name); - - state_transition(task, LWS_TP_STATUS_STOPPING); - goto done; - } - - /* - * So tries times this is the maximum time between SYNC asking - * for a callback on writable and actually getting it we are - * willing to sit still for. - * - * If it is exceeded, we will stop the task. - */ - abstime.tv_sec = time(NULL) + 2; - abstime.tv_nsec = 0; - - task->wanted_writeable_cb = 1; - lws_memory_barrier(); - - /* - * This will cause lws_threadpool_tsi_context() to get called - * from each tsi service context, where we can safely ask for - * a callback on writeable on the wsi we are associated with. - */ - lws_cancel_service(lws_get_context(wsi)); - - /* - * so the danger here is that we asked for a writable callback - * on the wsi, but for whatever reason, we are never going to - * get one. To avoid deadlocking forever, we allow a set time - * for the sync to happen naturally, otherwise the cond wait - * times out and we stop the task. - */ - - if (pthread_cond_timedwait(&task->wake_idle, &pool->lock, - &abstime) == ETIMEDOUT) { - task->late_sync_retries++; - if (!tries) { - lwsl_err("%s: %s: task %p (%s): SYNC timed out " - "(associated wsi %p)\n", - __func__, pool->tp->name, task, - task->name, task->args.wsi); - - state_transition(task, LWS_TP_STATUS_STOPPING); - goto done; - } - - continue; - } else - break; - } - - if (task->status == LWS_TP_STATUS_SYNCING) - state_transition(task, temp); - - lwsl_debug("%s: %p: LWS_TP_RETURN_SYNC out\n", __func__, task); - -done: - pthread_mutex_unlock(&pool->lock); /* ----------------- - pool unlock */ - - return 0; -} - -static void * -lws_threadpool_worker(void *d) -{ - struct lws_threadpool_task **c, **c2, *task; - struct lws_pool *pool = d; - struct lws_threadpool *tp = pool->tp; - char buf[160]; - - while (!tp->destroying) { - - /* we have no running task... wait and get one from the queue */ - - pthread_mutex_lock(&tp->lock); /* =================== tp lock */ - - /* - * if there's no task already waiting in the queue, wait for - * the wake_idle condition to signal us that might have changed - */ - while (!tp->task_queue_head && !tp->destroying) - pthread_cond_wait(&tp->wake_idle, &tp->lock); - - if (tp->destroying) { - pthread_mutex_unlock(&tp->lock); /* ------ tp unlock */ - continue; - } - - c = &tp->task_queue_head; - c2 = NULL; - task = NULL; - pool->task = NULL; - - /* look at the queue tail */ - while (*c) { - c2 = c; - c = &(*c)->task_queue_next; - } - - /* is there a task at the queue tail? */ - if (c2 && *c2) { - pool->task = task = *c2; - task->acquired = pool->acquired = lws_now_usecs(); - /* remove it from the queue */ - *c2 = task->task_queue_next; - task->task_queue_next = NULL; - tp->queue_depth--; - /* mark it as running */ - state_transition(task, LWS_TP_STATUS_RUNNING); - } - - /* someone else got it first... wait and try again */ - if (!task) { - pthread_mutex_unlock(&tp->lock); /* ------ tp unlock */ - continue; - } - - task->wanted_writeable_cb = 0; - - /* we have acquired a new task */ - - __lws_threadpool_task_dump(task, buf, sizeof(buf)); - - lwsl_thread("%s: %s: worker %d ACQUIRING: %s\n", - __func__, tp->name, pool->worker_index, buf); - tp->running_tasks++; - - pthread_mutex_unlock(&tp->lock); /* --------------- tp unlock */ - - /* - * 1) The task can return with LWS_TP_RETURN_CHECKING_IN to - * "resurface" periodically, and get called again with - * cont = 1 immediately to indicate it is picking up where it - * left off if the task is not being "stopped". - * - * This allows long tasks to respond to requests to stop in - * a clean and opaque way. - * - * 2) The task can return with LWS_TP_RETURN_SYNC to register - * a "callback on writable" request on the service thread and - * block until it hears back from the WRITABLE handler. - * - * This allows the work on the thread to be synchronized to the - * previous work being dispatched cleanly. - * - * 3) The task can return with LWS_TP_RETURN_FINISHED to - * indicate its work is completed nicely. - * - * 4) The task can return with LWS_TP_RETURN_STOPPED to indicate - * it stopped and cleaned up after incomplete work. - */ - - do { - lws_usec_t then; - int n; - - if (tp->destroying || !task->args.wsi) { - lwsl_info("%s: stopping on wsi gone\n", __func__); - state_transition(task, LWS_TP_STATUS_STOPPING); - } - - then = lws_now_usecs(); - n = task->args.task(task->args.user, task->status); - lwsl_debug(" %d, status %d\n", n, task->status); - us_accrue(&task->acc_running, then); - if (n & LWS_TP_RETURN_FLAG_OUTLIVE) - task->outlive = 1; - switch (n & 7) { - case LWS_TP_RETURN_CHECKING_IN: - /* if not destroying the tp, continue */ - break; - case LWS_TP_RETURN_SYNC: - if (!task->args.wsi) { - lwsl_debug("%s: task that wants to " - "outlive lost wsi asked " - "to sync: bypassed\n", - __func__); - break; - } - /* block until writable acknowledges */ - then = lws_now_usecs(); - lws_threadpool_worker_sync(pool, task); - us_accrue(&task->acc_syncing, then); - break; - case LWS_TP_RETURN_FINISHED: - state_transition(task, LWS_TP_STATUS_FINISHED); - break; - case LWS_TP_RETURN_STOPPED: - state_transition(task, LWS_TP_STATUS_STOPPED); - break; - } - } while (task->status == LWS_TP_STATUS_RUNNING); - - pthread_mutex_lock(&tp->lock); /* =================== tp lock */ - - tp->running_tasks--; - - if (pool->task->status == LWS_TP_STATUS_STOPPING) - state_transition(task, LWS_TP_STATUS_STOPPED); - - /* move the task to the done queue */ - - pool->task->task_queue_next = tp->task_done_head; - tp->task_done_head = task; - tp->done_queue_depth++; - pool->task->done = lws_now_usecs(); - - if (!pool->task->args.wsi && - (pool->task->status == LWS_TP_STATUS_STOPPED || - pool->task->status == LWS_TP_STATUS_FINISHED)) { - - __lws_threadpool_task_dump(pool->task, buf, sizeof(buf)); - lwsl_thread("%s: %s: worker %d REAPING: %s\n", - __func__, tp->name, pool->worker_index, - buf); - - /* - * there is no longer any wsi attached, so nothing is - * going to take care of reaping us. So we must take - * care of it ourselves. - */ - __lws_threadpool_reap(pool->task); - } else { - - __lws_threadpool_task_dump(pool->task, buf, sizeof(buf)); - lwsl_thread("%s: %s: worker %d DONE: %s\n", - __func__, tp->name, pool->worker_index, - buf); - - /* signal the associated wsi to take a fresh look at - * task status */ - - if (pool->task->args.wsi) { - task->wanted_writeable_cb = 1; - - lws_cancel_service( - lws_get_context(pool->task->args.wsi)); - } - } - - pool->task = NULL; - pthread_mutex_unlock(&tp->lock); /* --------------- tp unlock */ - } - - /* threadpool is being destroyed */ - - pthread_exit(NULL); - - return NULL; -} - -struct lws_threadpool * -lws_threadpool_create(struct lws_context *context, - const struct lws_threadpool_create_args *args, - const char *format, ...) -{ - struct lws_threadpool *tp; - va_list ap; - int n; - - tp = lws_malloc(sizeof(*tp) + (sizeof(struct lws_pool) * args->threads), - "threadpool alloc"); - if (!tp) - return NULL; - - memset(tp, 0, sizeof(*tp) + (sizeof(struct lws_pool) * args->threads)); - tp->pool_list = (struct lws_pool *)(tp + 1); - tp->max_queue_depth = args->max_queue_depth; - - va_start(ap, format); - n = vsnprintf(tp->name, sizeof(tp->name) - 1, format, ap); - va_end(ap); - - lws_context_lock(context, __func__); - - tp->context = context; - tp->tp_list = context->tp_list_head; - context->tp_list_head = tp; - - lws_context_unlock(context); - - pthread_mutex_init(&tp->lock, NULL); - pthread_cond_init(&tp->wake_idle, NULL); - - for (n = 0; n < args->threads; n++) { -#if defined(LWS_HAS_PTHREAD_SETNAME_NP) - char name[16]; -#endif - tp->pool_list[n].tp = tp; - tp->pool_list[n].worker_index = n; - pthread_mutex_init(&tp->pool_list[n].lock, NULL); - if (pthread_create(&tp->pool_list[n].thread, NULL, - lws_threadpool_worker, &tp->pool_list[n])) { - lwsl_err("thread creation failed\n"); - } else { -#if defined(LWS_HAS_PTHREAD_SETNAME_NP) - lws_snprintf(name, sizeof(name), "%s-%d", tp->name, n); - pthread_setname_np(tp->pool_list[n].thread, name); -#endif - tp->threads_in_pool++; - } - } - - return tp; -} - -void -lws_threadpool_finish(struct lws_threadpool *tp) -{ - struct lws_threadpool_task **c, *task; - - pthread_mutex_lock(&tp->lock); /* ======================== tpool lock */ - - /* nothing new can start, running jobs will abort as STOPPED and the - * pool threads will exit ASAP (they are joined in destroy) */ - tp->destroying = 1; - - /* stop everyone in the pending queue and move to the done queue */ - - c = &tp->task_queue_head; - while (*c) { - task = *c; - *c = task->task_queue_next; - task->task_queue_next = tp->task_done_head; - tp->task_done_head = task; - state_transition(task, LWS_TP_STATUS_STOPPED); - tp->queue_depth--; - tp->done_queue_depth++; - task->done = lws_now_usecs(); - - c = &task->task_queue_next; - } - - pthread_mutex_unlock(&tp->lock); /* -------------------- tpool unlock */ - - pthread_cond_broadcast(&tp->wake_idle); -} - -void -lws_threadpool_destroy(struct lws_threadpool *tp) -{ - struct lws_threadpool_task *task, *next; - struct lws_threadpool **ptp; - void *retval; - int n; - - /* remove us from the context list of threadpools */ - - lws_context_lock(tp->context, __func__); - - ptp = &tp->context->tp_list_head; - while (*ptp) { - if (*ptp == tp) { - *ptp = tp->tp_list; - break; - } - ptp = &(*ptp)->tp_list; - } - - lws_context_unlock(tp->context); - - - pthread_mutex_lock(&tp->lock); /* ======================== tpool lock */ - - tp->destroying = 1; - pthread_cond_broadcast(&tp->wake_idle); - pthread_mutex_unlock(&tp->lock); /* -------------------- tpool unlock */ - - lws_threadpool_dump(tp); - - for (n = 0; n < tp->threads_in_pool; n++) { - task = tp->pool_list[n].task; - - /* he could be sitting waiting for SYNC */ - - if (task != NULL) - pthread_cond_broadcast(&task->wake_idle); - - pthread_join(tp->pool_list[n].thread, &retval); - pthread_mutex_destroy(&tp->pool_list[n].lock); - } - lwsl_info("%s: all threadpools exited\n", __func__); - - task = tp->task_done_head; - while (task) { - next = task->task_queue_next; - lws_threadpool_task_cleanup_destroy(task); - tp->done_queue_depth--; - task = next; - } - - pthread_mutex_destroy(&tp->lock); - - lws_free(tp); -} - -/* - * we want to stop and destroy the task and related priv. The wsi may no - * longer exist. - */ - -int -lws_threadpool_dequeue(struct lws *wsi) -{ - struct lws_threadpool *tp; - struct lws_threadpool_task **c, *task; - int n; - - task = wsi->tp_task; - if (!task) - return 0; - - tp = task->tp; - pthread_mutex_lock(&tp->lock); /* ======================== tpool lock */ - - if (task->outlive && !tp->destroying) { - - /* disconnect from wsi, and wsi from task */ - - wsi->tp_task = NULL; - task->args.wsi = NULL; - - goto bail; - } - - - c = &tp->task_queue_head; - - /* is he queued waiting for a chance to run? Mark him as stopped and - * move him on to the done queue */ - - while (*c) { - if ((*c) == task) { - *c = task->task_queue_next; - task->task_queue_next = tp->task_done_head; - tp->task_done_head = task; - state_transition(task, LWS_TP_STATUS_STOPPED); - tp->queue_depth--; - tp->done_queue_depth++; - task->done = lws_now_usecs(); - - lwsl_debug("%s: tp %p: removed queued task wsi %p\n", - __func__, tp, task->args.wsi); - - break; - } - c = &(*c)->task_queue_next; - } - - /* is he on the done queue? */ - - c = &tp->task_done_head; - while (*c) { - if ((*c) == task) { - *c = task->task_queue_next; - task->task_queue_next = NULL; - lws_threadpool_task_cleanup_destroy(task); - tp->done_queue_depth--; - goto bail; - } - c = &(*c)->task_queue_next; - } - - /* he's not in the queue... is he already running on a thread? */ - - for (n = 0; n < tp->threads_in_pool; n++) { - if (!tp->pool_list[n].task || tp->pool_list[n].task != task) - continue; - - /* - * ensure we don't collide with tests or changes in the - * worker thread - */ - pthread_mutex_lock(&tp->pool_list[n].lock); - - /* - * mark him as having been requested to stop... - * the caller will hear about it in his service thread - * context as a request to close - */ - state_transition(task, LWS_TP_STATUS_STOPPING); - - /* disconnect from wsi, and wsi from task */ - - task->args.wsi->tp_task = NULL; - task->args.wsi = NULL; - - pthread_mutex_unlock(&tp->pool_list[n].lock); - - lwsl_debug("%s: tp %p: request stop running task " - "for wsi %p\n", __func__, tp, task->args.wsi); - - break; - } - - if (n == tp->threads_in_pool) { - /* can't find it */ - lwsl_notice("%s: tp %p: no task for wsi %p, decoupling\n", - __func__, tp, task->args.wsi); - task->args.wsi->tp_task = NULL; - task->args.wsi = NULL; - } - -bail: - pthread_mutex_unlock(&tp->lock); /* -------------------- tpool unlock */ - - return 0; -} - -struct lws_threadpool_task * -lws_threadpool_enqueue(struct lws_threadpool *tp, - const struct lws_threadpool_task_args *args, - const char *format, ...) -{ - struct lws_threadpool_task *task = NULL; - va_list ap; - - if (tp->destroying) - return NULL; - - pthread_mutex_lock(&tp->lock); /* ======================== tpool lock */ - - /* - * if there's room on the queue, the job always goes on the queue - * first, then any free thread may pick it up after the wake_idle - */ - - if (tp->queue_depth == tp->max_queue_depth) { - lwsl_notice("%s: queue reached limit %d\n", __func__, - tp->max_queue_depth); - - goto bail; - } - - /* - * create the task object - */ - - task = lws_malloc(sizeof(*task), __func__); - if (!task) - goto bail; - - memset(task, 0, sizeof(*task)); - pthread_cond_init(&task->wake_idle, NULL); - task->args = *args; - task->tp = tp; - task->created = lws_now_usecs(); - - va_start(ap, format); - vsnprintf(task->name, sizeof(task->name) - 1, format, ap); - va_end(ap); - - /* - * add him on the tp task queue - */ - - task->task_queue_next = tp->task_queue_head; - state_transition(task, LWS_TP_STATUS_QUEUED); - tp->task_queue_head = task; - tp->queue_depth++; - - /* - * mark the wsi itself as depending on this tp (so wsi close for - * whatever reason can clean up) - */ - - args->wsi->tp_task = task; - - lwsl_thread("%s: tp %s: enqueued task %p (%s) for wsi %p, depth %d\n", - __func__, tp->name, task, task->name, args->wsi, - tp->queue_depth); - - /* alert any idle thread there's something new on the task list */ - - lws_memory_barrier(); - pthread_cond_signal(&tp->wake_idle); - -bail: - pthread_mutex_unlock(&tp->lock); /* -------------------- tpool unlock */ - - return task; -} - -/* this should be called from the service thread */ - -enum lws_threadpool_task_status -lws_threadpool_task_status_wsi(struct lws *wsi, - struct lws_threadpool_task **task, void **user) -{ - enum lws_threadpool_task_status status; - struct lws_threadpool *tp; - - *task = wsi->tp_task; - if (!*task) - return -1; - - tp = (*task)->tp; - *user = (*task)->args.user; - status = (*task)->status; - - if (status == LWS_TP_STATUS_FINISHED || - status == LWS_TP_STATUS_STOPPED) { - char buf[160]; - - pthread_mutex_lock(&tp->lock); /* ================ tpool lock */ - __lws_threadpool_task_dump(*task, buf, sizeof(buf)); - lwsl_thread("%s: %s: service thread REAPING: %s\n", - __func__, tp->name, buf); - __lws_threadpool_reap(*task); - lws_memory_barrier(); - pthread_mutex_unlock(&tp->lock); /* ------------ tpool unlock */ - } - - return status; -} - -void -lws_threadpool_task_sync(struct lws_threadpool_task *task, int stop) -{ - lwsl_debug("%s\n", __func__); - - if (stop) - state_transition(task, LWS_TP_STATUS_STOPPING); - - pthread_cond_signal(&task->wake_idle); -} diff -Nru libwebsockets-4.0.20/lib/output.c libwebsockets-2.4.2/lib/output.c --- libwebsockets-4.0.20/lib/output.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/output.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,883 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +static int +lws_0405_frame_mask_generate(struct lws *wsi) +{ +#if 0 + wsi->u.ws.mask[0] = 0; + wsi->u.ws.mask[1] = 0; + wsi->u.ws.mask[2] = 0; + wsi->u.ws.mask[3] = 0; +#else + int n; + /* fetch the per-frame nonce */ + + n = lws_get_random(lws_get_context(wsi), wsi->u.ws.mask, 4); + if (n != 4) { + lwsl_parser("Unable to read from random device %s %d\n", + SYSTEM_RANDOM_FILEPATH, n); + return 1; + } +#endif + /* start masking from first byte of masking key buffer */ + wsi->u.ws.mask_idx = 0; + + return 0; +} + +/* + * notice this returns number of bytes consumed, or -1 + */ +int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len) +{ + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + size_t real_len = len; + unsigned int n; + int m; + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1); + + if (!len) + return 0; + /* just ignore sends after we cleared the truncation buffer */ + if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE && + !wsi->trunc_len) + return len; + + if (wsi->trunc_len && (buf < wsi->trunc_alloc || + buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) { + char dump[20]; + strncpy(dump, (char *)buf, sizeof(dump) - 1); + dump[sizeof(dump) - 1] = '\0'; +#if defined(LWS_WITH_ESP8266) + lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n", + wsi, (unsigned long)len, dump); +#else + lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n" + " It's illegal to do an lws_write outside of\n" + " the writable callback: fix your code\n", + wsi, (unsigned long)len, dump); +#endif + assert(0); + + return -1; + } + + m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_DO_SEND, &buf, len); + if (m < 0) + return -1; + if (m) /* handled */ { + n = m; + goto handle_truncated_send; + } + + if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd)) + lwsl_warn("** error invalid sock but expected to send\n"); + + /* limit sending */ + if (wsi->protocol->tx_packet_size) + n = wsi->protocol->tx_packet_size; + else { + n = wsi->protocol->rx_buffer_size; + if (!n) + n = context->pt_serv_buf_size; + } + n += LWS_PRE + 4; + if (n > len) + n = len; +#if defined(LWS_WITH_ESP8266) + if (wsi->pending_send_completion) { + n = 0; + goto handle_truncated_send; + } +#endif + + /* nope, send it on the socket directly */ + lws_latency_pre(context, wsi); + n = lws_ssl_capable_write(wsi, buf, n); + lws_latency(context, wsi, "send lws_issue_raw", n, n == len); + + switch (n) { + case LWS_SSL_CAPABLE_ERROR: + /* we're going to close, let close know sends aren't possible */ + wsi->socket_is_permanently_unusable = 1; + return -1; + case LWS_SSL_CAPABLE_MORE_SERVICE: + /* nothing got sent, not fatal, retry the whole thing later */ + n = 0; + break; + } + +handle_truncated_send: + /* + * we were already handling a truncated send? + */ + if (wsi->trunc_len) { + lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len); + wsi->trunc_offset += n; + wsi->trunc_len -= n; + + if (!wsi->trunc_len) { + lwsl_info("***** %p partial send completed\n", wsi); + /* done with it, but don't free it */ + n = real_len; + if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) { + lwsl_info("***** %p signalling to close now\n", wsi); + return -1; /* retry closing now */ + } + } + /* always callback on writeable */ + lws_callback_on_writable(wsi); + + return n; + } + + if ((unsigned int)n == real_len) + /* what we just sent went out cleanly */ + return n; + + /* + * Newly truncated send. Buffer the remainder (it will get + * first priority next time the socket is writable) + */ + lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n, + (unsigned long)real_len); + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1); + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n); + + /* + * - if we still have a suitable malloc lying around, use it + * - or, if too small, reallocate it + * - or, if no buffer, create it + */ + if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) { + lws_free(wsi->trunc_alloc); + + wsi->trunc_alloc_len = real_len - n; + wsi->trunc_alloc = lws_malloc(real_len - n, "truncated send alloc"); + if (!wsi->trunc_alloc) { + lwsl_err("truncated send: unable to malloc %lu\n", + (unsigned long)(real_len - n)); + return -1; + } + } + wsi->trunc_offset = 0; + wsi->trunc_len = real_len - n; + memcpy(wsi->trunc_alloc, buf + n, real_len - n); + + /* since something buffered, force it to get another chance to send */ + lws_callback_on_writable(wsi); + + return real_len; +} + +LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len, + enum lws_write_protocol wp) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int masked7 = (wsi->mode == LWSCM_WS_CLIENT); + unsigned char is_masked_bit = 0; + unsigned char *dropmask = NULL; + struct lws_tokens eff_buf; + size_t orig_len = len; + int pre = 0, n; + + if (wsi->parent_carries_io) { + struct lws_write_passthru pas; + + pas.buf = buf; + pas.len = len; + pas.wp = wp; + pas.wsi = wsi; + + if (wsi->parent->protocol->callback(wsi->parent, + LWS_CALLBACK_CHILD_WRITE_VIA_PARENT, + wsi->parent->user_space, + (void *)&pas, 0)) + return 1; + + return len; + } + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1); + + if ((int)len < 0) { + lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__, + (int)len, (unsigned long)len); + return -1; + } + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_WRITE, len); + +#ifdef LWS_WITH_ACCESS_LOG + wsi->access_log.sent += len; +#endif + if (wsi->vhost) + wsi->vhost->conn_stats.tx += len; + + if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) { + /* remove us from the list */ + struct lws **w = &pt->tx_draining_ext_list; + + wsi->u.ws.tx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.tx_draining_ext_list; + break; + } + w = &((*w)->u.ws.tx_draining_ext_list); + } + wsi->u.ws.tx_draining_ext_list = NULL; + wp = (wsi->u.ws.tx_draining_stashed_wp & 0xc0) | + LWS_WRITE_CONTINUATION; + + lwsl_ext("FORCED draining wp to 0x%02X\n", wp); + } + + lws_restart_ws_ping_pong_timer(wsi); + + if ((wp & 0x1f) == LWS_WRITE_HTTP || + (wp & 0x1f) == LWS_WRITE_HTTP_FINAL || + (wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION || + (wp & 0x1f) == LWS_WRITE_HTTP_HEADERS) + goto send_raw; + + /* if not in a state to send stuff, then just send nothing */ + + if (wsi->state != LWSS_ESTABLISHED && + ((wsi->state != LWSS_RETURNED_CLOSE_ALREADY && + wsi->state != LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION && + wsi->state != LWSS_AWAITING_CLOSE_ACK) || + wp != LWS_WRITE_CLOSE)) { + lwsl_debug("binning\n"); + return 0; + } + + /* if we are continuing a frame that already had its header done */ + + if (wsi->u.ws.inside_frame) { + lwsl_debug("INSIDE FRAME\n"); + goto do_more_inside_frame; + } + + wsi->u.ws.clean_buffer = 1; + + /* + * give a chance to the extensions to modify payload + * the extension may decide to produce unlimited payload erratically + * (eg, compression extension), so we require only that if he produces + * something, it will be a complete fragment of the length known at + * the time (just the fragment length known), and if he has + * more we will come back next time he is writeable and allow him to + * produce more fragments until he's drained. + * + * This allows what is sent each time it is writeable to be limited to + * a size that can be sent without partial sends or blocking, allows + * interleaving of control frames and other connection service. + */ + eff_buf.token = (char *)buf; + eff_buf.token_len = len; + + switch ((int)wp) { + case LWS_WRITE_PING: + case LWS_WRITE_PONG: + case LWS_WRITE_CLOSE: + break; + default: + lwsl_debug("LWS_EXT_CB_PAYLOAD_TX\n"); + n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &eff_buf, wp); + if (n < 0) + return -1; + + if (n && eff_buf.token_len) { + lwsl_debug("drain len %d\n", (int)eff_buf.token_len); + /* extension requires further draining */ + wsi->u.ws.tx_draining_ext = 1; + wsi->u.ws.tx_draining_ext_list = pt->tx_draining_ext_list; + pt->tx_draining_ext_list = wsi; + /* we must come back to do more */ + lws_callback_on_writable(wsi); + /* + * keep a copy of the write type for the overall + * action that has provoked generation of these + * fragments, so the last guy can use its FIN state. + */ + wsi->u.ws.tx_draining_stashed_wp = wp; + /* this is definitely not actually the last fragment + * because the extension asserted he has more coming + * So make sure this intermediate one doesn't go out + * with a FIN. + */ + wp |= LWS_WRITE_NO_FIN; + } + + if (eff_buf.token_len && wsi->u.ws.stashed_write_pending) { + wsi->u.ws.stashed_write_pending = 0; + wp = (wp &0xc0) | (int)wsi->u.ws.stashed_write_type; + } + } + + /* + * an extension did something we need to keep... for example, if + * compression extension, it has already updated its state according + * to this being issued + */ + if ((char *)buf != eff_buf.token) { + /* + * ext might eat it, but not have anything to issue yet. + * In that case we have to follow his lead, but stash and + * replace the write type that was lost here the first time. + */ + if (len && !eff_buf.token_len) { + if (!wsi->u.ws.stashed_write_pending) + wsi->u.ws.stashed_write_type = (char)wp & 0x3f; + wsi->u.ws.stashed_write_pending = 1; + return len; + } + /* + * extension recreated it: + * need to buffer this if not all sent + */ + wsi->u.ws.clean_buffer = 0; + } + + buf = (unsigned char *)eff_buf.token; + len = eff_buf.token_len; + + if (!buf) { + lwsl_err("null buf (%d)\n", (int)len); + return -1; + } + + switch (wsi->ietf_spec_revision) { + case 13: + if (masked7) { + pre += 4; + dropmask = &buf[0 - pre]; + is_masked_bit = 0x80; + } + + switch (wp & 0xf) { + case LWS_WRITE_TEXT: + n = LWSWSOPC_TEXT_FRAME; + break; + case LWS_WRITE_BINARY: + n = LWSWSOPC_BINARY_FRAME; + break; + case LWS_WRITE_CONTINUATION: + n = LWSWSOPC_CONTINUATION; + break; + + case LWS_WRITE_CLOSE: + n = LWSWSOPC_CLOSE; + break; + case LWS_WRITE_PING: + n = LWSWSOPC_PING; + break; + case LWS_WRITE_PONG: + n = LWSWSOPC_PONG; + break; + default: + lwsl_warn("lws_write: unknown write opc / wp\n"); + return -1; + } + + if (!(wp & LWS_WRITE_NO_FIN)) + n |= 1 << 7; + + if (len < 126) { + pre += 2; + buf[-pre] = n; + buf[-pre + 1] = (unsigned char)(len | is_masked_bit); + } else { + if (len < 65536) { + pre += 4; + buf[-pre] = n; + buf[-pre + 1] = 126 | is_masked_bit; + buf[-pre + 2] = (unsigned char)(len >> 8); + buf[-pre + 3] = (unsigned char)len; + } else { + pre += 10; + buf[-pre] = n; + buf[-pre + 1] = 127 | is_masked_bit; +#if defined __LP64__ + buf[-pre + 2] = (len >> 56) & 0x7f; + buf[-pre + 3] = len >> 48; + buf[-pre + 4] = len >> 40; + buf[-pre + 5] = len >> 32; +#else + buf[-pre + 2] = 0; + buf[-pre + 3] = 0; + buf[-pre + 4] = 0; + buf[-pre + 5] = 0; +#endif + buf[-pre + 6] = (unsigned char)(len >> 24); + buf[-pre + 7] = (unsigned char)(len >> 16); + buf[-pre + 8] = (unsigned char)(len >> 8); + buf[-pre + 9] = (unsigned char)len; + } + } + break; + } + +do_more_inside_frame: + + /* + * Deal with masking if we are in client -> server direction and + * the wp demands it + */ + + if (masked7) { + if (!wsi->u.ws.inside_frame) + if (lws_0405_frame_mask_generate(wsi)) { + lwsl_err("frame mask generation failed\n"); + return -1; + } + + /* + * in v7, just mask the payload + */ + if (dropmask) { /* never set if already inside frame */ + for (n = 4; n < (int)len + 4; n++) + dropmask[n] = dropmask[n] ^ wsi->u.ws.mask[ + (wsi->u.ws.mask_idx++) & 3]; + + /* copy the frame nonce into place */ + memcpy(dropmask, wsi->u.ws.mask, 4); + } + } + +send_raw: + switch ((int)(wp & 0x1f)) { + case LWS_WRITE_CLOSE: +/* lwsl_hexdump(&buf[-pre], len); */ + case LWS_WRITE_HTTP: + case LWS_WRITE_HTTP_FINAL: + case LWS_WRITE_HTTP_HEADERS: + case LWS_WRITE_HTTP_HEADERS_CONTINUATION: + case LWS_WRITE_PONG: + case LWS_WRITE_PING: +#ifdef LWS_WITH_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) { + unsigned char flags = 0; + + n = LWS_H2_FRAME_TYPE_DATA; + if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS) { + n = LWS_H2_FRAME_TYPE_HEADERS; + if (!(wp & LWS_WRITE_NO_FIN)) + flags = LWS_H2_FLAG_END_HEADERS; + if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) { + flags |= LWS_H2_FLAG_END_STREAM; + wsi->u.h2.send_END_STREAM = 1; + } + } + + if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION) { + n = LWS_H2_FRAME_TYPE_CONTINUATION; + if (!(wp & LWS_WRITE_NO_FIN)) + flags = LWS_H2_FLAG_END_HEADERS; + if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) { + flags |= LWS_H2_FLAG_END_STREAM; + wsi->u.h2.send_END_STREAM = 1; + } + } + + if (((wp & 0x1f) == LWS_WRITE_HTTP || + (wp & 0x1f) == LWS_WRITE_HTTP_FINAL) && + wsi->u.http.tx_content_length) { + wsi->u.http.tx_content_remain -= len; + lwsl_info("%s: wsi %p: tx_content_remain = %llu\n", __func__, wsi, + (unsigned long long)wsi->u.http.tx_content_remain); + if (!wsi->u.http.tx_content_remain) { + lwsl_info("%s: selecting final write mode\n", __func__); + wp = LWS_WRITE_HTTP_FINAL; + } + } + + if ((wp & 0x1f) == LWS_WRITE_HTTP_FINAL || (wp & LWS_WRITE_H2_STREAM_END)) { + //lws_get_network_wsi(wsi)->u.h2.END_STREAM) { + lwsl_info("%s: setting END_STREAM\n", __func__); + flags |= LWS_H2_FLAG_END_STREAM; + wsi->u.h2.send_END_STREAM = 1; + } + + return lws_h2_frame_write(wsi, n, flags, + wsi->u.h2.my_sid, len, buf); + } +#endif + return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre); + default: + break; + } + + /* + * give any active extensions a chance to munge the buffer + * before send. We pass in a pointer to an lws_tokens struct + * prepared with the default buffer and content length that's in + * there. Rather than rewrite the default buffer, extensions + * that expect to grow the buffer can adapt .token to + * point to their own per-connection buffer in the extension + * user allocation. By default with no extensions or no + * extension callback handling, just the normal input buffer is + * used then so it is efficient. + * + * callback returns 1 in case it wants to spill more buffers + * + * This takes care of holding the buffer if send is incomplete, ie, + * if wsi->u.ws.clean_buffer is 0 (meaning an extension meddled with + * the buffer). If wsi->u.ws.clean_buffer is 1, it will instead + * return to the user code how much OF THE USER BUFFER was consumed. + */ + + n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre); + wsi->u.ws.inside_frame = 1; + if (n <= 0) + return n; + + if (n == (int)len + pre) { + /* everything in the buffer was handled (or rebuffered...) */ + wsi->u.ws.inside_frame = 0; + return orig_len; + } + + /* + * it is how many bytes of user buffer got sent... may be < orig_len + * in which case callback when writable has already been arranged + * and user code can call lws_write() again with the rest + * later. + */ + + return n - pre; +} + +LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_process_html_args args; + lws_filepos_t amount, poss; + unsigned char *p, *pstart; +#if defined(LWS_WITH_RANGES) + unsigned char finished = 0; +#endif + int n, m; + + lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream); + + while (!lws_send_pipe_choked(wsi)) { + + if (wsi->trunc_len) { + if (lws_issue_raw(wsi, wsi->trunc_alloc + + wsi->trunc_offset, + wsi->trunc_len) < 0) { + lwsl_info("%s: closing\n", __func__); + goto file_had_it; + } + continue; + } + + if (wsi->u.http.filepos == wsi->u.http.filelen) + goto all_sent; + + n = 0; + + pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH; + + p = pstart; + +#if defined(LWS_WITH_RANGES) + if (wsi->u.http.range.count_ranges && !wsi->u.http.range.inside) { + + lwsl_notice("%s: doing range start %llu\n", __func__, wsi->u.http.range.start); + + if ((long long)lws_vfs_file_seek_cur(wsi->u.http.fop_fd, + wsi->u.http.range.start - + wsi->u.http.filepos) < 0) + goto file_had_it; + + wsi->u.http.filepos = wsi->u.http.range.start; + + if (wsi->u.http.range.count_ranges > 1) { + n = lws_snprintf((char *)p, context->pt_serv_buf_size - LWS_H2_FRAME_HEADER_LENGTH, + "_lws\x0d\x0a" + "Content-Type: %s\x0d\x0a" + "Content-Range: bytes %llu-%llu/%llu\x0d\x0a" + "\x0d\x0a", + wsi->u.http.multipart_content_type, + wsi->u.http.range.start, + wsi->u.http.range.end, + wsi->u.http.range.extent); + p += n; + } + + wsi->u.http.range.budget = wsi->u.http.range.end - + wsi->u.http.range.start + 1; + wsi->u.http.range.inside = 1; + } +#endif + + poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH; + + if (poss > wsi->u.http.tx_content_remain) + poss = wsi->u.http.tx_content_remain; + + /* + * if there is a hint about how much we will do well to send at one time, + * restrict ourselves to only trying to send that. + */ + if (wsi->protocol->tx_packet_size && + poss > wsi->protocol->tx_packet_size) + poss = wsi->protocol->tx_packet_size; + +#if defined(LWS_WITH_HTTP2) + m = lws_h2_tx_cr_get(wsi); + if (!m) { + lwsl_info("%s: came here with no tx credit", __func__); + return 0; + } + if (m < poss) + poss = m; + /* + * consumption of the actual payload amount sent will be handled + * when the http2 data frame is sent + */ +#endif + +#if defined(LWS_WITH_RANGES) + if (wsi->u.http.range.count_ranges) { + if (wsi->u.http.range.count_ranges > 1) + poss -= 7; /* allow for final boundary */ + if (poss > wsi->u.http.range.budget) + poss = wsi->u.http.range.budget; + } +#endif + if (wsi->sending_chunked) { + /* we need to drop the chunk size in here */ + p += 10; + /* allow for the chunk to grow by 128 in translation */ + poss -= 10 + 128; + } + + if (lws_vfs_file_read(wsi->u.http.fop_fd, &amount, p, poss) < 0) + goto file_had_it; /* caller will close */ + + if (wsi->sending_chunked) + n = (int)amount; + else + n = (p - pstart) + (int)amount; + + lwsl_debug("%s: sending %d\n", __func__, n); + + if (n) { + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, + context->timeout_secs); + + if (wsi->sending_chunked) { + args.p = (char *)p; + args.len = n; + args.max_len = (unsigned int)poss + 128; + args.final = wsi->u.http.filepos + n == + wsi->u.http.filelen; + if (user_callback_handle_rxflow( + wsi->vhost->protocols[(int)wsi->protocol_interpret_idx].callback, wsi, + LWS_CALLBACK_PROCESS_HTML, + wsi->user_space, &args, 0) < 0) + goto file_had_it; + n = args.len; + p = (unsigned char *)args.p; + } else + p = pstart; + +#if defined(LWS_WITH_RANGES) + if (wsi->u.http.range.send_ctr + 1 == + wsi->u.http.range.count_ranges && // last range + wsi->u.http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart) + wsi->u.http.range.budget - amount == 0) {// final part + n += lws_snprintf((char *)pstart + n, 6, + "_lws\x0d\x0a"); // append trailing boundary + lwsl_debug("added trailing boundary\n"); + } +#endif + m = lws_write(wsi, p, n, + wsi->u.http.filepos == wsi->u.http.filelen ? + LWS_WRITE_HTTP_FINAL : + LWS_WRITE_HTTP + ); + if (m < 0) + goto file_had_it; + + wsi->u.http.filepos += amount; + +#if defined(LWS_WITH_RANGES) + if (wsi->u.http.range.count_ranges >= 1) { + wsi->u.http.range.budget -= amount; + if (wsi->u.http.range.budget == 0) { + lwsl_notice("range budget exhausted\n"); + wsi->u.http.range.inside = 0; + wsi->u.http.range.send_ctr++; + + if (lws_ranges_next(&wsi->u.http.range) < 1) { + finished = 1; + goto all_sent; + } + } + } +#endif + + if (m != n) { + /* adjust for what was not sent */ + if (lws_vfs_file_seek_cur(wsi->u.http.fop_fd, + m - n) == + (unsigned long)-1) + goto file_had_it; + } + } + +all_sent: + if ((!wsi->trunc_len && wsi->u.http.filepos >= wsi->u.http.filelen) +#if defined(LWS_WITH_RANGES) + || finished) +#else + ) +#endif + { + wsi->state = LWSS_HTTP; + /* we might be in keepalive, so close it off here */ + lws_vfs_file_close(&wsi->u.http.fop_fd); + + lwsl_debug("file completed\n"); + + if (wsi->protocol->callback && + user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION, + wsi->user_space, NULL, + 0) < 0) { + /* + * For http/1.x, the choices from + * transaction_completed are either + * 0 to use the connection for pipelined + * or nonzero to hang it up. + * + * However for http/2. while we are + * still interested in hanging up the + * nwsi if there was a network-level + * fatal error, simply completing the + * transaction is a matter of the stream + * state, not the root connection at the + * network level + */ + if (wsi->http2_substream) + return 1; + else + return -1; + } + + return 1; /* >0 indicates completed */ + } + } + + lws_callback_on_writable(wsi); + + return 0; /* indicates further processing must be done */ + +file_had_it: + lws_vfs_file_close(&wsi->u.http.fop_fd); + + return -1; +} + +#if LWS_POSIX +LWS_VISIBLE int +lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int n; + + lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1); + + n = recv(wsi->desc.sockfd, (char *)buf, len, 0); + if (n >= 0) { + if (wsi->vhost) + wsi->vhost->conn_stats.rx += n; + lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n); + lws_restart_ws_ping_pong_timer(wsi); + return n; + } +#if LWS_POSIX + if (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK || + LWS_ERRNO == LWS_EINTR) + return LWS_SSL_CAPABLE_MORE_SERVICE; +#endif + lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO); + return LWS_SSL_CAPABLE_ERROR; +} + +LWS_VISIBLE int +lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len) +{ + int n = 0; + +#if LWS_POSIX + n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL); +// lwsl_info("%s: sent len %d result %d", __func__, len, n); + if (n >= 0) + return n; + + if (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK || + LWS_ERRNO == LWS_EINTR) { + if (LWS_ERRNO == LWS_EWOULDBLOCK) { + lws_set_blocking_send(wsi); + } + + return LWS_SSL_CAPABLE_MORE_SERVICE; + } +#else + (void)n; + (void)wsi; + (void)buf; + (void)len; + // !!! +#endif + + lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n", + len, wsi->desc.sockfd, n, LWS_ERRNO); + return LWS_SSL_CAPABLE_ERROR; +} +#endif +LWS_VISIBLE int +lws_ssl_pending_no_ssl(struct lws *wsi) +{ + (void)wsi; +#if defined(LWS_WITH_ESP32) + return 100; +#else + return 0; +#endif +} diff -Nru libwebsockets-4.0.20/lib/plat/freertos/esp32/esp32-helpers.c libwebsockets-2.4.2/lib/plat/freertos/esp32/esp32-helpers.c --- libwebsockets-4.0.20/lib/plat/freertos/esp32/esp32-helpers.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/freertos/esp32/esp32-helpers.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1373 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "romfs.h" -#include -#include -#include -#include -#include "soc/ledc_reg.h" -#include "driver/ledc.h" - -struct lws_esp32 lws_esp32 = { - .model = CONFIG_LWS_MODEL_NAME, - .serial = "unknown", -}; - -/* - * Group AP / Station State - */ - -enum lws_gapss { - LWS_GAPSS_INITIAL, /* just started up, init and move to - * LWS_GAPSS_SCAN */ - LWS_GAPSS_SCAN, /* - * Unconnected, scanning: AP known in one of the - * config slots -> configure it, start timeout + - * LWS_GAPSS_STAT, if no AP already up in same - * group with lower MAC, after a random period - * start up our AP (LWS_GAPSS_AP) - */ - LWS_GAPSS_AP, /* - * Trying to be the group AP... periodically do - * a scan LWS_GAPSS_AP_SCAN, faster and then - * slower - */ - LWS_GAPSS_AP_SCAN, /* - * doing a scan while trying to be the group - * AP... if we see a lower MAC being the AP for - * the same group AP, abandon being an AP and - * join that AP as a station - */ - LWS_GAPSS_STAT_GRP_AP, /* - * We have decided to join another group member - * who is being the AP, as its MAC is lower than - * ours. This is a stable state, but we still - * do periodic scans LWS_GAPSS_STAT_GRP_AP_SCAN - * and will always prefer an AP configured in a - * slot. - */ - LWS_GAPSS_STAT_GRP_AP_SCAN, - /* - * We have joined a group member who is doing - * the AP job... we want to check every now and - * then if a configured AP has appeared that we - * should better use instead. Otherwise stay in - * LWS_GAPSS_STAT_GRP_AP - */ - LWS_GAPSS_STAT, /* - * trying to connect to another non-group AP. - * If we don't get an IP within a timeout and - * retries, blacklist it and go back - */ - LWS_GAPSS_STAT_HAPPY, -}; - -static const char *gapss_str[] = { - "LWS_GAPSS_INITIAL", - "LWS_GAPSS_SCAN", - "LWS_GAPSS_AP", - "LWS_GAPSS_AP_SCAN", - "LWS_GAPSS_STAT_GRP_AP", - "LWS_GAPSS_STAT_GRP_AP_SCAN", - "LWS_GAPSS_STAT", - "LWS_GAPSS_STAT_HAPPY", -}; - -static romfs_t lws_esp32_romfs; -static TimerHandle_t leds_timer, scan_timer, debounce_timer, association_timer -#if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) -, mdns_timer -#endif -; -static enum lws_gapss gapss = LWS_GAPSS_INITIAL; -#if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) -static mdns_result_t *mdns_results_head; -#endif - -#define GPIO_SW 14 - -struct esp32_file { - const struct inode *i; -}; - -static void lws_gapss_to(enum lws_gapss to) -{ - lwsl_notice("gapss from %s to %s\n", gapss_str[gapss], gapss_str[to]); - gapss = to; -} - -uint32_t lws_esp32_get_reboot_type(void) -{ - uint32_t *p = (uint32_t *)LWS_MAGIC_REBOOT_TYPE_ADS, val = *p; - nvs_handle nvh; - size_t s = 0; - int n = 0; - - ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh)); - if (nvs_get_blob(nvh, "ssl-pub.pem", NULL, &s) == ESP_OK) - n = 1; - if (nvs_get_blob(nvh, "ssl-pri.pem", NULL, &s) == ESP_OK) - n |= 2; - nvs_close(nvh); - - /* - * in the case the SSL certs are not there, don't require - * the button to be down to access all features. - */ - if (n != 3) - val = LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON; - - return val; -} - -static void render_ip(char *dest, int len, uint8_t *ip) -{ - snprintf(dest, len, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); -} - -void lws_esp32_restart_guided(uint32_t type) -{ - uint32_t *p_force_factory_magic = (uint32_t *)LWS_MAGIC_REBOOT_TYPE_ADS; - - lwsl_notice("%s: %x\n", __func__, type); - *p_force_factory_magic = type; - - esp_restart(); -} - -/* - * esp-idf goes crazy with zero length str nvs. Use this as a workaround - * to delete the key in that case. - */ - -esp_err_t lws_nvs_set_str(nvs_handle handle, const char* key, const char* value) -{ - if (*value) - return nvs_set_str(handle, key, value); - - return nvs_erase_key(handle, key); -} - -static wifi_scan_config_t scan_config = { - .ssid = 0, - .bssid = 0, - .channel = 0, - .show_hidden = true -}; - -static char scan_ongoing = 0, scan_timer_exists = 0; -static int try_slot = -1; - -static wifi_config_t config = { - .ap = { - .channel = 6, - .authmode = WIFI_AUTH_OPEN, - .max_connection = 1, - } }, sta_config = { - .sta = { - .bssid_set = 0, - } }; - -static void lws_esp32_scan_timer_cb(TimerHandle_t th) -{ - int n; - - lwsl_notice("%s\n", __func__); - scan_ongoing = 0; - n = esp_wifi_scan_start(&scan_config, false); - if (n != ESP_OK) - lwsl_err("scan start failed %d\n", n); -} - -static void lws_esp32_assoc_timer_cb(TimerHandle_t th) -{ - int n; - - xTimerStop(association_timer, 0); - - if (gapss == LWS_GAPSS_STAT_HAPPY) { - lwsl_debug("%s: saw we were happy\n", __func__); - - return; - } - - lwsl_notice("%s: forcing rescan\n", __func__); - - lws_gapss_to(LWS_GAPSS_SCAN); - scan_ongoing = 0; - n = esp_wifi_scan_start(&scan_config, false); - if (n != ESP_OK) - lwsl_err("scan start failed %d\n", n); -} - - -#if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) - -void __attribute__(( weak )) -lws_group_member_event(int e, void *p) -{ -} - -void __attribute__(( weak )) -lws_get_iframe_size(int *w, int *h) -{ - *w = 320; - *h = 160; -} - -void lws_group_member_event_call(int e, void *p) -{ - lws_group_member_event(e, p); -} - -static int -get_txt_param(const mdns_result_t *mr, const char *param, char *result, int len) -{ - const char *p; - - *result = '\0'; - - p = strstr(mr->txt->key, param); - if (!p) { - *result = '\0'; - return 1; - } - - lws_strncpy(result, mr->txt->value, len); - - return 0; -} - -static void lws_esp32_mdns_timer_cb(TimerHandle_t th) -{ - uint64_t now = lws_now_usecs(); - struct lws_group_member *p, **p1; - const mdns_result_t *r = mdns_results_head; - - while (r) { - char ch = 0, group[16]; - - get_txt_param(r, "group", group, sizeof(group)); - if (strcmp(group, lws_esp32.group)) /* not our group */ { - lwsl_notice("group %s vs %s %s\n", - group, lws_esp32.group, r->txt->value); - continue; - } - - p = lws_esp32.first; - while (p) { - if (strcmp(r->hostname, p->host)) - goto next; - if (memcmp(&r->addr, &p->addr, sizeof(r->addr))) - goto next; - - p->last_seen = now; - break; -next: - p = p->next; - } - if (!p) { /* did not find */ - char temp[8]; - - p = lws_malloc(sizeof(*p), "group"); - if (!p) - continue; - lws_strncpy(p->host, r->hostname, sizeof(p->host)); - - get_txt_param(r, "model", p->model, sizeof(p->model)); - get_txt_param(r, "role", p->role, sizeof(p->role)); - get_txt_param(r, "mac", p->mac, sizeof(p->mac)); - get_txt_param(r, "width", temp, sizeof(temp)); - p->width = atoi(temp); - get_txt_param(r, "height", temp, sizeof(temp)); - p->height = atoi(temp); - - memcpy(&p->addr, &r->addr, sizeof(p->addr)); -// memcpy(&p->addrv6, &r->addrv6, sizeof(p->addrv6)); - p->last_seen = now; - p->flags = 0; - p->next = lws_esp32.first; - lws_esp32.first = p; - lws_esp32.extant_group_members++; - - lws_group_member_event_call(LWS_SYSTEM_GROUP_MEMBER_ADD, p); - } else { - if (memcmp(&p->addr, &r->addr, sizeof(p->addr))) { - memcpy(&p->addr, &r->addr, sizeof(p->addr)); - ch = 1; - } -/* if (memcmp(&p->addrv6, &r->addrv6, sizeof(p->addrv6))) { - memcpy(&p->addrv6, &r->addrv6, sizeof(p->addrv6)); - ch = 1; - } */ - if (ch) - lws_group_member_event_call(LWS_SYSTEM_GROUP_MEMBER_CHANGE, p); - } - } - - mdns_query_results_free(mdns_results_head); - - /* garbage-collect group members not seen for too long */ - p1 = &lws_esp32.first; - while (*p1) { - p = *p1; - if (!(p->flags & LWS_GROUP_FLAG_SELF) && - now - p->last_seen > 60000000) { - lws_esp32.extant_group_members--; - *p1 = p->next; - - lws_group_member_event_call(LWS_SYSTEM_GROUP_MEMBER_REMOVE, p); - lws_free(p); - continue; - } - p1 = &(*p1)->next; - } - - mdns_query_txt(lws_esp32.group, "_lwsgrmem", "_tcp", 0, - &mdns_results_head); - xTimerStart(mdns_timer, 0); -} -#endif - -void __attribute__(( weak )) -lws_esp32_button(int down) -{ -} - -void IRAM_ATTR -gpio_irq(void *arg) -{ - gpio_set_intr_type(GPIO_SW, GPIO_INTR_DISABLE); - xTimerStart(debounce_timer, 0); -} - -static void lws_esp32_debounce_timer_cb(TimerHandle_t th) -{ - if (lws_esp32.button_is_down) - gpio_set_intr_type(GPIO_SW, GPIO_INTR_POSEDGE); - else - gpio_set_intr_type(GPIO_SW, GPIO_INTR_NEGEDGE); - - lws_esp32.button_is_down = gpio_get_level(GPIO_SW); - - lws_esp32_button(lws_esp32.button_is_down); -} - - -static int -start_scan() -{ - /* if no APs configured, no point... */ - - if (!lws_esp32.ssid[0][0] && - !lws_esp32.ssid[1][0] && - !lws_esp32.ssid[2][0] && - !lws_esp32.ssid[3][0]) - return 0; - - if (scan_timer_exists && !scan_ongoing) { - // lwsl_notice("Starting scan timer...\n"); - scan_ongoing = 1; - xTimerStart(scan_timer, 0); - } - - return 0; -} - - - -static void -end_scan() -{ - wifi_ap_record_t ap_records[10]; - uint16_t count_ap_records; - int n, m; - - count_ap_records = LWS_ARRAY_SIZE(ap_records); - if (esp_wifi_scan_get_ap_records(&count_ap_records, ap_records)) { - lwsl_err("%s: failed\n", __func__); - return; - } - - if (!count_ap_records) - goto passthru; - - if (gapss != LWS_GAPSS_SCAN) { - lwsl_info("ignoring scan as gapss %s\n", gapss_str[gapss]); - goto passthru; - } - - /* no point if no APs set up */ - if (!lws_esp32.ssid[0][0] && - !lws_esp32.ssid[1][0] && - !lws_esp32.ssid[2][0] && - !lws_esp32.ssid[3][0]) - goto passthru; - - lwsl_info("checking %d scan records\n", count_ap_records); - - for (n = 0; n < 4; n++) { - - if (!lws_esp32.ssid[(n + try_slot + 1) & 3][0]) - continue; - - lwsl_debug("looking for %s\n", - lws_esp32.ssid[(n + try_slot + 1) & 3]); - - /* this ssid appears in scan results? */ - - for (m = 0; m < count_ap_records; m++) { - // lwsl_notice(" %s\n", ap_records[m].ssid); - if (!strcmp((char *)ap_records[m].ssid, - lws_esp32.ssid[(n + try_slot + 1) & 3])) - goto hit; - } - - continue; - -hit: - m = (n + try_slot + 1) & 3; - try_slot = m; - lwsl_info("Attempting connection with slot %d: %s:\n", m, - lws_esp32.ssid[m]); - /* set the ssid we last tried to connect to */ - lws_strncpy(lws_esp32.active_ssid, lws_esp32.ssid[m], - sizeof(lws_esp32.active_ssid)); - - lws_strncpy((char *)sta_config.sta.ssid, lws_esp32.ssid[m], - sizeof(sta_config.sta.ssid)); - lws_strncpy((char *)sta_config.sta.password, lws_esp32.password[m], - sizeof(sta_config.sta.password)); - - tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, - (const char *)&config.ap.ssid[7]); - lws_gapss_to(LWS_GAPSS_STAT); - xTimerStop(association_timer, 0); - xTimerStart(association_timer, 0); - - esp_wifi_set_config(WIFI_IF_STA, &sta_config); - esp_wifi_connect(); - break; - } - - if (n == 4) - start_scan(); - -passthru: - if (lws_esp32.scan_consumer) - lws_esp32.scan_consumer(count_ap_records, ap_records, - lws_esp32.scan_consumer_arg); - -} - -static void -lws_set_genled(int n) -{ - lws_esp32.genled_t = lws_now_usecs(); - lws_esp32.genled = n; -} - -int -lws_esp32_leds_network_indication(void) -{ - uint64_t us, r; - int n, fadein = 100, speed = 1199, div = 1, base = 0; - - r = lws_now_usecs(); - us = r - lws_esp32.genled_t; - - switch (lws_esp32.genled) { - case LWSESP32_GENLED__INIT: - lws_esp32.genled = LWSESP32_GENLED__LOST_NETWORK; - /* fallthru */ - case LWSESP32_GENLED__LOST_NETWORK: - fadein = us / 10000; /* 100 steps in 1s */ - if (fadein > 100) { - fadein = 100; - lws_esp32.genled = LWSESP32_GENLED__NO_NETWORK; - } - /* fallthru */ - case LWSESP32_GENLED__NO_NETWORK: - break; - case LWSESP32_GENLED__CONN_AP: - base = 4096; - speed = 933; - div = 2; - break; - case LWSESP32_GENLED__GOT_IP: - fadein = us / 10000; /* 100 steps in 1s */ - if (fadein > 100) { - fadein = 100; - lws_esp32.genled = LWSESP32_GENLED__OK; - } - fadein = 100 - fadein; /* we are fading out */ - /* fallthru */ - case LWSESP32_GENLED__OK: - if (lws_esp32.genled == LWSESP32_GENLED__OK) - return 0; - - base = 4096; - speed = 766; - div = 3; - break; - } - - n = base + (lws_esp32_sine_interp(r / speed) / div); - return (n * fadein) / 100; -} - -esp_err_t lws_esp32_event_passthru(void *ctx, system_event_t *event) -{ -#if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) - struct lws_group_member *mem; - int n; -#endif - nvs_handle nvh; - uint32_t use; - - switch((int)event->event_id) { - case SYSTEM_EVENT_STA_START: - //esp_wifi_connect(); -// break; - /* fallthru */ - case SYSTEM_EVENT_STA_DISCONNECTED: - lwsl_notice("SYSTEM_EVENT_STA_DISCONNECTED\n"); - if (sntp_enabled()) - sntp_stop(); - lws_esp32.conn_ap = 0; - lws_esp32.inet = 0; - lws_esp32.sta_ip[0] = '\0'; - lws_esp32.sta_mask[0] = '\0'; - lws_esp32.sta_gw[0] = '\0'; - lws_gapss_to(LWS_GAPSS_SCAN); - mdns_free(); - lws_set_genled(LWSESP32_GENLED__LOST_NETWORK); - start_scan(); - esp_wifi_connect(); - break; - - case SYSTEM_EVENT_STA_CONNECTED: - lws_esp32.conn_ap = 1; - lws_set_genled(LWSESP32_GENLED__CONN_AP); - break; - - case SYSTEM_EVENT_STA_GOT_IP: - lwsl_notice("SYSTEM_EVENT_STA_GOT_IP\n"); - - lws_esp32.inet = 1; - lws_set_genled(LWSESP32_GENLED__GOT_IP); - - render_ip(lws_esp32.sta_ip, sizeof(lws_esp32.sta_ip) - 1, - (uint8_t *)&event->event_info.got_ip.ip_info.ip); - render_ip(lws_esp32.sta_mask, sizeof(lws_esp32.sta_mask) - 1, - (uint8_t *)&event->event_info.got_ip.ip_info.netmask); - render_ip(lws_esp32.sta_gw, sizeof(lws_esp32.sta_gw) - 1, - (uint8_t *)&event->event_info.got_ip.ip_info.gw); - - if (!nvs_open("lws-station", NVS_READWRITE, &nvh)) { - char slot[8]; - - lws_snprintf(slot, sizeof(slot) - 1, "%duse", try_slot); - use = 0; - nvs_get_u32(nvh, slot, &use); - nvs_set_u32(nvh, slot, use + 1); - nvs_commit(nvh); - nvs_close(nvh); - } - - lws_gapss_to(LWS_GAPSS_STAT_HAPPY); - -#if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) - n = mdns_init(); - if (!n) { - static mdns_txt_item_t txta[6]; - static char wh[2][6]; - int w, h; - - mdns_hostname_set(lws_esp32.hostname); - mdns_instance_name_set(lws_esp32.group); - - lws_get_iframe_size(&w, &h); - - txta[0].key = "model"; - txta[1].key = "group"; - txta[2].key = "role"; - txta[3].key = "mac"; - txta[4].key = "width"; - txta[5].key = "height"; - - txta[0].value = lws_esp32.model; - txta[1].value = lws_esp32.group; - txta[2].value = lws_esp32.role; - txta[3].value = lws_esp32.mac; - txta[4].value = wh[0]; - txta[5].value = wh[1]; - - lws_snprintf(wh[0], 6, "%d", w); - lws_snprintf(wh[1], 6, "%d", h); - - mdns_service_add(lws_esp32.group, - "_lwsgrmem", "_tcp", 443, txta, - LWS_ARRAY_SIZE(txta)); - - mem = lws_esp32.first; - while (mem) { - if (mem->flags & 1) - break; - mem = mem->next; - } - - if (!mem) { - struct lws_group_member *mem = - lws_malloc(sizeof(*mem), "group"); - if (mem) { - mem->last_seen = ~(uint64_t)0; - strcpy(mem->model, lws_esp32.model); - strcpy(mem->role, lws_esp32.role); - strcpy(mem->host, lws_esp32.hostname); - strcpy(mem->mac, lws_esp32.mac); - mem->flags = LWS_GROUP_FLAG_SELF; - lws_get_iframe_size(&mem->width, - &mem->height); - memcpy(&mem->addr, - &event->event_info.got_ip.ip_info.ip, - sizeof(mem->addr)); - memcpy(&mem->addrv6, - &event->event_info.got_ip6.ip6_info.ip, - sizeof(mem->addrv6)); - mem->next = lws_esp32.first; - lws_esp32.first = mem; - lws_esp32.extant_group_members++; - - lws_group_member_event_call( - LWS_SYSTEM_GROUP_MEMBER_ADD, mem); - } - } else { /* update our IP */ - memcpy(&mem->addr, - &event->event_info.got_ip.ip_info.ip, - sizeof(mem->addr)); - memcpy(&mem->addrv6, - &event->event_info.got_ip6.ip6_info.ip, - sizeof(mem->addrv6)); - lws_group_member_event_call( - LWS_SYSTEM_GROUP_MEMBER_CHANGE, mem); - } - - } else - lwsl_err("unable to init mdns on STA: %d\n", n); - - mdns_query_txt(lws_esp32.group, "_lwsgrmem", "_tcp", 0, - &mdns_results_head); - xTimerStart(mdns_timer, 0); -#endif - - lwsl_notice(" --- Got IP %s\n", lws_esp32.sta_ip); - if (!sntp_enabled()) { - sntp_setoperatingmode(SNTP_OPMODE_POLL); - sntp_setservername(0, "pool.ntp.org"); - sntp_init(); - } - break; - - case SYSTEM_EVENT_SCAN_DONE: - lwsl_notice("SYSTEM_EVENT_SCAN_DONE\n"); - end_scan(); - break; - - default: - break; - } - - return ESP_OK; -} - -#if defined(LWS_WITH_FILE_OPS) -static lws_fop_fd_t IRAM_ATTR -esp32_lws_fops_open(const struct lws_plat_file_ops *fops, const char *filename, - const char *vfs_path, lws_fop_flags_t *flags) -{ - struct esp32_file *f = malloc(sizeof(*f)); - lws_fop_fd_t fop_fd; - size_t len, csum; - - lwsl_notice("%s: %s\n", __func__, filename); - - if (!f) - return NULL; - f->i = romfs_get_info(lws_esp32_romfs, filename, &len, &csum); - if (!f->i) - goto bail; - - fop_fd = malloc(sizeof(*fop_fd)); - if (!fop_fd) - goto bail; - - fop_fd->fops = fops; - fop_fd->filesystem_priv = f; - fop_fd->mod_time = csum; - *flags |= LWS_FOP_FLAG_MOD_TIME_VALID; - fop_fd->flags = *flags; - - fop_fd->len = len; - fop_fd->pos = 0; - - return fop_fd; - -bail: - free(f); - - return NULL; -} - -static int IRAM_ATTR -esp32_lws_fops_close(lws_fop_fd_t *fop_fd) -{ - free((*fop_fd)->filesystem_priv); - free(*fop_fd); - - *fop_fd = NULL; - - return 0; -} -static lws_fileofs_t IRAM_ATTR -esp32_lws_fops_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset_from_cur_pos) -{ - fop_fd->pos += offset_from_cur_pos; - - if (fop_fd->pos > fop_fd->len) - fop_fd->pos = fop_fd->len; - - return 0; -} - -static int IRAM_ATTR -esp32_lws_fops_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, uint8_t *buf, - lws_filepos_t len) -{ - struct esp32_file *f = fop_fd->filesystem_priv; -#if 0 - if ((long)buf & 3) { - lwsl_err("misaligned buf\n"); - - return -1; - } -#endif - if (fop_fd->pos >= fop_fd->len) - return 0; - - if (len > fop_fd->len - fop_fd->pos) - len = fop_fd->len - fop_fd->pos; - - spi_flash_read((uint32_t)(char *)f->i + fop_fd->pos, buf, len); - - *amount = len; - fop_fd->pos += len; - - return 0; -} - -static const struct lws_plat_file_ops fops = { - .next = &fops_zip, - .LWS_FOP_OPEN = esp32_lws_fops_open, - .LWS_FOP_CLOSE = esp32_lws_fops_close, - .LWS_FOP_READ = esp32_lws_fops_read, - .LWS_FOP_SEEK_CUR = esp32_lws_fops_seek_cur, -}; -#endif - -int -lws_esp32_wlan_nvs_get(int retry) -{ - nvs_handle nvh; - char lws_esp32_force_ap = 0, slot[12]; - size_t s; - uint8_t mac[6]; - int n; - - esp_efuse_mac_get_default(mac); - mac[5] |= 1; /* match the AP MAC */ - snprintf(lws_esp32.serial, sizeof(lws_esp32.serial) - 1, - "%02X%02X%02X", mac[3], mac[4], mac[5]); - snprintf(lws_esp32.mac, sizeof(lws_esp32.mac) - 1, - "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], - mac[4], mac[5]); - - ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh)); - - config.sta.ssid[0] = '\0'; - config.sta.password[0] = '\0'; - - for (n = 0; n < 4; n++) { - lws_snprintf(slot, sizeof(slot) - 1, "%dssid", n); - s = sizeof(lws_esp32.ssid[0]) - 1; - lws_esp32.ssid[n][0] = '\0'; - nvs_get_str(nvh, slot, lws_esp32.ssid[n], &s); - - lws_snprintf(slot, sizeof(slot) - 1, "%dpassword", n); - s = sizeof(lws_esp32.password[0]) - 1; - lws_esp32.password[n][0] = '\0'; - nvs_get_str(nvh, slot, lws_esp32.password[n], &s); - } - - s = sizeof(lws_esp32.serial) - 1; - if (nvs_get_str(nvh, "serial", lws_esp32.serial, &s) != ESP_OK) - lws_esp32_force_ap = 1; - else - snprintf((char *)config.ap.ssid, sizeof(config.ap.ssid) - 1, - "config-%s-%s", lws_esp32.model, lws_esp32.serial); - s = sizeof(lws_esp32.opts) - 1; - if (nvs_get_str(nvh, "opts", lws_esp32.opts, &s) != ESP_OK) - lws_esp32_force_ap = 1; - - lws_esp32.access_pw[0] = '\0'; - nvs_get_str(nvh, "access_pw", lws_esp32.access_pw, &s); - - lws_esp32.group[0] = '\0'; - s = sizeof(lws_esp32.group); - nvs_get_str(nvh, "group", lws_esp32.group, &s); - - lws_esp32.role[0] = '\0'; - s = sizeof(lws_esp32.role); - nvs_get_str(nvh, "role", lws_esp32.role, &s); - - /* if group and role defined: group-role */ - if (lws_esp32.group[0] && lws_esp32.role[0]) - lws_snprintf(lws_esp32.hostname, sizeof(lws_esp32.hostname) - 1, - "%s-%s", lws_esp32.group, lws_esp32.role); - else /* otherwise model-serial */ - lws_snprintf(lws_esp32.hostname, sizeof(lws_esp32.hostname) - 1, - "%s-%s", lws_esp32.model, lws_esp32.serial); - - nvs_close(nvh); - - lws_gapss_to(LWS_GAPSS_SCAN); - start_scan(); - - return lws_esp32_force_ap; -} - - -void -lws_esp32_wlan_config(void) -{ - ledc_timer_config_t ledc_timer = { - .bit_num = LEDC_TIMER_13_BIT, - .freq_hz = 5000, - .speed_mode = LEDC_HIGH_SPEED_MODE, - .timer_num = LEDC_TIMER_0 - }; - int n; - - lwsl_debug("%s\n", __func__); - - ledc_timer_config(&ledc_timer); - - lws_set_genled(LWSESP32_GENLED__INIT); - - /* user code needs to provide lws_esp32_leds_timer_cb */ - - leds_timer = xTimerCreate("lws_leds", pdMS_TO_TICKS(25), 1, NULL, - (TimerCallbackFunction_t)lws_esp32_leds_timer_cb); - scan_timer = xTimerCreate("lws_scan", pdMS_TO_TICKS(10000), 0, NULL, - (TimerCallbackFunction_t)lws_esp32_scan_timer_cb); - debounce_timer = xTimerCreate("lws_db", pdMS_TO_TICKS(100), 0, NULL, - (TimerCallbackFunction_t)lws_esp32_debounce_timer_cb); - association_timer = xTimerCreate("lws_assoc", pdMS_TO_TICKS(10000), 0, NULL, - (TimerCallbackFunction_t)lws_esp32_assoc_timer_cb); - -#if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) - mdns_timer = xTimerCreate("lws_mdns", pdMS_TO_TICKS(5000), 0, NULL, - (TimerCallbackFunction_t)lws_esp32_mdns_timer_cb); -#endif - scan_timer_exists = 1; - xTimerStart(leds_timer, 0); - - *(volatile uint32_t *)PERIPHS_IO_MUX_MTMS_U = FUNC_MTMS_GPIO14; - - gpio_output_set(0, 0, 0, (1 << GPIO_SW)); - - n = gpio_install_isr_service(0); - if (!n) { - gpio_config_t c; - - c.intr_type = GPIO_INTR_NEGEDGE; - c.mode = GPIO_MODE_INPUT; - c.pin_bit_mask = 1 << GPIO_SW; - c.pull_down_en = 0; - c.pull_up_en = 0; - gpio_config(&c); - - if (gpio_isr_handler_add(GPIO_SW, gpio_irq, NULL)) - lwsl_notice("isr handler add for 14 failed\n"); - } else - lwsl_notice("failed to install gpio isr service: %d\n", n); - - lws_esp32_wlan_nvs_get(0); - tcpip_adapter_init(); -} - -void -lws_esp32_wlan_start_ap(void) -{ - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - - ESP_ERROR_CHECK( esp_wifi_init(&cfg)); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM)); - - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_APSTA) ); - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_AP, &config) ); - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &sta_config)); - ESP_ERROR_CHECK( esp_wifi_start()); - - esp_wifi_scan_start(&scan_config, false); - - if (sta_config.sta.ssid[0]) { - tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, - (const char *)&config.ap.ssid[7]); - // esp_wifi_set_auto_connect(1); - ESP_ERROR_CHECK( esp_wifi_connect()); - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &sta_config)); - ESP_ERROR_CHECK( esp_wifi_connect()); - } -} - -void -lws_esp32_wlan_start_station(void) -{ - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - - ESP_ERROR_CHECK( esp_wifi_init(&cfg)); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM)); - - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA)); - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &sta_config)); - - ESP_ERROR_CHECK( esp_wifi_start()); - - tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, - (const char *)&config.ap.ssid[7]); - //esp_wifi_set_auto_connect(1); - //ESP_ERROR_CHECK( esp_wifi_connect()); - - lws_esp32_scan_timer_cb(NULL); -} - -const esp_partition_t * -lws_esp_ota_get_boot_partition(void) -{ - const esp_partition_t *part = esp_ota_get_boot_partition(), - *factory_part, *ota; - esp_image_header_t eih, ota_eih; - uint32_t *p_force_factory_magic = (uint32_t *)LWS_MAGIC_REBOOT_TYPE_ADS; - - /* confirm what we are told is the boot part is sane */ - spi_flash_read(part->address , &eih, sizeof(eih)); - factory_part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, - ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL); - ota = esp_partition_find_first(ESP_PARTITION_TYPE_APP, - ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL); - spi_flash_read(ota->address , &ota_eih, sizeof(ota_eih)); - - if (eih.spi_mode == 0xff || - *p_force_factory_magic == LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY || - *p_force_factory_magic == LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON - ) { - /* - * we believed we were going to boot OTA, but we fell - * back to FACTORY in the bootloader when we saw it - * had been erased. esp_ota_get_boot_partition() still - * says the OTA partition then even if we are in the - * factory partition right now. - */ - part = factory_part; - } - -#ifdef CONFIG_LWS_IS_FACTORY_APPLICATION - else - if (ota_eih.spi_mode != 0xff && - part->address != factory_part->address) { - uint8_t buf[4096]; - uint32_t n; - /* - * we are a FACTORY image running in an OTA slot... - * it means we were just written and need to copy - * ourselves into the FACTORY slot. - */ - lwsl_notice("Copying FACTORY update into place " - "0x%x len 0x%x\n", factory_part->address, - factory_part->size); - esp_task_wdt_reset(); - if (spi_flash_erase_range(factory_part->address, - factory_part->size)) { - lwsl_err("spi: Failed to erase\n"); - goto retry; - } - - for (n = 0; n < factory_part->size; n += sizeof(buf)) { - esp_task_wdt_reset(); - spi_flash_read(part->address + n , buf, - sizeof(buf)); - if (spi_flash_write(factory_part->address + n, - buf, sizeof(buf))) { - lwsl_err("spi: Failed to write\n"); - goto retry; - } - } - - /* - * We send a message to the bootloader to erase the OTA header, we will come back up in - * factory where the user can reload the OTA image - */ - lwsl_notice(" FACTORY copy successful, rebooting\n"); - lws_esp32_restart_guided(LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY_ERASE_OTA); -retry: - esp_restart(); - } -#endif - - return part; -} - - -void -lws_esp32_set_creation_defaults(struct lws_context_creation_info *info) -{ - const esp_partition_t *part; - - memset(info, 0, sizeof(*info)); - - lws_set_log_level(63, lwsl_emit_syslog); - - part = lws_esp_ota_get_boot_partition(); - (void)part; - - info->vhost_name = "default"; - info->port = 443; - info->fd_limit_per_thread = 16; - info->max_http_header_pool = 5; - info->max_http_header_data = 1024; - info->pt_serv_buf_size = 4096; - info->keepalive_timeout = 30; - info->timeout_secs = 30; - info->simultaneous_ssl_restriction = 2; - info->options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; -} - -int -lws_esp32_get_image_info(const esp_partition_t *part, struct lws_esp32_image *i, - char *json, int json_len) -{ - esp_image_segment_header_t eis; - esp_image_header_t eih; - uint32_t hdr; - - spi_flash_read(part->address , &eih, sizeof(eih)); - hdr = part->address + sizeof(eih); - - if (eih.magic != ESP_IMAGE_HEADER_MAGIC) { - lwsl_notice("%s: bad image header magic\n", __func__); - return 1; - } - - eis.data_len = 0; - while (eih.segment_count-- && eis.data_len != 0xffffffff) { - spi_flash_read(hdr, &eis, sizeof(eis)); - hdr += sizeof(eis) + eis.data_len; - } - hdr += (~hdr & 15) + 1; - - if (eih.hash_appended) - hdr += 0x20; - -// lwsl_notice("romfs estimated at 0x%x\n", hdr); - - i->romfs = hdr + 0x4; - spi_flash_read(hdr, &i->romfs_len, sizeof(i->romfs_len)); - i->json = i->romfs + i->romfs_len + 4; - spi_flash_read(i->json - 4, &i->json_len, sizeof(i->json_len)); - - if (i->json_len < json_len - 1) - json_len = i->json_len; - spi_flash_read(i->json, json, json_len); - json[json_len] = '\0'; - - return 0; -} - -static int -_rngf(void *context, unsigned char *buf, size_t len) -{ - if (lws_get_random(context, buf, len) == len) - return 0; - - return -1; -} - -int -lws_esp32_selfsigned(struct lws_vhost *vhost) -{ - mbedtls_x509write_cert crt; - char subject[200]; - mbedtls_pk_context mpk; - int buf_size = 4096, n; - uint8_t *buf = malloc(buf_size); /* malloc because given to user code */ - mbedtls_mpi mpi; - nvs_handle nvh; - size_t s; - - lwsl_notice("%s: %s\n", __func__, vhost->name); - - if (!buf) - return -1; - - if (nvs_open("lws-station", NVS_READWRITE, &nvh)) { - lwsl_notice("%s: can't open nvs\n", __func__); - free(buf); - return 1; - } - - n = 0; - if (!nvs_get_blob(nvh, vhost->tls.alloc_cert_path, NULL, &s)) - n |= 1; - if (!nvs_get_blob(nvh, vhost->tls.key_path, NULL, &s)) - n |= 2; - - nvs_close(nvh); - if (n == 3) { - lwsl_notice("%s: certs exist\n", __func__); - free(buf); - return 0; /* certs already exist */ - } - - lwsl_notice("%s: creating selfsigned initial certs\n", __func__); - - mbedtls_x509write_crt_init(&crt); - - mbedtls_pk_init(&mpk); - if (mbedtls_pk_setup(&mpk, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA))) { - lwsl_notice("%s: pk_setup failed\n", __func__); - goto fail; - } - lwsl_notice("%s: generating 2048-bit RSA keypair... " - "this may take a minute or so...\n", __func__); - n = mbedtls_rsa_gen_key(mbedtls_pk_rsa(mpk), _rngf, vhost->context, - 2048, 65537); - if (n) { - lwsl_notice("%s: failed to generate keys\n", __func__); - goto fail1; - } - lwsl_notice("%s: keys done\n", __func__); - - /* subject must be formatted like "C=TW,O=warmcat,CN=myserver" */ - - lws_snprintf(subject, sizeof(subject) - 1, - "C=TW,ST=New Taipei City,L=Taipei,O=warmcat,CN=%s", - lws_esp32.hostname); - - if (mbedtls_x509write_crt_set_subject_name(&crt, subject)) { - lwsl_notice("set SN failed\n"); - goto fail1; - } - mbedtls_x509write_crt_set_subject_key(&crt, &mpk); - if (mbedtls_x509write_crt_set_issuer_name(&crt, subject)) { - lwsl_notice("set IN failed\n"); - goto fail1; - } - mbedtls_x509write_crt_set_issuer_key(&crt, &mpk); - - lws_get_random(vhost->context, &n, sizeof(n)); - lws_snprintf(subject, sizeof(subject), "%d", n); - - mbedtls_mpi_init(&mpi); - mbedtls_mpi_read_string(&mpi, 10, subject); - mbedtls_x509write_crt_set_serial(&crt, &mpi); - mbedtls_mpi_free(&mpi); - - mbedtls_x509write_crt_set_validity(&crt, "20171105235959", - "20491231235959"); - - mbedtls_x509write_crt_set_key_usage(&crt, - MBEDTLS_X509_KU_DIGITAL_SIGNATURE | - MBEDTLS_X509_KU_KEY_ENCIPHERMENT); - - - mbedtls_x509write_crt_set_md_alg(&crt, MBEDTLS_MD_SHA256); - - n = mbedtls_x509write_crt_pem(&crt, buf, buf_size, _rngf, - vhost->context); - if (n < 0) { - lwsl_notice("%s: write crt der failed\n", __func__); - goto fail1; - } - - lws_plat_write_cert(vhost, 0, 0, buf, strlen((const char *)buf)); - - if (mbedtls_pk_write_key_pem(&mpk, buf, buf_size)) { - lwsl_notice("write key pem failed\n"); - goto fail1; - } - - lws_plat_write_cert(vhost, 1, 0, buf, strlen((const char *)buf)); - - mbedtls_pk_free(&mpk); - mbedtls_x509write_crt_free(&crt); - - lwsl_notice("%s: cert creation complete\n", __func__); - - return n; - -fail1: - mbedtls_pk_free(&mpk); -fail: - mbedtls_x509write_crt_free(&crt); - free(buf); - - nvs_close(nvh); - - return -1; -} - -void -lws_esp32_update_acme_info(void) -{ - int n; - - n = lws_plat_read_file("acme-email", lws_esp32.le_email, - sizeof(lws_esp32.le_email) - 1); - if (n >= 0) - lws_esp32.le_email[n] = '\0'; - - n = lws_plat_read_file("acme-cn", lws_esp32.le_dns, - sizeof(lws_esp32.le_dns) - 1); - if (n >= 0) - lws_esp32.le_dns[n] = '\0'; -} - -struct lws_context * -lws_esp32_init(struct lws_context_creation_info *info, struct lws_vhost **pvh) -{ - const esp_partition_t *part = lws_esp_ota_get_boot_partition(); - struct lws_context *context; - struct lws_esp32_image i; - struct lws_vhost *vhost; - struct lws wsi; - char buf[512]; - - context = lws_create_context(info); - if (context == NULL) { - lwsl_err("Failed to create context\n"); - return NULL; - } - - lws_esp32_get_image_info(part, &i, buf, sizeof(buf) - 1); - - lws_esp32_romfs = (romfs_t)i.romfs; - if (!romfs_mount_check(lws_esp32_romfs)) { - lwsl_err("mount error on ROMFS at %p 0x%x\n", lws_esp32_romfs, - i.romfs); - return NULL; - } - - lwsl_notice("ROMFS length %uKiB\n", i.romfs_len >> 10); - - puts(buf); - - /* set the lws vfs to use our romfs */ -#if defined(LWS_WITH_FILE_OPS) - lws_set_fops(context, &fops); -#endif - - info->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX | - LWS_SERVER_OPTION_IGNORE_MISSING_CERT; - - vhost = lws_create_vhost(context, info); - if (!vhost) { - lwsl_err("Failed to create vhost\n"); - return NULL; - } - - lws_esp32_update_acme_info(); - - lws_esp32_selfsigned(vhost); - wsi.context = vhost->context; - wsi.vhost = vhost; - - lws_tls_server_certs_load(vhost, &wsi, info->ssl_cert_filepath, - info->ssl_private_key_filepath, NULL, 0, NULL, 0); - - lws_init_vhost_client_ssl(info, vhost); - - if (pvh) - *pvh = vhost; - - if (lws_protocol_init(context)) - return NULL; - - return context; -} - -static const uint16_t sineq16[] = { - 0x0000, 0x0191, 0x031e, 0x04a4, 0x061e, 0x0789, 0x08e2, 0x0a24, - 0x0b4e, 0x0c5c, 0x0d4b, 0x0e1a, 0x0ec6, 0x0f4d, 0x0faf, 0x0fea, -}; - -static uint16_t sine_lu(int n) -{ - switch ((n >> 4) & 3) { - case 1: - return 4096 + sineq16[n & 15]; - case 2: - return 4096 + sineq16[15 - (n & 15)]; - case 3: - return 4096 - sineq16[n & 15]; - default: - return 4096 - sineq16[15 - (n & 15)]; - } -} - -/* useful for sine led fade patterns */ - -uint16_t lws_esp32_sine_interp(int n) -{ - /* - * 2: quadrant - * 4: table entry in quadrant - * 4: interp (LSB) - * - * total 10 bits / 1024 steps per cycle - * - * + 0: 0 - * + 256: 4096 - * + 512: 8192 - * + 768: 4096 - * +1023: 0 - */ - - return (sine_lu(n >> 4) * (15 - (n & 15)) + - sine_lu((n >> 4) + 1) * (n & 15)) / 15; -} diff -Nru libwebsockets-4.0.20/lib/plat/freertos/esp32/esp_attr.h libwebsockets-2.4.2/lib/plat/freertos/esp32/esp_attr.h --- libwebsockets-4.0.20/lib/plat/freertos/esp32/esp_attr.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/freertos/esp32/esp_attr.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef __ESP_ATTR_H__ -#define __ESP_ATTR_H__ - -#define ROMFN_ATTR - -//Normally, the linker script will put all code and rodata in flash, -//and all variables in shared RAM. These macros can be used to redirect -//particular functions/variables to other memory regions. - -// Forces code into IRAM instead of flash. -#define IRAM_ATTR __attribute__((section(".iram1"))) - -// Forces data into DRAM instead of flash -#define DRAM_ATTR __attribute__((section(".dram1"))) - -// Forces data to be 4 bytes aligned -#define WORD_ALIGNED_ATTR __attribute__((aligned(4))) - -// Forces data to be placed to DMA-capable places -#define DMA_ATTR WORD_ALIGNED_ATTR DRAM_ATTR - -// Forces a string into DRAM instead of flash -// Use as ets_printf(DRAM_STR("Hello world!\n")); -#define DRAM_STR(str) (__extension__({static const DRAM_ATTR char __c[] = (str); (const char *)&__c;})) - -// Forces code into RTC fast memory. See "docs/deep-sleep-stub.rst" -#define RTC_IRAM_ATTR __attribute__((section(".rtc.text"))) - -// Forces data into RTC slow memory. See "docs/deep-sleep-stub.rst" -// Any variable marked with this attribute will keep its value -// during a deep sleep / wake cycle. -#define RTC_DATA_ATTR __attribute__((section(".rtc.data"))) - -// Forces read-only data into RTC slow memory. See "docs/deep-sleep-stub.rst" -#define RTC_RODATA_ATTR __attribute__((section(".rtc.rodata"))) - -// Forces data into noinit section to avoid initialization after restart. -#define __NOINIT_ATTR __attribute__((section(".noinit"))) - -// Forces data into RTC slow memory of .noinit section. -// Any variable marked with this attribute will keep its value -// after restart or during a deep sleep / wake cycle. -#define RTC_NOINIT_ATTR __attribute__((section(".rtc_noinit"))) - -#endif /* __ESP_ATTR_H__ */ diff -Nru libwebsockets-4.0.20/lib/plat/freertos/freertos-fds.c libwebsockets-2.4.2/lib/plat/freertos/freertos-fds.c --- libwebsockets-4.0.20/lib/plat/freertos/freertos-fds.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/freertos/freertos-fds.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -void -lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - - pt->fds[pt->fds_count++].revents = 0; -} - -void -lws_plat_delete_socket_from_fds(struct lws_context *context, - struct lws *wsi, int m) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - - pt->fds_count--; -} - -int -lws_plat_change_pollfd(struct lws_context *context, - struct lws *wsi, struct lws_pollfd *pfd) -{ - return 0; -} - -int -insert_wsi(const struct lws_context *context, struct lws *wsi) -{ - assert(context->lws_lookup[wsi->desc.sockfd - - lws_plat_socket_offset()] == 0); - - context->lws_lookup[wsi->desc.sockfd - \ - lws_plat_socket_offset()] = wsi; - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/plat/freertos/freertos-file.c libwebsockets-2.4.2/lib/plat/freertos/freertos-file.c --- libwebsockets-4.0.20/lib/plat/freertos/freertos-file.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/freertos/freertos-file.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,226 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -int lws_plat_apply_FD_CLOEXEC(int n) -{ - return 0; -} - - -lws_fop_fd_t IRAM_ATTR -_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, - const char *vpath, lws_fop_flags_t *flags) -{ - struct stat stat_buf; - lws_fop_fd_t fop_fd; - int ret = open(filename, *flags, 0664); - - if (ret < 0) - return NULL; - - if (fstat(ret, &stat_buf) < 0) - goto bail; - - fop_fd = lws_malloc(sizeof(*fop_fd), "fops open"); - if (!fop_fd) - goto bail; - - fop_fd->fops = fops; - fop_fd->fd = ret; - fop_fd->flags = *flags; - fop_fd->filesystem_priv = NULL; /* we don't use it */ - fop_fd->pos = 0; - fop_fd->len = stat_buf.st_size; - - return fop_fd; - -bail: - close(ret); - - return NULL; -} - -int IRAM_ATTR -_lws_plat_file_close(lws_fop_fd_t *fops_fd) -{ - int fd = (*fops_fd)->fd; - - lws_free(*fops_fd); - *fops_fd = NULL; - - return close(fd); -} - -lws_fileofs_t IRAM_ATTR -_lws_plat_file_seek_cur(lws_fop_fd_t fops_fd, lws_fileofs_t offset) -{ - return lseek(fops_fd->fd, offset, SEEK_CUR); -} - -int IRAM_ATTR -_lws_plat_file_read(lws_fop_fd_t fops_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len) -{ - long n; - - n = read(fops_fd->fd, buf, len); - if (n == -1) { - *amount = 0; - return -1; - } - fops_fd->pos += n; - *amount = n; - - return 0; -} - -int IRAM_ATTR -_lws_plat_file_write(lws_fop_fd_t fops_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len) -{ - long n; - - n = write(fops_fd->fd, buf, len); - if (n == -1) { - *amount = 0; - return -1; - } - fops_fd->pos += n; - *amount = n; - - return 0; -} - -#if defined(LWS_AMAZON_RTOS) -int -lws_find_string_in_file(const char *filename, const char *string, int stringlen) -{ - return 0; -} -#else -int -lws_find_string_in_file(const char *filename, const char *string, int stringlen) -{ - nvs_handle nvh; - size_t s; - int n; - char buf[64], result[64]; - const char *p = strchr(string, ':'), *q; - - if (!p) - return 0; - - q = string; - n = 0; - while (n < sizeof(buf) - 1 && q != p) - buf[n++] = *q++; - buf[n] = '\0'; - - ESP_ERROR_CHECK(nvs_open(filename, NVS_READWRITE, &nvh)); - - s = sizeof(result) - 1; - n = nvs_get_str(nvh, buf, result, &s); - nvs_close(nvh); - - if (n != ESP_OK) - return 0; - - return !strcmp(p + 1, result); -} -#endif - -#if !defined(LWS_AMAZON_RTOS) -int -lws_plat_write_file(const char *filename, void *buf, int len) -{ - nvs_handle nvh; - int n; - - if (nvs_open("lws-station", NVS_READWRITE, &nvh)) { - lwsl_notice("%s: failed to open nvs\n", __func__); - return -1; - } - - n = nvs_set_blob(nvh, filename, buf, len); - if (n >= 0) - nvs_commit(nvh); - - nvs_close(nvh); - - lwsl_notice("%s: wrote %s (%d)\n", __func__, filename, n); - - return n; -} - -/* we write vhostname.cert.pem and vhostname.key.pem, 0 return means OK */ - -int -lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf, - int len) -{ - const char *name = vhost->tls.alloc_cert_path; - - if (is_key) - name = vhost->tls.key_path; - - return lws_plat_write_file(name, buf, len) < 0; -} - -int -lws_plat_read_file(const char *filename, void *buf, int len) -{ - nvs_handle nvh; - size_t s = 0; - int n = 0; - - if (nvs_open("lws-station", NVS_READWRITE, &nvh)) { - lwsl_notice("%s: failed to open nvs\n", __func__); - return 1; - } - - ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh)); - if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) - goto bail; - if (s > (size_t)len) - goto bail; - - n = nvs_get_blob(nvh, filename, buf, &s); - - nvs_close(nvh); - - lwsl_notice("%s: read %s (%d)\n", __func__, filename, (int)s); - - if (n) - return -1; - - return (int)s; - -bail: - nvs_close(nvh); - - return -1; -} -#endif /* LWS_AMAZON_RTOS */ diff -Nru libwebsockets-4.0.20/lib/plat/freertos/freertos-init.c libwebsockets-2.4.2/lib/plat/freertos/freertos-init.c --- libwebsockets-4.0.20/lib/plat/freertos/freertos-init.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/freertos/freertos-init.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,115 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -int -lws_plat_context_early_init(void) -{ - return 0; -} - -void -lws_plat_context_early_destroy(struct lws_context *context) -{ -#if defined(LWS_AMAZON_RTOS) - mbedtls_ctr_drbg_free(&context->mcdc); - mbedtls_entropy_free(&context->mec); -#endif -} - -void -lws_plat_context_late_destroy(struct lws_context *context) -{ -#ifdef LWS_WITH_PLUGINS - if (context->plugin_list) - lws_plat_plugins_destroy(context); -#endif - - if (context->lws_lookup) - lws_free(context->lws_lookup); -} - -#if defined(LWS_WITH_HTTP2) -/* - * These are the default SETTINGS used on this platform. The user - * can selectively modify them for a vhost during vhost creation. - */ -const struct http2_settings lws_h2_defaults_esp32 = { { - 1, - /* H2SET_HEADER_TABLE_SIZE */ 512, - /* H2SET_ENABLE_PUSH */ 0, - /* H2SET_MAX_CONCURRENT_STREAMS */ 8, - /* H2SET_INITIAL_WINDOW_SIZE */ 0, - /* H2SET_MAX_FRAME_SIZE */ 16384, - /* H2SET_MAX_HEADER_LIST_SIZE */ 512, - /* H2SET_RESERVED7 */ 0, - /* H2SET_ENABLE_CONNECT_PROTOCOL */ 1, -}}; -#endif - -int -lws_plat_init(struct lws_context *context, - const struct lws_context_creation_info *info) -{ -#if defined(LWS_AMAZON_RTOS) - int n; - - /* initialize platform random through mbedtls */ - mbedtls_entropy_init(&context->mec); - mbedtls_ctr_drbg_init(&context->mcdc); - - n = mbedtls_ctr_drbg_seed(&context->mcdc, mbedtls_entropy_func, - &context->mec, NULL, 0); - if (n) { - lwsl_err("%s: mbedtls_ctr_drbg_seed() returned 0x%x\n", - __func__, n); - - return 1; - } -#endif - - /* master context has the global fd lookup array */ - context->lws_lookup = lws_zalloc(sizeof(struct lws *) * - context->max_fds, "esp32 lws_lookup"); - if (context->lws_lookup == NULL) { - lwsl_err("OOM on lws_lookup array for %d connections\n", - context->max_fds); - return 1; - } - - lwsl_notice(" mem: platform fd map: %5lu bytes\n", - (unsigned long)(sizeof(struct lws *) * context->max_fds)); - -#ifdef LWS_WITH_PLUGINS - if (info->plugin_dirs) - lws_plat_plugins_init(context, info->plugin_dirs); -#endif -#if defined(LWS_WITH_HTTP2) - /* override settings */ - context->set = lws_h2_defaults_esp32; -#endif - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/plat/freertos/freertos-misc.c libwebsockets-2.4.2/lib/plat/freertos/freertos-misc.c --- libwebsockets-4.0.20/lib/plat/freertos/freertos-misc.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/freertos/freertos-misc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -lws_usec_t -lws_now_usecs(void) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec; -} - -size_t -lws_get_random(struct lws_context *context, void *buf, size_t len) -{ -#if defined(LWS_WITH_ESP32) - uint8_t *pb = buf; - - while (len) { - uint32_t r = esp_random(); - uint8_t *p = (uint8_t *)&r; - int b = 4; - - if (len < b) - b = len; - - len -= b; - - while (b--) - *pb++ = p[b]; - } - - return pb - (uint8_t *)buf; -#else - int n; - - n = mbedtls_ctr_drbg_random(&context->mcdc, buf, len); - if (!n) - return len; - - /* failed */ - - lwsl_err("%s: mbedtls_ctr_drbg_random returned 0x%x\n", __func__, n); - - return 0; -#endif -} - - -void lwsl_emit_syslog(int level, const char *line) -{ - lwsl_emit_stderr(level, line); -} - -int -lws_plat_drop_app_privileges(struct lws_context *context, int actually_init) -{ - return 0; -} - -int -lws_plat_recommended_rsa_bits(void) -{ - /* - * 2048-bit key generation takes up to a minute on ESP32, 4096 - * is like 15 minutes + - */ - return 2048; -} diff -Nru libwebsockets-4.0.20/lib/plat/freertos/freertos-pipe.c libwebsockets-2.4.2/lib/plat/freertos/freertos-pipe.c --- libwebsockets-4.0.20/lib/plat/freertos/freertos-pipe.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/freertos/freertos-pipe.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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" - -int -lws_plat_pipe_create(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct sockaddr_in *si = &wsi->context->frt_pipe_si; - lws_sockfd_type *fd = pt->dummy_pipe_fds; - - /* - * There's no pipe abstraction on lwip / freertos... use a UDP socket - * listening on 127.0.0.1:54321 and send a byte to it from a second UDP - * socket to cancel the wait. - */ - - fd[0] = socket(AF_INET, SOCK_DGRAM, 0); - if (fd[0] < 0) - goto bail; - - fd[1] = socket(AF_INET, SOCK_DGRAM, 0); - if (fd[1] < 0) - goto bail; - - /* - * No need for memset since it's in zalloc'd context... it's in the - * context so we can reuse the prepared sockaddr to send tp fd[0] whem - * we want to cancel the wait - */ - - si->sin_family = AF_INET; - si->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - si->sin_port = htons(54321); - - if (bind(fd[0], (const struct sockaddr *)si, sizeof(*si)) < 0) - goto bail; - - return 0; - -bail: - lwsl_err("%s: failed\n", __func__); - - return 1; -} - -int -lws_plat_pipe_signal(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct sockaddr_in *si = &wsi->context->frt_pipe_si; - lws_sockfd_type *fd = pt->dummy_pipe_fds; - uint8_t u = 0; - int n; - - /* - * Send a single UDP byte payload to the listening socket fd[0], forcing - * the event loop wait to wake. fd[1] and context->frt_pipe_si are - * set at context creation and are static, the UDP sendto is supposed to - * be threadsafe for lwip: - * - * https://lwip.fandom.com/wiki/LwIP_and_multithreading - * - * Sockets generally can't be used by more than one application thread - * (on udp/raw netconn, doing a sendto/recv is currently possible). - */ - - n = sendto(fd[1], &u, 1, 0, (struct sockaddr *)si, sizeof(*si)); - - return n != 1; -} - -void -lws_plat_pipe_close(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - lws_sockfd_type *fd = pt->dummy_pipe_fds; - - if (fd[0] && fd[0] != -1) - close(fd[0]); - if (fd[1] && fd[1] != -1) - close(fd[1]); - - fd[0] = fd[1] = -1; -} diff -Nru libwebsockets-4.0.20/lib/plat/freertos/freertos-resolv.c libwebsockets-2.4.2/lib/plat/freertos/freertos-resolv.c --- libwebsockets-4.0.20/lib/plat/freertos/freertos-resolv.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/freertos/freertos-resolv.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -lws_async_dns_server_check_t -lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46) -{ - uint32_t ipv4; - lws_async_dns_server_check_t s = LADNS_CONF_SERVER_CHANGED; - - FreeRTOS_GetAddressConfiguration(NULL, NULL, NULL, &ipv4); - - sa46->sa4.sin_family = AF_INET; - if (sa46->sa4.sin_addr.s_addr == ipv4) - s = LADNS_CONF_SERVER_SAME; - - sa46->sa4.sin_addr.s_addr = ipv4; - - return s; -} diff -Nru libwebsockets-4.0.20/lib/plat/freertos/freertos-service.c libwebsockets-2.4.2/lib/plat/freertos/freertos-service.c --- libwebsockets-4.0.20/lib/plat/freertos/freertos-service.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/freertos/freertos-service.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,219 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -int -lws_plat_service(struct lws_context *context, int timeout_ms) -{ - int n = _lws_plat_service_tsi(context, timeout_ms, 0); - -#if !defined(LWS_AMAZON_RTOS) - esp_task_wdt_reset(); -#endif - - return n; -} - - -int -_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) -{ - struct lws_context_per_thread *pt; - lws_usec_t timeout_us; - int n = -1, m, c, a = 0; - - /* stay dead once we are dead */ - - if (!context || !context->vhost_list) - return 1; - - pt = &context->pt[tsi]; - lws_stats_bump(pt, LWSSTATS_C_SERVICE_ENTRY, 1); - - { - unsigned long m = lws_now_secs(); - - if (m > context->time_last_state_dump) { - context->time_last_state_dump = m; -#if defined(LWS_AMAZON_RTOS) - n = xPortGetFreeHeapSize(); -#else - n = esp_get_free_heap_size(); -#endif - if ((unsigned int)n != context->last_free_heap) { - if ((unsigned int)n > context->last_free_heap) - lwsl_notice(" heap :%ld (+%ld)\n", - (unsigned long)n, - (unsigned long)(n - - context->last_free_heap)); - else - lwsl_notice(" heap :%ld (-%ld)\n", - (unsigned long)n, - (unsigned long)( - context->last_free_heap - - n)); - context->last_free_heap = n; - } - } - } - - if (timeout_ms < 0) - timeout_ms = 0; - else - /* force a default timeout of 23 days */ - timeout_ms = 2000000000; - timeout_us = ((lws_usec_t)timeout_ms) * LWS_US_PER_MS; - - if (!pt->service_tid_detected) { - struct lws *_lws = pt->fake_wsi; - - if (!_lws) - return 1; - _lws->context = context; - - pt->service_tid = context->vhost_list->protocols[0].callback( - _lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); - pt->service_tid_detected = 1; - } - - /* - * is there anybody with pending stuff that needs service forcing? - */ - if (lws_service_adjust_timeout(context, 1, tsi)) { - -again: - a = 0; - if (timeout_us) { - lws_usec_t us; - - lws_pt_lock(pt, __func__); - /* don't stay in poll wait longer than next hr timeout */ - us = __lws_sul_service_ripe(&pt->pt_sul_owner, lws_now_usecs()); - if (us && us < timeout_us) - timeout_us = us; - - lws_pt_unlock(pt); - } - - // n = poll(pt->fds, pt->fds_count, timeout_ms); - { - fd_set readfds, writefds, errfds; - struct timeval tv = { timeout_us / LWS_US_PER_SEC, - timeout_us % LWS_US_PER_SEC }, *ptv = &tv; - int max_fd = 0; - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&errfds); - - for (n = 0; n < (int)pt->fds_count; n++) { - pt->fds[n].revents = 0; - if (pt->fds[n].fd >= max_fd) - max_fd = pt->fds[n].fd; - if (pt->fds[n].events & LWS_POLLIN) - FD_SET(pt->fds[n].fd, &readfds); - if (pt->fds[n].events & LWS_POLLOUT) - FD_SET(pt->fds[n].fd, &writefds); - FD_SET(pt->fds[n].fd, &errfds); - } - - n = select(max_fd + 1, &readfds, &writefds, &errfds, ptv); - n = 0; - - #if defined(LWS_WITH_DETAILED_LATENCY) - /* - * so we can track how long it took before we actually read a POLLIN - * that was signalled when we last exited poll() - */ - if (context->detailed_latency_cb) - pt->ust_left_poll = lws_now_usecs(); - #endif - - for (m = 0; m < (int)pt->fds_count; m++) { - c = 0; - if (FD_ISSET(pt->fds[m].fd, &readfds)) { - pt->fds[m].revents |= LWS_POLLIN; - c = 1; - } - if (FD_ISSET(pt->fds[m].fd, &writefds)) { - pt->fds[m].revents |= LWS_POLLOUT; - c = 1; - } - if (FD_ISSET(pt->fds[m].fd, &errfds)) { - // lwsl_notice("errfds %d\n", pt->fds[m].fd); - pt->fds[m].revents |= LWS_POLLHUP; - c = 1; - } - - if (c) - n++; - } - } - - m = 0; - - #if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) - m |= !!pt->ws.rx_draining_ext_list; - #endif - - if (pt->context->tls_ops && - pt->context->tls_ops->fake_POLLIN_for_buffered) - m |= pt->context->tls_ops->fake_POLLIN_for_buffered(pt); - - if (!m && !n) - return 0; - } else - a = 1; - - m = lws_service_flag_pending(context, tsi); - if (m) - c = -1; /* unknown limit */ - else - if (n < 0) { - if (LWS_ERRNO != LWS_EINTR) - return -1; - return 0; - } else - c = n; - - /* any socket with events to service? */ - for (n = 0; n < (int)pt->fds_count && c; n++) { - if (!pt->fds[n].revents) - continue; - - c--; - - m = lws_service_fd_tsi(context, &pt->fds[n], tsi); - if (m < 0) - return -1; - /* if something closed, retry this slot */ - if (m) - n--; - } - - if (a) - goto again; - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/plat/freertos/freertos-sockets.c libwebsockets-2.4.2/lib/plat/freertos/freertos-sockets.c --- libwebsockets-4.0.20/lib/plat/freertos/freertos-sockets.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/freertos/freertos-sockets.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,261 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -int -lws_send_pipe_choked(struct lws *wsi) -{ - struct lws *wsi_eff = wsi; - fd_set writefds; - struct timeval tv = { 0, 0 }; - int n; -#if defined(LWS_WITH_HTTP2) - wsi_eff = lws_get_network_wsi(wsi); -#endif - - /* the fact we checked implies we avoided back-to-back writes */ - wsi_eff->could_have_pending = 0; - - /* treat the fact we got a truncated send pending as if we're choked */ - if (lws_has_buffered_out(wsi) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - || wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more -#endif - ) - return 1; - - FD_ZERO(&writefds); - FD_SET(wsi_eff->desc.sockfd, &writefds); - - n = select(wsi_eff->desc.sockfd + 1, NULL, &writefds, NULL, &tv); - if (n < 0) - return 1; /* choked */ - - return !n; /* n = 0 = not writable = choked */ -} - -int -lws_poll_listen_fd(struct lws_pollfd *fd) -{ - fd_set readfds; - struct timeval tv = { 0, 0 }; - - FD_ZERO(&readfds); - FD_SET(fd->fd, &readfds); - - return select(fd->fd + 1, &readfds, NULL, NULL, &tv); -} - -int -lws_plat_set_nonblocking(lws_sockfd_type fd) -{ - return fcntl(fd, F_SETFL, O_NONBLOCK) < 0; -} - -int -lws_plat_set_socket_options(struct lws_vhost *vhost, int fd, int unix_skt) -{ - int optval = 1; - socklen_t optlen = sizeof(optval); - -#if defined(__APPLE__) || \ - defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ - defined(__NetBSD__) || \ - defined(__OpenBSD__) - struct protoent *tcp_proto; -#endif - - if (vhost->ka_time) { - /* enable keepalive on this socket */ - optval = 1; - if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, - (const void *)&optval, optlen) < 0) - return 1; - -#if defined(__APPLE__) || \ - defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ - defined(__NetBSD__) || \ - defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun) - - /* - * didn't find a way to set these per-socket, need to - * tune kernel systemwide values - */ -#else - /* set the keepalive conditions we want on it too */ - optval = vhost->ka_time; - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, - (const void *)&optval, optlen) < 0) - return 1; - - optval = vhost->ka_interval; - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, - (const void *)&optval, optlen) < 0) - return 1; - - optval = vhost->ka_probes; - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, - (const void *)&optval, optlen) < 0) - return 1; -#endif - } - - /* Disable Nagle */ - optval = 1; - if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &optval, optlen) < 0) - return 1; - - return lws_plat_set_nonblocking(fd); -} - -/* cast a struct sockaddr_in6 * into addr for ipv6 */ - -int -lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, - size_t addrlen) -{ -#if 0 - int rc = LWS_ITOSA_NOT_EXIST; - - struct ifaddrs *ifr; - struct ifaddrs *ifc; -#ifdef LWS_WITH_IPV6 - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; -#endif - - getifaddrs(&ifr); - for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) { - if (!ifc->ifa_addr) - continue; - - lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname); - - if (strcmp(ifc->ifa_name, ifname)) - continue; - - switch (ifc->ifa_addr->sa_family) { - case AF_INET: -#ifdef LWS_WITH_IPV6 - if (ipv6) { - /* map IPv4 to IPv6 */ - memset((char *)&addr6->sin6_addr, 0, - sizeof(struct in6_addr)); - addr6->sin6_addr.s6_addr[10] = 0xff; - addr6->sin6_addr.s6_addr[11] = 0xff; - memcpy(&addr6->sin6_addr.s6_addr[12], - &((struct sockaddr_in *)ifc->ifa_addr)->sin_addr, - sizeof(struct in_addr)); - } else -#endif - memcpy(addr, - (struct sockaddr_in *)ifc->ifa_addr, - sizeof(struct sockaddr_in)); - break; -#ifdef LWS_WITH_IPV6 - case AF_INET6: - memcpy(&addr6->sin6_addr, - &((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr, - sizeof(struct in6_addr)); - break; -#endif - default: - continue; - } - rc = LWS_ITOSA_USABLE; - } - - freeifaddrs(ifr); - - if (rc == LWS_ITOSA_NOT_EXIST) { - /* check if bind to IP address */ -#ifdef LWS_WITH_IPV6 - if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) - rc = LWS_ITOSA_USABLE; - else -#endif - if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1) - rc = LWS_ITOSA_USABLE; - } - - return rc; -#endif - - return LWS_ITOSA_NOT_EXIST; -} - -const char * -lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) -{ - return inet_ntop(af, src, dst, cnt); -} - -int -lws_plat_inet_pton(int af, const char *src, void *dst) -{ - return 1; // inet_pton(af, src, dst); -} - -int -lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len) -{ - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -} - -int -lws_plat_rawudp_broadcast(uint8_t *p, const uint8_t *canned, int canned_len, - int n, int fd, const char *iface) -{ - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -} - -int -lws_plat_if_up(const char *ifname, int fd, int up) -{ - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -} - -int -lws_plat_BINDTODEVICE(lws_sockfd_type fd, const char *ifname) -{ - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -} - -int -lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, - uint8_t *gateway_ip) -{ - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -} diff -Nru libwebsockets-4.0.20/lib/plat/freertos/private-lib-plat-freertos.h libwebsockets-2.4.2/lib/plat/freertos/private-lib-plat-freertos.h --- libwebsockets-4.0.20/lib/plat/freertos/private-lib-plat-freertos.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/freertos/private-lib-plat-freertos.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,112 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * Included from lib/private-lib-core.h if LWS_PLAT_FREERTOS - */ - -#define SOMAXCONN 3 - -#if defined(LWS_AMAZON_RTOS) - int - open(const char *path, int oflag, ...); -#else - #include -#endif - - #include - #include - #include - #include - #include - #include - - #ifndef __cplusplus - #include - #endif - #include - #include -#if defined(LWS_AMAZON_RTOS) -const char * -gai_strerror(int); -#else - #include -#endif - -#if defined(LWS_AMAZON_RTOS) - #include "FreeRTOS.h" -#if defined(LWS_WITH_SYS_ASYNC_DNS) - #include "FreeRTOS_IP.h" -#endif - #include "timers.h" - #include -#else - #include "freertos/timers.h" - #include - #include - #include -#endif - -#if defined(LWS_WITH_ESP32) -#include "lwip/apps/sntp.h" -#endif - -#include - - #if defined(LWS_BUILTIN_GETIFADDRS) - #include "./misc/getifaddrs.h" - #endif - - #define LWS_ERRNO errno - #define LWS_EAGAIN EAGAIN - #define LWS_EALREADY EALREADY - #define LWS_EINPROGRESS EINPROGRESS - #define LWS_EINTR EINTR - #define LWS_EISCONN EISCONN - #define LWS_ENOTCONN ENOTCONN - #define LWS_EWOULDBLOCK EWOULDBLOCK - #define LWS_EADDRINUSE EADDRINUSE - - #define lws_set_blocking_send(wsi) - - #ifndef LWS_NO_FORK - #ifdef LWS_HAVE_SYS_PRCTL_H - #include - #endif - #endif - -#if !defined(MSG_NOSIGNAL) -#define MSG_NOSIGNAL 0 -#endif - -#define compatible_close(x) close(x) -#define lws_plat_socket_offset() LWIP_SOCKET_OFFSET -#define wsi_from_fd(A,B) A->lws_lookup[B - lws_plat_socket_offset()] - -struct lws_context; -struct lws; - -int -insert_wsi(const struct lws_context *context, struct lws *wsi); - -#define delete_from_fd(A,B) A->lws_lookup[B - lws_plat_socket_offset()] = 0 - diff -Nru libwebsockets-4.0.20/lib/plat/lws-plat-esp32.c libwebsockets-2.4.2/lib/plat/lws-plat-esp32.c --- libwebsockets-4.0.20/lib/plat/lws-plat-esp32.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/lws-plat-esp32.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1757 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" +#include "freertos/timers.h" +#include +#include + +/* + * included from libwebsockets.c for unix builds + */ + +unsigned long long time_in_microseconds(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec; +} + +LWS_VISIBLE int +lws_get_random(struct lws_context *context, void *buf, int len) +{ + uint8_t *pb = buf; + + while (len) { + uint32_t r = esp_random(); + uint8_t *p = (uint8_t *)&r; + int b = 4; + + if (len < b) + b = len; + + len -= b; + + while (b--) + *pb++ = p[b]; + } + + return pb - (uint8_t *)buf; +} + +LWS_VISIBLE int +lws_send_pipe_choked(struct lws *wsi) +{ + struct lws *wsi_eff = wsi; + fd_set writefds; + struct timeval tv = { 0, 0 }; +#if defined(LWS_WITH_HTTP2) + wsi_eff = lws_get_network_wsi(wsi); +#endif + + /* treat the fact we got a truncated send pending as if we're choked */ + if (wsi_eff->trunc_len) + return 1; + + FD_ZERO(&writefds); + FD_SET(wsi_eff->desc.sockfd, &writefds); + + if (select(wsi_eff->desc.sockfd + 1, NULL, &writefds, NULL, &tv) < 1) + return 1; + + return 0; +} + +LWS_VISIBLE int +lws_poll_listen_fd(struct lws_pollfd *fd) +{ + fd_set readfds; + struct timeval tv = { 0, 0 }; + + FD_ZERO(&readfds); + FD_SET(fd->fd, &readfds); + + return select(fd->fd + 1, &readfds, NULL, NULL, &tv); +} + +LWS_VISIBLE void +lws_cancel_service_pt(struct lws *wsi) +{ +} + +LWS_VISIBLE void +lws_cancel_service(struct lws_context *context) +{ +} + +LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) +{ + printf("%d: %s", level, line); +} + +LWS_VISIBLE LWS_EXTERN int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt; + int n = -1, m, c; + + /* stay dead once we are dead */ + + if (!context || !context->vhost_list) + return 1; + + pt = &context->pt[tsi]; + lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1); + + { + unsigned long m = lws_now_secs(); + + if (m > context->time_last_state_dump) { + context->time_last_state_dump = m; + n = esp_get_free_heap_size(); + if (n != context->last_free_heap) { + if (n > context->last_free_heap) + lwsl_notice(" heap :%d (+%d)\n", n, + n - context->last_free_heap); + else + lwsl_notice(" heap :%d (-%d)\n", n, + context->last_free_heap - n); + context->last_free_heap = n; + } + } + } + + if (timeout_ms < 0) + goto faked_service; + + if (!context->service_tid_detected) { + struct lws _lws; + + memset(&_lws, 0, sizeof(_lws)); + _lws.context = context; + + context->service_tid_detected = + context->vhost_list->protocols[0].callback( + &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + context->service_tid = context->service_tid_detected; + context->service_tid_detected = 1; + } + + /* + * is there anybody with pending stuff that needs service forcing? + */ + if (!lws_service_adjust_timeout(context, 1, tsi)) { + /* -1 timeout means just do forced service */ + _lws_plat_service_tsi(context, -1, pt->tid); + /* still somebody left who wants forced service? */ + if (!lws_service_adjust_timeout(context, 1, pt->tid)) + /* yes... come back again quickly */ + timeout_ms = 0; + } + +// n = poll(pt->fds, pt->fds_count, timeout_ms); + { + fd_set readfds, writefds, errfds; + struct timeval tv = { timeout_ms / 1000, + (timeout_ms % 1000) * 1000 }, *ptv = &tv; + int max_fd = 0; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&errfds); + + for (n = 0; n < pt->fds_count; n++) { + pt->fds[n].revents = 0; + if (pt->fds[n].fd >= max_fd) + max_fd = pt->fds[n].fd; + if (pt->fds[n].events & LWS_POLLIN) + FD_SET(pt->fds[n].fd, &readfds); + if (pt->fds[n].events & LWS_POLLOUT) + FD_SET(pt->fds[n].fd, &writefds); + FD_SET(pt->fds[n].fd, &errfds); + } + + n = select(max_fd + 1, &readfds, &writefds, &errfds, ptv); + for (n = 0; n < pt->fds_count; n++) { + if (FD_ISSET(pt->fds[n].fd, &readfds)) + pt->fds[n].revents |= LWS_POLLIN; + if (FD_ISSET(pt->fds[n].fd, &writefds)) + pt->fds[n].revents |= LWS_POLLOUT; + if (FD_ISSET(pt->fds[n].fd, &errfds)) + pt->fds[n].revents |= LWS_POLLHUP; + } + } + + +#ifdef LWS_OPENSSL_SUPPORT + if (!pt->rx_draining_ext_list && + !lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) { +#else + if (!pt->rx_draining_ext_list && !n) /* poll timeout */ { +#endif + lws_service_fd_tsi(context, NULL, tsi); + return 0; + } + +faked_service: + m = lws_service_flag_pending(context, tsi); + if (m) + c = -1; /* unknown limit */ + else + if (n < 0) { + if (LWS_ERRNO != LWS_EINTR) + return -1; + return 0; + } else + c = n; + + /* any socket with events to service? */ + for (n = 0; n < pt->fds_count && c; n++) { + if (!pt->fds[n].revents) + continue; + + c--; + + m = lws_service_fd_tsi(context, &pt->fds[n], tsi); + if (m < 0) + return -1; + /* if something closed, retry this slot */ + if (m) + n--; + } + + return 0; +} + +LWS_VISIBLE int +lws_plat_check_connection_error(struct lws *wsi) +{ + return 0; +} + +LWS_VISIBLE int +lws_plat_service(struct lws_context *context, int timeout_ms) +{ + return _lws_plat_service_tsi(context, timeout_ms, 0); +} + +LWS_VISIBLE int +lws_plat_set_socket_options(struct lws_vhost *vhost, int fd) +{ + int optval = 1; + socklen_t optlen = sizeof(optval); + +#if defined(__APPLE__) || \ + defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) + struct protoent *tcp_proto; +#endif + + if (vhost->ka_time) { + /* enable keepalive on this socket */ + optval = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const void *)&optval, optlen) < 0) + return 1; + +#if defined(__APPLE__) || \ + defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || \ + defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun) + + /* + * didn't find a way to set these per-socket, need to + * tune kernel systemwide values + */ +#else + /* set the keepalive conditions we want on it too */ + optval = vhost->ka_time; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, + (const void *)&optval, optlen) < 0) + return 1; + + optval = vhost->ka_interval; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, + (const void *)&optval, optlen) < 0) + return 1; + + optval = vhost->ka_probes; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, + (const void *)&optval, optlen) < 0) + return 1; +#endif + } + + /* Disable Nagle */ + optval = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &optval, optlen) < 0) + return 1; + + /* We are nonblocking... */ + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + return 1; + + return 0; +} + +LWS_VISIBLE void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info) +{ +} + +LWS_VISIBLE int +lws_plat_context_early_init(void) +{ + return 0; +} + +LWS_VISIBLE void +lws_plat_context_early_destroy(struct lws_context *context) +{ +} + +LWS_VISIBLE void +lws_plat_context_late_destroy(struct lws_context *context) +{ +#ifdef LWS_WITH_PLUGINS + if (context->plugin_list) + lws_plat_plugins_destroy(context); +#endif + + if (context->lws_lookup) + lws_free(context->lws_lookup); +} + +/* cast a struct sockaddr_in6 * into addr for ipv6 */ + +LWS_VISIBLE int +lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, + size_t addrlen) +{ +#if 0 + int rc = -1; + + struct ifaddrs *ifr; + struct ifaddrs *ifc; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; +#endif + + getifaddrs(&ifr); + for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) { + if (!ifc->ifa_addr) + continue; + + lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname); + + if (strcmp(ifc->ifa_name, ifname)) + continue; + + switch (ifc->ifa_addr->sa_family) { + case AF_INET: +#ifdef LWS_WITH_IPV6 + if (ipv6) { + /* map IPv4 to IPv6 */ + bzero((char *)&addr6->sin6_addr, + sizeof(struct in6_addr)); + addr6->sin6_addr.s6_addr[10] = 0xff; + addr6->sin6_addr.s6_addr[11] = 0xff; + memcpy(&addr6->sin6_addr.s6_addr[12], + &((struct sockaddr_in *)ifc->ifa_addr)->sin_addr, + sizeof(struct in_addr)); + } else +#endif + memcpy(addr, + (struct sockaddr_in *)ifc->ifa_addr, + sizeof(struct sockaddr_in)); + break; +#ifdef LWS_WITH_IPV6 + case AF_INET6: + memcpy(&addr6->sin6_addr, + &((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr, + sizeof(struct in6_addr)); + break; +#endif + default: + continue; + } + rc = 0; + } + + freeifaddrs(ifr); + + if (rc == -1) { + /* check if bind to IP address */ +#ifdef LWS_WITH_IPV6 + if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) + rc = 0; + else +#endif + if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1) + rc = 0; + } + + return rc; +#endif + + return -1; +} + +LWS_VISIBLE void +lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + pt->fds[pt->fds_count++].revents = 0; +} + +LWS_VISIBLE void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + pt->fds_count--; +} + +LWS_VISIBLE void +lws_plat_service_periodic(struct lws_context *context) +{ +} + +LWS_VISIBLE int +lws_plat_change_pollfd(struct lws_context *context, + struct lws *wsi, struct lws_pollfd *pfd) +{ + return 0; +} + +LWS_VISIBLE const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) +{ + return inet_ntop(af, src, dst, cnt); +} + +LWS_VISIBLE int +lws_plat_inet_pton(int af, const char *src, void *dst) +{ + return 1; // inet_pton(af, src, dst); +} + +LWS_VISIBLE lws_fop_fd_t IRAM_ATTR +_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, + const char *vpath, lws_fop_flags_t *flags) +{ + struct stat stat_buf; + lws_fop_fd_t fop_fd; + int ret = open(filename, *flags, 0664); + + if (ret < 0) + return NULL; + + if (fstat(ret, &stat_buf) < 0) + goto bail; + + fop_fd = lws_malloc(sizeof(*fop_fd), "fops open"); + if (!fop_fd) + goto bail; + + fop_fd->fops = fops; + fop_fd->fd = ret; + fop_fd->flags = *flags; + fop_fd->filesystem_priv = NULL; /* we don't use it */ + fop_fd->pos = 0; + fop_fd->len = stat_buf.st_size; + + return fop_fd; + +bail: + close(ret); + + return NULL; +} + +LWS_VISIBLE int IRAM_ATTR +_lws_plat_file_close(lws_fop_fd_t *fops_fd) +{ + int fd = (*fops_fd)->fd; + + lws_free(*fops_fd); + *fops_fd = NULL; + + return close(fd); +} + +LWS_VISIBLE lws_fileofs_t IRAM_ATTR +_lws_plat_file_seek_cur(lws_fop_fd_t fops_fd, lws_fileofs_t offset) +{ + return lseek(fops_fd->fd, offset, SEEK_CUR); +} + +LWS_VISIBLE int IRAM_ATTR +_lws_plat_file_read(lws_fop_fd_t fops_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + long n; + + n = read(fops_fd->fd, buf, len); + if (n == -1) { + *amount = 0; + return -1; + } + fops_fd->pos += n; + *amount = n; + + return 0; +} + +LWS_VISIBLE int IRAM_ATTR +_lws_plat_file_write(lws_fop_fd_t fops_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + long n; + + n = write(fops_fd->fd, buf, len); + if (n == -1) { + *amount = 0; + return -1; + } + fops_fd->pos += n; + *amount = n; + + return 0; +} + +#if defined(LWS_WITH_HTTP2) +/* + * These are the default SETTINGS used on this platform. The user + * can selectively modify them for a vhost during vhost creation. + */ +const struct http2_settings const lws_h2_defaults_esp32 = { { + 1, + /* H2SET_HEADER_TABLE_SIZE */ 512, + /* H2SET_ENABLE_PUSH */ 0, + /* H2SET_MAX_CONCURRENT_STREAMS */ 8, + /* H2SET_INITIAL_WINDOW_SIZE */ 65535, + /* H2SET_MAX_FRAME_SIZE */ 16384, + /* H2SET_MAX_HEADER_LIST_SIZE */ 512, +}}; +#endif + +LWS_VISIBLE int +lws_plat_init(struct lws_context *context, + struct lws_context_creation_info *info) +{ + /* master context has the global fd lookup array */ + context->lws_lookup = lws_zalloc(sizeof(struct lws *) * + context->max_fds, "esp32 lws_lookup"); + if (context->lws_lookup == NULL) { + lwsl_err("OOM on lws_lookup array for %d connections\n", + context->max_fds); + return 1; + } + + lwsl_notice(" mem: platform fd map: %5lu bytes\n", + (unsigned long)(sizeof(struct lws *) * context->max_fds)); + +#ifdef LWS_WITH_PLUGINS + if (info->plugin_dirs) + lws_plat_plugins_init(context, info->plugin_dirs); +#endif +#if defined(LWS_WITH_HTTP2) + /* override settings */ + context->set = lws_h2_defaults_esp32; +#endif + + return 0; +} + +LWS_VISIBLE void esp32_uvtimer_cb(TimerHandle_t t) +{ + struct timer_mapping *p = pvTimerGetTimerID(t); + + p->cb(p->t); +} + +/* helper functionality */ + +#include "romfs.h" +#include +#include +#include +#include +#include "soc/ledc_reg.h" +#include "driver/ledc.h" + +struct lws_esp32 lws_esp32 = { + .model = CONFIG_LWS_MODEL_NAME, + .serial = "unknown", + .region = WIFI_COUNTRY_US, // default to safest option +}; + +/* + * Group AP / Station State + */ + +enum lws_gapss { + LWS_GAPSS_INITIAL, /* just started up, init and move to LWS_GAPSS_SCAN */ + LWS_GAPSS_SCAN, /* + * Unconnected, scanning: AP known in one of the config + * slots -> configure it, start timeout + LWS_GAPSS_STAT, + * if no AP already up in same group with lower MAC, + * after a random period start up our AP (LWS_GAPSS_AP) + */ + LWS_GAPSS_AP, /* + * Trying to be the group AP... periodically do a scan + * LWS_GAPSS_AP_SCAN, faster and then slower + */ + LWS_GAPSS_AP_SCAN, /* + * doing a scan while trying to be the group AP... if + * we see a lower MAC being the AP for the same group + * AP, abandon being an AP and join that AP as a + * station + */ + LWS_GAPSS_STAT_GRP_AP, /* + * We have decided to join another group member who is + * being the AP, as its MAC is lower than ours. This + * is a stable state, but we still do periodic scans + * (LWS_GAPSS_STAT_GRP_AP_SCAN) and will always prefer + * an AP configured in a slot. + */ + LWS_GAPSS_STAT_GRP_AP_SCAN, + /* + * We have joined a group member who is doing the AP + * job... we want to check every now and then if a + * configured AP has appeared that we should better + * use instead. Otherwise stay in LWS_GAPSS_STAT_GRP_AP + */ + LWS_GAPSS_STAT, /* + * trying to connect to another non-group AP. If we + * don't get an IP within a timeout and retries, + * blacklist it and go back + */ + LWS_GAPSS_STAT_HAPPY, +}; + +static const char *gapss_str[] = { + "LWS_GAPSS_INITIAL", + "LWS_GAPSS_SCAN", + "LWS_GAPSS_AP", + "LWS_GAPSS_AP_SCAN", + "LWS_GAPSS_STAT_GRP_AP", + "LWS_GAPSS_STAT_GRP_AP_SCAN", + "LWS_GAPSS_STAT", + "LWS_GAPSS_STAT_HAPPY", +}; + +static romfs_t lws_esp32_romfs; +static TimerHandle_t leds_timer, scan_timer, debounce_timer +#if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) +, mdns_timer +#endif +; +static enum lws_gapss gapss = LWS_GAPSS_INITIAL; +static char bdown; + +#define GPIO_SW 14 + +struct esp32_file { + const struct inode *i; +}; + +static void lws_gapss_to(enum lws_gapss to) +{ + lwsl_notice("gapss from %s to %s\n", gapss_str[gapss], gapss_str[to]); + gapss = to; +} + +uint32_t lws_esp32_get_reboot_type(void) +{ + uint32_t *p = (uint32_t *)LWS_MAGIC_REBOOT_TYPE_ADS, val = *p; + nvs_handle nvh; + size_t s = 0; + int n = 0; + + ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh)); + if (nvs_get_blob(nvh, "ssl-pub.pem", NULL, &s) == ESP_OK) + n = 1; + if (nvs_get_blob(nvh, "ssl-pri.pem", NULL, &s) == ESP_OK) + n |= 2; + nvs_close(nvh); + + /* + * in the case the SSL certs are not there, don't require + * the button to be down to access all features. + */ + if (n != 3) + val = LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON; + + return val; +} + +static void render_ip(char *dest, int len, uint8_t *ip) +{ + snprintf(dest, len, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); +} + +void lws_esp32_restart_guided(uint32_t type) +{ + uint32_t *p_force_factory_magic = (uint32_t *)LWS_MAGIC_REBOOT_TYPE_ADS; + + lwsl_notice("%s: %x\n", __func__, type); + *p_force_factory_magic = type; + + esp_restart(); +} + +/* + * esp-idf goes crazy with zero length str nvs. Use this as a workaround + * to delete the key in that case. + */ + +esp_err_t lws_nvs_set_str(nvs_handle handle, const char* key, const char* value) +{ + if (*value) + return nvs_set_str(handle, key, value); + + return nvs_erase_key(handle, key); +} + +static wifi_scan_config_t scan_config = { + .ssid = 0, + .bssid = 0, + .channel = 0, + .show_hidden = true +}; + +static char scan_ongoing = 0, scan_timer_exists = 0; +static int try_slot = -1; + +static wifi_config_t config = { + .ap = { + .channel = 6, + .authmode = WIFI_AUTH_OPEN, + .max_connection = 1, + } }, sta_config = { + .sta = { + .bssid_set = 0, + } }; + +static void lws_esp32_scan_timer_cb(TimerHandle_t th) +{ + int n; + + lwsl_notice("%s\n", __func__); + scan_ongoing = 0; + n = esp_wifi_scan_start(&scan_config, false); + if (n != ESP_OK) + lwsl_err("scan start failed %d\n", n); +} + +#if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) + +void __attribute__(( weak )) +lws_group_member_event(int e, void *p) +{ +} + +void __attribute__(( weak )) +lws_get_iframe_size(int *w, int *h) +{ + *w = 320; + *h = 160; +} + +void lws_group_member_event_call(int e, void *p) +{ + lws_group_member_event(e, p); +} + +static int +get_txt_param(const char *txt, const char *param, char *result, int len) +{ + const char *p; + +again: + p = strstr(txt, param); + if (!p) { + *result = '\0'; + return 1; + } + + p += strlen(param); + if (*p != '=') { + txt = p; + goto again; + } + p++; + while (*p && *p != '&' && --len) + *result++ = *p++; + + *result = '\0'; + + return 0; +} + +static void lws_esp32_mdns_timer_cb(TimerHandle_t th) +{ + uint64_t now = time_in_microseconds(); + struct lws_group_member *p, **p1; + const mdns_result_t *r; + int n, m; + + if (!lws_esp32.mdns) + return; + n = mdns_query_end(lws_esp32.mdns); + + for (m = 0; m < n; m++) { + char ch = 0, group[16]; + + r = mdns_result_get(lws_esp32.mdns, m); + + get_txt_param(r->txt, "group", group, sizeof(group)); + if (strcmp(group, lws_esp32.group)) /* not our group */ { + lwsl_notice("group %s vs %s %s\n", + group, lws_esp32.group, r->txt); + continue; + } + + p = lws_esp32.first; + while (p) { + if (strcmp(r->host, p->host)) + goto next; + if (memcmp(&r->addr, &p->addr, sizeof(r->addr))) + goto next; + + p->last_seen = now; + break; +next: + p = p->next; + } + if (!p) { /* did not find */ + char temp[8]; + + p = lws_malloc(sizeof(*p), "group"); + if (!p) + continue; + strncpy(p->host, r->host, sizeof(p->host) - 1); + p->host[sizeof(p->host) - 1] = '\0'; + + get_txt_param(r->txt, "model", p->model, sizeof(p->model)); + get_txt_param(r->txt, "role", p->role, sizeof(p->role)); + get_txt_param(r->txt, "mac", p->mac, sizeof(p->mac)); + get_txt_param(r->txt, "width", temp, sizeof(temp)); + p->width = atoi(temp); + get_txt_param(r->txt, "height", temp, sizeof(temp)); + p->height = atoi(temp); + + memcpy(&p->addr, &r->addr, sizeof(p->addr)); + memcpy(&p->addrv6, &r->addrv6, sizeof(p->addrv6)); + p->last_seen = now; + p->flags = 0; + p->next = lws_esp32.first; + lws_esp32.first = p; + lws_esp32.extant_group_members++; + + lws_group_member_event_call(LWS_SYSTEM_GROUP_MEMBER_ADD, p); + } else { + if (memcmp(&p->addr, &r->addr, sizeof(p->addr))) { + memcpy(&p->addr, &r->addr, sizeof(p->addr)); + ch = 1; + } + if (memcmp(&p->addrv6, &r->addrv6, sizeof(p->addrv6))) { + memcpy(&p->addrv6, &r->addrv6, sizeof(p->addrv6)); + ch = 1; + } + if (ch) + lws_group_member_event_call(LWS_SYSTEM_GROUP_MEMBER_CHANGE, p); + } + } + + mdns_result_free(lws_esp32.mdns); + + /* garbage-collect group members not seen for too long */ + p1 = &lws_esp32.first; + while (*p1) { + p = *p1; + if (!(p->flags & LWS_GROUP_FLAG_SELF) && + now - p->last_seen > 60000000) { + lws_esp32.extant_group_members--; + *p1 = p->next; + + lws_group_member_event_call(LWS_SYSTEM_GROUP_MEMBER_REMOVE, p); + lws_free(p); + continue; + } + p1 = &(*p1)->next; + } + + mdns_query(lws_esp32.mdns, "_lwsgrmem", "_tcp", 0); + xTimerStart(mdns_timer, 0); +} +#endif + +void __attribute__(( weak )) +lws_esp32_button(int down) +{ +} + +void IRAM_ATTR +gpio_irq(void *arg) +{ + bdown ^= 1; + gpio_set_intr_type(GPIO_SW, GPIO_INTR_DISABLE); + xTimerStart(debounce_timer, 0); + + lws_esp32_button(bdown); +} + +static void lws_esp32_debounce_timer_cb(TimerHandle_t th) +{ + if (bdown) + gpio_set_intr_type(GPIO_SW, GPIO_INTR_POSEDGE); + else + gpio_set_intr_type(GPIO_SW, GPIO_INTR_NEGEDGE); +} + + +static int +start_scan() +{ + /* if no APs configured, no point... */ + + if (!lws_esp32.ssid[0][0] && + !lws_esp32.ssid[1][0] && + !lws_esp32.ssid[2][0] && + !lws_esp32.ssid[3][0]) + return 0; + + if (scan_timer_exists && !scan_ongoing) { + // lwsl_notice("Starting scan timer...\n"); + scan_ongoing = 1; + xTimerStart(scan_timer, 0); + } + + return 0; +} + + + +static void +end_scan() +{ + wifi_ap_record_t ap_records[10]; + uint16_t count_ap_records; + int n, m; + + count_ap_records = ARRAY_SIZE(ap_records); + if (esp_wifi_scan_get_ap_records(&count_ap_records, ap_records) != ESP_OK) { + lwsl_err("%s: failed\n", __func__); + return; + } + + if (!count_ap_records) + goto passthru; + + if (gapss != LWS_GAPSS_SCAN) { + lwsl_notice("ignoring scan as gapss %s\n", gapss_str[gapss]); + goto passthru; + } + + /* no point if no APs set up */ + if (!lws_esp32.ssid[0][0] && + !lws_esp32.ssid[1][0] && + !lws_esp32.ssid[2][0] && + !lws_esp32.ssid[3][0]) + goto passthru; + + lwsl_notice("checking %d scan records\n", count_ap_records); + + for (n = 0; n < 4; n++) { + + if (!lws_esp32.ssid[(n + try_slot + 1) & 3][0]) + continue; + + lwsl_notice("looking for %s\n", lws_esp32.ssid[(n + try_slot + 1) & 3]); + + /* this ssid appears in scan results? */ + + for (m = 0; m < count_ap_records; m++) { + // lwsl_notice(" %s\n", ap_records[m].ssid); + if (strcmp((char *)ap_records[m].ssid, lws_esp32.ssid[(n + try_slot + 1) & 3]) == 0) + goto hit; + } + + continue; + +hit: + m = (n + try_slot + 1) & 3; + try_slot = m; + lwsl_notice("Attempting connection with slot %d: %s:\n", m, + lws_esp32.ssid[m]); + /* set the ssid we last tried to connect to */ + strncpy(lws_esp32.active_ssid, lws_esp32.ssid[m], + sizeof(lws_esp32.active_ssid) - 1); + lws_esp32.active_ssid[sizeof(lws_esp32.active_ssid) - 1] = '\0'; + + strncpy((char *)sta_config.sta.ssid, lws_esp32.ssid[m], sizeof(sta_config.sta.ssid) - 1); + strncpy((char *)sta_config.sta.password, lws_esp32.password[m], sizeof(sta_config.sta.password) - 1); + + tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, (const char *)&config.ap.ssid[7]); + lws_gapss_to(LWS_GAPSS_STAT); + + esp_wifi_set_config(WIFI_IF_STA, &sta_config); + esp_wifi_connect(); + break; + } + + if (n == 4) + start_scan(); + +passthru: + if (lws_esp32.scan_consumer) + lws_esp32.scan_consumer(count_ap_records, ap_records, lws_esp32.scan_consumer_arg); + +} + +static void +lws_set_genled(int n) +{ + lws_esp32.genled_t = time_in_microseconds(); + lws_esp32.genled = n; +} + +int +lws_esp32_leds_network_indication(void) +{ + uint64_t us, r; + int n, fadein = 100, speed = 1199, div = 1, base = 0; + + r = time_in_microseconds(); + us = r - lws_esp32.genled_t; + + switch (lws_esp32.genled) { + case LWSESP32_GENLED__INIT: + lws_esp32.genled = LWSESP32_GENLED__LOST_NETWORK; + /* fallthru */ + case LWSESP32_GENLED__LOST_NETWORK: + fadein = us / 10000; /* 100 steps in 1s */ + if (fadein > 100) { + fadein = 100; + lws_esp32.genled = LWSESP32_GENLED__NO_NETWORK; + } + /* fallthru */ + case LWSESP32_GENLED__NO_NETWORK: + break; + case LWSESP32_GENLED__CONN_AP: + base = 4096; + speed = 933; + div = 2; + break; + case LWSESP32_GENLED__GOT_IP: + fadein = us / 10000; /* 100 steps in 1s */ + if (fadein > 100) { + fadein = 100; + lws_esp32.genled = LWSESP32_GENLED__OK; + } + fadein = 100 - fadein; /* we are fading out */ + /* fallthru */ + case LWSESP32_GENLED__OK: + if (lws_esp32.genled == LWSESP32_GENLED__OK) + return 0; + + base = 4096; + speed = 766; + div = 3; + break; + } + + n = base + (lws_esp32_sine_interp(r / speed) / div); + return (n * fadein) / 100; +} + +esp_err_t lws_esp32_event_passthru(void *ctx, system_event_t *event) +{ +#if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) + struct lws_group_member *mem; + int n; +#endif + char slot[8]; + nvs_handle nvh; + uint32_t use; + + switch((int)event->event_id) { + case SYSTEM_EVENT_STA_START: + //esp_wifi_connect(); +// break; + /* fallthru */ + case SYSTEM_EVENT_STA_DISCONNECTED: + lwsl_notice("SYSTEM_EVENT_STA_DISCONNECTED\n"); + lws_esp32.conn_ap = 0; + lws_esp32.inet = 0; + lws_esp32.sta_ip[0] = '\0'; + lws_esp32.sta_mask[0] = '\0'; + lws_esp32.sta_gw[0] = '\0'; + lws_gapss_to(LWS_GAPSS_SCAN); + if (lws_esp32.mdns) + mdns_service_remove_all(lws_esp32.mdns); + mdns_free(lws_esp32.mdns); + lws_esp32.mdns = NULL; + lws_set_genled(LWSESP32_GENLED__LOST_NETWORK); + start_scan(); + esp_wifi_connect(); + break; + + case SYSTEM_EVENT_STA_CONNECTED: + lws_esp32.conn_ap = 1; + lws_set_genled(LWSESP32_GENLED__CONN_AP); + break; + + case SYSTEM_EVENT_STA_GOT_IP: + lwsl_notice("SYSTEM_EVENT_STA_GOT_IP\n"); + + lws_esp32.inet = 1; + lws_set_genled(LWSESP32_GENLED__GOT_IP); + + render_ip(lws_esp32.sta_ip, sizeof(lws_esp32.sta_ip) - 1, + (uint8_t *)&event->event_info.got_ip.ip_info.ip); + render_ip(lws_esp32.sta_mask, sizeof(lws_esp32.sta_mask) - 1, + (uint8_t *)&event->event_info.got_ip.ip_info.netmask); + render_ip(lws_esp32.sta_gw, sizeof(lws_esp32.sta_gw) - 1, + (uint8_t *)&event->event_info.got_ip.ip_info.gw); + + if (!nvs_open("lws-station", NVS_READWRITE, &nvh)) { + lws_snprintf(slot, sizeof(slot) - 1, "%duse", try_slot); + use = 0; + nvs_get_u32(nvh, slot, &use); + nvs_set_u32(nvh, slot, use + 1); + nvs_commit(nvh); + nvs_close(nvh); + } + + lws_gapss_to(LWS_GAPSS_STAT_HAPPY); + +#if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) + n = mdns_init(TCPIP_ADAPTER_IF_STA, &lws_esp32.mdns); + if (!n) { + static char *txta[6]; + int w, h; + + mdns_set_hostname(lws_esp32.mdns, lws_esp32.hostname); + mdns_set_instance(lws_esp32.mdns, lws_esp32.group); + mdns_service_add(lws_esp32.mdns, "_lwsgrmem", "_tcp", 443); + if (txta[0]) + lws_free(txta[0]); + txta[0] = lws_malloc(32 * ARRAY_SIZE(txta), "group"); + if (!txta[0]) { + lwsl_notice("mdns OOM\n"); + break; + } + txta[1] = &txta[0][32]; + txta[2] = &txta[1][32]; + txta[3] = &txta[2][32]; + txta[4] = &txta[3][32]; + txta[5] = &txta[4][32]; + + lws_get_iframe_size(&w, &h); + + lws_snprintf(txta[0], 31, "model=%s", lws_esp32.model); + lws_snprintf(txta[1], 31, "group=%s", lws_esp32.group); + lws_snprintf(txta[2], 31, "role=%s", lws_esp32.role); + lws_snprintf(txta[3], 31, "mac=%s", lws_esp32.mac); + lws_snprintf(txta[4], 31, "width=%d", w); + lws_snprintf(txta[5], 31, "height=%d", h); + + mem = lws_esp32.first; + while (mem) { + if (mem->flags & 1) + break; + mem = mem->next; + } + + if (!mem) { + struct lws_group_member *mem = lws_malloc(sizeof(*mem), "group"); + if (mem) { + mem->last_seen = ~(uint64_t)0; + strcpy(mem->model, lws_esp32.model); + strcpy(mem->role, lws_esp32.role); + strcpy(mem->host, lws_esp32.hostname); + strcpy(mem->mac, lws_esp32.mac); + mem->flags = LWS_GROUP_FLAG_SELF; + lws_get_iframe_size(&mem->width, &mem->height); + memcpy(&mem->addr, &event->event_info.got_ip.ip_info.ip, + sizeof(mem->addr)); + memcpy(&mem->addrv6, &event->event_info.got_ip6.ip6_info.ip, + sizeof(mem->addrv6)); + mem->next = lws_esp32.first; + lws_esp32.first = mem; + lws_esp32.extant_group_members++; + + lws_group_member_event_call(LWS_SYSTEM_GROUP_MEMBER_ADD, mem); + } + } else { /* update our IP */ + memcpy(&mem->addr, &event->event_info.got_ip.ip_info.ip, + sizeof(mem->addr)); + memcpy(&mem->addrv6, &event->event_info.got_ip6.ip6_info.ip, + sizeof(mem->addrv6)); + lws_group_member_event_call(LWS_SYSTEM_GROUP_MEMBER_CHANGE, mem); + } + + + if (mdns_service_txt_set(lws_esp32.mdns, "_lwsgrmem", "_tcp", ARRAY_SIZE(txta), + (const char **)txta)) + lwsl_notice("txt set failed\n"); + } else + lwsl_err("unable to init mdns on STA: %d\n", n); + + mdns_query(lws_esp32.mdns, "_lwsgrmem", "_tcp", 0); + xTimerStart(mdns_timer, 0); +#endif + + lwsl_notice(" --- Got IP %s\n", lws_esp32.sta_ip); + break; + + case SYSTEM_EVENT_SCAN_DONE: + lwsl_notice("SYSTEM_EVENT_SCAN_DONE\n"); + end_scan(); + break; + + default: + break; + } + + return ESP_OK; +} + +static lws_fop_fd_t IRAM_ATTR +esp32_lws_fops_open(const struct lws_plat_file_ops *fops, const char *filename, + const char *vfs_path, lws_fop_flags_t *flags) +{ + struct esp32_file *f = malloc(sizeof(*f)); + lws_fop_fd_t fop_fd; + size_t len, csum; + + lwsl_notice("%s: %s\n", __func__, filename); + + if (!f) + return NULL; + f->i = romfs_get_info(lws_esp32_romfs, filename, &len, &csum); + if (!f->i) + goto bail; + + fop_fd = malloc(sizeof(*fop_fd)); + if (!fop_fd) + goto bail; + + fop_fd->fops = fops; + fop_fd->filesystem_priv = f; + fop_fd->mod_time = csum; + *flags |= LWS_FOP_FLAG_MOD_TIME_VALID; + fop_fd->flags = *flags; + + fop_fd->len = len; + fop_fd->pos = 0; + + return fop_fd; + +bail: + free(f); + + return NULL; +} + +static int IRAM_ATTR +esp32_lws_fops_close(lws_fop_fd_t *fop_fd) +{ + free((*fop_fd)->filesystem_priv); + free(*fop_fd); + + *fop_fd = NULL; + + return 0; +} +static lws_fileofs_t IRAM_ATTR +esp32_lws_fops_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset_from_cur_pos) +{ + fop_fd->pos += offset_from_cur_pos; + + if (fop_fd->pos > fop_fd->len) + fop_fd->pos = fop_fd->len; + + return 0; +} + +static int IRAM_ATTR +esp32_lws_fops_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, uint8_t *buf, + lws_filepos_t len) +{ + struct esp32_file *f = fop_fd->filesystem_priv; +#if 0 + if ((long)buf & 3) { + lwsl_err("misaligned buf\n"); + + return -1; + } +#endif + if (fop_fd->pos >= fop_fd->len) + return 0; + + if (len > fop_fd->len - fop_fd->pos) + len = fop_fd->len - fop_fd->pos; + + spi_flash_read((uint32_t)(char *)f->i + fop_fd->pos, buf, len); + + *amount = len; + fop_fd->pos += len; + + return 0; +} + +static const struct lws_plat_file_ops fops = { + .next = &fops_zip, + .LWS_FOP_OPEN = esp32_lws_fops_open, + .LWS_FOP_CLOSE = esp32_lws_fops_close, + .LWS_FOP_READ = esp32_lws_fops_read, + .LWS_FOP_SEEK_CUR = esp32_lws_fops_seek_cur, +}; + +int +lws_esp32_wlan_nvs_get(int retry) +{ + nvs_handle nvh; + char r[2], lws_esp32_force_ap = 0, slot[12]; + size_t s; + uint8_t mac[6]; + int n; + + esp_efuse_mac_get_default(mac); + mac[5] |= 1; /* match the AP MAC */ + snprintf(lws_esp32.serial, sizeof(lws_esp32.serial) - 1, "%02X%02X%02X", mac[3], mac[4], mac[5]); + snprintf(lws_esp32.mac, sizeof(lws_esp32.mac) - 1, "%02X%02X%02X%02X%02X%02X", mac[0], + mac[1], mac[2], mac[3], mac[4], mac[5]); + + ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh)); + + config.sta.ssid[0] = '\0'; + config.sta.password[0] = '\0'; + + for (n = 0; n < 4; n++) { + lws_snprintf(slot, sizeof(slot) - 1, "%dssid", n); + s = sizeof(lws_esp32.ssid[0]) - 1; + lws_esp32.ssid[n][0] = '\0'; + nvs_get_str(nvh, slot, lws_esp32.ssid[n], &s); + + lws_snprintf(slot, sizeof(slot) - 1, "%dpassword", n); + s = sizeof(lws_esp32.password[0]) - 1; + lws_esp32.password[n][0] = '\0'; + nvs_get_str(nvh, slot, lws_esp32.password[n], &s); + } + + s = sizeof(lws_esp32.serial) - 1; + if (nvs_get_str(nvh, "serial", lws_esp32.serial, &s) != ESP_OK) + lws_esp32_force_ap = 1; + else + snprintf((char *)config.ap.ssid, sizeof(config.ap.ssid) - 1, + "config-%s-%s", lws_esp32.model, lws_esp32.serial); + s = sizeof(lws_esp32.opts) - 1; + if (nvs_get_str(nvh, "opts", lws_esp32.opts, &s) != ESP_OK) + lws_esp32_force_ap = 1; + + s = sizeof(r); + if (nvs_get_str(nvh, "region", r, &s) != ESP_OK) + lws_esp32_force_ap = 1; + else + lws_esp32.region = atoi(r); + lws_esp32.access_pw[0] = '\0'; + nvs_get_str(nvh, "access_pw", lws_esp32.access_pw, &s); + + lws_esp32.group[0] = '\0'; + s = sizeof(lws_esp32.group); + nvs_get_str(nvh, "group", lws_esp32.group, &s); + + lws_esp32.role[0] = '\0'; + s = sizeof(lws_esp32.role); + nvs_get_str(nvh, "role", lws_esp32.role, &s); + + /* if group and role defined: group-role */ + if (lws_esp32.group[0] && lws_esp32.role[0]) + lws_snprintf(lws_esp32.hostname, sizeof(lws_esp32.hostname) - 1, + "%s-%s", lws_esp32.group, lws_esp32.role); + else /* otherwise model-serial */ + lws_snprintf(lws_esp32.hostname, sizeof(lws_esp32.hostname) - 1, + "%s-%s", lws_esp32.model, lws_esp32.serial); + + nvs_close(nvh); + + lws_gapss_to(LWS_GAPSS_SCAN); + start_scan(); + + return lws_esp32_force_ap; +} + + +void +lws_esp32_wlan_config(void) +{ + ledc_timer_config_t ledc_timer = { + .bit_num = LEDC_TIMER_13_BIT, + .freq_hz = 5000, + .speed_mode = LEDC_HIGH_SPEED_MODE, + .timer_num = LEDC_TIMER_0 + }; + int n; + + ledc_timer_config(&ledc_timer); + + lws_set_genled(LWSESP32_GENLED__INIT); + + /* user code needs to provide lws_esp32_leds_timer_cb */ + + leds_timer = xTimerCreate("lws_leds", pdMS_TO_TICKS(25), 1, NULL, + (TimerCallbackFunction_t)lws_esp32_leds_timer_cb); + scan_timer = xTimerCreate("lws_scan", pdMS_TO_TICKS(10000), 0, NULL, + (TimerCallbackFunction_t)lws_esp32_scan_timer_cb); + debounce_timer = xTimerCreate("lws_db", pdMS_TO_TICKS(100), 0, NULL, + (TimerCallbackFunction_t)lws_esp32_debounce_timer_cb); + +#if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) + mdns_timer = xTimerCreate("lws_mdns", pdMS_TO_TICKS(5000), 0, NULL, + (TimerCallbackFunction_t)lws_esp32_mdns_timer_cb); +#endif + scan_timer_exists = 1; + xTimerStart(leds_timer, 0); + + *(volatile uint32_t *)PERIPHS_IO_MUX_MTMS_U = FUNC_MTMS_GPIO14; + + gpio_output_set(0, 0, 0, (1 << GPIO_SW)); + + n = gpio_install_isr_service(0); + if (!n) { + gpio_config_t c; + + c.intr_type = GPIO_INTR_NEGEDGE; + c.mode = GPIO_MODE_INPUT; + c.pin_bit_mask = 1 << GPIO_SW; + c.pull_down_en = 0; + c.pull_up_en = 0; + gpio_config(&c); + + if (gpio_isr_handler_add(GPIO_SW, gpio_irq, NULL)) + lwsl_notice("isr handler add for 14 failed\n"); + } else + lwsl_notice("failed to install gpio isr service: %d\n", n); + + lws_esp32_wlan_nvs_get(0); + tcpip_adapter_init(); +} + +void +lws_esp32_wlan_start_ap(void) +{ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + + ESP_ERROR_CHECK( esp_wifi_init(&cfg)); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM)); + esp_wifi_set_country(lws_esp32.region); + + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_APSTA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_AP, &config) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &sta_config)); + ESP_ERROR_CHECK( esp_wifi_start()); + + esp_wifi_scan_start(&scan_config, false); + + if (sta_config.sta.ssid[0]) { + tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, (const char *)&config.ap.ssid[7]); + esp_wifi_set_auto_connect(1); + ESP_ERROR_CHECK( esp_wifi_connect()); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &sta_config)); + ESP_ERROR_CHECK( esp_wifi_connect()); + } +} + +void +lws_esp32_wlan_start_station(void) +{ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + + ESP_ERROR_CHECK( esp_wifi_init(&cfg)); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM)); + esp_wifi_set_country(lws_esp32.region); + + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &sta_config)); + + ESP_ERROR_CHECK( esp_wifi_start()); + + tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, (const char *)&config.ap.ssid[7]); + esp_wifi_set_auto_connect(1); + //ESP_ERROR_CHECK( esp_wifi_connect()); + + lws_esp32_scan_timer_cb(NULL); +} + +const esp_partition_t * +lws_esp_ota_get_boot_partition(void) +{ + const esp_partition_t *part = esp_ota_get_boot_partition(), *factory_part, *ota; + esp_image_header_t eih, ota_eih; + uint32_t *p_force_factory_magic = (uint32_t *)LWS_MAGIC_REBOOT_TYPE_ADS; + + /* confirm what we are told is the boot part is sane */ + spi_flash_read(part->address , &eih, sizeof(eih)); + factory_part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, + ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL); + ota = esp_partition_find_first(ESP_PARTITION_TYPE_APP, + ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL); + spi_flash_read(ota->address , &ota_eih, sizeof(ota_eih)); + + if (eih.spi_mode == 0xff || + *p_force_factory_magic == LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY || + *p_force_factory_magic == LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON + ) { + /* + * we believed we were going to boot OTA, but we fell + * back to FACTORY in the bootloader when we saw it + * had been erased. esp_ota_get_boot_partition() still + * says the OTA partition then even if we are in the + * factory partition right now. + */ + part = factory_part; + } + +#ifdef CONFIG_LWS_IS_FACTORY_APPLICATION + else + if (ota_eih.spi_mode != 0xff && + part->address != factory_part->address) { + uint8_t buf[4096]; + uint32_t n; + /* + * we are a FACTORY image running in an OTA slot... + * it means we were just written and need to copy + * ourselves into the FACTORY slot. + */ + lwsl_notice("Copying FACTORY update into place 0x%x len 0x%x\n", + factory_part->address, factory_part->size); + esp_task_wdt_feed(); + if (spi_flash_erase_range(factory_part->address, factory_part->size) != ESP_OK) { + lwsl_err("spi: Failed to erase\n"); + goto retry; + } + + for (n = 0; n < factory_part->size; n += sizeof(buf)) { + esp_task_wdt_feed(); + spi_flash_read(part->address + n , buf, sizeof(buf)); + if (spi_flash_write(factory_part->address + n, buf, sizeof(buf)) != ESP_OK) { + lwsl_err("spi: Failed to write\n"); + goto retry; + } + } + + /* destroy our OTA image header */ + spi_flash_erase_range(ota->address, 4096); + + /* + * with no viable OTA image, we will come back up in factory + * where the user can reload the OTA image + */ + lwsl_notice(" FACTORY copy successful, rebooting\n"); +retry: + esp_restart(); + } +#endif + + return part; +} + + +void +lws_esp32_set_creation_defaults(struct lws_context_creation_info *info) +{ + const esp_partition_t *part; + + memset(info, 0, sizeof(*info)); + + lws_set_log_level(63, lwsl_emit_syslog); + + part = lws_esp_ota_get_boot_partition(); + (void)part; + + info->port = 443; + info->fd_limit_per_thread = 10; + info->max_http_header_pool = 16; + info->max_http_header_data = 512; + info->pt_serv_buf_size = 2048; + info->keepalive_timeout = 30; + info->timeout_secs = 30; + info->simultaneous_ssl_restriction = 4; + info->options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | + LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + + info->ssl_cert_filepath = "ssl-pub.pem"; + info->ssl_private_key_filepath = "ssl-pri.pem"; +} + +int +lws_esp32_get_image_info(const esp_partition_t *part, struct lws_esp32_image *i, + char *json, int json_len) +{ + esp_image_segment_header_t eis; + esp_image_header_t eih; + uint32_t hdr; + + spi_flash_read(part->address , &eih, sizeof(eih)); + hdr = part->address + sizeof(eih); + + if (eih.magic != ESP_IMAGE_HEADER_MAGIC) + return 1; + + eis.data_len = 0; + while (eih.segment_count-- && eis.data_len != 0xffffffff) { + spi_flash_read(hdr, &eis, sizeof(eis)); + hdr += sizeof(eis) + eis.data_len; + } + hdr += (~hdr & 15) + 1; + + if (eih.hash_appended) + hdr += 0x20; + +// lwsl_notice("romfs estimated at 0x%x\n", hdr); + + i->romfs = hdr + 0x4; + spi_flash_read(hdr, &i->romfs_len, sizeof(i->romfs_len)); + i->json = i->romfs + i->romfs_len + 4; + spi_flash_read(i->json - 4, &i->json_len, sizeof(i->json_len)); + + if (i->json_len < json_len - 1) + json_len = i->json_len; + spi_flash_read(i->json, json, json_len); + json[json_len] = '\0'; + + return 0; +} + +struct lws_context * +lws_esp32_init(struct lws_context_creation_info *info, struct lws_vhost **pvh) +{ + const esp_partition_t *part = lws_esp_ota_get_boot_partition(); + struct lws_context *context; + struct lws_esp32_image i; + struct lws_vhost *vhost; + nvs_handle nvh; + char buf[512]; + size_t s; + int n; + + ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh)); + n = 0; + s = 1; + if (nvs_get_blob(nvh, "ssl-pub.pem", NULL, &s) == ESP_OK) + n = 1; + s = 1; + if (nvs_get_blob(nvh, "ssl-pri.pem", NULL, &s) == ESP_OK) + n |= 2; + nvs_close(nvh); + + if (n != 3) { + /* we are not configured for SSL yet... fall back to port 80 / http */ + info->port = 80; + info->options &= ~LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + lwsl_notice("No SSL certs... using port 80\n"); + } + + context = lws_create_context(info); + if (context == NULL) { + lwsl_err("Failed to create context\n"); + return NULL; + } + + lws_esp32_get_image_info(part, &i, buf, sizeof(buf) - 1); + + lws_esp32_romfs = (romfs_t)i.romfs; + if (!romfs_mount_check(lws_esp32_romfs)) { + lwsl_err("Failed to mount ROMFS at %p 0x%x\n", lws_esp32_romfs, i.romfs); + return NULL; + } + + lwsl_notice("ROMFS length %uKiB\n", i.romfs_len >> 10); + + puts(buf); + + /* set the lws vfs to use our romfs */ + + lws_set_fops(context, &fops); + + vhost = lws_create_vhost(context, info); + if (!vhost) + lwsl_err("Failed to create vhost\n"); + else + lws_init_vhost_client_ssl(info, vhost); + + if (pvh) + *pvh = vhost; + + lws_protocol_init(context); + + return context; +} + +static const uint16_t sineq16[] = { + 0x0000, 0x0191, 0x031e, 0x04a4, 0x061e, 0x0789, 0x08e2, 0x0a24, + 0x0b4e, 0x0c5c, 0x0d4b, 0x0e1a, 0x0ec6, 0x0f4d, 0x0faf, 0x0fea, +}; + +static uint16_t sine_lu(int n) +{ + switch ((n >> 4) & 3) { + case 1: + return 4096 + sineq16[n & 15]; + case 2: + return 4096 + sineq16[15 - (n & 15)]; + case 3: + return 4096 - sineq16[n & 15]; + default: + return 4096 - sineq16[15 - (n & 15)]; + } +} + +/* useful for sine led fade patterns */ + +uint16_t lws_esp32_sine_interp(int n) +{ + /* + * 2: quadrant + * 4: table entry in quadrant + * 4: interp (LSB) + * + * total 10 bits / 1024 steps per cycle + * + * + 0: 0 + * + 256: 4096 + * + 512: 8192 + * + 768: 4096 + * +1023: 0 + */ + + return (sine_lu(n >> 4) * (15 - (n & 15)) + + sine_lu((n >> 4) + 1) * (n & 15)) / 15; +} + diff -Nru libwebsockets-4.0.20/lib/plat/lws-plat-esp8266.c libwebsockets-2.4.2/lib/plat/lws-plat-esp8266.c --- libwebsockets-4.0.20/lib/plat/lws-plat-esp8266.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/lws-plat-esp8266.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,700 @@ +#include "private-libwebsockets.h" + +#include "ip_addr.h" + +/* forced into this because new espconn accepted callbacks carry no context ptr */ +static struct lws_context *hacky_context; +static unsigned int time_high, ot; + +/* + * included from libwebsockets.c for esp8266 builds + */ + +unsigned long long time_in_microseconds(void) +{ + unsigned int t = system_get_time(); + + if (ot > t) + time_high++; + ot = t; + + return (((long long)time_high) << 32) | t; +} + +int gettimeofday(struct timeval *tv, void *tz) +{ + unsigned long long t = time_in_microseconds(); + + tv->tv_sec = t / 1000000; + tv->tv_usec = t % 1000000; + + return 0; +} + +time_t time(time_t *tloc) +{ + unsigned long long t = time_in_microseconds(); + + if (tloc) + *tloc = t / 1000000; + + return 0; +} + +LWS_VISIBLE int +lws_get_random(struct lws_context *context, void *buf, int len) +{ +// return read(context->fd_random, (char *)buf, len); + return 0; +} + +LWS_VISIBLE int +lws_send_pipe_choked(struct lws *wsi) +{ + return wsi->pending_send_completion; +} + +LWS_VISIBLE struct lws * +wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd) +{ + int n; + + for (n = 0; n < context->max_fds; n++) + if (context->connpool[n] == fd) + return (struct lws *)context->connpool[n + context->max_fds]; + + return NULL; +} + +LWS_VISIBLE int +lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len) +{ + //lwsl_notice("%s: wsi %p: len %d\n", __func__, wsi, len); + + wsi->pending_send_completion++; + espconn_send(wsi->desc.sockfd, buf, len); + + return len; +} + +void abort(void) +{ + while(1) ; +} + +void exit(int n) +{ + abort(); +} + +void _sint(void *s) +{ +} + +LWS_VISIBLE int +insert_wsi(struct lws_context *context, struct lws *wsi) +{ + (void)context; + (void)wsi; + + return 0; +} + +LWS_VISIBLE int +delete_from_fd(struct lws_context *context, lws_sockfd_type fd) +{ + (void)context; + (void)fd; + + return 1; +} + +struct tm *localtime(const time_t *timep) +{ + return NULL; +} +struct tm *localtime_r(const time_t *timep, struct tm *t) +{ + return NULL; +} + +int atoi(const char *s) +{ + int n = 0; + + while (*s && (*s >= '0' && *s <= '9')) + n = (n * 10) + ((*s++) - '0'); + + return n; +} + +#undef isxdigit +int isxdigit(int c) +{ + if (c >= 'A' && c <= 'F') + return 1; + + if (c >= 'a' && c <= 'f') + return 1; + + if (c >= '0' && c <= '9') + return 1; + + return 0; +} + +int strcasecmp(const char *s1, const char *s2) +{ + char a, b; + while (*s1 && *s2) { + a = *s1++; + b = *s2++; + + if (a == b) + continue; + + if (a >= 'a' && a <= 'z') + a -= 'a' - 'A'; + if (b >= 'a' && b <= 'z') + b -= 'a' - 'A'; + + if (a != b) + return 1; + } + + return 0; +} + +LWS_VISIBLE int +lws_poll_listen_fd(struct lws_pollfd *fd) +{ + return 0; +} + +LWS_VISIBLE void +lws_cancel_service_pt(struct lws *wsi) +{ +} + +LWS_VISIBLE void +lws_cancel_service(struct lws_context *context) +{ +} + +LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) +{ + extern void output_redirect(const char *str); + output_redirect(line); +} + +LWS_VISIBLE LWS_EXTERN int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + return 0; +} + +LWS_VISIBLE int +lws_plat_check_connection_error(struct lws *wsi) +{ + return 0; +} + +LWS_VISIBLE int +lws_plat_service(struct lws_context *context, int timeout_ms) +{ +// return _lws_plat_service_tsi(context, timeout_ms, 0); + return 0; +} + +static int +esp8266_find_free_conn(struct lws_context *context) +{ + int n; + + for (n = 0; n < context->max_fds; n++) + if (!context->connpool[n]) { + lwsl_info(" using connpool %d\n", n); + return n; + } + + lwsl_err("%s: no free conns\n", __func__); + + return -1; +} + +lws_sockfd_type +esp8266_create_tcp_listen_socket(struct lws_vhost *vh) +{ + int n = esp8266_find_free_conn(vh->context); + struct espconn *conn; + + if (n < 0) + return NULL; + + conn = lws_zalloc(sizeof *conn, "listen skt"); + if (!conn) + return NULL; + + vh->context->connpool[n] = conn; + + conn->type = ESPCONN_TCP; + conn->state = ESPCONN_NONE; + conn->proto.tcp = &vh->tcp; + + return conn; +} + +const char * +lws_plat_get_peer_simple(struct lws *wsi, char *name, int namelen) +{ + unsigned char *p = wsi->desc.sockfd->proto.tcp->remote_ip; + + lws_snprintf(name, namelen, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]); + + return name; +} + +LWS_VISIBLE int +lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len) +{ + //lwsl_notice("%s\n", __func__); + + if (!wsi->context->rxd) + return 0; + + if (len < wsi->context->rxd_len) + lwsl_err("trunc read (%d vs %d)\n", len, wsi->context->rxd_len); + else + len = wsi->context->rxd_len; + + ets_memcpy(buf, wsi->context->rxd, len); + + wsi->context->rxd = NULL; + + return len; +} + +static void +cb_1Hz(void *arg) +{ + struct lws_context *context = arg; + struct lws_context_per_thread *pt = &context->pt[0]; + struct lws *wsi; + struct lws_pollfd *pollfd; + int n; + + /* Service any ah that has pending rx */ + for (n = 0; n < context->max_http_header_pool; n++) + if (pt->ah_pool[n].rxpos != pt->ah_pool[n].rxlen) { + wsi = pt->ah_pool[n].wsi; + pollfd = &pt->fds[wsi->position_in_fds_table]; + if (pollfd->events & LWS_POLLIN) { + pollfd->revents |= LWS_POLLIN; + lws_service_fd(context, pollfd); + } + } + + /* handle timeouts */ + + lws_service_fd(context, NULL); +} + +static void +esp8266_cb_rx(void *arg, char *data, unsigned short len) +{ + struct espconn *conn = arg; + struct lws *wsi = conn->reverse; + struct lws_context_per_thread *pt = &wsi->context->pt[0]; + struct lws_pollfd pollfd; + int n = 0; + + /* + * if we're doing HTTP headers, and we have no ah, check if there is + * a free ah, if not, have to buffer it + */ + if (!wsi->hdr_parsing_completed && !wsi->u.hdr.ah) { + for (n = 0; n < wsi->context->max_http_header_pool; n++) + if (!pt->ah_pool[n].in_use) + break; + + n = n == wsi->context->max_http_header_pool; + } + + if (!(pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN) || n) { + wsi->premature_rx = realloc(wsi->premature_rx, + wsi->prem_rx_size + len); + if (!wsi->premature_rx) + return; + os_memcpy((char *)wsi->premature_rx + wsi->prem_rx_size, data, len); + wsi->prem_rx_size += len; + // lwsl_notice("%s: wsi %p: len %d BUFFERING\n", __func__, wsi, len); + + if (n) /* we know it will fail, but we will get on the wait list */ + n = lws_header_table_attach(wsi, 0); + + (void)n; + return; + } + + //lwsl_err("%s: wsi %p. len %d\n", __func__, wsi, len); + + pollfd.fd = arg; + pollfd.events = LWS_POLLIN; + pollfd.revents = LWS_POLLIN; + + wsi->context->rxd = data; + wsi->context->rxd_len = len; + + lws_service_fd(lws_get_context(wsi), &pollfd); + +} + +static void +esp8266_cb_sent(void *arg) +{ + struct espconn *conn = arg; + struct lws *wsi = conn->reverse; + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + +// lwsl_err("%s: wsi %p (psc %d) wsi->position_in_fds_table=%d\n", __func__, wsi, wsi->pending_send_completion, wsi->position_in_fds_table); + + wsi->pending_send_completion--; + if (wsi->close_is_pending_send_completion && + !wsi->pending_send_completion && + !lws_partial_buffered(wsi)) { + lwsl_notice("doing delayed close\n"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + } + + if (pt->fds[wsi->position_in_fds_table].events & LWS_POLLOUT) { + struct lws_pollfd pollfd; + + pollfd.fd = arg; + pollfd.events = LWS_POLLOUT; + pollfd.revents = LWS_POLLOUT; + +// lwsl_notice("informing POLLOUT\n"); + + lws_service_fd(lws_get_context(wsi), &pollfd); + } +} + +static void +esp8266_cb_disconnected(void *arg) +{ + struct espconn *conn = arg; + struct lws *wsi = conn->reverse; + int n; + + lwsl_notice("%s: %p\n", __func__, wsi); + + for (n = 0; n < hacky_context->max_fds; n++) + if (hacky_context->connpool[n] == arg) { + hacky_context->connpool[n] = NULL; + lwsl_info(" freed connpool %d\n", n); + } + + if (wsi) { + conn->reverse = NULL; + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + lwsl_notice("closed ok\n"); + } +} + +static void +esp8266_cb_recon(void *arg, signed char err) +{ + struct espconn *conn = arg; + + lwsl_err("%s: wsi %p. err %d\n", __func__, conn->reverse, err); + + conn->state = ESPCONN_CLOSE; + + esp8266_cb_disconnected(arg); +} + +/* + * there is no reliable indication of which listen socket we were accepted on. + */ + +static void +esp8266_cb_connect(void *arg) +{ + struct espconn *cs = arg; +// struct ip_addr *ipa = (struct ip_addr *)cs->proto.tcp->remote_ip; + struct lws_vhost *vh = hacky_context->vhost_list; +// struct ip_info info; + struct lws *wsi; + int n; + + lwsl_notice("%s: (wsi coming): %p\n", __func__, cs->reverse); +#if 0 + wifi_get_ip_info(0, &info); + if (ip_addr_netcmp(ipa, &info.ip, &info.netmask)) { + /* we are on the same subnet as the AP, ie, connected to AP */ + while (vh && strcmp(vh->name, "ap")) + vh = vh->vhost_next; + } else + while (vh && !strcmp(vh->name, "ap")) + vh = vh->vhost_next; + + if (!vh) + goto bail; +#endif + n = esp8266_find_free_conn(hacky_context); + if (n < 0) + goto bail; + + hacky_context->connpool[n] = cs; + + espconn_recv_hold(cs); + + wsi = lws_adopt_socket_vhost(vh, cs); + if (!wsi) + goto bail; + + lwsl_err("%s: wsi %p (using free_conn %d): vh %s\n", __func__, wsi, n, vh->name); + + espconn_regist_recvcb(cs, esp8266_cb_rx); + espconn_regist_reconcb(cs, esp8266_cb_recon); + espconn_regist_disconcb(cs, esp8266_cb_disconnected); + espconn_regist_sentcb(cs, esp8266_cb_sent); + + espconn_set_opt(cs, ESPCONN_NODELAY | ESPCONN_REUSEADDR); + espconn_regist_time(cs, 7200, 1); + + return; + +bail: + lwsl_err("%s: bailed]n", __func__); + espconn_disconnect(cs); +} + +void +esp8266_tcp_stream_bind(lws_sockfd_type fd, int port, struct lws *wsi) +{ + fd->proto.tcp->local_port = port; + fd->reverse = wsi; + + hacky_context = wsi->context; + + espconn_regist_connectcb(fd, esp8266_cb_connect); + /* hmmm it means, listen() + accept() */ + espconn_accept(fd); + + espconn_tcp_set_max_con_allow(fd, 10); +} + +void +esp8266_tcp_stream_accept(lws_sockfd_type fd, struct lws *wsi) +{ + int n; + + fd->reverse = wsi; + + for (n = 0; n < wsi->context->max_fds ; n++) + if (wsi->context->connpool[n] == wsi->desc.sockfd) + wsi->position_in_fds_table = n; +} + +LWS_VISIBLE int +lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd) +{ + return 0; +} + +LWS_VISIBLE void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info) +{ +} + +LWS_VISIBLE int +lws_plat_context_early_init(void) +{ + espconn_tcp_set_max_con(12); + + return 0; +} + +LWS_VISIBLE void +lws_plat_context_early_destroy(struct lws_context *context) +{ +} + +LWS_VISIBLE void +lws_plat_context_late_destroy(struct lws_context *context) +{ +#if 0 + struct lws_context_per_thread *pt = &context->pt[0]; + int m = context->count_threads; + + if (context->lws_lookup) + lws_free(context->lws_lookup); + + while (m--) { + close(pt->dummy_pipe_fds[0]); + close(pt->dummy_pipe_fds[1]); + pt++; + } +#endif +// close(context->fd_random); +} + +/* cast a struct sockaddr_in6 * into addr for ipv6 */ + +LWS_VISIBLE int +lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, + size_t addrlen) +{ + return 0; +} + +LWS_VISIBLE void +lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + context->connpool[wsi->position_in_fds_table + context->max_fds] = (lws_sockfd_type)wsi; + wsi->desc.sockfd->reverse = wsi; + pt->fds_count++; +} + +LWS_VISIBLE void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int n; + + for (n = 0; n < wsi->context->max_fds; n++) + if (wsi->context->connpool[n] == wsi->desc.sockfd) { + wsi->context->connpool[n] = NULL; + wsi->context->connpool[n + wsi->context->max_fds] = NULL; + lwsl_notice(" freed connpool %d\n", n); + } + + wsi->desc.sockfd->reverse = NULL; + pt->fds_count--; +} + +LWS_VISIBLE void +lws_plat_service_periodic(struct lws_context *context) +{ +} + +LWS_VISIBLE int +lws_plat_change_pollfd(struct lws_context *context, + struct lws *wsi, struct lws_pollfd *pfd) +{ + void *p; + + //lwsl_notice("%s: %p: wsi->pift=%d, events %d\n", + // __func__, wsi, wsi->position_in_fds_table, pfd->events); + + if (pfd->events & LWS_POLLIN) { + if (wsi->premature_rx) { + lwsl_notice("replaying buffered rx: wsi %p\n", wsi); + p = wsi->premature_rx; + wsi->premature_rx = NULL; + esp8266_cb_rx(wsi->desc.sockfd, + (char *)p + wsi->prem_rx_pos, + wsi->prem_rx_size - wsi->prem_rx_pos); + wsi->prem_rx_size = 0; + wsi->prem_rx_pos = 0; + lws_free(p); + } + if (espconn_recv_unhold(wsi->desc.sockfd) < 0) + return -1; + } else + if (espconn_recv_hold(wsi->desc.sockfd) < 0) + return -1; + + if (!(pfd->events & LWS_POLLOUT)) + return 0; + + if (!wsi->pending_send_completion) { + pfd->revents |= LWS_POLLOUT; + +// lwsl_notice("doing POLLOUT\n"); + lws_service_fd(lws_get_context(wsi), pfd); + } //else + //lwsl_notice("pending sc\n"); + + return 0; +} + +LWS_VISIBLE const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) +{ +// return inet_ntop(af, src, dst, cnt); + return 0; +} + +LWS_VISIBLE int +lws_plat_inet_pton(int af, const char *src, void *dst) +{ + //return inet_pton(af, src, dst); + return 1; +} + +LWS_VISIBLE int +lws_plat_init(struct lws_context *context, + struct lws_context_creation_info *info) +{ +// struct lws_context_per_thread *pt = &context->pt[0]; +// int n = context->count_threads, fd; + + /* master context has the global fd lookup array */ + context->connpool = lws_zalloc(sizeof(struct espconn *) * + context->max_fds * 2, "connpool"); + if (context->connpool == NULL) { + lwsl_err("OOM on lws_lookup array for %d connections\n", + context->max_fds); + return 1; + } + + lwsl_notice(" mem: platform fd map: %5u bytes\n", + sizeof(struct espconn *) * context->max_fds); +// fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY); + +// context->fd_random = fd; +// if (context->fd_random < 0) { +// lwsl_err("Unable to open random device %s %d\n", +// SYSTEM_RANDOM_FILEPATH, context->fd_random); +// return 1; +// } + + os_memset(&context->to_timer, 0, sizeof(os_timer_t)); + os_timer_disarm(&context->to_timer); + os_timer_setfn(&context->to_timer, (os_timer_func_t *)cb_1Hz, context); + os_timer_arm(&context->to_timer, 1000, 1); + + if (!lws_libev_init_fd_table(context) && + !lws_libuv_init_fd_table(context) && + !lws_libevent_init_fd_table(context)) { + /* otherwise libev handled it instead */ +#if 0 + while (n--) { + if (pipe(pt->dummy_pipe_fds)) { + lwsl_err("Unable to create pipe\n"); + return 1; + } + + /* use the read end of pipe as first item */ + pt->fds[0].fd = pt->dummy_pipe_fds[0]; + pt->fds[0].events = LWS_POLLIN; + pt->fds[0].revents = 0; + pt->fds_count = 1; + pt++; + } +#endif + } + + +#ifdef LWS_WITH_PLUGINS + if (info->plugin_dirs) + lws_plat_plugins_init(context, info->plugin_dirs); +#endif + + return 0; +} diff -Nru libwebsockets-4.0.20/lib/plat/lws-plat-optee.c libwebsockets-2.4.2/lib/plat/lws-plat-optee.c --- libwebsockets-4.0.20/lib/plat/lws-plat-optee.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/lws-plat-optee.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,330 @@ +#include "private-libwebsockets.h" + +/* + * included from libwebsockets.c for OPTEE builds + */ + +void TEE_GenerateRandom(void *randomBuffer, uint32_t randomBufferLen); + +unsigned long long time_in_microseconds(void) +{ + return ((unsigned long long)time(NULL)) * 1000000; +} +#if 0 +LWS_VISIBLE int +lws_get_random(struct lws_context *context, void *buf, int len) +{ + TEE_GenerateRandom(buf, len); + + return len; +} +#endif +LWS_VISIBLE int +lws_send_pipe_choked(struct lws *wsi) +{ +#if 0 + struct lws_pollfd fds; + + /* treat the fact we got a truncated send pending as if we're choked */ + if (wsi->trunc_len) + return 1; + + fds.fd = wsi->desc.sockfd; + fds.events = POLLOUT; + fds.revents = 0; + + if (poll(&fds, 1, 0) != 1) + return 1; + + if ((fds.revents & POLLOUT) == 0) + return 1; +#endif + /* okay to send another packet without blocking */ + + return 0; +} + +LWS_VISIBLE int +lws_poll_listen_fd(struct lws_pollfd *fd) +{ +// return poll(fd, 1, 0); + + return 0; +} + +LWS_VISIBLE void +lws_cancel_service_pt(struct lws *wsi) +{ +#if 0 + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char buf = 0; + + if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1) + lwsl_err("Cannot write to dummy pipe"); +#endif +} + +LWS_VISIBLE void +lws_cancel_service(struct lws_context *context) +{ +#if 0 + struct lws_context_per_thread *pt = &context->pt[0]; + char buf = 0, m = context->count_threads; + + while (m--) { + if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1) + lwsl_err("Cannot write to dummy pipe"); + pt++; + } +#endif +} +#if 0 +LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) +{ + IMSG("%d: %s\n", level, line); +} +#endif + +LWS_VISIBLE LWS_EXTERN int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt; + int n = -1, m, c; + //char buf; + + /* stay dead once we are dead */ + + if (!context || !context->vhost_list) + return 1; + + pt = &context->pt[tsi]; + + if (timeout_ms < 0) + goto faked_service; + + if (!context->service_tid_detected) { + struct lws _lws; + + memset(&_lws, 0, sizeof(_lws)); + _lws.context = context; + + context->service_tid_detected = + context->vhost_list->protocols[0].callback( + &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + context->service_tid = context->service_tid_detected; + context->service_tid_detected = 1; + } + + /* + * is there anybody with pending stuff that needs service forcing? + */ + if (!lws_service_adjust_timeout(context, 1, tsi)) { + lwsl_notice("%s: doing forced service\n", __func__); + /* -1 timeout means just do forced service */ + _lws_plat_service_tsi(context, -1, pt->tid); + /* still somebody left who wants forced service? */ + if (!lws_service_adjust_timeout(context, 1, pt->tid)) + /* yes... come back again quickly */ + timeout_ms = 0; + } +#if 1 + n = poll(pt->fds, pt->fds_count, timeout_ms); + +#ifdef LWS_OPENSSL_SUPPORT + if (!pt->rx_draining_ext_list && + !lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) { +#else + if (!pt->rx_draining_ext_list && !n) /* poll timeout */ { +#endif + lws_service_fd_tsi(context, NULL, tsi); + return 0; + } +#endif +faked_service: + m = lws_service_flag_pending(context, tsi); + if (m) + c = -1; /* unknown limit */ + else + if (n < 0) { + if (LWS_ERRNO != LWS_EINTR) + return -1; + return 0; + } else + c = n; + + /* any socket with events to service? */ + for (n = 0; n < pt->fds_count && c; n++) { + if (!pt->fds[n].revents) + continue; + + c--; +#if 0 + if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) { + if (read(pt->fds[n].fd, &buf, 1) != 1) + lwsl_err("Cannot read from dummy pipe."); + continue; + } +#endif + m = lws_service_fd_tsi(context, &pt->fds[n], tsi); + if (m < 0) + return -1; + /* if something closed, retry this slot */ + if (m) + n--; + } + + return 0; +} + +LWS_VISIBLE int +lws_plat_check_connection_error(struct lws *wsi) +{ + return 0; +} + +LWS_VISIBLE int +lws_plat_service(struct lws_context *context, int timeout_ms) +{ + return _lws_plat_service_tsi(context, timeout_ms, 0); +} + +LWS_VISIBLE int +lws_plat_set_socket_options(struct lws_vhost *vhost, int fd) +{ + return 0; +} + +LWS_VISIBLE void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info) +{ +} + +LWS_VISIBLE int +lws_plat_context_early_init(void) +{ + return 0; +} + +LWS_VISIBLE void +lws_plat_context_early_destroy(struct lws_context *context) +{ +} + +LWS_VISIBLE void +lws_plat_context_late_destroy(struct lws_context *context) +{ + if (context->lws_lookup) + lws_free(context->lws_lookup); +} + +/* cast a struct sockaddr_in6 * into addr for ipv6 */ + +LWS_VISIBLE int +lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, + size_t addrlen) +{ + return -1; +} + +LWS_VISIBLE void +lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + pt->fds[pt->fds_count++].revents = 0; +} + +LWS_VISIBLE void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + pt->fds_count--; +} + +LWS_VISIBLE void +lws_plat_service_periodic(struct lws_context *context) +{ +} + +LWS_VISIBLE int +lws_plat_change_pollfd(struct lws_context *context, + struct lws *wsi, struct lws_pollfd *pfd) +{ + return 0; +} + +LWS_VISIBLE const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) +{ + //return inet_ntop(af, src, dst, cnt); + return "lws_plat_inet_ntop"; +} + +LWS_VISIBLE int +lws_plat_inet_pton(int af, const char *src, void *dst) +{ + //return inet_pton(af, src, dst); + return 1; +} + +LWS_VISIBLE lws_fop_fd_t +_lws_plat_file_open(lws_plat_file_open(struct lws_plat_file_ops *fops, + const char *filename, lws_fop_flags_t *flags) +{ + return NULL; +} + +LWS_VISIBLE int +_lws_plat_file_close(lws_fop_fd_t *fop_fd) +{ + return 0; +} + +LWS_VISIBLE lws_fileofs_t +_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + return 0; +} + +LWS_VISIBLE int +_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + + return 0; +} + +LWS_VISIBLE int +_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + + return 0; +} + + +LWS_VISIBLE int +lws_plat_init(struct lws_context *context, + struct lws_context_creation_info *info) +{ + /* master context has the global fd lookup array */ + context->lws_lookup = lws_zalloc(sizeof(struct lws *) * + context->max_fds, "lws_lookup"); + if (context->lws_lookup == NULL) { + lwsl_err("OOM on lws_lookup array for %d connections\n", + context->max_fds); + return 1; + } + + lwsl_notice(" mem: platform fd map: %5lu bytes\n", + (long)sizeof(struct lws *) * context->max_fds); + +#ifdef LWS_WITH_PLUGINS + if (info->plugin_dirs) + lws_plat_plugins_init(context, info->plugin_dirs); +#endif + + return 0; +} diff -Nru libwebsockets-4.0.20/lib/plat/lws-plat-unix.c libwebsockets-2.4.2/lib/plat/lws-plat-unix.c --- libwebsockets-4.0.20/lib/plat/lws-plat-unix.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/lws-plat-unix.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,850 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#include +#include + +#ifdef LWS_WITH_PLUGINS +#include +#endif +#include + +unsigned long long time_in_microseconds(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec; +} + +LWS_VISIBLE int +lws_get_random(struct lws_context *context, void *buf, int len) +{ + return read(context->fd_random, (char *)buf, len); +} + +LWS_VISIBLE int +lws_send_pipe_choked(struct lws *wsi) +{ + struct lws_pollfd fds; + struct lws *wsi_eff = wsi; + +#if defined(LWS_WITH_HTTP2) + wsi_eff = lws_get_network_wsi(wsi); +#endif + /* treat the fact we got a truncated send pending as if we're choked */ + if (wsi_eff->trunc_len) + return 1; + + fds.fd = wsi_eff->desc.sockfd; + fds.events = POLLOUT; + fds.revents = 0; + + if (poll(&fds, 1, 0) != 1) + return 1; + + if ((fds.revents & POLLOUT) == 0) + return 1; + + /* okay to send another packet without blocking */ + + return 0; +} + +LWS_VISIBLE int +lws_poll_listen_fd(struct lws_pollfd *fd) +{ + return poll(fd, 1, 0); +} + +LWS_VISIBLE void +lws_cancel_service_pt(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char buf = 0; + + if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1) + lwsl_err("Cannot write to dummy pipe"); +} + +LWS_VISIBLE void +lws_cancel_service(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + char buf = 0, m = context->count_threads; + + while (m--) { + if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1) + lwsl_err("Cannot write to dummy pipe"); + pt++; + } +} + +LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) +{ + int syslog_level = LOG_DEBUG; + + switch (level) { + case LLL_ERR: + syslog_level = LOG_ERR; + break; + case LLL_WARN: + syslog_level = LOG_WARNING; + break; + case LLL_NOTICE: + syslog_level = LOG_NOTICE; + break; + case LLL_INFO: + syslog_level = LOG_INFO; + break; + } + syslog(syslog_level, "%s", line); +} + +LWS_VISIBLE LWS_EXTERN int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt; + int n = -1, m, c; + char buf; + + /* stay dead once we are dead */ + + if (!context || !context->vhost_list) + return 1; + + pt = &context->pt[tsi]; + + lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1); + + if (timeout_ms < 0) + goto faked_service; + + lws_libev_run(context, tsi); + lws_libuv_run(context, tsi); + lws_libevent_run(context, tsi); + + if (!context->service_tid_detected) { + struct lws _lws; + + memset(&_lws, 0, sizeof(_lws)); + _lws.context = context; + + context->service_tid_detected = + context->vhost_list->protocols[0].callback( + &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + context->service_tid = context->service_tid_detected; + context->service_tid_detected = 1; + } + + /* + * is there anybody with pending stuff that needs service forcing? + */ + if (!lws_service_adjust_timeout(context, 1, tsi)) { + /* -1 timeout means just do forced service */ + _lws_plat_service_tsi(context, -1, pt->tid); + /* still somebody left who wants forced service? */ + if (!lws_service_adjust_timeout(context, 1, pt->tid)) + /* yes... come back again quickly */ + timeout_ms = 0; + } + + n = poll(pt->fds, pt->fds_count, timeout_ms); + +#ifdef LWS_OPENSSL_SUPPORT + if (!n && !pt->rx_draining_ext_list && + !lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) { +#else + if (!pt->rx_draining_ext_list && !n) /* poll timeout */ { +#endif + lws_service_fd_tsi(context, NULL, tsi); + return 0; + } + +faked_service: + m = lws_service_flag_pending(context, tsi); + if (m) + c = -1; /* unknown limit */ + else + if (n < 0) { + if (LWS_ERRNO != LWS_EINTR) + return -1; + return 0; + } else + c = n; + + /* any socket with events to service? */ + for (n = 0; n < pt->fds_count && c; n++) { + if (!pt->fds[n].revents) + continue; + + c--; + + if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) { + if (read(pt->fds[n].fd, &buf, 1) != 1) + lwsl_err("Cannot read from dummy pipe."); + continue; + } + + m = lws_service_fd_tsi(context, &pt->fds[n], tsi); + if (m < 0) + return -1; + /* if something closed, retry this slot */ + if (m) + n--; + } + + return 0; +} + +LWS_VISIBLE int +lws_plat_check_connection_error(struct lws *wsi) +{ + return 0; +} + +LWS_VISIBLE int +lws_plat_service(struct lws_context *context, int timeout_ms) +{ + return _lws_plat_service_tsi(context, timeout_ms, 0); +} + +LWS_VISIBLE int +lws_plat_set_socket_options(struct lws_vhost *vhost, int fd) +{ + int optval = 1; + socklen_t optlen = sizeof(optval); + +#if defined(__APPLE__) || \ + defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) || \ + defined(__HAIKU__) + struct protoent *tcp_proto; +#endif + + if (vhost->ka_time) { + /* enable keepalive on this socket */ + optval = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const void *)&optval, optlen) < 0) + return 1; + +#if defined(__APPLE__) || \ + defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || \ + defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun) || \ + defined(__HAIKU__) + + /* + * didn't find a way to set these per-socket, need to + * tune kernel systemwide values + */ +#else + /* set the keepalive conditions we want on it too */ + optval = vhost->ka_time; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, + (const void *)&optval, optlen) < 0) + return 1; + + optval = vhost->ka_interval; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, + (const void *)&optval, optlen) < 0) + return 1; + + optval = vhost->ka_probes; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, + (const void *)&optval, optlen) < 0) + return 1; +#endif + } + +#if defined(SO_BINDTODEVICE) + if (vhost->bind_iface && vhost->iface) { + lwsl_info("binding listen skt to %s using SO_BINDTODEVICE\n", vhost->iface); + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, vhost->iface, + strlen(vhost->iface)) < 0) { + lwsl_warn("Failed to bind to device %s\n", vhost->iface); + return 1; + } + } +#endif + + /* Disable Nagle */ + optval = 1; +#if defined (__sun) + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0) + return 1; +#elif !defined(__APPLE__) && \ + !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && \ + !defined(__NetBSD__) && \ + !defined(__OpenBSD__) && \ + !defined(__HAIKU__) + if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0) + return 1; +#else + tcp_proto = getprotobyname("TCP"); + if (setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen) < 0) + return 1; +#endif + + /* We are nonblocking... */ + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + return 1; + + return 0; +} + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) +static void +_lws_plat_apply_caps(int mode, cap_value_t *cv, int count) +{ + cap_t caps; + + if (!count) + return; + + caps = cap_get_proc(); + + cap_set_flag(caps, mode, count, cv, CAP_SET); + cap_set_proc(caps); + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + cap_free(caps); +} +#endif + +LWS_VISIBLE void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info) +{ +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + int n; +#endif + + if (info->gid && info->gid != -1) + if (setgid(info->gid)) + lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO)); + + if (info->uid && info->uid != -1) { + struct passwd *p = getpwuid(info->uid); + + if (p) { + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + _lws_plat_apply_caps(CAP_PERMITTED, info->caps, info->count_caps); +#endif + + initgroups(p->pw_name, info->gid); + if (setuid(info->uid)) + lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO)); + else + lwsl_notice("Set privs to user '%s'\n", p->pw_name); + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + _lws_plat_apply_caps(CAP_EFFECTIVE, info->caps, info->count_caps); + + if (info->count_caps) + for (n = 0; n < info->count_caps; n++) + lwsl_notice(" RETAINING CAPABILITY %d\n", (int)info->caps[n]); +#endif + + } else + lwsl_warn("getpwuid: unable to find uid %d", info->uid); + } +} + +#ifdef LWS_WITH_PLUGINS + +#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0 + +/* libuv.c implements these in a cross-platform way */ + +#else + +static int filter(const struct dirent *ent) +{ + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + return 0; + + return 1; +} + +LWS_VISIBLE int +lws_plat_plugins_init(struct lws_context * context, const char * const *d) +{ + struct lws_plugin_capability lcaps; + struct lws_plugin *plugin; + lws_plugin_init_func initfunc; + struct dirent **namelist; + int n, i, m, ret = 0; + char path[256]; + void *l; + + lwsl_notice(" Plugins:\n"); + + while (d && *d) { + n = scandir(*d, &namelist, filter, alphasort); + if (n < 0) { + lwsl_err("Scandir on %s failed\n", *d); + return 1; + } + + for (i = 0; i < n; i++) { + if (strlen(namelist[i]->d_name) < 7) + goto inval; + + lwsl_notice(" %s\n", namelist[i]->d_name); + + lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d, + namelist[i]->d_name); + l = dlopen(path, RTLD_NOW); + if (!l) { + lwsl_err("Error loading DSO: %s\n", dlerror()); + while (i++ < n) + free(namelist[i]); + goto bail; + } + /* we could open it, can we get his init function? */ + m = lws_snprintf(path, sizeof(path) - 1, "init_%s", + namelist[i]->d_name + 3 /* snip lib... */); + path[m - 3] = '\0'; /* snip the .so */ + initfunc = dlsym(l, path); + if (!initfunc) { + lwsl_err("Failed to get init on %s: %s", + namelist[i]->d_name, dlerror()); + dlclose(l); + } + lcaps.api_magic = LWS_PLUGIN_API_MAGIC; + m = initfunc(context, &lcaps); + if (m) { + lwsl_err("Initializing %s failed %d\n", + namelist[i]->d_name, m); + dlclose(l); + goto skip; + } + + plugin = lws_malloc(sizeof(*plugin), "plugin"); + if (!plugin) { + lwsl_err("OOM\n"); + goto bail; + } + plugin->list = context->plugin_list; + context->plugin_list = plugin; + strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1); + plugin->name[sizeof(plugin->name) - 1] = '\0'; + plugin->l = l; + plugin->caps = lcaps; + context->plugin_protocol_count += lcaps.count_protocols; + context->plugin_extension_count += lcaps.count_extensions; + + free(namelist[i]); + continue; + + skip: + dlclose(l); + inval: + free(namelist[i]); + } + free(namelist); + d++; + } + +bail: + free(namelist); + + return ret; +} + +LWS_VISIBLE int +lws_plat_plugins_destroy(struct lws_context * context) +{ + struct lws_plugin *plugin = context->plugin_list, *p; + lws_plugin_destroy_func func; + char path[256]; + int m; + + if (!plugin) + return 0; + + lwsl_notice("%s\n", __func__); + + while (plugin) { + p = plugin; + m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + 3); + path[m - 3] = '\0'; + func = dlsym(plugin->l, path); + if (!func) { + lwsl_err("Failed to get destroy on %s: %s", + plugin->name, dlerror()); + goto next; + } + m = func(context); + if (m) + lwsl_err("Initializing %s failed %d\n", + plugin->name, m); +next: + dlclose(p->l); + plugin = p->list; + p->list = NULL; + free(p); + } + + context->plugin_list = NULL; + + return 0; +} + +#endif +#endif + + +#if 0 +static void +sigabrt_handler(int x) +{ + printf("%s\n", __func__); +} +#endif + +LWS_VISIBLE int +lws_plat_context_early_init(void) +{ +#if !defined(LWS_AVOID_SIGPIPE_IGN) + signal(SIGPIPE, SIG_IGN); +#endif + + return 0; +} + +LWS_VISIBLE void +lws_plat_context_early_destroy(struct lws_context *context) +{ +} + +LWS_VISIBLE void +lws_plat_context_late_destroy(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int m = context->count_threads; + +#ifdef LWS_WITH_PLUGINS + if (context->plugin_list) + lws_plat_plugins_destroy(context); +#endif + + if (context->lws_lookup) + lws_free(context->lws_lookup); + + while (m--) { + if (pt->dummy_pipe_fds[0]) + close(pt->dummy_pipe_fds[0]); + if (pt->dummy_pipe_fds[1]) + close(pt->dummy_pipe_fds[1]); + pt++; + } + if (!context->fd_random) + lwsl_err("ZERO RANDOM FD\n"); + if (context->fd_random != LWS_INVALID_FILE) + close(context->fd_random); +} + +/* cast a struct sockaddr_in6 * into addr for ipv6 */ + +LWS_VISIBLE int +lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, + size_t addrlen) +{ + int rc = -1; + + struct ifaddrs *ifr; + struct ifaddrs *ifc; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; +#endif + + getifaddrs(&ifr); + for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) { + if (!ifc->ifa_addr) + continue; + + lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname); + + if (strcmp(ifc->ifa_name, ifname)) + continue; + + switch (ifc->ifa_addr->sa_family) { + case AF_INET: +#ifdef LWS_WITH_IPV6 + if (ipv6) { + /* map IPv4 to IPv6 */ + bzero((char *)&addr6->sin6_addr, + sizeof(struct in6_addr)); + addr6->sin6_addr.s6_addr[10] = 0xff; + addr6->sin6_addr.s6_addr[11] = 0xff; + memcpy(&addr6->sin6_addr.s6_addr[12], + &((struct sockaddr_in *)ifc->ifa_addr)->sin_addr, + sizeof(struct in_addr)); + } else +#endif + memcpy(addr, + (struct sockaddr_in *)ifc->ifa_addr, + sizeof(struct sockaddr_in)); + break; +#ifdef LWS_WITH_IPV6 + case AF_INET6: + memcpy(&addr6->sin6_addr, + &((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr, + sizeof(struct in6_addr)); + break; +#endif + default: + continue; + } + rc = 0; + } + + freeifaddrs(ifr); + + if (rc == -1) { + /* check if bind to IP address */ +#ifdef LWS_WITH_IPV6 + if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) + rc = 0; + else +#endif + if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1) + rc = 0; + } + + return rc; +} + +LWS_VISIBLE void +lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ); + + pt->fds[pt->fds_count++].revents = 0; +} + +LWS_VISIBLE void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); + lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); + + pt->fds_count--; +} + +LWS_VISIBLE void +lws_plat_service_periodic(struct lws_context *context) +{ + /* if our parent went down, don't linger around */ + if (context->started_with_parent && + kill(context->started_with_parent, 0) < 0) + kill(getpid(), SIGTERM); +} + +LWS_VISIBLE int +lws_plat_change_pollfd(struct lws_context *context, + struct lws *wsi, struct lws_pollfd *pfd) +{ + return 0; +} + +LWS_VISIBLE const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) +{ + return inet_ntop(af, src, dst, cnt); +} + +LWS_VISIBLE int +lws_plat_inet_pton(int af, const char *src, void *dst) +{ + return inet_pton(af, src, dst); +} + +LWS_VISIBLE lws_fop_fd_t +_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, + const char *vpath, lws_fop_flags_t *flags) +{ + struct stat stat_buf; + int ret = open(filename, (*flags) & LWS_FOP_FLAGS_MASK, 0664); + lws_fop_fd_t fop_fd; + + if (ret < 0) + return NULL; + + if (fstat(ret, &stat_buf) < 0) + goto bail; + + fop_fd = malloc(sizeof(*fop_fd)); + if (!fop_fd) + goto bail; + + fop_fd->fops = fops; + fop_fd->flags = *flags; + fop_fd->fd = ret; + fop_fd->filesystem_priv = NULL; /* we don't use it */ + fop_fd->len = stat_buf.st_size; + fop_fd->pos = 0; + + return fop_fd; + +bail: + close(ret); + return NULL; +} + +LWS_VISIBLE int +_lws_plat_file_close(lws_fop_fd_t *fop_fd) +{ + int fd = (*fop_fd)->fd; + + free(*fop_fd); + *fop_fd = NULL; + + return close(fd); +} + +LWS_VISIBLE lws_fileofs_t +_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + lws_fileofs_t r; + + if (offset > 0 && offset > fop_fd->len - fop_fd->pos) + offset = fop_fd->len - fop_fd->pos; + + if ((lws_fileofs_t)fop_fd->pos + offset < 0) + offset = -fop_fd->pos; + + r = lseek(fop_fd->fd, offset, SEEK_CUR); + + if (r >= 0) + fop_fd->pos = r; + else + lwsl_err("error seeking from cur %ld, offset %ld\n", + (long)fop_fd->pos, (long)offset); + + return r; +} + +LWS_VISIBLE int +_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + long n; + + n = read((int)fop_fd->fd, buf, len); + if (n == -1) { + *amount = 0; + return -1; + } + fop_fd->pos += n; + lwsl_debug("%s: read %ld of req %ld, pos %ld, len %ld\n", __func__, n, + (long)len, (long)fop_fd->pos, (long)fop_fd->len); + *amount = n; + + return 0; +} + +LWS_VISIBLE int +_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + long n; + + n = write((int)fop_fd->fd, buf, len); + if (n == -1) { + *amount = 0; + return -1; + } + + fop_fd->pos += n; + *amount = n; + + return 0; +} + + +LWS_VISIBLE int +lws_plat_init(struct lws_context *context, + struct lws_context_creation_info *info) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int n = context->count_threads, fd; + + /* master context has the global fd lookup array */ + context->lws_lookup = lws_zalloc(sizeof(struct lws *) * + context->max_fds, "lws_lookup"); + if (context->lws_lookup == NULL) { + lwsl_err("OOM on lws_lookup array for %d connections\n", + context->max_fds); + return 1; + } + + lwsl_info(" mem: platform fd map: %5lu bytes\n", + (unsigned long)(sizeof(struct lws *) * context->max_fds)); + fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY); + + context->fd_random = fd; + if (context->fd_random < 0) { + lwsl_err("Unable to open random device %s %d\n", + SYSTEM_RANDOM_FILEPATH, context->fd_random); + return 1; + } + + if (!lws_libev_init_fd_table(context) && + !lws_libuv_init_fd_table(context) && + !lws_libevent_init_fd_table(context)) { + /* otherwise libev/uv/event handled it instead */ + + while (n--) { + if (pipe(pt->dummy_pipe_fds)) { + lwsl_err("Unable to create pipe\n"); + return 1; + } + + /* use the read end of pipe as first item */ + pt->fds[0].fd = pt->dummy_pipe_fds[0]; + pt->fds[0].events = LWS_POLLIN; + pt->fds[0].revents = 0; + pt->fds_count = 1; + pt++; + } + } + +#ifdef LWS_WITH_PLUGINS + if (info->plugin_dirs) + lws_plat_plugins_init(context, info->plugin_dirs); +#endif + + return 0; +} diff -Nru libwebsockets-4.0.20/lib/plat/lws-plat-win.c libwebsockets-2.4.2/lib/plat/lws-plat-win.c --- libwebsockets-4.0.20/lib/plat/lws-plat-win.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/lws-plat-win.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,745 @@ +#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#endif +#include "private-libwebsockets.h" + +unsigned long long +time_in_microseconds() +{ +#ifndef DELTA_EPOCH_IN_MICROSECS +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif + FILETIME filetime; + ULARGE_INTEGER datetime; + +#ifdef _WIN32_WCE + GetCurrentFT(&filetime); +#else + GetSystemTimeAsFileTime(&filetime); +#endif + + /* + * As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a + * ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can + * prevent alignment faults on 64-bit Windows). + */ + memcpy(&datetime, &filetime, sizeof(datetime)); + + /* Windows file times are in 100s of nanoseconds. */ + return (datetime.QuadPart - DELTA_EPOCH_IN_MICROSECS) / 10; +} + +#ifdef _WIN32_WCE +time_t time(time_t *t) +{ + time_t ret = time_in_microseconds() / 1000000; + + if(t != NULL) + *t = ret; + + return ret; +} +#endif + +/* file descriptor hash management */ + +struct lws * +wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd) +{ + int h = LWS_FD_HASH(fd); + int n = 0; + + for (n = 0; n < context->fd_hashtable[h].length; n++) + if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) + return context->fd_hashtable[h].wsi[n]; + + return NULL; +} + +int +insert_wsi(struct lws_context *context, struct lws *wsi) +{ + int h = LWS_FD_HASH(wsi->desc.sockfd); + + if (context->fd_hashtable[h].length == (getdtablesize() - 1)) { + lwsl_err("hash table overflow\n"); + return 1; + } + + context->fd_hashtable[h].wsi[context->fd_hashtable[h].length++] = wsi; + + return 0; +} + +int +delete_from_fd(struct lws_context *context, lws_sockfd_type fd) +{ + int h = LWS_FD_HASH(fd); + int n = 0; + + for (n = 0; n < context->fd_hashtable[h].length; n++) + if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) { + while (n < context->fd_hashtable[h].length) { + context->fd_hashtable[h].wsi[n] = + context->fd_hashtable[h].wsi[n + 1]; + n++; + } + context->fd_hashtable[h].length--; + + return 0; + } + + lwsl_err("Failed to find fd %d requested for " + "delete in hashtable\n", fd); + return 1; +} + +LWS_VISIBLE int lws_get_random(struct lws_context *context, + void *buf, int len) +{ + int n; + char *p = (char *)buf; + + for (n = 0; n < len; n++) + p[n] = (unsigned char)rand(); + + return n; +} + +LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi) +{ + /* treat the fact we got a truncated send pending as if we're choked */ + if (wsi->trunc_len) + return 1; + + return (int)wsi->sock_send_blocking; +} + +LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd) +{ + fd_set readfds; + struct timeval tv = { 0, 0 }; + + assert((fd->events & LWS_POLLIN) == LWS_POLLIN); + + FD_ZERO(&readfds); + FD_SET(fd->fd, &readfds); + + return select(fd->fd + 1, &readfds, NULL, NULL, &tv); +} + +LWS_VISIBLE void +lws_cancel_service(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int n = context->count_threads; + + while (n--) { + WSASetEvent(pt->events[0]); + pt++; + } +} + +LWS_VISIBLE void +lws_cancel_service_pt(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + WSASetEvent(pt->events[0]); +} + +LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) +{ + lwsl_emit_stderr(level, line); +} + +LWS_VISIBLE LWS_EXTERN int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt; + WSANETWORKEVENTS networkevents; + struct lws_pollfd *pfd; + struct lws *wsi; + unsigned int i; + DWORD ev; + int n, m; + + /* stay dead once we are dead */ + if (context == NULL || !context->vhost_list) + return 1; + + pt = &context->pt[tsi]; + + if (!context->service_tid_detected) { + struct lws _lws; + + memset(&_lws, 0, sizeof(_lws)); + _lws.context = context; + + context->service_tid_detected = context->vhost_list-> + protocols[0].callback(&_lws, LWS_CALLBACK_GET_THREAD_ID, + NULL, NULL, 0); + context->service_tid = context->service_tid_detected; + context->service_tid_detected = 1; + } + + if (timeout_ms < 0) + { + if (lws_service_flag_pending(context, tsi)) { + /* any socket with events to service? */ + for (n = 0; n < (int)pt->fds_count; n++) { + if (!pt->fds[n].revents) + continue; + + m = lws_service_fd_tsi(context, &pt->fds[n], tsi); + if (m < 0) + return -1; + /* if something closed, retry this slot */ + if (m) + n--; + } + } + return 0; + } + + for (i = 0; i < pt->fds_count; ++i) { + pfd = &pt->fds[i]; + + if (!(pfd->events & LWS_POLLOUT)) + continue; + + wsi = wsi_from_fd(context, pfd->fd); + if (wsi->listener) + continue; + if (!wsi || wsi->sock_send_blocking) + continue; + pfd->revents = LWS_POLLOUT; + n = lws_service_fd(context, pfd); + if (n < 0) + return -1; + /* if something closed, retry this slot */ + if (n) + i--; + + if (wsi->trunc_len) + WSASetEvent(pt->events[0]); + } + + /* + * is there anybody with pending stuff that needs service forcing? + */ + if (!lws_service_adjust_timeout(context, 1, tsi)) { + /* -1 timeout means just do forced service */ + _lws_plat_service_tsi(context, -1, pt->tid); + /* still somebody left who wants forced service? */ + if (!lws_service_adjust_timeout(context, 1, pt->tid)) + /* yes... come back again quickly */ + timeout_ms = 0; + } + + ev = WSAWaitForMultipleEvents( 1, pt->events , FALSE, timeout_ms, FALSE); + if (ev == WSA_WAIT_EVENT_0) { + unsigned int eIdx; + + WSAResetEvent(pt->events[0]); + + for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) { + if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0, &networkevents) == SOCKET_ERROR) { + lwsl_err("WSAEnumNetworkEvents() failed with error %d\n", LWS_ERRNO); + return -1; + } + + pfd = &pt->fds[eIdx]; + pfd->revents = (short)networkevents.lNetworkEvents; + + if ((networkevents.lNetworkEvents & FD_CONNECT) && + networkevents.iErrorCode[FD_CONNECT_BIT] && + networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EALREADY && + networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EINPROGRESS && + networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EWOULDBLOCK && + networkevents.iErrorCode[FD_CONNECT_BIT] != WSAEINVAL) { + lwsl_debug("Unable to connect errno=%d\n", + networkevents.iErrorCode[FD_CONNECT_BIT]); + pfd->revents |= LWS_POLLHUP; + } + + if (pfd->revents & LWS_POLLOUT) { + wsi = wsi_from_fd(context, pfd->fd); + if (wsi) + wsi->sock_send_blocking = 0; + } + /* if something closed, retry this slot */ + if (pfd->revents & LWS_POLLHUP) + --eIdx; + + if( pfd->revents != 0 ) { + lws_service_fd_tsi(context, pfd, tsi); + + } + } + } + + context->service_tid = 0; + + if (ev == WSA_WAIT_TIMEOUT) { + lws_service_fd(context, NULL); + } + return 0;; +} + +LWS_VISIBLE int +lws_plat_service(struct lws_context *context, int timeout_ms) +{ + return _lws_plat_service_tsi(context, timeout_ms, 0); +} + +LWS_VISIBLE int +lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd) +{ + int optval = 1; + int optlen = sizeof(optval); + u_long optl = 1; + DWORD dwBytesRet; + struct tcp_keepalive alive; + int protonbr; +#ifndef _WIN32_WCE + struct protoent *tcp_proto; +#endif + + if (vhost->ka_time) { + /* enable keepalive on this socket */ + optval = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const char *)&optval, optlen) < 0) + return 1; + + alive.onoff = TRUE; + alive.keepalivetime = vhost->ka_time; + alive.keepaliveinterval = vhost->ka_interval; + + if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), + NULL, 0, &dwBytesRet, NULL, NULL)) + return 1; + } + + /* Disable Nagle */ + optval = 1; +#ifndef _WIN32_WCE + tcp_proto = getprotobyname("TCP"); + if (!tcp_proto) { + lwsl_err("getprotobyname() failed with error %d\n", LWS_ERRNO); + return 1; + } + protonbr = tcp_proto->p_proto; +#else + protonbr = 6; +#endif + + setsockopt(fd, protonbr, TCP_NODELAY, (const char *)&optval, optlen); + + /* We are nonblocking... */ + ioctlsocket(fd, FIONBIO, &optl); + + return 0; +} + +LWS_VISIBLE void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info) +{ +} + +LWS_VISIBLE int +lws_plat_context_early_init(void) +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + + /* Use the MAKEWORD(lowbyte, highbyte) macro from Windef.h */ + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (!err) + return 0; + /* + * Tell the user that we could not find a usable + * Winsock DLL + */ + lwsl_err("WSAStartup failed with error: %d\n", err); + + return 1; +} + +LWS_VISIBLE void +lws_plat_context_early_destroy(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int n = context->count_threads; + + while (n--) { + if (pt->events) { + WSACloseEvent(pt->events[0]); + lws_free(pt->events); + } + pt++; + } +} + +LWS_VISIBLE void +lws_plat_context_late_destroy(struct lws_context *context) +{ + int n; + + for (n = 0; n < FD_HASHTABLE_MODULUS; n++) { + if (context->fd_hashtable[n].wsi) + lws_free(context->fd_hashtable[n].wsi); + } + + WSACleanup(); +} + +LWS_VISIBLE LWS_EXTERN int +lws_interface_to_sa(int ipv6, + const char *ifname, struct sockaddr_in *addr, size_t addrlen) +{ +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; + + if (ipv6) { + if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) { + return 0; + } + } +#endif + + long long address = inet_addr(ifname); + + if (address == INADDR_NONE) { + struct hostent *entry = gethostbyname(ifname); + if (entry) + address = ((struct in_addr *)entry->h_addr_list[0])->s_addr; + } + + if (address == INADDR_NONE) + return -1; + + addr->sin_addr.s_addr = (lws_intptr_t)address; + + return 0; +} + +LWS_VISIBLE void +lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + pt->fds[pt->fds_count++].revents = 0; + pt->events[pt->fds_count] = pt->events[0]; + WSAEventSelect(wsi->desc.sockfd, pt->events[0], + LWS_POLLIN | LWS_POLLHUP | FD_CONNECT); +} + +LWS_VISIBLE void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + pt->events[m + 1] = pt->events[pt->fds_count--]; +} + +LWS_VISIBLE void +lws_plat_service_periodic(struct lws_context *context) +{ +} + +LWS_VISIBLE int +lws_plat_check_connection_error(struct lws *wsi) +{ + int optVal; + int optLen = sizeof(int); + + if (getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR, + (char*)&optVal, &optLen) != SOCKET_ERROR && optVal && + optVal != LWS_EALREADY && optVal != LWS_EINPROGRESS && + optVal != LWS_EWOULDBLOCK && optVal != WSAEINVAL) { + lwsl_debug("Connect failed SO_ERROR=%d\n", optVal); + return 1; + } + + return 0; +} + +LWS_VISIBLE int +lws_plat_change_pollfd(struct lws_context *context, + struct lws *wsi, struct lws_pollfd *pfd) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + long networkevents = LWS_POLLHUP | FD_CONNECT; + + if ((pfd->events & LWS_POLLIN)) + networkevents |= LWS_POLLIN; + + if ((pfd->events & LWS_POLLOUT)) + networkevents |= LWS_POLLOUT; + + if (WSAEventSelect(wsi->desc.sockfd, + pt->events[0], + networkevents) != SOCKET_ERROR) + return 0; + + lwsl_err("WSAEventSelect() failed with error %d\n", LWS_ERRNO); + + return 1; +} + +LWS_VISIBLE const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) +{ + WCHAR *buffer; + DWORD bufferlen = cnt; + BOOL ok = FALSE; + + buffer = lws_malloc(bufferlen * 2, "inet_ntop"); + if (!buffer) { + lwsl_err("Out of memory\n"); + return NULL; + } + + if (af == AF_INET) { + struct sockaddr_in srcaddr; + bzero(&srcaddr, sizeof(srcaddr)); + srcaddr.sin_family = AF_INET; + memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr)); + + if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen)) + ok = TRUE; +#ifdef LWS_WITH_IPV6 + } else if (af == AF_INET6) { + struct sockaddr_in6 srcaddr; + bzero(&srcaddr, sizeof(srcaddr)); + srcaddr.sin6_family = AF_INET6; + memcpy(&(srcaddr.sin6_addr), src, sizeof(srcaddr.sin6_addr)); + + if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen)) + ok = TRUE; +#endif + } else + lwsl_err("Unsupported type\n"); + + if (!ok) { + int rv = WSAGetLastError(); + lwsl_err("WSAAddressToString() : %d\n", rv); + } else { + if (WideCharToMultiByte(CP_ACP, 0, buffer, bufferlen, dst, cnt, 0, NULL) <= 0) + ok = FALSE; + } + + lws_free(buffer); + return ok ? dst : NULL; +} + +LWS_VISIBLE int +lws_plat_inet_pton(int af, const char *src, void *dst) +{ + WCHAR *buffer; + DWORD bufferlen = strlen(src) + 1; + BOOL ok = FALSE; + + buffer = lws_malloc(bufferlen * 2, "inet_pton"); + if (!buffer) { + lwsl_err("Out of memory\n"); + return -1; + } + + if (MultiByteToWideChar(CP_ACP, 0, src, bufferlen, buffer, bufferlen) <= 0) { + lwsl_err("Failed to convert multi byte to wide char\n"); + lws_free(buffer); + return -1; + } + + if (af == AF_INET) { + struct sockaddr_in dstaddr; + int dstaddrlen = sizeof(dstaddr); + bzero(&dstaddr, sizeof(dstaddr)); + dstaddr.sin_family = AF_INET; + + if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) { + ok = TRUE; + memcpy(dst, &dstaddr.sin_addr, sizeof(dstaddr.sin_addr)); + } +#ifdef LWS_WITH_IPV6 + } else if (af == AF_INET6) { + struct sockaddr_in6 dstaddr; + int dstaddrlen = sizeof(dstaddr); + bzero(&dstaddr, sizeof(dstaddr)); + dstaddr.sin6_family = AF_INET6; + + if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) { + ok = TRUE; + memcpy(dst, &dstaddr.sin6_addr, sizeof(dstaddr.sin6_addr)); + } +#endif + } else + lwsl_err("Unsupported type\n"); + + if (!ok) { + int rv = WSAGetLastError(); + lwsl_err("WSAAddressToString() : %d\n", rv); + } + + lws_free(buffer); + return ok ? 1 : -1; +} + +LWS_VISIBLE lws_fop_fd_t +_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, + const char *vpath, lws_fop_flags_t *flags) +{ + HANDLE ret; + WCHAR buf[MAX_PATH]; + lws_fop_fd_t fop_fd; + LARGE_INTEGER llFileSize = {0}; + + MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, ARRAY_SIZE(buf)); + if (((*flags) & 7) == _O_RDONLY) { + ret = CreateFileW(buf, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } else { + ret = CreateFileW(buf, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + + if (ret == LWS_INVALID_FILE) + goto bail; + + fop_fd = malloc(sizeof(*fop_fd)); + if (!fop_fd) + goto bail; + + fop_fd->fops = fops; + fop_fd->fd = ret; + fop_fd->filesystem_priv = NULL; /* we don't use it */ + fop_fd->flags = *flags; + fop_fd->len = GetFileSize(ret, NULL); + if(GetFileSizeEx(ret, &llFileSize)) + fop_fd->len = llFileSize.QuadPart; + + fop_fd->pos = 0; + + return fop_fd; + +bail: + return NULL; +} + +LWS_VISIBLE int +_lws_plat_file_close(lws_fop_fd_t *fop_fd) +{ + HANDLE fd = (*fop_fd)->fd; + + free(*fop_fd); + *fop_fd = NULL; + + CloseHandle((HANDLE)fd); + + return 0; +} + +LWS_VISIBLE lws_fileofs_t +_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + LARGE_INTEGER l; + + l.QuadPart = offset; + return SetFilePointerEx((HANDLE)fop_fd->fd, l, NULL, FILE_CURRENT); +} + +LWS_VISIBLE int +_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + DWORD _amount; + + if (!ReadFile((HANDLE)fop_fd->fd, buf, (DWORD)len, &_amount, NULL)) { + *amount = 0; + + return 1; + } + + fop_fd->pos += _amount; + *amount = (unsigned long)_amount; + + return 0; +} + +LWS_VISIBLE int +_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t* buf, lws_filepos_t len) +{ + DWORD _amount; + + if (!WriteFile((HANDLE)fop_fd->fd, buf, (DWORD)len, &_amount, NULL)) { + *amount = 0; + + return 1; + } + + fop_fd->pos += _amount; + *amount = (unsigned long)_amount; + + return 0; +} + +LWS_VISIBLE int +lws_plat_init(struct lws_context *context, + struct lws_context_creation_info *info) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int i, n = context->count_threads; + + for (i = 0; i < FD_HASHTABLE_MODULUS; i++) { + context->fd_hashtable[i].wsi = + lws_zalloc(sizeof(struct lws*) * context->max_fds, "win hashtable"); + + if (!context->fd_hashtable[i].wsi) + return -1; + } + + while (n--) { + pt->events = lws_malloc(sizeof(WSAEVENT) * + (context->fd_limit_per_thread + 1), "event table"); + if (pt->events == NULL) { + lwsl_err("Unable to allocate events array for %d connections\n", + context->fd_limit_per_thread + 1); + return 1; + } + + pt->fds_count = 0; + pt->events[0] = WSACreateEvent(); + + pt++; + } + + context->fd_random = 0; + +#ifdef LWS_WITH_PLUGINS + if (info->plugin_dirs) + lws_plat_plugins_init(context, info->plugin_dirs); +#endif + + return 0; +} + + +int kill(int pid, int sig) +{ + lwsl_err("Sorry Windows doesn't support kill()."); + exit(0); +} + +int fork(void) +{ + lwsl_err("Sorry Windows doesn't support fork()."); + exit(0); +} + diff -Nru libwebsockets-4.0.20/lib/plat/optee/lws-plat-optee.c libwebsockets-2.4.2/lib/plat/optee/lws-plat-optee.c --- libwebsockets-4.0.20/lib/plat/optee/lws-plat-optee.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/optee/lws-plat-optee.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,237 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_NETWORK) -#include -#endif - -int errno; - -#if !defined(LWS_WITH_NETWORK) -char * -strcpy(char *dest, const char *src) -{ - char *desto = dest; - - while (*src) - *(dest++) = *(src++); - - *(dest++) = '\0'; - - return desto; -} - -char *strncpy(char *dest, const char *src, size_t limit) -{ - char *desto = dest; - - while (*src && limit--) - *(dest++) = *(src++); - - if (limit) - *(dest++) = '\0'; - - return desto; -} - -#endif - -int lws_plat_apply_FD_CLOEXEC(int n) -{ - return 0; -} - -void TEE_GenerateRandom(void *randomBuffer, uint32_t randomBufferLen); -#if defined(LWS_WITH_NETWORK) -uint64_t -lws_now_usecs(void) -{ - return ((unsigned long long)time(NULL)) * 1000000; -} -#endif - -size_t -lws_get_random(struct lws_context *context, void *buf, size_t len) -{ -#if defined(LWS_WITH_NETWORK) - TEE_GenerateRandom(buf, len); -#else - crypto_rng_read(buf, len); -#endif - - return len; -} - - -static const char * const colours[] = { - "[31;1m", /* LLL_ERR */ - "[36;1m", /* LLL_WARN */ - "[35;1m", /* LLL_NOTICE */ - "[32;1m", /* LLL_INFO */ - "[34;1m", /* LLL_DEBUG */ - "[33;1m", /* LLL_PARSER */ - "[33;1m", /* LLL_HEADER */ - "[33;1m", /* LLL_EXT */ - "[33;1m", /* LLL_CLIENT */ - "[33;1m", /* LLL_LATENCY */ - "[30;1m", /* LLL_USER */ -}; - -void lwsl_emit_optee(int level, const char *line) -{ - char buf[50], linecp[512]; - int n, m = LWS_ARRAY_SIZE(colours) - 1; - - lwsl_timestamp(level, buf, sizeof(buf)); - - n = 1 << (LWS_ARRAY_SIZE(colours) - 1); - while (n) { - if (level & n) - break; - m--; - n >>= 1; - } - n = strlen(line); - if ((unsigned int)n > sizeof(linecp) - 1) - n = sizeof(linecp) - 1; - if (n) { - memcpy(linecp, line, n - 1); - linecp[n - 1] = '\0'; - } else - linecp[0] = '\0'; - EMSG("%c%s%s%s%c[0m", 27, colours[m], buf, linecp, 27); -} - -int -lws_plat_set_nonblocking(lws_sockfd_type fd) -{ - return 0; -} - -int -lws_plat_drop_app_privileges(struct lws_context *context, int actually_init) -{ - return 0; -} - -int -lws_plat_context_early_init(void) -{ - return 0; -} - -void -lws_plat_context_early_destroy(struct lws_context *context) -{ -} - -void -lws_plat_context_late_destroy(struct lws_context *context) -{ -#if defined(LWS_WITH_NETWORK) - if (context->lws_lookup) - lws_free(context->lws_lookup); -#endif -} - -lws_fop_fd_t -_lws_plat_file_open(const struct lws_plat_file_ops *fops, - const char *filename, const char *vpath, lws_fop_flags_t *flags) -{ - return NULL; -} - -int -_lws_plat_file_close(lws_fop_fd_t *fop_fd) -{ - return 0; -} - -lws_fileofs_t -_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) -{ - return 0; -} - - int -_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len) -{ - - return 0; -} - - int -_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len) -{ - - return 0; -} - - -int -lws_plat_init(struct lws_context *context, - const struct lws_context_creation_info *info) -{ -#if defined(LWS_WITH_NETWORK) - /* master context has the global fd lookup array */ - context->lws_lookup = lws_zalloc(sizeof(struct lws *) * - context->max_fds, "lws_lookup"); - if (context->lws_lookup == NULL) { - lwsl_err("OOM on lws_lookup array for %d connections\n", - context->max_fds); - return 1; - } - - lwsl_notice(" mem: platform fd map: %5lu bytes\n", - (long)sizeof(struct lws *) * context->max_fds); -#endif -#ifdef LWS_WITH_PLUGINS - if (info->plugin_dirs) - lws_plat_plugins_init(context, info->plugin_dirs); -#endif - - return 0; -} - -int -lws_plat_write_file(const char *filename, void *buf, int len) -{ - return 1; -} - -int -lws_plat_read_file(const char *filename, void *buf, int len) -{ - return -1; -} - -int -lws_plat_recommended_rsa_bits(void) -{ - return 4096; -} diff -Nru libwebsockets-4.0.20/lib/plat/optee/network.c libwebsockets-2.4.2/lib/plat/optee/network.c --- libwebsockets-4.0.20/lib/plat/optee/network.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/optee/network.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,250 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - - -int -lws_plat_pipe_create(struct lws *wsi) -{ - return 1; -} - -int -lws_plat_pipe_signal(struct lws *wsi) -{ - return 1; -} - -void -lws_plat_pipe_close(struct lws *wsi) -{ -} - -int -lws_send_pipe_choked(struct lws *wsi) -{ - struct lws *wsi_eff; - -#if defined(LWS_WITH_HTTP2) - wsi_eff = lws_get_network_wsi(wsi); -#else - wsi_eff = wsi; -#endif - - /* the fact we checked implies we avoided back-to-back writes */ - wsi_eff->could_have_pending = 0; - - /* treat the fact we got a truncated send pending as if we're choked */ - if (lws_has_buffered_out(wsi_eff) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - || wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more -#endif - ) - return 1; - - /* okay to send another packet without blocking */ - - return 0; -} - -int -lws_poll_listen_fd(struct lws_pollfd *fd) -{ -// return poll(fd, 1, 0); - - return 0; -} - - -int -_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) -{ - lws_usec_t timeout_us = timeout_ms * LWS_US_PER_MS; - struct lws_context_per_thread *pt; - int n = -1, m, c, a = 0; - //char buf; - - /* stay dead once we are dead */ - - if (!context || !context->vhost_list) - return 1; - - pt = &context->pt[tsi]; - - if (timeout_ms < 0) - timeout_ms = 0; - else - timeout_ms = 2000000000; - - if (!pt->service_tid_detected) { - struct lws _lws; - - memset(&_lws, 0, sizeof(_lws)); - _lws.context = context; - - pt->service_tid = context->vhost_list->protocols[0].callback( - &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); - pt->service_tid_detected = 1; - } - - /* - * is there anybody with pending stuff that needs service forcing? - */ - if (lws_service_adjust_timeout(context, 1, tsi)) { -again: - a = 0; - if (timeout_us) { - lws_usec_t us; - - lws_pt_lock(pt, __func__); - /* don't stay in poll wait longer than next hr timeout */ - us = __lws_sul_service_ripe(&pt->pt_sul_owner, lws_now_usecs()); - if (us && us < timeout_us) - timeout_us = us; - - lws_pt_unlock(pt); - } - - n = poll(pt->fds, pt->fds_count, timeout_us / LWS_US_PER_MS); - - m = 0; - - if (pt->context->tls_ops && - pt->context->tls_ops->fake_POLLIN_for_buffered) - m = pt->context->tls_ops->fake_POLLIN_for_buffered(pt); - - if (/*!pt->ws.rx_draining_ext_list && */!m && !n) /* nothing to do */ - return 0; - } else - a = 1; - - m = lws_service_flag_pending(context, tsi); - if (m) - c = -1; /* unknown limit */ - else - if (n < 0) { - if (LWS_ERRNO != LWS_EINTR) - return -1; - return 0; - } else - c = n; - - /* any socket with events to service? */ - for (n = 0; n < (int)pt->fds_count && c; n++) { - if (!pt->fds[n].revents) - continue; - - c--; -#if 0 - if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) { - if (read(pt->fds[n].fd, &buf, 1) != 1) - lwsl_err("Cannot read from dummy pipe."); - continue; - } -#endif - m = lws_service_fd_tsi(context, &pt->fds[n], tsi); - if (m < 0) - return -1; - /* if something closed, retry this slot */ - if (m) - n--; - } - - if (a) - goto again; - - return 0; -} - -int -lws_plat_service(struct lws_context *context, int timeout_ms) -{ - return _lws_plat_service_tsi(context, timeout_ms, 0); -} - -int -lws_plat_set_socket_options(struct lws_vhost *vhost, int fd, int unix_skt) -{ - return 0; -} - - -int -lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf, - int len) -{ - return 1; -} - - -/* cast a struct sockaddr_in6 * into addr for ipv6 */ - -int -lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, - size_t addrlen) -{ - return -1; -} - -void -lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - - pt->fds[pt->fds_count++].revents = 0; -} - -void -lws_plat_delete_socket_from_fds(struct lws_context *context, - struct lws *wsi, int m) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - - pt->fds_count--; -} - -int -lws_plat_change_pollfd(struct lws_context *context, - struct lws *wsi, struct lws_pollfd *pfd) -{ - return 0; -} - -const char * -lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) -{ - //return inet_ntop(af, src, dst, cnt); - return "lws_plat_inet_ntop"; -} - -int -lws_plat_inet_pton(int af, const char *src, void *dst) -{ - //return inet_pton(af, src, dst); - return 1; -} - - diff -Nru libwebsockets-4.0.20/lib/plat/optee/private-lib-plat-optee.h libwebsockets-2.4.2/lib/plat/optee/private-lib-plat-optee.h --- libwebsockets-4.0.20/lib/plat/optee/private-lib-plat-optee.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/optee/private-lib-plat-optee.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * Included from lib/private-lib-core.h if LWS_WITH_OPTEE - */ - - #include - #include - - #define LWS_ERRNO errno - #define LWS_EAGAIN EAGAIN - #define LWS_EALREADY EALREADY - #define LWS_EINPROGRESS EINPROGRESS - #define LWS_EINTR EINTR - #define LWS_EISCONN EISCONN - #define LWS_ENOTCONN ENOTCONN - #define LWS_EWOULDBLOCK EWOULDBLOCK - #define LWS_EADDRINUSE EADDRINUSE - - #define lws_set_blocking_send(wsi) - -#define compatible_close(x) close(x) -#define lws_plat_socket_offset() (0) -#define wsi_from_fd(A,B) A->lws_lookup[B - lws_plat_socket_offset()] -#define insert_wsi(A,B) assert(A->lws_lookup[B->desc.sockfd - \ - lws_plat_socket_offset()] == 0); \ - A->lws_lookup[B->desc.sockfd - \ - lws_plat_socket_offset()] = B -#define delete_from_fd(A,B) A->lws_lookup[B - lws_plat_socket_offset()] = 0 - diff -Nru libwebsockets-4.0.20/lib/plat/unix/android/android-resolv.c libwebsockets-2.4.2/lib/plat/unix/android/android-resolv.c --- libwebsockets-4.0.20/lib/plat/unix/android/android-resolv.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/android/android-resolv.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -lws_async_dns_server_check_t -lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46) -{ - char d[PROP_VALUE_MAX], *p; - uint32_t ip32; - uint8_t i[4]; - int n; - - d[0] = '\0'; - if (__system_property_get("net.dns1", d) <= 0) - return LADNS_CONF_SERVER_UNKNOWN; - - for (n = 0; n < 4; n++) { - i[n] = atoi(d); - p = strchr(d, '.'); - if (n != 3 && !p) - return LADNS_CONF_SERVER_UNKNOWN; - } - - ip32 = (i[0] << 24) | (i[1] << 16) | (i[2] << 8) | i[3]; - n = ip32 == sa46->sa4.sin_addr.s_addr; - sa46->sa4.sin_family = AF_INET; - sa46->sa4.sin_addr.s_addr = ip32; - - return n ? LADNS_CONF_SERVER_SAME : LADNS_CONF_SERVER_CHANGED; -} - diff -Nru libwebsockets-4.0.20/lib/plat/unix/private-lib-plat-unix.h libwebsockets-2.4.2/lib/plat/unix/private-lib-plat-unix.h --- libwebsockets-4.0.20/lib/plat/unix/private-lib-plat-unix.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/private-lib-plat-unix.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,195 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * Included from lib/private-lib-core.h if no explicit platform - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#ifndef __cplusplus -#include -#endif -#include -#include - -#include -#include -#include -#include -#include -#include -#if defined(LWS_HAVE_EVENTFD) -#include -#endif - -#if defined(__APPLE__) -#include -#endif -#if defined(__FreeBSD__) -#include -#endif -#if defined(__linux__) -#include -#include -#include -#endif -#if defined(__QNX__) - #include - #if defined(__LITTLEENDIAN__) - #define BYTE_ORDER __LITTLEENDIAN__ - #define LITTLE_ENDIAN __LITTLEENDIAN__ - #define BIG_ENDIAN 4321 /* to show byte order (taken from gcc); for suppres warning that BIG_ENDIAN is not defined. */ - #endif - #if defined(__BIGENDIAN__) - #define BYTE_ORDER __BIGENDIAN__ - #define LITTLE_ENDIAN 1234 /* to show byte order (taken from gcc); for suppres warning that LITTLE_ENDIAN is not defined. */ - #define BIG_ENDIAN __BIGENDIAN__ - #endif -#endif - -#if defined(__sun) && defined(__GNUC__) - -#include - -#if !defined (BYTE_ORDER) -#define BYTE_ORDER __BYTE_ORDER__ -#endif - -#if !defined(LITTLE_ENDIAN) -#define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ -#endif - -#if !defined(BIG_ENDIAN) -#define BIG_ENDIAN __ORDER_BIG_ENDIAN__ -#endif - -#endif /* sun + GNUC */ - -#if !defined(BYTE_ORDER) -#define BYTE_ORDER __BYTE_ORDER -#endif -#if !defined(LITTLE_ENDIAN) -#define LITTLE_ENDIAN __LITTLE_ENDIAN -#endif -#if !defined(BIG_ENDIAN) -#define BIG_ENDIAN __BIG_ENDIAN -#endif - -#if defined(LWS_BUILTIN_GETIFADDRS) -#include "./misc/getifaddrs.h" -#else - -#if defined(__HAIKU__) -#define _BSD_SOURCE -#endif -#include - -#endif - -#if defined (__sun) || defined(__HAIKU__) || defined(__QNX__) || defined(__ANDROID__) -#include - -#if defined(__ANDROID__) -#include -#endif - -#else -#include -#endif - -#ifdef __QNX__ -# include "netinet/tcp_var.h" -# define TCP_KEEPINTVL TCPCTL_KEEPINTVL -# define TCP_KEEPIDLE TCPCTL_KEEPIDLE -# define TCP_KEEPCNT TCPCTL_KEEPCNT -#endif - -#define LWS_ERRNO errno -#define LWS_EAGAIN EAGAIN -#define LWS_EALREADY EALREADY -#define LWS_EINPROGRESS EINPROGRESS -#define LWS_EINTR EINTR -#define LWS_EISCONN EISCONN -#define LWS_ENOTCONN ENOTCONN -#define LWS_EWOULDBLOCK EWOULDBLOCK -#define LWS_EADDRINUSE EADDRINUSE -#define lws_set_blocking_send(wsi) -#define LWS_SOCK_INVALID (-1) - -struct lws_context; - -struct lws * -wsi_from_fd(const struct lws_context *context, int fd); - -int -insert_wsi(const struct lws_context *context, struct lws *wsi); - -int -lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, - uint8_t *gateway_ip); - -void -delete_from_fd(const struct lws_context *context, int fd); - -#ifndef LWS_NO_FORK -#ifdef LWS_HAVE_SYS_PRCTL_H -#include -#endif -#endif - -#define compatible_close(x) close(x) -#define lws_plat_socket_offset() (0) - -/* - * Mac OSX as well as iOS do not define the MSG_NOSIGNAL flag, - * but happily have something equivalent in the SO_NOSIGPIPE flag. - */ -#ifdef __APPLE__ -#define MSG_NOSIGNAL SO_NOSIGPIPE -#endif - -/* - * Solaris 11.X only supports POSIX 2001, MSG_NOSIGNAL appears in - * POSIX 2008. - */ -#if defined(__sun) && !defined(MSG_NOSIGNAL) - #define MSG_NOSIGNAL 0 -#endif - -int -lws_plat_BINDTODEVICE(int fd, const char *ifname); - -int -lws_plat_rawudp_broadcast(uint8_t *p, const uint8_t *canned, int canned_len, - int n, int fd, const char *iface); - -int -lws_plat_if_up(const char *ifname, int fd, int up); diff -Nru libwebsockets-4.0.20/lib/plat/unix/unix-caps.c libwebsockets-2.4.2/lib/plat/unix/unix-caps.c --- libwebsockets-4.0.20/lib/plat/unix/unix-caps.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/unix-caps.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,200 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#include "private-lib-core.h" - -#include -#include - -#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) -static void -_lws_plat_apply_caps(int mode, const cap_value_t *cv, int count) -{ - cap_t caps; - - if (!count) - return; - - caps = cap_get_proc(); - - cap_set_flag(caps, mode, count, cv, CAP_SET); - cap_set_proc(caps); - prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); - cap_free(caps); -} -#endif - -int -lws_plat_user_colon_group_to_ids(const char *u_colon_g, uid_t *puid, gid_t *pgid) -{ - char *colon = strchr(u_colon_g, ':'), u[33]; - struct passwd *p; - struct group *g; - int ulen; - - if (!colon) - return 1; - - ulen = lws_ptr_diff(colon, u_colon_g); - if (ulen < 2 || ulen > (int)sizeof(u) - 1) - return 1; - - memcpy(u, u_colon_g, ulen); - u[ulen] = '\0'; - - colon++; - - g = getgrnam(colon); - if (!g) { - lwsl_err("%s: unknown group '%s'\n", __func__, colon); - - return 1; - } - *pgid = g->gr_gid; - - p = getpwnam(u); - if (!p) { - lwsl_err("%s: unknown group '%s'\n", __func__, u); - - return 1; - } - *puid = p->pw_uid; - - return 0; -} - -int -lws_plat_drop_app_privileges(struct lws_context *context, int actually_drop) -{ - struct passwd *p; - struct group *g; - - /* if he gave us the groupname, align gid to match it */ - - if (context->groupname) { - g = getgrnam(context->groupname); - - if (g) { - lwsl_info("%s: group %s -> gid %u\n", __func__, - context->groupname, g->gr_gid); - context->gid = g->gr_gid; - } else { - lwsl_err("%s: unknown groupname '%s'\n", __func__, - context->groupname); - - return 1; - } - } - - /* if he gave us the username, align uid to match it */ - - if (context->username) { - p = getpwnam(context->username); - - if (p) { - context->uid = p->pw_uid; - - lwsl_info("%s: username %s -> uid %u\n", __func__, - context->username, (unsigned int)p->pw_uid); - } else { - lwsl_err("%s: unknown username %s\n", __func__, - context->username); - - return 1; - } - } - - if (!actually_drop) - return 0; - - /* if he gave us the gid or we have it from the groupname, set it */ - - if (context->gid && context->gid != -1) { - g = getgrgid(context->gid); - - if (!g) { - lwsl_err("%s: cannot find name for gid %d\n", - __func__, context->gid); - - return 1; - } - - if (setgid(context->gid)) { - lwsl_err("%s: setgid: %s failed\n", __func__, - strerror(LWS_ERRNO)); - - return 1; - } - - lwsl_notice("%s: effective group '%s'\n", __func__, - g->gr_name); - } else - lwsl_info("%s: not changing group\n", __func__); - - - /* if he gave us the uid or we have it from the username, set it */ - - if (context->uid && context->uid != -1) { - p = getpwuid(context->uid); - - if (!p) { - lwsl_err("%s: getpwuid: unable to find uid %d\n", - __func__, context->uid); - return 1; - } - -#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) - _lws_plat_apply_caps(CAP_PERMITTED, context->caps, - context->count_caps); -#endif - - initgroups(p->pw_name, context->gid); - if (setuid(context->uid)) { - lwsl_err("%s: setuid: %s failed\n", __func__, - strerror(LWS_ERRNO)); - - return 1; - } else - lwsl_notice("%s: effective user '%s'\n", - __func__, p->pw_name); - -#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) - _lws_plat_apply_caps(CAP_EFFECTIVE, context->caps, - context->count_caps); - - if (context->count_caps) { - int n; - for (n = 0; n < context->count_caps; n++) - lwsl_notice(" RETAINING CAP %d\n", - (int)context->caps[n]); - } -#endif - } else - lwsl_info("%s: not changing user\n", __func__); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/plat/unix/unix-fds.c libwebsockets-2.4.2/lib/plat/unix/unix-fds.c --- libwebsockets-4.0.20/lib/plat/unix/unix-fds.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/unix-fds.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,179 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#include "private-lib-core.h" - -struct lws * -wsi_from_fd(const struct lws_context *context, int fd) -{ - struct lws **p, **done; - - if (!context->max_fds_unrelated_to_ulimit) - return context->lws_lookup[fd - lws_plat_socket_offset()]; - - /* slow fds handling */ - - p = context->lws_lookup; - done = &p[context->max_fds]; - - while (p != done) { - if (*p && (*p)->desc.sockfd == fd) - return *p; - p++; - } - - return NULL; -} - -int -insert_wsi(const struct lws_context *context, struct lws *wsi) -{ - struct lws **p, **done; - - if (!context->max_fds_unrelated_to_ulimit) { - assert(context->lws_lookup[wsi->desc.sockfd - - lws_plat_socket_offset()] == 0); - - context->lws_lookup[wsi->desc.sockfd - \ - lws_plat_socket_offset()] = wsi; - - return 0; - } - - /* slow fds handling */ - - p = context->lws_lookup; - done = &p[context->max_fds]; - -#if defined(_DEBUG) - - /* confirm it doesn't already exist */ - - while (p != done && *p != wsi) - p++; - - assert(p == done); - p = context->lws_lookup; - - /* confirm fd doesn't already exist */ - - while (p != done && (!*p || (*p)->desc.sockfd != wsi->desc.sockfd)) - p++; - - if (p != done) { - lwsl_err("%s: wsi %p already says it has fd %d\n", - __func__, *p, wsi->desc.sockfd); - assert(0); - } - p = context->lws_lookup; -#endif - - /* find an empty slot */ - - while (p != done && *p) - p++; - - if (p == done) { - lwsl_err("%s: reached max fds\n", __func__); - return 1; - } - - *p = wsi; - - return 0; -} - -void -delete_from_fd(const struct lws_context *context, int fd) -{ - - struct lws **p, **done; - - if (!context->max_fds_unrelated_to_ulimit) { - context->lws_lookup[fd - lws_plat_socket_offset()] = NULL; - - return; - } - - /* slow fds handling */ - - p = context->lws_lookup; - done = &p[context->max_fds]; - - /* find the match */ - - while (p != done && (!*p || (*p)->desc.sockfd != fd)) - p++; - - if (p == done) - lwsl_err("%s: fd %d not found\n", __func__, fd); - else - *p = NULL; - -#if defined(_DEBUG) - p = context->lws_lookup; - while (p != done && (!*p || (*p)->desc.sockfd != fd)) - p++; - - if (p != done) { - lwsl_err("%s: fd %d in lws_lookup again at %d\n", __func__, - fd, (int)(p - context->lws_lookup)); - assert(0); - } -#endif -} - -void -lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - - if (context->event_loop_ops->io) - context->event_loop_ops->io(wsi, LWS_EV_START | LWS_EV_READ); - - pt->fds[pt->fds_count++].revents = 0; -} - -void -lws_plat_delete_socket_from_fds(struct lws_context *context, - struct lws *wsi, int m) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - - if (context->event_loop_ops->io) - context->event_loop_ops->io(wsi, - LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); - - pt->fds_count--; -} - -int -lws_plat_change_pollfd(struct lws_context *context, - struct lws *wsi, struct lws_pollfd *pfd) -{ - return 0; -} diff -Nru libwebsockets-4.0.20/lib/plat/unix/unix-file.c libwebsockets-2.4.2/lib/plat/unix/unix-file.c --- libwebsockets-4.0.20/lib/plat/unix/unix-file.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/unix-file.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,177 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#include "private-lib-core.h" - -#include -#include - -#ifdef LWS_WITH_PLUGINS -#include -#endif -#include - -int lws_plat_apply_FD_CLOEXEC(int n) -{ - if (n == -1) - return 0; - - return fcntl(n, F_SETFD, FD_CLOEXEC); -} - -int -lws_plat_write_file(const char *filename, void *buf, int len) -{ - int m, fd; - - fd = lws_open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); - - if (fd == -1) - return 1; - - m = write(fd, buf, len); - close(fd); - - return m != len; -} - -int -lws_plat_read_file(const char *filename, void *buf, int len) -{ - int n, fd = lws_open(filename, O_RDONLY); - if (fd == -1) - return -1; - - n = read(fd, buf, len); - close(fd); - - return n; -} - -lws_fop_fd_t -_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, - const char *vpath, lws_fop_flags_t *flags) -{ - struct stat stat_buf; - int ret = lws_open(filename, (*flags) & LWS_FOP_FLAGS_MASK, 0664); - lws_fop_fd_t fop_fd; - - if (ret < 0) - return NULL; - - if (fstat(ret, &stat_buf) < 0) - goto bail; - - fop_fd = malloc(sizeof(*fop_fd)); - if (!fop_fd) - goto bail; - - fop_fd->fops = fops; - fop_fd->flags = *flags; - fop_fd->fd = ret; - fop_fd->filesystem_priv = NULL; /* we don't use it */ - fop_fd->len = stat_buf.st_size; - fop_fd->pos = 0; - - return fop_fd; - -bail: - close(ret); - return NULL; -} - -int -_lws_plat_file_close(lws_fop_fd_t *fop_fd) -{ - int fd = (*fop_fd)->fd; - - free(*fop_fd); - *fop_fd = NULL; - - return close(fd); -} - -lws_fileofs_t -_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) -{ - lws_fileofs_t r; - - if (offset > 0 && - offset > (lws_fileofs_t)fop_fd->len - (lws_fileofs_t)fop_fd->pos) - offset = fop_fd->len - fop_fd->pos; - - if ((lws_fileofs_t)fop_fd->pos + offset < 0) - offset = -fop_fd->pos; - - r = lseek(fop_fd->fd, offset, SEEK_CUR); - - if (r >= 0) - fop_fd->pos = r; - else - lwsl_err("error seeking from cur %ld, offset %ld\n", - (long)fop_fd->pos, (long)offset); - - return r; -} - -int -_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len) -{ - long n; - - n = read((int)fop_fd->fd, buf, len); - if (n == -1) { - *amount = 0; - return -1; - } - fop_fd->pos += n; - lwsl_debug("%s: read %ld of req %ld, pos %ld, len %ld\n", __func__, n, - (long)len, (long)fop_fd->pos, (long)fop_fd->len); - *amount = n; - - return 0; -} - -int -_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len) -{ - long n; - - n = write((int)fop_fd->fd, buf, len); - if (n == -1) { - *amount = 0; - return -1; - } - - fop_fd->pos += n; - *amount = n; - - return 0; -} - diff -Nru libwebsockets-4.0.20/lib/plat/unix/unix-init.c libwebsockets-2.4.2/lib/plat/unix/unix-init.c --- libwebsockets-4.0.20/lib/plat/unix/unix-init.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/unix-init.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,181 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#include "private-lib-core.h" - -#include -#include - -#ifdef LWS_WITH_PLUGINS -#include -#endif -#include - -#if defined(LWS_WITH_NETWORK) -static void -lws_sul_plat_unix(lws_sorted_usec_list_t *sul) -{ - struct lws_context_per_thread *pt = - lws_container_of(sul, struct lws_context_per_thread, sul_plat); - struct lws_context *context = pt->context; - -#if !defined(LWS_NO_DAEMONIZE) - /* if our parent went down, don't linger around */ - if (pt->context->started_with_parent && - kill(pt->context->started_with_parent, 0) < 0) - kill(getpid(), SIGTERM); -#endif - - if (pt->context->deprecated && !pt->context->count_wsi_allocated) { - lwsl_notice("%s: ending deprecated context\n", __func__); - kill(getpid(), SIGINT); - return; - } - - lws_check_deferred_free(context, 0, 0); - -#if defined(LWS_WITH_SERVER) - lws_context_lock(context, "periodic checks"); - lws_start_foreach_llp(struct lws_vhost **, pv, - context->no_listener_vhost_list) { - struct lws_vhost *v = *pv; - lwsl_debug("deferred iface: checking if on vh %s\n", (*pv)->name); - if (_lws_vhost_init_server(NULL, *pv) == 0) { - /* became happy */ - lwsl_notice("vh %s: became connected\n", v->name); - *pv = v->no_listener_vhost_list; - v->no_listener_vhost_list = NULL; - break; - } - } lws_end_foreach_llp(pv, no_listener_vhost_list); - lws_context_unlock(context); -#endif - - __lws_sul_insert(&pt->pt_sul_owner, &pt->sul_plat, 30 * LWS_US_PER_SEC); -} -#endif - -int -lws_plat_init(struct lws_context *context, - const struct lws_context_creation_info *info) -{ - int fd; -#if defined(LWS_WITH_NETWORK) - /* - * master context has the process-global fd lookup array. This can be - * done two different ways now; one or the other is done depending on if - * info->fd_limit_per_thread was snonzero - * - * - default: allocate a worst-case lookup array sized for ulimit -n - * and use the fd directly as an index into it - * - * - slow: allocate context->max_fds entries only (which can be - * forced at context creation time to be - * info->fd_limit_per_thread * the number of threads) - * and search the array to lookup fds - * - * the default way is optimized for server, if you only use one or two - * client wsi the slow way may save a lot of memory. - * - * Both ways allocate an array of struct lws *... one allocates it for - * all possible fd indexes the process could produce and uses it as a - * map, the other allocates for an amount of wsi the lws context is - * expected to use and searches through it to manipulate it. - */ - - context->lws_lookup = lws_zalloc(sizeof(struct lws *) * - context->max_fds, "lws_lookup"); - - if (!context->lws_lookup) { - lwsl_err("%s: OOM on alloc lws_lookup array for %d conn\n", - __func__, context->max_fds); - return 1; - } - - lwsl_info(" mem: platform fd map: %5lu B\n", - (unsigned long)(sizeof(struct lws *) * context->max_fds)); -#endif -#if defined(LWS_WITH_FILE_OPS) - fd = lws_open(SYSTEM_RANDOM_FILEPATH, O_RDONLY); -#else - fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY); -#endif - context->fd_random = fd; - if (context->fd_random < 0) { - lwsl_err("Unable to open random device %s %d\n", - SYSTEM_RANDOM_FILEPATH, context->fd_random); - return 1; - } - -#if defined(LWS_WITH_PLUGINS) - if (info->plugin_dirs) - lws_plat_plugins_init(context, info->plugin_dirs); -#endif - - -#if defined(LWS_WITH_NETWORK) - /* we only need to do this on pt[0] */ - - context->pt[0].sul_plat.cb = lws_sul_plat_unix; - __lws_sul_insert(&context->pt[0].pt_sul_owner, &context->pt[0].sul_plat, - 30 * LWS_US_PER_SEC); -#endif - - return 0; -} - -int -lws_plat_context_early_init(void) -{ -#if !defined(LWS_AVOID_SIGPIPE_IGN) - signal(SIGPIPE, SIG_IGN); -#endif - - return 0; -} - -void -lws_plat_context_early_destroy(struct lws_context *context) -{ -} - -void -lws_plat_context_late_destroy(struct lws_context *context) -{ -#ifdef LWS_WITH_PLUGINS - if (context->plugin_list) - lws_plat_plugins_destroy(context); -#endif -#if defined(LWS_WITH_NETWORK) - if (context->lws_lookup) - lws_free_set_NULL(context->lws_lookup); -#endif - if (!context->fd_random) - lwsl_err("ZERO RANDOM FD\n"); - if (context->fd_random != LWS_INVALID_FILE) - close(context->fd_random); -} diff -Nru libwebsockets-4.0.20/lib/plat/unix/unix-misc.c libwebsockets-2.4.2/lib/plat/unix/unix-misc.c --- libwebsockets-4.0.20/lib/plat/unix/unix-misc.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/unix-misc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#include "private-lib-core.h" - -lws_usec_t -lws_now_usecs(void) -{ -#if defined(LWS_HAVE_CLOCK_GETTIME) - struct timespec ts; - - if (clock_gettime(CLOCK_MONOTONIC, &ts)) - return 0; - - return (((lws_usec_t)ts.tv_sec) * LWS_US_PER_SEC) + - ((lws_usec_t)ts.tv_nsec / LWS_NS_PER_US); -#else - struct timeval now; - - gettimeofday(&now, NULL); - return (((lws_usec_t)now.tv_sec) * LWS_US_PER_SEC) + - (lws_usec_t)now.tv_usec; -#endif -} - -size_t -lws_get_random(struct lws_context *context, void *buf, size_t len) -{ -#if defined(__COVERITY__) - memset(buf, 0, len); - return len; -#else - /* coverity[tainted_scalar] */ - return (size_t)read(context->fd_random, (char *)buf, len); -#endif -} - -void lwsl_emit_syslog(int level, const char *line) -{ - int syslog_level = LOG_DEBUG; - - switch (level) { - case LLL_ERR: - syslog_level = LOG_ERR; - break; - case LLL_WARN: - syslog_level = LOG_WARNING; - break; - case LLL_NOTICE: - syslog_level = LOG_NOTICE; - break; - case LLL_INFO: - syslog_level = LOG_INFO; - break; - } - syslog(syslog_level, "%s", line); -} - - -int -lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf, - int len) -{ - int n; - - n = write(fd, buf, len); - - fsync(fd); - if (lseek(fd, 0, SEEK_SET) < 0) - return 1; - - return n != len; -} - - -int -lws_plat_recommended_rsa_bits(void) -{ - return 4096; -} diff -Nru libwebsockets-4.0.20/lib/plat/unix/unix-pipe.c libwebsockets-2.4.2/lib/plat/unix/unix-pipe.c --- libwebsockets-4.0.20/lib/plat/unix/unix-pipe.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/unix-pipe.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#include "private-lib-core.h" - - -int -lws_plat_pipe_create(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; -#if defined(LWS_HAVE_EVENTFD) - pt->dummy_pipe_fds[0] = eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK); - pt->dummy_pipe_fds[1] = -1; - return pt->dummy_pipe_fds[0]<0?-1:0; -#elif defined(LWS_HAVE_PIPE2) - return pipe2(pt->dummy_pipe_fds, O_NONBLOCK); -#else - return pipe(pt->dummy_pipe_fds); -#endif -} - -int -lws_plat_pipe_signal(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; -#if defined(LWS_HAVE_EVENTFD) - eventfd_t value = 1; - return eventfd_write(pt->dummy_pipe_fds[0], value); -#else - char buf = 0; - int n; - - n = write(pt->dummy_pipe_fds[1], &buf, 1); - - return n != 1; -#endif -} - -void -lws_plat_pipe_close(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - if (pt->dummy_pipe_fds[0] && pt->dummy_pipe_fds[0] != -1) - close(pt->dummy_pipe_fds[0]); - if (pt->dummy_pipe_fds[1] && pt->dummy_pipe_fds[1] != -1) - close(pt->dummy_pipe_fds[1]); - - pt->dummy_pipe_fds[0] = pt->dummy_pipe_fds[1] = -1; -} - diff -Nru libwebsockets-4.0.20/lib/plat/unix/unix-plugins.c libwebsockets-2.4.2/lib/plat/unix/unix-plugins.c --- libwebsockets-4.0.20/lib/plat/unix/unix-plugins.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/unix-plugins.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,182 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#include "private-lib-core.h" - -#include -#include - -#ifdef LWS_WITH_PLUGINS -#include -#endif -#include - -static int filter(const struct dirent *ent) -{ - if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) - return 0; - - return 1; -} - -int -lws_plat_plugins_init(struct lws_context * context, const char * const *d) -{ - struct lws_plugin_capability lcaps; - struct lws_plugin *plugin; - lws_plugin_init_func initfunc; - struct dirent **namelist; - int n, i, m, ret = 0; - char path[256]; - void *l; - -#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) - if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) - return lws_uv_plugins_init(context, d); -#endif - - lwsl_notice(" Plugins:\n"); - - while (d && *d) { - n = scandir(*d, &namelist, filter, alphasort); - if (n < 0) { - lwsl_err("Scandir on %s failed\n", *d); - return 1; - } - - for (i = 0; i < n; i++) { - if (strlen(namelist[i]->d_name) < 7) - goto inval; - - lwsl_notice(" %s\n", namelist[i]->d_name); - - lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d, - namelist[i]->d_name); - l = dlopen(path, RTLD_NOW); - if (!l) { - lwsl_err("Error loading DSO: %s\n", dlerror()); - while (i++ < n) - free(namelist[i]); - goto bail; - } - /* we could open it, can we get his init function? */ - m = lws_snprintf(path, sizeof(path) - 1, "init_%s", - namelist[i]->d_name + 3 /* snip lib... */); - path[m - 3] = '\0'; /* snip the .so */ - initfunc = dlsym(l, path); - if (!initfunc) { - lwsl_err("%s: Failed to get init '%s' on %s: %s\n", - __func__, path, namelist[i]->d_name, dlerror()); - goto skip; - } - lcaps.api_magic = LWS_PLUGIN_API_MAGIC; - m = initfunc(context, &lcaps); - if (m) { - lwsl_err("Initializing %s failed %d\n", - namelist[i]->d_name, m); - goto skip; - } - - plugin = lws_malloc(sizeof(*plugin), "plugin"); - if (!plugin) { - dlclose(l); - lwsl_err("OOM\n"); - goto bail; - } - plugin->list = context->plugin_list; - context->plugin_list = plugin; - lws_strncpy(plugin->name, namelist[i]->d_name, - sizeof(plugin->name)); - plugin->l = l; - plugin->caps = lcaps; - context->plugin_protocol_count += lcaps.count_protocols; - context->plugin_extension_count += lcaps.count_extensions; - - free(namelist[i]); - continue; - - skip: - dlclose(l); - inval: - free(namelist[i]); - } - free(namelist); - d++; - } - - return 0; - -bail: - free(namelist); - - return ret; -} - -int -lws_plat_plugins_destroy(struct lws_context * context) -{ - struct lws_plugin *plugin = context->plugin_list, *p; - lws_plugin_destroy_func func; - char path[256]; - int m; - -#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) - if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) - return lws_uv_plugins_destroy(context); -#endif - - if (!plugin) - return 0; - - lwsl_notice("%s\n", __func__); - - while (plugin) { - p = plugin; - m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", - plugin->name + 3); - path[m - 3] = '\0'; - func = dlsym(plugin->l, path); - if (!func) { - lwsl_err("Failed to get destroy on %s: %s", - plugin->name, dlerror()); - goto next; - } - m = func(context); - if (m) - lwsl_err("Initializing %s failed %d\n", - plugin->name, m); -next: - dlclose(p->l); - plugin = p->list; - p->list = NULL; - free(p); - } - - context->plugin_list = NULL; - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/plat/unix/unix-resolv.c libwebsockets-2.4.2/lib/plat/unix/unix-resolv.c --- libwebsockets-4.0.20/lib/plat/unix/unix-resolv.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/unix-resolv.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -lws_async_dns_server_check_t -lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46) -{ - lws_async_dns_server_check_t s = LADNS_CONF_SERVER_CHANGED; - char resolv[512], ads[48]; - lws_sockaddr46 sa46t; - lws_tokenize_t ts; - int fd, n, ns = 0; - - /* grab the first chunk of /etc/resolv.conf */ - - fd = open("/etc/resolv.conf", LWS_O_RDONLY); - if (fd < 0) - return LADNS_CONF_SERVER_UNKNOWN; - - n = read(fd, resolv, sizeof(resolv) - 1); - close(fd); - if (n < 0) - return LADNS_CONF_SERVER_UNKNOWN; - - resolv[n] = '\0'; - lws_tokenize_init(&ts, resolv, LWS_TOKENIZE_F_DOT_NONTERM | - LWS_TOKENIZE_F_NO_FLOATS | - LWS_TOKENIZE_F_NO_INTEGERS | - LWS_TOKENIZE_F_MINUS_NONTERM | - LWS_TOKENIZE_F_HASH_COMMENT); - do { - ts.e = lws_tokenize(&ts); - if (ts.e != LWS_TOKZE_TOKEN) { - ns = 0; - continue; - } - - if (!ns && !strncmp("nameserver", ts.token, ts.token_len)) { - ns = 1; - continue; - } - if (!ns) - continue; - - /* we are a token just after the "nameserver" token */ - - ns = 0; - if (ts.token_len > (int)sizeof(ads) - 1) - continue; - - memcpy(ads, ts.token, ts.token_len); - ads[ts.token_len] = '\0'; - if (lws_sa46_parse_numeric_address(ads, &sa46t) < 0) - continue; - - if (!lws_sa46_compare_ads(sa46, &sa46t)) - s = LADNS_CONF_SERVER_SAME; - - *sa46 = sa46t; - - return s; - - } while (ts.e > 0); - - return LADNS_CONF_SERVER_UNKNOWN; -} diff -Nru libwebsockets-4.0.20/lib/plat/unix/unix-service.c libwebsockets-2.4.2/lib/plat/unix/unix-service.c --- libwebsockets-4.0.20/lib/plat/unix/unix-service.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/unix-service.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,222 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#include "private-lib-core.h" - -int -lws_poll_listen_fd(struct lws_pollfd *fd) -{ - return poll(fd, 1, 0); -} - -int -_lws_plat_service_forced_tsi(struct lws_context *context, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - int m, n, r; - - r = lws_service_flag_pending(context, tsi); - - /* any socket with events to service? */ - for (n = 0; n < (int)pt->fds_count; n++) { - if (!pt->fds[n].revents) - continue; - - m = lws_service_fd_tsi(context, &pt->fds[n], tsi); - if (m < 0) { - lwsl_err("%s: lws_service_fd_tsi returned %d\n", - __func__, m); - return -1; - } - /* if something closed, retry this slot */ - if (m) - n--; - } - - lws_service_do_ripe_rxflow(pt); - - return r; -} - -#define LWS_POLL_WAIT_LIMIT 2000000000 - -int -_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) -{ - volatile struct lws_foreign_thread_pollfd *ftp, *next; - volatile struct lws_context_per_thread *vpt; - struct lws_context_per_thread *pt; - lws_usec_t timeout_us, us; - int n = -1; -#if (defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)) || defined(LWS_WITH_TLS) - int m; -#endif - - /* stay dead once we are dead */ - - if (!context || !context->vhost_list) - return 1; - - pt = &context->pt[tsi]; - vpt = (volatile struct lws_context_per_thread *)pt; - - lws_stats_bump(pt, LWSSTATS_C_SERVICE_ENTRY, 1); - - if (timeout_ms < 0) - timeout_ms = 0; - else - /* force a default timeout of 23 days */ - timeout_ms = LWS_POLL_WAIT_LIMIT; - timeout_us = ((lws_usec_t)timeout_ms) * LWS_US_PER_MS; - - if (context->event_loop_ops->run_pt) - context->event_loop_ops->run_pt(context, tsi); - - if (!pt->service_tid_detected) { - struct lws _lws; - - memset(&_lws, 0, sizeof(_lws)); - _lws.context = context; - - pt->service_tid = context->vhost_list->protocols[0].callback( - &_lws, LWS_CALLBACK_GET_THREAD_ID, - NULL, NULL, 0); - pt->service_tid_detected = 1; - } - - us = lws_now_usecs(); - lws_pt_lock(pt, __func__); - /* - * service ripe scheduled events, and limit wait to next expected one - */ - us = __lws_sul_service_ripe(&pt->pt_sul_owner, us); - if (us && us < timeout_us) - timeout_us = us; - - lws_pt_unlock(pt); - - /* - * is there anybody with pending stuff that needs service forcing? - */ - if (!lws_service_adjust_timeout(context, 1, tsi)) - timeout_us = 0; - - /* ensure we don't wrap at 2^31 with poll()'s signed int ms */ - - timeout_us /= LWS_US_PER_MS; /* ms now */ - - vpt->inside_poll = 1; - lws_memory_barrier(); - n = poll(pt->fds, pt->fds_count, timeout_us /* ms now */ ); - vpt->inside_poll = 0; - lws_memory_barrier(); - - #if defined(LWS_WITH_DETAILED_LATENCY) - /* - * so we can track how long it took before we actually read a - * POLLIN that was signalled when we last exited poll() - */ - if (context->detailed_latency_cb) - pt->ust_left_poll = lws_now_usecs(); -#endif - - /* Collision will be rare and brief. Spin until it completes */ - while (vpt->foreign_spinlock) - ; - - /* - * At this point we are not inside a foreign thread pollfd - * change, and we have marked ourselves as outside the poll() - * wait. So we are the only guys that can modify the - * lws_foreign_thread_pollfd list on the pt. Drain the list - * and apply the changes to the affected pollfds in the correct - * order. - */ - - lws_pt_lock(pt, __func__); - - ftp = vpt->foreign_pfd_list; - //lwsl_notice("cleared list %p\n", ftp); - while (ftp) { - struct lws *wsi; - struct lws_pollfd *pfd; - - next = ftp->next; - pfd = &vpt->fds[ftp->fd_index]; - if (lws_socket_is_valid(pfd->fd)) { - wsi = wsi_from_fd(context, pfd->fd); - if (wsi) - __lws_change_pollfd(wsi, ftp->_and, - ftp->_or); - } - lws_free((void *)ftp); - ftp = next; - } - vpt->foreign_pfd_list = NULL; - lws_memory_barrier(); - - lws_pt_unlock(pt); - -#if (defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)) || defined(LWS_WITH_TLS) - m = 0; -#endif -#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) - m |= !!pt->ws.rx_draining_ext_list; -#endif - -#if defined(LWS_WITH_TLS) - if (pt->context->tls_ops && - pt->context->tls_ops->fake_POLLIN_for_buffered) - m |= pt->context->tls_ops->fake_POLLIN_for_buffered(pt); -#endif - - if ( -#if (defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)) || defined(LWS_WITH_TLS) - !m && -#endif - !n) { /* nothing to do */ - lws_service_do_ripe_rxflow(pt); - - return 0; - } - - if (_lws_plat_service_forced_tsi(context, tsi) < 0) - return -1; - - if (pt->destroy_self) { - lws_context_destroy(pt->context); - return -1; - } - - return 0; -} - -int -lws_plat_service(struct lws_context *context, int timeout_ms) -{ - return _lws_plat_service_tsi(context, timeout_ms, 0); -} diff -Nru libwebsockets-4.0.20/lib/plat/unix/unix-sockets.c libwebsockets-2.4.2/lib/plat/unix/unix-sockets.c --- libwebsockets-4.0.20/lib/plat/unix/unix-sockets.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/unix/unix-sockets.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,461 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#include "private-lib-core.h" - -#include - -#if !defined(LWS_DETECTED_PLAT_IOS) -#include -#endif - -#include - -#include -#include - - - -int -lws_send_pipe_choked(struct lws *wsi) -{ - struct lws_pollfd fds; - struct lws *wsi_eff; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws && wsi->ws->tx_draining_ext) - return 1; -#endif - -#if defined(LWS_WITH_HTTP2) - wsi_eff = lws_get_network_wsi(wsi); -#else - wsi_eff = wsi; -#endif - - /* the fact we checked implies we avoided back-to-back writes */ - wsi_eff->could_have_pending = 0; - - /* treat the fact we got a truncated send pending as if we're choked */ - if (lws_has_buffered_out(wsi_eff) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - ||wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more -#endif - ) - return 1; - - fds.fd = wsi_eff->desc.sockfd; - fds.events = POLLOUT; - fds.revents = 0; - - if (poll(&fds, 1, 0) != 1) - return 1; - - if ((fds.revents & POLLOUT) == 0) - return 1; - - /* okay to send another packet without blocking */ - - return 0; -} - -int -lws_plat_set_nonblocking(lws_sockfd_type fd) -{ - return fcntl(fd, F_SETFL, O_NONBLOCK) < 0; -} - -int -lws_plat_set_socket_options(struct lws_vhost *vhost, int fd, int unix_skt) -{ - int optval = 1; - socklen_t optlen = sizeof(optval); - -#if defined(__APPLE__) || \ - defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ - defined(__NetBSD__) || \ - defined(__OpenBSD__) || \ - defined(__HAIKU__) - struct protoent *tcp_proto; -#endif - - (void)fcntl(fd, F_SETFD, FD_CLOEXEC); - - if (!unix_skt && vhost->ka_time) { - /* enable keepalive on this socket */ - optval = 1; - if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, - (const void *)&optval, optlen) < 0) - return 1; - -#if defined(__APPLE__) || \ - defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ - defined(__NetBSD__) || \ - defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun) || \ - defined(__HAIKU__) - - /* - * didn't find a way to set these per-socket, need to - * tune kernel systemwide values - */ -#else - /* set the keepalive conditions we want on it too */ - -#if defined(LWS_HAVE_TCP_USER_TIMEOUT) - optval = 1000 * (vhost->ka_time + - (vhost->ka_interval * vhost->ka_probes)); - if (setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, - (const void *)&optval, optlen) < 0) - return 1; -#endif - optval = vhost->ka_time; - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, - (const void *)&optval, optlen) < 0) - return 1; - - optval = vhost->ka_interval; - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, - (const void *)&optval, optlen) < 0) - return 1; - - optval = vhost->ka_probes; - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, - (const void *)&optval, optlen) < 0) - return 1; -#endif - } - -#if defined(SO_BINDTODEVICE) - if (!unix_skt && vhost->bind_iface && vhost->iface) { - lwsl_info("binding listen skt to %s using SO_BINDTODEVICE\n", vhost->iface); - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, vhost->iface, - strlen(vhost->iface)) < 0) { - lwsl_warn("Failed to bind to device %s\n", vhost->iface); - return 1; - } - } -#endif - - /* Disable Nagle */ - optval = 1; -#if defined (__sun) || defined(__QNX__) - if (!unix_skt && setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0) - return 1; -#elif !defined(__APPLE__) && \ - !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && \ - !defined(__NetBSD__) && \ - !defined(__OpenBSD__) && \ - !defined(__HAIKU__) - if (!unix_skt && setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0) - return 1; -#else - tcp_proto = getprotobyname("TCP"); - if (!unix_skt && setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen) < 0) - return 1; -#endif - - return lws_plat_set_nonblocking(fd); -} - - -/* cast a struct sockaddr_in6 * into addr for ipv6 */ - -int -lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, - size_t addrlen) -{ - int rc = LWS_ITOSA_NOT_EXIST; - - struct ifaddrs *ifr; - struct ifaddrs *ifc; -#ifdef LWS_WITH_IPV6 - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; -#endif - - getifaddrs(&ifr); - for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) { - if (!ifc->ifa_addr) - continue; - - lwsl_debug(" interface %s vs %s (fam %d) ipv6 %d\n", - ifc->ifa_name, ifname, - ifc->ifa_addr->sa_family, ipv6); - - if (strcmp(ifc->ifa_name, ifname)) - continue; - - switch (ifc->ifa_addr->sa_family) { -#if defined(AF_PACKET) - case AF_PACKET: - /* interface exists but is not usable */ - rc = LWS_ITOSA_NOT_USABLE; - continue; -#endif - - case AF_INET: -#ifdef LWS_WITH_IPV6 - if (ipv6) { - /* map IPv4 to IPv6 */ - memset((char *)&addr6->sin6_addr, 0, - sizeof(struct in6_addr)); - addr6->sin6_addr.s6_addr[10] = 0xff; - addr6->sin6_addr.s6_addr[11] = 0xff; - memcpy(&addr6->sin6_addr.s6_addr[12], - &((struct sockaddr_in *)ifc->ifa_addr)->sin_addr, - sizeof(struct in_addr)); - lwsl_debug("%s: uplevelling ipv4 bind to ipv6\n", __func__); - } else -#endif - memcpy(addr, - (struct sockaddr_in *)ifc->ifa_addr, - sizeof(struct sockaddr_in)); - break; -#ifdef LWS_WITH_IPV6 - case AF_INET6: - memcpy(&addr6->sin6_addr, - &((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr, - sizeof(struct in6_addr)); - break; -#endif - default: - continue; - } - rc = LWS_ITOSA_USABLE; - } - - freeifaddrs(ifr); - - if (rc) { - /* check if bind to IP address */ -#ifdef LWS_WITH_IPV6 - if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) - rc = LWS_ITOSA_USABLE; - else -#endif - if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1) - rc = LWS_ITOSA_USABLE; - } - - return rc; -} - - -const char * -lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) -{ - return inet_ntop(af, src, dst, cnt); -} - -int -lws_plat_inet_pton(int af, const char *src, void *dst) -{ - return inet_pton(af, src, dst); -} - -int -lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len) -{ -#if defined(__linux__) - struct ifreq i; - - memset(&i, 0, sizeof(i)); - lws_strncpy(i.ifr_name, ifname, sizeof(i.ifr_name)); - - if (ioctl(fd, SIOCGIFHWADDR, &i) < 0) - return -1; - - memcpy(hwaddr, &i.ifr_hwaddr.sa_data, 6); - - return 6; -#else - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -#endif -} - -int -lws_plat_rawudp_broadcast(uint8_t *p, const uint8_t *canned, int canned_len, - int n, int fd, const char *iface) -{ -#if defined(__linux__) - struct sockaddr_ll sll; - uint16_t *p16 = (uint16_t *)p; - uint32_t ucs = 0; - - memcpy(p, canned, canned_len); - - p[2] = n >> 8; - p[3] = n; - - while (p16 < (uint16_t *)(p + 20)) - ucs += ntohs(*p16++); - - ucs += ucs >> 16; - ucs ^= 0xffff; - - p[10] = ucs >> 8; - p[11] = ucs; - p[24] = (n - 20) >> 8; - p[25] = (n - 20); - - memset(&sll, 0, sizeof(sll)); - sll.sll_family = AF_PACKET; - sll.sll_protocol = htons(0x800); - sll.sll_halen = 6; - sll.sll_ifindex = if_nametoindex(iface); - memset(sll.sll_addr, 0xff, 6); - - return sendto(fd, p, n, 0, (struct sockaddr *)&sll, sizeof(sll)); -#else - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -#endif -} - -int -lws_plat_if_up(const char *ifname, int fd, int up) -{ -#if defined(__linux__) - struct ifreq ifr; - - memset(&ifr, 0, sizeof(ifr)); - lws_strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - - if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { - lwsl_err("%s: SIOCGIFFLAGS fail\n", __func__); - return 1; - } - - if (up) - ifr.ifr_flags |= IFF_UP; - else - ifr.ifr_flags &= ~IFF_UP; - - if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { - lwsl_err("%s: SIOCSIFFLAGS fail\n", __func__); - return 1; - } - - return 0; -#else - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -#endif -} - -int -lws_plat_BINDTODEVICE(lws_sockfd_type fd, const char *ifname) -{ -#if defined(__linux__) - struct ifreq i; - - memset(&i, 0, sizeof(i)); - i.ifr_addr.sa_family = AF_INET; - lws_strncpy(i.ifr_ifrn.ifrn_name, ifname, - sizeof(i.ifr_ifrn.ifrn_name)); - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &i, sizeof(i)) < 0) { - lwsl_notice("%s: failed %d\n", __func__, LWS_ERRNO); - return 1; - } - - return 0; -#else - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -#endif -} - -int -lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, - uint8_t *gateway_ip) -{ -#if defined(__linux__) - struct sockaddr_in *addr; - struct sockaddr_in sin; - struct rtentry route; - struct ifreq ifr; - - memset(&ifr, 0, sizeof(ifr)); - memset(&route, 0, sizeof(route)); - memset(&sin, 0, sizeof(sin)); - - lws_strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - - lws_plat_if_up(ifname, fd, 0); - - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(*(uint32_t *)ip); - - memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); - if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) { - lwsl_err("%s: SIOCSIFADDR fail\n", __func__); - return 1; - } - - sin.sin_addr.s_addr = htonl(*(uint32_t *)mask_ip); - memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); - if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0) { - lwsl_err("%s: SIOCSIFNETMASK fail\n", __func__); - return 1; - } - - lws_plat_if_up(ifname, fd, 1); - - addr = (struct sockaddr_in *)&route.rt_gateway; - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = htonl(*(uint32_t *)gateway_ip); - - addr = (struct sockaddr_in *)&route.rt_dst; - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = 0; - - addr = (struct sockaddr_in *)&route.rt_genmask; - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = 0; - - route.rt_flags = RTF_UP | RTF_GATEWAY; - route.rt_metric = 100; - route.rt_dev = (char *)ifname; - - if (ioctl(fd, SIOCADDRT, &route) < 0) { - lwsl_err("%s: SIOCADDRT 0x%x fail: %d\n", __func__, - (unsigned int)htonl(*(uint32_t *)gateway_ip), LWS_ERRNO); - return 1; - } - - return 0; -#else - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -#endif -} diff -Nru libwebsockets-4.0.20/lib/plat/windows/private-lib-plat-windows.h libwebsockets-2.4.2/lib/plat/windows/private-lib-plat-windows.h --- libwebsockets-4.0.20/lib/plat/windows/private-lib-plat-windows.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/windows/private-lib-plat-windows.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,150 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * Included from lib/private-lib-core.h if defined(WIN32) || defined(_WIN32) - */ - - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - - #if defined(WINVER) && (WINVER < 0x0501) - #undef WINVER - #undef _WIN32_WINNT - #define WINVER 0x0501 - #define _WIN32_WINNT WINVER - #endif - - #define LWS_NO_DAEMONIZE - #define LWS_ERRNO WSAGetLastError() - #define LWS_EAGAIN WSAEWOULDBLOCK - #define LWS_EALREADY WSAEALREADY - #define LWS_EINPROGRESS WSAEINPROGRESS - #define LWS_EINTR WSAEINTR - #define LWS_EISCONN WSAEISCONN - #define LWS_ENOTCONN WSAENOTCONN - #define LWS_EWOULDBLOCK WSAEWOULDBLOCK - #define LWS_EADDRINUSE WSAEADDRINUSE - #define MSG_NOSIGNAL 0 - #define SHUT_RDWR SD_BOTH - #define SOL_TCP IPPROTO_TCP - #define SHUT_WR SD_SEND - - #define compatible_close(fd) closesocket(fd) - #define lws_set_blocking_send(wsi) wsi->sock_send_blocking = 1 - - #include - #include - #include - #include - #ifdef LWS_HAVE_IN6ADDR_H - #include - #endif - #include - #include - - #if !defined(LWS_HAVE_ATOLL) - #if defined(LWS_HAVE__ATOI64) - #define atoll _atoi64 - #else - #warning No atoll or _atoi64 available, using atoi - #define atoll atoi - #endif - #endif - - #ifndef __func__ - #define __func__ __FUNCTION__ - #endif - - #ifdef LWS_HAVE__VSNPRINTF - #define vsnprintf _vsnprintf - #endif - -/* we don't have an implementation for this on windows... */ -int kill(int pid, int sig); -int fork(void); -#ifndef SIGINT -#define SIGINT 2 -#endif - -#include - -#ifndef BIG_ENDIAN - #define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */ -#endif -#ifndef LITTLE_ENDIAN - #define LITTLE_ENDIAN 1234 -#endif -#ifndef BYTE_ORDER - #define BYTE_ORDER LITTLE_ENDIAN -#endif - -#undef __P -#ifndef __P - #if __STDC__ - #define __P(protos) protos - #else - #define __P(protos) () - #endif -#endif - -#ifdef _WIN32 - #ifndef FD_HASHTABLE_MODULUS - #define FD_HASHTABLE_MODULUS 32 - #endif -#endif - -#define lws_plat_socket_offset() (0) - -struct lws; -struct lws_context; - -#define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS) -struct lws_fd_hashtable { - struct lws **wsi; - int length; -}; - - -#ifdef LWS_DLL -#ifdef LWS_INTERNAL -#define LWS_EXTERN extern __declspec(dllexport) -#else -#define LWS_EXTERN extern __declspec(dllimport) -#endif -#else -#define LWS_EXTERN extern -#endif - -typedef SOCKET lws_sockfd_type; -typedef HANDLE lws_filefd_type; -#define LWS_WIN32_HANDLE_TYPES - -LWS_EXTERN struct lws * -wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd); - -LWS_EXTERN int -insert_wsi(struct lws_context *context, struct lws *wsi); - -LWS_EXTERN int -delete_from_fd(struct lws_context *context, lws_sockfd_type fd); diff -Nru libwebsockets-4.0.20/lib/plat/windows/windows-fds.c libwebsockets-2.4.2/lib/plat/windows/windows-fds.c --- libwebsockets-4.0.20/lib/plat/windows/windows-fds.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/windows/windows-fds.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif -#include "private-lib-core.h" - -struct lws * -wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd) -{ - int h = LWS_FD_HASH(fd); - int n = 0; - - for (n = 0; n < context->fd_hashtable[h].length; n++) - if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) - return context->fd_hashtable[h].wsi[n]; - - return NULL; -} - -int -insert_wsi(struct lws_context *context, struct lws *wsi) -{ - int h = LWS_FD_HASH(wsi->desc.sockfd); - - if (context->fd_hashtable[h].length == (getdtablesize() - 1)) { - lwsl_err("hash table overflow\n"); - return 1; - } - - context->fd_hashtable[h].wsi[context->fd_hashtable[h].length++] = wsi; - - return 0; -} - -int -delete_from_fd(struct lws_context *context, lws_sockfd_type fd) -{ - int h = LWS_FD_HASH(fd); - int n = 0; - - for (n = 0; n < context->fd_hashtable[h].length; n++) - if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) { - while (n < context->fd_hashtable[h].length) { - context->fd_hashtable[h].wsi[n] = - context->fd_hashtable[h].wsi[n + 1]; - n++; - } - context->fd_hashtable[h].length--; - - return 0; - } - - lwsl_err("Failed to find fd %d requested for " - "delete in hashtable\n", fd); - return 1; -} diff -Nru libwebsockets-4.0.20/lib/plat/windows/windows-file.c libwebsockets-2.4.2/lib/plat/windows/windows-file.c --- libwebsockets-4.0.20/lib/plat/windows/windows-file.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/windows/windows-file.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,176 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif -#include "private-lib-core.h" - -int lws_plat_apply_FD_CLOEXEC(int n) -{ - return 0; -} - -lws_fop_fd_t -_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, - const char *vpath, lws_fop_flags_t *flags) -{ - HANDLE ret; - WCHAR buf[MAX_PATH]; - lws_fop_fd_t fop_fd; - LARGE_INTEGER llFileSize = {0}; - - MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, LWS_ARRAY_SIZE(buf)); - if (((*flags) & 7) == _O_RDONLY) { - ret = CreateFileW(buf, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - } else { - ret = CreateFileW(buf, GENERIC_WRITE, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - } - - if (ret == LWS_INVALID_FILE) - goto bail; - - fop_fd = malloc(sizeof(*fop_fd)); - if (!fop_fd) - goto bail; - - fop_fd->fops = fops; - fop_fd->fd = ret; - fop_fd->filesystem_priv = NULL; /* we don't use it */ - fop_fd->flags = *flags; - fop_fd->len = GetFileSize(ret, NULL); - if(GetFileSizeEx(ret, &llFileSize)) - fop_fd->len = llFileSize.QuadPart; - - fop_fd->pos = 0; - - return fop_fd; - -bail: - return NULL; -} - -int -_lws_plat_file_close(lws_fop_fd_t *fop_fd) -{ - HANDLE fd = (*fop_fd)->fd; - - free(*fop_fd); - *fop_fd = NULL; - - CloseHandle((HANDLE)fd); - - return 0; -} - -lws_fileofs_t -_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) -{ - LARGE_INTEGER l; - - l.QuadPart = offset; - return SetFilePointerEx((HANDLE)fop_fd->fd, l, NULL, FILE_CURRENT); -} - -int -_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len) -{ - DWORD _amount; - - if (!ReadFile((HANDLE)fop_fd->fd, buf, (DWORD)len, &_amount, NULL)) { - *amount = 0; - - return 1; - } - - fop_fd->pos += _amount; - *amount = (unsigned long)_amount; - - return 0; -} - -int -_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t* buf, lws_filepos_t len) -{ - DWORD _amount; - - if (!WriteFile((HANDLE)fop_fd->fd, buf, (DWORD)len, &_amount, NULL)) { - *amount = 0; - - return 1; - } - - fop_fd->pos += _amount; - *amount = (unsigned long)_amount; - - return 0; -} - - -int -lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf, - int len) -{ - int n; - - n = write(fd, buf, len); - - lseek(fd, 0, SEEK_SET); - - return n != len; -} - -int -lws_plat_write_file(const char *filename, void *buf, int len) -{ - int m, fd; - - fd = lws_open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); - - if (fd == -1) - return -1; - - m = write(fd, buf, len); - close(fd); - - return m != len; -} - -int -lws_plat_read_file(const char *filename, void *buf, int len) -{ - int n, fd = lws_open(filename, O_RDONLY); - if (fd == -1) - return -1; - - n = read(fd, buf, len); - close(fd); - - return n; -} - diff -Nru libwebsockets-4.0.20/lib/plat/windows/windows-init.c libwebsockets-2.4.2/lib/plat/windows/windows-init.c --- libwebsockets-4.0.20/lib/plat/windows/windows-init.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/windows/windows-init.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,116 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif -#include "private-lib-core.h" - -int -lws_plat_drop_app_privileges(struct lws_context *context, int actually_set) -{ - return 0; -} - -int -lws_plat_context_early_init(void) -{ - WORD wVersionRequested; - WSADATA wsaData; - int err; - - /* Use the MAKEWORD(lowbyte, highbyte) macro from Windef.h */ - wVersionRequested = MAKEWORD(2, 2); - - err = WSAStartup(wVersionRequested, &wsaData); - if (!err) - return 0; - /* - * Tell the user that we could not find a usable - * Winsock DLL - */ - lwsl_err("WSAStartup failed with error: %d\n", err); - - return 1; -} - -int -lws_plat_init(struct lws_context *context, - const struct lws_context_creation_info *info) -{ - struct lws_context_per_thread *pt = &context->pt[0]; - int i, n = context->count_threads; - - for (i = 0; i < FD_HASHTABLE_MODULUS; i++) { - context->fd_hashtable[i].wsi = - lws_zalloc(sizeof(struct lws*) * context->max_fds, - "win hashtable"); - - if (!context->fd_hashtable[i].wsi) - return -1; - } - - while (n--) { - pt->fds_count = 0; - pt->events = WSACreateEvent(); /* the cancel event */ - InitializeCriticalSection(&pt->interrupt_lock); - - pt++; - } - - context->fd_random = 0; - -#ifdef LWS_WITH_PLUGINS - if (info->plugin_dirs) - lws_plat_plugins_init(context, info->plugin_dirs); -#endif - - return 0; -} - -void -lws_plat_context_early_destroy(struct lws_context *context) -{ - struct lws_context_per_thread *pt = &context->pt[0]; - int n = context->count_threads; - - while (n--) { - WSACloseEvent(pt->events); - DeleteCriticalSection(&pt->interrupt_lock); - pt++; - } -} - -void -lws_plat_context_late_destroy(struct lws_context *context) -{ - int n; - - for (n = 0; n < FD_HASHTABLE_MODULUS; n++) { - if (context->fd_hashtable[n].wsi) - lws_free(context->fd_hashtable[n].wsi); - } - - WSACleanup(); -} diff -Nru libwebsockets-4.0.20/lib/plat/windows/windows-misc.c libwebsockets-2.4.2/lib/plat/windows/windows-misc.c --- libwebsockets-4.0.20/lib/plat/windows/windows-misc.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/windows/windows-misc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif -#include "private-lib-core.h" - - -lws_usec_t -lws_now_usecs(void) -{ -#ifndef DELTA_EPOCH_IN_MICROSECS -#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL -#endif - FILETIME filetime; - ULARGE_INTEGER datetime; - -#ifdef _WIN32_WCE - GetCurrentFT(&filetime); -#else - GetSystemTimeAsFileTime(&filetime); -#endif - - /* - * As per Windows documentation for FILETIME, copy the resulting - * FILETIME structure to a ULARGE_INTEGER structure using memcpy - * (using memcpy instead of direct assignment can prevent alignment - * faults on 64-bit Windows). - */ - memcpy(&datetime, &filetime, sizeof(datetime)); - - /* Windows file times are in 100s of nanoseconds. */ - return (datetime.QuadPart / 10) - DELTA_EPOCH_IN_MICROSECS; -} - - -#ifdef _WIN32_WCE -time_t time(time_t *t) -{ - time_t ret = lws_now_usecs() / 1000000; - - if(t != NULL) - *t = ret; - - return ret; -} -#endif - -size_t -lws_get_random(struct lws_context *context, void *buf, size_t len) -{ - size_t n; - char *p = (char *)buf; - - for (n = 0; n < len; n++) - p[n] = (unsigned char)rand(); - - return n; -} - - -void -lwsl_emit_syslog(int level, const char *line) -{ - lwsl_emit_stderr(level, line); -} - - -int kill(int pid, int sig) -{ - lwsl_err("Sorry Windows doesn't support kill()."); - exit(0); -} - -int fork(void) -{ - lwsl_err("Sorry Windows doesn't support fork()."); - exit(0); -} - - -int -lws_plat_recommended_rsa_bits(void) -{ - return 4096; -} - - - diff -Nru libwebsockets-4.0.20/lib/plat/windows/windows-pipe.c libwebsockets-2.4.2/lib/plat/windows/windows-pipe.c --- libwebsockets-4.0.20/lib/plat/windows/windows-pipe.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/windows/windows-pipe.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif -#include "private-lib-core.h" - -int -lws_plat_pipe_create(struct lws *wsi) -{ - return 1; -} - -int -lws_plat_pipe_signal(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - EnterCriticalSection(&pt->interrupt_lock); - pt->interrupt_requested = 1; - LeaveCriticalSection(&pt->interrupt_lock); - WSASetEvent(pt->events); /* trigger the cancel event */ - - return 0; -} - -void -lws_plat_pipe_close(struct lws *wsi) -{ -} diff -Nru libwebsockets-4.0.20/lib/plat/windows/windows-plugins.c libwebsockets-2.4.2/lib/plat/windows/windows-plugins.c --- libwebsockets-4.0.20/lib/plat/windows/windows-plugins.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/windows/windows-plugins.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif -#include "private-lib-core.h" - -int -lws_plat_plugins_init(struct lws_context * context, const char * const *d) -{ -#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) - if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) - return lws_uv_plugins_init(context, d); -#endif - - return 0; -} - -int -lws_plat_plugins_destroy(struct lws_context * context) -{ -#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) - if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) - return lws_uv_plugins_destroy(context); -#endif - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/plat/windows/windows-resolv.c libwebsockets-2.4.2/lib/plat/windows/windows-resolv.c --- libwebsockets-4.0.20/lib/plat/windows/windows-resolv.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/windows/windows-resolv.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -/* - * Adapted from tadns 1.1, from http://adns.sourceforge.net/ - * Original license --> - * - * Copyright (c) 2004-2005 Sergey Lyubka - * - * "THE BEER-WARE LICENSE" (Revision 42): - * Sergey Lyubka wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. - * - * Integrated into lws, largely rewritten and relicensed (as allowed above) - * - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -lws_async_dns_server_check_t -lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46) -{ - char subkey[512], value[128], *key = - "SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces"; - HKEY hKey, hSub; - LONG err; - int i, n; - - if ((err = RegOpenKey(HKEY_LOCAL_MACHINE, key, &hKey)) != ERROR_SUCCESS) { - lwsl_err("%s: cannot open reg key %s: %d\n", __func__, key, err); - - return 1; - } - - for (i = 0; RegEnumKey(hKey, i, subkey, sizeof(subkey)) == ERROR_SUCCESS; i++) { - DWORD type, len = sizeof(value); - - if (RegOpenKey(hKey, subkey, &hSub) == ERROR_SUCCESS && - (RegQueryValueEx(hSub, "NameServer", 0, - &type, value, &len) == ERROR_SUCCESS || - RegQueryValueEx(hSub, "DhcpNameServer", 0, - &type, value, &len) == ERROR_SUCCESS)) { - n = lws_sa46_parse_numeric_address(value, sa46); - RegCloseKey(hSub); - RegCloseKey(hKey); - return n == 0 ? LADNS_CONF_SERVER_CHANGED : - LADNS_CONF_SERVER_UNKNOWN; - } - } - RegCloseKey(hKey); - - return LADNS_CONF_SERVER_UNKNOWN; -} - diff -Nru libwebsockets-4.0.20/lib/plat/windows/windows-service.c libwebsockets-2.4.2/lib/plat/windows/windows-service.c --- libwebsockets-4.0.20/lib/plat/windows/windows-service.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/windows/windows-service.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,230 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif -#include "private-lib-core.h" - - -int -_lws_plat_service_forced_tsi(struct lws_context *context, int tsi) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - int m, n; - - lws_service_flag_pending(context, tsi); - - /* any socket with events to service? */ - for (n = 0; n < (int)pt->fds_count; n++) { - if (!pt->fds[n].revents) - continue; - - m = lws_service_fd_tsi(context, &pt->fds[n], tsi); - if (m < 0) - return -1; - /* if something closed, retry this slot */ - if (m) - n--; - } - - lws_service_do_ripe_rxflow(pt); - - return 0; -} - - -int -_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) -{ - struct lws_context_per_thread *pt; - WSANETWORKEVENTS networkevents; - struct lws_pollfd *pfd; - lws_usec_t timeout_us; - struct lws *wsi; - unsigned int i; - DWORD ev; - int n; - unsigned int eIdx; - int interrupt_requested; - - /* stay dead once we are dead */ - if (context == NULL || !context->vhost_list) - return 1; - - pt = &context->pt[tsi]; - - if (!pt->service_tid_detected) { - struct lws _lws; - - memset(&_lws, 0, sizeof(_lws)); - _lws.context = context; - - pt->service_tid = context->vhost_list-> - protocols[0].callback(&_lws, LWS_CALLBACK_GET_THREAD_ID, - NULL, NULL, 0); - pt->service_tid_detected = 1; - } - - if (timeout_ms < 0) - timeout_ms = 0; - else - /* force a default timeout of 23 days */ - timeout_ms = 2000000000; - timeout_us = ((lws_usec_t)timeout_ms) * LWS_US_PER_MS; - - if (context->event_loop_ops->run_pt) - context->event_loop_ops->run_pt(context, tsi); - - for (i = 0; i < pt->fds_count; ++i) { - pfd = &pt->fds[i]; - - if (!(pfd->events & LWS_POLLOUT)) - continue; - - wsi = wsi_from_fd(context, pfd->fd); - if (!wsi || wsi->listener) - continue; - if (wsi->sock_send_blocking) - continue; - pfd->revents = LWS_POLLOUT; - n = lws_service_fd(context, pfd); - if (n < 0) - return -1; - - /* - * Force WSAWaitForMultipleEvents() to check events - * and then return immediately. - */ - timeout_us = 0; - - /* if something closed, retry this slot */ - if (n) - i--; - } - - /* - * is there anybody with pending stuff that needs service forcing? - */ - if (!lws_service_adjust_timeout(context, 1, tsi)) - _lws_plat_service_forced_tsi(context, tsi); - - /* - * service pending callbakcs and get maximum wait time - */ - { - lws_usec_t us; - - lws_pt_lock(pt, __func__); - /* don't stay in poll wait longer than next hr timeout */ - us = __lws_sul_service_ripe(&pt->pt_sul_owner, lws_now_usecs()); - if (us && us < timeout_us) - timeout_us = us; - - lws_pt_unlock(pt); - } - - for (n = 0; n < (int)pt->fds_count; n++) - WSAEventSelect(pt->fds[n].fd, pt->events, - FD_READ | (!!(pt->fds[n].events & LWS_POLLOUT) * FD_WRITE) | - FD_OOB | FD_ACCEPT | - FD_CONNECT | FD_CLOSE | FD_QOS | - FD_ROUTING_INTERFACE_CHANGE | - FD_ADDRESS_LIST_CHANGE); - - ev = WSAWaitForMultipleEvents(1, &pt->events, FALSE, - (DWORD)(timeout_us / LWS_US_PER_MS), FALSE); - if (ev == WSA_WAIT_EVENT_0) { - EnterCriticalSection(&pt->interrupt_lock); - interrupt_requested = pt->interrupt_requested; - pt->interrupt_requested = 0; - LeaveCriticalSection(&pt->interrupt_lock); - if (interrupt_requested) { - lws_broadcast(pt, LWS_CALLBACK_EVENT_WAIT_CANCELLED, - NULL, 0); - return 0; - } - -#if defined(LWS_WITH_TLS) - if (pt->context->tls_ops && - pt->context->tls_ops->fake_POLLIN_for_buffered) - pt->context->tls_ops->fake_POLLIN_for_buffered(pt); -#endif - - for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) { - unsigned int err; - - if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, pt->events, - &networkevents) == SOCKET_ERROR) { - lwsl_err("WSAEnumNetworkEvents() failed " - "with error %d\n", LWS_ERRNO); - return -1; - } - - if (!networkevents.lNetworkEvents) - networkevents.lNetworkEvents = LWS_POLLOUT; - - pfd = &pt->fds[eIdx]; - pfd->revents = (short)networkevents.lNetworkEvents; - - err = networkevents.iErrorCode[FD_CONNECT_BIT]; - - if ((networkevents.lNetworkEvents & FD_CONNECT) && - err && err != LWS_EALREADY && - err != LWS_EINPROGRESS && err != LWS_EWOULDBLOCK && - err != WSAEINVAL) { - lwsl_debug("Unable to connect errno=%d\n", err); - pfd->revents |= LWS_POLLHUP; - } - - if (pfd->revents & LWS_POLLOUT) { - wsi = wsi_from_fd(context, pfd->fd); - if (wsi) - wsi->sock_send_blocking = 0; - } - /* if something closed, retry this slot */ - if (pfd->revents & LWS_POLLHUP) - --eIdx; - - if (pfd->revents) { - recv(pfd->fd, NULL, 0, 0); - lws_service_fd_tsi(context, pfd, tsi); - } - } - - return 0; - } - - // if (ev == WSA_WAIT_TIMEOUT) { } - // if (ev == WSA_WAIT_FAILED) - // return 0; - - return 0; -} - -int -lws_plat_service(struct lws_context *context, int timeout_ms) -{ - return _lws_plat_service_tsi(context, timeout_ms, 0); -} diff -Nru libwebsockets-4.0.20/lib/plat/windows/windows-sockets.c libwebsockets-2.4.2/lib/plat/windows/windows-sockets.c --- libwebsockets-4.0.20/lib/plat/windows/windows-sockets.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/plat/windows/windows-sockets.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,383 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif -#include "private-lib-core.h" - - -int -lws_send_pipe_choked(struct lws *wsi) -{ struct lws *wsi_eff; - -#if defined(LWS_WITH_HTTP2) - wsi_eff = lws_get_network_wsi(wsi); -#else - wsi_eff = wsi; -#endif - /* the fact we checked implies we avoided back-to-back writes */ - wsi_eff->could_have_pending = 0; - - /* treat the fact we got a truncated send pending as if we're choked */ - if (lws_has_buffered_out(wsi_eff) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - ||wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more -#endif - ) - return 1; - - return (int)wsi_eff->sock_send_blocking; -} - -int -lws_poll_listen_fd(struct lws_pollfd *fd) -{ - fd_set readfds; - struct timeval tv = { 0, 0 }; - - assert((fd->events & LWS_POLLIN) == LWS_POLLIN); - - FD_ZERO(&readfds); - FD_SET(fd->fd, &readfds); - - return select(((int)fd->fd) + 1, &readfds, NULL, NULL, &tv); -} - -int -lws_plat_set_nonblocking(lws_sockfd_type fd) -{ - u_long optl = 1; - int result = !!ioctlsocket(fd, FIONBIO, &optl); - if (result) - { - int error = LWS_ERRNO; - lwsl_err("ioctlsocket FIONBIO 1 failed with error %d\n", error); - } - return result; -} - -int -lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd, - int unix_skt) -{ - int optval = 1; - int optlen = sizeof(optval); - DWORD dwBytesRet; - struct tcp_keepalive alive; - int protonbr; -#ifndef _WIN32_WCE - struct protoent *tcp_proto; -#endif - - if (vhost->ka_time) { - /* enable keepalive on this socket */ - optval = 1; - if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, - (const char *)&optval, optlen) < 0) { - int error = LWS_ERRNO; - lwsl_err("setsockopt SO_KEEPALIVE 1 failed with error %d\n", error); - return 1; - } - - alive.onoff = TRUE; - alive.keepalivetime = vhost->ka_time * 1000; - alive.keepaliveinterval = vhost->ka_interval * 1000; - - if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), - NULL, 0, &dwBytesRet, NULL, NULL)) { - int error = LWS_ERRNO; - lwsl_err("WSAIoctl SIO_KEEPALIVE_VALS 1 %lu %lu failed with error %d\n", alive.keepalivetime, alive.keepaliveinterval, error); - return 1; - } - } - - /* Disable Nagle */ - optval = 1; -#ifndef _WIN32_WCE - tcp_proto = getprotobyname("TCP"); - if (!tcp_proto) { - int error = LWS_ERRNO; - lwsl_warn("getprotobyname(\"TCP\") failed with error, falling back to 6 %d\n", error); - protonbr = 6; /* IPPROTO_TCP */ - } else - protonbr = tcp_proto->p_proto; -#else - protonbr = 6; -#endif - - if (setsockopt(fd, protonbr, TCP_NODELAY, (const char *)&optval, optlen) ) { - int error = LWS_ERRNO; - lwsl_warn("setsockopt TCP_NODELAY 1 failed with error %d\n", error); - } - - - return lws_plat_set_nonblocking(fd); -} - - -int -lws_interface_to_sa(int ipv6, - const char *ifname, struct sockaddr_in *addr, size_t addrlen) -{ - long long address; -#ifdef LWS_WITH_IPV6 - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; - - if (ipv6) { - if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) { - return LWS_ITOSA_USABLE; - } - } -#endif - - address = inet_addr(ifname); - - if (address == INADDR_NONE) { - struct hostent *entry = gethostbyname(ifname); - if (entry) - address = ((struct in_addr *)entry->h_addr_list[0])->s_addr; - } - - if (address == INADDR_NONE) - return LWS_ITOSA_NOT_EXIST; - - addr->sin_addr.s_addr = (unsigned long)(lws_intptr_t)address; - - return LWS_ITOSA_USABLE; -} - -void -lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - int n = LWS_POLLIN | LWS_POLLHUP | FD_CONNECT; - - if (wsi->udp) { - lwsl_info("%s: UDP\n", __func__); - n = LWS_POLLIN; - } - - pt->fds[pt->fds_count++].revents = 0; - WSAEventSelect(wsi->desc.sockfd, pt->events, n); -} - -void -lws_plat_delete_socket_from_fds(struct lws_context *context, - struct lws *wsi, int m) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - - pt->fds_count--; -} - - -int -lws_plat_check_connection_error(struct lws *wsi) -{ - int optVal; - int optLen = sizeof(int); - - if (getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR, - (char*)&optVal, &optLen) != SOCKET_ERROR && optVal && - optVal != LWS_EALREADY && optVal != LWS_EINPROGRESS && - optVal != LWS_EWOULDBLOCK && optVal != WSAEINVAL) { - lwsl_debug("Connect failed SO_ERROR=%d\n", optVal); - return 1; - } - - return 0; -} - -int -lws_plat_change_pollfd(struct lws_context *context, - struct lws *wsi, struct lws_pollfd *pfd) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - long e = LWS_POLLHUP | FD_CONNECT; - - if ((pfd->events & LWS_POLLIN)) - e |= LWS_POLLIN; - - if ((pfd->events & LWS_POLLOUT)) - e |= LWS_POLLOUT; - - if (WSAEventSelect(wsi->desc.sockfd, pt->events, e) != SOCKET_ERROR) - return 0; - - lwsl_err("WSAEventSelect() failed with error %d\n", LWS_ERRNO); - - return 1; -} - -const char * -lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) -{ - WCHAR *buffer; - size_t bufferlen = (size_t)cnt; - BOOL ok = FALSE; - - buffer = lws_malloc(bufferlen * 2, "inet_ntop"); - if (!buffer) { - lwsl_err("Out of memory\n"); - return NULL; - } - - if (af == AF_INET) { - struct sockaddr_in srcaddr; - memset(&srcaddr, 0, sizeof(srcaddr)); - srcaddr.sin_family = AF_INET; - memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr)); - - if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, - sizeof(srcaddr), 0, buffer, - (LPDWORD)&bufferlen)) - ok = TRUE; -#ifdef LWS_WITH_IPV6 - } else if (af == AF_INET6) { - struct sockaddr_in6 srcaddr; - memset(&srcaddr, 0, sizeof(srcaddr)); - srcaddr.sin6_family = AF_INET6; - memcpy(&(srcaddr.sin6_addr), src, sizeof(srcaddr.sin6_addr)); - - if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, - sizeof(srcaddr), 0, buffer, - (LPDWORD)&bufferlen)) - ok = TRUE; -#endif - } else - lwsl_err("Unsupported type\n"); - - if (!ok) { - int rv = WSAGetLastError(); - lwsl_err("WSAAddressToString() : %d\n", rv); - } else { - if (WideCharToMultiByte(CP_ACP, 0, buffer, (int)bufferlen, dst, - cnt, 0, NULL) <= 0) - ok = FALSE; - } - - lws_free(buffer); - return ok ? dst : NULL; -} - -int -lws_plat_inet_pton(int af, const char *src, void *dst) -{ - WCHAR *buffer; - size_t bufferlen = strlen(src) + 1; - BOOL ok = FALSE; - - buffer = lws_malloc(bufferlen * 2, "inet_pton"); - if (!buffer) { - lwsl_err("Out of memory\n"); - return -1; - } - - if (MultiByteToWideChar(CP_ACP, 0, src, (int)bufferlen, buffer, - (int)bufferlen) <= 0) { - lwsl_err("Failed to convert multi byte to wide char\n"); - lws_free(buffer); - return -1; - } - - if (af == AF_INET) { - struct sockaddr_in dstaddr; - int dstaddrlen = sizeof(dstaddr); - - memset(&dstaddr, 0, sizeof(dstaddr)); - dstaddr.sin_family = AF_INET; - - if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) { - ok = TRUE; - memcpy(dst, &dstaddr.sin_addr, sizeof(dstaddr.sin_addr)); - } -#ifdef LWS_WITH_IPV6 - } else if (af == AF_INET6) { - struct sockaddr_in6 dstaddr; - int dstaddrlen = sizeof(dstaddr); - - memset(&dstaddr, 0, sizeof(dstaddr)); - dstaddr.sin6_family = AF_INET6; - - if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) { - ok = TRUE; - memcpy(dst, &dstaddr.sin6_addr, sizeof(dstaddr.sin6_addr)); - } -#endif - } else - lwsl_err("Unsupported type\n"); - - if (!ok) { - int rv = WSAGetLastError(); - lwsl_err("WSAAddressToString() : %d\n", rv); - } - - lws_free(buffer); - return ok ? 1 : -1; -} - -int -lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len) -{ - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -} - -int -lws_plat_rawudp_broadcast(uint8_t *p, const uint8_t *canned, int canned_len, - int n, int fd, const char *iface) -{ - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -} - -int -lws_plat_if_up(const char *ifname, int fd, int up) -{ - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -} - -int -lws_plat_BINDTODEVICE(lws_sockfd_type fd, const char *ifname) -{ - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -} - -int -lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, - uint8_t *gateway_ip) -{ - lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); - - return -1; -} - diff -Nru libwebsockets-4.0.20/lib/pollfd.c libwebsockets-2.4.2/lib/pollfd.c --- libwebsockets-4.0.20/lib/pollfd.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/pollfd.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,562 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +int +_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa) +{ + struct lws_context_per_thread *pt; + struct lws_context *context; + int ret = 0, pa_events = 1; + struct lws_pollfd *pfd; + int sampled_tid, tid; + + if (!wsi || wsi->position_in_fds_table < 0) + return 0; + + if (wsi->handling_pollout && !_and && _or == LWS_POLLOUT) { + /* + * Happening alongside service thread handling POLLOUT. + * The danger is when he is finished, he will disable POLLOUT, + * countermanding what we changed here. + * + * Instead of changing the fds, inform the service thread + * what happened, and ask it to leave POLLOUT active on exit + */ + wsi->leave_pollout_active = 1; + /* + * by definition service thread is not in poll wait, so no need + * to cancel service + */ + + lwsl_debug("%s: using leave_pollout_active\n", __func__); + + return 0; + } + + context = wsi->context; + pt = &context->pt[(int)wsi->tsi]; + assert(wsi->position_in_fds_table >= 0 && + wsi->position_in_fds_table < pt->fds_count); + + pfd = &pt->fds[wsi->position_in_fds_table]; + pa->fd = wsi->desc.sockfd; + pa->prev_events = pfd->events; + pa->events = pfd->events = (pfd->events & ~_and) | _or; + + if (wsi->http2_substream) + return 0; + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD, + wsi->user_space, (void *)pa, 0)) { + ret = -1; + goto bail; + } + + if (_and & LWS_POLLIN) { + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ); + lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ); + } + if (_or & LWS_POLLIN) { + lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ); + } + if (_and & LWS_POLLOUT) { + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + } + if (_or & LWS_POLLOUT) { + lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE); + lws_libevent_io(wsi, LWS_EV_START | LWS_EV_WRITE); + } + + /* + * if we changed something in this pollfd... + * ... and we're running in a different thread context + * than the service thread... + * ... and the service thread is waiting ... + * then cancel it to force a restart with our changed events + */ +#if LWS_POSIX + pa_events = pa->prev_events != pa->events; +#endif + + if (pa_events) { + + if (lws_plat_change_pollfd(context, wsi, pfd)) { + lwsl_info("%s failed\n", __func__); + ret = -1; + goto bail; + } + + sampled_tid = context->service_tid; + if (sampled_tid) { + tid = wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + if (tid == -1) { + ret = -1; + goto bail; + } + if (tid != sampled_tid) + lws_cancel_service_pt(wsi); + } + } +bail: + return ret; +} + +#ifndef LWS_NO_SERVER +static void +lws_accept_modulation(struct lws_context_per_thread *pt, int allow) +{ +// multithread listen seems broken +#if 0 + struct lws_vhost *vh = context->vhost_list; + struct lws_pollargs pa1; + + while (vh) { + if (allow) + _lws_change_pollfd(pt->wsi_listening, + 0, LWS_POLLIN, &pa1); + else + _lws_change_pollfd(pt->wsi_listening, + LWS_POLLIN, 0, &pa1); + vh = vh->vhost_next; + } +#endif +} +#endif + +int +insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 }; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int ret = 0; + + + lwsl_debug("%s: %p: tsi=%d, sock=%d, pos-in-fds=%d\n", + __func__, wsi, wsi->tsi, wsi->desc.sockfd, pt->fds_count); + + if ((unsigned int)pt->fds_count >= context->fd_limit_per_thread) { + lwsl_err("Too many fds (%d vs %d)\n", context->max_fds, + context->fd_limit_per_thread ); + return 1; + } + +#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266) + if (wsi->desc.sockfd >= context->max_fds) { + lwsl_err("Socket fd %d is too high (%d)\n", + wsi->desc.sockfd, context->max_fds); + return 1; + } +#endif + + assert(wsi); + assert(wsi->vhost); + assert(lws_socket_is_valid(wsi->desc.sockfd)); + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *) &pa, 1)) + return -1; + + lws_pt_lock(pt); + pt->count_conns++; + insert_wsi(context, wsi); +#if defined(LWS_WITH_ESP8266) + if (wsi->position_in_fds_table == -1) +#endif + wsi->position_in_fds_table = pt->fds_count; + + pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd; +#if LWS_POSIX + pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN; +#else + pt->fds[wsi->position_in_fds_table].events = 0; +#endif + pa.events = pt->fds[pt->fds_count].events; + + lws_plat_insert_socket_into_fds(context, wsi); + + /* external POLL support via protocol 0 */ + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD, + wsi->user_space, (void *) &pa, 0)) + ret = -1; +#ifndef LWS_NO_SERVER + /* if no more room, defeat accepts on this thread */ + if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1) + lws_accept_modulation(pt, 0); +#endif + lws_pt_unlock(pt); + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + wsi->user_space, (void *)&pa, 1)) + ret = -1; + + return ret; +} + +int +remove_wsi_socket_from_fds(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_pollargs pa = { wsi->desc.sockfd, 0, 0 }; +#if !defined(LWS_WITH_ESP8266) + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws *end_wsi; + int v; +#endif + int m, ret = 0; + + if (wsi->parent_carries_io) { + lws_same_vh_protocol_remove(wsi); + return 0; + } + +#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266) + if (wsi->desc.sockfd > context->max_fds) { + lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd, + context->max_fds); + return 1; + } +#endif + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *)&pa, 1)) + return -1; + + lws_same_vh_protocol_remove(wsi); + + /* the guy who is to be deleted's slot index in pt->fds */ + m = wsi->position_in_fds_table; + +#if !defined(LWS_WITH_ESP8266) + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | + LWS_EV_PREPARE_DELETION); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | + LWS_EV_PREPARE_DELETION); + + lws_pt_lock(pt); + + lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n", + __func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table, + pt->fds_count, pt->fds[pt->fds_count].fd); + + /* have the last guy take up the now vacant slot */ + pt->fds[m] = pt->fds[pt->fds_count - 1]; +#endif + /* this decrements pt->fds_count */ + lws_plat_delete_socket_from_fds(context, wsi, m); +#if !defined(LWS_WITH_ESP8266) + v = (int) pt->fds[m].fd; + /* end guy's "position in fds table" is now the deletion guy's old one */ + end_wsi = wsi_from_fd(context, v); + if (!end_wsi) { + lwsl_err("no wsi found for sock fd %d at pos %d, pt->fds_count=%d\n", + (int)pt->fds[m].fd, m, pt->fds_count); + assert(0); + } else + end_wsi->position_in_fds_table = m; + + /* deletion guy's lws_lookup entry needs nuking */ + delete_from_fd(context, wsi->desc.sockfd); + /* removed wsi has no position any more */ + wsi->position_in_fds_table = -1; + + /* remove also from external POLL support via protocol 0 */ + if (lws_socket_is_valid(wsi->desc.sockfd)) + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD, + wsi->user_space, (void *) &pa, 0)) + ret = -1; +#ifndef LWS_NO_SERVER + if (!context->being_destroyed) + /* if this made some room, accept connects on this thread */ + if ((unsigned int)pt->fds_count < context->fd_limit_per_thread - 1) + lws_accept_modulation(pt, 1); +#endif + lws_pt_unlock(pt); + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + wsi->user_space, (void *) &pa, 1)) + ret = -1; +#endif + return ret; +} + +int +lws_change_pollfd(struct lws *wsi, int _and, int _or) +{ + struct lws_context_per_thread *pt; + struct lws_context *context; + struct lws_pollargs pa; + int ret = 0; + + if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0) + return 1; + + context = lws_get_context(wsi); + if (!context) + return 1; + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *) &pa, 0)) + return -1; + + pt = &context->pt[(int)wsi->tsi]; + + lws_pt_lock(pt); + ret = _lws_change_pollfd(wsi, _and, _or, &pa); + lws_pt_unlock(pt); + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + wsi->user_space, (void *) &pa, 0)) + ret = -1; + + return ret; +} + +LWS_VISIBLE int +lws_callback_on_writable(struct lws *wsi) +{ + struct lws_context_per_thread *pt; +#ifdef LWS_WITH_HTTP2 + struct lws *network_wsi, *wsi2; + int already; +#endif + int n; + + if (wsi->state == LWSS_SHUTDOWN) + return 0; + + if (wsi->socket_is_permanently_unusable) + return 0; + + pt = &wsi->context->pt[(int)wsi->tsi]; + + if (wsi->parent_carries_io) { +#if defined(LWS_WITH_STATS) + if (!wsi->active_writable_req_us) { + wsi->active_writable_req_us = time_in_microseconds(); + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1); + } +#endif + n = lws_callback_on_writable(wsi->parent); + if (n < 0) + return n; + + wsi->parent_pending_cb_on_writable = 1; + return 1; + } + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_REQ, 1); +#if defined(LWS_WITH_STATS) + if (!wsi->active_writable_req_us) { + wsi->active_writable_req_us = time_in_microseconds(); + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1); + } +#endif + +#ifdef LWS_WITH_HTTP2 + lwsl_info("%s: %p\n", __func__, wsi); + + if (wsi->mode != LWSCM_HTTP2_SERVING) + goto network_sock; + + if (wsi->u.h2.requested_POLLOUT) { + lwsl_info("already pending writable\n"); + return 1; + } + + /* is this for DATA or for control messages? */ + if (wsi->upgraded_to_http2 && !wsi->u.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->u.h2.tx_cr); + wsi->u.h2.skint = 1; + return 0; + } + + wsi->u.h2.skint = 0; + network_wsi = lws_get_network_wsi(wsi); + already = network_wsi->u.h2.requested_POLLOUT; + + /* mark everybody above him as requesting pollout */ + + wsi2 = wsi; + while (wsi2) { + wsi2->u.h2.requested_POLLOUT = 1; + lwsl_info("mark %p pending writable\n", wsi2); + wsi2 = wsi2->u.h2.parent_wsi; + } + + /* for network action, act only on the network wsi */ + + wsi = network_wsi; + if (already) + return 1; +network_sock: +#endif + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0)) + return 1; + + if (wsi->position_in_fds_table < 0) { + lwsl_debug("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd); + return -1; + } + + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) + return -1; + + return 1; +} + +/* + * stitch protocol choice into the vh protocol linked list + * We always insert ourselves at the start of the list + * + * X <-> B + * X <-> pAn <-> pB + * + * Illegal to attach more than once without detach inbetween + */ +void +lws_same_vh_protocol_insert(struct lws *wsi, int n) +{ + if (wsi->same_vh_protocol_prev || wsi->same_vh_protocol_next) { + lws_same_vh_protocol_remove(wsi); + lwsl_notice("Attempted to attach wsi twice to same vh prot\n"); + } + + wsi->same_vh_protocol_prev = &wsi->vhost->same_vh_protocol_list[n]; + /* old first guy is our next */ + wsi->same_vh_protocol_next = wsi->vhost->same_vh_protocol_list[n]; + /* we become the new first guy */ + wsi->vhost->same_vh_protocol_list[n] = wsi; + + if (wsi->same_vh_protocol_next) + /* old first guy points back to us now */ + wsi->same_vh_protocol_next->same_vh_protocol_prev = + &wsi->same_vh_protocol_next; +} + +void +lws_same_vh_protocol_remove(struct lws *wsi) +{ + /* + * detach ourselves from vh protocol list if we're on one + * A -> B -> C + * A -> C , or, B -> C, or A -> B + * + * OK to call on already-detached wsi + */ + lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi); + + if (wsi->same_vh_protocol_prev) { + assert (*(wsi->same_vh_protocol_prev) == wsi); + lwsl_info("have prev %p, setting him to our next %p\n", + wsi->same_vh_protocol_prev, + wsi->same_vh_protocol_next); + + /* guy who pointed to us should point to our next */ + *(wsi->same_vh_protocol_prev) = wsi->same_vh_protocol_next; + } + + /* our next should point back to our prev */ + if (wsi->same_vh_protocol_next) { + wsi->same_vh_protocol_next->same_vh_protocol_prev = + wsi->same_vh_protocol_prev; + } + + wsi->same_vh_protocol_prev = NULL; + wsi->same_vh_protocol_next = NULL; +} + + +LWS_VISIBLE int +lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost, + const struct lws_protocols *protocol) +{ + struct lws *wsi; + + if (protocol < vhost->protocols || + protocol >= (vhost->protocols + vhost->count_protocols)) { + lwsl_err("%s: protocol %p is not from vhost %p (%p - %p)\n", + __func__, protocol, vhost->protocols, vhost, + (vhost->protocols + vhost->count_protocols)); + + return -1; + } + + wsi = vhost->same_vh_protocol_list[protocol - vhost->protocols]; + while (wsi) { + assert(wsi->protocol == protocol); + assert(*wsi->same_vh_protocol_prev == wsi); + if (wsi->same_vh_protocol_next) + assert(wsi->same_vh_protocol_next->same_vh_protocol_prev == + &wsi->same_vh_protocol_next); + + lws_callback_on_writable(wsi); + wsi = wsi->same_vh_protocol_next; + } + + return 0; +} + +LWS_VISIBLE int +lws_callback_on_writable_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol) +{ + struct lws_vhost *vhost; + int n; + + if (!context) + return 0; + + vhost = context->vhost_list; + + while (vhost) { + for (n = 0; n < vhost->count_protocols; n++) + if (protocol->callback == + vhost->protocols[n].callback && + !strcmp(protocol->name, vhost->protocols[n].name)) + break; + if (n != vhost->count_protocols) + lws_callback_on_writable_all_protocol_vhost( + vhost, &vhost->protocols[n]); + + vhost = vhost->vhost_next; + } + + return 0; +} diff -Nru libwebsockets-4.0.20/lib/private-libwebsockets.h libwebsockets-2.4.2/lib/private-libwebsockets.h --- libwebsockets-4.0.20/lib/private-libwebsockets.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/private-libwebsockets.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,2615 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2016 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "lws_config.h" +#include "lws_config_private.h" + + +#if defined(LWS_WITH_CGI) && defined(LWS_HAVE_VFORK) +#define _GNU_SOURCE +#endif + +#if defined(__COVERITY__) +typedef struct { long double x, y; } _Float128; +#endif + +#ifdef LWS_HAVE_SYS_TYPES_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(LWS_WITH_ESP32) +#define MSG_NOSIGNAL 0 +#define SOMAXCONN 3 +#endif + +#if defined(LWS_WITH_ESP8266) +#include +#define assert(n) + +/* rom-provided stdc functions for free, ensure use these instead of libc ones */ + +int ets_vsprintf(char *str, const char *format, va_list argptr); +int ets_vsnprintf(char *buffer, size_t sizeOfBuffer, const char *format, va_list argptr); +int ets_snprintf(char *str, size_t size, const char *format, ...); +int ets_sprintf(char *str, const char *format, ...); +int os_printf_plus(const char *format, ...); +#undef malloc +#undef realloc +#undef free +void *pvPortMalloc(size_t s, const char *f, int line); +#define malloc(s) pvPortMalloc(s, "", 0) +void *pvPortRealloc(void *p, size_t s, const char *f, int line); +#define realloc(p, s) pvPortRealloc(p, s, "", 0) +void vPortFree(void *p, const char *f, int line); +#define free(p) vPortFree(p, "", 0) +#undef memcpy +void *ets_memcpy(void *dest, const void *src, size_t n); +#define memcpy ets_memcpy +void *ets_memset(void *dest, int v, size_t n); +#define memset ets_memset +char *ets_strcpy(char *dest, const char *src); +#define strcpy ets_strcpy +char *ets_strncpy(char *dest, const char *src, size_t n); +#define strncpy ets_strncpy +char *ets_strstr(const char *haystack, const char *needle); +#define strstr ets_strstr +int ets_strcmp(const char *s1, const char *s2); +int ets_strncmp(const char *s1, const char *s2, size_t n); +#define strcmp ets_strcmp +#define strncmp ets_strncmp +size_t ets_strlen(const char *s); +#define strlen ets_strlen +void *ets_memmove(void *dest, const void *src, size_t n); +#define memmove ets_memmove +char *ets_strchr(const char *s, int c); +#define strchr_ets_strchr +#undef _DEBUG +#include + +#else +#define STORE_IN_ROM +#include +#endif +#if LWS_MAX_SMP > 1 +#include +#endif + +#ifdef LWS_HAVE_SYS_STAT_H +#include +#endif + +#if defined(WIN32) || defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#if (WINVER < 0x0501) +#undef WINVER +#undef _WIN32_WINNT +#define WINVER 0x0501 +#define _WIN32_WINNT WINVER +#endif +#define LWS_NO_DAEMONIZE +#define LWS_ERRNO WSAGetLastError() +#define LWS_EAGAIN WSAEWOULDBLOCK +#define LWS_EALREADY WSAEALREADY +#define LWS_EINPROGRESS WSAEINPROGRESS +#define LWS_EINTR WSAEINTR +#define LWS_EISCONN WSAEISCONN +#define LWS_EWOULDBLOCK WSAEWOULDBLOCK +#define MSG_NOSIGNAL 0 +#define SHUT_RDWR SD_BOTH +#define SOL_TCP IPPROTO_TCP +#define SHUT_WR SD_SEND + +#define compatible_close(fd) closesocket(fd) +#define lws_set_blocking_send(wsi) wsi->sock_send_blocking = 1 +#define lws_socket_is_valid(x) (!!x) +#define LWS_SOCK_INVALID 0 +#include +#include +#include +#include +#ifdef LWS_HAVE_IN6ADDR_H +#include +#endif +#include +#include + +#if !defined(LWS_HAVE_ATOLL) +#if defined(LWS_HAVE__ATOI64) +#define atoll _atoi64 +#else +#warning No atoll or _atoi64 available, using atoi +#define atoll atoi +#endif +#endif + +#ifndef __func__ +#define __func__ __FUNCTION__ +#endif + +#ifdef LWS_HAVE__VSNPRINTF +#define vsnprintf _vsnprintf +#endif + +/* we don't have an implementation for this on windows... */ +int kill(int pid, int sig); +int fork(void); +#ifndef SIGINT +#define SIGINT 2 +#endif + +#else /* not windows --> */ + +#include +#include +#include +#include + +#ifndef __cplusplus +#include +#endif +#include +#include +#ifdef LWS_WITH_ESP8266 +#include +#define vsnprintf ets_vsnprintf +#define snprintf ets_snprintf +#define sprintf ets_sprintf + +int kill(int pid, int sig); + +#else +#include +#endif +#ifdef LWS_WITH_HTTP_PROXY +#include +#include +#endif +#if defined(LWS_BUILTIN_GETIFADDRS) + #include "./misc/getifaddrs.h" +#else + #if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) + #if defined(__HAIKU__) + #define _BSD_SOURCE + #endif + #include + #endif +#endif +#if defined (__ANDROID__) +#include +#include +#elif defined (__sun) || defined(__HAIKU__) +#include +#else +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) +#include +#endif +#endif +#include +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) +#include +#include +#include +#include +#include +#include +#endif +#ifdef LWS_WITH_LIBEV +#include +#endif +#ifdef LWS_WITH_LIBUV +#include +#endif +#ifdef LWS_WITH_LIBEVENT +#include +#endif + +#ifndef LWS_NO_FORK +#ifdef LWS_HAVE_SYS_PRCTL_H +#include +#endif +#endif + +#include + +#define LWS_ERRNO errno +#define LWS_EAGAIN EAGAIN +#define LWS_EALREADY EALREADY +#define LWS_EINPROGRESS EINPROGRESS +#define LWS_EINTR EINTR +#define LWS_EISCONN EISCONN +#define LWS_EWOULDBLOCK EWOULDBLOCK + +#define lws_set_blocking_send(wsi) + +#if defined(LWS_WITH_ESP8266) +#define lws_socket_is_valid(x) ((x) != NULL) +#define LWS_SOCK_INVALID (NULL) +struct lws; +const char * +lws_plat_get_peer_simple(struct lws *wsi, char *name, int namelen); +#else +#define lws_socket_is_valid(x) (x >= 0) +#define LWS_SOCK_INVALID (-1) +#endif +#endif + +#ifndef LWS_HAVE_BZERO +#ifndef bzero +#define bzero(b, len) (memset((b), '\0', (len)), (void) 0) +#endif +#endif + +#ifndef LWS_HAVE_STRERROR +#define strerror(x) "" +#endif + +#ifdef LWS_OPENSSL_SUPPORT + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL +#include +#include +#else +#include +#include +#define OPENSSL_NO_TLSEXT +#endif /* not USE_OLD_CYASSL */ +#else +#if defined(LWS_WITH_ESP32) +#define OPENSSL_NO_TLSEXT +#else +#if defined(LWS_WITH_MBEDTLS) +#include +#include +#else +#include +#include +#include +#include +#include +#ifdef LWS_HAVE_OPENSSL_ECDH_H +#include +#endif +#include +#endif +#if defined(OPENSSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x0009080afL) +/* later openssl defines this to negate the presence of tlsext... but it was only + * introduced at 0.9.8j. Earlier versions don't know it exists so don't + * define it... making it look like the feature exists... + */ +#define OPENSSL_NO_TLSEXT +#endif +#endif +#endif /* not ESP32 */ +#endif /* not USE_WOLFSSL */ +#endif + +#include "libwebsockets.h" +#if defined(WIN32) || defined(_WIN32) +#else +static inline int compatible_close(int fd) { return close(fd); } +#endif + +#if defined(WIN32) || defined(_WIN32) +#include +#endif + +#if defined(LWS_WITH_ESP8266) +#undef compatible_close +#define compatible_close(fd) { fd->state=ESPCONN_CLOSE; espconn_delete(fd); } +lws_sockfd_type +esp8266_create_tcp_stream_socket(void); +void +esp8266_tcp_stream_bind(lws_sockfd_type fd, int port, struct lws *wsi); +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */ +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#endif + + +#if defined(WIN32) || defined(_WIN32) + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */ +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif + +#undef __P +#ifndef __P +#if __STDC__ +#define __P(protos) protos +#else +#define __P(protos) () +#endif +#endif + +#else + +#include +#include + +#if defined(__APPLE__) +#include +#elif defined(__FreeBSD__) +#include +#elif defined(__linux__) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__QNX__) + #include + #if defined(__LITTLEENDIAN__) + #define BYTE_ORDER __LITTLEENDIAN__ + #define LITTLE_ENDIAN __LITTLEENDIAN__ + #define BIG_ENDIAN 4321 /* to show byte order (taken from gcc); for suppres warning that BIG_ENDIAN is not defined. */ + #endif + #if defined(__BIGENDIAN__) + #define BYTE_ORDER __BIGENDIAN__ + #define LITTLE_ENDIAN 1234 /* to show byte order (taken from gcc); for suppres warning that LITTLE_ENDIAN is not defined. */ + #define BIG_ENDIAN __BIGENDIAN__ + #endif +#endif + +#if defined(__sun) && defined(__GNUC__) + +#include + +#if !defined (BYTE_ORDER) +# define BYTE_ORDER __BYTE_ORDER__ +#endif + +#if !defined(LITTLE_ENDIAN) +# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +#endif + +#if !defined(BIG_ENDIAN) +# define BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#endif + +#endif /* sun + GNUC */ + +#if !defined(BYTE_ORDER) +# define BYTE_ORDER __BYTE_ORDER +#endif +#if !defined(LITTLE_ENDIAN) +# define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif +#if !defined(BIG_ENDIAN) +# define BIG_ENDIAN __BIG_ENDIAN +#endif + +#endif + +/* + * Mac OSX as well as iOS do not define the MSG_NOSIGNAL flag, + * but happily have something equivalent in the SO_NOSIGPIPE flag. + */ +#ifdef __APPLE__ +#define MSG_NOSIGNAL SO_NOSIGPIPE +#endif + +/* + * Solaris 11.X only supports POSIX 2001, MSG_NOSIGNAL appears in + * POSIX 2008. + */ +#ifdef __sun +#define MSG_NOSIGNAL 0 +#endif + +#ifdef _WIN32 +#ifndef FD_HASHTABLE_MODULUS +#define FD_HASHTABLE_MODULUS 32 +#endif +#endif + +#ifndef LWS_DEF_HEADER_LEN +#define LWS_DEF_HEADER_LEN 4096 +#endif +#ifndef LWS_DEF_HEADER_POOL +#define LWS_DEF_HEADER_POOL 4 +#endif +#ifndef LWS_MAX_PROTOCOLS +#define LWS_MAX_PROTOCOLS 5 +#endif +#ifndef LWS_MAX_EXTENSIONS_ACTIVE +#define LWS_MAX_EXTENSIONS_ACTIVE 2 +#endif +#ifndef LWS_MAX_EXT_OFFERS +#define LWS_MAX_EXT_OFFERS 8 +#endif +#ifndef SPEC_LATEST_SUPPORTED +#define SPEC_LATEST_SUPPORTED 13 +#endif +#ifndef AWAITING_TIMEOUT +#define AWAITING_TIMEOUT 20 +#endif +#ifndef CIPHERS_LIST_STRING +#define CIPHERS_LIST_STRING "DEFAULT" +#endif +#ifndef LWS_SOMAXCONN +#define LWS_SOMAXCONN SOMAXCONN +#endif + +#define MAX_WEBSOCKET_04_KEY_LEN 128 + +#ifndef SYSTEM_RANDOM_FILEPATH +#define SYSTEM_RANDOM_FILEPATH "/dev/urandom" +#endif + +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, +}; + + +enum lws_connection_states { + LWSS_HTTP, + LWSS_HTTP_ISSUING_FILE, + LWSS_HTTP_HEADERS, + LWSS_HTTP_BODY, + LWSS_DEAD_SOCKET, + LWSS_ESTABLISHED, + LWSS_CLIENT_HTTP_ESTABLISHED, + LWSS_CLIENT_UNCONNECTED, + LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION, + LWSS_RETURNED_CLOSE_ALREADY, + LWSS_AWAITING_CLOSE_ACK, + LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE, + LWSS_SHUTDOWN, + + LWSS_HTTP2_AWAIT_CLIENT_PREFACE, + LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS, + LWSS_HTTP2_ESTABLISHED, + + LWSS_CGI, +}; + +enum http_version { + HTTP_VERSION_1_0, + HTTP_VERSION_1_1, + HTTP_VERSION_2 +}; + +enum http_connection_type { + HTTP_CONNECTION_CLOSE, + HTTP_CONNECTION_KEEP_ALIVE +}; + +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_PAYLOAD_UNTIL_LENGTH_EXHAUSTED +}; + +#define LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP 32 + +enum connection_mode { + LWSCM_HTTP_SERVING, + LWSCM_HTTP_SERVING_ACCEPTED, /* actual HTTP service going on */ + LWSCM_PRE_WS_SERVING_ACCEPT, + + LWSCM_WS_SERVING, + LWSCM_WS_CLIENT, + + LWSCM_HTTP2_SERVING, + + /* transient, ssl delay hiding */ + LWSCM_SSL_ACK_PENDING, + LWSCM_SSL_INIT, + /* as above, but complete into LWSCM_RAW */ + LWSCM_SSL_ACK_PENDING_RAW, + LWSCM_SSL_INIT_RAW, + + /* special internal types */ + LWSCM_SERVER_LISTENER, + LWSCM_CGI, /* stdin, stdout, stderr for another cgi master wsi */ + LWSCM_RAW, /* raw with bulk handling */ + LWSCM_RAW_FILEDESC, /* raw without bulk handling */ + + /* HTTP Client related */ + LWSCM_HTTP_CLIENT = LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP, + LWSCM_HTTP_CLIENT_ACCEPTED, /* actual HTTP service going on */ + LWSCM_WSCL_WAITING_CONNECT, + LWSCM_WSCL_WAITING_PROXY_REPLY, + LWSCM_WSCL_ISSUE_HANDSHAKE, + LWSCM_WSCL_ISSUE_HANDSHAKE2, + LWSCM_WSCL_ISSUE_HTTP_BODY, + LWSCM_WSCL_WAITING_SSL, + LWSCM_WSCL_WAITING_SERVER_REPLY, + LWSCM_WSCL_WAITING_EXTENSION_CONNECT, + LWSCM_WSCL_PENDING_CANDIDATE_CHILD, + LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY, + LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY, + LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY, + + /****** add new things just above ---^ ******/ + + +}; + +/* enums of socks version */ +enum socks_version { + SOCKS_VERSION_4 = 4, + SOCKS_VERSION_5 = 5 +}; + +/* enums of subnegotiation version */ +enum socks_subnegotiation_version { + SOCKS_SUBNEGOTIATION_VERSION_1 = 1, +}; + +/* enums of socks commands */ +enum socks_command { + SOCKS_COMMAND_CONNECT = 1, + SOCKS_COMMAND_BIND = 2, + SOCKS_COMMAND_UDP_ASSOCIATE = 3 +}; + +/* enums of socks address type */ +enum socks_atyp { + SOCKS_ATYP_IPV4 = 1, + SOCKS_ATYP_DOMAINNAME = 3, + SOCKS_ATYP_IPV6 = 4 +}; + +/* enums of socks authentication methods */ +enum socks_auth_method { + SOCKS_AUTH_NO_AUTH = 0, + SOCKS_AUTH_GSSAPI = 1, + SOCKS_AUTH_USERNAME_PASSWORD = 2 +}; + +/* enums of subnegotiation status */ +enum socks_subnegotiation_status { + SOCKS_SUBNEGOTIATION_STATUS_SUCCESS = 0, +}; + +/* enums of socks request reply */ +enum socks_request_reply { + SOCKS_REQUEST_REPLY_SUCCESS = 0, + SOCKS_REQUEST_REPLY_FAILURE_GENERAL = 1, + SOCKS_REQUEST_REPLY_CONNECTION_NOT_ALLOWED = 2, + SOCKS_REQUEST_REPLY_NETWORK_UNREACHABLE = 3, + SOCKS_REQUEST_REPLY_HOST_UNREACHABLE = 4, + SOCKS_REQUEST_REPLY_CONNECTION_REFUSED = 5, + SOCKS_REQUEST_REPLY_TTL_EXPIRED = 6, + SOCKS_REQUEST_REPLY_COMMAND_NOT_SUPPORTED = 7, + SOCKS_REQUEST_REPLY_ATYP_NOT_SUPPORTED = 8 +}; + +/* enums used to generate socks messages */ +enum socks_msg_type { + /* greeting */ + SOCKS_MSG_GREETING, + /* credential, user name and password */ + SOCKS_MSG_USERNAME_PASSWORD, + /* connect command */ + SOCKS_MSG_CONNECT +}; + +enum { + LWS_RXFLOW_ALLOW = (1 << 0), + LWS_RXFLOW_PENDING_CHANGE = (1 << 1), +}; + +struct lws_ring { + void *buf; + void (*destroy_element)(void *element); + size_t buflen; + size_t element_len; + uint32_t head; + uint32_t oldest_tail; +}; + +/* this is not usable directly by user code any more, lws_close_reason() */ +#define LWS_WRITE_CLOSE 4 + +struct lws_protocols; +struct lws; + +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) + +struct lws_io_watcher { +#ifdef LWS_WITH_LIBEV + ev_io ev_watcher; +#endif +#ifdef LWS_WITH_LIBUV + uv_poll_t uv_watcher; +#endif +#ifdef LWS_WITH_LIBEVENT + struct event *event_watcher; +#endif + struct lws_context *context; + + uint8_t actual_events; +}; + +struct lws_signal_watcher { +#ifdef LWS_WITH_LIBEV + ev_signal ev_watcher; +#endif +#ifdef LWS_WITH_LIBUV + uv_signal_t uv_watcher; +#endif +#ifdef LWS_WITH_LIBEVENT + struct event *event_watcher; +#endif + struct lws_context *context; +}; +#endif + +#ifdef _WIN32 +#define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS) +struct lws_fd_hashtable { + struct lws **wsi; + int length; +}; +#endif + +/* + * This is totally opaque to code using the library. It's exported as a + * forward-reference pointer-only declaration; the user can use the pointer with + * other APIs to get information out of it. + */ + +#if defined(LWS_WITH_ESP32) +typedef uint16_t ah_data_idx_t; +#else +typedef uint32_t ah_data_idx_t; +#endif + +struct lws_fragments { + ah_data_idx_t offset; + uint16_t len; + uint8_t nfrag; /* which ah->frag[] continues this content, or 0 */ + uint8_t flags; /* only http2 cares */ +}; + +/* + * 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_ESP32) + uint8_t rx[256]; +#else + uint8_t rx[2048]; +#endif + + int16_t rxpos; + int16_t rxlen; + uint32_t pos; + uint32_t http_response; + int hdr_token_idx; + +#ifndef LWS_NO_CLIENT + char initial_handshake_hash_base64[30]; +#endif + + uint8_t in_use; + uint8_t nfrag; +}; + +/* + * so we can have n connections being serviced simultaneously, + * these things need to be isolated per-thread. + */ + +struct lws_context_per_thread { +#if LWS_MAX_SMP > 1 + pthread_mutex_t lock; +#endif + struct lws_pollfd *fds; +#if defined(LWS_WITH_ESP8266) + struct lws **lws_vs_fds_index; +#endif + struct lws *rx_draining_ext_list; + struct lws *tx_draining_ext_list; + struct lws *timeout_list; +#if defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) + struct lws_context *context; +#endif +#ifdef LWS_WITH_CGI + struct lws_cgi *cgi_list; +#endif + void *http_header_data; + struct allocated_headers *ah_list; + struct lws *ah_wait_list; + int ah_wait_list_length; +#ifdef LWS_OPENSSL_SUPPORT + struct lws *pending_read_list; /* linked list */ +#endif +#if defined(LWS_WITH_LIBEV) + struct ev_loop *io_loop_ev; +#endif +#if defined(LWS_WITH_LIBUV) + uv_loop_t *io_loop_uv; + uv_signal_t signals[8]; + uv_timer_t uv_timeout_watcher; + uv_idle_t uv_idle; +#endif +#if defined(LWS_WITH_LIBEVENT) + struct event_base *io_loop_event_base; +#endif +#if defined(LWS_WITH_LIBEV) + struct lws_io_watcher w_accept; +#endif +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) + struct lws_signal_watcher w_sigint; + unsigned char ev_loop_foreign:1; +#endif + + unsigned long count_conns; + /* + * usable by anything in the service code, but only if the scope + * does not last longer than the service action (since next service + * of any socket can likewise use it and overwrite) + */ + unsigned char *serv_buf; +#ifdef _WIN32 + WSAEVENT *events; +#else + lws_sockfd_type dummy_pipe_fds[2]; +#endif + unsigned int fds_count; + uint32_t ah_pool_length; + + short ah_count_in_use; + unsigned char tid; + unsigned char lock_depth; +}; + +struct lws_conn_stats { + unsigned long long rx, tx; + unsigned long h1_conn, h1_trans, h2_trans, ws_upg, h2_alpn, h2_subs, + h2_upg, rejected; +}; + +void +lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs); + + +enum lws_h2_settings { + H2SET_HEADER_TABLE_SIZE = 1, + H2SET_ENABLE_PUSH, + H2SET_MAX_CONCURRENT_STREAMS, + H2SET_INITIAL_WINDOW_SIZE, + H2SET_MAX_FRAME_SIZE, + H2SET_MAX_HEADER_LIST_SIZE, + + H2SET_COUNT /* always last */ +}; + +struct http2_settings { + uint32_t s[H2SET_COUNT]; +}; + +/* + * virtual host -related context information + * vhostwide SSL context + * vhostwide proxy + * + * hierarchy: + * + * context -> vhost -> wsi + * + * incoming connection non-SSL vhost binding: + * + * listen socket -> wsi -> select vhost after first headers + * + * incoming connection SSL vhost binding: + * + * SSL SNI -> wsi -> bind after SSL negotiation + */ + +struct lws_vhost { +#if !defined(LWS_WITH_ESP8266) + char http_proxy_address[128]; + char proxy_basic_auth_token[128]; +#if defined(LWS_WITH_HTTP2) + struct http2_settings set; +#endif +#if defined(LWS_WITH_SOCKS5) + char socks_proxy_address[128]; + char socks_user[96]; + char socks_password[96]; +#endif +#endif +#if defined(LWS_WITH_ESP8266) + /* listen sockets need a place to hang their hat */ + esp_tcp tcp; +#endif + struct lws_conn_stats conn_stats; + struct lws_context *context; + struct lws_vhost *vhost_next; + const struct lws_http_mount *mount_list; + struct lws *lserv_wsi; + const char *name; + const char *iface; +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32) + int bind_iface; +#endif + const struct lws_protocols *protocols; + void **protocol_vh_privs; + const struct lws_protocol_vhost_options *pvo; + const struct lws_protocol_vhost_options *headers; + struct lws **same_vh_protocol_list; +#ifdef LWS_OPENSSL_SUPPORT + SSL_CTX *ssl_ctx; + SSL_CTX *ssl_client_ctx; +#endif +#if defined(LWS_WITH_MBEDTLS) + X509 *x509_client_CA; +#endif +#ifndef LWS_NO_EXTENSIONS + const struct lws_extension *extensions; +#endif + void *user; + + int listen_port; + unsigned int http_proxy_port; +#if defined(LWS_WITH_SOCKS5) + unsigned int socks_proxy_port; +#endif + unsigned int options; + int count_protocols; + int ka_time; + int ka_probes; + int ka_interval; + int keepalive_timeout; + int timeout_secs_ah_idle; + int ssl_info_event_mask; +#ifdef LWS_WITH_ACCESS_LOG + int log_fd; +#endif + +#ifdef LWS_OPENSSL_SUPPORT + int use_ssl; + int allow_non_ssl_on_ssl_port; + unsigned int user_supplied_ssl_ctx:1; +#endif + + unsigned int created_vhost_protocols:1; + unsigned int being_destroyed:1; + + unsigned char default_protocol_index; + unsigned char raw_protocol_index; +}; + +struct lws_deferred_free +{ + struct lws_deferred_free *next; + time_t deadline; + void *payload; +}; + +typedef union { +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 sa6; +#endif + struct sockaddr_in sa4; +} sockaddr46; + + +#if defined(LWS_WITH_PEER_LIMITS) +struct lws_peer { + struct lws_peer *next; + struct lws_peer *peer_wait_list; + + time_t time_created; + time_t time_closed_all; + + uint8_t addr[32]; + uint32_t hash; + uint32_t count_wsi; + uint32_t count_ah; + + uint32_t total_wsi; + uint32_t total_ah; + + uint8_t af; +}; +#endif + +/* + * the rest is managed per-context, that includes + * + * - processwide single fd -> wsi lookup + * - contextwide headers pool + */ + +struct lws_context { + time_t last_timeout_check_s; + time_t last_ws_ping_pong_check_s; + time_t time_up; + const struct lws_plat_file_ops *fops; + struct lws_plat_file_ops fops_platform; +#if defined(LWS_WITH_HTTP2) + struct http2_settings set; +#endif +#if defined(LWS_WITH_ZIP_FOPS) + struct lws_plat_file_ops fops_zip; +#endif + struct lws_context_per_thread pt[LWS_MAX_SMP]; + struct lws_conn_stats conn_stats; +#if LWS_MAX_SMP > 1 + pthread_mutex_t lock; + int lock_depth; +#endif +#ifdef _WIN32 +/* different implementation between unix and windows */ + struct lws_fd_hashtable fd_hashtable[FD_HASHTABLE_MODULUS]; +#else +#if defined(LWS_WITH_ESP8266) + struct espconn **connpool; /* .reverse points to the wsi */ + void *rxd; + int rxd_len; + os_timer_t to_timer; +#else + struct lws **lws_lookup; /* fd to wsi */ +#endif +#endif + struct lws_vhost *vhost_list; + struct lws_vhost *vhost_pending_destruction_list; + struct lws_plugin *plugin_list; + struct lws_deferred_free *deferred_free_list; +#if defined(LWS_WITH_PEER_LIMITS) + struct lws_peer **pl_hash_table; + struct lws_peer *peer_wait_list; + time_t next_cull; +#endif + + void *external_baggage_free_on_destroy; + const struct lws_token_limits *token_limits; + void *user_space; + const char *server_string; + const struct lws_protocol_vhost_options *reject_service_keywords; + lws_reload_func deprecation_cb; + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + cap_value_t caps[4]; + char count_caps; +#endif + +#if defined(LWS_WITH_LIBEV) + lws_ev_signal_cb_t * lws_ev_sigint_cb; +#endif +#if defined(LWS_WITH_LIBUV) + uv_signal_cb lws_uv_sigint_cb; + uv_loop_t pu_loop; +#endif +#if defined(LWS_WITH_LIBEVENT) + lws_event_signal_cb_t * lws_event_sigint_cb; +#endif + char canonical_hostname[128]; +#ifdef LWS_LATENCY + unsigned long worst_latency; + char worst_latency_info[256]; +#endif + +#if defined(LWS_WITH_STATS) + uint64_t lws_stats[LWSSTATS_SIZE]; + uint64_t last_dump; + int updated; +#endif +#if defined(LWS_WITH_ESP32) + unsigned long time_last_state_dump; + uint32_t last_free_heap; +#endif + + int max_fds; +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) + int use_ev_sigint; +#endif + int started_with_parent; + int uid, gid; + + int fd_random; + + int count_wsi_allocated; + int count_cgi_spawned; + unsigned int options; + unsigned int fd_limit_per_thread; + unsigned int timeout_secs; + unsigned int pt_serv_buf_size; + int max_http_header_data; + int simultaneous_ssl_restriction; + int simultaneous_ssl; +#if defined(LWS_WITH_PEER_LIMITS) + uint32_t pl_hash_elements; /* protected by context->lock */ + uint32_t count_peers; /* protected by context->lock */ + unsigned short ip_limit_ah; + unsigned short ip_limit_wsi; +#endif + unsigned int deprecated:1; + unsigned int being_destroyed:1; + unsigned int being_destroyed1:1; + unsigned int requested_kill:1; + unsigned int protocol_init_done:1; + unsigned int ssl_gate_accepts:1; + unsigned int doing_protocol_init; + /* + * set to the Thread ID that's doing the service loop just before entry + * to poll indicates service thread likely idling in poll() + * volatile because other threads may check it as part of processing + * for pollfd event change. + */ + volatile int service_tid; + int service_tid_detected; + + short max_http_header_pool; + short count_threads; + short plugin_protocol_count; + short plugin_extension_count; + short server_string_len; + unsigned short ws_ping_pong_interval; + unsigned short deprecation_pending_listen_close_count; + + uint8_t max_fi; +}; + +int +lws_check_deferred_free(struct lws_context *context, int force); + +#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x] +#define lws_get_vh_protocol(vh, x) vh->protocols[x] + +LWS_EXTERN void +lws_close_free_wsi_final(struct lws *wsi); +LWS_EXTERN void +lws_libuv_closehandle(struct lws *wsi); +LWS_EXTERN void +lws_libuv_closehandle_manually(struct lws *wsi); +LWS_EXTERN int +lws_libuv_check_watcher_active(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_plugins_init(struct lws_context * context, const char * const *d); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_plugins_destroy(struct lws_context * context); + +LWS_EXTERN void +lws_restart_ws_ping_pong_timer(struct lws *wsi); + +struct lws * +lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); + + +enum { + LWS_EV_READ = (1 << 0), + LWS_EV_WRITE = (1 << 1), + LWS_EV_START = (1 << 2), + LWS_EV_STOP = (1 << 3), + + LWS_EV_PREPARE_DELETION = (1 << 31), +}; + +#if defined(LWS_WITH_LIBEV) +LWS_EXTERN void +lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc); +LWS_EXTERN void +lws_libev_io(struct lws *wsi, int flags); +LWS_EXTERN int +lws_libev_init_fd_table(struct lws_context *context); +LWS_EXTERN void +lws_libev_destroyloop(struct lws_context *context, int tsi); +LWS_EXTERN void +lws_libev_run(const struct lws_context *context, int tsi); +#define LWS_LIBEV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV) +LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info); +#else +#define lws_libev_accept(_a, _b) ((void) 0) +#define lws_libev_io(_a, _b) ((void) 0) +#define lws_libev_init_fd_table(_a) (0) +#define lws_libev_run(_a, _b) ((void) 0) +#define lws_libev_destroyloop(_a, _b) ((void) 0) +#define LWS_LIBEV_ENABLED(context) (0) +#if LWS_POSIX && !defined(LWS_WITH_ESP32) +#define lws_feature_status_libev(_a) \ + lwsl_info("libev support not compiled in\n") +#else +#define lws_feature_status_libev(_a) +#endif +#endif + +#if defined(LWS_WITH_LIBUV) +LWS_EXTERN void +lws_libuv_accept(struct lws *new_wsi, lws_sock_file_fd_type desc); +LWS_EXTERN void +lws_libuv_io(struct lws *wsi, int flags); +LWS_EXTERN int +lws_libuv_init_fd_table(struct lws_context *context); +LWS_EXTERN void +lws_libuv_run(const struct lws_context *context, int tsi); +LWS_EXTERN void +lws_libuv_destroyloop(struct lws_context *context, int tsi); +LWS_EXTERN int +lws_uv_initvhost(struct lws_vhost* vh, struct lws*); +#define LWS_LIBUV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV) +LWS_EXTERN void lws_feature_status_libuv(struct lws_context_creation_info *info); +#else +#define lws_libuv_accept(_a, _b) ((void) 0) +#define lws_libuv_io(_a, _b) ((void) 0) +#define lws_libuv_init_fd_table(_a) (0) +#define lws_libuv_run(_a, _b) ((void) 0) +#define lws_libuv_destroyloop(_a, _b) ((void) 0) +#define LWS_LIBUV_ENABLED(context) (0) +#if LWS_POSIX && !defined(LWS_WITH_ESP32) +#define lws_feature_status_libuv(_a) \ + lwsl_notice("libuv support not compiled in\n") +#else +#define lws_feature_status_libuv(_a) +#endif +#endif + +#if defined(LWS_WITH_LIBEVENT) +LWS_EXTERN void +lws_libevent_accept(struct lws *new_wsi, lws_sock_file_fd_type desc); +LWS_EXTERN void +lws_libevent_io(struct lws *wsi, int flags); +LWS_EXTERN int +lws_libevent_init_fd_table(struct lws_context *context); +LWS_EXTERN void +lws_libevent_destroyloop(struct lws_context *context, int tsi); +LWS_EXTERN void +lws_libevent_run(const struct lws_context *context, int tsi); +#define LWS_LIBEVENT_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT) +LWS_EXTERN void lws_feature_status_libevent(struct lws_context_creation_info *info); +#else +#define lws_libevent_accept(_a, _b) ((void) 0) +#define lws_libevent_io(_a, _b) ((void) 0) +#define lws_libevent_init_fd_table(_a) (0) +#define lws_libevent_run(_a, _b) ((void) 0) +#define lws_libevent_destroyloop(_a, _b) ((void) 0) +#define LWS_LIBEVENT_ENABLED(context) (0) +#if LWS_POSIX && !defined(LWS_WITH_ESP32) +#define lws_feature_status_libevent(_a) \ + lwsl_notice("libevent support not compiled in\n") +#else +#define lws_feature_status_libevent(_a) +#endif +#endif + + +#ifdef LWS_WITH_IPV6 +#define LWS_IPV6_ENABLED(vh) \ + (!lws_check_opt(vh->context->options, LWS_SERVER_OPTION_DISABLE_IPV6) && \ + !lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_IPV6)) +#else +#define LWS_IPV6_ENABLED(context) (0) +#endif + +#ifdef LWS_WITH_UNIX_SOCK +#define LWS_UNIX_SOCK_ENABLED(vhost) \ + (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK) +#else +#define LWS_UNIX_SOCK_ENABLED(vhost) (0) +#endif + +enum uri_path_states { + URIPS_IDLE, + URIPS_SEEN_SLASH, + URIPS_SEEN_SLASH_DOT, + URIPS_SEEN_SLASH_DOT_DOT, +}; + +enum uri_esc_states { + URIES_IDLE, + URIES_SEEN_PERCENT, + URIES_SEEN_PERCENT_H1, +}; + +/* notice that these union members: + * + * hdr + * http + * http2 + * + * all have a pointer to allocated_headers struct as their first member. + * + * It means for allocated_headers access, the three union paths can all be + * used interchangeably to access the same data + */ + + +#ifndef LWS_NO_CLIENT +struct client_info_stash { + char address[256]; + char path[4096]; + char host[256]; + char origin[256]; + char protocol[256]; + char method[16]; + char iface[16]; +}; +#endif + +struct _lws_header_related { + /* MUST be first in struct */ + struct allocated_headers *ah; + struct lws *ah_wait_list; + unsigned char *preamble_rx; +#ifndef LWS_NO_CLIENT + struct client_info_stash *stash; +#endif + unsigned int preamble_rx_len; + enum uri_path_states ups; + enum uri_esc_states ues; + short lextable_pos; + unsigned int current_token_limit; + + char esc_stash; + char post_literal_equal; + unsigned char parser_state; /* enum lws_token_indexes */ +}; + +#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 + +struct _lws_http_mode_related { + /* MUST be first in struct */ + struct allocated_headers *ah; /* mirroring _lws_header_related */ + struct lws *ah_wait_list; + unsigned char *preamble_rx; +#ifndef LWS_NO_CLIENT + struct client_info_stash *stash; +#endif + unsigned int preamble_rx_len; + struct lws *new_wsi_list; + lws_filepos_t filepos; + lws_filepos_t filelen; + lws_fop_fd_t fop_fd; + +#if defined(LWS_WITH_RANGES) + struct lws_range_parsing range; + char multipart_content_type[64]; +#endif + + enum http_version request_version; + enum http_connection_type connection_type; + lws_filepos_t tx_content_length; + lws_filepos_t tx_content_remain; + lws_filepos_t rx_content_length; + lws_filepos_t rx_content_remain; +}; + +#define LWS_H2_FRAME_HEADER_LENGTH 9 + +#ifdef LWS_WITH_HTTP2 + +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 + */ +}; + +#define LWS_H2_STREAM_ID_MASTER 0 +#define LWS_H2_SETTINGS_LEN 6 + +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_PONG, + LWS_H2_PPS_GOAWAY, + LWS_H2_PPS_RST_STREAM, + LWS_H2_PPS_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; +}; + +#define LWS_H2_RX_SCRATCH_SIZE 512 + +/* + * 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 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 */ + char *rx_scratch; + + 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; + + 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; + + uint32_t rx_scratch_pos; + uint32_t rx_scratch_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 { + /* + * having this first lets us also re-use all HTTP union code + * and in turn, http_mode_related has allocated headers in right + * place so we can use the header apis on the wsi directly still + */ + struct _lws_http_mode_related http; /* MUST BE FIRST IN STRUCT */ + + struct lws_h2_netconn *h2n; /* malloc'd for root net conn */ + struct lws *parent_wsi; + struct lws *child_list; + struct lws *sibling_list; + + char *pending_status_body; + + int tx_cr; + int peer_tx_cr_est; + unsigned int my_sid; + unsigned int child_count; + int my_priority; + uint32_t dependent_on; + + unsigned int END_STREAM:1; + unsigned int END_HEADERS:1; + unsigned int send_END_STREAM:1; + unsigned int GOING_AWAY; + unsigned int requested_POLLOUT:1; + unsigned int skint:1; + + uint16_t round_robin_POLLOUT; + uint16_t count_POLLOUT_children; + uint8_t h2_state; /* the RFC7540 state of the connection */ + uint8_t weight; + + uint8_t initialized; +}; + +#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->u.h2.parent_wsi) + +#endif + +struct _lws_websocket_related { + /* cheapest way to deal with ah overlap with ws union transition */ + struct _lws_header_related hdr; + char *rx_ubuf; + unsigned int rx_ubuf_alloc; + struct lws *rx_draining_ext_list; + struct lws *tx_draining_ext_list; + time_t time_next_ping_check; + size_t rx_packet_length; + unsigned int rx_ubuf_head; + unsigned char mask[4]; + /* Also used for close content... control opcode == < 128 */ + unsigned char ping_payload_buf[128 - 3 + LWS_PRE]; + + unsigned char ping_payload_len; + unsigned char mask_idx; + unsigned char opcode; + unsigned char rsv; + unsigned char rsv_first_msg; + /* zero if no info, or length including 2-byte close code */ + unsigned char close_in_ping_buffer_len; + unsigned char utf8; + unsigned char stashed_write_type; + unsigned char tx_draining_stashed_wp; + + unsigned int final:1; + unsigned int frame_is_binary:1; + unsigned int all_zero_nonce:1; + unsigned int this_frame_masked:1; + unsigned int inside_frame:1; /* next write will be more of frame */ + unsigned int clean_buffer:1; /* buffer not rewritten by extension */ + unsigned int payload_is_close:1; /* process as PONG, but it is close */ + unsigned int ping_pending_flag:1; + unsigned int continuation_possible:1; + unsigned int owed_a_fin:1; + unsigned int check_utf8:1; + unsigned int defeat_check_utf8:1; + unsigned int pmce_compressed_message:1; + unsigned int stashed_write_pending:1; + unsigned int rx_draining_ext:1; + unsigned int tx_draining_ext:1; + unsigned int send_check_ping:1; + unsigned int first_fragment:1; +}; + +#ifdef LWS_WITH_CGI + +#define LWS_HTTP_CHUNK_HDR_SIZE 16 + +enum { + SIGNIFICANT_HDR_CONTENT_LENGTH, + SIGNIFICANT_HDR_LOCATION, + SIGNIFICANT_HDR_STATUS, + SIGNIFICANT_HDR_TRANSFER_ENCODING, + + SIGNIFICANT_HDR_COUNT +}; + +/* wsi who is master of the cgi points to an lws_cgi */ + +struct lws_cgi { + struct lws_cgi *cgi_list; + struct lws *stdwsi[3]; /* points to the associated stdin/out/err wsis */ + struct lws *wsi; /* owner */ + unsigned char *headers_buf; + unsigned char *headers_start; + unsigned char *headers_pos; + unsigned char *headers_dumped; + unsigned char *headers_end; + lws_filepos_t content_length; + lws_filepos_t content_length_seen; + int pipe_fds[3][2]; + int match[SIGNIFICANT_HDR_COUNT]; + int pid; + int response_code; + int lp; + char l[12]; + + unsigned int being_closed:1; + unsigned int explicitly_chunked:1; + + unsigned char chunked_grace; +}; +#endif + +signed char char_to_hex(const char c); + +#ifndef LWS_NO_CLIENT +enum lws_chunk_parser { + ELCP_HEX, + ELCP_CR, + ELCP_CONTENT, + ELCP_POST_CR, + ELCP_POST_LF, +}; +#endif + +enum lws_parse_urldecode_results { + LPUR_CONTINUE, + LPUR_SWALLOW, + LPUR_FORBID, + LPUR_EXCESSIVE, +}; + +struct lws_rewrite; + +#ifdef LWS_WITH_ACCESS_LOG +struct lws_access_log { + char *header_log; + char *user_agent; + char *referrer; + unsigned long sent; + int response; +}; +#endif + +struct lws { + + /* structs */ + /* members with mutually exclusive lifetimes are unionized */ + + union u { + struct _lws_http_mode_related http; +#ifdef LWS_WITH_HTTP2 + struct _lws_h2_related h2; +#endif + struct _lws_header_related hdr; + struct _lws_websocket_related ws; + } u; + + /* lifetime members */ + +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) + struct lws_io_watcher w_read; +#endif +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBEVENT) + struct lws_io_watcher w_write; +#endif +#ifdef LWS_WITH_ACCESS_LOG + struct lws_access_log access_log; +#endif + time_t pending_timeout_limit; + + /* pointers */ + + struct lws_context *context; + struct lws_vhost *vhost; + struct lws *parent; /* points to parent, if any */ + struct lws *child_list; /* points to first child */ + struct lws *sibling_list; /* subsequent children at same level */ +#ifdef LWS_WITH_CGI + struct lws_cgi *cgi; /* wsi being cgi master have one of these */ +#endif + const struct lws_protocols *protocol; + struct lws **same_vh_protocol_prev, *same_vh_protocol_next; + struct lws *timeout_list; + struct lws **timeout_list_prev; +#if defined(LWS_WITH_PEER_LIMITS) + struct lws_peer *peer; +#endif + + void *user_space; + void *opaque_parent_data; + /* rxflow handling */ + unsigned char *rxflow_buffer; + /* truncated send handling */ + unsigned char *trunc_alloc; /* non-NULL means buffering in progress */ + +#if defined (LWS_WITH_ESP8266) + void *premature_rx; + unsigned short prem_rx_size, prem_rx_pos; +#endif + +#ifndef LWS_NO_EXTENSIONS + const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE]; + void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE]; +#endif +#ifdef LWS_OPENSSL_SUPPORT + SSL *ssl; + BIO *client_bio; + struct lws *pending_read_list_prev, *pending_read_list_next; +#if defined(LWS_WITH_STATS) + uint64_t accept_start_us; + char seen_rx; +#endif +#endif +#ifdef LWS_WITH_HTTP_PROXY + struct lws_rewrite *rw; +#endif +#ifdef LWS_LATENCY + unsigned long action_start; + unsigned long latency_start; +#endif + lws_sock_file_fd_type desc; /* .filefd / .sockfd */ +#if defined(LWS_WITH_STATS) + uint64_t active_writable_req_us; +#endif + /* ints */ + int position_in_fds_table; + uint32_t rxflow_len; + uint32_t rxflow_pos; + unsigned int trunc_alloc_len; /* size of malloc */ + unsigned int trunc_offset; /* where we are in terms of spilling */ + unsigned int trunc_len; /* how much is buffered */ +#ifndef LWS_NO_CLIENT + int chunk_remaining; +#endif + unsigned int cache_secs; + + unsigned int hdr_parsing_completed:1; + unsigned int http2_substream:1; + unsigned int upgraded_to_http2:1; + unsigned int seen_nonpseudoheader:1; + unsigned int listener:1; + unsigned int user_space_externally_allocated:1; + unsigned int socket_is_permanently_unusable:1; + unsigned int rxflow_change_to:2; + unsigned int more_rx_waiting:1; /* has to live here since ah may stick to end */ + unsigned int conn_stat_done:1; + unsigned int cache_reuse:1; + unsigned int cache_revalidate:1; + unsigned int cache_intermediaries:1; + unsigned int favoured_pollin:1; + unsigned int sending_chunked:1; + unsigned int already_did_cce:1; + unsigned int told_user_closed:1; + unsigned int waiting_to_send_close_frame:1; + unsigned int ipv6:1; + unsigned int parent_carries_io:1; + unsigned int parent_pending_cb_on_writable:1; + unsigned int cgi_stdout_zero_length:1; + unsigned int seen_zero_length_recv:1; + unsigned int rxflow_will_be_applied:1; + +#if defined(LWS_WITH_ESP8266) + unsigned int pending_send_completion:3; + unsigned int close_is_pending_send_completion:1; +#endif +#ifdef LWS_WITH_ACCESS_LOG + unsigned int access_log_pending:1; +#endif +#ifndef LWS_NO_CLIENT + unsigned int do_ws:1; /* whether we are doing http or ws flow */ + unsigned int chunked:1; /* if the clientside connection is chunked */ + unsigned int client_rx_avail:1; + unsigned int client_http_body_pending:1; +#endif +#ifdef LWS_WITH_HTTP_PROXY + unsigned int perform_rewrite:1; +#endif +#ifndef LWS_NO_EXTENSIONS + unsigned int extension_data_pending:1; +#endif +#ifdef LWS_OPENSSL_SUPPORT + unsigned int use_ssl:4; +#endif +#ifdef _WIN32 + unsigned int sock_send_blocking:1; +#endif +#ifdef LWS_OPENSSL_SUPPORT + unsigned int redirect_to_https:1; +#endif + + /* volatile to make sure code is aware other thread can change */ + volatile unsigned int handling_pollout:1; + volatile unsigned int leave_pollout_active:1; + +#ifndef LWS_NO_CLIENT + unsigned short c_port; +#endif + + /* chars */ +#ifndef LWS_NO_EXTENSIONS + unsigned char count_act_ext; +#endif + uint8_t ietf_spec_revision; + char mode; /* enum connection_mode */ + char state; /* enum lws_connection_states */ + char state_pre_close; + char lws_rx_parse_state; /* enum lws_rx_parse_state */ + char rx_frame_type; /* enum lws_write_protocol */ + char pending_timeout; /* enum pending_timeout */ + char tsi; /* thread service index we belong to */ + char protocol_interpret_idx; + char redirects; + uint8_t rxflow_bitmap; +#ifdef LWS_WITH_CGI + char cgi_channel; /* which of stdin/out/err */ + char hdr_state; +#endif +#ifndef LWS_NO_CLIENT + char chunk_parser; /* enum lws_chunk_parser */ +#endif +#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT) + char reason_bf; /* internal writeable callback reason bitfield */ +#endif +}; + +#define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap)) + +LWS_EXTERN int log_level; + +LWS_EXTERN int +lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, + const char *iface); + +#if defined(LWS_WITH_IPV6) +LWS_EXTERN unsigned long +lws_get_addr_scope(const char *ipaddr); +#endif + +LWS_EXTERN void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status); + +LWS_EXTERN void +lws_free_wsi(struct lws *wsi); + +LWS_EXTERN int +remove_wsi_socket_from_fds(struct lws *wsi); +LWS_EXTERN int +lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len); + +#ifndef LWS_LATENCY +static inline void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completion) { + do { + (void)context; (void)wsi; (void)action; (void)ret; + (void)completion; + } while (0); +} +static inline void +lws_latency_pre(struct lws_context *context, struct lws *wsi) { + do { (void)context; (void)wsi; } while (0); +} +#else +#define lws_latency_pre(_context, _wsi) lws_latency(_context, _wsi, NULL, 0, 0) +extern void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completion); +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_client_rx_sm(struct lws *wsi, unsigned char c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse(struct lws *wsi, unsigned char c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse_urldecode(struct lws *wsi, uint8_t *_c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_action(struct lws *wsi); + +LWS_EXTERN int +lws_b64_selftest(void); + +LWS_EXTERN int +lws_service_flag_pending(struct lws_context *context, int tsi); + +#if defined(_WIN32) || defined(LWS_WITH_ESP8266) +LWS_EXTERN struct lws * +wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd); + +LWS_EXTERN int +insert_wsi(struct lws_context *context, struct lws *wsi); + +LWS_EXTERN int +delete_from_fd(struct lws_context *context, lws_sockfd_type fd); +#else +#define wsi_from_fd(A,B) A->lws_lookup[B] +#define insert_wsi(A,B) assert(A->lws_lookup[B->desc.sockfd] == 0); A->lws_lookup[B->desc.sockfd]=B +#define delete_from_fd(A,B) A->lws_lookup[B]=0 +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len); + + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_service_timeout_check(struct lws *wsi, unsigned int sec); + +LWS_EXTERN void +lws_remove_from_timeout_list(struct lws *wsi); + +LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_client_connect_2(struct lws *wsi); + +LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT +lws_client_reset(struct lws **wsi, int ssl, const char *address, int port, + const char *path, const char *host); + +LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_create_new_server_wsi(struct lws_vhost *vhost); + +LWS_EXTERN char * LWS_WARN_UNUSED_RESULT +lws_generate_client_handshake(struct lws *wsi, char *pkt); + +LWS_EXTERN int +lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); + +LWS_EXTERN struct lws * +lws_client_connect_via_info2(struct lws *wsi); + +LWS_EXTERN int +_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah); + +/* + * EXTENSIONS + */ + +#ifndef LWS_NO_EXTENSIONS +LWS_VISIBLE void +lws_context_init_extensions(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); + +#else +#define lws_any_extension_handled(_a, _b, _c, _d) (0) +#define lws_ext_cb_active(_a, _b, _c, _d) (0) +#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0) +#define lws_issue_raw_ext_access lws_issue_raw +#define lws_context_init_extensions(_a, _b) +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_client_interpret_server_handshake(struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_rx_sm(struct lws *wsi, unsigned char c); + +LWS_EXTERN int +lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf, size_t *len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len); + +LWS_EXTERN void +lws_union_transition(struct lws *wsi, enum connection_mode mode); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +user_callback_handle_rxflow(lws_callback_function, struct lws *wsi, + enum lws_callback_reasons reason, void *user, + void *in, size_t len); +#ifdef LWS_WITH_HTTP2 +struct lws * lws_h2_get_nth_child(struct lws *wsi, int n); +LWS_EXTERN void lws_h2_init(struct lws *wsi); +LWS_EXTERN int +lws_h2_settings(struct lws *nwsi, struct http2_settings *settings, + unsigned char *buf, int len); +LWS_EXTERN int +lws_h2_parser(struct lws *wsi, unsigned char c); +LWS_EXTERN int lws_h2_do_pps_send(struct lws *wsi); +LWS_EXTERN int lws_h2_frame_write(struct lws *wsi, int type, int flags, + unsigned int sid, unsigned int len, + unsigned char *buf); +LWS_EXTERN struct lws * +lws_h2_wsi_from_id(struct lws *wsi, unsigned int sid); +LWS_EXTERN int lws_hpack_interpret(struct lws *wsi, + unsigned char c); +LWS_EXTERN 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); +LWS_EXTERN 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); +LWS_EXTERN int +lws_add_http2_header_status(struct lws *wsi, + unsigned int code, unsigned char **p, + unsigned char *end); +LWS_EXTERN int +lws_h2_configure_if_upgraded(struct lws *wsi); +LWS_EXTERN void +lws_hpack_destroy_dynamic_header(struct lws *wsi); +LWS_EXTERN int +lws_hpack_dynamic_size(struct lws *wsi, int size); +LWS_EXTERN int +lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason); +LWS_EXTERN int +lws_h2_tx_cr_get(struct lws *wsi); +LWS_EXTERN void +lws_h2_tx_cr_consume(struct lws *wsi, int consumed); +LWS_EXTERN int +lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h); +LWS_EXTERN void +lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pss); + +LWS_EXTERN const struct http2_settings lws_h2_defaults; +#else +#define lws_h2_configure_if_upgraded(x) +#endif + +LWS_EXTERN int +lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd); + +LWS_EXTERN int +lws_plat_check_connection_error(struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_header_table_attach(struct lws *wsi, int autoservice); + +LWS_EXTERN int +lws_header_table_detach(struct lws *wsi, int autoservice); + +LWS_EXTERN void +lws_header_table_reset(struct lws *wsi, int autoservice); +void +_lws_header_table_reset(struct allocated_headers *ah); + +void +lws_header_table_force_to_detachable_state(struct lws *wsi); +int +lws_header_table_is_in_detachable_state(struct lws *wsi); + +LWS_EXTERN char * LWS_WARN_UNUSED_RESULT +lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ensure_user_space(struct lws *wsi); + +LWS_EXTERN int +lws_change_pollfd(struct lws *wsi, int _and, int _or); + +#ifndef LWS_NO_SERVER +int lws_context_init_server(struct lws_context_creation_info *info, + struct lws_vhost *vhost); +LWS_EXTERN struct lws_vhost * +lws_select_vhost(struct lws_context *context, int port, const char *servername); +LWS_EXTERN int +handshake_0405(struct lws_context *context, struct lws *wsi); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len); +LWS_EXTERN void +lws_server_get_canonical_hostname(struct lws_context *context, + struct lws_context_creation_info *info); +#else +#define lws_context_init_server(_a, _b) (0) +#define lws_interpret_incoming_packet(_a, _b, _c) (0) +#define lws_server_get_canonical_hostname(_a, _b) +#endif + +#ifndef LWS_NO_DAEMONIZE +LWS_EXTERN int get_daemonize_pid(); +#else +#define get_daemonize_pid() (0) +#endif + +#if !defined(LWS_WITH_ESP8266) +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +interface_to_sa(struct lws_vhost *vh, const char *ifname, + struct sockaddr_in *addr, size_t addrlen); +#endif +LWS_EXTERN void lwsl_emit_stderr(int level, const char *line); + +enum lws_ssl_capable_status { + LWS_SSL_CAPABLE_ERROR = -1, + LWS_SSL_CAPABLE_MORE_SERVICE = -2, +}; + +#ifndef LWS_OPENSSL_SUPPORT +#define LWS_SSL_ENABLED(context) (0) +#define lws_context_init_server_ssl(_a, _b) (0) +#define lws_ssl_destroy(_a) +#define lws_context_init_http2_ssl(_a) +#define lws_ssl_capable_read lws_ssl_capable_read_no_ssl +#define lws_ssl_capable_write lws_ssl_capable_write_no_ssl +#define lws_ssl_pending lws_ssl_pending_no_ssl +#define lws_server_socket_service_ssl(_b, _c) (0) +#define lws_ssl_close(_a) (0) +#define lws_ssl_context_destroy(_a) +#define lws_ssl_SSL_CTX_destroy(_a) +#define lws_ssl_remove_wsi_from_buffered_list(_a) +#define lws_context_init_ssl_library(_a) +#define lws_ssl_anybody_has_buffered_read_tsi(_a, _b) (0) +#else +#define LWS_SSL_ENABLED(context) (context->use_ssl) +LWS_EXTERN int openssl_websocket_private_data_index; +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_pending(struct lws *wsi); +LWS_EXTERN int +lws_context_init_ssl_library(struct lws_context_creation_info *info); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd); +LWS_EXTERN int +lws_ssl_close(struct lws *wsi); +LWS_EXTERN void +lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost); +LWS_EXTERN void +lws_ssl_context_destroy(struct lws_context *context); +LWS_VISIBLE void +lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_bio_create(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_connect1(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_connect2(struct lws *wsi); +LWS_EXTERN void +lws_ssl_elaborate_error(void); +LWS_EXTERN int +lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi); +#ifndef LWS_NO_SERVER +LWS_EXTERN int +lws_context_init_server_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost); +#else +#define lws_context_init_server_ssl(_a, _b) (0) +#endif +LWS_EXTERN void +lws_ssl_destroy(struct lws_vhost *vhost); +/* HTTP2-related */ + +#ifdef LWS_WITH_HTTP2 +LWS_EXTERN void +lws_context_init_http2_ssl(struct lws_vhost *vhost); +#else +#define lws_context_init_http2_ssl(_a) +#endif +#endif + +#if LWS_MAX_SMP > 1 +static LWS_INLINE void +lws_pt_mutex_init(struct lws_context_per_thread *pt) +{ + pthread_mutex_init(&pt->lock, NULL); +} + +static LWS_INLINE void +lws_pt_mutex_destroy(struct lws_context_per_thread *pt) +{ + pthread_mutex_destroy(&pt->lock); +} + +static LWS_INLINE void +lws_pt_lock(struct lws_context_per_thread *pt) +{ + if (!pt->lock_depth++) + pthread_mutex_lock(&pt->lock); +} + +static LWS_INLINE void +lws_pt_unlock(struct lws_context_per_thread *pt) +{ + if (!(--pt->lock_depth)) + pthread_mutex_unlock(&pt->lock); +} +static LWS_INLINE void +lws_context_lock(struct lws_context *context) +{ + if (!context->lock_depth++) + pthread_mutex_lock(&context->lock); +} + +static LWS_INLINE void +lws_context_unlock(struct lws_context *context) +{ + if (!(--context->lock_depth)) + pthread_mutex_unlock(&context->lock); +} + +#else +#define lws_pt_mutex_init(_a) (void)(_a) +#define lws_pt_mutex_destroy(_a) (void)(_a) +#define lws_pt_lock(_a) (void)(_a) +#define lws_pt_unlock(_a) (void)(_a) +#define lws_context_lock(_a) (void)(_a) +#define lws_context_unlock(_a) (void)(_a) +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_pending_no_ssl(struct lws *wsi); + +#ifdef LWS_WITH_HTTP_PROXY +struct lws_rewrite { + hubbub_parser *parser; + hubbub_parser_optparams params; + const char *from, *to; + int from_len, to_len; + unsigned char *p, *end; + struct lws *wsi; +}; +static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len) +{ + if (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 + +#ifndef LWS_NO_CLIENT +LWS_EXTERN int lws_client_socket_service(struct lws_context *context, + struct lws *wsi, + struct lws_pollfd *pollfd); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed_client(struct lws *wsi); +#ifdef LWS_OPENSSL_SUPPORT +LWS_EXTERN int +lws_context_init_client_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost); + +LWS_EXTERN void +lws_ssl_info_callback(const SSL *ssl, int where, int ret); + +#else + #define lws_context_init_client_ssl(_a, _b) (0) +#endif +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len); +LWS_EXTERN void +lws_decode_ssl_error(void); +#else +#define lws_context_init_client_ssl(_a, _b) (0) +#define lws_handshake_client(_a, _b, _c) (0) +#endif + +LWS_EXTERN int +_lws_rx_flow_control(struct lws *wsi); + +LWS_EXTERN int +_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa); + +#ifndef LWS_NO_SERVER +LWS_EXTERN int +lws_server_socket_service(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pollfd); +LWS_EXTERN int +lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len); +#else +#define lws_server_socket_service(_a, _b, _c) (0) +#define lws_handshake_server(_a, _b, _c) (0) +#endif + +#ifdef LWS_WITH_ACCESS_LOG +LWS_EXTERN int +lws_access_log(struct lws *wsi); +LWS_EXTERN void +lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth); +#else +#define lws_access_log(_a) +#endif + +LWS_EXTERN int +lws_cgi_kill_terminated(struct lws_context_per_thread *pt); + +LWS_EXTERN void +lws_cgi_remove_and_kill(struct lws *wsi); + +int +lws_protocol_init(struct lws_context *context); + +int +lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p); + +const struct lws_http_mount * +lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len); + +/* + * custom allocator + */ +LWS_EXTERN void * +lws_realloc(void *ptr, size_t size, const char *reason); + +LWS_EXTERN void * LWS_WARN_UNUSED_RESULT +lws_zalloc(size_t size, const char *reason); + +#ifdef LWS_PLAT_OPTEE +void *lws_malloc(size_t size, const char *reason); +void lws_free(void *p); +#define lws_free_set_NULL(P) do { lws_free(P); (P) = NULL; } while(0) +#else +#define lws_malloc(S, R) lws_realloc(NULL, S, R) +#define lws_free(P) lws_realloc(P, 0, "lws_free") +#define lws_free_set_NULL(P) do { lws_realloc(P, 0, "free"); (P) = NULL; } while(0) +#endif + +const struct lws_plat_file_ops * +lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, + const char **vpath); + +/* lws_plat_ */ +LWS_EXTERN void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m); +LWS_EXTERN void +lws_plat_insert_socket_into_fds(struct lws_context *context, + struct lws *wsi); +LWS_EXTERN void +lws_plat_service_periodic(struct lws_context *context); + +LWS_EXTERN int +lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pfd); +LWS_EXTERN void +lws_add_wsi_to_draining_ext_list(struct lws *wsi); +LWS_EXTERN void +lws_remove_wsi_from_draining_ext_list(struct lws *wsi); +LWS_EXTERN int +lws_plat_context_early_init(void); +LWS_EXTERN void +lws_plat_context_early_destroy(struct lws_context *context); +LWS_EXTERN void +lws_plat_context_late_destroy(struct lws_context *context); +LWS_EXTERN int +lws_poll_listen_fd(struct lws_pollfd *fd); +LWS_EXTERN int +lws_plat_service(struct lws_context *context, int timeout_ms); +LWS_EXTERN LWS_VISIBLE int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi); +LWS_EXTERN int +lws_plat_init(struct lws_context *context, + struct lws_context_creation_info *info); +LWS_EXTERN void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info); +LWS_EXTERN unsigned long long +time_in_microseconds(void); +LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_plat_inet_pton(int af, const char *src, void *dst); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len); +LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount); +LWS_EXTERN int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount); + +LWS_EXTERN void +lws_same_vh_protocol_remove(struct lws *wsi); +LWS_EXTERN void +lws_same_vh_protocol_insert(struct lws *wsi, int n); + +#if defined(LWS_WITH_STATS) +void +lws_stats_atomic_bump(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t bump); +void +lws_stats_atomic_max(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t val); +#else +static inline uint64_t lws_stats_atomic_bump(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t bump) { + (void)context; (void)pt; (void)index; (void)bump; return 0; } +static inline uint64_t lws_stats_atomic_max(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t val) { + (void)context; (void)pt; (void)index; (void)val; return 0; } +#endif + +/* socks */ +void socks_generate_msg(struct lws *wsi, enum socks_msg_type type, + ssize_t *msg_len); + +#if defined(LWS_WITH_PEER_LIMITS) +void +lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer); +int +lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer); +void +lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer); +void +lws_peer_cull_peer_wait_list(struct lws_context *context); +struct lws_peer * +lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd); +void +lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer, + struct lws *wsi); +#endif + +#ifdef __cplusplus +}; +#endif diff -Nru libwebsockets-4.0.20/lib/README.md libwebsockets-2.4.2/lib/README.md --- libwebsockets-4.0.20/lib/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -## Library sources layout - -Code that goes in the libwebsockets library itself lives down ./lib - -Path|Sources ----|--- -lib/core|Core lws code related to generic fd and wsi servicing and management -lib/core-net|Core lws code that applies only if networking enabled -lib/event-libs|Code containing optional event-lib specific adaptations -lib/jose|JOSE / JWS / JWK / JWE implementations -lib/misc|Code for various mostly optional miscellaneous features -lib/plat|Platform-specific adaptation code -lib/roles|Code for specific optional wsi roles, eg, http/1, h2, ws, raw, etc -lib/system|Code for system-level features, eg, dhcpclient -lib/tls|Code supporting the various TLS libraries - diff -Nru libwebsockets-4.0.20/lib/roles/cgi/cgi-server.c libwebsockets-2.4.2/lib/roles/cgi/cgi-server.c --- libwebsockets-4.0.20/lib/roles/cgi/cgi-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/cgi/cgi-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1049 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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(_GNU_SOURCE) -#define _GNU_SOURCE -#endif - -#include "private-lib-core.h" - -#if defined(WIN32) || defined(_WIN32) -#else -#include -#endif - -static const char *hex = "0123456789ABCDEF"; - -static int -urlencode(const char *in, int inlen, char *out, int outlen) -{ - char *start = out, *end = out + outlen; - - while (inlen-- && out < end - 4) { - if ((*in >= 'A' && *in <= 'Z') || - (*in >= 'a' && *in <= 'z') || - (*in >= '0' && *in <= '9') || - *in == '-' || - *in == '_' || - *in == '.' || - *in == '~') { - *out++ = *in++; - continue; - } - if (*in == ' ') { - *out++ = '+'; - in++; - continue; - } - *out++ = '%'; - *out++ = hex[(*in) >> 4]; - *out++ = hex[(*in++) & 15]; - } - *out = '\0'; - - if (out >= end - 4) - return -1; - - return out - start; -} - -int -lws_cgi(struct lws *wsi, const char * const *exec_array, - int script_uri_path_len, int timeout_secs, - const struct lws_protocol_vhost_options *mp_cgienv) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct lws_spawn_piped_info info; - char *env_array[30], cgi_path[500], e[1024], *p = e, - *end = p + sizeof(e) - 1, tok[256], *t, *sum, *sumend; - struct lws_cgi *cgi; - int n, m = 0, i, uritok = -1, c; - - /* - * give the master wsi a cgi struct - */ - - wsi->http.cgi = lws_zalloc(sizeof(*wsi->http.cgi), "new cgi"); - if (!wsi->http.cgi) { - lwsl_err("%s: OOM\n", __func__); - return -1; - } - - wsi->http.cgi->response_code = HTTP_STATUS_OK; - - cgi = wsi->http.cgi; - cgi->wsi = wsi; /* set cgi's owning wsi */ - sum = cgi->summary; - sumend = sum + strlen(cgi->summary) - 1; - - if (timeout_secs) - lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs); - - /* the cgi stdout is always sending us http1.x header data first */ - wsi->hdr_state = LCHS_HEADER; - - /* add us to the pt list of active cgis */ - lwsl_debug("%s: adding cgi %p to list\n", __func__, wsi->http.cgi); - cgi->cgi_list = pt->http.cgi_list; - pt->http.cgi_list = cgi; - - sum += lws_snprintf(sum, sumend - sum, "%s ", exec_array[0]); - - if (0) { - char *pct = lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_CONTENT_ENCODING); - - if (pct && !strcmp(pct, "gzip")) - wsi->http.cgi->gzip_inflate = 1; - } - - /* prepare his CGI env */ - - n = 0; - - if (lws_is_ssl(wsi)) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "HTTPS=ON"); - p++; - } - - if (wsi->http.ah) { - static const unsigned char meths[] = { - WSI_TOKEN_GET_URI, - WSI_TOKEN_POST_URI, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - WSI_TOKEN_OPTIONS_URI, - WSI_TOKEN_PUT_URI, - WSI_TOKEN_PATCH_URI, - WSI_TOKEN_DELETE_URI, -#endif - WSI_TOKEN_CONNECT, - WSI_TOKEN_HEAD_URI, - #ifdef LWS_WITH_HTTP2 - WSI_TOKEN_HTTP_COLON_PATH, - #endif - }; - static const char * const meth_names[] = { - "GET", "POST", -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - "OPTIONS", "PUT", "PATCH", "DELETE", -#endif - "CONNECT", "HEAD", ":path" - }; - - if (script_uri_path_len >= 0) - for (m = 0; m < (int)LWS_ARRAY_SIZE(meths); m++) - if (lws_hdr_total_length(wsi, meths[m]) >= - script_uri_path_len) { - uritok = meths[m]; - break; - } - - if (script_uri_path_len < 0 && uritok < 0) - goto bail; -// if (script_uri_path_len < 0) -// uritok = 0; - - if (m >= 0) { - env_array[n++] = p; - if (m < (int)LWS_ARRAY_SIZE(meths) - 1) { - p += lws_snprintf(p, end - p, - "REQUEST_METHOD=%s", - meth_names[m]); - sum += lws_snprintf(sum, sumend - sum, "%s ", - meth_names[m]); -#if defined(LWS_ROLE_H2) - } else { - p += lws_snprintf(p, end - p, - "REQUEST_METHOD=%s", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD)); - sum += lws_snprintf(sum, sumend - sum, "%s ", - lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_COLON_METHOD)); -#endif - } - p++; - } - - if (uritok >= 0) - sum += lws_snprintf(sum, sumend - sum, "%s ", - lws_hdr_simple_ptr(wsi, uritok)); - - env_array[n++] = p; - p += lws_snprintf(p, end - p, "QUERY_STRING="); - /* dump the individual URI Arg parameters */ - m = 0; - while (script_uri_path_len >= 0) { - i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok), - WSI_TOKEN_HTTP_URI_ARGS, m); - if (i < 0) - break; - t = tok; - while (*t && *t != '=' && p < end - 4) - *p++ = *t++; - if (*t == '=') - *p++ = *t++; - i = urlencode(t, i- (t - tok), p, end - p); - if (i > 0) { - p += i; - *p++ = '&'; - } - m++; - } - if (m) - p--; - *p++ = '\0'; - - if (uritok >= 0) { - strcpy(cgi_path, "REQUEST_URI="); - c = lws_hdr_copy(wsi, cgi_path + 12, - sizeof(cgi_path) - 12, uritok); - if (c < 0) - goto bail; - - cgi_path[sizeof(cgi_path) - 1] = '\0'; - env_array[n++] = cgi_path; - } - - sum += lws_snprintf(sum, sumend - sum, "%s", env_array[n - 1]); - - if (script_uri_path_len >= 0) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "PATH_INFO=%s", - cgi_path + 12 + script_uri_path_len); - p++; - } - } -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - if (script_uri_path_len >= 0 && - lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "HTTP_REFERER=%s", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER)); - p++; - } -#endif - if (script_uri_path_len >= 0 && - lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "HTTP_HOST=%s", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); - p++; - } - if (script_uri_path_len >= 0 && - lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "HTTP_COOKIE="); - m = lws_hdr_copy(wsi, p, end - p, WSI_TOKEN_HTTP_COOKIE); - if (m > 0) - p += lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE); - *p++ = '\0'; - } -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - if (script_uri_path_len >= 0 && - lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "HTTP_USER_AGENT=%s", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT)); - p++; - } -#endif - if (script_uri_path_len >= 0 && - lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING)) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "HTTP_CONTENT_ENCODING=%s", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING)); - p++; - } - if (script_uri_path_len >= 0 && - lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT)) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "HTTP_ACCEPT=%s", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT)); - p++; - } - if (script_uri_path_len >= 0 && - lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING)) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "HTTP_ACCEPT_ENCODING=%s", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING)); - p++; - } - if (script_uri_path_len >= 0 && - uritok == WSI_TOKEN_POST_URI) { - if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "CONTENT_TYPE=%s", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)); - p++; - } - if (!wsi->http.cgi->gzip_inflate && - lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "CONTENT_LENGTH=%s", - lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_CONTENT_LENGTH)); - p++; - } - - if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) - wsi->http.cgi->post_in_expected = - atoll(lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_CONTENT_LENGTH)); - } - - - env_array[n++] = p; - p += lws_snprintf(p, end - p, "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin"); - p++; - - env_array[n++] = p; - p += lws_snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]); - p++; - - while (mp_cgienv) { - env_array[n++] = p; - p += lws_snprintf(p, end - p, "%s=%s", mp_cgienv->name, - mp_cgienv->value); - if (!strcmp(mp_cgienv->name, "GIT_PROJECT_ROOT")) { - wsi->http.cgi->implied_chunked = 1; - wsi->http.cgi->explicitly_chunked = 1; - } - lwsl_info(" Applying mount-specific cgi env '%s'\n", - env_array[n - 1]); - p++; - mp_cgienv = mp_cgienv->next; - } - - env_array[n++] = p; - p += lws_snprintf(p, end - p, "SERVER_SOFTWARE=lws"); - p++; - - env_array[n] = NULL; - -#if 0 - for (m = 0; m < n; m++) - lwsl_notice(" %s\n", env_array[m]); -#endif - - memset(&info, 0, sizeof(info)); - info.env_array = env_array; - info.exec_array = exec_array; - info.max_log_lines = 20000; - info.opt_parent = wsi; - info.timeout_us = 5 * 60 * LWS_US_PER_SEC; - info.tsi = wsi->tsi; - info.vh = wsi->vhost; - info.ops = &role_ops_cgi; - info.plsp = &wsi->http.cgi->lsp; - - /* - * Actually having made the env, as a cgi we don't need the ah - * any more - */ - if (script_uri_path_len >= 0) { - lws_header_table_detach(wsi, 0); - info.disable_ctrlc = 1; - } - - wsi->http.cgi->lsp = lws_spawn_piped(&info); - if (!wsi->http.cgi->lsp) { - lwsl_err("%s: spawn failed\n", __func__); - goto bail; - } - - /* we are the parent process */ - - wsi->context->count_cgi_spawned++; - - /* inform cgi owner of the child PID */ - n = user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_CGI_PROCESS_ATTACH, - wsi->user_space, NULL, cgi->lsp->child_pid); - (void)n; - - return 0; - -bail: - lws_free_set_NULL(wsi->http.cgi); - - lwsl_err("%s: failed\n", __func__); - - return -1; -} - -/* we have to parse out these headers in the CGI output */ - -static const char * const significant_hdr[SIGNIFICANT_HDR_COUNT] = { - "content-length: ", - "location: ", - "status: ", - "transfer-encoding: chunked", - "content-encoding: gzip", -}; - -enum header_recode { - HR_NAME, - HR_WHITESPACE, - HR_ARG, - HR_CRLF, -}; - -int -lws_cgi_write_split_stdout_headers(struct lws *wsi) -{ - int n, m, cmd; - unsigned char buf[LWS_PRE + 4096], *start = &buf[LWS_PRE], *p = start, - *end = &buf[sizeof(buf) - 1 - LWS_PRE], *name, - *value = NULL; - char c, hrs; - - if (!wsi->http.cgi) - return -1; - - while (wsi->hdr_state != LHCS_PAYLOAD) { - /* - * We have to separate header / finalize and payload chunks, - * since they need to be handled separately - */ - switch (wsi->hdr_state) { - case LHCS_RESPONSE: - lwsl_debug("LHCS_RESPONSE: issuing response %d\n", - wsi->http.cgi->response_code); - if (lws_add_http_header_status(wsi, - wsi->http.cgi->response_code, - &p, end)) - return 1; - if (!wsi->http.cgi->explicitly_chunked && - !wsi->http.cgi->content_length && - lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_TRANSFER_ENCODING, - (unsigned char *)"chunked", 7, &p, end)) - return 1; - if (!(wsi->mux_substream)) - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_CONNECTION, - (unsigned char *)"close", 5, - &p, end)) - return 1; - n = lws_write(wsi, start, p - start, - LWS_WRITE_HTTP_HEADERS | LWS_WRITE_NO_FIN); - - /* - * so we have a bunch of http/1 style ascii headers - * starting from wsi->http.cgi->headers_buf through - * wsi->http.cgi->headers_pos. These are OK for http/1 - * connections, but they're no good for http/2 conns. - * - * Let's redo them at headers_pos forward using the - * correct coding for http/1 or http/2 - */ - if (!wsi->mux_substream) - goto post_hpack_recode; - - p = wsi->http.cgi->headers_start; - wsi->http.cgi->headers_start = - wsi->http.cgi->headers_pos; - wsi->http.cgi->headers_dumped = - wsi->http.cgi->headers_start; - hrs = HR_NAME; - name = buf; - - while (p < wsi->http.cgi->headers_start) { - switch (hrs) { - case HR_NAME: - /* - * in http/2 upper-case header names - * are illegal. So convert to lower- - * case. - */ - if (name - buf > 64) - return -1; - if (*p != ':') { - if (*p >= 'A' && *p <= 'Z') - *name++ = (*p++) + - ('a' - 'A'); - else - *name++ = *p++; - } else { - p++; - *name++ = '\0'; - value = name; - hrs = HR_WHITESPACE; - } - break; - case HR_WHITESPACE: - if (*p == ' ') { - p++; - break; - } - hrs = HR_ARG; - /* fallthru */ - case HR_ARG: - if (name > end - 64) - return -1; - - if (*p != '\x0a' && *p != '\x0d') { - *name++ = *p++; - break; - } - hrs = HR_CRLF; - /* fallthru */ - case HR_CRLF: - if ((*p != '\x0a' && *p != '\x0d') || - p + 1 == wsi->http.cgi->headers_start) { - *name = '\0'; - if ((strcmp((const char *)buf, - "transfer-encoding") - )) { - lwsl_debug("+ %s: %s\n", - buf, value); - if ( - lws_add_http_header_by_name(wsi, buf, - (unsigned char *)value, name - value, - (unsigned char **)&wsi->http.cgi->headers_pos, - (unsigned char *)wsi->http.cgi->headers_end)) - return 1; - hrs = HR_NAME; - name = buf; - break; - } - } - p++; - break; - } - } -post_hpack_recode: - /* finalize cached headers before dumping them */ - if (lws_finalize_http_header(wsi, - (unsigned char **)&wsi->http.cgi->headers_pos, - (unsigned char *)wsi->http.cgi->headers_end)) { - - lwsl_notice("finalize failed\n"); - return -1; - } - - wsi->hdr_state = LHCS_DUMP_HEADERS; - wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_HEADERS; - lws_callback_on_writable(wsi); - /* back to the loop for writeability again */ - return 0; - - case LHCS_DUMP_HEADERS: - - n = wsi->http.cgi->headers_pos - - wsi->http.cgi->headers_dumped; - if (n > 512) - n = 512; - - lwsl_debug("LHCS_DUMP_HEADERS: %d\n", n); - - cmd = LWS_WRITE_HTTP_HEADERS_CONTINUATION; - if (wsi->http.cgi->headers_dumped + n != - wsi->http.cgi->headers_pos) { - lwsl_notice("adding no fin flag\n"); - cmd |= LWS_WRITE_NO_FIN; - } - - m = lws_write(wsi, - (unsigned char *)wsi->http.cgi->headers_dumped, - n, cmd); - if (m < 0) { - lwsl_debug("%s: write says %d\n", __func__, m); - return -1; - } - wsi->http.cgi->headers_dumped += n; - if (wsi->http.cgi->headers_dumped == - wsi->http.cgi->headers_pos) { - wsi->hdr_state = LHCS_PAYLOAD; - lws_free_set_NULL(wsi->http.cgi->headers_buf); - lwsl_debug("freed cgi headers\n"); - } else { - wsi->reason_bf |= - LWS_CB_REASON_AUX_BF__CGI_HEADERS; - lws_callback_on_writable(wsi); - } - - /* writeability becomes uncertain now we wrote - * something, we must return to the event loop - */ - return 0; - } - - if (!wsi->http.cgi->headers_buf) { - /* if we don't already have a headers buf, cook one */ - n = 2048; - if (wsi->mux_substream) - n = 4096; - wsi->http.cgi->headers_buf = lws_malloc(n + LWS_PRE, - "cgi hdr buf"); - if (!wsi->http.cgi->headers_buf) { - lwsl_err("OOM\n"); - return -1; - } - - lwsl_debug("allocated cgi hdrs\n"); - wsi->http.cgi->headers_start = - wsi->http.cgi->headers_buf + LWS_PRE; - wsi->http.cgi->headers_pos = wsi->http.cgi->headers_start; - wsi->http.cgi->headers_dumped = wsi->http.cgi->headers_pos; - wsi->http.cgi->headers_end = - wsi->http.cgi->headers_buf + n - 1; - - for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) { - wsi->http.cgi->match[n] = 0; - wsi->http.cgi->lp = 0; - } - } - - n = lws_get_socket_fd(wsi->http.cgi->lsp->stdwsi[LWS_STDOUT]); - if (n < 0) - return -1; - n = read(n, &c, 1); - if (n < 0) { - if (errno != EAGAIN) { - lwsl_debug("%s: read says %d\n", __func__, n); - return -1; - } - else - n = 0; - - if (wsi->http.cgi->headers_pos >= - wsi->http.cgi->headers_end - 4) { - lwsl_notice("CGI hdrs > buf size\n"); - - return -1; - } - } - if (!n) - goto agin; - - lwsl_debug("-- 0x%02X %c %d %d\n", (unsigned char)c, c, - wsi->http.cgi->match[1], wsi->hdr_state); - if (!c) - return -1; - switch (wsi->hdr_state) { - case LCHS_HEADER: - hdr: - for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) { - /* - * significant headers with - * numeric decimal payloads - */ - if (!significant_hdr[n][wsi->http.cgi->match[n]] && - (c >= '0' && c <= '9') && - wsi->http.cgi->lp < (int)sizeof(wsi->http.cgi->l) - 1) { - wsi->http.cgi->l[wsi->http.cgi->lp++] = c; - wsi->http.cgi->l[wsi->http.cgi->lp] = '\0'; - switch (n) { - case SIGNIFICANT_HDR_CONTENT_LENGTH: - wsi->http.cgi->content_length = - atoll(wsi->http.cgi->l); - break; - case SIGNIFICANT_HDR_STATUS: - wsi->http.cgi->response_code = - atol(wsi->http.cgi->l); - lwsl_debug("Status set to %d\n", - wsi->http.cgi->response_code); - break; - default: - break; - } - } - /* hits up to the NUL are sticky until next hdr */ - if (significant_hdr[n][wsi->http.cgi->match[n]]) { - if (tolower(c) == - significant_hdr[n][wsi->http.cgi->match[n]]) - wsi->http.cgi->match[n]++; - else - wsi->http.cgi->match[n] = 0; - } - } - - /* some cgi only send us \x0a for EOL */ - if (c == '\x0a') { - wsi->hdr_state = LCHS_SINGLE_0A; - *wsi->http.cgi->headers_pos++ = '\x0d'; - } - *wsi->http.cgi->headers_pos++ = c; - if (c == '\x0d') - wsi->hdr_state = LCHS_LF1; - - if (wsi->hdr_state != LCHS_HEADER && - !significant_hdr[SIGNIFICANT_HDR_TRANSFER_ENCODING] - [wsi->http.cgi->match[ - SIGNIFICANT_HDR_TRANSFER_ENCODING]]) { - lwsl_info("cgi produced chunked\n"); - wsi->http.cgi->explicitly_chunked = 1; - } - - /* presence of Location: mandates 302 retcode */ - if (wsi->hdr_state != LCHS_HEADER && - !significant_hdr[SIGNIFICANT_HDR_LOCATION][ - wsi->http.cgi->match[SIGNIFICANT_HDR_LOCATION]]) { - lwsl_debug("CGI: Location hdr seen\n"); - wsi->http.cgi->response_code = 302; - } - break; - case LCHS_LF1: - *wsi->http.cgi->headers_pos++ = c; - if (c == '\x0a') { - wsi->hdr_state = LCHS_CR2; - break; - } - /* we got \r[^\n]... it's unreasonable */ - lwsl_debug("%s: funny CRLF 0x%02X\n", __func__, - (unsigned char)c); - return -1; - - case LCHS_CR2: - if (c == '\x0d') { - /* drop the \x0d */ - wsi->hdr_state = LCHS_LF2; - break; - } - wsi->hdr_state = LCHS_HEADER; - for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) - wsi->http.cgi->match[n] = 0; - wsi->http.cgi->lp = 0; - goto hdr; - - case LCHS_LF2: - case LCHS_SINGLE_0A: - m = wsi->hdr_state; - if (c == '\x0a') { - lwsl_debug("Content-Length: %lld\n", - (unsigned long long) - wsi->http.cgi->content_length); - wsi->hdr_state = LHCS_RESPONSE; - /* - * drop the \0xa ... finalize - * will add it if needed (HTTP/1) - */ - break; - } - if (m == LCHS_LF2) - /* we got \r\n\r[^\n]... unreasonable */ - return -1; - /* we got \x0anext header, it's reasonable */ - *wsi->http.cgi->headers_pos++ = c; - wsi->hdr_state = LCHS_HEADER; - for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) - wsi->http.cgi->match[n] = 0; - wsi->http.cgi->lp = 0; - break; - case LHCS_PAYLOAD: - break; - } - -agin: - /* ran out of input, ended the hdrs, or filled up the hdrs buf */ - if (!n || wsi->hdr_state == LHCS_PAYLOAD) - return 0; - } - - /* payload processing */ - - m = !wsi->http.cgi->implied_chunked && !wsi->mux_substream && - // !wsi->http.cgi->explicitly_chunked && - !wsi->http.cgi->content_length; - n = lws_get_socket_fd(wsi->http.cgi->lsp->stdwsi[LWS_STDOUT]); - if (n < 0) - return -1; - n = read(n, start, sizeof(buf) - LWS_PRE); - - if (n < 0 && errno != EAGAIN) { - lwsl_debug("%s: stdout read says %d\n", __func__, n); - return -1; - } - if (n > 0) { - // lwsl_hexdump_notice(buf, n); - - if (!wsi->mux_substream && m) { - char chdr[LWS_HTTP_CHUNK_HDR_SIZE]; - m = lws_snprintf(chdr, LWS_HTTP_CHUNK_HDR_SIZE - 3, - "%X\x0d\x0a", n); - memmove(start + m, start, n); - memcpy(start, chdr, m); - memcpy(start + m + n, "\x0d\x0a", 2); - n += m + 2; - } - - -#if defined(LWS_WITH_HTTP2) - if (wsi->mux_substream) { - struct lws *nwsi = lws_get_network_wsi(wsi); - - __lws_set_timeout(wsi, - PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31); - - if (!nwsi->immortal_substream_count) - __lws_set_timeout(nwsi, - PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31); - } -#endif - - cmd = LWS_WRITE_HTTP; - if (wsi->http.cgi->content_length_seen + n == - wsi->http.cgi->content_length) - cmd = LWS_WRITE_HTTP_FINAL; - - m = lws_write(wsi, (unsigned char *)start, n, cmd); - //lwsl_notice("write %d\n", m); - if (m < 0) { - lwsl_debug("%s: stdout write says %d\n", __func__, m); - return -1; - } - wsi->http.cgi->content_length_seen += n; - } else { - - if (!wsi->mux_substream && m) { - uint8_t term[LWS_PRE + 6]; - - lwsl_notice("%s: sent trailer\n", __func__); - memcpy(term + LWS_PRE, (uint8_t *)"0\x0d\x0a\x0d\x0a", 5); - - if (lws_write(wsi, term + LWS_PRE, 5, - LWS_WRITE_HTTP_FINAL) != 5) - return -1; - - wsi->http.cgi->cgi_transaction_over = 1; - - return 0; - } - - if (wsi->cgi_stdout_zero_length) { - lwsl_debug("%s: stdout is POLLHUP'd\n", __func__); - if (wsi->mux_substream) - m = lws_write(wsi, (unsigned char *)start, 0, - LWS_WRITE_HTTP_FINAL); - else - return -1; - return 1; - } - wsi->cgi_stdout_zero_length = 1; - } - return 0; -} - -int -lws_cgi_kill(struct lws *wsi) -{ - struct lws_cgi_args args; - pid_t pid; - int n, m; - - lwsl_debug("%s: %p\n", __func__, wsi); - - if (!wsi->http.cgi || !wsi->http.cgi->lsp) - return 0; - - pid = wsi->http.cgi->lsp->child_pid; - - args.stdwsi = &wsi->http.cgi->lsp->stdwsi[0]; - lws_spawn_piped_kill_child_process(wsi->http.cgi->lsp); - /* that has invalidated and NULL'd wsi->http.cgi->lsp */ - - if (pid != -1) { - m = wsi->http.cgi->being_closed; - n = user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_CGI_TERMINATED, - wsi->user_space, (void *)&args, - pid); - if (n && !m) - lws_close_free_wsi(wsi, 0, "lws_cgi_kill"); - } - - return 0; -} - -int -lws_cgi_kill_terminated(struct lws_context_per_thread *pt) -{ - struct lws_cgi **pcgi, *cgi = NULL; - int status, n = 1; - - while (n > 0) { - /* find finished guys but don't reap yet */ - n = waitpid(-1, &status, WNOHANG); - if (n <= 0) - continue; - lwsl_debug("%s: observed PID %d terminated\n", __func__, n); - - pcgi = &pt->http.cgi_list; - - /* check all the subprocesses on the cgi list */ - while (*pcgi) { - /* get the next one first as list may change */ - cgi = *pcgi; - pcgi = &(*pcgi)->cgi_list; - - if (cgi->lsp->child_pid <= 0) - continue; - - /* finish sending cached headers */ - if (cgi->headers_buf) - continue; - - /* wait for stdout to be drained */ - if (cgi->content_length > cgi->content_length_seen) - continue; - - if (cgi->content_length) { - lwsl_debug("%s: wsi %p: expected content " - "length seen: %lld\n", __func__, - cgi->wsi, - (unsigned long long)cgi->content_length_seen); - } - - /* reap it */ - waitpid(n, &status, WNOHANG); - /* - * he's already terminated so no need for kill() - * but we should do the terminated cgi callback - * and close him if he's not already closing - */ - if (n == cgi->lsp->child_pid) { - lwsl_debug("%s: found PID %d on cgi list\n", - __func__, n); - - if (!cgi->content_length) { - /* - * well, if he sends chunked... - * give him 2s after the - * cgi terminated to send buffered - */ - cgi->chunked_grace++; - continue; - } - - /* defeat kill() */ - cgi->lsp->child_pid = 0; - lws_cgi_kill(cgi->wsi); - - break; - } - cgi = NULL; - } - /* if not found on the cgi list, as he's one of ours, reap */ - if (!cgi) { - lwsl_debug("%s: reading PID %d although no cgi match\n", - __func__, n); - waitpid(n, &status, WNOHANG); - } - } - - pcgi = &pt->http.cgi_list; - - /* check all the subprocesses on the cgi list */ - while (*pcgi) { - /* get the next one first as list may change */ - cgi = *pcgi; - pcgi = &(*pcgi)->cgi_list; - - if (cgi->lsp->child_pid <= 0) - continue; - - /* we deferred killing him after reaping his PID */ - if (cgi->chunked_grace) { - cgi->chunked_grace++; - if (cgi->chunked_grace < 2) - continue; - goto finish_him; - } - - /* finish sending cached headers */ - if (cgi->headers_buf) - continue; - - /* wait for stdout to be drained */ - if (cgi->content_length > cgi->content_length_seen) - continue; - - if (cgi->content_length) - lwsl_debug("%s: wsi %p: expected " - "content len seen: %lld\n", __func__, - cgi->wsi, - (unsigned long long)cgi->content_length_seen); - - /* reap it */ - if (waitpid(cgi->lsp->child_pid, &status, WNOHANG) > 0) { - - if (!cgi->content_length) { - /* - * well, if he sends chunked... - * give him 2s after the - * cgi terminated to send buffered - */ - cgi->chunked_grace++; - continue; - } -finish_him: - lwsl_debug("%s: found PID %d on cgi list\n", - __func__, cgi->lsp->child_pid); - - /* defeat kill() */ - cgi->lsp->child_pid = 0; - lws_cgi_kill(cgi->wsi); - - break; - } - } - - return 0; -} - -struct lws * -lws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch) -{ - if (!wsi->http.cgi) - return NULL; - - return wsi->http.cgi->lsp->stdwsi[ch]; -} - -void -lws_cgi_remove_and_kill(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct lws_cgi **pcgi = &pt->http.cgi_list; - - /* remove us from the cgi list */ - lwsl_debug("%s: remove cgi %p from list\n", __func__, wsi->http.cgi); - while (*pcgi) { - if (*pcgi == wsi->http.cgi) { - /* drop us from the pt cgi list */ - *pcgi = (*pcgi)->cgi_list; - break; - } - pcgi = &(*pcgi)->cgi_list; - } - if (wsi->http.cgi->headers_buf) { - lwsl_debug("close: freed cgi headers\n"); - lws_free_set_NULL(wsi->http.cgi->headers_buf); - } - /* we have a cgi going, we must kill it */ - wsi->http.cgi->being_closed = 1; - lws_cgi_kill(wsi); -} diff -Nru libwebsockets-4.0.20/lib/roles/cgi/ops-cgi.c libwebsockets-2.4.2/lib/roles/cgi/ops-cgi.c --- libwebsockets-4.0.20/lib/roles/cgi/ops-cgi.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/cgi/ops-cgi.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,147 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -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_HANDLED; - - if (wsi->lsp_channel == LWS_STDIN && - !(pollfd->revents & pollfd->events & LWS_POLLOUT)) - return LWS_HPI_RET_HANDLED; - - if (wsi->lsp_channel == LWS_STDIN && - lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { - lwsl_info("failed at set pollfd\n"); - return LWS_HPI_RET_WSI_ALREADY_DIED; - } - - args.ch = wsi->lsp_channel; - args.stdwsi = &wsi->parent->http.cgi->lsp->stdwsi[0]; - args.hdr_state = wsi->hdr_state; - - lwsl_debug("CGI LWS_STDOUT %p wsistate 0x%x\n", - wsi->parent, wsi->wsistate); - - if (user_callback_handle_rxflow(wsi->parent->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; -} - -static 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); - - __lws_sul_insert(&pt->pt_sul_owner, &pt->sul_cgi, - 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) -{ - if (!destroy) { - - pt->sul_cgi.cb = lws_cgi_sul_cb; - - __lws_sul_insert(&pt->pt_sul_owner, &pt->sul_cgi, - 3 * LWS_US_PER_SEC); - } else - lws_dll2_remove(&pt->sul_cgi.list); - - return 0; -} - - -const struct lws_role_ops role_ops_cgi = { - /* role name */ "cgi", - /* alpn id */ NULL, - /* check_upgrades */ NULL, - /* pt_init_destroy */ rops_pt_init_destroy_cgi, - /* init_vhost */ NULL, - /* destroy_vhost */ NULL, - /* service_flag_pending */ NULL, - /* handle_POLLIN */ rops_handle_POLLIN_cgi, - /* handle_POLLOUT */ rops_handle_POLLOUT_cgi, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ NULL, - /* tx_credit */ NULL, - /* write_role_protocol */ NULL, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ NULL, - /* close_via_role_protocol */ NULL, - /* close_role */ NULL, - /* close_kill_connection */ NULL, - /* destroy_role */ rops_destroy_role_cgi, - /* adoption_bind */ NULL, - /* client_bind */ NULL, - /* issue_keepalive */ NULL, - /* 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, -}; diff -Nru libwebsockets-4.0.20/lib/roles/cgi/private-lib-roles-cgi.h libwebsockets-2.4.2/lib/roles/cgi/private-lib-roles-cgi.h --- libwebsockets-4.0.20/lib/roles/cgi/private-lib-roles-cgi.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/cgi/private-lib-roles-cgi.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 -#else -#include -#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 master of the cgi points to an lws_cgi */ - -struct lws_cgi { - struct lws_cgi *cgi_list; - - struct lws_spawn_piped *lsp; - - 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; - - 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; -}; diff -Nru libwebsockets-4.0.20/lib/roles/dbus/dbus.c libwebsockets-2.4.2/lib/roles/dbus/dbus.c --- libwebsockets-4.0.20/lib/roles/dbus/dbus.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/dbus/dbus.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,545 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -#include - -/* - * retreives existing or creates new shadow wsi for fd owned by dbus stuff. - * - * Requires 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; - - wsi = lws_zalloc(sizeof(*wsi), "shadow wsi"); - if (wsi == NULL) { - lwsl_err("Out of mem\n"); - return NULL; - } - - lwsl_info("%s: creating shadow wsi\n", __func__); - - wsi->context = ctx->vh->context; - wsi->desc.sockfd = fd; - lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_dbus); - wsi->protocol = ctx->vh->protocols; - wsi->tsi = ctx->tsi; - wsi->shadow = 1; - wsi->opaque_parent_data = ctx; - ctx->w[0] = w; - - 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); - lws_free(wsi); - return NULL; - } - - ctx->vh->context->count_wsi_allocated++; - - return wsi; -} - -/* - * Requires vhost lock - */ - -static int -__lws_shadow_wsi_destroy(struct lws_dbus_ctx *ctx, struct lws *wsi) -{ - lwsl_info("%s: destroying shadow wsi\n", __func__); - - if (__remove_wsi_socket_from_fds(wsi)) { - lwsl_err("%s: unable to remove %d from fds\n", __func__, - wsi->desc.sockfd); - - return 1; - } - - ctx->vh->context->count_wsi_allocated--; - 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_pt_lock(pt, __func__); - - wsi = __lws_shadow_wsi(ctx, w, dbus_watch_get_unix_fd(w), 1); - if (!wsi) { - lws_pt_unlock(pt); - 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]) - 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: w %p, fd %d, data %p, flags %d\n", __func__, w, - dbus_watch_get_unix_fd(w), data, lws_flags); - - __lws_change_pollfd(wsi, 0, lws_flags); - - lws_pt_unlock(pt); - - return TRUE; -} - -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_pt_lock(pt, __func__); - - wsi = __lws_shadow_wsi(ctx, w, dbus_watch_get_unix_fd(w), 0); - 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: w %p, fd %d, data %p, clearing lws flags %d\n", - __func__, w, dbus_watch_get_unix_fd(w), data, lws_flags); - - __lws_change_pollfd(wsi, lws_flags, 0); - -bail: - lws_pt_unlock(pt); -} - -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 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); - - 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); -} - -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 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_dll2_remove(rdt); - lws_free(rdt); - } - } lws_end_foreach_dll_safe(rdt, nx); - - __lws_sul_insert(&pt->pt_sul_owner, &pt->dbus.sul, - 3 * LWS_US_PER_SEC); -} - -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) { - - pt->dbus.sul.cb = lws_dbus_sul_cb; - - __lws_sul_insert(&pt->pt_sul_owner, &pt->dbus.sul, - 3 * LWS_US_PER_SEC); - } else - lws_dll2_remove(&pt->dbus.sul.list); - - return 0; -} - -const struct lws_role_ops role_ops_dbus = { - /* role name */ "dbus", - /* alpn id */ NULL, - /* check_upgrades */ NULL, - /* pt_init_destroy */ rops_pt_init_destroy_dbus, - /* init_vhost */ NULL, - /* destroy_vhost */ NULL, - /* service_flag_pending */ NULL, - /* handle_POLLIN */ rops_handle_POLLIN_dbus, - /* handle_POLLOUT */ NULL, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ NULL, - /* tx_credit */ NULL, - /* write_role_protocol */ NULL, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ NULL, - /* close_via_role_protocol */ NULL, - /* close_role */ NULL, - /* close_kill_connection */ NULL, - /* destroy_role */ NULL, - /* adoption_bind */ NULL, - /* client_bind */ NULL, - /* issue_keepalive */ NULL, - /* 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, -}; diff -Nru libwebsockets-4.0.20/lib/roles/dbus/private-lib-roles-dbus.h libwebsockets-2.4.2/lib/roles/dbus/private-lib-roles-dbus.h --- libwebsockets-4.0.20/lib/roles/dbus/private-lib-roles-dbus.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/dbus/private-lib-roles-dbus.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -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; -}; diff -Nru libwebsockets-4.0.20/lib/roles/dbus/README.md libwebsockets-2.4.2/lib/roles/dbus/README.md --- libwebsockets-4.0.20/lib/roles/dbus/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/dbus/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -# 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 ` -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 -``` - -This includes `` 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 - - - diff -Nru libwebsockets-4.0.20/lib/roles/h1/ops-h1.c libwebsockets-2.4.2/lib/roles/h1/ops-h1.c --- libwebsockets-4.0.20/lib/roles/h1/ops-h1.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/h1/ops-h1.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1185 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -#ifndef min -#define min(a, b) ((a) < (b) ? (a) : (b)) -#endif - - -/* - * We have to take care about parsing because the headers may be split - * into multiple fragments. They may contain unknown headers with arbitrary - * argument lengths. So, we parse using a single-character at a time state - * machine that is completely independent of packet size. - * - * Returns <0 for error or length of chars consumed from buf (up to len) - */ - -int -lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len) -{ - unsigned char *last_char, *oldbuf = buf; - lws_filepos_t body_chunk_len; - size_t n; - - lwsl_debug("%s: h1 path: wsi state 0x%x\n", __func__, lwsi_state(wsi)); - - switch (lwsi_state(wsi)) { - - case LRS_ISSUING_FILE: - return 0; - - case LRS_ESTABLISHED: - - if (lwsi_role_ws(wsi)) - goto ws_mode; - - if (lwsi_role_client(wsi)) - break; - - wsi->hdr_parsing_completed = 0; - - /* fallthru */ - - case LRS_HEADERS: - if (!wsi->http.ah) { - lwsl_err("%s: LRS_HEADERS: NULL ah\n", __func__); - assert(0); - } - lwsl_parser("issuing %d bytes to parser\n", (int)len); -#if defined(LWS_ROLE_WS) && defined(LWS_WITH_CLIENT) - if (lws_ws_handshake_client(wsi, &buf, (size_t)len)) - goto bail; -#endif - last_char = buf; - if (lws_handshake_server(wsi, &buf, (size_t)len)) - /* Handshake indicates this session is done. */ - goto bail; - - /* we might have transitioned to RAW */ - if (wsi->role_ops == &role_ops_raw_skt -#if defined(LWS_ROLE_RAW_FILE) - || - wsi->role_ops == &role_ops_raw_file -#endif - ) - /* we gave the read buffer to RAW handler already */ - goto read_ok; - - /* - * It's possible that we've exhausted our data already, or - * rx flow control has stopped us dealing with this early, - * but lws_handshake_server doesn't update len for us. - * Figure out how much was read, so that we can proceed - * appropriately: - */ - len -= (buf - last_char); - - if (!wsi->hdr_parsing_completed) - /* More header content on the way */ - goto read_ok; - - switch (lwsi_state(wsi)) { - case LRS_ESTABLISHED: - case LRS_HEADERS: - goto read_ok; - case LRS_ISSUING_FILE: - goto read_ok; - case LRS_DISCARD_BODY: - case LRS_BODY: - wsi->http.rx_content_remain = - wsi->http.rx_content_length; - if (wsi->http.rx_content_remain) - goto http_postbody; - - /* there is no POST content */ - goto postbody_completion; - default: - break; - } - break; - - case LRS_DISCARD_BODY: - case LRS_BODY: -http_postbody: - lwsl_debug("%s: http post body: remain %d\n", __func__, - (int)wsi->http.rx_content_remain); - - if (!wsi->http.rx_content_remain) - goto postbody_completion; - - while (len && wsi->http.rx_content_remain) { - /* Copy as much as possible, up to the limit of: - * what we have in the read buffer (len) - * remaining portion of the POST body (content_remain) - */ - body_chunk_len = min(wsi->http.rx_content_remain, len); - wsi->http.rx_content_remain -= body_chunk_len; - // len -= body_chunk_len; -#ifdef LWS_WITH_CGI - if (wsi->http.cgi) { - struct lws_cgi_args args; - - args.ch = LWS_STDIN; - args.stdwsi = &wsi->http.cgi->lsp->stdwsi[0]; - args.data = buf; - args.len = body_chunk_len; - - /* returns how much used */ - n = user_callback_handle_rxflow( - wsi->protocol->callback, - wsi, LWS_CALLBACK_CGI_STDIN_DATA, - wsi->user_space, - (void *)&args, 0); - if ((int)n < 0) - goto bail; - } else { -#endif - if (lwsi_state(wsi) != LRS_DISCARD_BODY) { - n = wsi->protocol->callback(wsi, - LWS_CALLBACK_HTTP_BODY, wsi->user_space, - buf, (size_t)body_chunk_len); - if (n) - goto bail; - } - n = (size_t)body_chunk_len; -#ifdef LWS_WITH_CGI - } -#endif - buf += n; - - if (wsi->http.rx_content_remain) { - lws_set_timeout(wsi, - PENDING_TIMEOUT_HTTP_CONTENT, - wsi->context->timeout_secs); - break; - } - /* he sent all the content in time */ -postbody_completion: -#ifdef LWS_WITH_CGI - /* - * If we're running a cgi, we can't let him off the - * hook just because he sent his POST data - */ - if (wsi->http.cgi) - lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, - wsi->context->timeout_secs); - else -#endif - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); -#ifdef LWS_WITH_CGI - if (!wsi->http.cgi) -#endif - { -#if defined(LWS_WITH_SERVER) - if (lwsi_state(wsi) == LRS_DISCARD_BODY) { - /* - * repeat the transaction completed - * that got us into this state, having - * consumed the pending body now - */ - - if (lws_http_transaction_completed(wsi)) - return -1; - break; - } -#endif - lwsl_info("HTTP_BODY_COMPLETION: %p (%s)\n", - wsi, wsi->protocol->name); - - n = wsi->protocol->callback(wsi, - LWS_CALLBACK_HTTP_BODY_COMPLETION, - wsi->user_space, NULL, 0); - if (n) - goto bail; - - if (wsi->mux_substream) - lwsi_set_state(wsi, LRS_ESTABLISHED); - } - - break; - } - break; - - case LRS_RETURNED_CLOSE: - case LRS_AWAITING_CLOSE_ACK: - case LRS_WAITING_TO_SEND_CLOSE: - case LRS_SHUTDOWN: - -ws_mode: -#if defined(LWS_WITH_CLIENT) && defined(LWS_ROLE_WS) - // lwsl_notice("%s: ws_mode\n", __func__); - if (lws_ws_handshake_client(wsi, &buf, (size_t)len)) - goto bail; -#endif -#if defined(LWS_ROLE_WS) - if (lwsi_role_ws(wsi) && lwsi_role_server(wsi) && - /* - * for h2 we are on the swsi - */ - lws_parse_ws(wsi, &buf, (size_t)len) < 0) { - lwsl_info("%s: lws_parse_ws bailed\n", __func__); - goto bail; - } -#endif - // lwsl_notice("%s: ws_mode: buf moved on by %d\n", __func__, - // lws_ptr_diff(buf, oldbuf)); - break; - - case LRS_DEFERRING_ACTION: - lwsl_notice("%s: LRS_DEFERRING_ACTION\n", __func__); - break; - - case LRS_SSL_ACK_PENDING: - break; - - case LRS_FLUSHING_BEFORE_CLOSE: - break; - - case LRS_DEAD_SOCKET: - lwsl_err("%s: Unhandled state LRS_DEAD_SOCKET\n", __func__); - goto bail; - // assert(0); - /* fallthru */ - - default: - lwsl_err("%s: Unhandled state %d\n", __func__, lwsi_state(wsi)); - assert(0); - goto bail; - } - -read_ok: - /* Nothing more to do for now */ -// lwsl_info("%s: %p: read_ok, used %ld (len %d, state %d)\n", __func__, -// wsi, (long)(buf - oldbuf), (int)len, wsi->state); - - return lws_ptr_diff(buf, oldbuf); - -bail: - /* - * h2 / h2-ws calls us recursively in - * - * lws_read_h1()-> - * lws_h2_parser()-> - * lws_read_h1() - * - * pattern, having stripped the h2 framing in the middle. - * - * When taking down the whole connection, make sure that only the - * outer lws_read() does the wsi close. - */ - if (!wsi->outer_will_close) - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "lws_read_h1 bail"); - - return -1; -} -#if defined(LWS_WITH_SERVER) -static int -lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct lws_tokens ebuf; - int n, buffered; - - if (lwsi_state(wsi) == LRS_DEFERRING_ACTION) - goto try_pollout; - - /* any incoming data ready? */ - - if (!(pollfd->revents & pollfd->events & LWS_POLLIN)) - goto try_pollout; - - /* - * If we previously just did POLLIN when IN and OUT were signaled - * (because POLLIN processing may have used up the POLLOUT), don't let - * that happen twice in a row... next time we see the situation favour - * POLLOUT - */ - - if (wsi->favoured_pollin && - (pollfd->revents & pollfd->events & LWS_POLLOUT)) { - // lwsl_notice("favouring pollout\n"); - wsi->favoured_pollin = 0; - goto try_pollout; - } - - /* - * We haven't processed that the tunnel is set up yet, so - * defer reading - */ - - if (lwsi_state(wsi) == LRS_SSL_ACK_PENDING) - return LWS_HPI_RET_HANDLED; - - /* these states imply we MUST have an ah attached */ - - if ((lwsi_state(wsi) == LRS_ESTABLISHED || - lwsi_state(wsi) == LRS_ISSUING_FILE || - lwsi_state(wsi) == LRS_HEADERS || - lwsi_state(wsi) == LRS_DISCARD_BODY || - lwsi_state(wsi) == LRS_BODY)) { - - if (!wsi->http.ah && lws_header_table_attach(wsi, 0)) { - lwsl_info("%s: wsi %p: ah not available\n", __func__, - wsi); - goto try_pollout; - } - - /* - * We got here because there was specifically POLLIN... - * regardless of our buflist state, we need to get it, - * and either use it, or append to the buflist and use - * buflist head material. - * - * We will not notice a connection close until the buflist is - * exhausted and we tried to do a read of some kind. - */ - - ebuf.token = NULL; - ebuf.len = 0; - buffered = lws_buflist_aware_read(pt, wsi, &ebuf, 0, __func__); - switch (ebuf.len) { - case 0: - lwsl_info("%s: read 0 len a\n", __func__); - wsi->seen_zero_length_recv = 1; - if (lws_change_pollfd(wsi, LWS_POLLIN, 0)) - goto fail; -#if !defined(LWS_WITHOUT_EXTENSIONS) - /* - * autobahn requires us to win the race between close - * and draining the extensions - */ - if (wsi->ws && - (wsi->ws->rx_draining_ext || - wsi->ws->tx_draining_ext)) - goto try_pollout; -#endif - /* - * normally, we respond to close with logically closing - * our side immediately - */ - goto fail; - - case LWS_SSL_CAPABLE_ERROR: - goto fail; - case LWS_SSL_CAPABLE_MORE_SERVICE: - goto try_pollout; - } - - /* just ignore incoming if waiting for close */ - if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) { - lwsl_notice("%s: just ignoring\n", __func__); - goto try_pollout; - } - - if (lwsi_state(wsi) == LRS_ISSUING_FILE) { - // lwsl_notice("stashing: wsi %p: bd %d\n", wsi, buffered); - if (lws_buflist_aware_finished_consuming(wsi, &ebuf, 0, - buffered, __func__)) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - - goto try_pollout; - } - - /* - * Otherwise give it to whoever wants it according to the - * connection state - */ -#if defined(LWS_ROLE_H2) - if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY) - n = lws_read_h2(wsi, ebuf.token, ebuf.len); - else -#endif - n = lws_read_h1(wsi, ebuf.token, ebuf.len); - if (n < 0) /* we closed wsi */ - return LWS_HPI_RET_WSI_ALREADY_DIED; - - // lwsl_notice("%s: consumed %d\n", __func__, n); - - if (lws_buflist_aware_finished_consuming(wsi, &ebuf, n, - buffered, __func__)) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - - /* - * during the parsing our role changed to something non-http, - * so the ah has no further meaning - */ - - if (wsi->http.ah && - !lwsi_role_h1(wsi) && - !lwsi_role_h2(wsi) && - !lwsi_role_cgi(wsi)) - lws_header_table_detach(wsi, 0); - - /* - * He may have used up the writability above, if we will defer - * POLLOUT processing in favour of POLLIN, note it - */ - - if (pollfd->revents & LWS_POLLOUT) - wsi->favoured_pollin = 1; - - return LWS_HPI_RET_HANDLED; - } - - /* - * He may have used up the writability above, if we will defer POLLOUT - * processing in favour of POLLIN, note it - */ - - if (pollfd->revents & LWS_POLLOUT) - wsi->favoured_pollin = 1; - -try_pollout: - - /* this handles POLLOUT for http serving fragments */ - - if (!(pollfd->revents & LWS_POLLOUT)) - return LWS_HPI_RET_HANDLED; - - /* one shot */ - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { - lwsl_notice("%s a\n", __func__); - goto fail; - } - - /* clear back-to-back write detection */ - wsi->could_have_pending = 0; - - if (lwsi_state(wsi) == LRS_DEFERRING_ACTION) { - lwsl_debug("%s: LRS_DEFERRING_ACTION now writable\n", __func__); - - lwsi_set_state(wsi, LRS_ESTABLISHED); - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { - lwsl_info("failed at set pollfd\n"); - goto fail; - } - } - - if (!wsi->hdr_parsing_completed) - return LWS_HPI_RET_HANDLED; - - if (lwsi_state(wsi) != LRS_ISSUING_FILE) { - - if (lws_has_buffered_out(wsi)) { - //lwsl_notice("%s: completing partial\n", __func__); - if (lws_issue_raw(wsi, NULL, 0) < 0) { - lwsl_info("%s signalling to close\n", __func__); - goto fail; - } - return LWS_HPI_RET_HANDLED; - } - - lws_stats_bump(pt, LWSSTATS_C_WRITEABLE_CB, 1); -#if defined(LWS_WITH_STATS) - if (wsi->active_writable_req_us) { - uint64_t ul = lws_now_usecs() - - wsi->active_writable_req_us; - - lws_stats_bump(pt, LWSSTATS_US_WRITABLE_DELAY_AVG, ul); - lws_stats_max(pt, - LWSSTATS_US_WORST_WRITABLE_DELAY, ul); - wsi->active_writable_req_us = 0; - } -#endif - - n = user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_HTTP_WRITEABLE, - wsi->user_space, NULL, 0); - if (n < 0) { - lwsl_info("writeable_fail\n"); - goto fail; - } - - return LWS_HPI_RET_HANDLED; - } - -#if defined(LWS_WITH_FILE_OPS) - - /* >0 == completion, <0 == error - * - * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when - * it's done. That's the case even if we just completed the - * send, so wait for that. - */ - n = lws_serve_http_file_fragment(wsi); - if (n < 0) - goto fail; -#endif - - return LWS_HPI_RET_HANDLED; - - -fail: - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "server socket svc fail"); - - return LWS_HPI_RET_WSI_ALREADY_DIED; -} -#endif - -static int -rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_pollfd *pollfd) -{ - if (lwsi_state(wsi) == LRS_IDLING) { - uint8_t buf[1]; - int rlen; - - /* - * h1 staggered spins here in IDLING if we don't close it. - * It shows POLLIN but the tls connection returns ERROR if - * you try to read it. - */ - - // lwsl_notice("%s: %p: wsistate 0x%x %s, revents 0x%x\n", - // __func__, wsi, wsi->wsistate, wsi->role_ops->name, - // pollfd->revents); - - rlen = lws_ssl_capable_read(wsi, buf, sizeof(buf)); - if (rlen == LWS_SSL_CAPABLE_ERROR) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - -#ifdef LWS_WITH_CGI - if (wsi->http.cgi && (pollfd->revents & LWS_POLLOUT)) { - if (lws_handle_POLLOUT_event(wsi, pollfd)) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - - return LWS_HPI_RET_HANDLED; - } -#endif - -#if 0 - - /* - * !!! lws_serve_http_file_fragment() seems to duplicate most of - * lws_handle_POLLOUT_event() in its own loop... - */ - lwsl_debug("%s: %d %d\n", __func__, (pollfd->revents & LWS_POLLOUT), - lwsi_state_can_handle_POLLOUT(wsi)); - - 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); - /* the write failed... it's had it */ - wsi->socket_is_permanently_unusable = 1; - - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } -#endif - - - /* Priority 2: pre- compression transform */ - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - if (wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more) { - enum lws_write_protocol wp = LWS_WRITE_HTTP; - - lwsl_info("%s: completing comp partial (buflist_comp %p, may %d)\n", - __func__, wsi->http.comp_ctx.buflist_comp, - wsi->http.comp_ctx.may_have_more - ); - - if (wsi->role_ops->write_role_protocol(wsi, NULL, 0, &wp) < 0) { - lwsl_info("%s signalling to close\n", __func__); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - lws_callback_on_writable(wsi); - - if (!wsi->http.comp_ctx.buflist_comp && - !wsi->http.comp_ctx.may_have_more && - wsi->http.deferred_transaction_completed) { - wsi->http.deferred_transaction_completed = 0; - if (lws_http_transaction_completed(wsi)) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - return LWS_HPI_RET_HANDLED; - } -#endif - - if (lws_is_flowcontrolled(wsi)) - /* We cannot deal with any kind of new RX because we are - * RX-flowcontrolled. - */ - return LWS_HPI_RET_HANDLED; - -#if defined(LWS_WITH_SERVER) - if (!lwsi_role_client(wsi)) { - int n; - - lwsl_debug("%s: %p: wsistate 0x%x\n", __func__, wsi, - (int)wsi->wsistate); - n = lws_h1_server_socket_service(wsi, pollfd); - if (n != LWS_HPI_RET_HANDLED) - return n; - if (lwsi_state(wsi) != LRS_SSL_INIT) - if (lws_server_socket_service_ssl(wsi, - LWS_SOCK_INVALID)) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - - return LWS_HPI_RET_HANDLED; - } -#endif - -#if defined(LWS_WITH_CLIENT) - if ((pollfd->revents & LWS_POLLIN) && - wsi->hdr_parsing_completed && !wsi->told_user_closed) { - - /* - * In SSL mode we get POLLIN notification about - * encrypted data in. - * - * But that is not necessarily related to decrypted - * data out becoming available; in may need to perform - * other in or out before that happens. - * - * simply mark ourselves as having readable data - * and turn off our POLLIN - */ - wsi->client_rx_avail = 1; - if (lws_change_pollfd(wsi, LWS_POLLIN, 0)) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - - //lwsl_notice("calling back %s\n", wsi->protocol->name); - - /* let user code know, he'll usually ask for writeable - * callback and drain / re-enable it there - */ - if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_RECEIVE_CLIENT_HTTP, - wsi->user_space, NULL, 0)) { - lwsl_info("RECEIVE_CLIENT_HTTP closed it\n"); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - return LWS_HPI_RET_HANDLED; - } -#endif - -// if (lwsi_state(wsi) == LRS_ESTABLISHED) -// return LWS_HPI_RET_HANDLED; - -#if defined(LWS_WITH_CLIENT) - if ((pollfd->revents & LWS_POLLOUT) && - lws_handle_POLLOUT_event(wsi, pollfd)) { - lwsl_debug("POLLOUT event closed it\n"); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - if (lws_client_socket_service(wsi, pollfd)) - return LWS_HPI_RET_WSI_ALREADY_DIED; -#endif - - return LWS_HPI_RET_HANDLED; -} - -static int -rops_handle_POLLOUT_h1(struct lws *wsi) -{ - - if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY) { -#if defined(LWS_WITH_HTTP_PROXY) - if (wsi->http.proxy_clientside) { - unsigned char *buf, prebuf[LWS_PRE + 1024]; - size_t len = lws_buflist_next_segment_len( - &wsi->parent->http.buflist_post_body, &buf); - int n; - - if (len > sizeof(prebuf) - LWS_PRE) - len = sizeof(prebuf) - LWS_PRE; - - if (len) { - - memcpy(prebuf + LWS_PRE, buf, len); - - lwsl_debug("%s: %p: proxying body %d %d %d %d %d\n", - __func__, wsi, (int)len, - (int)wsi->http.tx_content_length, - (int)wsi->http.tx_content_remain, - (int)wsi->http.rx_content_length, - (int)wsi->http.rx_content_remain - ); - - n = lws_write(wsi, prebuf + LWS_PRE, len, LWS_WRITE_HTTP); - if (n < 0) { - lwsl_err("%s: PROXY_BODY: write %d failed\n", - __func__, (int)len); - return LWS_HP_RET_BAIL_DIE; - } - - lws_buflist_use_segment(&wsi->parent->http.buflist_post_body, len); - } - - if (wsi->parent->http.buflist_post_body) - lws_callback_on_writable(wsi); - else { -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - /* prepare ourselves to do the parsing */ - wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART; - wsi->http.ah->lextable_pos = 0; -#if defined(LWS_WITH_CUSTOM_HEADERS) - wsi->http.ah->unk_pos = 0; -#endif -#endif - lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY); - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, - wsi->context->timeout_secs); - } - } -#endif - return LWS_HP_RET_USER_SERVICE; - } - - if (lwsi_role_client(wsi)) - return LWS_HP_RET_USER_SERVICE; - - return LWS_HP_RET_BAIL_OK; -} - -static int -rops_write_role_protocol_h1(struct lws *wsi, unsigned char *buf, size_t len, - enum lws_write_protocol *wp) -{ - size_t olen = len; - int n; - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - if (wsi->http.lcs && (((*wp) & 0x1f) == LWS_WRITE_HTTP_FINAL || - ((*wp) & 0x1f) == LWS_WRITE_HTTP)) { - unsigned char mtubuf[1500 + LWS_PRE + - LWS_HTTP_CHUNK_HDR_MAX_SIZE + - LWS_HTTP_CHUNK_TRL_MAX_SIZE], - *out = mtubuf + LWS_PRE + - LWS_HTTP_CHUNK_HDR_MAX_SIZE; - size_t o = sizeof(mtubuf) - LWS_PRE - - LWS_HTTP_CHUNK_HDR_MAX_SIZE - - LWS_HTTP_CHUNK_TRL_MAX_SIZE; - - n = lws_http_compression_transform(wsi, buf, len, wp, &out, &o); - if (n) - return n; - - lwsl_info("%s: %p: transformed %d bytes to %d " - "(wp 0x%x, more %d)\n", __func__, wsi, (int)len, - (int)o, (int)*wp, wsi->http.comp_ctx.may_have_more); - - if (!o) - return olen; - - if (wsi->http.comp_ctx.chunking) { - char c[LWS_HTTP_CHUNK_HDR_MAX_SIZE + 2]; - /* - * this only needs dealing with on http/1.1 to allow - * pipelining - */ - n = lws_snprintf(c, sizeof(c), "%X\x0d\x0a", (int)o); - lwsl_info("%s: chunk (%d) %s", __func__, (int)o, c); - out -= n; - o += n; - memcpy(out, c, n); - out[o++] = '\x0d'; - out[o++] = '\x0a'; - - if (((*wp) & 0x1f) == LWS_WRITE_HTTP_FINAL) { - lwsl_info("%s: final chunk\n", __func__); - out[o++] = '0'; - out[o++] = '\x0d'; - out[o++] = '\x0a'; - out[o++] = '\x0d'; - out[o++] = '\x0a'; - } - } - - buf = out; - len = o; - } -#endif - - n = lws_issue_raw(wsi, (unsigned char *)buf, len); - if (n < 0) - return n; - - /* hide there may have been compression */ - - return (int)olen; -} - -static int -rops_alpn_negotiated_h1(struct lws *wsi, const char *alpn) -{ - lwsl_debug("%s: client %d\n", __func__, lwsi_role_client(wsi)); -#if defined(LWS_WITH_CLIENT) - if (lwsi_role_client(wsi)) { - /* - * If alpn asserts it is http/1.1, server support for KA is - * mandatory. - * - * Knowing this lets us proceed with sending pipelined headers - * before we received the first response headers. - */ - wsi->keepalive_active = 1; - } -#endif - - return 0; -} - -static int -rops_destroy_role_h1(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct allocated_headers *ah; - - /* we may not have an ah, but may be on the waiting list... */ - lwsl_info("%s: ah det due to close\n", __func__); - __lws_header_table_detach(wsi, 0); - - ah = pt->http.ah_list; - - while (ah) { - if (ah->in_use && ah->wsi == wsi) { - lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi); - ah->in_use = 0; - ah->wsi = NULL; - pt->http.ah_count_in_use--; - break; - } - ah = ah->next; - } - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - lws_http_compression_destroy(wsi); -#endif - -#ifdef LWS_ROLE_WS - lws_free_set_NULL(wsi->ws); -#endif - return 0; -} - -#if defined(LWS_WITH_SERVER) - -static int -rops_adoption_bind_h1(struct lws *wsi, int type, const char *vh_prot_name) -{ - if (!(type & LWS_ADOPT_HTTP)) - return 0; /* no match */ - - if (type & _LWS_ADOPT_FINISH && !lwsi_role_http(wsi)) - return 0; - - if (type & _LWS_ADOPT_FINISH) { - if (!lws_header_table_attach(wsi, 0)) - lwsl_debug("Attached ah immediately\n"); - else - lwsl_info("%s: waiting for ah\n", __func__); - - return 1; - } - - lws_role_transition(wsi, LWSIFR_SERVER, (type & LWS_ADOPT_ALLOW_SSL) ? - LRS_SSL_INIT : LRS_HEADERS, &role_ops_h1); - - /* - * We have to bind to h1 as a default even when we're actually going to - * replace it as an h2 bind later. So don't take this seriously if the - * default is disabled (ws upgrade caees properly about it) - */ - - if (!vh_prot_name && wsi->vhost->default_protocol_index < - wsi->vhost->count_protocols) - wsi->protocol = &wsi->vhost->protocols[ - wsi->vhost->default_protocol_index]; - else - wsi->protocol = &wsi->vhost->protocols[0]; - - /* the transport is accepted... give him time to negotiate */ - lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, - wsi->context->timeout_secs); - - return 1; /* bound */ -} - -#endif - -#if defined(LWS_WITH_CLIENT) - -static const char * const http_methods[] = { - "GET", "POST", "OPTIONS", "HEAD", "PUT", "PATCH", "DELETE", "CONNECT" -}; - -static int -rops_client_bind_h1(struct lws *wsi, const struct lws_client_connect_info *i) -{ - int n; - - if (!i) { - /* we are finalizing an already-selected role */ - - /* - * If we stay in http, assuming there wasn't already-set - * external user_space, since we know our initial protocol - * we can assign the user space now, otherwise do it after the - * ws subprotocol negotiated - */ - if (!wsi->user_space && wsi->stash->cis[CIS_METHOD]) - if (lws_ensure_user_space(wsi)) - return 1; - - /* - * For ws, default to http/1.1 only. If i->alpn had been set - * though, defer to whatever he has set in there (eg, "h2"). - * - * The problem is he has to commit to h2 before he can find - * out if the server has the SETTINGS for ws-over-h2 enabled; - * if not then ws is not possible on that connection. So we - * only try h2 if he assertively said to use h2 alpn, otherwise - * ws implies alpn restriction to h1. - */ - if (!wsi->stash->cis[CIS_METHOD] && !wsi->stash->cis[CIS_ALPN]) - wsi->stash->cis[CIS_ALPN] = "http/1.1"; - - /* 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 (lws_header_table_attach(wsi, 0) -#if defined(LWS_WITH_CLIENT) - < 0) - /* - * if we failed here, the connection is already closed - * and freed. - */ - return -1; -#else - ) - return 0; -#endif - - return 0; - } - - /* - * Clients that want to be h1, h2, or ws all start out as h1 - * (we don't yet know if the server supports h2 or ws), unless their - * alpn is only "h2" - */ - -// if (i->alpn && !strcmp(i->alpn, "h2")) -// return 0; /* we are h1, he only wants h2 */ - - if (!i->method) { /* websockets */ -#if defined(LWS_ROLE_WS) - if (lws_create_client_ws_object(i, wsi)) - goto fail_wsi; -#else - lwsl_err("%s: ws role not configured\n", __func__); - - goto fail_wsi; -#endif - goto bind_h1; - } - - /* if a recognized http method, bind to it */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(http_methods); n++) - if (!strcmp(i->method, http_methods[n])) - goto bind_h1; - - /* other roles may bind to it */ - - return 0; /* no match */ - -bind_h1: - /* assert the mode and union status (hdr) clearly */ - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1); - - return 1; /* matched */ - -fail_wsi: - return -1; -} -#endif - -#if 0 -static int -rops_perform_user_POLLOUT_h1(struct lws *wsi) -{ - volatile struct lws *vwsi = (volatile struct lws *)wsi; - int n; - - /* priority 1: post compression-transform buffered output */ - - if (lws_has_buffered_out(wsi)) { - lwsl_debug("%s: completing partial\n", __func__); - if (lws_issue_raw(wsi, NULL, 0) < 0) { - lwsl_info("%s signalling to close\n", __func__); - return -1; - } - n = 0; - vwsi->leave_pollout_active = 1; - goto cleanup; - } - - /* priority 2: pre compression-transform buffered output */ - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - if (wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more) { - enum lws_write_protocol wp = LWS_WRITE_HTTP; - - lwsl_info("%s: completing comp partial" - "(buflist_comp %p, may %d)\n", - __func__, wsi->http.comp_ctx.buflist_comp, - wsi->http.comp_ctx.may_have_more); - - if (rops_write_role_protocol_h1(wsi, NULL, 0, &wp) < 0) { - lwsl_info("%s signalling to close\n", __func__); - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "comp write fail"); - } - n = 0; - vwsi->leave_pollout_active = 1; - goto cleanup; - } -#endif - - /* priority 3: if no buffered out and waiting for that... */ - - if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) { - wsi->socket_is_permanently_unusable = 1; - return -1; - } - - /* priority 4: user writeable callback */ - - vwsi = (volatile struct lws *)wsi; - vwsi->leave_pollout_active = 0; - - n = lws_callback_as_writeable(wsi); - -cleanup: - vwsi->handling_pollout = 0; - - if (vwsi->leave_pollout_active) - lws_change_pollfd(wsi, 0, LWS_POLLOUT); - - return n; -} -#endif - -static int -rops_close_kill_connection_h1(struct lws *wsi, enum lws_close_status reason) -{ -#if defined(LWS_WITH_HTTP_PROXY) - if (!wsi->http.proxy_clientside) - return 0; - - wsi->http.proxy_clientside = 0; - - if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_COMPLETED_CLIENT_HTTP, - wsi->user_space, NULL, 0)) - return 0; -#endif - return 0; -} - -int -rops_pt_init_destroy_h1(struct lws_context *context, - const struct lws_context_creation_info *info, - struct lws_context_per_thread *pt, int destroy) -{ - /* - * We only want to do this once... we will do it if no h2 support - * otherwise let h2 ops do it. - */ -#if !defined(LWS_ROLE_H2) && defined(LWS_WITH_SERVER) - if (!destroy) { - - pt->sul_ah_lifecheck.cb = lws_sul_http_ah_lifecheck; - - __lws_sul_insert(&pt->pt_sul_owner, &pt->sul_ah_lifecheck, - 30 * LWS_US_PER_SEC); - } else - lws_dll2_remove(&pt->sul_ah_lifecheck.list); -#endif - - return 0; -} - -const struct lws_role_ops role_ops_h1 = { - /* role name */ "h1", - /* alpn id */ "http/1.1", - /* check_upgrades */ NULL, - /* pt_init_destroy */ rops_pt_init_destroy_h1, - /* init_vhost */ NULL, - /* destroy_vhost */ NULL, - /* service_flag_pending */ NULL, - /* handle_POLLIN */ rops_handle_POLLIN_h1, - /* handle_POLLOUT */ rops_handle_POLLOUT_h1, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ NULL, - /* tx_credit */ NULL, - /* write_role_protocol */ rops_write_role_protocol_h1, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ rops_alpn_negotiated_h1, - /* close_via_role_protocol */ NULL, - /* close_role */ NULL, - /* close_kill_connection */ rops_close_kill_connection_h1, - /* destroy_role */ rops_destroy_role_h1, -#if defined(LWS_WITH_SERVER) - /* adoption_bind */ rops_adoption_bind_h1, -#else - NULL, -#endif -#if defined(LWS_WITH_CLIENT) - /* client_bind */ rops_client_bind_h1, -#else - NULL, -#endif - /* issue_keepalive */ NULL, - /* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, - LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED }, - /* rx_cb clnt, srv */ { LWS_CALLBACK_RECEIVE_CLIENT_HTTP, - 0 /* may be POST, etc */ }, - /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE, - LWS_CALLBACK_HTTP_WRITEABLE }, - /* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP, - LWS_CALLBACK_CLOSED_HTTP }, - /* protocol_bind cb c, srv */ { LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL, - LWS_CALLBACK_HTTP_BIND_PROTOCOL }, - /* protocol_unbind cb c, srv */ { LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL, - LWS_CALLBACK_HTTP_DROP_PROTOCOL }, - /* file_handle */ 0, -}; diff -Nru libwebsockets-4.0.20/lib/roles/h1/private-lib-roles-h1.h libwebsockets-2.4.2/lib/roles/h1/private-lib-roles-h1.h --- libwebsockets-4.0.20/lib/roles/h1/private-lib-roles-h1.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/h1/private-lib-roles-h1.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - */ - -extern const struct lws_role_ops role_ops_h1; -#define lwsi_role_h1(wsi) (wsi->role_ops == &role_ops_h1) diff -Nru libwebsockets-4.0.20/lib/roles/h2/hpack.c libwebsockets-2.4.2/lib/roles/h2/hpack.c --- libwebsockets-4.0.20/lib/roles/h2/hpack.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/h2/hpack.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1462 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -/* - * Official static header table for HPACK - * +-------+-----------------------------+---------------+ - | 1 | :authority | | - | 2 | :method | GET | - | 3 | :method | POST | - | 4 | :path | / | - | 5 | :path | /index.html | - | 6 | :scheme | http | - | 7 | :scheme | https | - | 8 | :status | 200 | - | 9 | :status | 204 | - | 10 | :status | 206 | - | 11 | :status | 304 | - | 12 | :status | 400 | - | 13 | :status | 404 | - | 14 | :status | 500 | - | 15 | accept-charset | | - | 16 | accept-encoding | gzip, deflate | - | 17 | accept-language | | - | 18 | accept-ranges | | - | 19 | accept | | - | 20 | access-control-allow-origin | | - | 21 | age | | - | 22 | allow | | - | 23 | authorization | | - | 24 | cache-control | | - | 25 | content-disposition | | - | 26 | content-encoding | | - | 27 | content-language | | - | 28 | content-length | | - | 29 | content-location | | - | 30 | content-range | | - | 31 | content-type | | - | 32 | cookie | | - | 33 | date | | - | 34 | etag | | - | 35 | expect | | - | 36 | expires | | - | 37 | from | | - | 38 | host | | - | 39 | if-match | | - | 40 | if-modified-since | | - | 41 | if-none-match | | - | 42 | if-range | | - | 43 | if-unmodified-since | | - | 44 | last-modified | | - | 45 | link | | - | 46 | location | | - | 47 | max-forwards | | - | 48 | proxy-authenticate | | - | 49 | proxy-authorization | | - | 50 | range | | - | 51 | referer | | - | 52 | refresh | | - | 53 | retry-after | | - | 54 | server | | - | 55 | set-cookie | | - | 56 | strict-transport-security | | - | 57 | transfer-encoding | | - | 58 | user-agent | | - | 59 | vary | | - | 60 | via | | - | 61 | www-authenticate | | - +-------+-----------------------------+---------------+ -*/ - -static const uint8_t static_hdr_len[62] = { - 0, /* starts at 1 */ - 10, 7, 7, 5, 5, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 14, 15, 15, 13, 6, 27, - 3, 5, 13, 13, 19, 16, 16, 14, 16, 13, - 12, 6, 4, 4, 6, 7, 4, 4, 8, 17, - 13, 8, 19, 13, 4, 8, 12, 18, 19, 5, - 7, 7, 11, 6, 10, 25, 17, 10, 4, 3, - 16 -}; - -static const unsigned char static_token[] = { - 0, - WSI_TOKEN_HTTP_COLON_AUTHORITY, - WSI_TOKEN_HTTP_COLON_METHOD, - WSI_TOKEN_HTTP_COLON_METHOD, - WSI_TOKEN_HTTP_COLON_PATH, - WSI_TOKEN_HTTP_COLON_PATH, - WSI_TOKEN_HTTP_COLON_SCHEME, - WSI_TOKEN_HTTP_COLON_SCHEME, - WSI_TOKEN_HTTP_COLON_STATUS, - WSI_TOKEN_HTTP_COLON_STATUS, - WSI_TOKEN_HTTP_COLON_STATUS, - WSI_TOKEN_HTTP_COLON_STATUS, - WSI_TOKEN_HTTP_COLON_STATUS, - WSI_TOKEN_HTTP_COLON_STATUS, - WSI_TOKEN_HTTP_COLON_STATUS, - WSI_TOKEN_HTTP_ACCEPT_CHARSET, - WSI_TOKEN_HTTP_ACCEPT_ENCODING, - WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, - WSI_TOKEN_HTTP_ACCEPT_RANGES, - WSI_TOKEN_HTTP_ACCEPT, - WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN, - WSI_TOKEN_HTTP_AGE, - WSI_TOKEN_HTTP_ALLOW, - WSI_TOKEN_HTTP_AUTHORIZATION, - WSI_TOKEN_HTTP_CACHE_CONTROL, - WSI_TOKEN_HTTP_CONTENT_DISPOSITION, - WSI_TOKEN_HTTP_CONTENT_ENCODING, - WSI_TOKEN_HTTP_CONTENT_LANGUAGE, - WSI_TOKEN_HTTP_CONTENT_LENGTH, - WSI_TOKEN_HTTP_CONTENT_LOCATION, - WSI_TOKEN_HTTP_CONTENT_RANGE, - WSI_TOKEN_HTTP_CONTENT_TYPE, - WSI_TOKEN_HTTP_COOKIE, - WSI_TOKEN_HTTP_DATE, - WSI_TOKEN_HTTP_ETAG, - WSI_TOKEN_HTTP_EXPECT, - WSI_TOKEN_HTTP_EXPIRES, - WSI_TOKEN_HTTP_FROM, - WSI_TOKEN_HOST, - WSI_TOKEN_HTTP_IF_MATCH, - WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, - WSI_TOKEN_HTTP_IF_NONE_MATCH, - WSI_TOKEN_HTTP_IF_RANGE, - WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE, - WSI_TOKEN_HTTP_LAST_MODIFIED, - WSI_TOKEN_HTTP_LINK, - WSI_TOKEN_HTTP_LOCATION, - WSI_TOKEN_HTTP_MAX_FORWARDS, - WSI_TOKEN_HTTP_PROXY_AUTHENTICATE, - WSI_TOKEN_HTTP_PROXY_AUTHORIZATION, - WSI_TOKEN_HTTP_RANGE, - WSI_TOKEN_HTTP_REFERER, - WSI_TOKEN_HTTP_REFRESH, - WSI_TOKEN_HTTP_RETRY_AFTER, - WSI_TOKEN_HTTP_SERVER, - WSI_TOKEN_HTTP_SET_COOKIE, - WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY, - WSI_TOKEN_HTTP_TRANSFER_ENCODING, - WSI_TOKEN_HTTP_USER_AGENT, - WSI_TOKEN_HTTP_VARY, - WSI_TOKEN_HTTP_VIA, - WSI_TOKEN_HTTP_WWW_AUTHENTICATE, -}; - -/* some of the entries imply values as well as header names */ - -static const char * const http2_canned[] = { - "", - "", - "GET", - "POST", - "/", - "/index.html", - "http", - "https", - "200", - "204", - "206", - "304", - "400", - "404", - "500", - "", - "gzip, deflate" -}; - -/* see minihuf.c */ - -#include "huftable.h" - -static int huftable_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); -} - -static int lws_frag_start(struct lws *wsi, int hdr_token_idx) -{ - struct allocated_headers *ah = wsi->http.ah; - - if (!ah) { - lwsl_notice("%s: no ah\n", __func__); - return 1; - } - - ah->hdr_token_idx = -1; - - lwsl_header("%s: token %d ah->pos = %d, ah->nfrag = %d\n", - __func__, hdr_token_idx, ah->pos, ah->nfrag); - - if (!hdr_token_idx) { - lwsl_err("%s: zero hdr_token_idx\n", __func__); - return 1; - } - - if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frag_index)) { - lwsl_err("%s: frag index %d too big\n", __func__, ah->nfrag); - return 1; - } - - if ((hdr_token_idx == WSI_TOKEN_HTTP_COLON_AUTHORITY || - hdr_token_idx == WSI_TOKEN_HTTP_COLON_METHOD || - hdr_token_idx == WSI_TOKEN_HTTP_COLON_PATH || - hdr_token_idx == WSI_TOKEN_COLON_PROTOCOL || - hdr_token_idx == WSI_TOKEN_HTTP_COLON_SCHEME) && - ah->frag_index[hdr_token_idx]) { - if (!(ah->frags[ah->frag_index[hdr_token_idx]].flags & 1)) { - lws_h2_goaway(lws_get_network_wsi(wsi), - H2_ERR_PROTOCOL_ERROR, - "Duplicated pseudoheader"); - return 1; - } - } - - if (ah->nfrag == 0) - ah->nfrag = 1; - - ah->frags[ah->nfrag].offset = ah->pos; - ah->frags[ah->nfrag].len = 0; - ah->frags[ah->nfrag].nfrag = 0; - ah->frags[ah->nfrag].flags = 2; /* we had reason to set it */ - - ah->hdr_token_idx = hdr_token_idx; - - /* - * Okay, but we could be, eg, the second or subsequent cookie: header - */ - - if (ah->frag_index[hdr_token_idx]) { - int n; - - /* find the last fragment for this header... */ - n = ah->frag_index[hdr_token_idx]; - while (ah->frags[n].nfrag) - n = ah->frags[n].nfrag; - /* and point it to continue in our continuation fragment */ - ah->frags[n].nfrag = ah->nfrag; - - /* cookie continuations need a separator token of ';' */ - if (hdr_token_idx == WSI_TOKEN_HTTP_COOKIE) { - ah->data[ah->pos++] = ';'; - ah->frags[ah->nfrag].len++; - } - } else - ah->frag_index[hdr_token_idx] = ah->nfrag; - - return 0; -} - -static int lws_frag_append(struct lws *wsi, unsigned char c) -{ - struct allocated_headers *ah = wsi->http.ah; - - ah->data[ah->pos++] = c; - ah->frags[ah->nfrag].len++; - - return (int)ah->pos >= wsi->context->max_http_header_data; -} - -static int lws_frag_end(struct lws *wsi) -{ - lwsl_header("%s\n", __func__); - if (lws_frag_append(wsi, 0)) - return 1; - - /* don't account for the terminating NUL in the logical length */ - wsi->http.ah->frags[wsi->http.ah->nfrag].len--; - - wsi->http.ah->nfrag++; - return 0; -} - -int -lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h) -{ - struct allocated_headers *ah = wsi->http.ah; - int n; - - if (!ah) - return 0; - - n = ah->frag_index[h]; - if (!n) - return 0; - - return !!(ah->frags[n].flags & 2); -} - -static void lws_dump_header(struct lws *wsi, int hdr) -{ - char s[200]; - const unsigned char *p; - int len; - - if (hdr == LWS_HPACK_IGNORE_ENTRY) { - lwsl_notice("hdr tok ignored\n"); - return; - } - - (void)p; - - len = lws_hdr_copy(wsi, s, sizeof(s) - 1, hdr); - if (len < 0) - strcpy(s, "(too big to show)"); - else - s[len] = '\0'; - p = lws_token_to_string(hdr); - lwsl_header(" hdr tok %d (%s) = '%s' (len %d)\n", hdr, - p ? (char *)p : (char *)"null", s, len); -} - -/* - * dynamic table - * - * [ 0 .... num_entries - 1] - * - * Starts filling at 0+ - * - * #62 is *most recently entered* - * - * Number of entries is not restricted, but aggregated size of the entry - * payloads is. Unfortunately the way HPACK does this is specific to an - * imagined implementation, and lws implementation is much more efficient - * (ignoring unknown headers and using the lws token index for the header - * name part). - */ - -/* - * returns 0 if dynamic entry (arg and len are filled) - * returns -1 if failure - * returns nonzero token index if actually static token - */ -static int -lws_token_from_index(struct lws *wsi, int index, const char **arg, int *len, - uint32_t *hdr_len) -{ - struct hpack_dynamic_table *dyn; - - if (index == LWS_HPACK_IGNORE_ENTRY) - return LWS_HPACK_IGNORE_ENTRY; - - /* dynamic table only belongs to network wsi */ - wsi = lws_get_network_wsi(wsi); - if (!wsi->h2.h2n) - return -1; - - dyn = &wsi->h2.h2n->hpack_dyn_table; - - if (index < 0) - return -1; - - if (index < (int)LWS_ARRAY_SIZE(static_token)) { - if (arg && index < (int)LWS_ARRAY_SIZE(http2_canned)) { - *arg = http2_canned[index]; - *len = (int)strlen(http2_canned[index]); - } - if (hdr_len) - *hdr_len = static_hdr_len[index]; - - return static_token[index]; - } - - if (!dyn) { - lwsl_notice("no dynamic table\n"); - return -1; - } - - if (index >= (int)LWS_ARRAY_SIZE(static_token) + dyn->used_entries) { - lwsl_info(" %s: adjusted index %d >= %d\n", __func__, index, - (int)LWS_ARRAY_SIZE(static_token) + dyn->used_entries); - lws_h2_goaway(wsi, H2_ERR_COMPRESSION_ERROR, - "index out of range"); - return -1; - } - - index -= (int)LWS_ARRAY_SIZE(static_token); - index = (dyn->pos - 1 - index) % dyn->num_entries; - if (index < 0) - index += dyn->num_entries; - - lwsl_header("%s: dyn index %d, tok %d\n", __func__, index, - dyn->entries[index].lws_hdr_idx); - - if (arg && len) { - *arg = dyn->entries[index].value; - *len = dyn->entries[index].value_len; - } - - if (hdr_len) - *hdr_len = dyn->entries[index].hdr_len; - - return dyn->entries[index].lws_hdr_idx; -} - -static int -lws_h2_dynamic_table_dump(struct lws *wsi) -{ -#if 0 - struct lws *nwsi = lws_get_network_wsi(wsi); - struct hpack_dynamic_table *dyn; - int n, m; - const char *p; - - if (!nwsi->h2.h2n) - return 1; - dyn = &nwsi->h2.h2n->hpack_dyn_table; - - lwsl_header("Dump dyn table for nwsi %p (%d / %d members, pos = %d, " - "start index %d, virt used %d / %d)\n", nwsi, - dyn->used_entries, dyn->num_entries, dyn->pos, - (uint32_t)LWS_ARRAY_SIZE(static_token), - dyn->virtual_payload_usage, dyn->virtual_payload_max); - - for (n = 0; n < dyn->used_entries; n++) { - m = (dyn->pos - 1 - n) % dyn->num_entries; - if (m < 0) - m += dyn->num_entries; - if (dyn->entries[m].lws_hdr_idx != LWS_HPACK_IGNORE_ENTRY) - p = (const char *)lws_token_to_string( - dyn->entries[m].lws_hdr_idx); - else - p = "(ignored)"; - lwsl_header(" %3d: tok %s: (len %d) val '%s'\n", - (int)(n + LWS_ARRAY_SIZE(static_token)), p, - dyn->entries[m].hdr_len, dyn->entries[m].value ? - dyn->entries[m].value : "null"); - } -#endif - return 0; -} - -static void -lws_dynamic_free(struct hpack_dynamic_table *dyn, int idx) -{ - lwsl_header("freeing %d for reuse\n", idx); - dyn->virtual_payload_usage -= dyn->entries[idx].value_len + - dyn->entries[idx].hdr_len; - lws_free_set_NULL(dyn->entries[idx].value); - dyn->entries[idx].value = NULL; - dyn->entries[idx].value_len = 0; - dyn->entries[idx].hdr_len = 0; - dyn->entries[idx].lws_hdr_idx = LWS_HPACK_IGNORE_ENTRY; - dyn->used_entries--; -} - -/* - * There are two address spaces, 1) internal ringbuffer and 2) HPACK indexes. - * - * Internal ringbuffer: - * - * The internal ringbuffer wraps as we keep filling it, dyn->pos points to - * the next index to be written. - * - * HPACK indexes: - * - * The last-written entry becomes entry 0, the previously-last-written entry - * becomes entry 1 etc. - */ - -static int -lws_dynamic_token_insert(struct lws *wsi, int hdr_len, - int lws_hdr_index, char *arg, int len) -{ - struct hpack_dynamic_table *dyn; - int new_index; - - /* dynamic table only belongs to network wsi */ - wsi = lws_get_network_wsi(wsi); - if (!wsi->h2.h2n) - return 1; - dyn = &wsi->h2.h2n->hpack_dyn_table; - - if (!dyn->entries) { - lwsl_err("%s: unsized dyn table\n", __func__); - - return 1; - } - lws_h2_dynamic_table_dump(wsi); - - new_index = (dyn->pos) % dyn->num_entries; - if (dyn->num_entries && dyn->used_entries == dyn->num_entries) { - if (dyn->virtual_payload_usage < dyn->virtual_payload_max) - lwsl_err("Dropping header content before limit!\n"); - /* we have to drop the oldest to make space */ - lws_dynamic_free(dyn, new_index); - } - - /* - * evict guys to make room, allowing for some overage. We have to - * take care about getting a single huge header, and evicting - * everything - */ - - while (dyn->virtual_payload_usage && - dyn->used_entries && - dyn->virtual_payload_usage + hdr_len + len > - dyn->virtual_payload_max + 1024) { - int n = (dyn->pos - dyn->used_entries) % dyn->num_entries; - if (n < 0) - n += dyn->num_entries; - lws_dynamic_free(dyn, n); - } - - if (dyn->used_entries < dyn->num_entries) - dyn->used_entries++; - - dyn->entries[new_index].value_len = 0; - - if (lws_hdr_index != LWS_HPACK_IGNORE_ENTRY) { - if (dyn->entries[new_index].value) - lws_free_set_NULL(dyn->entries[new_index].value); - dyn->entries[new_index].value = - lws_malloc(len + 1, "hpack dyn"); - if (!dyn->entries[new_index].value) - return 1; - - memcpy(dyn->entries[new_index].value, arg, len); - dyn->entries[new_index].value[len] = '\0'; - dyn->entries[new_index].value_len = len; - } else - dyn->entries[new_index].value = NULL; - - dyn->entries[new_index].lws_hdr_idx = lws_hdr_index; - dyn->entries[new_index].hdr_len = hdr_len; - - dyn->virtual_payload_usage += hdr_len + len; - - lwsl_info("%s: index %ld: lws_hdr_index 0x%x, hdr len %d, '%s' len %d\n", - __func__, (long)LWS_ARRAY_SIZE(static_token), - lws_hdr_index, hdr_len, dyn->entries[new_index].value ? - dyn->entries[new_index].value : "null", len); - - dyn->pos = (dyn->pos + 1) % dyn->num_entries; - - lws_h2_dynamic_table_dump(wsi); - - return 0; -} - -int -lws_hpack_dynamic_size(struct lws *wsi, int size) -{ - struct hpack_dynamic_table *dyn; - struct hpack_dt_entry *dte; - struct lws *nwsi; - int min, n = 0, m; - - /* - * "size" here is coming from the http/2 SETTING - * SETTINGS_HEADER_TABLE_SIZE. This is a (virtual, in our case) - * linear buffer containing dynamic header names and values... when it - * is full, old entries are evicted. - * - * We encode the header as an lws_hdr_idx, which is all the rest of - * lws cares about; if there is no matching header we store an empty - * entry in the dyn table as a placeholder. - * - * So to make the two systems work together we keep an accounting of - * what we are using to decide when to evict... we must only evict - * things when the remote peer's accounting also makes him feel he - * should evict something. - */ - - nwsi = lws_get_network_wsi(wsi); - if (!nwsi->h2.h2n) - goto bail; - - dyn = &nwsi->h2.h2n->hpack_dyn_table; - lwsl_info("%s: from %d to %d, lim %u\n", __func__, - (int)dyn->num_entries, size, - (unsigned int)nwsi->vhost->h2.set.s[H2SET_HEADER_TABLE_SIZE]); - - if (!size) { - size = dyn->num_entries * 8; - lws_hpack_destroy_dynamic_header(wsi); - } - - if (size > (int)nwsi->vhost->h2.set.s[H2SET_HEADER_TABLE_SIZE]) { - lwsl_info("rejecting hpack dyn size %u vs %u\n", size, - (unsigned int)nwsi->vhost->h2.set.s[H2SET_HEADER_TABLE_SIZE]); - - // this seems necessary to work with some browsers - - if (nwsi->vhost->h2.set.s[H2SET_HEADER_TABLE_SIZE] == 65536 && - size == 65537) { /* h2spec */ - lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, - "Asked for header table bigger than we told"); - goto bail; - } - - size = nwsi->vhost->h2.set.s[H2SET_HEADER_TABLE_SIZE]; - } - - dyn->virtual_payload_max = size; - - size = size / 8; - min = size; - if (min > dyn->used_entries) - min = dyn->used_entries; - - if (size == dyn->num_entries) - return 0; - - if (dyn->num_entries < min) - min = dyn->num_entries; - - // lwsl_notice("dte requested size %d\n", size); - - dte = lws_zalloc(sizeof(*dte) * (size + 1), "dynamic table entries"); - if (!dte) - goto bail; - - while (dyn->virtual_payload_usage && dyn->used_entries && - dyn->virtual_payload_usage > dyn->virtual_payload_max) { - n = (dyn->pos - dyn->used_entries) % dyn->num_entries; - if (n < 0) - n += dyn->num_entries; - lws_dynamic_free(dyn, n); - } - - if (min > dyn->used_entries) - min = dyn->used_entries; - - if (dyn->entries) { - for (n = 0; n < min; n++) { - m = (dyn->pos - dyn->used_entries + n) % - dyn->num_entries; - if (m < 0) - m += dyn->num_entries; - dte[n] = dyn->entries[m]; - } - - lws_free(dyn->entries); - } - - dyn->entries = dte; - dyn->num_entries = size; - dyn->used_entries = min; - if (size) - dyn->pos = min % size; - else - dyn->pos = 0; - - lws_h2_dynamic_table_dump(wsi); - - return 0; - -bail: - lwsl_info("%s: failed to resize to %d\n", __func__, size); - - return 1; -} - -void -lws_hpack_destroy_dynamic_header(struct lws *wsi) -{ - struct hpack_dynamic_table *dyn; - int n; - - if (!wsi->h2.h2n) - return; - - dyn = &wsi->h2.h2n->hpack_dyn_table; - - if (!dyn->entries) - return; - - for (n = 0; n < dyn->num_entries; n++) - if (dyn->entries[n].value) - lws_free_set_NULL(dyn->entries[n].value); - - lws_free_set_NULL(dyn->entries); -} - -static int -lws_hpack_use_idx_hdr(struct lws *wsi, int idx, int known_token) -{ - const char *arg = NULL; - int len = 0; - const char *p = NULL; - int tok = lws_token_from_index(wsi, idx, &arg, &len, NULL); - - if (tok == LWS_HPACK_IGNORE_ENTRY) { - lwsl_header("%s: lws_token says ignore, returning\n", __func__); - return 0; - } - - if (tok == -1) { - lwsl_info("%s: idx %d mapped to tok %d\n", __func__, idx, tok); - return 1; - } - - if (arg) { - /* dynamic result */ - if (known_token > 0) - tok = known_token; - lwsl_header("%s: dyn: idx %d '%s' tok %d\n", __func__, idx, arg, - tok); - } else - lwsl_header("writing indexed hdr %d (tok %d '%s')\n", idx, tok, - lws_token_to_string(tok)); - - if (tok == LWS_HPACK_IGNORE_ENTRY) - return 0; - - if (arg) - p = arg; - - if (idx < (int)LWS_ARRAY_SIZE(http2_canned)) - p = http2_canned[idx]; - - if (lws_frag_start(wsi, tok)) - return 1; - - if (p) - while (*p && len--) - if (lws_frag_append(wsi, *p++)) - return 1; - - if (lws_frag_end(wsi)) - return 1; - - lws_dump_header(wsi, tok); - - return 0; -} - -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x10,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x03,0x00,0x80,0x0f,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x07,0x00,0x00,0x3e,0x00,0x00,0x00,0x80,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x03,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x07,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,0x0e,0x04,0x00,0x00,0x00,0x00,0x00, -}; -#endif - -static int -lws_hpack_handle_pseudo_rules(struct lws *nwsi, struct lws *wsi, int m) -{ - if (m == LWS_HPACK_IGNORE_ENTRY || m == -1) - return 0; - - if (wsi->seen_nonpseudoheader && - (lws_header_implies_psuedoheader_map[m >> 3] & (1 << (m & 7)))) { - - lwsl_info("lws tok %d seems to be a pseudoheader\n", m); - - /* - * it's not legal to see a - * pseudoheader after normal - * headers - */ - lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, - "Pseudoheader after normal hdrs"); - return 1; - } - - if (!(lws_header_implies_psuedoheader_map[m >> 3] & (1 << (m & 7)))) - wsi->seen_nonpseudoheader = 1; - - return 0; -} - -int lws_hpack_interpret(struct lws *wsi, unsigned char c) -{ - struct lws *nwsi = lws_get_network_wsi(wsi); - struct lws_h2_netconn *h2n = nwsi->h2.h2n; - struct allocated_headers *ah = wsi->http.ah; - unsigned int prev; - unsigned char c1; - int n, m, plen; - - if (!h2n) - return -1; - - /* - * HPKT_INDEXED_HDR_7 1xxxxxxx: just "header field" - * HPKT_INDEXED_HDR_6_VALUE_INCR 01xxxxxx: NEW indexed hdr + val - * HPKT_LITERAL_HDR_VALUE_INCR 01000000: NEW literal hdr + val - * HPKT_INDEXED_HDR_4_VALUE 0000xxxx: indexed hdr + val - * HPKT_INDEXED_HDR_4_VALUE_NEVER 0001xxxx: NEVER NEW indexed hdr + val - * HPKT_LITERAL_HDR_VALUE 00000000: literal hdr + val - * HPKT_LITERAL_HDR_VALUE_NEVER 00010000: NEVER NEW literal hdr + val - */ - switch (h2n->hpack) { - - case HPKS_TYPE: - h2n->is_first_header_char = 1; - h2n->huff_pad = 0; - h2n->zero_huff_padding = 0; - h2n->last_action_dyntable_resize = 0; - h2n->ext_count = 0; - h2n->hpack_hdr_len = 0; - h2n->unknown_header = 0; - ah->parser_state = 255; - - if (c & 0x80) { /* 1.... indexed header field only */ - /* just a possibly-extended integer */ - h2n->hpack_type = HPKT_INDEXED_HDR_7; - lwsl_header("HPKT_INDEXED_HDR_7 hdr %d\n", c & 0x7f); - lws_h2_dynamic_table_dump(wsi); - - h2n->hdr_idx = c & 0x7f; - if ((c & 0x7f) == 0x7f) { - h2n->hpack_len = 0; - h2n->hpack_m = 0x7f; - h2n->hpack = HPKS_IDX_EXT; - break; - } - if (!h2n->hdr_idx) { - lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, - "hdr index 0 seen"); - return 1; - } - - m = lws_token_from_index(wsi, h2n->hdr_idx, - NULL, NULL, NULL); - if (lws_hpack_handle_pseudo_rules(nwsi, wsi, m)) - return 1; - - lwsl_header("HPKT_INDEXED_HDR_7: hdr %d\n", c & 0x7f); - if (lws_hpack_use_idx_hdr(wsi, c & 0x7f, -1)) { - lwsl_header("%s: idx hdr wr fail\n", __func__); - return 1; - } - /* stay at same state */ - break; - } - if (c & 0x40) { /* 01.... indexed or literal header incr idx */ - /* - * [possibly-ext hdr idx (6) | new literal hdr name] - * H + possibly-ext value length - * literal value - */ - h2n->hdr_idx = 0; - if (c == 0x40) { /* literal header */ - lwsl_header(" HPKT_LITERAL_HDR_VALUE_INCR\n"); - h2n->hpack_type = HPKT_LITERAL_HDR_VALUE_INCR; - h2n->value = 0; - h2n->hpack_len = 0; - h2n->hpack = HPKS_HLEN; - break; - } - /* indexed header */ - h2n->hpack_type = HPKT_INDEXED_HDR_6_VALUE_INCR; - lwsl_header(" HPKT_INDEXED_HDR_6_VALUE_INCR (hdr %d)\n", - c & 0x3f); - h2n->hdr_idx = c & 0x3f; - if ((c & 0x3f) == 0x3f) { - h2n->hpack_m = 0x3f; - h2n->hpack_len = 0; - h2n->hpack = HPKS_IDX_EXT; - break; - } - - h2n->value = 1; - h2n->hpack = HPKS_HLEN; - if (!h2n->hdr_idx) { - lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, - "hdr index 0 seen"); - return 1; - } - break; - } - switch(c & 0xf0) { - case 0x10: /* literal header never index */ - case 0: /* literal header without indexing */ - /* - * follows 0x40 except 4-bit hdr idx - * and don't add to index - */ - if (c == 0) { /* literal name */ - h2n->hpack_type = HPKT_LITERAL_HDR_VALUE; - lwsl_header(" HPKT_LITERAL_HDR_VALUE\n"); - h2n->hpack = HPKS_HLEN; - h2n->value = 0; - break; - } - if (c == 0x10) { /* literal name NEVER */ - h2n->hpack_type = HPKT_LITERAL_HDR_VALUE_NEVER; - lwsl_header(" HPKT_LITERAL_HDR_VALUE_NEVER\n"); - h2n->hpack = HPKS_HLEN; - h2n->value = 0; - break; - } - lwsl_header("indexed\n"); - /* indexed name */ - if (c & 0x10) { - h2n->hpack_type = HPKT_INDEXED_HDR_4_VALUE_NEVER; - lwsl_header("HPKT_LITERAL_HDR_4_VALUE_NEVER\n"); - } else { - h2n->hpack_type = HPKT_INDEXED_HDR_4_VALUE; - lwsl_header(" HPKT_INDEXED_HDR_4_VALUE\n"); - } - h2n->hdr_idx = 0; - if ((c & 0xf) == 0xf) { - h2n->hpack_len = c & 0xf; - h2n->hpack_m = 0xf; - h2n->hpack_len = 0; - h2n->hpack = HPKS_IDX_EXT; - break; - } - h2n->hdr_idx = c & 0xf; - h2n->value = 1; - h2n->hpack = HPKS_HLEN; - break; - - case 0x20: - case 0x30: /* header table size update */ - /* possibly-extended size value (5) */ - lwsl_header("HPKT_SIZE_5 %x\n", c &0x1f); - h2n->hpack_type = HPKT_SIZE_5; - h2n->hpack_len = c & 0x1f; - if (h2n->hpack_len == 0x1f) { - h2n->hpack_m = 0x1f; - h2n->hpack_len = 0; - h2n->hpack = HPKS_IDX_EXT; - break; - } - h2n->last_action_dyntable_resize = 1; - if (lws_hpack_dynamic_size(wsi, h2n->hpack_len)) - return 1; - break; - } - break; - - case HPKS_IDX_EXT: - h2n->hpack_len = h2n->hpack_len | - ((c & 0x7f) << h2n->ext_count); - h2n->ext_count += 7; - if (c & 0x80) /* extended int not complete yet */ - break; - - /* extended integer done */ - h2n->hpack_len += h2n->hpack_m; - lwsl_header("HPKS_IDX_EXT: hpack_len %u\n", (unsigned int)h2n->hpack_len); - - switch (h2n->hpack_type) { - case HPKT_INDEXED_HDR_7: - if (lws_hpack_use_idx_hdr(wsi, h2n->hpack_len, - h2n->hdr_idx)) { - lwsl_notice("%s: hd7 use fail\n", __func__); - return 1; - } - h2n->hpack = HPKS_TYPE; - break; - - case HPKT_SIZE_5: - h2n->last_action_dyntable_resize = 1; - if (lws_hpack_dynamic_size(wsi, h2n->hpack_len)) - return 1; - h2n->hpack = HPKS_TYPE; - break; - - default: - h2n->hdr_idx = h2n->hpack_len; - if (!h2n->hdr_idx) { - lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, - "extended header index was 0"); - return 1; - } - h2n->value = 1; - h2n->hpack = HPKS_HLEN; - break; - } - break; - - case HPKS_HLEN: /* [ H | 7+ ] */ - h2n->huff = !!(c & 0x80); - h2n->hpack_pos = 0; - h2n->hpack_len = c & 0x7f; - - if (h2n->hpack_len == 0x7f) { - h2n->hpack_m = 0x7f; - h2n->hpack_len = 0; - h2n->ext_count = 0; - h2n->hpack = HPKS_HLEN_EXT; - break; - } - - if (h2n->value && !h2n->hpack_len) { - lwsl_debug("%s: zero-length header data\n", __func__); - h2n->hpack = HPKS_TYPE; - goto fin; - } - -pre_data: - h2n->hpack = HPKS_DATA; - if (!h2n->value || !h2n->hdr_idx) { - ah->parser_state = WSI_TOKEN_NAME_PART; - ah->lextable_pos = 0; - h2n->unknown_header = 0; - break; - } - - if (h2n->hpack_type == HPKT_LITERAL_HDR_VALUE || - h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR || - h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER) { - n = ah->parser_state; - if (n == 255) { - n = -1; - h2n->hdr_idx = -1; - } else - h2n->hdr_idx = 1; - } else { - n = lws_token_from_index(wsi, h2n->hdr_idx, NULL, - NULL, NULL); - lwsl_header(" lws_tok_from_idx(%u) says %d\n", - (unsigned int)h2n->hdr_idx, n); - } - - if (n == LWS_HPACK_IGNORE_ENTRY || n == -1) - h2n->hdr_idx = LWS_HPACK_IGNORE_ENTRY; - - switch (h2n->hpack_type) { - /* - * hpack types with literal headers were parsed by the lws - * header SM... on recognition of a known lws header, it does - * the correct lws_frag_start() for us already. Other types - * (ie, indexed header) need us to do it here. - */ - case HPKT_LITERAL_HDR_VALUE_INCR: - case HPKT_LITERAL_HDR_VALUE: - case HPKT_LITERAL_HDR_VALUE_NEVER: - break; - default: - if (n != -1 && n != LWS_HPACK_IGNORE_ENTRY && - lws_frag_start(wsi, n)) { - lwsl_header("%s: frag start failed\n", - __func__); - return 1; - } - break; - } - break; - - case HPKS_HLEN_EXT: - h2n->hpack_len = h2n->hpack_len | - ((c & 0x7f) << h2n->ext_count); - h2n->ext_count += 7; - if (c & 0x80) /* extended integer not complete yet */ - break; - - h2n->hpack_len += h2n->hpack_m; - goto pre_data; - - case HPKS_DATA: - //lwsl_header(" 0x%02X huff %d\n", c, h2n->huff); - c1 = c; - - for (n = 0; n < 8; n++) { - if (h2n->huff) { - char b = (c >> 7) & 1; - prev = h2n->hpack_pos; - h2n->hpack_pos = huftable_decode( - h2n->hpack_pos, b); - c <<= 1; - if (h2n->hpack_pos == 0xffff) { - lwsl_notice("Huffman err\n"); - return 1; - } - if (!(h2n->hpack_pos & 0x8000)) { - if (!b) - h2n->zero_huff_padding = 1; - h2n->huff_pad++; - continue; - } - c1 = h2n->hpack_pos & 0x7fff; - h2n->hpack_pos = 0; - h2n->huff_pad = 0; - h2n->zero_huff_padding = 0; - - /* EOS |11111111|11111111|11111111|111111 */ - if (!c1 && prev == HUFTABLE_0x100_PREV) { - lws_h2_goaway(nwsi, - H2_ERR_COMPRESSION_ERROR, - "Huffman EOT seen"); - return 1; - } - } else - n = 8; - - if (h2n->value) { /* value */ - - if (h2n->hdr_idx && - h2n->hdr_idx != LWS_HPACK_IGNORE_ENTRY) { - - if (ah->hdr_token_idx == - WSI_TOKEN_HTTP_COLON_PATH) { - - switch (lws_parse_urldecode( - wsi, &c1)) { - case LPUR_CONTINUE: - break; - case LPUR_SWALLOW: - goto swallow; - case LPUR_EXCESSIVE: - case LPUR_FORBID: - lws_h2_goaway(nwsi, - H2_ERR_PROTOCOL_ERROR, - "Evil URI"); - return 1; - - default: - return -1; - } - } - if (lws_frag_append(wsi, c1)) { - lwsl_notice( - "%s: frag app fail\n", - __func__); - return 1; - } - } //else - //lwsl_header("ignoring %c\n", c1); - } else { - /* - * Convert name using existing parser, - * If h2n->unknown_header == 0, result is - * in wsi->parser_state - * using WSI_TOKEN_GET_URI. - * - * If unknown header h2n->unknown_header - * will be set. - */ - h2n->hpack_hdr_len++; - if (h2n->is_first_header_char) { - h2n->is_first_header_char = 0; - h2n->first_hdr_char = c1; - } - lwsl_header("parser: %c\n", c1); - /* uppercase header names illegal */ - if (c1 >= 'A' && c1 <= 'Z') { - lws_h2_goaway(nwsi, - H2_ERR_COMPRESSION_ERROR, - "Uppercase literal hpack hdr"); - return 1; - } - plen = 1; - if (!h2n->unknown_header && - lws_parse(wsi, &c1, &plen)) - h2n->unknown_header = 1; - } -swallow: - (void)n; - } // for n - - if (--h2n->hpack_len) - break; - - /* - * The header (h2n->value = 0) or the payload (h2n->value = 1) - * is complete. - */ - - if (h2n->huff && (h2n->huff_pad > 7 || - (h2n->zero_huff_padding && h2n->huff_pad))) { - lwsl_info("zero_huff_padding: %d huff_pad: %d\n", - h2n->zero_huff_padding, h2n->huff_pad); - lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, - "Huffman padding excessive or wrong"); - return 1; - } -fin: - if (!h2n->value && ( - h2n->hpack_type == HPKT_LITERAL_HDR_VALUE || - h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR || - h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER)) { - h2n->hdr_idx = LWS_HPACK_IGNORE_ENTRY; - lwsl_header("wsi->parser_state: %d\n", - ah->parser_state); - - if (ah->parser_state == WSI_TOKEN_NAME_PART) { - /* h2 headers come without the colon */ - c1 = ':'; - plen = 1; - n = lws_parse(wsi, &c1, &plen); - (void)n; - } - - if (ah->parser_state == WSI_TOKEN_NAME_PART || -#if defined(LWS_WITH_CUSTOM_HEADERS) - ah->parser_state == WSI_TOKEN_UNKNOWN_VALUE_PART || -#endif - ah->parser_state == WSI_TOKEN_SKIPPING) { - h2n->unknown_header = 1; - ah->parser_state = -1; - wsi->seen_nonpseudoheader = 1; - } - } - - n = 8; - - /* we have the header */ - if (!h2n->value) { - h2n->value = 1; - h2n->hpack = HPKS_HLEN; - h2n->huff_pad = 0; - h2n->zero_huff_padding = 0; - h2n->ext_count = 0; - break; - } - - /* - * we have got both the header and value - */ - - m = -1; - switch (h2n->hpack_type) { - /* - * These are the only two that insert to the dyntable - */ - /* NEW indexed hdr with value */ - case HPKT_INDEXED_HDR_6_VALUE_INCR: - /* header length is determined by known index */ - m = lws_token_from_index(wsi, h2n->hdr_idx, NULL, NULL, - &h2n->hpack_hdr_len); - goto add_it; - /* NEW literal hdr with value */ - case HPKT_LITERAL_HDR_VALUE_INCR: - /* - * hdr is a new literal, so length is already in - * h2n->hpack_hdr_len - */ - m = ah->parser_state; - if (h2n->unknown_header || - ah->parser_state == WSI_TOKEN_NAME_PART || - ah->parser_state == WSI_TOKEN_SKIPPING) { - if (h2n->first_hdr_char == ':') { - lwsl_info("HPKT_LITERAL_HDR_VALUE_INCR:" - " end state %d unk hdr %d\n", - ah->parser_state, - h2n->unknown_header); - /* unknown pseudoheaders are illegal */ - lws_h2_goaway(nwsi, - H2_ERR_PROTOCOL_ERROR, - "Unknown pseudoheader"); - return 1; - } - m = LWS_HPACK_IGNORE_ENTRY; - } -add_it: - /* - * mark us as having been set at the time of dynamic - * token insertion. - */ - ah->frags[ah->nfrag].flags |= 1; - - if (lws_dynamic_token_insert(wsi, h2n->hpack_hdr_len, m, - &ah->data[ah->frags[ah->nfrag].offset], - ah->frags[ah->nfrag].len)) { - lwsl_notice("%s: tok_insert fail\n", __func__); - return 1; - } - break; - - default: - break; - } - - if (h2n->hdr_idx != LWS_HPACK_IGNORE_ENTRY && lws_frag_end(wsi)) - return 1; - - if (h2n->hpack_type != HPKT_INDEXED_HDR_6_VALUE_INCR) { - - if (h2n->hpack_type == HPKT_LITERAL_HDR_VALUE || - h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR || - h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER) { - m = ah->parser_state; - if (m == 255) - m = -1; - } else - m = lws_token_from_index(wsi, h2n->hdr_idx, - NULL, NULL, NULL); - } - - if (m != -1 && m != LWS_HPACK_IGNORE_ENTRY) - lws_dump_header(wsi, m); - - if (lws_hpack_handle_pseudo_rules(nwsi, wsi, m)) - return 1; - - h2n->is_first_header_char = 1; - h2n->hpack = HPKS_TYPE; - break; - } - - return 0; -} - - - -static int -lws_h2_num_start(int starting_bits, unsigned long num) -{ - unsigned int mask = (1 << starting_bits) - 1; - - if (num < mask) - return (int)num; - - return mask; -} - -static int -lws_h2_num(int starting_bits, unsigned long num, - unsigned char **p, unsigned char *end) -{ - unsigned int mask = (1 << starting_bits) - 1; - - if (num < mask) - return 0; - - num -= mask; - do { - if (num > 127) - *((*p)++) = 0x80 | (num & 0x7f); - else - *((*p)++) = 0x00 | (num & 0x7f); - if (*p >= end) - return 1; - num >>= 7; - } while (num); - - return 0; -} - -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 len; - - lwsl_header("%s: %p %s:%s (len %d)\n", __func__, *p, name, value, - length); - - len = (int)strlen((char *)name); - if (len) - if (name[len - 1] == ':') - len--; - - if (wsi->mux_substream && !strncmp((const char *)name, - "transfer-encoding", len)) { - lwsl_header("rejecting %s\n", name); - - return 0; - } - - if (end - *p < len + length + 8) - return 1; - - *((*p)++) = 0; /* literal hdr, literal name, */ - - *((*p)++) = 0 | lws_h2_num_start(7, len); /* non-HUF */ - if (lws_h2_num(7, len, p, end)) - return 1; - - /* upper-case header names are verboten in h2, but OK on h1, so - * they're not illegal per se. Silently convert them for h2... */ - - while(len--) - *((*p)++) = tolower((int)*name++); - - *((*p)++) = 0 | lws_h2_num_start(7, length); /* non-HUF */ - if (lws_h2_num(7, length, p, end)) - return 1; - - memcpy(*p, value, length); - *p += length; - - return 0; -} - -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) -{ - const unsigned char *name; - - name = lws_token_to_string(token); - if (!name) - return 1; - - return lws_add_http2_header_by_name(wsi, name, value, length, p, end); -} - -int lws_add_http2_header_status(struct lws *wsi, unsigned int code, - unsigned char **p, unsigned char *end) -{ - unsigned char status[10]; - int n; - - wsi->h2.send_END_STREAM = 0; // !!(code >= 400); - - n = sprintf((char *)status, "%u", code); - if (lws_add_http2_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_STATUS, - status, n, p, end)) - - return 1; - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/roles/h2/http2.c libwebsockets-2.4.2/lib/roles/h2/http2.c --- libwebsockets-4.0.20/lib/roles/h2/http2.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/h2/http2.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2644 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -/* - * bitmap of control messages that are valid to receive for each http2 state - */ - -static const uint16_t http2_rx_validity[] = { - /* LWS_H2S_IDLE */ - (1 << LWS_H2_FRAME_TYPE_SETTINGS) | - (1 << LWS_H2_FRAME_TYPE_PRIORITY) | -// (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE)| /* ignore */ - (1 << LWS_H2_FRAME_TYPE_HEADERS) | - (1 << LWS_H2_FRAME_TYPE_CONTINUATION), - /* LWS_H2S_RESERVED_LOCAL */ - (1 << LWS_H2_FRAME_TYPE_SETTINGS) | - (1 << LWS_H2_FRAME_TYPE_RST_STREAM) | - (1 << LWS_H2_FRAME_TYPE_PRIORITY) | - (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE), - /* LWS_H2S_RESERVED_REMOTE */ - (1 << LWS_H2_FRAME_TYPE_SETTINGS) | - (1 << LWS_H2_FRAME_TYPE_HEADERS) | - (1 << LWS_H2_FRAME_TYPE_CONTINUATION) | - (1 << LWS_H2_FRAME_TYPE_RST_STREAM) | - (1 << LWS_H2_FRAME_TYPE_PRIORITY), - /* LWS_H2S_OPEN */ - (1 << LWS_H2_FRAME_TYPE_DATA) | - (1 << LWS_H2_FRAME_TYPE_HEADERS) | - (1 << LWS_H2_FRAME_TYPE_PRIORITY) | - (1 << LWS_H2_FRAME_TYPE_RST_STREAM) | - (1 << LWS_H2_FRAME_TYPE_SETTINGS) | - (1 << LWS_H2_FRAME_TYPE_PUSH_PROMISE) | - (1 << LWS_H2_FRAME_TYPE_PING) | - (1 << LWS_H2_FRAME_TYPE_GOAWAY) | - (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) | - (1 << LWS_H2_FRAME_TYPE_CONTINUATION), - /* LWS_H2S_HALF_CLOSED_REMOTE */ - (1 << LWS_H2_FRAME_TYPE_SETTINGS) | - (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) | - (1 << LWS_H2_FRAME_TYPE_PRIORITY) | - (1 << LWS_H2_FRAME_TYPE_RST_STREAM), - /* LWS_H2S_HALF_CLOSED_LOCAL */ - (1 << LWS_H2_FRAME_TYPE_DATA) | - (1 << LWS_H2_FRAME_TYPE_HEADERS) | - (1 << LWS_H2_FRAME_TYPE_PRIORITY) | - (1 << LWS_H2_FRAME_TYPE_RST_STREAM) | - (1 << LWS_H2_FRAME_TYPE_SETTINGS) | - (1 << LWS_H2_FRAME_TYPE_PUSH_PROMISE) | - (1 << LWS_H2_FRAME_TYPE_PING) | - (1 << LWS_H2_FRAME_TYPE_GOAWAY) | - (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) | - (1 << LWS_H2_FRAME_TYPE_CONTINUATION), - /* LWS_H2S_CLOSED */ - (1 << LWS_H2_FRAME_TYPE_SETTINGS) | - (1 << LWS_H2_FRAME_TYPE_PRIORITY) | - (1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) | - (1 << LWS_H2_FRAME_TYPE_RST_STREAM), -}; - -static const char *preface = "PRI * HTTP/2.0\x0d\x0a\x0d\x0aSM\x0d\x0a\x0d\x0a"; - -static const char * const h2_state_names[] = { - "LWS_H2S_IDLE", - "LWS_H2S_RESERVED_LOCAL", - "LWS_H2S_RESERVED_REMOTE", - "LWS_H2S_OPEN", - "LWS_H2S_HALF_CLOSED_REMOTE", - "LWS_H2S_HALF_CLOSED_LOCAL", - "LWS_H2S_CLOSED", -}; - -#if 0 -static const char * const h2_setting_names[] = { - "", - "H2SET_HEADER_TABLE_SIZE", - "H2SET_ENABLE_PUSH", - "H2SET_MAX_CONCURRENT_STREAMS", - "H2SET_INITIAL_WINDOW_SIZE", - "H2SET_MAX_FRAME_SIZE", - "H2SET_MAX_HEADER_LIST_SIZE", - "reserved", - "H2SET_ENABLE_CONNECT_PROTOCOL" -}; - -void -lws_h2_dump_settings(struct http2_settings *set) -{ - int n; - - for (n = 1; n < H2SET_COUNT; n++) - lwsl_notice(" %30s: %10d\n", h2_setting_names[n], set->s[n]); -} -#else -void -lws_h2_dump_settings(struct http2_settings *set) -{ -} -#endif - -struct lws_h2_protocol_send * -lws_h2_new_pps(enum lws_h2_protocol_send_type type) -{ - struct lws_h2_protocol_send *pps = lws_malloc(sizeof(*pps), "pps"); - - if (pps) - pps->type = type; - - return pps; -} - -void lws_h2_init(struct lws *wsi) -{ - wsi->h2.h2n->our_set = wsi->vhost->h2.set; - wsi->h2.h2n->peer_set = lws_h2_defaults; -} - -void -lws_h2_state(struct lws *wsi, enum lws_h2_states s) -{ - if (!wsi) - return; - lwsl_info("%s: wsi %p: state %s -> %s\n", __func__, wsi, - h2_state_names[wsi->h2.h2_state], - h2_state_names[s]); - - (void)h2_state_names; - wsi->h2.h2_state = (uint8_t)s; -} - -int -lws_h2_update_peer_txcredit(struct lws *wsi, int sid, int bump) -{ - struct lws *nwsi = lws_get_network_wsi(wsi); - struct lws_h2_protocol_send *pps; - - assert(wsi); - - if (!bump) - return 0; - - if (sid == -1) - sid = wsi->mux.my_sid; - - lwsl_info("%s: sid %d: bump %d -> %d\n", __func__, sid, bump, - (int)wsi->txc.peer_tx_cr_est + bump); - - pps = lws_h2_new_pps(LWS_H2_PPS_UPDATE_WINDOW); - if (!pps) - return 1; - - pps->u.update_window.sid = sid; - pps->u.update_window.credit = bump; - wsi->txc.peer_tx_cr_est += bump; - - lws_wsi_txc_describe(&wsi->txc, __func__, wsi->mux.my_sid); - - lws_pps_schedule(wsi, pps); - - pps = lws_h2_new_pps(LWS_H2_PPS_UPDATE_WINDOW); - if (!pps) - return 1; - - pps->u.update_window.sid = 0; - pps->u.update_window.credit = bump; - nwsi->txc.peer_tx_cr_est += bump; - - lws_wsi_txc_describe(&nwsi->txc, __func__, nwsi->mux.my_sid); - - lws_pps_schedule(nwsi, pps); - - return 0; -} - -int -lws_h2_get_peer_txcredit_estimate(struct lws *wsi) -{ - lws_wsi_txc_describe(&wsi->txc, __func__, wsi->mux.my_sid); - return (int)wsi->txc.peer_tx_cr_est; -} - -static int -lws_h2_update_peer_txcredit_thresh(struct lws *wsi, int sid, int threshold, int bump) -{ - if (wsi->txc.peer_tx_cr_est > threshold) - return 0; - - return lws_h2_update_peer_txcredit(wsi, sid, bump); -} - -struct lws * -lws_wsi_server_new(struct lws_vhost *vh, struct lws *parent_wsi, - unsigned int sid) -{ - struct lws *wsi; - struct lws *nwsi = lws_get_network_wsi(parent_wsi); - struct lws_h2_netconn *h2n = nwsi->h2.h2n; - - /* - * The identifier of a newly established stream MUST be numerically - * greater than all streams that the initiating endpoint has opened or - * reserved. This governs streams that are opened using a HEADERS frame - * and streams that are reserved using PUSH_PROMISE. An endpoint that - * receives an unexpected stream identifier MUST respond with a - * connection error (Section 5.4.1) of type PROTOCOL_ERROR. - */ - if (sid <= h2n->highest_sid_opened) { - lwsl_info("%s: tried to open lower sid %d (%d)\n", __func__, - sid, (int)h2n->highest_sid_opened); - lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, "Bad sid"); - return NULL; - } - - /* no more children allowed by parent */ - if (parent_wsi->mux.child_count + 1 > - parent_wsi->h2.h2n->our_set.s[H2SET_MAX_CONCURRENT_STREAMS]) { - lwsl_notice("reached concurrent stream limit\n"); - return NULL; - } - wsi = lws_create_new_server_wsi(vh, parent_wsi->tsi); - if (!wsi) { - lwsl_notice("new server wsi failed (vh %p)\n", vh); - return NULL; - } - - h2n->highest_sid_opened = sid; - - lws_wsi_mux_insert(wsi, parent_wsi, sid); - if (sid >= h2n->highest_sid) - h2n->highest_sid = sid + 2; - - wsi->mux_substream = 1; - wsi->seen_nonpseudoheader = 0; - - wsi->txc.tx_cr = nwsi->h2.h2n->peer_set.s[H2SET_INITIAL_WINDOW_SIZE]; - wsi->txc.peer_tx_cr_est = - nwsi->h2.h2n->our_set.s[H2SET_INITIAL_WINDOW_SIZE]; - - lwsi_set_state(wsi, LRS_ESTABLISHED); - lwsi_set_role(wsi, lwsi_role(parent_wsi)); - - wsi->protocol = &vh->protocols[0]; - if (lws_ensure_user_space(wsi)) - goto bail1; - -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.h2_subs++; -#endif - - /* get the ball rolling */ - lws_validity_confirmed(wsi); - - lwsl_info("%s: %p new ch %p, sid %d, usersp=%p\n", __func__, - parent_wsi, wsi, sid, wsi->user_space); - - lws_wsi_txc_describe(&wsi->txc, __func__, wsi->mux.my_sid); - lws_wsi_txc_describe(&nwsi->txc, __func__, 0); - - return wsi; - -bail1: - /* undo the insert */ - parent_wsi->mux.child_list = wsi->mux.sibling_list; - parent_wsi->mux.child_count--; - - vh->context->count_wsi_allocated--; - - if (wsi->user_space) - lws_free_set_NULL(wsi->user_space); - vh->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, NULL, NULL, 0); - lws_vhost_unbind_wsi(wsi); - lws_free(wsi); - - return NULL; -} - -struct lws * -lws_wsi_h2_adopt(struct lws *parent_wsi, struct lws *wsi) -{ - struct lws *nwsi = lws_get_network_wsi(parent_wsi); - - /* no more children allowed by parent */ - if (parent_wsi->mux.child_count + 1 > - parent_wsi->h2.h2n->our_set.s[H2SET_MAX_CONCURRENT_STREAMS]) { - lwsl_notice("reached concurrent stream limit\n"); - return NULL; - } - - /* sid is set just before issuing the headers, ensuring monoticity */ - - wsi->seen_nonpseudoheader = 0; -#if defined(LWS_WITH_CLIENT) - wsi->client_mux_substream = 1; -#endif - wsi->h2.initialized = 1; - -#if 0 - /* only assign sid at header send time when we know it */ - if (!wsi->mux.my_sid) { - wsi->mux.my_sid = nwsi->h2.h2n->highest_sid; - nwsi->h2.h2n->highest_sid += 2; - } -#endif - - lwsl_info("%s: binding wsi %p to sid %d (next %d)\n", __func__, - wsi, (int)wsi->mux.my_sid, (int)nwsi->h2.h2n->highest_sid); - - lws_wsi_mux_insert(wsi, parent_wsi, wsi->mux.my_sid); - - wsi->txc.tx_cr = nwsi->h2.h2n->peer_set.s[H2SET_INITIAL_WINDOW_SIZE]; - wsi->txc.peer_tx_cr_est = - nwsi->h2.h2n->our_set.s[H2SET_INITIAL_WINDOW_SIZE]; - - lws_wsi_txc_describe(&wsi->txc, __func__, wsi->mux.my_sid); - - if (lws_ensure_user_space(wsi)) - goto bail1; - - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_H2_WAITING_TO_SEND_HEADERS, - &role_ops_h2); - - lws_callback_on_writable(wsi); - -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.h2_subs++; -#endif - - return wsi; - -bail1: - /* undo the insert */ - parent_wsi->mux.child_list = wsi->mux.sibling_list; - parent_wsi->mux.child_count--; - - if (wsi->user_space) - lws_free_set_NULL(wsi->user_space); - wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_DESTROY, NULL, NULL, 0); - lws_free(wsi); - - return NULL; -} - - -int lws_h2_issue_preface(struct lws *wsi) -{ - struct lws_h2_netconn *h2n = wsi->h2.h2n; - struct lws_h2_protocol_send *pps; - - if (lws_issue_raw(wsi, (uint8_t *)preface, strlen(preface)) != - (int)strlen(preface)) - return 1; - - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_H2_WAITING_TO_SEND_HEADERS, - &role_ops_h2); - - h2n->count = 0; - wsi->txc.tx_cr = 65535; - - /* - * we must send a settings frame - */ - pps = lws_h2_new_pps(LWS_H2_PPS_MY_SETTINGS); - if (!pps) - return 1; - lws_pps_schedule(wsi, pps); - lwsl_info("%s: h2 client sending settings\n", __func__); - - return 0; -} - -void -lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pps) -{ - struct lws *nwsi = lws_get_network_wsi(wsi); - struct lws_h2_netconn *h2n = nwsi->h2.h2n; - - pps->next = h2n->pps; - h2n->pps = pps; - lws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_DISABLE | - LWS_RXFLOW_REASON_H2_PPS_PENDING); - lws_callback_on_writable(wsi); -} - -int -lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason) -{ - struct lws_h2_netconn *h2n = wsi->h2.h2n; - struct lws_h2_protocol_send *pps; - - if (h2n->type == LWS_H2_FRAME_TYPE_COUNT) - return 0; - - pps = lws_h2_new_pps(LWS_H2_PPS_GOAWAY); - if (!pps) - return 1; - - lwsl_info("%s: %p: ERR 0x%x, '%s'\n", __func__, wsi, (int)err, reason); - - pps->u.ga.err = err; - pps->u.ga.highest_sid = h2n->highest_sid; - lws_strncpy(pps->u.ga.str, reason, sizeof(pps->u.ga.str)); - lws_pps_schedule(wsi, pps); - - h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */ - - return 0; -} - -int -lws_h2_rst_stream(struct lws *wsi, uint32_t err, const char *reason) -{ - struct lws *nwsi = lws_get_network_wsi(wsi); - struct lws_h2_netconn *h2n = nwsi->h2.h2n; - struct lws_h2_protocol_send *pps; - - if (!h2n) - return 0; - - if (!wsi->h2_stream_carries_ws && h2n->type == LWS_H2_FRAME_TYPE_COUNT) - return 0; - - pps = lws_h2_new_pps(LWS_H2_PPS_RST_STREAM); - if (!pps) - return 1; - - lwsl_info("%s: RST_STREAM 0x%x, sid %d, REASON '%s'\n", __func__, - (int)err, wsi->mux.my_sid, reason); - - pps->u.rs.sid = wsi->mux.my_sid; - pps->u.rs.err = err; - - lws_pps_schedule(wsi, pps); - - h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */ - lws_h2_state(wsi, LWS_H2_STATE_CLOSED); - - return 0; -} - -int -lws_h2_settings(struct lws *wsi, struct http2_settings *settings, - unsigned char *buf, int len) -{ - struct lws *nwsi = lws_get_network_wsi(wsi); - unsigned int a, b; - - if (!len) - return 0; - - if (len < LWS_H2_SETTINGS_LEN) - return 1; - - while (len >= LWS_H2_SETTINGS_LEN) { - a = (buf[0] << 8) | buf[1]; - if (!a || a >= H2SET_COUNT) - goto skip; - b = buf[2] << 24 | buf[3] << 16 | buf[4] << 8 | buf[5]; - - switch (a) { - case H2SET_HEADER_TABLE_SIZE: - break; - case H2SET_ENABLE_PUSH: - if (b > 1) { - lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, - "ENABLE_PUSH invalid arg"); - return 1; - } - break; - case H2SET_MAX_CONCURRENT_STREAMS: - break; - case H2SET_INITIAL_WINDOW_SIZE: - if (b > 0x7fffffff) { - lws_h2_goaway(nwsi, H2_ERR_FLOW_CONTROL_ERROR, - "Inital Window beyond max"); - return 1; - } - -#if defined(LWS_WITH_CLIENT) -#if defined(LWS_AMAZON_RTOS) || defined(LWS_AMAZON_LINUX) - if ( -#else - if (wsi->flags & LCCSCF_H2_QUIRK_OVERFLOWS_TXCR && -#endif - b == 0x7fffffff) { - b >>= 4; - - break; - } -#endif - - /* - * In addition to changing the flow-control window for - * streams that are not yet active, a SETTINGS frame - * can alter the initial flow-control window size for - * streams with active flow-control windows (that is, - * streams in the "open" or "half-closed (remote)" - * state). When the value of - * SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver - * MUST adjust the size of all stream flow-control - * windows that it maintains by the difference between - * the new value and the old value. - */ - - lws_start_foreach_ll(struct lws *, w, - nwsi->mux.child_list) { - lwsl_info("%s: adi child tc cr %d +%d -> %d", - __func__, (int)w->txc.tx_cr, - b - (unsigned int)settings->s[a], - (int)w->txc.tx_cr + b - - (unsigned int)settings->s[a]); - w->txc.tx_cr += b - settings->s[a]; - if (w->txc.tx_cr > 0 && - w->txc.tx_cr <= - (int32_t)(b - settings->s[a])) - - lws_callback_on_writable(w); - } lws_end_foreach_ll(w, mux.sibling_list); - - break; - case H2SET_MAX_FRAME_SIZE: - if (b < wsi->vhost->h2.set.s[H2SET_MAX_FRAME_SIZE]) { - lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, - "Frame size < initial"); - return 1; - } - if (b > 0x00ffffff) { - lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, - "Settings Frame size above max"); - return 1; - } - break; - case H2SET_MAX_HEADER_LIST_SIZE: - break; - } - settings->s[a] = b; - lwsl_info("http2 settings %d <- 0x%x\n", a, b); -skip: - len -= LWS_H2_SETTINGS_LEN; - buf += LWS_H2_SETTINGS_LEN; - } - - if (len) - return 1; - - lws_h2_dump_settings(settings); - - return 0; -} - -/* RFC7640 Sect 6.9 - * - * The WINDOW_UPDATE frame can be specific to a stream or to the entire - * connection. In the former case, the frame's stream identifier - * indicates the affected stream; in the latter, the value "0" indicates - * that the entire connection is the subject of the frame. - * - * ... - * - * Two flow-control windows are applicable: the stream flow-control - * window and the connection flow-control window. The sender MUST NOT - * send a flow-controlled frame with a length that exceeds the space - * available in either of the flow-control windows advertised by the - * receiver. Frames with zero length with the END_STREAM flag set (that - * is, an empty DATA frame) MAY be sent if there is no available space - * in either flow-control window. - */ - -int -lws_h2_tx_cr_get(struct lws *wsi) -{ - int c = wsi->txc.tx_cr; - struct lws *nwsi = lws_get_network_wsi(wsi); - - if (!wsi->mux_substream && !nwsi->upgraded_to_http2) - return ~0x80000000; - - lwsl_info ("%s: %p: own tx credit %d: nwsi credit %d\n", - __func__, wsi, c, (int)nwsi->txc.tx_cr); - - if (nwsi->txc.tx_cr < c) - c = nwsi->txc.tx_cr; - - if (c < 0) - return 0; - - return c; -} - -void -lws_h2_tx_cr_consume(struct lws *wsi, int consumed) -{ - struct lws *nwsi = lws_get_network_wsi(wsi); - - wsi->txc.tx_cr -= consumed; - - if (nwsi != wsi) - nwsi->txc.tx_cr -= consumed; -} - -int lws_h2_frame_write(struct lws *wsi, int type, int flags, - unsigned int sid, unsigned int len, unsigned char *buf) -{ - struct lws *nwsi = lws_get_network_wsi(wsi); - unsigned char *p = &buf[-LWS_H2_FRAME_HEADER_LENGTH]; - int n; - - //if (wsi->h2_stream_carries_ws) - // lwsl_hexdump_level(LLL_NOTICE, buf, len); - - *p++ = len >> 16; - *p++ = len >> 8; - *p++ = len; - *p++ = type; - *p++ = flags; - *p++ = sid >> 24; - *p++ = sid >> 16; - *p++ = sid >> 8; - *p++ = sid; - - lwsl_debug("%s: %p (eff %p). typ %d, fl 0x%x, sid=%d, len=%d, " - "txcr=%d, nwsi->txcr=%d\n", __func__, wsi, nwsi, type, flags, - sid, len, (int)wsi->txc.tx_cr, (int)nwsi->txc.tx_cr); - - if (type == LWS_H2_FRAME_TYPE_DATA) { - if (wsi->txc.tx_cr < (int)len) - lwsl_info("%s: %p: sending payload len %d" - " but tx_cr only %d!\n", __func__, wsi, - len, (int)wsi->txc.tx_cr); - lws_h2_tx_cr_consume(wsi, len); - } - - n = lws_issue_raw(nwsi, &buf[-LWS_H2_FRAME_HEADER_LENGTH], - len + LWS_H2_FRAME_HEADER_LENGTH); - if (n < 0) - return n; - - if (n >= LWS_H2_FRAME_HEADER_LENGTH) - return n - LWS_H2_FRAME_HEADER_LENGTH; - - return n; -} - -static void lws_h2_set_bin(struct lws *wsi, int n, unsigned char *buf) -{ - *buf++ = n >> 8; - *buf++ = n; - *buf++ = wsi->h2.h2n->our_set.s[n] >> 24; - *buf++ = wsi->h2.h2n->our_set.s[n] >> 16; - *buf++ = wsi->h2.h2n->our_set.s[n] >> 8; - *buf = wsi->h2.h2n->our_set.s[n]; -} - -/* we get called on the network connection */ - -int lws_h2_do_pps_send(struct lws *wsi) -{ - struct lws_h2_netconn *h2n = wsi->h2.h2n; - struct lws_h2_protocol_send *pps = NULL; - struct lws *cwsi; - uint8_t set[LWS_PRE + 64], *p = &set[LWS_PRE], *q; - int n, m = 0, flags = 0; - - if (!h2n) - return 1; - - /* get the oldest pps */ - - lws_start_foreach_llp(struct lws_h2_protocol_send **, pps1, h2n->pps) { - if ((*pps1)->next == NULL) { /* we are the oldest in the list */ - pps = *pps1; /* remove us from the list */ - *pps1 = NULL; - continue; - } - } lws_end_foreach_llp(pps1, next); - - if (!pps) - return 1; - - lwsl_info("%s: %p: %d\n", __func__, wsi, pps->type); - - switch (pps->type) { - - case LWS_H2_PPS_MY_SETTINGS: - - /* - * if any of our settings varies from h2 "default defaults" - * then we must inform the peer - */ - for (n = 1; n < H2SET_COUNT; n++) - if (h2n->our_set.s[n] != lws_h2_defaults.s[n]) { - lwsl_debug("sending SETTING %d 0x%x\n", n, - (unsigned int) - wsi->h2.h2n->our_set.s[n]); - - lws_h2_set_bin(wsi, n, &set[LWS_PRE + m]); - m += sizeof(h2n->one_setting); - } - n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_SETTINGS, - flags, LWS_H2_STREAM_ID_MASTER, m, - &set[LWS_PRE]); - if (n != m) { - lwsl_info("send %d %d\n", n, m); - goto bail; - } - break; - - case LWS_H2_PPS_SETTINGS_INITIAL_UPDATE_WINDOW: - q = &set[LWS_PRE]; - *q++ = H2SET_INITIAL_WINDOW_SIZE >> 8; - *q++ = H2SET_INITIAL_WINDOW_SIZE; - *q++ = pps->u.update_window.credit >> 24; - *q++ = pps->u.update_window.credit >> 16; - *q++ = pps->u.update_window.credit >> 8; - *q = pps->u.update_window.credit; - - lwsl_debug("%s: resetting initial window to %d\n", __func__, - (int)pps->u.update_window.credit); - - n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_SETTINGS, - flags, LWS_H2_STREAM_ID_MASTER, 6, - &set[LWS_PRE]); - if (n != 6) { - lwsl_info("send %d %d\n", n, m); - goto bail; - } - break; - - case LWS_H2_PPS_ACK_SETTINGS: - /* send ack ... always empty */ - n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_SETTINGS, 1, - LWS_H2_STREAM_ID_MASTER, 0, - &set[LWS_PRE]); - if (n) { - lwsl_err("ack tells %d\n", n); - goto bail; - } - wsi->h2_acked_settings = 0; - /* this is the end of the preface dance then? */ - if (lwsi_state(wsi) == LRS_H2_AWAIT_SETTINGS) { - lwsi_set_state(wsi, LRS_ESTABLISHED); -#if defined(LWS_WITH_FILE_OPS) - wsi->http.fop_fd = NULL; -#endif - if (lws_is_ssl(lws_get_network_wsi(wsi))) - break; - /* - * we need to treat the headers from the upgrade as the - * first job. So these need to get shifted to sid 1. - */ - h2n->swsi = lws_wsi_server_new(wsi->vhost, wsi, 1); - if (!h2n->swsi) - goto bail; - - /* pass on the initial headers to SID 1 */ - h2n->swsi->http.ah = wsi->http.ah; - wsi->http.ah = NULL; - - lwsl_info("%s: inherited headers %p\n", __func__, - h2n->swsi->http.ah); - h2n->swsi->txc.tx_cr = - h2n->our_set.s[H2SET_INITIAL_WINDOW_SIZE]; - lwsl_info("initial tx credit on conn %p: %d\n", - h2n->swsi, (int)h2n->swsi->txc.tx_cr); - h2n->swsi->h2.initialized = 1; - /* demanded by HTTP2 */ - h2n->swsi->h2.END_STREAM = 1; - lwsl_info("servicing initial http request\n"); - -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.h2_trans++; -#endif -#if defined(LWS_WITH_SERVER) - if (lws_http_action(h2n->swsi)) - goto bail; -#endif - break; - } - break; - - /* - * h2 only has PING... ACK = 0 = ping, ACK = 1 = pong - */ - - case LWS_H2_PPS_PING: - case LWS_H2_PPS_PONG: - if (pps->type == LWS_H2_PPS_PING) - lwsl_info("sending PING\n"); - else { - lwsl_info("sending PONG\n"); - flags = LWS_H2_FLAG_SETTINGS_ACK; - } - - memcpy(&set[LWS_PRE], pps->u.ping.ping_payload, 8); - n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_PING, flags, - LWS_H2_STREAM_ID_MASTER, 8, - &set[LWS_PRE]); - if (n != 8) - goto bail; - - break; - - case LWS_H2_PPS_GOAWAY: - lwsl_info("LWS_H2_PPS_GOAWAY\n"); - *p++ = pps->u.ga.highest_sid >> 24; - *p++ = pps->u.ga.highest_sid >> 16; - *p++ = pps->u.ga.highest_sid >> 8; - *p++ = pps->u.ga.highest_sid; - *p++ = pps->u.ga.err >> 24; - *p++ = pps->u.ga.err >> 16; - *p++ = pps->u.ga.err >> 8; - *p++ = pps->u.ga.err; - q = (unsigned char *)pps->u.ga.str; - n = 0; - while (*q && n++ < (int)sizeof(pps->u.ga.str)) - *p++ = *q++; - h2n->we_told_goaway = 1; - n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_GOAWAY, 0, - LWS_H2_STREAM_ID_MASTER, - lws_ptr_diff(p, &set[LWS_PRE]), - &set[LWS_PRE]); - if (n != 4) { - lwsl_info("send %d %d\n", n, m); - goto bail; - } - goto bail; - - case LWS_H2_PPS_RST_STREAM: - lwsl_info("LWS_H2_PPS_RST_STREAM\n"); - *p++ = pps->u.rs.err >> 24; - *p++ = pps->u.rs.err >> 16; - *p++ = pps->u.rs.err >> 8; - *p++ = pps->u.rs.err; - n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_RST_STREAM, - 0, pps->u.rs.sid, 4, &set[LWS_PRE]); - if (n != 4) { - lwsl_info("send %d %d\n", n, m); - goto bail; - } - cwsi = lws_wsi_mux_from_id(wsi, pps->u.rs.sid); - if (cwsi) { - lwsl_debug("%s: closing cwsi %p %s %s (wsi %p)\n", - __func__, cwsi, cwsi->role_ops->name, - cwsi->protocol->name, wsi); - lws_close_free_wsi(cwsi, 0, "reset stream"); - } - break; - - case LWS_H2_PPS_UPDATE_WINDOW: - lwsl_info("Issuing LWS_H2_PPS_UPDATE_WINDOW: sid %d: add %d\n", - (int)pps->u.update_window.sid, - (int)pps->u.update_window.credit); - *p++ = (pps->u.update_window.credit >> 24) & 0x7f; /* 31b */ - *p++ = pps->u.update_window.credit >> 16; - *p++ = pps->u.update_window.credit >> 8; - *p++ = pps->u.update_window.credit; - n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_WINDOW_UPDATE, - 0, pps->u.update_window.sid, 4, - &set[LWS_PRE]); - if (n != 4) { - lwsl_info("send %d %d\n", n, m); - goto bail; - } - break; - - default: - break; - } - - lws_free(pps); - - return 0; - -bail: - lws_free(pps); - - return 1; -} - -static int -lws_h2_parse_end_of_frame(struct lws *wsi); - -/* - * The frame header part has just completely arrived. - * Perform actions for header completion. - */ -static int -lws_h2_parse_frame_header(struct lws *wsi) -{ - struct lws_h2_netconn *h2n = wsi->h2.h2n; - struct lws_h2_protocol_send *pps; - int n; - - /* - * We just got the frame header - */ - h2n->count = 0; - h2n->swsi = wsi; - /* b31 is a reserved bit */ - h2n->sid = h2n->sid & 0x7fffffff; - - if (h2n->sid && !(h2n->sid & 1)) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "Even Stream ID"); - - return 0; - } - - /* let the network wsi live a bit longer if subs are active */ - - if (!wsi->immortal_substream_count) - lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, - wsi->vhost->keepalive_timeout ? - wsi->vhost->keepalive_timeout : 31); - - if (h2n->sid) - h2n->swsi = lws_wsi_mux_from_id(wsi, h2n->sid); - - lwsl_debug("%p (%p): fr hdr: typ 0x%x, fla 0x%x, sid 0x%x, len 0x%x\n", - wsi, h2n->swsi, h2n->type, h2n->flags, (unsigned int)h2n->sid, - (unsigned int)h2n->length); - - if (h2n->we_told_goaway && h2n->sid > h2n->highest_sid) - h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */ - - if (h2n->type == LWS_H2_FRAME_TYPE_COUNT) - return 0; - - if (h2n->length > h2n->our_set.s[H2SET_MAX_FRAME_SIZE]) { - /* - * peer sent us something bigger than we told - * it we would allow - */ - lwsl_info("%s: received oversize frame %d\n", __func__, - (unsigned int)h2n->length); - lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, - "Peer ignored our frame size setting"); - return 1; - } - - if (h2n->swsi) - lwsl_info("%s: wsi %p, State: %s, received cmd %d\n", - __func__, h2n->swsi, - h2_state_names[h2n->swsi->h2.h2_state], h2n->type); - else { - /* if it's data, either way no swsi means CLOSED state */ - if (h2n->type == LWS_H2_FRAME_TYPE_DATA) { - if (h2n->sid <= h2n->highest_sid_opened -#if defined(LWS_WITH_CLIENT) - && wsi->client_h2_alpn -#endif - ) { - lwsl_notice("ignoring straggling data fl 0x%x\n", - h2n->flags); - /* ie, IGNORE */ - h2n->type = LWS_H2_FRAME_TYPE_COUNT; - } else { - lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED, - "Data for nonexistent sid"); - return 0; - } - } - /* if the sid is credible, treat as wsi for it closed */ - if (h2n->sid > h2n->highest_sid_opened && - h2n->type != LWS_H2_FRAME_TYPE_HEADERS && - h2n->type != LWS_H2_FRAME_TYPE_PRIORITY) { - /* if not credible, reject it */ - lwsl_info("%s: wsi %p, No child for sid %d, rxcmd %d\n", - __func__, h2n->swsi, (unsigned int)h2n->sid, h2n->type); - lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED, - "Data for nonexistent sid"); - return 0; - } - } - - if (h2n->swsi && h2n->sid && - !(http2_rx_validity[h2n->swsi->h2.h2_state] & (1 << h2n->type))) { - lwsl_info("%s: wsi %p, State: %s, ILLEGAL cmdrx %d (OK 0x%x)\n", - __func__, h2n->swsi, - h2_state_names[h2n->swsi->h2.h2_state], h2n->type, - http2_rx_validity[h2n->swsi->h2.h2_state]); - - if (h2n->swsi->h2.h2_state == LWS_H2_STATE_CLOSED || - h2n->swsi->h2.h2_state == LWS_H2_STATE_HALF_CLOSED_REMOTE) - n = H2_ERR_STREAM_CLOSED; - else - n = H2_ERR_PROTOCOL_ERROR; - lws_h2_goaway(wsi, n, "invalid rx for state"); - - return 0; - } - - if (h2n->cont_exp && (h2n->cont_exp_sid != h2n->sid || - h2n->type != LWS_H2_FRAME_TYPE_CONTINUATION)) { - lwsl_info("%s: expected cont on sid %u (got %d on sid %u)\n", - __func__, (unsigned int)h2n->cont_exp_sid, h2n->type, - (unsigned int)h2n->sid); - h2n->cont_exp = 0; - if (h2n->cont_exp_headers) - n = H2_ERR_COMPRESSION_ERROR; - else - n = H2_ERR_PROTOCOL_ERROR; - lws_h2_goaway(wsi, n, "Continuation hdrs State"); - - return 0; - } - - switch (h2n->type) { - case LWS_H2_FRAME_TYPE_DATA: - lwsl_info("seen incoming LWS_H2_FRAME_TYPE_DATA start\n"); - if (!h2n->sid) { - lwsl_info("DATA: 0 sid\n"); - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "DATA 0 sid"); - break; - } - lwsl_info("Frame header DATA: sid %u, flags 0x%x, len %u\n", - (unsigned int)h2n->sid, h2n->flags, - (unsigned int)h2n->length); - - if (!h2n->swsi) { - lwsl_notice("DATA: NULL swsi\n"); - break; - } - - lwsl_info("DATA rx on state %d\n", h2n->swsi->h2.h2_state); - - if ( - h2n->swsi->h2.h2_state == LWS_H2_STATE_HALF_CLOSED_REMOTE || - h2n->swsi->h2.h2_state == LWS_H2_STATE_CLOSED) { - lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED, "conn closed"); - break; - } - - if (h2n->length == 0) - lws_h2_parse_end_of_frame(wsi); - - break; - - case LWS_H2_FRAME_TYPE_PRIORITY: - lwsl_info("LWS_H2_FRAME_TYPE_PRIORITY complete frame\n"); - if (!h2n->sid) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "Priority has 0 sid"); - break; - } - if (h2n->length != 5) { - lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, - "Priority has length other than 5"); - break; - } - break; - case LWS_H2_FRAME_TYPE_PUSH_PROMISE: - lwsl_info("LWS_H2_FRAME_TYPE_PUSH_PROMISE complete frame\n"); - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "Server only"); - break; - - case LWS_H2_FRAME_TYPE_GOAWAY: - lwsl_debug("LWS_H2_FRAME_TYPE_GOAWAY received\n"); - break; - - case LWS_H2_FRAME_TYPE_RST_STREAM: - if (!h2n->sid) - return 1; - if (!h2n->swsi) { - if (h2n->sid <= h2n->highest_sid_opened) - break; - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "crazy sid on RST_STREAM"); - return 1; - } - if (h2n->length != 4) { - lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, - "RST_STREAM can only be length 4"); - break; - } - lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED); - break; - - case LWS_H2_FRAME_TYPE_SETTINGS: - lwsl_info("LWS_H2_FRAME_TYPE_SETTINGS complete frame\n"); - /* nonzero sid on settings is illegal */ - if (h2n->sid) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "Settings has nonzero sid"); - break; - } - - if (!(h2n->flags & LWS_H2_FLAG_SETTINGS_ACK)) { - if ((!h2n->length) || h2n->length % 6) { - lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, - "Settings length error"); - break; - } - - if (h2n->type == LWS_H2_FRAME_TYPE_COUNT) - return 0; - - if (wsi->upgraded_to_http2 && -#if defined(LWS_WITH_CLIENT) - (!(wsi->flags & LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM) || -#else - ( -#endif - !wsi->h2_acked_settings)) { - - pps = lws_h2_new_pps(LWS_H2_PPS_ACK_SETTINGS); - if (!pps) - return 1; - lws_pps_schedule(wsi, pps); - wsi->h2_acked_settings = 1; - } - break; - } - /* came to us with ACK set... not allowed to have payload */ - - if (h2n->length) { - lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, - "Settings with ACK not allowed payload"); - break; - } - break; - case LWS_H2_FRAME_TYPE_PING: - if (h2n->sid) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "Ping has nonzero sid"); - break; - } - if (h2n->length != 8) { - lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, - "Ping payload can only be 8"); - break; - } - break; - case LWS_H2_FRAME_TYPE_CONTINUATION: - lwsl_info("LWS_H2_FRAME_TYPE_CONTINUATION: sid = %u %d %d\n", - (unsigned int)h2n->sid, (int)h2n->cont_exp, - (int)h2n->cont_exp_sid); - - if (!h2n->cont_exp || - h2n->cont_exp_sid != h2n->sid || - !h2n->sid || - !h2n->swsi) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "unexpected CONTINUATION"); - break; - } - - if (h2n->swsi->h2.END_HEADERS) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "END_HEADERS already seen"); - break; - } - /* END_STREAM is in HEADERS, skip resetting it */ - goto update_end_headers; - - case LWS_H2_FRAME_TYPE_HEADERS: - lwsl_info("HEADERS: frame header: sid = %u\n", - (unsigned int)h2n->sid); - if (!h2n->sid) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "sid 0"); - return 1; - } - - if (h2n->swsi && !h2n->swsi->h2.END_STREAM && - h2n->swsi->h2.END_HEADERS && - !(h2n->flags & LWS_H2_FLAG_END_STREAM)) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "extra HEADERS together"); - return 1; - } - -#if defined(LWS_WITH_CLIENT) - if (wsi->client_h2_alpn) { - if (h2n->sid) { - h2n->swsi = lws_wsi_mux_from_id(wsi, h2n->sid); - lwsl_info("HEADERS: nwsi %p: sid %u mapped " - "to wsi %p\n", wsi, - (unsigned int)h2n->sid, h2n->swsi); - if (!h2n->swsi) - break; - } - goto update_end_headers; - } -#endif - - if (!h2n->swsi) { - /* no more children allowed by parent */ - if (wsi->mux.child_count + 1 > - wsi->h2.h2n->our_set.s[H2SET_MAX_CONCURRENT_STREAMS]) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "Another stream not allowed"); - - return 1; - } - - /* - * The peer has sent us a HEADERS implying the creation - * of a new stream - */ - - h2n->swsi = lws_wsi_server_new(wsi->vhost, wsi, - h2n->sid); - if (!h2n->swsi) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "OOM"); - - return 1; - } - - if (h2n->sid >= h2n->highest_sid) - h2n->highest_sid = h2n->sid + 2; - - h2n->swsi->h2.initialized = 1; - - if (lws_h2_update_peer_txcredit(h2n->swsi, - h2n->swsi->mux.my_sid, 4 * 65536)) - goto cleanup_wsi; - } - - /* - * ah needs attaching to child wsi, even though - * we only fill it from network wsi - */ - if (!h2n->swsi->http.ah) - if (lws_header_table_attach(h2n->swsi, 0)) { - lwsl_err("%s: Failed to get ah\n", __func__); - return 1; - } - - /* - * The first use of a new stream identifier implicitly closes - * all streams in the "idle" state that might have been - * initiated by that peer with a lower-valued stream identifier. - * - * For example, if a client sends a HEADERS frame on stream 7 - * without ever sending a frame on stream 5, then stream 5 - * transitions to the "closed" state when the first frame for - * stream 7 is sent or received. - */ - lws_start_foreach_ll(struct lws *, w, wsi->mux.child_list) { - if (w->mux.my_sid < h2n->sid && - w->h2.h2_state == LWS_H2_STATE_IDLE) - lws_close_free_wsi(w, 0, "h2 sid close"); - assert(w->mux.sibling_list != w); - } lws_end_foreach_ll(w, mux.sibling_list); - - if (lws_check_opt(h2n->swsi->vhost->options, - LWS_SERVER_OPTION_VH_H2_HALF_CLOSED_LONG_POLL)) { - - /* - * We don't directly timeout streams that enter the - * half-closed remote state, allowing immortal long - * poll - */ - lws_mux_mark_immortal(h2n->swsi); - lwsl_info("%s: %p: h2 stream entering long poll\n", - __func__, h2n->swsi); - - } else { - h2n->swsi->h2.END_STREAM = - !!(h2n->flags & LWS_H2_FLAG_END_STREAM); - lwsl_debug("%s: hdr END_STREAM = %d\n",__func__, - h2n->swsi->h2.END_STREAM); - } - - h2n->cont_exp = !(h2n->flags & LWS_H2_FLAG_END_HEADERS); - h2n->cont_exp_sid = h2n->sid; - h2n->cont_exp_headers = 1; - // lws_header_table_reset(h2n->swsi, 0); - -update_end_headers: - /* no END_HEADERS means CONTINUATION must come */ - h2n->swsi->h2.END_HEADERS = - !!(h2n->flags & LWS_H2_FLAG_END_HEADERS); - lwsl_info("%p: END_HEADERS %d\n", h2n->swsi, - h2n->swsi->h2.END_HEADERS); - if (h2n->swsi->h2.END_HEADERS) - h2n->cont_exp = 0; - lwsl_debug("END_HEADERS %d\n", h2n->swsi->h2.END_HEADERS); - break; - -cleanup_wsi: - - return 1; - - case LWS_H2_FRAME_TYPE_WINDOW_UPDATE: - if (h2n->length != 4) { - lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR, - "window update frame not 4"); - break; - } - lwsl_info("LWS_H2_FRAME_TYPE_WINDOW_UPDATE\n"); - break; - case LWS_H2_FRAME_TYPE_COUNT: - break; - default: - lwsl_info("%s: ILLEGAL FRAME TYPE %d\n", __func__, h2n->type); - h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */ - break; - } - if (h2n->length == 0) - h2n->frame_state = 0; - - return 0; -} - -static const char * const method_names[] = { - "GET", "POST", -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - "OPTIONS", "PUT", "PATCH", "DELETE", -#endif - "CONNECT", "HEAD" -}; -static unsigned char method_index[] = { - WSI_TOKEN_GET_URI, - WSI_TOKEN_POST_URI, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - WSI_TOKEN_OPTIONS_URI, - WSI_TOKEN_PUT_URI, - WSI_TOKEN_PATCH_URI, - WSI_TOKEN_DELETE_URI, -#endif - WSI_TOKEN_CONNECT, - WSI_TOKEN_HEAD_URI, -}; - -/* - * The last byte of the whole frame has been handled. - * Perform actions for frame completion. - * - * This is the crunch time for parsing that may have occured on a network - * wsi with a pending partial send... we may call lws_http_action() to send - * a response, conflicting with the partial. - * - * So in that case we change the wsi state and do the lws_http_action() in the - * WRITABLE handler as a priority. - */ -static int -lws_h2_parse_end_of_frame(struct lws *wsi) -{ - struct lws_h2_netconn *h2n = wsi->h2.h2n; - struct lws *eff_wsi = wsi; - const char *p; - int n; - - h2n->frame_state = 0; - h2n->count = 0; - - if (h2n->sid) - h2n->swsi = lws_wsi_mux_from_id(wsi, h2n->sid); - - if (h2n->sid > h2n->highest_sid) - h2n->highest_sid = h2n->sid; - - if (h2n->collected_priority && (h2n->dep & ~(1u << 31)) == h2n->sid) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "depends on own sid"); - return 0; - } - - switch (h2n->type) { - - case LWS_H2_FRAME_TYPE_SETTINGS: - -#if defined(LWS_WITH_CLIENT) - if (wsi->client_h2_alpn && !wsi->client_mux_migrated && - !(h2n->flags & LWS_H2_FLAG_SETTINGS_ACK)) { - struct lws_h2_protocol_send *pps; - - /* migrate original client ask on to substream 1 */ -#if defined(LWS_WITH_FILE_OPS) - wsi->http.fop_fd = NULL; -#endif - lwsl_info("%s: migrating\n", __func__); - wsi->client_mux_migrated = 1; - /* - * we need to treat the headers from the upgrade as the - * first job. So these need to get shifted to sid 1. - */ - h2n->swsi = lws_wsi_server_new(wsi->vhost, wsi, 1); - if (!h2n->swsi) - return 1; - h2n->sid = 1; - - assert(lws_wsi_mux_from_id(wsi, 1) == h2n->swsi); - - lws_role_transition(wsi, LWSIFR_CLIENT, - LRS_H2_WAITING_TO_SEND_HEADERS, - &role_ops_h2); - - lws_role_transition(h2n->swsi, LWSIFR_CLIENT, - LRS_H2_WAITING_TO_SEND_HEADERS, - &role_ops_h2); - - /* pass on the initial headers to SID 1 */ - h2n->swsi->http.ah = wsi->http.ah; - h2n->swsi->client_mux_substream = 1; - h2n->swsi->client_h2_alpn = 1; -#if defined(LWS_WITH_CLIENT) - h2n->swsi->flags = wsi->flags; -#endif - - h2n->swsi->protocol = wsi->protocol; - if (h2n->swsi->user_space && - !h2n->swsi->user_space_externally_allocated) - lws_free(h2n->swsi->user_space); - h2n->swsi->user_space = wsi->user_space; - h2n->swsi->user_space_externally_allocated = - wsi->user_space_externally_allocated; - h2n->swsi->opaque_user_data = wsi->opaque_user_data; - wsi->opaque_user_data = NULL; - h2n->swsi->txc.manual_initial_tx_credit = - wsi->txc.manual_initial_tx_credit; - - wsi->user_space = NULL; - - if (h2n->swsi->http.ah) - h2n->swsi->http.ah->wsi = h2n->swsi; - wsi->http.ah = NULL; - - lwsl_info("%s: MIGRATING nwsi %p: swsi %p\n", __func__, - wsi, h2n->swsi); - h2n->swsi->txc.tx_cr = - h2n->peer_set.s[H2SET_INITIAL_WINDOW_SIZE]; - lwsl_info("%s: initial tx credit on conn %p: %d\n", - __func__, h2n->swsi, (int)h2n->swsi->txc.tx_cr); - h2n->swsi->h2.initialized = 1; - - /* set our initial window size */ - if (!wsi->h2.initialized) { - wsi->txc.tx_cr = - h2n->peer_set.s[H2SET_INITIAL_WINDOW_SIZE]; - - lwsl_info("%s: initial tx credit for us to " - "write on master %p: %d\n", __func__, - wsi, (int)wsi->txc.tx_cr); - wsi->h2.initialized = 1; - } - - lws_callback_on_writable(h2n->swsi); - - if (!wsi->h2_acked_settings || - !(wsi->flags & LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM) - ) { - pps = lws_h2_new_pps(LWS_H2_PPS_ACK_SETTINGS); - if (!pps) - return 1; - lws_pps_schedule(wsi, pps); - lwsl_info("%s: SETTINGS ack PPS\n", __func__); - wsi->h2_acked_settings = 1; - } - - /* also attach any queued guys */ - - lws_wsi_mux_apply_queue(wsi); - } -#endif - break; - - case LWS_H2_FRAME_TYPE_CONTINUATION: - case LWS_H2_FRAME_TYPE_HEADERS: - - if (!h2n->swsi) - break; - - /* service the http request itself */ - - if (h2n->last_action_dyntable_resize) { - lws_h2_goaway(wsi, H2_ERR_COMPRESSION_ERROR, - "dyntable resize last in headers"); - break; - } - - if (!h2n->swsi->h2.END_HEADERS) { - /* we are not finished yet */ - lwsl_info("witholding http action for continuation\n"); - h2n->cont_exp_sid = h2n->sid; - h2n->cont_exp = 1; - break; - } - - /* confirm the hpack stream state is reasonable for finishing */ - - if (h2n->hpack != HPKS_TYPE) { - /* hpack incomplete */ - lwsl_info("hpack incomplete %d (type %d, len %u)\n", - h2n->hpack, h2n->type, - (unsigned int)h2n->hpack_len); - lws_h2_goaway(wsi, H2_ERR_COMPRESSION_ERROR, - "hpack incomplete"); - break; - } - - /* this is the last part of HEADERS */ - switch (h2n->swsi->h2.h2_state) { - case LWS_H2_STATE_IDLE: - lws_h2_state(h2n->swsi, LWS_H2_STATE_OPEN); - break; - case LWS_H2_STATE_RESERVED_REMOTE: - lws_h2_state(h2n->swsi, LWS_H2_STATE_HALF_CLOSED_LOCAL); - break; - } - - lwsl_info("http req, wsi=%p, h2n->swsi=%p\n", wsi, h2n->swsi); - h2n->swsi->hdr_parsing_completed = 1; - -#if defined(LWS_WITH_CLIENT) - if (h2n->swsi->client_mux_substream && - lws_client_interpret_server_handshake(h2n->swsi)) { - lwsl_info("%s: cli int serv hs closed it\n", __func__); - break; - } -#endif - - if (lws_hdr_extant(h2n->swsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { - h2n->swsi->http.rx_content_length = atoll( - lws_hdr_simple_ptr(h2n->swsi, - WSI_TOKEN_HTTP_CONTENT_LENGTH)); - h2n->swsi->http.rx_content_remain = - h2n->swsi->http.rx_content_length; - lwsl_info("setting rx_content_length %lld\n", - (long long)h2n->swsi->http.rx_content_length); - } - - { - int n = 0, len; - char buf[256]; - const unsigned char *c; - - do { - c = lws_token_to_string(n); - if (!c) { - n++; - continue; - } - - len = lws_hdr_total_length(h2n->swsi, n); - if (!len || len > (int)sizeof(buf) - 1) { - n++; - continue; - } - - if (lws_hdr_copy(h2n->swsi, buf, sizeof buf, - n) < 0) { - lwsl_info(" %s !oversize!\n", - (char *)c); - } else { - buf[sizeof(buf) - 1] = '\0'; - - lwsl_info(" %s = %s\n", - (char *)c, buf); - } - n++; - } while (c); - } - - if (h2n->swsi->h2.h2_state == LWS_H2_STATE_HALF_CLOSED_REMOTE || - h2n->swsi->h2.h2_state == LWS_H2_STATE_CLOSED) { - lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED, - "Banning service on CLOSED_REMOTE"); - break; - } - - switch (h2n->swsi->h2.h2_state) { - case LWS_H2_STATE_IDLE: - lws_h2_state(h2n->swsi, LWS_H2_STATE_OPEN); - break; - case LWS_H2_STATE_OPEN: - if (h2n->swsi->h2.END_STREAM) - lws_h2_state(h2n->swsi, - LWS_H2_STATE_HALF_CLOSED_REMOTE); - break; - case LWS_H2_STATE_HALF_CLOSED_LOCAL: - if (h2n->swsi->h2.END_STREAM) - lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED); - break; - } - -#if defined(LWS_WITH_CLIENT) - if (h2n->swsi->client_mux_substream) { - lwsl_info("%s: wsi %p: headers: client path (h2 state %s)\n", - __func__, wsi, h2_state_names[h2n->swsi->h2.h2_state]); - break; - } -#endif - - if (!lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_PATH) || - !lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_METHOD) || - !lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_SCHEME) || - lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_STATUS) || - lws_hdr_extant(h2n->swsi, WSI_TOKEN_CONNECTION)) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "Pseudoheader checks"); - break; - } - - if (lws_hdr_extant(h2n->swsi, WSI_TOKEN_TE)) { - n = lws_hdr_total_length(h2n->swsi, WSI_TOKEN_TE); - - if (n != 8 || - strncmp(lws_hdr_simple_ptr(h2n->swsi, WSI_TOKEN_TE), - "trailers", n)) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "Illegal transfer-encoding"); - break; - } - } - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - lws_http_compression_validate(h2n->swsi); -#endif - -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.h2_trans++; -#endif - p = lws_hdr_simple_ptr(h2n->swsi, WSI_TOKEN_HTTP_COLON_METHOD); - /* - * duplicate :path into the individual method uri header - * index, so that it looks the same as h1 in the ah - */ - for (n = 0; n < (int)LWS_ARRAY_SIZE(method_names); n++) - if (!strcasecmp(p, method_names[n])) { - h2n->swsi->http.ah->frag_index[method_index[n]] = - h2n->swsi->http.ah->frag_index[ - WSI_TOKEN_HTTP_COLON_PATH]; - break; - } - - lwsl_debug("%s: setting DEF_ACT from 0x%x\n", __func__, - (unsigned int)h2n->swsi->wsistate); - lwsi_set_state(h2n->swsi, LRS_DEFERRING_ACTION); - lws_callback_on_writable(h2n->swsi); - break; - - case LWS_H2_FRAME_TYPE_DATA: - lwsl_info("%s: DATA flags 0x%x\n", __func__, h2n->flags); - if (!h2n->swsi) - break; - - if (lws_hdr_total_length(h2n->swsi, - WSI_TOKEN_HTTP_CONTENT_LENGTH) && - h2n->swsi->h2.END_STREAM && - h2n->swsi->http.rx_content_length && - h2n->swsi->http.rx_content_remain) { - lws_h2_rst_stream(h2n->swsi, H2_ERR_PROTOCOL_ERROR, - "Not enough rx content"); - break; - } - - if (h2n->swsi->h2.END_STREAM && - h2n->swsi->h2.h2_state == LWS_H2_STATE_OPEN) - lws_h2_state(h2n->swsi, - LWS_H2_STATE_HALF_CLOSED_REMOTE); - - if (h2n->swsi->h2.END_STREAM && - h2n->swsi->h2.h2_state == LWS_H2_STATE_HALF_CLOSED_LOCAL) - lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED); - -#if defined(LWS_WITH_CLIENT) - /* - * client... remote END_STREAM implies we weren't going to - * send anything else anyway. - */ - - if (h2n->swsi->client_mux_substream && - (h2n->flags & LWS_H2_FLAG_END_STREAM)) { - lwsl_info("%s: %p: DATA: end stream\n", - __func__, h2n->swsi); - - if (h2n->swsi->h2.h2_state == LWS_H2_STATE_OPEN) { - lws_h2_state(h2n->swsi, - LWS_H2_STATE_HALF_CLOSED_REMOTE); - // lws_h2_rst_stream(h2n->swsi, H2_ERR_NO_ERROR, - // "client done"); - - // if (lws_http_transaction_completed_client(h2n->swsi)) - // lwsl_debug("tx completed returned close\n"); - } - - //if (h2n->swsi->h2.h2_state == LWS_H2_STATE_HALF_CLOSED_LOCAL) - { - lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED); - - lws_h2_rst_stream(h2n->swsi, H2_ERR_NO_ERROR, - "client done"); - - if (lws_http_transaction_completed_client(h2n->swsi)) - lwsl_debug("tx completed returned close\n"); - } - } -#endif - break; - - case LWS_H2_FRAME_TYPE_PING: - if (h2n->flags & LWS_H2_FLAG_SETTINGS_ACK) - lws_validity_confirmed(wsi); - else { - /* they're sending us a ping request */ - struct lws_h2_protocol_send *pps = - lws_h2_new_pps(LWS_H2_PPS_PONG); - if (!pps) - return 1; - - lwsl_info("rx ping, preparing pong\n"); - - memcpy(pps->u.ping.ping_payload, h2n->ping_payload, 8); - lws_pps_schedule(wsi, pps); - } - - break; - - case LWS_H2_FRAME_TYPE_WINDOW_UPDATE: - /* - * We only have an unsigned 31-bit (positive) increment possible - */ - h2n->hpack_e_dep &= ~(1u << 31); - lwsl_info("WINDOW_UPDATE: sid %u %u (0x%x)\n", - (unsigned int)h2n->sid, - (unsigned int)h2n->hpack_e_dep, - (unsigned int)h2n->hpack_e_dep); - - if (h2n->sid) - eff_wsi = h2n->swsi; - - if (!eff_wsi) { - if (h2n->sid > h2n->highest_sid_opened) - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "alien sid"); - break; /* ignore */ - } - - if (eff_wsi->vhost->options & - LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW && - (uint64_t)eff_wsi->txc.tx_cr + (uint64_t)h2n->hpack_e_dep > - (uint64_t)0x7fffffff) - h2n->hpack_e_dep = 0x7fffffff - eff_wsi->txc.tx_cr; - - if ((uint64_t)eff_wsi->txc.tx_cr + (uint64_t)h2n->hpack_e_dep > - (uint64_t)0x7fffffff) { - if (h2n->sid) - lws_h2_rst_stream(h2n->swsi, - H2_ERR_FLOW_CONTROL_ERROR, - "Flow control exceeded max"); - else - lws_h2_goaway(wsi, H2_ERR_FLOW_CONTROL_ERROR, - "Flow control exceeded max"); - break; - } - - if (!h2n->hpack_e_dep) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "Zero length window update"); - break; - } - n = eff_wsi->txc.tx_cr; - eff_wsi->txc.tx_cr += h2n->hpack_e_dep; - - lws_wsi_txc_report_manual_txcr_in(eff_wsi, - (int32_t)h2n->hpack_e_dep); - - lws_wsi_txc_describe(&eff_wsi->txc, "WINDOW_UPDATE in", - eff_wsi->mux.my_sid); - - if (n <= 0 && eff_wsi->txc.tx_cr <= 0) - /* it helps, but won't change sendability for anyone */ - break; - - /* - * It may have changed sendability (depends on SID 0 tx credit - * too)... for us and any children waiting on us... reassess - * blockage for all children first - */ - lws_start_foreach_ll(struct lws *, w, wsi->mux.child_list) { - lws_callback_on_writable(w); - } lws_end_foreach_ll(w, mux.sibling_list); - - if (eff_wsi->txc.skint && - !lws_wsi_txc_check_skint(&eff_wsi->txc, - lws_h2_tx_cr_get(eff_wsi))) - /* - * This one became un-skint, schedule a writeable - * callback - */ - lws_callback_on_writable(eff_wsi); - - break; - - case LWS_H2_FRAME_TYPE_GOAWAY: - lwsl_notice("GOAWAY: last sid %u, error 0x%08X, string '%s'\n", - (unsigned int)h2n->goaway_last_sid, - (unsigned int)h2n->goaway_err, h2n->goaway_str); - - return 1; - - case LWS_H2_FRAME_TYPE_RST_STREAM: - lwsl_info("LWS_H2_FRAME_TYPE_RST_STREAM: sid %u: reason 0x%x\n", - (unsigned int)h2n->sid, - (unsigned int)h2n->hpack_e_dep); - break; - - case LWS_H2_FRAME_TYPE_COUNT: /* IGNORING FRAME */ - break; - } - - return 0; -} - -/* - * This may want to send something on the network wsi, which may be in the - * middle of a partial send. PPS sends are OK because they are queued to - * go through the WRITABLE handler already. - * - * The read parser for the network wsi has no choice but to parse its stream - * anyway, because otherwise it will not be able to get tx credit window - * messages. - * - * Therefore if we will send non-PPS, ie, lws_http_action() for a stream - * wsi, we must change its state and handle it as a priority in the - * POLLOUT handler instead of writing it here. - * - * About closing... for the main network wsi, it should return nonzero to - * close it all. If it needs to close an swsi, it can do it here. - */ -int -lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen, - lws_filepos_t *inused) -{ - struct lws_h2_netconn *h2n = wsi->h2.h2n; - struct lws_h2_protocol_send *pps; - unsigned char c, *oldin = in; - int n, m; - - if (!h2n) - goto fail; - - while (inlen--) { - - c = *in++; - - // lwsl_notice("%s: 0x%x\n", __func__, c); - - switch (lwsi_state(wsi)) { - case LRS_H2_AWAIT_PREFACE: - if (preface[h2n->count++] != c) - goto fail; - - if (preface[h2n->count]) - break; - - lwsl_info("http2: %p: established\n", wsi); - lwsi_set_state(wsi, LRS_H2_AWAIT_SETTINGS); - lws_validity_confirmed(wsi); - h2n->count = 0; - wsi->txc.tx_cr = 65535; - - /* - * we must send a settings frame -- empty one is OK... - * that must be the first thing sent by server - * and the peer must send a SETTINGS with ACK flag... - */ - pps = lws_h2_new_pps(LWS_H2_PPS_MY_SETTINGS); - if (!pps) - goto fail; - lws_pps_schedule(wsi, pps); - break; - - case LRS_H2_WAITING_TO_SEND_HEADERS: - case LRS_ESTABLISHED: - case LRS_H2_AWAIT_SETTINGS: - if (h2n->frame_state != LWS_H2_FRAME_HEADER_LENGTH) - goto try_frame_start; - - /* - * post-header, preamble / payload / padding part - */ - h2n->count++; - - if (h2n->flags & LWS_H2_FLAG_PADDED && - !h2n->pad_length) { - /* - * Get the padding count... actual padding is - * at the end of the frame. - */ - h2n->padding = c; - h2n->pad_length = 1; - h2n->preamble++; - - if (h2n->padding > h2n->length - 1) - lws_h2_goaway(wsi, - H2_ERR_PROTOCOL_ERROR, - "execssive padding"); - break; /* we consumed this */ - } - - if (h2n->flags & LWS_H2_FLAG_PRIORITY && - !h2n->collected_priority) { - /* going to be 5 preamble bytes */ - - lwsl_debug("PRIORITY FLAG: 0x%x\n", c); - - if (h2n->preamble++ - h2n->pad_length < 4) { - h2n->dep = ((h2n->dep) << 8) | c; - break; /* we consumed this */ - } - h2n->weight_temp = c; - h2n->collected_priority = 1; - lwsl_debug("PRI FL: dep 0x%x, weight 0x%02X\n", - (unsigned int)h2n->dep, - h2n->weight_temp); - break; /* we consumed this */ - } - if (h2n->padding && h2n->count > - (h2n->length - h2n->padding)) { - if (c) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "nonzero padding"); - break; - } - goto frame_end; - } - - /* applies to wsi->h2.swsi which may be wsi */ - switch(h2n->type) { - - case LWS_H2_FRAME_TYPE_SETTINGS: - n = (h2n->count - 1 - h2n->preamble) % - LWS_H2_SETTINGS_LEN; - h2n->one_setting[n] = c; - if (n != LWS_H2_SETTINGS_LEN - 1) - break; - lws_h2_settings(wsi, &h2n->peer_set, - h2n->one_setting, - LWS_H2_SETTINGS_LEN); - break; - - case LWS_H2_FRAME_TYPE_CONTINUATION: - case LWS_H2_FRAME_TYPE_HEADERS: - if (!h2n->swsi) - break; - if (lws_hpack_interpret(h2n->swsi, c)) { - lwsl_info("%s: hpack failed\n", - __func__); - goto fail; - } - break; - - case LWS_H2_FRAME_TYPE_GOAWAY: - switch (h2n->inside++) { - case 0: - case 1: - case 2: - case 3: - h2n->goaway_last_sid <<= 8; - h2n->goaway_last_sid |= c; - h2n->goaway_str[0] = '\0'; - break; - - case 4: - case 5: - case 6: - case 7: - h2n->goaway_err <<= 8; - h2n->goaway_err |= c; - break; - - default: - if (h2n->inside - 9 < - sizeof(h2n->goaway_str) - 1) - h2n->goaway_str[ - h2n->inside - 9] = c; - h2n->goaway_str[ - sizeof(h2n->goaway_str) - 1] = '\0'; - break; - } - break; - - case LWS_H2_FRAME_TYPE_DATA: - - lwsl_info("%s: LWS_H2_FRAME_TYPE_DATA: fl 0x%x\n", - __func__, h2n->flags); - - /* - * let the network wsi live a bit longer if - * subs are active... our frame may take a long - * time to chew through - */ - if (!wsi->immortal_substream_count) - lws_set_timeout(wsi, - PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, - wsi->vhost->keepalive_timeout ? - wsi->vhost->keepalive_timeout : 31); - - if (!h2n->swsi) - break; - - if (lws_buflist_next_segment_len( - &h2n->swsi->buflist, NULL)) - lwsl_info("%s: substream has pending\n", - __func__); - - if (lwsi_role_http(h2n->swsi) && - lwsi_state(h2n->swsi) == LRS_ESTABLISHED) { - lwsi_set_state(h2n->swsi, LRS_BODY); - lwsl_info("%s: swsi %p to LRS_BODY\n", - __func__, h2n->swsi); - } - - if (lws_hdr_total_length(h2n->swsi, - WSI_TOKEN_HTTP_CONTENT_LENGTH) && - h2n->swsi->http.rx_content_length && - h2n->swsi->http.rx_content_remain < - inlen + 1 && /* last */ - h2n->inside < h2n->length) { - /* unread data in frame */ - lws_h2_goaway(wsi, - H2_ERR_PROTOCOL_ERROR, - "More rx than content_length told"); - break; - } - - /* - * We operate on a frame. The RX we have at - * hand may exceed the current frame. - */ - - n = (int)inlen + 1; - if (n > (int)(h2n->length - h2n->count + 1)) { - n = h2n->length - h2n->count + 1; - lwsl_debug("---- restricting len to %d " - "vs %ld\n", n, (long)inlen + 1); - } -#if defined(LWS_WITH_CLIENT) - if (h2n->swsi->client_mux_substream) { - if (!h2n->swsi->protocol) { - lwsl_err("%s: swsi %p doesn't have protocol\n", - __func__, h2n->swsi); - m = 1; - } else { - h2n->swsi->txc.peer_tx_cr_est -= n; - wsi->txc.peer_tx_cr_est -= n; - lws_wsi_txc_describe(&h2n->swsi->txc, - __func__, - h2n->swsi->mux.my_sid); - m = user_callback_handle_rxflow( - h2n->swsi->protocol->callback, - h2n->swsi, - LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, - h2n->swsi->user_space, - in - 1, n); - } - - in += n - 1; - h2n->inside += n; - h2n->count += n - 1; - inlen -= n - 1; - - if (m) { - lwsl_info("RECEIVE_CLIENT_HTTP " - "closed it\n"); - goto close_swsi_and_return; - } - - break; - } -#endif - - if (lwsi_state(h2n->swsi) == LRS_DEFERRING_ACTION) { - m = lws_buflist_append_segment( - &h2n->swsi->buflist, in - 1, n); - if (m < 0) - return -1; - if (m) { - struct lws_context_per_thread *pt; - - pt = &wsi->context->pt[(int)wsi->tsi]; - lwsl_debug("%s: added %p to rxflow list\n", - __func__, wsi); - lws_dll2_add_head( - &h2n->swsi->dll_buflist, - &pt->dll_buflist_owner); - } - in += n - 1; - h2n->inside += n; - h2n->count += n - 1; - inlen -= n - 1; - - lwsl_debug("%s: deferred %d\n", __func__, n); - goto do_windows; - } - - h2n->swsi->outer_will_close = 1; - /* - * choose the length for this go so that we end at - * the frame boundary, in the case there is already - * more waiting leave it for next time around - */ - - n = lws_read_h1(h2n->swsi, in - 1, n); - // lwsl_notice("%s: lws_read_h1 %d\n", __func__, n); - h2n->swsi->outer_will_close = 0; - /* - * can return 0 in POST body with - * content len exhausted somehow. - */ - if (n < 0 || - (!n && !lws_buflist_next_segment_len( - &wsi->buflist, NULL))) { - lwsl_info("%s: lws_read_h1 told %d %u / %u\n", - __func__, n, - (unsigned int)h2n->count, - (unsigned int)h2n->length); - in += h2n->length - h2n->count; - h2n->inside = h2n->length; - h2n->count = h2n->length - 1; - - //if (n < 0) - // goto already_closed_swsi; - goto close_swsi_and_return; - } - - inlen -= n - 1; - in += n - 1; - h2n->inside += n; - h2n->count += n - 1; - h2n->swsi->txc.peer_tx_cr_est -= n; - wsi->txc.peer_tx_cr_est -= n; - -do_windows: - -#if defined(LWS_WITH_CLIENT) - if (!(h2n->swsi->flags & LCCSCF_H2_MANUAL_RXFLOW)) -#endif - { - /* - * The default behaviour is we just keep - * cranking the other side's tx credit - * back up, for simple bulk transfer as - * fast as we can take it - */ - - m = n; //(2 * h2n->length) + 65536; - - /* update both the stream and nwsi */ - - lws_h2_update_peer_txcredit_thresh(h2n->swsi, - h2n->sid, m, m); - } -#if defined(LWS_WITH_CLIENT) - else { - /* - * If he's handling it himself, only - * repair the nwsi credit but allow the - * stream credit to run down until the - * user code deals with it - */ - lws_h2_update_peer_txcredit(wsi, 0, n); - h2n->swsi->txc.manual = 1; - } -#endif - break; - - case LWS_H2_FRAME_TYPE_PRIORITY: - if (h2n->count <= 4) { - h2n->dep <<= 8; - h2n->dep |= c; - break; - } - h2n->weight_temp = c; - lwsl_info("PRIORITY: dep 0x%x, weight 0x%02X\n", - (unsigned int)h2n->dep, h2n->weight_temp); - - if ((h2n->dep & ~(1u << 31)) == h2n->sid) { - lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, - "cant depend on own sid"); - break; - } - break; - - case LWS_H2_FRAME_TYPE_RST_STREAM: - h2n->hpack_e_dep <<= 8; - h2n->hpack_e_dep |= c; - break; - - case LWS_H2_FRAME_TYPE_PUSH_PROMISE: - break; - - case LWS_H2_FRAME_TYPE_PING: - if (h2n->flags & LWS_H2_FLAG_SETTINGS_ACK) { // ack - } else { /* they're sending us a ping request */ - if (h2n->count > 8) - return 1; - h2n->ping_payload[h2n->count - 1] = c; - } - break; - - case LWS_H2_FRAME_TYPE_WINDOW_UPDATE: - h2n->hpack_e_dep <<= 8; - h2n->hpack_e_dep |= c; - break; - - case LWS_H2_FRAME_TYPE_COUNT: /* IGNORING FRAME */ - break; - - default: - lwsl_notice("%s: unhandled frame type %d\n", - __func__, h2n->type); - - goto fail; - } - -frame_end: - if (h2n->count > h2n->length) { - lwsl_notice("%s: count > length %u %u\n", - __func__, (unsigned int)h2n->count, - (unsigned int)h2n->length); - goto fail; - } - if (h2n->count != h2n->length) - break; - - /* - * end of frame just happened - */ - if (lws_h2_parse_end_of_frame(wsi)) - goto fail; - - break; - -try_frame_start: - if (h2n->frame_state <= 8) { - - switch (h2n->frame_state++) { - case 0: - h2n->pad_length = 0; - h2n->collected_priority = 0; - h2n->padding = 0; - h2n->preamble = 0; - h2n->length = c; - h2n->inside = 0; - break; - case 1: - case 2: - h2n->length <<= 8; - h2n->length |= c; - break; - case 3: - h2n->type = c; - break; - case 4: - h2n->flags = c; - break; - - case 5: - case 6: - case 7: - case 8: - h2n->sid <<= 8; - h2n->sid |= c; - break; - } - } - - if (h2n->frame_state == LWS_H2_FRAME_HEADER_LENGTH) - if (lws_h2_parse_frame_header(wsi)) - goto fail; - break; - - default: - break; - } - } - - *inused = in - oldin; - - return 0; - -close_swsi_and_return: - - lws_close_free_wsi(h2n->swsi, 0, "close_swsi_and_return"); - h2n->swsi = NULL; - h2n->frame_state = 0; - h2n->count = 0; - -// already_closed_swsi: - *inused = in - oldin; - - return 2; - -fail: - *inused = in - oldin; - - return 1; -} - -#if defined(LWS_WITH_CLIENT) -int -lws_h2_client_handshake(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - uint8_t *buf, *start, *p, *p1, *end; - char *meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD), - *uri = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI); - struct lws *nwsi = lws_get_network_wsi(wsi); - int n, m; - /* - * The identifier of a newly established stream MUST be numerically - * greater than all streams that the initiating endpoint has opened or - * reserved. This governs streams that are opened using a HEADERS frame - * and streams that are reserved using PUSH_PROMISE. An endpoint that - * receives an unexpected stream identifier MUST respond with a - * connection error (Section 5.4.1) of type PROTOCOL_ERROR. - */ - int sid = nwsi->h2.h2n->highest_sid_opened + 2; - - lwsl_debug("%s\n", __func__); - - /* - * We MUST allocate our sid here at the point we're about to send the - * stream open. It's because we don't know the order in which multiple - * open streams will send their headers... in h2, sending the headers - * is the point the stream is opened. The peer requires that we only - * open streams in ascending sid order - */ - - wsi->mux.my_sid = nwsi->h2.h2n->highest_sid_opened = sid; - lwsl_info("%s: wsi %p: assigning SID %d at header send\n", __func__, wsi, sid); - - lwsl_info("%s: CLIENT_WAITING_TO_SEND_HEADERS: pollout (sid %d)\n", - __func__, wsi->mux.my_sid); - - p = start = buf = pt->serv_buf + LWS_PRE; - end = start + (wsi->context->pt_serv_buf_size / 2) - LWS_PRE - 1; - - /* it's time for us to send our client stream headers */ - - if (!meth) - meth = "GET"; - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_COLON_METHOD, - (unsigned char *)meth, - (int)strlen(meth), &p, end)) - goto fail_length; - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_COLON_SCHEME, - (unsigned char *)"https", 5, - &p, end)) - goto fail_length; - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_COLON_PATH, - (unsigned char *)uri, - lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI), - &p, end)) - goto fail_length; - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_COLON_AUTHORITY, - (unsigned char *)lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_ORIGIN), - lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_ORIGIN), - &p, end)) - goto fail_length; - - if (!wsi->client_h2_alpn && - lws_add_http_header_by_token(wsi, WSI_TOKEN_HOST, - (unsigned char *)lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_HOST), - lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_HOST), - &p, end)) - goto fail_length; - - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_USER_AGENT, - (unsigned char *)"lwsss", 5, - &p, end)) - goto fail_length; - - if (wsi->flags & LCCSCF_HTTP_MULTIPART_MIME) { - p1 = lws_http_multipart_headers(wsi, p); - if (!p1) - goto fail_length; - p = p1; - } - - if (wsi->flags & LCCSCF_HTTP_X_WWW_FORM_URLENCODED) { - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, - (unsigned char *)"application/x-www-form-urlencoded", - 33, &p, end)) - goto fail_length; - lws_client_http_body_pending(wsi, 1); - } - - /* give userland a chance to append, eg, cookies */ - - if (wsi->protocol->callback(wsi, - LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, - wsi->user_space, &p, (end - p) - 12)) - goto fail_length; - - if (lws_finalize_http_header(wsi, &p, end)) - goto fail_length; - -#if defined(LWS_WITH_DETAILED_LATENCY) - wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); -#endif - - m = LWS_WRITE_HTTP_HEADERS; -#if defined(LWS_WITH_CLIENT) - /* below is not needed in spec, indeed it destroys the long poll - * feature, but required by nghttp2 */ - if ((wsi->flags & LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM) && - !(wsi->client_http_body_pending)) - m |= LWS_WRITE_H2_STREAM_END; -#endif - - // lwsl_hexdump_notice(start, p - start); - - n = lws_write(wsi, start, p - start, m); - - if (n != (p - start)) { - lwsl_err("_write returned %d from %ld\n", n, - (long)(p - start)); - return -1; - } - - /* - * Normally let's charge up the peer tx credit a bit. But if - * MANUAL_REFLOW is set, just set it to the initial credit given in - * the client create info - */ - - n = 4 * 65536; - if (wsi->flags & LCCSCF_H2_MANUAL_RXFLOW) { - n = wsi->txc.manual_initial_tx_credit; - wsi->txc.manual = 1; - } - - if (lws_h2_update_peer_txcredit(wsi, wsi->mux.my_sid, n)) - return 1; - - lws_h2_state(wsi, LWS_H2_STATE_OPEN); - lwsi_set_state(wsi, LRS_ESTABLISHED); - - if (wsi->flags & LCCSCF_HTTP_MULTIPART_MIME) - lws_callback_on_writable(wsi); - - return 0; - -fail_length: - lwsl_err("Client hdrs too long: incr context info.pt_serv_buf_size\n"); - - return -1; -} -#endif - -#if defined(LWS_ROLE_WS) -int -lws_h2_ws_handshake(struct lws *wsi) -{ - uint8_t buf[LWS_PRE + 2048], *p = buf + LWS_PRE, *start = p, - *end = &buf[sizeof(buf) - 1]; - const struct lws_http_mount *hit; - const char * uri_ptr; - int n, m; - - if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) - return -1; - - if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) > 64) - return -1; - - if (wsi->proxied_ws_parent && wsi->child_list) { - if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) { - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_PROTOCOL, - (uint8_t *)lws_hdr_simple_ptr(wsi, - WSI_TOKEN_PROTOCOL), - (int)strlen(lws_hdr_simple_ptr(wsi, - WSI_TOKEN_PROTOCOL)), - &p, end)) - return -1; - } - } else { - - /* we can only return the protocol header if: - * - one came in, and ... */ - if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) && - /* - it is not an empty string */ - wsi->protocol->name && wsi->protocol->name[0]) { - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_PROTOCOL, - (unsigned char *)wsi->protocol->name, - (int)strlen(wsi->protocol->name), &p, end)) - return -1; - } - } - - if (lws_finalize_http_header(wsi, &p, end)) - return -1; - - m = lws_ptr_diff(p, start); - // lwsl_hexdump_notice(start, m); - n = lws_write(wsi, start, m, LWS_WRITE_HTTP_HEADERS); - if (n != m) { - lwsl_err("_write returned %d from %d\n", n, m); - - return -1; - } - - /* - * alright clean up, set our state to generic ws established, the - * mode / state of the nwsi will get the h2 processing done. - */ - - lwsi_set_state(wsi, LRS_ESTABLISHED); - wsi->lws_rx_parse_state = 0; // ==LWS_RXPS_NEW; - - uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_PATH); - n = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH); - hit = lws_find_mount(wsi, uri_ptr, n); - - if (hit && hit->cgienv && - wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO, wsi->user_space, - (void *)hit->cgienv, 0)) - return 1; - - lws_validity_confirmed(wsi); - - return 0; -} -#endif - -int -lws_read_h2(struct lws *wsi, unsigned char *buf, lws_filepos_t len) -{ - unsigned char *oldbuf = buf; - lws_filepos_t body_chunk_len; - - // lwsl_notice("%s: h2 path: wsistate 0x%x len %d\n", __func__, - // wsi->wsistate, (int)len); - - /* - * wsi here is always the network connection wsi, not a stream - * wsi. Once we unpicked the framing we will find the right - * swsi and make it the target of the frame. - * - * If it's ws over h2, the nwsi will get us here to do the h2 - * processing, and that will call us back with the swsi + - * ESTABLISHED state for the inner payload, handled in a later - * case. - */ - while (len) { - int m; - - /* - * we were accepting input but now we stopped doing so - */ - if (lws_is_flowcontrolled(wsi)) { - lws_rxflow_cache(wsi, buf, 0, (int)len); - buf += len; - break; - } - - /* - * lws_h2_parser() may send something; when it gets the - * whole frame, it will want to perform some action - * involving a reply. But we may be in a partial send - * situation on the network wsi... - * - * Even though we may be in a partial send and unable to - * send anything new, we still have to parse the network - * wsi in order to gain tx credit to send, which is - * potentially necessary to clear the old partial send. - * - * ALL network wsi-specific frames are sent by PPS - * already, these are sent as a priority on the writable - * handler, and so respect partial sends. The only - * problem is when a stream wsi wants to send an, eg, - * reply headers frame in response to the parsing - * we will do now... the *stream wsi* must stall in a - * different state until it is able to do so from a - * priority on the WRITABLE callback, same way that - * file transfers operate. - */ - - m = lws_h2_parser(wsi, buf, len, &body_chunk_len); - if (m && m != 2) { - lwsl_debug("%s: http2_parser bail: %d\n", __func__, m); - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "lws_read_h2 bail"); - - return -1; - } - if (m == 2) { - /* swsi has been closed */ - buf += body_chunk_len; - break; - } - - buf += body_chunk_len; - len -= body_chunk_len; - } - - return lws_ptr_diff(buf, oldbuf); -} - -int -lws_h2_client_stream_long_poll_rxonly(struct lws *wsi) -{ - - if (!wsi->mux_substream) - return 1; - - /* - * Elect to send an empty DATA with END_STREAM, to force the stream - * into HALF_CLOSED LOCAL - */ - wsi->h2.long_poll = 1; - wsi->h2.send_END_STREAM = 1; - - // lws_header_table_detach(wsi, 0); - - lws_callback_on_writable(wsi); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/roles/h2/huftable.h libwebsockets-2.4.2/lib/roles/h2/huftable.h --- libwebsockets-4.0.20/lib/roles/h2/huftable.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/h2/huftable.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,530 +0,0 @@ -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 diff -Nru libwebsockets-4.0.20/lib/roles/h2/minihuf.c libwebsockets-2.4.2/lib/roles/h2/minihuf.c --- libwebsockets-4.0.20/lib/roles/h2/minihuf.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/h2/minihuf.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,516 +0,0 @@ -/* - * minilex.c - * - * High efficiency lexical state parser - * - * Copyright (C)2011-2014 Andy Green - * - * 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 -#include -#include - -#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; -} diff -Nru libwebsockets-4.0.20/lib/roles/h2/ops-h2.c libwebsockets-2.4.2/lib/roles/h2/ops-h2.c --- libwebsockets-4.0.20/lib/roles/h2/ops-h2.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/h2/ops-h2.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1261 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -/* - * These are the standardized defaults. - * Override what actually goes in the vhost settings in platform or user code. - * Leave these alone because they are used to determine "what is different - * from the protocol defaults". - */ -const struct http2_settings lws_h2_defaults = { { - 1, - /* H2SET_HEADER_TABLE_SIZE */ 4096, - /* *** This controls how many entries in the dynamic table *** - * Allows the sender to inform the remote endpoint of the maximum - * size of the header compression table used to decode header - * blocks, in octets. The encoder can select any size equal to or - * less than this value by using signaling specific to the header - * compression format inside a header block (see [COMPRESSION]). - * The initial value is 4,096 octets. - */ - /* H2SET_ENABLE_PUSH */ 1, - /* H2SET_MAX_CONCURRENT_STREAMS */ 0x7fffffff, - /* H2SET_INITIAL_WINDOW_SIZE */ 65535, - /* H2SET_MAX_FRAME_SIZE */ 16384, - /* H2SET_MAX_HEADER_LIST_SIZE */ 0x7fffffff, - /*< This advisory setting informs a peer of the maximum size of - * header list that the sender is prepared to accept, in octets. - * The value is based on the uncompressed size of header fields, - * including the length of the name and value in octets plus an - * overhead of 32 octets for each header field. - */ - /* H2SET_RESERVED7 */ 0, - /* H2SET_ENABLE_CONNECT_PROTOCOL */ 0, -}}; - -/* these are the "lws defaults"... they can be overridden in plat */ - -const struct http2_settings lws_h2_stock_settings = { { - 1, - /* H2SET_HEADER_TABLE_SIZE */ 65536, /* ffox */ - /* *** This controls how many entries in the dynamic table *** - * Allows the sender to inform the remote endpoint of the maximum - * size of the header compression table used to decode header - * blocks, in octets. The encoder can select any size equal to or - * less than this value by using signaling specific to the header - * compression format inside a header block (see [COMPRESSION]). - * The initial value is 4,096 octets. - * - * Can't pass h2spec with less than 4096 here... - */ - /* H2SET_ENABLE_PUSH */ 0, - /* H2SET_MAX_CONCURRENT_STREAMS */ 24, - /* H2SET_INITIAL_WINDOW_SIZE */ 0, - /*< This is managed by explicit WINDOW_UPDATE. Because otherwise no - * way to precisely control it when we do want to. - */ - /* H2SET_MAX_FRAME_SIZE */ 16384, - /* H2SET_MAX_HEADER_LIST_SIZE */ 4096, - /*< This advisory setting informs a peer of the maximum size of - * header list that the sender is prepared to accept, in octets. - * The value is based on the uncompressed size of header fields, - * including the length of the name and value in octets plus an - * overhead of 32 octets for each header field. - */ - /* H2SET_RESERVED7 */ 0, - /* H2SET_ENABLE_CONNECT_PROTOCOL */ 1, -}}; - -/* - * The wsi at this level is the network wsi - */ - -static int -rops_handle_POLLIN_h2(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_pollfd *pollfd) -{ - struct lws_tokens ebuf; - unsigned int pending = 0; - char buffered = 0; - struct lws *wsi1; - int n, m; - -#ifdef LWS_WITH_CGI - if (wsi->http.cgi && (pollfd->revents & LWS_POLLOUT)) { - if (lws_handle_POLLOUT_event(wsi, pollfd)) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - - return LWS_HPI_RET_HANDLED; - } -#endif - - lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__, - (unsigned int)wsi->wsistate, pollfd->revents & LWS_POLLOUT); - - /* - * something went wrong with parsing the handshake, and - * we ended up back in the event loop without completing it - */ - if (lwsi_state(wsi) == LRS_PRE_WS_SERVING_ACCEPT) { - wsi->socket_is_permanently_unusable = 1; - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - if (lwsi_state(wsi) == LRS_WAITING_CONNECT) { -#if defined(LWS_WITH_CLIENT) - if ((pollfd->revents & LWS_POLLOUT) && - lws_handle_POLLOUT_event(wsi, pollfd)) { - lwsl_debug("POLLOUT event closed it\n"); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - n = lws_client_socket_service(wsi, pollfd); - 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); - /* the write failed... it's had it */ - wsi->socket_is_permanently_unusable = 1; - - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE || - lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE || - lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) { - /* - * we stopped caring about anything except control - * packets. Force flow control off, defeat tx - * draining. - */ - lws_rx_flow_control(wsi, 1); -#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws) - wsi->ws->tx_draining_ext = 0; -#endif - } - - if (wsi->mux_substream || wsi->upgraded_to_http2) { - wsi1 = lws_get_network_wsi(wsi); - if (wsi1 && lws_has_buffered_out(wsi1)) - /* - * We cannot deal with any kind of new RX - * because we are dealing with a partial send - * (new RX may trigger new http_action() that - * expect to be able to send) - */ - return LWS_HPI_RET_HANDLED; - } - -read: - /* 3: network wsi buflist needs to be drained */ - - // 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 (!lws_ssl_pending(wsi) && - !(pollfd->revents & pollfd->events & LWS_POLLIN)) - return LWS_HPI_RET_HANDLED; - - if (!(lwsi_role_client(wsi) && - (lwsi_state(wsi) != LRS_ESTABLISHED && - lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS))) { - - ebuf.token = pt->serv_buf; - ebuf.len = lws_ssl_capable_read(wsi, - ebuf.token, - wsi->context->pt_serv_buf_size); - 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; - } - - // lwsl_notice("%s: Actual RX %d\n", __func__, ebuf.len); - // if (ebuf.len > 0) - // lwsl_hexdump_notice(ebuf.token, ebuf.len); - } - - if (ebuf.len < 0) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - -drain: -#if defined(LWS_WITH_CLIENT) - if (lwsi_role_http(wsi) && lwsi_role_client(wsi) && - wsi->hdr_parsing_completed && !wsi->told_user_closed) { - - /* - * In SSL mode we get POLLIN notification about - * encrypted data in. - * - * But that is not necessarily related to decrypted - * data out becoming available; in may need to perform - * other in or out before that happens. - * - * simply mark ourselves as having readable data - * and turn off our POLLIN - */ - wsi->client_rx_avail = 1; - if (lws_change_pollfd(wsi, LWS_POLLIN, 0)) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - - /* let user code know, he'll usually ask for writeable - * callback and drain / re-enable it there - */ - if (user_callback_handle_rxflow( - wsi->protocol->callback, - wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP, - wsi->user_space, NULL, 0)) { - lwsl_info("RECEIVE_CLIENT_HTTP closed it\n"); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - return LWS_HPI_RET_HANDLED; - } -#endif - - /* service incoming data */ - - if (ebuf.len) { - n = 0; - if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY && - lwsi_state(wsi) != LRS_DISCARD_BODY) - n = lws_read_h2(wsi, ebuf.token, ebuf.len); - else - n = lws_read_h1(wsi, ebuf.token, ebuf.len); - - if (n < 0) { - /* we closed wsi */ - return LWS_HPI_RET_WSI_ALREADY_DIED; - } - - if (n && buffered) { - // lwsl_notice("%s: h2 use %d\n", __func__, n); - m = (int)lws_buflist_use_segment(&wsi->buflist, (size_t)n); - lwsl_info("%s: draining rxflow: used %d, next %d\n", - __func__, n, m); - if (!m) { - lwsl_notice("%s: removed %p from dll_buflist\n", - __func__, wsi); - lws_dll2_remove(&wsi->dll_buflist); - } - } else - if (n && n != ebuf.len) { - // lwsl_notice("%s: h2 append seg %d\n", __func__, ebuf.len - n); - m = lws_buflist_append_segment(&wsi->buflist, - ebuf.token + n, - ebuf.len - n); - if (m < 0) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - if (m) { - lwsl_debug("%s: added %p to rxflow list\n", - __func__, wsi); - if (lws_dll2_is_detached(&wsi->dll_buflist)) - lws_dll2_add_head(&wsi->dll_buflist, - &pt->dll_buflist_owner); - } - } - } - - // lws_buflist_describe(&wsi->buflist, wsi, __func__); - -#if 0 - - /* - * This seems to be too aggressive... we don't want the ah stuck - * there but eg, WINDOW_UPDATE may come and detach it if we leave - * it like that... it will get detached at stream close - */ - - if (wsi->http.ah -#if defined(LWS_WITH_CLIENT) - && !wsi->client_h2_alpn -#endif - ) { - lwsl_err("xxx\n"); - - lws_header_table_detach(wsi, 0); - } -#endif - - pending = lws_ssl_pending(wsi); - if (pending) { - // lwsl_info("going around\n"); - goto read; - } - - return LWS_HPI_RET_HANDLED; -} - -int rops_handle_POLLOUT_h2(struct lws *wsi) -{ - // lwsl_notice("%s\n", __func__); - - if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY) - return LWS_HP_RET_USER_SERVICE; - - /* - * Priority 1: H2 protocol packets - */ - if ((wsi->upgraded_to_http2 -#if defined(LWS_WITH_CLIENT) - || wsi->client_h2_alpn -#endif - ) && wsi->h2.h2n->pps) { - lwsl_info("servicing pps\n"); - /* - * this is called on the network connection, but may close - * substreams... that may affect callers - */ - if (lws_h2_do_pps_send(wsi)) { - wsi->socket_is_permanently_unusable = 1; - return LWS_HP_RET_BAIL_DIE; - } - if (wsi->h2.h2n->pps) - return LWS_HP_RET_BAIL_OK; - - /* we can resume whatever we were doing */ - lws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_ENABLE | - LWS_RXFLOW_REASON_H2_PPS_PENDING); - - return LWS_HP_RET_BAIL_OK; /* leave POLLOUT active */ - } - - /* Priority 2: if we are closing, not allowed to send more data frags - * which means user callback or tx ext flush banned now - */ - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE) - return LWS_HP_RET_USER_SERVICE; - - return LWS_HP_RET_USER_SERVICE; -} - -static int -rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len, - enum lws_write_protocol *wp) -{ - unsigned char flags = 0, base = (*wp) & 0x1f; - size_t olen = len; - int n; -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - unsigned char mtubuf[4096 + LWS_PRE]; -#endif - - /* if not in a state to send stuff, then just send nothing */ - - if (!lwsi_role_ws(wsi) && !wsi->mux_stream_immortal && - base != LWS_WRITE_HTTP && - base != LWS_WRITE_HTTP_FINAL && - base != LWS_WRITE_HTTP_HEADERS_CONTINUATION && - base != LWS_WRITE_HTTP_HEADERS && lwsi_state(wsi) != LRS_BODY && - ((lwsi_state(wsi) != LRS_RETURNED_CLOSE && - lwsi_state(wsi) != LRS_WAITING_TO_SEND_CLOSE && - lwsi_state(wsi) != LRS_ESTABLISHED && - lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK) -#if defined(LWS_ROLE_WS) - || base != LWS_WRITE_CLOSE -#endif - )) { - //assert(0); - lwsl_notice("%s: binning wsistate 0x%x %d: %s\n", __func__, - (unsigned int)wsi->wsistate, *wp, wsi->protocol ? - wsi->protocol->name : "no protocol"); - - return 0; - } - - /* compression transform... */ - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - if (wsi->http.lcs) { - unsigned char *out = mtubuf + LWS_PRE; - size_t o = sizeof(mtubuf) - LWS_PRE; - - n = lws_http_compression_transform(wsi, buf, len, wp, &out, &o); - if (n) - return n; - - lwsl_info("%s: %p: transformed %d bytes to %d " - "(wp 0x%x, more %d)\n", __func__, - wsi, (int)len, (int)o, (int)*wp, - wsi->http.comp_ctx.may_have_more); - - buf = out; - len = o; - base = (*wp) & 0x1f; - - if (!len) - return olen; - } -#endif - - /* - * ws-over-h2 also ends up here after the ws framing applied - */ - - n = LWS_H2_FRAME_TYPE_DATA; - if (base == LWS_WRITE_HTTP_HEADERS) { - n = LWS_H2_FRAME_TYPE_HEADERS; - if (!((*wp) & LWS_WRITE_NO_FIN)) - flags = LWS_H2_FLAG_END_HEADERS; - if (wsi->h2.send_END_STREAM || - ((*wp) & LWS_WRITE_H2_STREAM_END)) { - flags |= LWS_H2_FLAG_END_STREAM; - wsi->h2.send_END_STREAM = 1; - } - } - - if (base == LWS_WRITE_HTTP_HEADERS_CONTINUATION) { - n = LWS_H2_FRAME_TYPE_CONTINUATION; - if (!((*wp) & LWS_WRITE_NO_FIN)) - flags = LWS_H2_FLAG_END_HEADERS; - if (wsi->h2.send_END_STREAM || - ((*wp) & LWS_WRITE_H2_STREAM_END)) { - flags |= LWS_H2_FLAG_END_STREAM; - wsi->h2.send_END_STREAM = 1; - } - } - - if ((base == LWS_WRITE_HTTP || - base == LWS_WRITE_HTTP_FINAL) && - wsi->http.tx_content_length) { - wsi->http.tx_content_remain -= len; - lwsl_info("%s: wsi %p: tx_content_rem = %llu\n", __func__, wsi, - (unsigned long long)wsi->http.tx_content_remain); - if (!wsi->http.tx_content_remain) { - lwsl_info("%s: selecting final write mode\n", __func__); - base = *wp = LWS_WRITE_HTTP_FINAL; - } - } - - if (base == LWS_WRITE_HTTP_FINAL || ((*wp) & LWS_WRITE_H2_STREAM_END)) { - lwsl_info("%s: %p: setting END_STREAM\n", __func__, wsi); - flags |= LWS_H2_FLAG_END_STREAM; - wsi->h2.send_END_STREAM = 1; - } - - n = lws_h2_frame_write(wsi, n, flags, wsi->mux.my_sid, (int)len, buf); - if (n < 0) - return n; - - /* hide it may have been compressed... */ - - return (int)olen; -} - -static int -rops_check_upgrades_h2(struct lws *wsi) -{ -#if defined(LWS_ROLE_WS) - char *p; - - /* - * with H2 there's also a way to upgrade a stream to something - * else... :method is CONNECT and :protocol says the name of - * the new protocol we want to carry. We have to have sent a - * SETTINGS saying that we support it though. - */ - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD); - if (!wsi->vhost->h2.set.s[H2SET_ENABLE_CONNECT_PROTOCOL] || - !wsi->mux_substream || !p || strcmp(p, "CONNECT")) - return LWS_UPG_RET_CONTINUE; - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_COLON_PROTOCOL); - if (!p || strcmp(p, "websocket")) - return LWS_UPG_RET_CONTINUE; - -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.ws_upg++; -#endif - lwsl_info("Upgrade h2 to ws\n"); - lws_mux_mark_immortal(wsi); - wsi->h2_stream_carries_ws = 1; - - if (lws_process_ws_upgrade(wsi)) - return LWS_UPG_RET_BAIL; - - lwsl_info("Upgraded h2 to ws OK\n"); - - return LWS_UPG_RET_DONE; -#else - return LWS_UPG_RET_CONTINUE; -#endif -} - -static int -rops_init_vhost_h2(struct lws_vhost *vh, - const struct lws_context_creation_info *info) -{ - vh->h2.set = vh->context->set; - if (info->http2_settings[0]) { - int n; - - for (n = 1; n < LWS_H2_SETTINGS_LEN; n++) - vh->h2.set.s[n] = info->http2_settings[n]; - } - - return 0; -} - -int -rops_pt_init_destroy_h2(struct lws_context *context, - const struct lws_context_creation_info *info, - struct lws_context_per_thread *pt, int destroy) -{ - context->set = lws_h2_stock_settings; - - /* - * We only want to do this once... we will do it if we are built - * otherwise h1 ops will do it (or nobody if no http at all) - */ -#if !defined(LWS_ROLE_H2) && defined(LWS_WITH_SERVER) - if (!destroy) { - - pt->sul_ah_lifecheck.cb = lws_sul_http_ah_lifecheck; - - __lws_sul_insert(&pt->pt_sul_owner, &pt->sul_ah_lifecheck, - 30 * LWS_US_PER_SEC); - } else - lws_dll2_remove(&pt->sul_ah_lifecheck.list); -#endif - - return 0; -} - - -static int -rops_tx_credit_h2(struct lws *wsi, char peer_to_us, int add) -{ - struct lws *nwsi = lws_get_network_wsi(wsi); - int n; - - if (add) { - if (peer_to_us == LWSTXCR_PEER_TO_US) { - /* - * We want to tell the peer they can write an additional - * "add" bytes to us - */ - return lws_h2_update_peer_txcredit(wsi, -1, add); - } - - /* - * We're being told we can write an additional "add" bytes - * to the peer - */ - - wsi->txc.tx_cr += add; - nwsi->txc.tx_cr += add; - - return 0; - } - - if (peer_to_us == LWSTXCR_US_TO_PEER) - return lws_h2_tx_cr_get(wsi); - - n = wsi->txc.peer_tx_cr_est; - if (n > nwsi->txc.peer_tx_cr_est) - n = nwsi->txc.peer_tx_cr_est; - - return n; -} - -static int -rops_destroy_role_h2(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct allocated_headers *ah; - - /* we may not have an ah, but may be on the waiting list... */ - lwsl_info("%s: wsi %p: ah det due to close\n", __func__, wsi); - __lws_header_table_detach(wsi, 0); - - ah = pt->http.ah_list; - - while (ah) { - if (ah->in_use && ah->wsi == wsi) { - lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi); - ah->in_use = 0; - ah->wsi = NULL; - pt->http.ah_count_in_use--; - break; - } - ah = ah->next; - } - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - lws_http_compression_destroy(wsi); -#endif - - if (wsi->upgraded_to_http2 || wsi->mux_substream) { - lws_hpack_destroy_dynamic_header(wsi); - - if (wsi->h2.h2n) - lws_free_set_NULL(wsi->h2.h2n); - } - - return 0; -} - -static int -rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason) -{ - -#if defined(LWS_WITH_HTTP_PROXY) - if (wsi->http.proxy_clientside) { - - wsi->http.proxy_clientside = 0; - - if (user_callback_handle_rxflow(wsi->protocol->callback, - wsi, - LWS_CALLBACK_COMPLETED_CLIENT_HTTP, - wsi->user_space, NULL, 0)) - wsi->http.proxy_clientside = 0; - } -#endif - - if (wsi->mux_substream && wsi->h2_stream_carries_ws) - lws_h2_rst_stream(wsi, 0, "none"); -/* else - if (wsi->mux_substream) - lws_h2_rst_stream(wsi, H2_ERR_STREAM_CLOSED, "swsi got closed"); -*/ - - lwsl_info(" wsi: %p, his parent %p: siblings:\n", wsi, wsi->mux.parent_wsi); - lws_wsi_mux_dump_children(wsi); - - if (wsi->upgraded_to_http2 || wsi->mux_substream -#if defined(LWS_WITH_CLIENT) - || wsi->client_mux_substream -#endif - ) { - lwsl_info("closing %p: parent %p\n", wsi, wsi->mux.parent_wsi); - - if (wsi->mux.child_list && lwsl_visible(LLL_INFO)) { - lwsl_info(" parent %p: closing children: list:\n", wsi); - lws_wsi_mux_dump_children(wsi); - } - lws_wsi_mux_close_children(wsi, reason); - } - - if (wsi->upgraded_to_http2) { - /* remove pps */ - struct lws_h2_protocol_send *w = wsi->h2.h2n->pps, *w1; - - while (w) { - w1 = w->next; - free(w); - w = w1; - } - wsi->h2.h2n->pps = NULL; - } - - if (( -#if defined(LWS_WITH_CLIENT) - wsi->client_mux_substream || -#endif - wsi->mux_substream) && - wsi->mux.parent_wsi) { - lws_wsi_mux_sibling_disconnect(wsi); - if (wsi->h2.pending_status_body) - lws_free_set_NULL(wsi->h2.pending_status_body); - } - - return 0; -} - -static int -rops_callback_on_writable_h2(struct lws *wsi) -{ -#if defined(LWS_WITH_CLIENT) - struct lws *network_wsi; -#endif - int already; - -// if (!lwsi_role_h2(wsi) && !lwsi_role_h2_ENCAPSULATION(wsi)) -// return 0; - - if (wsi->mux.requested_POLLOUT -#if defined(LWS_WITH_CLIENT) - && !wsi->client_h2_alpn -#endif - ) { - lwsl_debug("already pending writable\n"); - // return 1; - } - - /* is this for DATA or for control messages? */ - - if (wsi->upgraded_to_http2 && !wsi->h2.h2n->pps && - lws_wsi_txc_check_skint(&wsi->txc, lws_h2_tx_cr_get(wsi))) { - /* - * refuse his efforts to get WRITABLE if we have no credit and - * no non-DATA pps to send - */ - lwsl_err("%s: skint\n", __func__); - return 0; - } - -#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_h2_alpn - && !network_wsi->client_mux_substream -#endif - ) - return 1; - - return 0; -} - -#if defined(LWS_WITH_SERVER) -static int -lws_h2_bind_for_post_before_action(struct lws *wsi) -{ - const char *p; - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD); - if (p && !strcmp(p, "POST")) { - const struct lws_http_mount *hit = - lws_find_mount(wsi, - lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_COLON_PATH), - lws_hdr_total_length(wsi, - WSI_TOKEN_HTTP_COLON_PATH)); - - lwsl_debug("%s: %s: hit %p: %s\n", __func__, - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_PATH), - hit, hit ? hit->origin : "null"); - if (hit) { - const struct lws_protocols *pp; - const char *name = hit->origin; - - if (hit->protocol) - name = hit->protocol; - - pp = lws_vhost_name_to_protocol(wsi->vhost, name); - if (!pp) { - lwsl_info("Unable to find protocol '%s'\n", name); - return 1; - } - - if (lws_bind_protocol(wsi, pp, __func__)) - return 1; - } - - lwsl_info("%s: setting LRS_BODY from 0x%x (%s)\n", __func__, - (int)wsi->wsistate, wsi->protocol->name); - lwsi_set_state(wsi, LRS_BODY); - } - - return 0; -} -#endif - -/* - * we are the 'network wsi' for potentially many muxed child wsi with - * no network connection of their own, who have to use us for all their - * network actions. So we use a round-robin scheme to share out the - * POLLOUT notifications to our children. - * - * But because any child could exhaust the socket's ability to take - * writes, we can only let one child get notified each time. - * - * In addition children may be closed / deleted / added between POLLOUT - * notifications, so we can't hold pointers - */ - -static int -rops_perform_user_POLLOUT_h2(struct lws *wsi) -{ - struct lws **wsi2; -#if defined(LWS_ROLE_WS) - int write_type = LWS_WRITE_PONG; -#endif - int n; - - wsi = lws_get_network_wsi(wsi); - - wsi->mux.requested_POLLOUT = 0; -// if (!wsi->h2.initialized) { -// lwsl_info("pollout on uninitialized http2 conn\n"); -// return 0; -// } - - lws_wsi_mux_dump_waiting_children(wsi); - - wsi2 = &wsi->mux.child_list; - if (!*wsi2) - return 0; - - do { - struct lws *w, **wa; - - wa = &(*wsi2)->mux.sibling_list; - if (!(*wsi2)->mux.requested_POLLOUT) - goto next_child; - - /* - * we're going to do writable callback for this child. - * move him to be the last child - */ - - lwsl_debug("servicing child %p\n", *wsi2); - - w = lws_wsi_mux_move_child_to_tail(wsi2); - - if (!w) { - wa = &wsi->mux.child_list; - goto next_child; - } - - lwsl_info("%s: child wsi %p, sid %d, (wsistate 0x%x)\n", - __func__, w, w->mux.my_sid, (unsigned int)w->wsistate); - - /* priority 1: post compression-transform buffered output */ - - if (lws_has_buffered_out(w)) { - lwsl_debug("%s: completing partial\n", __func__); - if (lws_issue_raw(w, NULL, 0) < 0) { - lwsl_info("%s signalling to close\n", __func__); - lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, - "h2 end stream 1"); - wa = &wsi->mux.child_list; - goto next_child; - } - lws_callback_on_writable(w); - wa = &wsi->mux.child_list; - goto next_child; - } - - /* priority 2: pre compression-transform buffered output */ - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - if (w->http.comp_ctx.buflist_comp || - w->http.comp_ctx.may_have_more) { - enum lws_write_protocol wp = LWS_WRITE_HTTP; - - lwsl_info("%s: completing comp partial" - "(buflist_comp %p, may %d)\n", - __func__, w->http.comp_ctx.buflist_comp, - w->http.comp_ctx.may_have_more); - - if (rops_write_role_protocol_h2(w, NULL, 0, &wp) < 0) { - lwsl_info("%s signalling to close\n", __func__); - lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, - "comp write fail"); - } - lws_callback_on_writable(w); - wa = &wsi->mux.child_list; - goto next_child; - } -#endif - - /* priority 3: if no buffered out and waiting for that... */ - - if (lwsi_state(w) == LRS_FLUSHING_BEFORE_CLOSE) { - w->socket_is_permanently_unusable = 1; - lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, - "h2 end stream 1"); - wa = &wsi->mux.child_list; - goto next_child; - } - - /* if we arrived here, even by looping, we checked choked */ - w->could_have_pending = 0; - wsi->could_have_pending = 0; - - if (w->h2.pending_status_body) { - w->h2.send_END_STREAM = 1; - n = lws_write(w, (uint8_t *)w->h2.pending_status_body + - LWS_PRE, - strlen(w->h2.pending_status_body + - LWS_PRE), LWS_WRITE_HTTP_FINAL); - lws_free_set_NULL(w->h2.pending_status_body); - lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, - "h2 end stream 1"); - wa = &wsi->mux.child_list; - goto next_child; - } - -#if defined(LWS_WITH_CLIENT) - if (lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS) { - if (lws_h2_client_handshake(w)) - return -1; - - goto next_child; - } -#endif - -#if defined(LWS_WITH_SERVER) - if (lwsi_state(w) == LRS_DEFERRING_ACTION) { - - /* - * we had to defer the http_action to the POLLOUT - * handler, because we know it will send something and - * only in the POLLOUT handler do we know for sure - * that there is no partial pending on the network wsi. - */ - - lwsi_set_state(w, LRS_ESTABLISHED); - - lws_h2_bind_for_post_before_action(w); - - lwsl_info(" h2 action start...\n"); - n = lws_http_action(w); - if (n < 0) - lwsl_info (" h2 action result %d\n", n); - else - lwsl_info(" h2 action result %d " - "(wsi->http.rx_content_remain %lld)\n", - n, w->http.rx_content_remain); - - /* - * Commonly we only managed to start a larger transfer - * that will complete asynchronously under its own wsi - * states. In those cases we will hear about - * END_STREAM going out in the POLLOUT handler. - */ - if (n >= 0 && !w->h2.pending_status_body && - (n || w->h2.send_END_STREAM)) { - lwsl_info("closing stream after h2 action\n"); - lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, - "h2 end stream"); - wa = &wsi->mux.child_list; - } - - if (n < 0) - wa = &wsi->mux.child_list; - - goto next_child; - } - -#if defined(LWS_WITH_FILE_OPS) - - if (lwsi_state(w) == LRS_ISSUING_FILE) { - - if (lws_wsi_txc_check_skint(&w->txc, - lws_h2_tx_cr_get(w))) { - wa = &wsi->mux.child_list; - goto next_child; - } - - ((volatile struct lws *)w)->leave_pollout_active = 0; - - /* >0 == completion, <0 == error - * - * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION - * callback when it's done. That's the case even if we - * just completed the send, so wait for that. - */ - n = lws_serve_http_file_fragment(w); - lwsl_debug("lws_serve_http_file_fragment says %d\n", n); - - /* - * We will often hear about out having sent the final - * DATA here... if so close the actual wsi - */ - if (n < 0 || w->h2.send_END_STREAM) { - lwsl_debug("Closing POLLOUT child %p\n", w); - lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, - "h2 end stream file"); - wa = &wsi->mux.child_list; - goto next_child; - } - if (n > 0) - if (lws_http_transaction_completed(w)) - return -1; - if (!n) { - lws_callback_on_writable(w); - (w)->mux.requested_POLLOUT = 1; - } - - goto next_child; - } -#endif -#endif - -#if defined(LWS_ROLE_WS) - - /* Notify peer that we decided to close */ - - if (lwsi_role_ws(w) && - lwsi_state(w) == LRS_WAITING_TO_SEND_CLOSE) { - lwsl_debug("sending close packet\n"); - w->waiting_to_send_close_frame = 0; - n = lws_write(w, &w->ws->ping_payload_buf[LWS_PRE], - w->ws->close_in_ping_buffer_len, - LWS_WRITE_CLOSE); - if (n >= 0) { - lwsi_set_state(w, LRS_AWAITING_CLOSE_ACK); - lws_set_timeout(w, PENDING_TIMEOUT_CLOSE_ACK, 5); - lwsl_debug("sent close frame, awaiting ack\n"); - } - - goto next_child; - } - - /* - * Acknowledge receipt of peer's notification he closed, - * then logically close ourself - */ - - if ((lwsi_role_ws(w) && w->ws->ping_pending_flag) || - (lwsi_state(w) == LRS_RETURNED_CLOSE && - w->ws->payload_is_close)) { - - if (w->ws->payload_is_close) - write_type = LWS_WRITE_CLOSE | - LWS_WRITE_H2_STREAM_END; - - n = lws_write(w, &w->ws->ping_payload_buf[LWS_PRE], - w->ws->ping_payload_len, write_type); - if (n < 0) - return -1; - - /* well he is sent, mark him done */ - w->ws->ping_pending_flag = 0; - if (w->ws->payload_is_close) { - /* oh... a close frame... then we are done */ - lwsl_debug("Ack'd peer's close packet\n"); - w->ws->payload_is_close = 0; - lwsi_set_state(w, LRS_RETURNED_CLOSE); - lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, - "returned close packet"); - wa = &wsi->mux.child_list; - goto next_child; - } - - lws_callback_on_writable(w); - (w)->mux.requested_POLLOUT = 1; - - /* otherwise for PING, leave POLLOUT active both ways */ - goto next_child; - } -#endif - - /* - * set client wsi to immortal long-poll mode; send END_STREAM - * flag on headers to indicate to a server, that allows - * it, that you want them to leave the stream in a long poll - * ro immortal state. We have to send headers so the client - * understands the http connection is ongoing. - */ - - if (w->h2.send_END_STREAM && w->h2.long_poll) { - uint8_t buf[LWS_PRE + 1]; - enum lws_write_protocol wp = 0; - - if (!rops_write_role_protocol_h2(w, buf + LWS_PRE, 0, - &wp)) { - lwsl_info("%s: wsi %p: entering ro long poll\n", - __func__, w); - lws_mux_mark_immortal(w); - } else - lwsl_err("%s: wsi %p: failed to set long poll\n", - __func__, w); - goto next_child; - } - - if (lws_callback_as_writeable(w)) { - lwsl_info("Closing POLLOUT child (end stream %d)\n", - w->h2.send_END_STREAM); - lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, - "h2 pollout handle"); - wa = &wsi->mux.child_list; - } else - if (w->h2.send_END_STREAM) - lws_h2_state(w, LWS_H2_STATE_HALF_CLOSED_LOCAL); - -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 -1; - - return 0; -} - -static struct lws * -rops_encapsulation_parent_h2(struct lws *wsi) -{ - if (wsi->mux.parent_wsi) - return wsi->mux.parent_wsi; - - return NULL; -} - -static int -rops_alpn_negotiated_h2(struct lws *wsi, const char *alpn) -{ - struct allocated_headers *ah; - - lwsl_debug("%s: client %d\n", __func__, lwsi_role_client(wsi)); -#if defined(LWS_WITH_CLIENT) - if (lwsi_role_client(wsi)) { - lwsl_info("%s: upgraded to H2\n", __func__); - wsi->client_h2_alpn = 1; - } -#endif - - wsi->upgraded_to_http2 = 1; -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.h2_alpn++; -#endif - - /* adopt the header info */ - - ah = wsi->http.ah; - - lws_role_transition(wsi, LWSIFR_SERVER, LRS_H2_AWAIT_PREFACE, - &role_ops_h2); - - /* http2 union member has http union struct at start */ - wsi->http.ah = ah; - - if (!wsi->h2.h2n) - wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n), "h2n"); - if (!wsi->h2.h2n) - return 1; - - lws_h2_init(wsi); - - /* HTTP2 union */ - - lws_hpack_dynamic_size(wsi, - wsi->h2.h2n->our_set.s[H2SET_HEADER_TABLE_SIZE]); - wsi->txc.tx_cr = 65535; - - lwsl_info("%s: wsi %p: configured for h2\n", __func__, wsi); - - return 0; -} - -static int -rops_issue_keepalive_h2(struct lws *wsi, int isvalid) -{ - struct lws *nwsi = lws_get_network_wsi(wsi); - struct lws_h2_protocol_send *pps; - uint64_t us = lws_now_usecs(); - - if (isvalid) { - _lws_validity_confirmed_role(nwsi); - - return 0; - } - - /* - * We can only send these frames on the network connection itself... - * we shouldn't be tracking validity on anything else - */ - - assert(wsi == nwsi); - - pps = lws_h2_new_pps(LWS_H2_PPS_PING); - if (!pps) - return 1; - - /* - * The peer is defined to copy us back the unchanged payload in another - * PING frame this time with ACK set. So by sending that out with the - * current time, it's an interesting opportunity to learn the effective - * RTT on the link when the PONG comes in, plus or minus the time to - * schedule the PPS. - */ - - memcpy(pps->u.ping.ping_payload, &us, 8); - lws_pps_schedule(nwsi, pps); - - return 0; -} - -const struct lws_role_ops role_ops_h2 = { - /* role name */ "h2", - /* alpn id */ "h2", - /* check_upgrades */ rops_check_upgrades_h2, - /* pt_init_destroy */ rops_pt_init_destroy_h2, - /* init_vhost */ rops_init_vhost_h2, - /* destroy_vhost */ NULL, - /* service_flag_pending */ NULL, - /* handle_POLLIN */ rops_handle_POLLIN_h2, - /* handle_POLLOUT */ rops_handle_POLLOUT_h2, - /* perform_user_POLLOUT */ rops_perform_user_POLLOUT_h2, - /* callback_on_writable */ rops_callback_on_writable_h2, - /* tx_credit */ rops_tx_credit_h2, - /* write_role_protocol */ rops_write_role_protocol_h2, - /* encapsulation_parent */ rops_encapsulation_parent_h2, - /* alpn_negotiated */ rops_alpn_negotiated_h2, - /* close_via_role_protocol */ NULL, - /* close_role */ NULL, - /* close_kill_connection */ rops_close_kill_connection_h2, - /* destroy_role */ rops_destroy_role_h2, - /* adoption_bind */ NULL, - /* client_bind */ NULL, - /* issue_keepalive */ rops_issue_keepalive_h2, - /* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, - LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED }, - /* rx cb clnt, srv */ { LWS_CALLBACK_RECEIVE_CLIENT_HTTP, - 0 /* may be POST, etc */ }, - /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE, - LWS_CALLBACK_HTTP_WRITEABLE }, - /* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP, - LWS_CALLBACK_CLOSED_HTTP }, - /* protocol_bind cb c, srv */ { LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL, - LWS_CALLBACK_HTTP_BIND_PROTOCOL }, - /* protocol_unbind cb c, srv */ { LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL, - LWS_CALLBACK_HTTP_DROP_PROTOCOL }, - /* file_handle */ 0, -}; diff -Nru libwebsockets-4.0.20/lib/roles/h2/private-lib-roles-h2.h libwebsockets-2.4.2/lib/roles/h2/private-lib-roles-h2.h --- libwebsockets-4.0.20/lib/roles/h2/private-lib-roles-h2.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/h2/private-lib-roles-h2.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,383 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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; - - 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); -LWS_EXTERN void lws_h2_init(struct lws *wsi); -LWS_EXTERN int -lws_h2_settings(struct lws *nwsi, struct http2_settings *settings, - unsigned char *buf, int len); -LWS_EXTERN int -lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen, - lws_filepos_t *inused); -LWS_EXTERN int -lws_h2_do_pps_send(struct lws *wsi); -LWS_EXTERN int -lws_h2_frame_write(struct lws *wsi, int type, int flags, unsigned int sid, - unsigned int len, unsigned char *buf); -LWS_EXTERN struct lws * -lws_wsi_mux_from_id(struct lws *wsi, unsigned int sid); -LWS_EXTERN int -lws_hpack_interpret(struct lws *wsi, unsigned char c); -LWS_EXTERN 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); -LWS_EXTERN 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); -LWS_EXTERN int -lws_add_http2_header_status(struct lws *wsi, - unsigned int code, unsigned char **p, - unsigned char *end); -LWS_EXTERN void -lws_hpack_destroy_dynamic_header(struct lws *wsi); -LWS_EXTERN int -lws_hpack_dynamic_size(struct lws *wsi, int size); -LWS_EXTERN int -lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason); -LWS_EXTERN int -lws_h2_tx_cr_get(struct lws *wsi); -LWS_EXTERN void -lws_h2_tx_cr_consume(struct lws *wsi, int consumed); -LWS_EXTERN int -lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h); -LWS_EXTERN void -lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pss); - -LWS_EXTERN const struct http2_settings lws_h2_defaults; -LWS_EXTERN int -lws_h2_ws_handshake(struct lws *wsi); -LWS_EXTERN int lws_h2_issue_preface(struct lws *wsi); -LWS_EXTERN int -lws_h2_client_handshake(struct lws *wsi); -LWS_EXTERN 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); diff -Nru libwebsockets-4.0.20/lib/roles/http/client/client-handshake.c libwebsockets-2.4.2/lib/roles/http/client/client-handshake.c --- libwebsockets-4.0.20/lib/roles/http/client/client-handshake.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/client/client-handshake.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1451 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_SYS_ASYNC_DNS) -static int -lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result) -{ - struct addrinfo hints; - int n; - - memset(&hints, 0, sizeof(hints)); - *result = NULL; - - hints.ai_socktype = SOCK_STREAM; - -#ifdef LWS_WITH_IPV6 - if (wsi->ipv6) { - -#if !defined(__ANDROID__) - hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_V4MAPPED; -#endif - } else -#endif - { - hints.ai_family = PF_UNSPEC; - } - - n = getaddrinfo(ads, NULL, &hints, result); - - lwsl_info("%s: getaddrinfo '%s' says %d\n", __func__, ads, n); - - return n; -} -#endif - -struct lws * -lws_client_connect_4_established(struct lws *wsi, struct lws *wsi_piggyback, - ssize_t plen) -{ -#if defined(LWS_CLIENT_HTTP_PROXYING) - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; -#endif - const char *meth; - struct lws_pollfd pfd; - const char *cce = ""; - int n, m, rawish = 0; - - meth = lws_wsi_client_stash_item(wsi, CIS_METHOD, - _WSI_TOKEN_CLIENT_METHOD); - - if (meth && (!strcmp(meth, "RAW") -#if defined(LWS_ROLE_MQTT) - || !strcmp(meth, "MQTT") -#endif - )) - rawish = 1; - - if (wsi_piggyback) - goto send_hs; - -#if defined(LWS_CLIENT_HTTP_PROXYING) -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - /* we are connected to server, or proxy */ - - /* http proxy */ - if (wsi->vhost->http.http_proxy_port) { - const char *cpa; - - cpa = lws_wsi_client_stash_item(wsi, CIS_ADDRESS, - _WSI_TOKEN_CLIENT_PEER_ADDRESS); - if (!cpa) - goto failed; - - lwsl_info("%s: going via proxy\n", __func__); - - plen = lws_snprintf((char *)pt->serv_buf, 256, - "CONNECT %s:%u HTTP/1.1\x0d\x0a" - "Host: %s:%u\x0d\x0a" - "User-agent: lws\x0d\x0a", cpa, wsi->ocport, - cpa, wsi->ocport); - -#if defined(LWS_WITH_HTTP_BASIC_AUTH) - if (wsi->vhost->proxy_basic_auth_token[0]) - plen += lws_snprintf((char *)pt->serv_buf + plen, 256, - "Proxy-authorization: basic %s\x0d\x0a", - wsi->vhost->proxy_basic_auth_token); -#endif - - plen += lws_snprintf((char *)pt->serv_buf + plen, 5, "\x0d\x0a"); - - /* lwsl_hexdump_notice(pt->serv_buf, plen); */ - - /* - * OK from now on we talk via the proxy, so connect to that - */ - if (wsi->stash) - wsi->stash->cis[CIS_ADDRESS] = - wsi->vhost->http.http_proxy_address; - else - if (lws_hdr_simple_create(wsi, - _WSI_TOKEN_CLIENT_PEER_ADDRESS, - wsi->vhost->http.http_proxy_address)) - goto failed; - wsi->c_port = wsi->vhost->http.http_proxy_port; - - n = send(wsi->desc.sockfd, (char *)pt->serv_buf, (int)plen, - MSG_NOSIGNAL); - if (n < 0) { - lwsl_debug("ERROR writing to proxy socket\n"); - cce = "proxy write failed"; - goto failed; - } - - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, - wsi->context->timeout_secs); - - lwsi_set_state(wsi, LRS_WAITING_PROXY_REPLY); - - return wsi; - } -#endif -#endif - -#if defined(LWS_WITH_SOCKS5) - if (lwsi_state(wsi) != LRS_ESTABLISHED) - switch (lws_socks5c_greet(wsi, &cce)) { - case -1: - goto failed; - case 1: - return wsi; - default: - break; - } -#endif - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) -send_hs: - - if (wsi_piggyback && - !lws_dll2_is_detached(&wsi->dll2_cli_txn_queue)) { - /* - * We are pipelining on an already-established connection... - * we can skip tls establishment. - * - * Set these queued guys to a state where they won't actually - * send their headers until we decide later. - */ - - lwsi_set_state(wsi, LRS_H2_WAITING_TO_SEND_HEADERS); - - /* - * we can't send our headers directly, because they have to - * be sent when the parent is writeable. The parent will check - * for anybody on his client transaction queue that is in - * LRS_H1C_ISSUE_HANDSHAKE2, and let them write. - * - * If we are trying to do this too early, before the master - * connection has written his own headers, then it will just - * wait in the queue until it's possible to send them. - */ - lws_callback_on_writable(wsi_piggyback); -#if defined(LWS_WITH_DETAILED_LATENCY) - wsi->detlat.earliest_write_req = - wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); -#endif - lwsl_info("%s: wsi %p: waiting to send hdrs (par state 0x%x)\n", - __func__, wsi, lwsi_state(wsi_piggyback)); - } else { - lwsl_info("%s: wsi %p: %s %s client created own conn (raw %d) vh %sm st 0x%x\n", - __func__, wsi, wsi->role_ops->name, - wsi->protocol->name, rawish, wsi->vhost->name, lwsi_state(wsi)); - - /* we are making our own connection */ - - if (!rawish) - lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE); - else { - /* for a method = "RAW" connection, this makes us - * established */ - - -#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS) - - /* we have connected if we got here */ - - if (lwsi_state(wsi) == LRS_WAITING_CONNECT && - (wsi->tls.use_ssl & LCCSCF_USE_SSL)) { - - - - /* we can retry this... just cook the SSL BIO the first time */ - - switch (lws_client_create_tls(wsi, &cce, 1)) { - case 0: - break; - case 1: - return wsi; - default: - goto failed; - } - - - - lwsl_notice("%s: wsi %p: st 0x%x\n", - __func__, wsi, lwsi_state(wsi)); - - if (lwsi_state(wsi) == LRS_WAITING_CONNECT) - lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, - wsi->context->timeout_secs); - - return wsi; - } -#endif - - /* clear his established timeout */ - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - m = wsi->role_ops->adoption_cb[0]; - if (m) { - n = user_callback_handle_rxflow( - wsi->protocol->callback, wsi, - m, wsi->user_space, NULL, 0); - if (n < 0) { - lwsl_info("LWS_CALLBACK_RAW_PROXY_CLI_ADOPT failed\n"); - goto failed; - } - } - - /* service.c pollout processing wants this */ - wsi->hdr_parsing_completed = 1; -#if defined(LWS_ROLE_MQTT) - if (!strcmp(meth, "MQTT")) { -#if defined(LWS_WITH_TLS) - if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { - lwsi_set_state(wsi, LRS_WAITING_SSL); - return wsi; - } -#endif - lwsl_info("%s: settings LRS_MQTTC_IDLE\n", - __func__); - lwsi_set_state(wsi, LRS_MQTTC_IDLE); - - /* - * provoke service to issue the CONNECT directly. - */ - lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, - AWAITING_TIMEOUT); - - assert(lws_socket_is_valid(wsi->desc.sockfd)); - - pfd.fd = wsi->desc.sockfd; - pfd.events = LWS_POLLIN; - pfd.revents = LWS_POLLOUT; - - lwsl_info("%s: going to service fd\n", __func__); - n = lws_service_fd(wsi->context, &pfd); - if (n < 0) { - cce = "first service failed"; - goto failed; - } - if (n) /* returns 1 on failure after closing wsi */ - return NULL; - return wsi; - } -#endif - lwsl_info("%s: setting ESTABLISHED\n", __func__); - lwsi_set_state(wsi, LRS_ESTABLISHED); - - return wsi; - } - - /* - * provoke service to issue the handshake directly. - * - * we need to do it this way because in the proxy case, this is - * the next state and executed only if and when we get a good - * proxy response inside the state machine... but notice in - * SSL case this may not have sent anything yet with 0 return, - * and won't until many retries from main loop. To stop that - * becoming endless, cover with a timeout. - */ - - lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, - wsi->context->timeout_secs); - - assert(lws_socket_is_valid(wsi->desc.sockfd)); - - pfd.fd = wsi->desc.sockfd; - pfd.events = LWS_POLLIN; - pfd.revents = LWS_POLLIN; - - n = lws_service_fd(wsi->context, &pfd); - if (n < 0) { - cce = "first service failed"; - goto failed; - } - if (n) /* returns 1 on failure after closing wsi */ - return NULL; - } -#endif - return wsi; - -failed: - lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce)); - - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2"); - - return NULL; -} - -struct lws * -lws_client_connect_3_connect(struct lws *wsi, const char *ads, - const struct addrinfo *result, int n, void *opaque) -{ -#if defined(LWS_WITH_UNIX_SOCK) - struct sockaddr_un sau; -#endif -#ifdef LWS_WITH_IPV6 - char ipv6only = lws_check_opt(wsi->vhost->options, - LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY | - LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE); -#endif - const struct sockaddr *psa = NULL; - const char *cce = "", *iface; - uint16_t port = wsi->c_port; - lws_sockaddr46 sa46; - ssize_t plen = 0; - char ni[48]; - int m; - -#if defined(LWS_WITH_IPV6) && defined(__ANDROID__) - ipv6only = 0; -#endif - - /* - * async dns calls back here for everybody who cares when it gets a - * result... but if we are piggybacking, we do not want to connect - * ourselves - */ - - if (!lws_dll2_is_detached(&wsi->dll2_cli_txn_queue)) - return wsi; -#if 0 - if (!ads && !result) { - cce = "dns resolution failed"; - if (!wsi->oom4) - goto oom4; - else - goto failed; - } -#endif -#if !defined(WIN32) - /* - * We can check using getsockopt if our connect actually completed. - * Posix connect() allows nonblocking to redo the connect to - * find out if it succeeded, for win32 we have to use this path - * and take WSAEALREADY as a successful connect. - */ - - if (lwsi_state(wsi) == LRS_WAITING_CONNECT && - lws_socket_is_valid(wsi->desc.sockfd)) { - socklen_t sl = sizeof(int); - int e = 0; - - /* - * this resets SO_ERROR after reading it. If there's an error - * condition the connect definitively failed. - */ - - if (!getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR, -#if defined(WIN32) - (char *) -#endif - &e, &sl)) { - if (!e) { - lwsl_info("%s: getsockopt check: conn OK\n", - __func__); - - goto conn_good; - } - - lwsl_debug("%s: getsockopt fd %d says err %d\n", __func__, - wsi->desc.sockfd, e); - } - - lwsl_debug("%s: getsockopt check: conn fail: errno %d\n", - __func__, LWS_ERRNO); - goto try_next_result_fds; - } -#endif - -#if defined(LWS_WITH_UNIX_SOCK) - if (ads && *ads == '+') { - ads++; - memset(&sa46, 0, sizeof(sa46)); - memset(&sau, 0, sizeof(sau)); - sau.sun_family = AF_UNIX; - strncpy(sau.sun_path, ads, sizeof(sau.sun_path)); - sau.sun_path[sizeof(sau.sun_path) - 1] = '\0'; - - lwsl_info("%s: Unix skt: %s\n", __func__, ads); - - if (sau.sun_path[0] == '@') - sau.sun_path[0] = '\0'; - - goto ads_known; - } -#endif - -#if defined(LWS_WITH_SYS_ASYNC_DNS) - if (n == LADNS_RET_FAILED) { - lwsl_notice("%s: adns failed %s\n", __func__, ads); - goto oom4; - } -#endif - - if (!wsi->dns_results) { - wsi->dns_results_next = wsi->dns_results = result; - if (result) - lwsl_debug("%s: result %p result->ai_next %p\n", - __func__, result, result->ai_next); - } - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (lwsi_state(wsi) == LRS_WAITING_DNS && - wsi->context->detailed_latency_cb) { - wsi->detlat.type = LDLT_NAME_RESOLUTION; - wsi->detlat.latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE] = - lws_now_usecs() - - wsi->detlat.earliest_write_req_pre_write; - wsi->detlat.latencies[LAT_DUR_USERCB] = 0; - lws_det_lat_cb(wsi->context, &wsi->detlat); - wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); - } -#endif -#if defined(LWS_CLIENT_HTTP_PROXYING) && \ - (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)) - - /* Decide what it is we need to connect to: - * - * Priority 1: connect to http proxy */ - - if (wsi->vhost->http.http_proxy_port) { - ads = wsi->vhost->http.http_proxy_address; - port = wsi->vhost->http.http_proxy_port; -#else - if (0) { -#endif - -#if defined(LWS_WITH_SOCKS5) - - /* Priority 2: Connect to SOCK5 Proxy */ - - } else if (wsi->vhost->socks_proxy_port) { - if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_GREETING, &plen)) { - cce = "socks msg too large"; - goto oom4; - } - - lwsl_client("Sending SOCKS Greeting\n"); - ads = wsi->vhost->socks_proxy_address; - port = wsi->vhost->socks_proxy_port; -#endif - } - - memset(&sa46, 0, sizeof(sa46)); - - if (n || !wsi->dns_results) { - /* lws_getaddrinfo46 failed, there is no usable result */ - lwsl_notice("%s: lws_getaddrinfo46 failed %d\n", - __func__, n); - - cce = "ipv6 lws_getaddrinfo46 failed"; - goto oom4; - } - - /* - * Let's try connecting to each of the results in turn until one works - * or we run out of results - */ - -next_result: - - psa = (const struct sockaddr *)&sa46; - n = sizeof(sa46); - memset(&sa46, 0, sizeof(sa46)); - - switch (wsi->dns_results_next->ai_family) { - case AF_INET: -#if defined(LWS_WITH_IPV6) - if (ipv6only) { - sa46.sa4.sin_family = AF_INET6; - - /* map IPv4 to IPv6 */ - memset((char *)&sa46.sa6.sin6_addr, 0, - sizeof(sa46.sa6.sin6_addr)); - sa46.sa6.sin6_addr.s6_addr[10] = 0xff; - sa46.sa6.sin6_addr.s6_addr[11] = 0xff; - memcpy(&sa46.sa6.sin6_addr.s6_addr[12], - &((struct sockaddr_in *) - wsi->dns_results_next->ai_addr)->sin_addr, - sizeof(struct in_addr)); - sa46.sa6.sin6_port = htons(port); - ni[0] = '\0'; - lws_write_numeric_address(sa46.sa6.sin6_addr.s6_addr, - 16, ni, sizeof(ni)); - lwsl_info("%s: %s ipv4->ipv6 %s\n", __func__, ads, ni); - break; - } -#endif - sa46.sa4.sin_family = AF_INET; - sa46.sa4.sin_addr.s_addr = - ((struct sockaddr_in *)wsi->dns_results_next->ai_addr)-> - sin_addr.s_addr; - memset(&sa46.sa4.sin_zero, 0, sizeof(sa46.sa4.sin_zero)); - sa46.sa4.sin_port = htons(port); - n = sizeof(struct sockaddr_in); - lws_write_numeric_address((uint8_t *)&sa46.sa4.sin_addr.s_addr, - 4, ni, sizeof(ni)); - lwsl_info("%s: %s ipv4 %s\n", __func__, ads, ni); - break; - case AF_INET6: -#if defined(LWS_WITH_IPV6) - if (!wsi->ipv6) - goto try_next_result; - sa46.sa4.sin_family = AF_INET6; - memcpy(&sa46.sa6.sin6_addr, - &((struct sockaddr_in6 *)wsi->dns_results_next->ai_addr)-> - sin6_addr, sizeof(struct in6_addr)); - sa46.sa6.sin6_scope_id = ((struct sockaddr_in6 *) - wsi->dns_results_next->ai_addr)->sin6_scope_id; - sa46.sa6.sin6_flowinfo = ((struct sockaddr_in6 *) - wsi->dns_results_next->ai_addr)->sin6_flowinfo; - sa46.sa6.sin6_port = htons(port); - lws_write_numeric_address((uint8_t *)&sa46.sa6.sin6_addr, - 16, ni, sizeof(ni)); - lwsl_info("%s: %s ipv6 %s\n", __func__, ads, ni); -#else - goto try_next_result; /* ipv4 only can't use this */ -#endif - break; - } - -#if defined(LWS_WITH_UNIX_SOCK) -ads_known: -#endif - - /* now we decided on ipv4 or ipv6, set the port and create socket*/ - - if (!lws_socket_is_valid(wsi->desc.sockfd)) { - - if (wsi->context->event_loop_ops->check_client_connect_ok && - wsi->context->event_loop_ops->check_client_connect_ok(wsi)) { - cce = "waiting for event loop watcher to close"; - goto oom4; - } - -#if defined(LWS_WITH_UNIX_SOCK) - if (wsi->unix_skt) - wsi->desc.sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - else -#endif - wsi->desc.sockfd = socket(sa46.sa4.sin_family, - SOCK_STREAM, 0); - - if (!lws_socket_is_valid(wsi->desc.sockfd)) { - lwsl_warn("Unable to open socket\n"); - goto try_next_result; - } - - if (lws_plat_set_socket_options(wsi->vhost, wsi->desc.sockfd, -#if defined(LWS_WITH_UNIX_SOCK) - wsi->unix_skt)) { -#else - 0)) { -#endif - lwsl_err("Failed to set wsi socket options\n"); - goto try_next_result_closesock; - } - - lwsl_debug("%s: %p: WAITING_CONNECT\n", __func__, wsi); - lwsi_set_state(wsi, LRS_WAITING_CONNECT); - - if (wsi->context->event_loop_ops->sock_accept) - if (wsi->context->event_loop_ops->sock_accept(wsi)) - goto try_next_result_closesock; - - if (__insert_wsi_socket_into_fds(wsi->context, wsi)) - goto try_next_result_closesock; - - if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) - goto try_next_result_fds; - - /* - * Past here, we can't simply free the structs as error - * handling as oom4 does. - * - * We can run the whole close flow, or unpick the fds inclusion - * and anything else we have done. - */ - wsi->oom4 = 1; - if (!wsi->protocol) - wsi->protocol = &wsi->vhost->protocols[0]; - - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, - wsi->context->timeout_secs); - - iface = lws_wsi_client_stash_item(wsi, CIS_IFACE, - _WSI_TOKEN_CLIENT_IFACE); - - if (iface && *iface) { - m = lws_socket_bind(wsi->vhost, wsi->desc.sockfd, 0, - iface, wsi->ipv6); - if (m < 0) - goto try_next_result_fds; - } - } - -#if defined(LWS_WITH_UNIX_SOCK) - if (wsi->unix_skt) { - psa = (const struct sockaddr *)&sau; - if (sau.sun_path[0]) - n = sizeof(uint16_t) + strlen(sau.sun_path); - else - n = sizeof(uint16_t) + strlen(&sau.sun_path[1]) + 1; - } else -#endif - - if (!psa) /* coverity */ - goto try_next_result_fds; - - /* - * The actual connection attempt - */ - -#if defined(LWS_WITH_DETAILED_LATENCY) - wsi->detlat.earliest_write_req = - wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); -#endif - - m = connect(wsi->desc.sockfd, (const struct sockaddr *)psa, n); - if (m == -1) { - int errno_copy = LWS_ERRNO; - - lwsl_debug("%s: connect says errno: %d\n", __func__, errno_copy); - - if (errno_copy != LWS_EALREADY && - errno_copy != LWS_EINPROGRESS && - errno_copy != LWS_EWOULDBLOCK -#ifdef _WIN32 - && errno_copy != WSAEINVAL - && errno_copy != WSAEISCONN -#endif - ) { -#if defined(_DEBUG) - char nads[48]; - lws_sa46_write_numeric_address(&sa46, nads, sizeof(nads)); - lwsl_info("%s: Connect failed: %s port %d\n", - __func__, nads, port); -#endif - goto try_next_result_fds; - } - -#if defined(WIN32) - if (lws_plat_check_connection_error(wsi)) - goto try_next_result_fds; - if (errno_copy == WSAEISCONN) - goto conn_good; -#endif - - /* - * must do specifically a POLLOUT poll to hear - * about the connect completion - */ - if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) - goto try_next_result_fds; - - return wsi; - } - -conn_good: - - lwsl_info("%s: Connection started %p\n", __func__, wsi->dns_results); - - /* the tcp connection has happend */ - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (wsi->context->detailed_latency_cb) { - wsi->detlat.type = LDLT_CONNECTION; - wsi->detlat.latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE] = - lws_now_usecs() - - wsi->detlat.earliest_write_req_pre_write; - wsi->detlat.latencies[LAT_DUR_USERCB] = 0; - lws_det_lat_cb(wsi->context, &wsi->detlat); - wsi->detlat.earliest_write_req = - wsi->detlat.earliest_write_req_pre_write = - lws_now_usecs(); - } -#endif - - lws_addrinfo_clean(wsi); - - if (wsi->protocol) - wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE, - wsi->user_space, NULL, 0); - - return lws_client_connect_4_established(wsi, NULL, plen); - -oom4: - if (lwsi_role_client(wsi) && wsi->protocol /* && lwsi_state_est(wsi) */) - lws_inform_client_conn_fail(wsi,(void *)cce, strlen(cce)); - - /* take care that we might be inserted in fds already */ - if (wsi->position_in_fds_table != LWS_NO_FDS_POS) - goto failed1; - - /* - * We can't be an active client connection any more, if we thought - * that was what we were going to be doing. It should be if we are - * failing by oom4 path, we are still called by - * lws_client_connect_via_info() and will be returning NULL to that, - * so nobody else should have had a chance to queue on us. - */ - { - struct lws_vhost *vhost = wsi->vhost; - - lws_vhost_lock(vhost); - __lws_free_wsi(wsi); - lws_vhost_unlock(vhost); - } - - return NULL; - - -try_next_result_fds: - wsi->oom4 = 0; - __remove_wsi_socket_from_fds(wsi); - -try_next_result_closesock: - compatible_close(wsi->desc.sockfd); - wsi->desc.sockfd = LWS_SOCK_INVALID; - -try_next_result: - if (wsi->dns_results_next) { - wsi->dns_results_next = wsi->dns_results_next->ai_next; - if (wsi->dns_results_next) - goto next_result; - } - lws_addrinfo_clean(wsi); - cce = "Unable to connect"; - -//failed: - lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce)); - -failed1: - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2"); - - return NULL; -} - -struct lws * -lws_client_connect_2_dnsreq(struct lws *wsi) -{ - struct addrinfo *result = NULL; - const char *meth = NULL, *ads; -#if defined(LWS_WITH_IPV6) - struct sockaddr_in addr; - const char *iface; -#endif - const char *adsin; - int n, port = 0; - struct lws *w; - - if (lwsi_state(wsi) == LRS_WAITING_DNS || - lwsi_state(wsi) == LRS_WAITING_CONNECT) { - lwsl_info("%s: LRS_WAITING_DNS / CONNECT\n", __func__); - - return wsi; - } - - /* - * The first job is figure out if we want to pipeline on or just join - * an existing "active connection" to the same place - */ - - meth = lws_wsi_client_stash_item(wsi, CIS_METHOD, - _WSI_TOKEN_CLIENT_METHOD); - - /* we only pipeline connections that said it was okay */ - - if (!wsi->client_pipeline) { - lwsl_debug("%s: new conn on no pipeline flag\n", __func__); - - goto solo; - } - - /* only pipeline things we associate with being a stream */ - - if (meth && strcmp(meth, "RAW") && strcmp(meth, "GET") && - strcmp(meth, "POST") && strcmp(meth, "PUT") && - strcmp(meth, "UDP") && strcmp(meth, "MQTT")) - goto solo; - - /* consult active connections to find out disposition */ - - adsin = lws_wsi_client_stash_item(wsi, CIS_ADDRESS, - _WSI_TOKEN_CLIENT_PEER_ADDRESS); - - switch (lws_vhost_active_conns(wsi, &w, adsin)) { - case ACTIVE_CONNS_SOLO: - break; - case ACTIVE_CONNS_MUXED: - lwsl_notice("%s: ACTIVE_CONNS_MUXED\n", __func__); - if (lwsi_role_h2(wsi)) { - - if (wsi->protocol->callback(wsi, - LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP, - wsi->user_space, NULL, 0)) - goto failed1; - - //lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); - //lwsi_set_state(w, LRS_ESTABLISHED); - lws_callback_on_writable(wsi); - } - - return wsi; - case ACTIVE_CONNS_QUEUED: - return lws_client_connect_4_established(wsi, w, 0); - } - -solo: - wsi->addrinfo_idx = 0; - - /* - * clients who will create their own fresh connection keep a copy of - * the hostname they originally connected to, in case other connections - * want to use it too - */ - - if (!wsi->cli_hostname_copy) { - if (wsi->stash && wsi->stash->cis[CIS_HOST]) - wsi->cli_hostname_copy = - lws_strdup(wsi->stash->cis[CIS_HOST]); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - else { - char *pa = lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_PEER_ADDRESS); - if (pa) - wsi->cli_hostname_copy = lws_strdup(pa); - } -#endif - } - - /* - * If we made our own connection, and we're doing a method that can - * take a pipeline, we are an "active client connection". - * - * Add ourselves to the vhost list of those so that others can - * piggyback on our transaction queue - */ - - if (meth && (!strcmp(meth, "RAW") || !strcmp(meth, "GET") || - !strcmp(meth, "POST") || !strcmp(meth, "PUT") || - !strcmp(meth, "MQTT")) && - lws_dll2_is_detached(&wsi->dll2_cli_txn_queue) && - lws_dll2_is_detached(&wsi->dll_cli_active_conns)) { - lws_vhost_lock(wsi->vhost); - lwsl_info("%s: adding active conn %p\n", __func__, wsi); - /* caution... we will have to unpick this on oom4 path */ - lws_dll2_add_head(&wsi->dll_cli_active_conns, - &wsi->vhost->dll_cli_active_conns_owner); - lws_vhost_unlock(wsi->vhost); - } - - /* - * unix socket destination? - */ - - if (wsi->stash) - ads = wsi->stash->cis[CIS_ADDRESS]; - else - ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); -#if defined(LWS_WITH_UNIX_SOCK) - if (*ads == '+') { - wsi->unix_skt = 1; - n = 0; - goto next_step; - } -#endif - - /* - * start off allowing ipv6 on connection if vhost allows it - */ - wsi->ipv6 = LWS_IPV6_ENABLED(wsi->vhost); -#ifdef LWS_WITH_IPV6 - if (wsi->stash) - iface = wsi->stash->cis[CIS_IFACE]; - else - iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); - - if (wsi->ipv6 && iface && - inet_pton(AF_INET, iface, &addr.sin_addr) == 1) { - lwsl_notice("%s: client connection forced to IPv4\n", __func__); - wsi->ipv6 = 0; - } -#endif - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (lwsi_state(wsi) == LRS_WAITING_DNS && - wsi->context->detailed_latency_cb) { - wsi->detlat.type = LDLT_NAME_RESOLUTION; - wsi->detlat.latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE] = - lws_now_usecs() - - wsi->detlat.earliest_write_req_pre_write; - wsi->detlat.latencies[LAT_DUR_USERCB] = 0; - lws_det_lat_cb(wsi->context, &wsi->detlat); - wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); - } -#endif - -#if defined(LWS_CLIENT_HTTP_PROXYING) && \ - (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)) - - /* Decide what it is we need to connect to: - * - * Priority 1: connect to http proxy */ - - if (wsi->vhost->http.http_proxy_port) { - ads = wsi->vhost->http.http_proxy_address; - port = wsi->vhost->http.http_proxy_port; -#else - if (0) { -#endif - -#if defined(LWS_WITH_SOCKS5) - - /* Priority 2: Connect to SOCK5 Proxy */ - - } else if (wsi->vhost->socks_proxy_port) { - lwsl_client("Sending SOCKS Greeting\n"); - ads = wsi->vhost->socks_proxy_address; - port = wsi->vhost->socks_proxy_port; -#endif - } else { - - /* Priority 3: Connect directly */ - - /* ads already set */ - port = wsi->c_port; - } - - /* - * prepare the actual connection - * to whatever we decided to connect to - */ - lwsi_set_state(wsi, LRS_WAITING_DNS); - - lwsl_info("%s: %p: lookup %s:%u\n", __func__, wsi, ads, port); - (void)port; - -#if defined(LWS_WITH_DETAILED_LATENCY) - wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); -#endif -#if !defined(LWS_WITH_SYS_ASYNC_DNS) - if (wsi->dns_results) - n = 0; - else - n = lws_getaddrinfo46(wsi, ads, &result); -#else - lwsi_set_state(wsi, LRS_WAITING_DNS); - /* this is either FAILED, CONTINUING, or already called connect_4 */ - - n = lws_async_dns_query(wsi->context, wsi->tsi, ads, LWS_ADNS_RECORD_A, - lws_client_connect_3_connect, wsi, NULL); - if (n == LADNS_RET_FAILED_WSI_CLOSED) - return NULL; - - if (n == LADNS_RET_FAILED) - goto failed1; - - return wsi; -#endif - -#if defined(LWS_WITH_UNIX_SOCK) -next_step: -#endif - return lws_client_connect_3_connect(wsi, ads, result, n, NULL); - -//#if defined(LWS_WITH_SYS_ASYNC_DNS) -failed1: - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2"); - - return NULL; -//#endif -} - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - -static uint8_t hnames2[] = { - _WSI_TOKEN_CLIENT_ORIGIN, - _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - _WSI_TOKEN_CLIENT_METHOD, - _WSI_TOKEN_CLIENT_IFACE, - _WSI_TOKEN_CLIENT_ALPN -}; - -/** - * lws_client_reset() - retarget a connected wsi to start over with a new - * connection (ie, redirect) - * this only works if still in HTTP, ie, not upgraded yet - * wsi: connection to reset - * address: network address of the new server - * port: port to connect to - * path: uri path to connect to on the new server - * host: host header to send to the new server - */ -struct lws * -lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, - const char *path, const char *host, char weak) -{ -#if defined(LWS_ROLE_WS) - struct _lws_websocket_related *ws; -#endif - char *stash, *p; - struct lws *wsi; - size_t size = 0; - int n; - - if (!pwsi) - return NULL; - - wsi = *pwsi; - - lwsl_debug("%s: wsi %p: redir %d: %s\n", __func__, wsi, wsi->redirects, - address); - - if (wsi->redirects == 3) { - lwsl_err("%s: Too many redirects\n", __func__); - return NULL; - } - wsi->redirects++; - - /* - * goal is to close our role part, close the sockfd, detach the ah - * but leave our wsi extant and still bound to whatever vhost it was - */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) - size += lws_hdr_total_length(wsi, hnames2[n]) + (size_t)1; - - if (size < (size_t)lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI) + 1) - size = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI) + (size_t)1; - - /* - * The incoming address and host can be from inside the existing ah - * we are going to detach and reattch - */ - - size += strlen(path) + 1 + strlen(address) + 1 + strlen(host) + 1 + 1; - - p = stash = lws_malloc(size, __func__); - if (!stash) - return NULL; - - /* - * _WSI_TOKEN_CLIENT_ORIGIN, - * _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - * _WSI_TOKEN_CLIENT_METHOD, - * _WSI_TOKEN_CLIENT_IFACE, - * _WSI_TOKEN_CLIENT_ALPN - * address - * host - * path - */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) - if (lws_hdr_total_length(wsi, hnames2[n])) { - memcpy(p, lws_hdr_simple_ptr(wsi, hnames2[n]), (size_t)( - lws_hdr_total_length(wsi, hnames2[n]) + 1)); - p += (size_t)(lws_hdr_total_length(wsi, hnames2[n]) + 1); - } else - *p++ = '\0'; - - memcpy(p, address, strlen(address) + (size_t)1); - address = p; - p += strlen(address) + 1; - memcpy(p, host, strlen(host) + (size_t)1); - host = p; - p += strlen(host) + 1; - memcpy(p, path, strlen(path) + (size_t)1); - path = p; - - if (!port) { - lwsl_info("%s: forcing port 443\n", __func__); - - port = 443; - ssl = 1; - } - - lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d, pifds %d\n", - address, port, path, ssl, wsi->position_in_fds_table); - - __remove_wsi_socket_from_fds(wsi); -#if defined(LWS_ROLE_WS) - if (weak) { - ws = wsi->ws; - wsi->ws = NULL; - } -#endif - __lws_reset_wsi(wsi); /* detaches ah here */ -#if defined(LWS_ROLE_WS) - if (weak) - wsi->ws = ws; -#endif - wsi->client_pipeline = 1; - - /* close the connection by hand */ - -#if defined(LWS_WITH_TLS) - lws_ssl_close(wsi); -#endif - - if (wsi->role_ops && wsi->role_ops->close_kill_connection) - wsi->role_ops->close_kill_connection(wsi, 1); - - if (wsi->context->event_loop_ops->close_handle_manually) - wsi->context->event_loop_ops->close_handle_manually(wsi); - else - if (wsi->desc.sockfd != LWS_SOCK_INVALID) - compatible_close(wsi->desc.sockfd); - -#if defined(LWS_WITH_TLS) - if (!ssl) - wsi->tls.use_ssl &= ~LCCSCF_USE_SSL; - else - wsi->tls.use_ssl |= LCCSCF_USE_SSL; -#else - if (ssl) { - lwsl_err("%s: not configured for ssl\n", __func__); - goto bail; - } -#endif - - if (wsi->protocol && wsi->role_ops && wsi->protocol_bind_balance) { - wsi->protocol->callback(wsi, - wsi->role_ops->protocol_unbind_cb[ - !!lwsi_role_server(wsi)], - wsi->user_space, (void *)__func__, 0); - - wsi->protocol_bind_balance = 0; - } - - wsi->desc.sockfd = LWS_SOCK_INVALID; - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1); -// wsi->protocol = NULL; - if (wsi->protocol) - lws_bind_protocol(wsi, wsi->protocol, "client_reset"); - wsi->pending_timeout = NO_PENDING_TIMEOUT; - wsi->c_port = port; - wsi->hdr_parsing_completed = 0; - - if (lws_header_table_attach(wsi, 0)) { - lwsl_err("%s: failed to get ah\n", __func__); - goto bail; - } - //_lws_header_table_reset(wsi->http.ah); - - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address)) - goto bail; - - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host)) - goto bail; - - /* - * _WSI_TOKEN_CLIENT_ORIGIN, - * _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - * _WSI_TOKEN_CLIENT_METHOD, - * _WSI_TOKEN_CLIENT_IFACE, - * _WSI_TOKEN_CLIENT_ALPN - * address - * host - * path - */ - - p = stash; - for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) { - if (lws_hdr_simple_create(wsi, hnames2[n], p)) - goto bail; - p += lws_hdr_total_length(wsi, hnames2[n]) + (size_t)1; - } - - stash[0] = '/'; - memmove(&stash[1], path, size - 1 < strlen(path) + 1 ? - size - 1 : strlen(path) + (size_t)1); - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash)) - goto bail; - - lws_free_set_NULL(stash); - -#if defined(LWS_WITH_HTTP2) - if (wsi->client_mux_substream) - wsi->h2.END_STREAM = wsi->h2.END_HEADERS = 0; -#endif - - *pwsi = lws_client_connect_2_dnsreq(wsi); - - return *pwsi; - -bail: - lws_free_set_NULL(stash); - - return NULL; -} - -#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_WITH_HUBBUB) -hubbub_error -html_parser_cb(const hubbub_token *token, void *pw) -{ - struct lws_rewrite *r = (struct lws_rewrite *)pw; - char buf[1024], *start = buf + LWS_PRE, *p = start, - *end = &buf[sizeof(buf) - 1], dotstar[128]; - size_t i; - - switch (token->type) { - case HUBBUB_TOKEN_DOCTYPE: - - lws_strnncpy(dotstar, token->data.doctype.name.ptr, - token->data.doctype.name.len, sizeof(dotstar)); - - p += lws_snprintf(p, end - p, "data.doctype.force_quirks ? - "(force-quirks) " : ""); - - if (token->data.doctype.public_missing) - lwsl_debug("\tpublic: missing\n"); - else { - lws_strnncpy(dotstar, token->data.doctype.public_id.ptr, - token->data.doctype.public_id.len, - sizeof(dotstar)); - p += lws_snprintf(p, end - p, "PUBLIC \"%s\"\n", - dotstar); - } - - if (token->data.doctype.system_missing) - lwsl_debug("\tsystem: missing\n"); - else { - lws_strnncpy(dotstar, token->data.doctype.system_id.ptr, - token->data.doctype.system_id.len, - sizeof(dotstar)); - p += lws_snprintf(p, end - p, " \"%s\">\n", dotstar); - } - - break; - case HUBBUB_TOKEN_START_TAG: - lws_strnncpy(dotstar, token->data.tag.name.ptr, - token->data.tag.name.len, sizeof(dotstar)); - p += lws_snprintf(p, end - p, "<%s", dotstar); - -/* (token->data.tag.self_closing) ? - "(self-closing) " : "", - (token->data.tag.n_attributes > 0) ? - "attributes:" : ""); -*/ - for (i = 0; i < token->data.tag.n_attributes; i++) { - if (!hstrcmp(&token->data.tag.attributes[i].name, "href", 4) || - !hstrcmp(&token->data.tag.attributes[i].name, "action", 6) || - !hstrcmp(&token->data.tag.attributes[i].name, "src", 3)) { - const char *pp = (const char *)token->data.tag.attributes[i].value.ptr; - int plen = (int) token->data.tag.attributes[i].value.len; - - if (strncmp(pp, "http:", 5) && strncmp(pp, "https:", 6)) { - - if (!hstrcmp(&token->data.tag.attributes[i].value, - r->from, r->from_len)) { - pp += r->from_len; - plen -= r->from_len; - } - lws_strnncpy(dotstar, - token->data.tag.attributes[i].name.ptr, - token->data.tag.attributes[i].name.len, - sizeof(dotstar)); - - p += lws_snprintf(p, end - p, " %s=\"%s", - dotstar, r->to); - lws_strnncpy(dotstar, pp, plen, sizeof(dotstar)); - p += lws_snprintf(p, end - p, " /%s\"", dotstar); - continue; - } - } - - lws_strnncpy(dotstar, - token->data.tag.attributes[i].name.ptr, - token->data.tag.attributes[i].name.len, - sizeof(dotstar)); - - p += lws_snprintf(p, end - p, " %s=\"", dotstar); - lws_strnncpy(dotstar, - token->data.tag.attributes[i].value.ptr, - token->data.tag.attributes[i].value.len, - sizeof(dotstar)); - p += lws_snprintf(p, end - p, "%s\"", dotstar); - } - p += lws_snprintf(p, end - p, ">"); - break; - case HUBBUB_TOKEN_END_TAG: - lws_strnncpy(dotstar, token->data.tag.name.ptr, - token->data.tag.name.len, sizeof(dotstar)); - p += lws_snprintf(p, end - p, "data.tag.self_closing) ? - "(self-closing) " : "", - (token->data.tag.n_attributes > 0) ? - "attributes:" : ""); -*/ - for (i = 0; i < token->data.tag.n_attributes; i++) { - lws_strnncpy(dotstar, - token->data.tag.attributes[i].name.ptr, - token->data.tag.attributes[i].name.len, - sizeof(dotstar)); - p += lws_snprintf(p, end - p, " %s='", dotstar); - lws_strnncpy(dotstar, - token->data.tag.attributes[i].value.ptr, - token->data.tag.attributes[i].value.len, - sizeof(dotstar)); - p += lws_snprintf(p, end - p, "%s'\n", dotstar); - } - p += lws_snprintf(p, end - p, ">"); - break; - case HUBBUB_TOKEN_COMMENT: - lws_strnncpy(dotstar, token->data.comment.ptr, - token->data.comment.len, sizeof(dotstar)); - p += lws_snprintf(p, end - p, "\n", dotstar); - break; - case HUBBUB_TOKEN_CHARACTER: - if (token->data.character.len == 1) { - if (*token->data.character.ptr == '<') { - p += lws_snprintf(p, end - p, "<"); - break; - } - if (*token->data.character.ptr == '>') { - p += lws_snprintf(p, end - p, ">"); - break; - } - if (*token->data.character.ptr == '&') { - p += lws_snprintf(p, end - p, "&"); - break; - } - } - lws_strnncpy(dotstar, token->data.character.ptr, - token->data.character.len, sizeof(dotstar)); - p += lws_snprintf(p, end - p, "%s", dotstar); - break; - case HUBBUB_TOKEN_EOF: - p += lws_snprintf(p, end - p, "\n"); - break; - } - - if (r->wsi->protocol_bind_balance && - user_callback_handle_rxflow(r->wsi->protocol->callback, - r->wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, - r->wsi->user_space, start, p - start)) - return -1; - - return HUBBUB_OK; -} -#endif - -#endif - -static const uint8_t hnames[] = { - _WSI_TOKEN_CLIENT_PEER_ADDRESS, - _WSI_TOKEN_CLIENT_URI, - _WSI_TOKEN_CLIENT_HOST, - _WSI_TOKEN_CLIENT_ORIGIN, - _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - _WSI_TOKEN_CLIENT_METHOD, - _WSI_TOKEN_CLIENT_IFACE, - _WSI_TOKEN_CLIENT_ALPN -}; - -struct lws * -lws_http_client_connect_via_info2(struct lws *wsi) -{ - struct client_info_stash *stash = wsi->stash; - int n; - - lwsl_debug("%s: %p (stash %p)\n", __func__, wsi, stash); - - if (!stash) - return wsi; - - wsi->opaque_user_data = wsi->stash->opaque_user_data; - - if (stash->cis[CIS_METHOD] && (!strcmp(stash->cis[CIS_METHOD], "RAW") || - !strcmp(stash->cis[CIS_METHOD], "MQTT"))) - goto no_ah; - - /* - * we're not necessarily in a position to action these right away, - * stash them... we only need during connect phase so into a temp - * allocated stash - */ - for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames); n++) - if (hnames[n] && stash->cis[n]) { - if (lws_hdr_simple_create(wsi, hnames[n], stash->cis[n])) - goto bail1; - } - -#if defined(LWS_WITH_SOCKS5) - if (!wsi->vhost->socks_proxy_port) - lws_free_set_NULL(wsi->stash); -#endif - -no_ah: - wsi->context->count_wsi_allocated++; - - return lws_client_connect_2_dnsreq(wsi); - -bail1: -#if defined(LWS_WITH_SOCKS5) - if (!wsi->vhost->socks_proxy_port) - lws_free_set_NULL(wsi->stash); -#endif - - return NULL; -} diff -Nru libwebsockets-4.0.20/lib/roles/http/client/client-http.c libwebsockets-2.4.2/lib/roles/http/client/client-http.c --- libwebsockets-4.0.20/lib/roles/http/client/client-http.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/client/client-http.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1450 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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_TLS) -int -lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1) -{ - int n; - - /* we can retry this... just cook the SSL BIO the first time */ - - if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { - - if (!wsi->tls.ssl) { - if (lws_ssl_client_bio_create(wsi) < 0) { - *pcce = "bio_create failed"; - return -1; - } - - if (!wsi->transaction_from_pipeline_queue && - lws_tls_restrict_borrow(wsi->context)) { - *pcce = "tls restriction limit"; - return -1; - } - } - - if (!do_c1) - return 0; - - n = lws_ssl_client_connect1(wsi); - if (!n) - return 1; /* caller should return 0 */ - if (n < 0) { - *pcce = "lws_ssl_client_connect1 failed"; - return -1; - } - } else - wsi->tls.ssl = NULL; - -#if defined (LWS_WITH_HTTP2) - if (wsi->client_h2_alpn) { - /* - * We connected to the server and set up tls, and - * negotiated "h2". - * - * So this is it, we are an h2 master client connection - * now, not an h1 client connection. - */ -#if defined(LWS_WITH_TLS) - lws_tls_server_conn_alpn(wsi); -#endif - - /* send the H2 preface to legitimize the connection */ - if (lws_h2_issue_preface(wsi)) { - *pcce = "error sending h2 preface"; - return -1; - } - } -#endif - - return 0; /* OK */ -} - -#endif - -void -lws_client_http_body_pending(struct lws *wsi, int something_left_to_send) -{ - wsi->client_http_body_pending = !!something_left_to_send; -} - -int -lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - char *p = (char *)&pt->serv_buf[0]; -#if defined(LWS_WITH_TLS) - char ebuf[128]; -#endif - const char *cce = NULL; - char *sb = p; - int n = 0; - - switch (lwsi_state(wsi)) { - - case LRS_WAITING_DNS: - /* - * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE - * timeout protection set in client-handshake.c - */ - lwsl_err("%s: wsi %p: WAITING_DNS\n", __func__, wsi); - 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_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: - goto start_ws_handshake; - default: - break; - } - break; -#endif - -#if defined(LWS_CLIENT_HTTP_PROXYING) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)) - - case LRS_WAITING_PROXY_REPLY: - - /* handle proxy hung up on us */ - - if (pollfd->revents & LWS_POLLHUP) { - - lwsl_warn("Proxy connection %p (fd=%d) dead\n", - (void *)wsi, pollfd->fd); - - cce = "proxy conn dead"; - goto bail3; - } - - n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); - if (n < 0) { - if (LWS_ERRNO == LWS_EAGAIN) { - lwsl_debug("Proxy read EAGAIN... retrying\n"); - return 0; - } - lwsl_err("ERROR reading from proxy socket\n"); - cce = "proxy read err"; - goto bail3; - } - - pt->serv_buf[13] = '\0'; - if (n < 13 || (strncmp(sb, "HTTP/1.0 200 ", 13) && - strncmp(sb, "HTTP/1.1 200 ", 13))) { - lwsl_err("%s: ERROR proxy did not reply with h1\n", - __func__); - /* lwsl_hexdump_notice(sb, n); */ - cce = "proxy not h1"; - goto bail3; - } - - lwsl_info("%s: proxy connection extablished\n", __func__); - - /* clear his proxy connection timeout */ - - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - /* fallthru */ - -#endif - - case LRS_H1C_ISSUE_HANDSHAKE: - - /* - * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE - * timeout protection set in client-handshake.c - * - * take care of our lws_callback_on_writable - * happening at a time when there's no real connection yet - */ -#if defined(LWS_WITH_SOCKS5) -start_ws_handshake: -#endif - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) - return -1; - -#if defined(LWS_WITH_TLS) - n = lws_client_create_tls(wsi, &cce, 1); - if (n < 0) - goto bail3; - if (n == 1) - return 0; - - /* fallthru */ - - case LRS_WAITING_SSL: - - if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { - n = lws_ssl_client_connect2(wsi, ebuf, sizeof(ebuf)); - if (!n) - return 0; - if (n < 0) { - cce = ebuf; - goto bail3; - } - } else - wsi->tls.ssl = NULL; -#endif -#if defined(LWS_WITH_DETAILED_LATENCY) - if (context->detailed_latency_cb) { - wsi->detlat.type = LDLT_TLS_NEG_CLIENT; - wsi->detlat.latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE] = - lws_now_usecs() - - wsi->detlat.earliest_write_req_pre_write; - wsi->detlat.latencies[LAT_DUR_USERCB] = 0; - lws_det_lat_cb(wsi->context, &wsi->detlat); - } -#endif -#if defined (LWS_WITH_HTTP2) - if (wsi->client_h2_alpn) { - /* - * We connected to the server and set up tls, and - * negotiated "h2". - * - * So this is it, we are an h2 master client connection - * now, not an h1 client connection. - */ -#if defined(LWS_WITH_TLS) - lws_tls_server_conn_alpn(wsi); -#endif - - /* send the H2 preface to legitimize the connection */ - if (lws_h2_issue_preface(wsi)) { - cce = "error sending h2 preface"; - goto bail3; - } - - // lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, - context->timeout_secs); - - break; - } -#endif - - /* fallthru */ - - case LRS_H1C_ISSUE_HANDSHAKE2: - p = lws_generate_client_handshake(wsi, p); - if (p == NULL) { - if (wsi->role_ops == &role_ops_raw_skt -#if defined(LWS_ROLE_RAW_FILE) - || wsi->role_ops == &role_ops_raw_file -#endif - ) - return 0; - - lwsl_err("Failed to generate handshake for client\n"); - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "chs"); - return 0; - } - - /* send our request to the server */ - - lwsl_info("%s: HANDSHAKE2: %p: sending headers " - "(wsistate 0x%lx), w sock %d\n", - __func__, wsi, (unsigned long)wsi->wsistate, - wsi->desc.sockfd); -#if defined(LWS_WITH_DETAILED_LATENCY) - wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); -#endif - n = lws_ssl_capable_write(wsi, (unsigned char *)sb, (int)(p - sb)); - switch (n) { - case LWS_SSL_CAPABLE_ERROR: - lwsl_debug("ERROR writing to client socket\n"); - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "cws"); - return 0; - case LWS_SSL_CAPABLE_MORE_SERVICE: - lws_callback_on_writable(wsi); - break; - } - - if (wsi->client_http_body_pending) { - lwsl_debug("body pending\n"); - lwsi_set_state(wsi, LRS_ISSUE_HTTP_BODY); - lws_set_timeout(wsi, - PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, - context->timeout_secs); - - if (wsi->flags & LCCSCF_HTTP_X_WWW_FORM_URLENCODED) - lws_callback_on_writable(wsi); -#if defined(LWS_WITH_HTTP_PROXY) - if (wsi->http.proxy_clientside) - lws_callback_on_writable(wsi); -#endif - /* user code must ask for writable callback */ - break; - } - - lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY); - wsi->hdr_parsing_completed = 0; - - if (lwsi_state(wsi) == LRS_IDLING) { - lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY); - wsi->hdr_parsing_completed = 0; -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART; - wsi->http.ah->lextable_pos = 0; - wsi->http.ah->unk_pos = 0; - /* If we're (re)starting on hdr, need other implied init */ - wsi->http.ah->ues = URIES_IDLE; -#endif - } - - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, - wsi->context->timeout_secs); - - lws_callback_on_writable(wsi); - - goto client_http_body_sent; - - case LRS_ISSUE_HTTP_BODY: -#if defined(LWS_WITH_HTTP_PROXY) - if (wsi->http.proxy_clientside) { - lws_callback_on_writable(wsi); - break; - } -#endif - if (wsi->client_http_body_pending) { - //lws_set_timeout(wsi, - // PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, - // context->timeout_secs); - /* user code must ask for writable callback */ - break; - } -client_http_body_sent: -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - /* prepare ourselves to do the parsing */ - wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART; - wsi->http.ah->lextable_pos = 0; - wsi->http.ah->unk_pos = 0; -#endif - lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY); - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, - context->timeout_secs); - break; - - case LRS_WAITING_SERVER_REPLY: - /* - * handle server hanging up on us... - * but if there is POLLIN waiting, handle that first - */ - if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) == - LWS_POLLHUP) { - - lwsl_debug("Server connection %p (fd=%d) dead\n", - (void *)wsi, pollfd->fd); - cce = "Peer hung up"; - goto bail3; - } - - if (!(pollfd->revents & LWS_POLLIN)) - break; - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - /* interpret the server response - * - * HTTP/1.1 101 Switching Protocols - * Upgrade: websocket - * Connection: Upgrade - * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= - * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== - * Sec-WebSocket-Protocol: chat - * - * we have to take some care here to only take from the - * socket bytewise. The browser may (and has been seen to - * in the case that onopen() performs websocket traffic) - * coalesce both handshake response and websocket traffic - * in one packet, since at that point the connection is - * definitively ready from browser pov. - */ - while (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE) { - struct lws_tokens eb; - int n, m, buffered; - - eb.token = NULL; - eb.len = 0; - buffered = lws_buflist_aware_read(pt, wsi, &eb, 0, __func__); - lwsl_debug("%s: buflist-aware-read %d %d\n", __func__, - buffered, eb.len); - if (eb.len == LWS_SSL_CAPABLE_MORE_SERVICE) - return 0; - if (buffered < 0 || eb.len < 0) { - cce = "read failed"; - goto bail3; - } - if (!eb.len) - return 0; - - n = eb.len; - if (lws_parse(wsi, eb.token, &n)) { - lwsl_warn("problems parsing header\n"); - cce = "problems parsing header"; - goto bail3; - } - - m = eb.len - n; - if (lws_buflist_aware_finished_consuming(wsi, &eb, m, - buffered, - __func__)) - return -1; - eb.token += m; - eb.len -= m; - - if (n) { - assert(wsi->http.ah->parser_state == - WSI_PARSING_COMPLETE); - - break; - } - } - - /* - * hs may also be coming in multiple packets, there is a 5-sec - * libwebsocket timeout still active here too, so if parsing did - * not complete just wait for next packet coming in this state - */ - if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE) - break; -#endif - - /* - * otherwise deal with the handshake. If there's any - * packet traffic already arrived we'll trigger poll() again - * right away and deal with it that way - */ - return lws_client_interpret_server_handshake(wsi); - -bail3: - 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; -} - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - -int LWS_WARN_UNUSED_RESULT -lws_http_transaction_completed_client(struct lws *wsi) -{ - int n; - - lwsl_info("%s: wsi: %p (%s)\n", __func__, wsi, wsi->protocol->name); - - if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_COMPLETED_CLIENT_HTTP, - wsi->user_space, NULL, 0)) { - lwsl_debug("%s: Completed call returned nonzero (role 0x%lx)\n", - __func__, (unsigned long)lwsi_role(wsi)); - return -1; - } - - wsi->http.rx_content_length = 0; - - /* - * For h1, wsi may pass some assets on to a queued child and be - * destroyed during this. - */ - n = _lws_generic_transaction_completed_active_conn(&wsi); - - if (wsi->http.ah) { - if (wsi->client_mux_substream) - /* - * As an h2 client, once we did our transaction, that is - * it for us. Further transactions will happen as new - * SIDs on the connection. - */ - __lws_header_table_detach(wsi, 0); - else - if (!n) - _lws_header_table_reset(wsi->http.ah); - } - - if (!n || !wsi->http.ah) - return 0; - - /* - * H1: we can serialize the queued guys into the same ah - * H2: everybody needs their own ah until their own STREAM_END - */ - - /* otherwise set ourselves up ready to go again */ - lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY); - - wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART; - wsi->http.ah->lextable_pos = 0; - wsi->http.ah->unk_pos = 0; - - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, - wsi->context->timeout_secs); - - /* If we're (re)starting on headers, need other implied init */ - wsi->http.ah->ues = URIES_IDLE; - lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); - - lwsl_info("%s: %p: new queued transaction\n", __func__, wsi); - lws_callback_on_writable(wsi); - - return 0; -} - -unsigned int -lws_http_client_http_response(struct lws *wsi) -{ - if (wsi->http.ah && wsi->http.ah->http_response) - return wsi->http.ah->http_response; - - return 0; -} -#endif - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - -int -lws_http_is_redirected_to_get(struct lws *wsi) -{ - return wsi->redirected_to_get; -} - -int -lws_client_interpret_server_handshake(struct lws *wsi) -{ - int n, port = 0, ssl = 0; - int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR; - const char *prot, *ads = NULL, *path, *cce = NULL; - struct allocated_headers *ah, *ah1; - struct lws *nwsi = lws_get_network_wsi(wsi); - char *p = NULL, *q; - char new_path[300]; - - lws_free_set_NULL(wsi->stash); - - ah = wsi->http.ah; - if (!wsi->do_ws) { - /* we are being an http client... - */ -#if defined(LWS_ROLE_H2) - if (wsi->client_h2_alpn || wsi->client_mux_substream) { - lwsl_debug("%s: %p: transitioning to h2 client\n", - __func__, wsi); - lws_role_transition(wsi, LWSIFR_CLIENT, - LRS_ESTABLISHED, &role_ops_h2); - } else -#endif - { -#if defined(LWS_ROLE_H1) - { - lwsl_debug("%s: %p: transitioning to h1 client\n", - __func__, wsi); - lws_role_transition(wsi, LWSIFR_CLIENT, - LRS_ESTABLISHED, &role_ops_h1); - } -#else - return -1; -#endif - } - - wsi->http.ah = ah; - ah->http_response = 0; - } - - /* - * well, what the server sent looked reasonable for syntax. - * Now let's confirm it sent all the necessary headers - * - * http (non-ws) client will expect something like this - * - * HTTP/1.0.200 - * server:.libwebsockets - * content-type:.text/html - * content-length:.17703 - * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000 - */ - - wsi->http.conn_type = HTTP_CONNECTION_KEEP_ALIVE; - if (!wsi->client_mux_substream) { - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP); - /* - if (wsi->do_ws && !p) { - lwsl_info("no URI\n"); - cce = "HS: URI missing"; - goto bail3; - } - */ - if (!p) { - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0); - wsi->http.conn_type = HTTP_CONNECTION_CLOSE; - } - if (!p) { - cce = "HS: URI missing"; - lwsl_info("no URI\n"); - goto bail3; - } -#if defined(LWS_ROLE_H2) - } else { - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_STATUS); - if (!p) { - cce = "HS: :status missing"; - lwsl_info("no status\n"); - goto bail3; - } -#endif - } -#if !defined(LWS_ROLE_H2) - if (!p) { - cce = "HS: :status missing"; - lwsl_info("no status\n"); - goto bail3; - } -#endif - n = atoi(p); - if (ah) - ah->http_response = n; - - if (!wsi->client_no_follow_redirect && -#if defined(LWS_WITH_HTTP_PROXY) - !wsi->http.proxy_clientside && -#endif - (n == 301 || n == 302 || n == 303 || n == 307 || n == 308)) { - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION); - if (!p) { - cce = "HS: Redirect code but no Location"; - goto bail3; - } - - /* - * Some redirect codes imply we have to change the method - * used for the subsequent transaction, commonly POST -> - * 303 -> GET. - */ - - if (n == 303) { - char *mp = lws_hdr_simple_ptr(wsi,_WSI_TOKEN_CLIENT_METHOD); - int ml = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_METHOD); - - if (ml >= 3 && mp) { - lwsl_info("%s: 303 switching to GET\n", __func__); - memcpy(mp, "GET", 4); - wsi->redirected_to_get = 1; - wsi->http.ah->frags[wsi->http.ah->frag_index[ - _WSI_TOKEN_CLIENT_METHOD]].len = 3; - } - } - - /* Relative reference absolute path */ - if (p[0] == '/' || !strchr(p, ':')) { -#if defined(LWS_WITH_TLS) - ssl = nwsi->tls.use_ssl & LCCSCF_USE_SSL; -#endif - ads = lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_PEER_ADDRESS); - port = nwsi->c_port; - path = p; - /* lws_client_reset expects leading / omitted */ - if (*path == '/') - path++; - } - /* Absolute (Full) URI */ - else if (strchr(p, ':')) { - if (lws_parse_uri(p, &prot, &ads, &port, &path)) { - cce = "HS: URI did not parse"; - goto bail3; - } - - if (!strcmp(prot, "wss") || !strcmp(prot, "https")) - ssl = LCCSCF_USE_SSL; - } - /* Relative reference relative path */ - else { - /* This doesn't try to calculate an absolute path, - * that will be left to the server */ -#if defined(LWS_WITH_TLS) - ssl = nwsi->tls.use_ssl & LCCSCF_USE_SSL; -#endif - ads = lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_PEER_ADDRESS); - port = wsi->c_port; - /* +1 as lws_client_reset expects leading / omitted */ - path = new_path + 1; - if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI)) - lws_strncpy(new_path, lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_URI), sizeof(new_path)); - else { - new_path[0] = '/'; - new_path[1] = '\0'; - } - q = strrchr(new_path, '/'); - if (q) - lws_strncpy(q + 1, p, sizeof(new_path) - - (q - new_path) - 1); - else - path = p; - } - -#if defined(LWS_WITH_TLS) - if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && !ssl) { - cce = "HS: Redirect attempted SSL downgrade"; - goto bail3; - } -#endif - - if (!ads) /* make coverity happy */ { - cce = "no ads"; - goto bail3; - } - - if (!lws_client_reset(&wsi, ssl, ads, port, path, ads, 1)) { - /* - * There are two ways to fail out with NULL return... - * simple, early problem where the wsi is intact, or - * we went through with the reconnect attempt and the - * wsi is already closed. In the latter case, the wsi - * has been set to NULL additionally. - */ - lwsl_err("Redirect failed\n"); - cce = "HS: Redirect failed"; - /* coverity[reverse_inull] */ - if (wsi) - goto bail3; - - /* wsi has closed */ - return 1; - } - return 0; - } - - if (!wsi->do_ws) { - - /* if h1 KA is allowed, enable the queued pipeline guys */ - - if (!wsi->client_h2_alpn && !wsi->client_mux_substream) { - /* ie, coming to this for the first time */ - if (wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE) - wsi->keepalive_active = 1; - else { - /* - * Ugh... now the main http connection has seen - * both sides, we learn the server doesn't - * support keepalive. - * - * That means any guys queued on us are going - * to have to be restarted from connect2 with - * their own connections. - */ - - /* - * stick around telling any new guys they can't - * pipeline to this server - */ - wsi->keepalive_rejected = 1; - - lws_vhost_lock(wsi->vhost); - lws_start_foreach_dll_safe(struct lws_dll2 *, - d, d1, - wsi->dll2_cli_txn_queue_owner.head) { - struct lws *ww = lws_container_of(d, - struct lws, - dll2_cli_txn_queue); - - /* remove him from our queue */ - lws_dll2_remove(&ww->dll2_cli_txn_queue); - /* give up on pipelining */ - ww->client_pipeline = 0; - - /* go back to "trying to connect" state */ - lws_role_transition(ww, LWSIFR_CLIENT, - LRS_UNCONNECTED, -#if defined(LWS_ROLE_H1) - &role_ops_h1); -#else -#if defined (LWS_ROLE_H2) - &role_ops_h2); -#else - &role_ops_raw); -#endif -#endif - ww->user_space = NULL; - } lws_end_foreach_dll_safe(d, d1); - lws_vhost_unlock(wsi->vhost); - } - } - -#ifdef LWS_WITH_HTTP_PROXY - wsi->http.perform_rewrite = 0; - if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) { - if (!strncmp(lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_CONTENT_TYPE), - "text/html", 9)) - wsi->http.perform_rewrite = 0; - } -#endif - - /* allocate the per-connection user memory (if any) */ - if (lws_ensure_user_space(wsi)) { - lwsl_err("Problem allocating wsi user mem\n"); - cce = "HS: OOM"; - goto bail2; - } - - /* he may choose to send us stuff in chunked transfer-coding */ - wsi->chunked = 0; - wsi->chunk_remaining = 0; /* ie, next thing is chunk size */ - if (lws_hdr_total_length(wsi, - WSI_TOKEN_HTTP_TRANSFER_ENCODING)) { - wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_TRANSFER_ENCODING), - "chunked"); - /* first thing is hex, after payload there is crlf */ - wsi->chunk_parser = ELCP_HEX; - } - - wsi->http.content_length_given = 0; - if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { - wsi->http.rx_content_length = - atoll(lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_CONTENT_LENGTH)); - lwsl_info("%s: incoming content length %llu\n", - __func__, (unsigned long long) - wsi->http.rx_content_length); - wsi->http.rx_content_remain = - wsi->http.rx_content_length; - wsi->http.content_length_given = 1; - } else { /* can't do 1.1 without a content length or chunked */ - if (!wsi->chunked) - wsi->http.conn_type = HTTP_CONNECTION_CLOSE; - lwsl_debug("%s: no content length\n", __func__); - } - - /* - * we seem to be good to go, give client last chance to check - * headers and OK it - */ - ah1 = wsi->http.ah; - wsi->http.ah = ah; - if (wsi->protocol->callback(wsi, - LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, - wsi->user_space, NULL, 0)) { - wsi->http.ah = ah1; - cce = "HS: disallowed by client filter"; - goto bail2; - } - - /* clear his proxy connection timeout */ - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; - - /* call him back to inform him he is up */ - if (wsi->protocol->callback(wsi, - LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP, - wsi->user_space, NULL, 0)) { - wsi->http.ah = ah1; - cce = "HS: disallowed at ESTABLISHED"; - goto bail3; - } - - wsi->http.ah = ah1; - - lwsl_info("%s: wsi %p: client connection up\n", __func__, wsi); - - /* - * Did we get a response from the server with an explicit - * content-length of zero? If so, this transaction is already - * completed at the end of the header processing... - */ - if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) && - !wsi->http.rx_content_length) - return !!lws_http_transaction_completed_client(wsi); - - /* - * We can also get a case where it's http/1 and there's no - * content-length at all, so anything that comes is the body - * until it hangs up on us. With that situation, hanging up - * on us past this point should generate a valid - * LWS_CALLBACK_COMPLETED_CLIENT_HTTP. - * - * In that situation, he can't pipeline because in h1 there's - * no post-header in-band way to signal the end of the - * transaction except hangup. - * - * lws_http_transaction_completed_client() is the right guy to - * issue it when we see the peer has hung up on us. - */ - - return 0; - } - -#if defined(LWS_ROLE_WS) - switch (lws_client_ws_upgrade(wsi, &cce)) { - case 2: - goto bail2; - case 3: - goto bail3; - } - - return 0; -#endif - -bail3: - close_reason = LWS_CLOSE_STATUS_NOSTATUS; - -bail2: - if (wsi->protocol) { - n = 0; - if (cce) - n = (int)strlen(cce); - - lws_inform_client_conn_fail(wsi, (void *)cce, (unsigned int)n); - } - - lwsl_info("closing connection (prot %s) " - "due to bail2 connection error: %s\n", wsi->protocol ? - wsi->protocol->name : "unknown", cce); - - /* closing will free up his parsing allocations */ - lws_close_free_wsi(wsi, close_reason, "c hs interp"); - - return 1; -} -#endif - -/* - * set the boundary string and the content-type for client multipart mime - */ - -uint8_t * -lws_http_multipart_headers(struct lws *wsi, uint8_t *p) -{ - char buf[10], arg[48]; - int n; - - lws_get_random(wsi->context, (uint8_t *)buf, sizeof(buf)); - lws_b64_encode_string(buf, sizeof(buf), - wsi->http.multipart_boundary, - sizeof(wsi->http.multipart_boundary)); - - n = lws_snprintf(arg, sizeof(arg), "multipart/form-data; boundary=\"%s\"", - wsi->http.multipart_boundary); - - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, - (uint8_t *)arg, n, &p, p + 100)) - return NULL; - - wsi->http.multipart = wsi->http.multipart_issue_boundary = 1; - lws_client_http_body_pending(wsi, 1); - - return p; -} - -int -lws_client_http_multipart(struct lws *wsi, const char *name, - const char *filename, const char *content_type, - char **p, char *end) -{ - /* - * Client conn must have been created with LCCSCF_HTTP_MULTIPART_MIME - * flag to use this api - */ - assert(wsi->http.multipart); - - if (!name) { - *p += lws_snprintf((char *)(*p), lws_ptr_diff(end, p), - "\xd\xa--%s--\xd\xa", - wsi->http.multipart_boundary); - - return 0; - } - - if (wsi->client_subsequent_mime_part) - *p += lws_snprintf((char *)(*p), lws_ptr_diff(end, p), "\xd\xa"); - wsi->client_subsequent_mime_part = 1; - - *p += lws_snprintf((char *)(*p), lws_ptr_diff(end, p), "--%s\xd\xa" - "Content-Disposition: form-data; " - "name=\"%s\"", - wsi->http.multipart_boundary, name); - if (filename) - *p += lws_snprintf((char *)(*p), lws_ptr_diff(end, p), - "; filename=\"%s\"", filename); - - if (content_type) - *p += lws_snprintf((char *)(*p), lws_ptr_diff(end, p), "\xd\xa" - "Content-Type: %s", content_type); - - *p += lws_snprintf((char *)(*p), lws_ptr_diff(end, p), "\xd\xa\xd\xa"); - - return *p == end; -} - -char * -lws_generate_client_handshake(struct lws *wsi, char *pkt) -{ - const char *meth, *pp = lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); - char *p = pkt, *p1; - - meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); - if (!meth) { - meth = "GET"; - wsi->do_ws = 1; - } else { - wsi->do_ws = 0; - } - - if (!strcmp(meth, "RAW")) { - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - lwsl_notice("client transition to raw\n"); - - if (pp) { - const struct lws_protocols *pr; - - pr = lws_vhost_name_to_protocol(wsi->vhost, pp); - - if (!pr) { - lwsl_err("protocol %s not enabled on vhost\n", - pp); - return NULL; - } - - lws_bind_protocol(wsi, pr, __func__); - } - - if ((wsi->protocol->callback)(wsi, LWS_CALLBACK_RAW_ADOPT, - wsi->user_space, NULL, 0)) - return NULL; - - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED, - &role_ops_raw_skt); - lws_header_table_detach(wsi, 1); - - return NULL; - } - - /* - * 04 example client handshake - * - * GET /chat HTTP/1.1 - * Host: server.example.com - * Upgrade: websocket - * Connection: Upgrade - * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== - * Sec-WebSocket-Origin: http://example.com - * Sec-WebSocket-Protocol: chat, superchat - * Sec-WebSocket-Version: 4 - */ - - p += lws_snprintf(p, 2048, "%s %s HTTP/1.1\x0d\x0a", meth, - lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI)); - - p += lws_snprintf(p, 64, "Pragma: no-cache\x0d\x0a" - "Cache-Control: no-cache\x0d\x0a"); - - p += lws_snprintf(p, 128, "Host: %s\x0d\x0a", - lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST)); - - if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) { - if (lws_check_opt(wsi->context->options, - LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN)) - p += lws_snprintf(p, 128, "Origin: %s\x0d\x0a", - lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_ORIGIN)); - else - p += lws_snprintf(p, 128, "Origin: http://%s\x0d\x0a", - lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_ORIGIN)); - } - - if (wsi->flags & LCCSCF_HTTP_MULTIPART_MIME) { - p1 = (char *)lws_http_multipart_headers(wsi, (uint8_t *)p); - if (!p1) - return NULL; - p = p1; - } - -#if defined(LWS_WITH_HTTP_PROXY) - if (wsi->parent && - lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { - p += lws_snprintf(p, 128, "Content-Length: %s\x0d\x0a", - lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)); - if (atoi(lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH))) - wsi->client_http_body_pending = 1; - } - if (wsi->parent && - lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_AUTHORIZATION)) { - p += lws_snprintf(p, 128, "Authorization: %s\x0d\x0a", - lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_AUTHORIZATION)); - } - if (wsi->parent && - lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE)) { - p += lws_snprintf(p, 128, "Content-Type: %s\x0d\x0a", - lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE)); - } -#endif - -#if defined(LWS_ROLE_WS) - if (wsi->do_ws) { - const char *conn1 = ""; - // if (!wsi->client_pipeline) - // conn1 = "close, "; - p = lws_generate_client_ws_handshake(wsi, p, conn1); - } else -#endif - { - if (!wsi->client_pipeline) - p += lws_snprintf(p, 64, "connection: close\x0d\x0a"); - } - - /* give userland a chance to append, eg, cookies */ - - if (wsi->protocol->callback(wsi, - LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, - wsi->user_space, &p, - (pkt + wsi->context->pt_serv_buf_size) - p - 12)) - return NULL; - - if (wsi->flags & LCCSCF_HTTP_X_WWW_FORM_URLENCODED) { - p += lws_snprintf(p, 128, "Content-Type: application/x-www-form-urlencoded\x0d\x0a"); - p += lws_snprintf(p, 128, "Content-Length: %lu\x0d\x0a", wsi->http.writeable_len); - lws_client_http_body_pending(wsi, 1); - } - - p += lws_snprintf(p, 4, "\x0d\x0a"); - - if (wsi->client_http_body_pending) - lws_callback_on_writable(wsi); - - // puts(pkt); - - return p; -} - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) -#if defined(LWS_WITH_HTTP_BASIC_AUTH) - -int -lws_http_basic_auth_gen(const char *user, const char *pw, char *buf, size_t len) -{ - size_t n = strlen(user), m = strlen(pw); - char b[128]; - - if (len < 6 + ((4 * (n + m + 1)) / 3) + 1) - return 1; - - memcpy(buf, "Basic ", 6); - - n = lws_snprintf(b, sizeof(b), "%s:%s", user, pw); - if (n >= sizeof(b) - 2) - return 2; - - lws_b64_encode_string(b, (int)n, buf + 6, (int)len - 6); - buf[len - 1] = '\0'; - - return 0; -} - -#endif - -int -lws_http_client_read(struct lws *wsi, char **buf, int *len) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct lws_tokens eb; - int buffered, n, consumed = 0; - - /* - * If the caller provided a non-NULL *buf and nonzero *len, we should - * use that as the buffer for the read action, limititing it to *len - * (actual payload will be less if chunked headers inside). - * - * If it's NULL / 0 length, buflist_aware_read will use the pt_serv_buf - */ - - eb.token = (unsigned char *)*buf; - eb.len = *len; - - buffered = lws_buflist_aware_read(pt, wsi, &eb, 0, __func__); - *buf = (char *)eb.token; /* may be pointing to buflist or pt_serv_buf */ - *len = 0; - - /* - * we're taking on responsibility for handling used / unused eb - * when we leave, via lws_buflist_aware_finished_consuming() - */ - -// lwsl_notice("%s: eb.len %d ENTRY chunk remaining %d\n", __func__, eb.len, -// wsi->chunk_remaining); - - /* allow the source to signal he has data again next time */ - if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) - return -1; - - if (buffered < 0) { - lwsl_debug("%s: SSL capable error\n", __func__); - - if (wsi->http.ah && - wsi->http.ah->parser_state == WSI_PARSING_COMPLETE && - !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) - /* - * We had the headers from this stream, but as there - * was no content-length: we had to wait until the - * stream ended to inform the user code the transaction - * has completed to the best of our knowledge - */ - if (lws_http_transaction_completed_client(wsi)) - /* - * We're going to close anyway, but that api has - * warn_unused_result - */ - return -1; - - return -1; - } - - if (eb.len <= 0) - return 0; - - *len = eb.len; - wsi->client_rx_avail = 0; - - /* - * server may insist on transfer-encoding: chunked, - * so http client must deal with it - */ -spin_chunks: - //lwsl_notice("%s: len %d SPIN chunk remaining %d\n", __func__, *len, - // wsi->chunk_remaining); - while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) { - switch (wsi->chunk_parser) { - case ELCP_HEX: - if ((*buf)[0] == '\x0d') { - wsi->chunk_parser = ELCP_CR; - break; - } - n = char_to_hex((*buf)[0]); - if (n < 0) { - lwsl_err("%s: chunking failure A\n", __func__); - return -1; - } - wsi->chunk_remaining <<= 4; - wsi->chunk_remaining |= n; - break; - case ELCP_CR: - if ((*buf)[0] != '\x0a') { - lwsl_err("%s: chunking failure B\n", __func__); - return -1; - } - if (wsi->chunk_remaining) { - wsi->chunk_parser = ELCP_CONTENT; - //lwsl_notice("starting chunk size %d (block rem %d)\n", - // wsi->chunk_remaining, *len); - break; - } - - wsi->chunk_parser = ELCP_TRAILER_CR; - break; - - case ELCP_CONTENT: - break; - - case ELCP_POST_CR: - if ((*buf)[0] != '\x0d') { - lwsl_err("%s: chunking failure C\n", __func__); - lwsl_hexdump_err(*buf, *len); - - return -1; - } - - wsi->chunk_parser = ELCP_POST_LF; - break; - - case ELCP_POST_LF: - if ((*buf)[0] != '\x0a') { - lwsl_err("%s: chunking failure D\n", __func__); - - return -1; - } - - wsi->chunk_parser = ELCP_HEX; - wsi->chunk_remaining = 0; - break; - - case ELCP_TRAILER_CR: - if ((*buf)[0] != '\x0d') { - lwsl_err("%s: chunking failure F\n", __func__); - lwsl_hexdump_err(*buf, *len); - - return -1; - } - - wsi->chunk_parser = ELCP_TRAILER_LF; - break; - - case ELCP_TRAILER_LF: - if ((*buf)[0] != '\x0a') { - lwsl_err("%s: chunking failure F\n", __func__); - lwsl_hexdump_err(*buf, *len); - - return -1; - } - - (*buf)++; - (*len)--; - consumed++; - - lwsl_info("final chunk\n"); - goto completed; - } - (*buf)++; - (*len)--; - consumed++; - } - - if (wsi->chunked && !wsi->chunk_remaining) - goto account_and_ret; - - if (wsi->http.rx_content_remain && - wsi->http.rx_content_remain < (unsigned int)*len) - n = (int)wsi->http.rx_content_remain; - else - n = *len; - - if (wsi->chunked && wsi->chunk_remaining && - wsi->chunk_remaining < n) - n = wsi->chunk_remaining; - -#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_WITH_HUBBUB) - /* hubbub */ - if (wsi->http.perform_rewrite) - lws_rewrite_parse(wsi->http.rw, (unsigned char *)*buf, n); - else -#endif - { - if ( -#if defined(LWS_WITH_HTTP_PROXY) - !wsi->protocol_bind_balance == - !!wsi->http.proxy_clientside -#else - !!wsi->protocol_bind_balance -#endif - ) { - if (user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, - wsi->user_space, *buf, n)) { - lwsl_info("%s: RECEIVE_CLIENT_HTTP_READ returned -1\n", - __func__); - - return -1; - } - } else - lwsl_notice("%s: swallowed read (%d)\n", __func__, n); - } - - (*buf) += n; - *len -= n; - if (wsi->chunked && wsi->chunk_remaining) - wsi->chunk_remaining -= n; - - //lwsl_notice("chunk_remaining <- %d, block remaining %d\n", - // wsi->chunk_remaining, *len); - - consumed += n; - //eb.token += n; - //eb.len -= n; - - if (wsi->chunked && !wsi->chunk_remaining) - wsi->chunk_parser = ELCP_POST_CR; - - if (wsi->chunked && *len) - goto spin_chunks; - - if (wsi->chunked) - goto account_and_ret; - - /* if we know the content length, decrement the content remaining */ - if (wsi->http.rx_content_length > 0) - wsi->http.rx_content_remain -= n; - - // lwsl_notice("rx_content_remain %lld, rx_content_length %lld, giv %d\n", - // wsi->http.rx_content_remain, wsi->http.rx_content_length, - // wsi->http.content_length_given); - - if (wsi->http.rx_content_remain || !wsi->http.content_length_given) - goto account_and_ret; - -completed: - - if (lws_http_transaction_completed_client(wsi)) { - lwsl_notice("%s: transaction completed says -1\n", __func__); - return -1; - } - -account_and_ret: -// lwsl_warn("%s: on way out, consuming %d / %d\n", __func__, consumed, eb.len); - if (lws_buflist_aware_finished_consuming(wsi, &eb, consumed, buffered, - __func__)) - return -1; - - return 0; -} - -#endif diff -Nru libwebsockets-4.0.20/lib/roles/http/compression/brotli/brotli.c libwebsockets-2.4.2/lib/roles/http/compression/brotli/brotli.c --- libwebsockets-4.0.20/lib/roles/http/compression/brotli/brotli.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/compression/brotli/brotli.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 = 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, -}; diff -Nru libwebsockets-4.0.20/lib/roles/http/compression/deflate/deflate.c libwebsockets-2.4.2/lib/roles/http/compression/deflate/deflate.c --- libwebsockets-4.0.20/lib/roles/http/compression/deflate/deflate.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/compression/deflate/deflate.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 = *ilen_iused; - - ctx->u.deflate->next_out = out; - ctx->u.deflate->avail_out = *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, -}; diff -Nru libwebsockets-4.0.20/lib/roles/http/compression/private-lib-roles-http-compression.h libwebsockets-2.4.2/lib/roles/http/compression/private-lib-roles-http-compression.h --- libwebsockets-4.0.20/lib/roles/http/compression/private-lib-roles-http-compression.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/compression/private-lib-roles-http-compression.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 -#else -#include -#endif -#if defined(LWS_WITH_HTTP_BROTLI) -#include -#include -#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); diff -Nru libwebsockets-4.0.20/lib/roles/http/compression/README.md libwebsockets-2.4.2/lib/roles/http/compression/README.md --- libwebsockets-4.0.20/lib/roles/http/compression/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/compression/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -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. diff -Nru libwebsockets-4.0.20/lib/roles/http/compression/stream.c libwebsockets-2.4.2/lib/roles/http/compression/stream.c --- libwebsockets-4.0.20/lib/roles/http/compression/stream.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/compression/stream.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,226 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 |= 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, - strlen(lcs_available[n]->encoding_name), p, end)) - return -1; - - lwsl_info("%s: wsi %p: applied %s content-encoding\n", __func__, - 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 = LWS_WRITE_HTTP | ((*wp) & ~0x1f); - } - - 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: %p: adding %d to comp buflist\n", - __func__,wsi, (int)len); - } - - len = lws_buflist_next_segment_len(&ctx->buflist_comp, &buf); - ilen_iused = len; - use = 1; - lwsl_debug("%s: %p: trying comp buflist %d\n", __func__, wsi, - (int)len); - } - - if (!buf && ilen_iused) - return 0; - - lwsl_debug("%s: %p: pre-process: ilen_iused %d, olen_oused %d\n", - __func__, 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 = LWS_WRITE_HTTP_FINAL | ((*wp) & ~0x1f); - - lwsl_debug("%s: %p: more %d, ilen_iused %d\n", __func__, 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: %p: marking %d of comp buflist as used " - "(ctx->buflist_comp %p)\n", __func__, 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; -} diff -Nru libwebsockets-4.0.20/lib/roles/http/header.c libwebsockets-2.4.2/lib/roles/http/header.c --- libwebsockets-4.0.20/lib/roles/http/header.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/header.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,634 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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]; -} - -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 (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi)) - return lws_add_http2_header_by_name(wsi, name, - value, length, p, end); -#else - (void)wsi; -#endif - if (name) { - while (*p < end && *name) - *((*p)++) = *name++; - if (*p == end) - return 1; - *((*p)++) = ' '; - } - if (*p + length + 3 >= end) - return 1; - - memcpy(*p, value, 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 (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(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 defined(LWS_WITH_DETAILED_LATENCY) - wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); -#endif - if (lws_write(wsi, start, 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 (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(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: wsi %p: tx_content_length/remain %llu\n", __func__, - 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 (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 && - (!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 = 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; - -#ifdef LWS_WITH_ACCESS_LOG - wsi->http.access_log.response = code; -#endif - -#ifdef LWS_WITH_HTTP2 - if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(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->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->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->context->server_string && - !(_code & LWSAHH_FLAG_NO_SERVER_NAME)) { - assert(wsi->context->server_string_len > 0); - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, - (unsigned char *)wsi->context->server_string, - wsi->context->server_string_len, p, end)) - return 1; - } - - if (wsi->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->vhost) { - lwsl_err("%s: wsi not bound to vhost\n", __func__); - - return 1; - } -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - if (!wsi->handling_404 && - wsi->vhost->http.error_document_404 && - code == HTTP_STATUS_NOT_FOUND) - /* we should do a redirect, and do the 404 there */ - if (lws_http_redirect(wsi, HTTP_STATUS_FOUND, - (uint8_t *)wsi->vhost->http.error_document_404, - (int)strlen(wsi->vhost->http.error_document_404), - &p, end) > 0) - return 0; -#endif - - /* if the redirect failed, just do a simple status */ - p = start; - - if (!html_body) - 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, "" - "" - "" - "

%u

%s", 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... - */ -#if defined(LWS_WITH_DETAILED_LATENCY) - wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); -#endif - m = lws_write(wsi, start, lws_ptr_diff(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 = len; - wsi->http.tx_content_remain = len; - - wsi->h2.pending_status_body = lws_malloc(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, len); - m = lws_write(wsi, start, 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, 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, *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->vhost && - (now - ah->assigned) < - ah->wsi->vhost->timeout_secs_ah_idle + 360)) { - ah = ah->next; - continue; - } - - /* - * a single ah session somehow got held for - * an unreasonable amount of time. - * - * Dump info on the connection... - */ - wsi = ah->wsi; - buf[0] = '\0'; -#if !defined(LWS_PLAT_OPTEE) - lws_get_peer_simple(wsi, buf, sizeof(buf)); -#else - buf[0] = '\0'; -#endif - lwsl_notice("ah excessive hold: wsi %p\n" - " peer address: %s\n" - " ah pos %lu\n", - wsi, buf, (unsigned long)ah->pos); - buf[0] = '\0'; - m = 0; - do { - c = lws_token_to_string(m); - if (!c) - break; - if (!(*c)) - break; - - len = lws_hdr_total_length(wsi, m); - if (!len || len > (int)sizeof(buf) - 1) { - m++; - continue; - } - - if (lws_hdr_copy(wsi, buf, sizeof buf, m) > 0) { - buf[sizeof(buf) - 1] = '\0'; - - lwsl_notice(" %s = %s\n", - (const char *)c, buf); - } - m++; - } while (1); - - /* explicitly detach the ah */ - lws_header_table_detach(wsi, 0); - - /* ... and then drop the connection */ - - __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "excessive ah"); - - ah = pt->http.ah_list; - } - - lws_pt_unlock(pt); -} -#endif diff -Nru libwebsockets-4.0.20/lib/roles/http/lextable.h libwebsockets-2.4.2/lib/roles/http/lextable.h --- libwebsockets-4.0.20/lib/roles/http/lextable.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/lextable.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,6733 +0,0 @@ -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) - /* 0: 0: get */ - /* 1: 1: post */ - /* 2: 3: host: */ - /* 3: 4: connection: */ - /* 4: 5: upgrade: */ - /* 5: 6: origin: */ - /* 6: 8: - */ - /* 7: 15: http/1.1 */ - /* 8: 17: accept: */ - /* 9: 19: if-modified-since: */ - /* 10: 20: if-none-match: */ - /* 11: 21: accept-encoding: */ - /* 12: 22: accept-language: */ - /* 13: 23: pragma: */ - /* 14: 24: cache-control: */ - /* 15: 25: authorization: */ - /* 16: 26: cookie: */ - /* 17: 27: content-length: */ - /* 18: 28: content-type: */ - /* 19: 29: date: */ - /* 20: 30: range: */ - /* 21: 41: accept-ranges: */ - /* 22: 43: age: */ - /* 23: 44: allow: */ - /* 24: 45: content-disposition: */ - /* 25: 46: content-encoding: */ - /* 26: 47: content-language: */ - /* 27: 48: content-location: */ - /* 28: 49: content-range: */ - /* 29: 50: etag: */ - /* 30: 51: expect: */ - /* 31: 52: expires: */ - /* 32: 53: from: */ - /* 33: 54: if-match: */ - /* 34: 55: if-range: */ - /* 35: 56: if-unmodified-since: */ - /* 36: 57: last-modified: */ - /* 37: 58: link: */ - /* 38: 59: location: */ - /* 39: 63: refresh: */ - /* 40: 64: retry-after: */ - /* 41: 65: server: */ - /* 42: 66: set-cookie: */ - /* 43: 68: transfer-encoding: */ - /* 44: 76: uri-args */ - /* 45: 79: http/1.0 */ - /* 46: 80: x-forwarded-for: */ - /* 47: 81: connect */ - /* 48: 82: head */ - /* 49: 86: x-auth-token: */ - /* 50: 87: */ -/* pos 0000: 0 */ 0x67 /* 'g' */, 0x34, 0x00 /* (to 0x0034 state 1) */, - 0x70 /* 'p' */, 0x36, 0x00 /* (to 0x0039 state 5) */, - 0x68 /* 'h' */, 0x3F, 0x00 /* (to 0x0045 state 10) */, - 0x63 /* 'c' */, 0x4B, 0x00 /* (to 0x0054 state 15) */, - 0x75 /* 'u' */, 0x6C, 0x00 /* (to 0x0078 state 26) */, - 0x6F /* 'o' */, 0x78, 0x00 /* (to 0x0087 state 34) */, - 0x0D /* '.' */, 0x7D, 0x00 /* (to 0x008F state 41) */, - 0x61 /* 'a' */, 0x8C, 0x00 /* (to 0x00A1 state 51) */, - 0x69 /* 'i' */, 0xA3, 0x00 /* (to 0x00BB state 58) */, - 0x64 /* 'd' */, 0x43, 0x01 /* (to 0x015E state 160) */, - 0x72 /* 'r' */, 0x46, 0x01 /* (to 0x0164 state 165) */, - 0x65 /* 'e' */, 0x92, 0x01 /* (to 0x01B3 state 229) */, - 0x66 /* 'f' */, 0xAE, 0x01 /* (to 0x01D2 state 245) */, - 0x6C /* 'l' */, 0xD0, 0x01 /* (to 0x01F7 state 278) */, - 0x73 /* 's' */, 0x0C, 0x02 /* (to 0x0236 state 321) */, - 0x74 /* 't' */, 0x21, 0x02 /* (to 0x024E state 337) */, - 0x78 /* 'x' */, 0x3C, 0x02 /* (to 0x026C state 364) */, - 0x08, /* fail */ -/* pos 0034: 1 */ 0xE5 /* 'e' -> */, -/* pos 0035: 2 */ 0xF4 /* 't' -> */, -/* pos 0036: 3 */ 0xA0 /* ' ' -> */, -/* pos 0037: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, -/* pos 0039: 5 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x0040 state 6) */, - 0x72 /* 'r' */, 0xCE, 0x00 /* (to 0x010A state 106) */, - 0x08, /* fail */ -/* pos 0040: 6 */ 0xF3 /* 's' -> */, -/* pos 0041: 7 */ 0xF4 /* 't' -> */, -/* pos 0042: 8 */ 0xA0 /* ' ' -> */, -/* pos 0043: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, -/* pos 0045: 10 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x004F state 11) */, - 0x74 /* 't' */, 0x4A, 0x00 /* (to 0x0092 state 43) */, - 0x65 /* 'e' */, 0x3A, 0x02 /* (to 0x0285 state 381) */, - 0x08, /* fail */ -/* pos 004f: 11 */ 0xF3 /* 's' -> */, -/* pos 0050: 12 */ 0xF4 /* 't' -> */, -/* pos 0051: 13 */ 0xBA /* ':' -> */, -/* pos 0052: 14 */ 0x00, 0x02 /* - terminal marker 2 - */, -/* pos 0054: 15 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x005B state 16) */, - 0x61 /* 'a' */, 0xBA, 0x00 /* (to 0x0111 state 112) */, - 0x08, /* fail */ -/* pos 005b: 16 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0062 state 17) */, - 0x6F /* 'o' */, 0xCF, 0x00 /* (to 0x012D state 138) */, - 0x08, /* fail */ -/* pos 0062: 17 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0069 state 18) */, - 0x74 /* 't' */, 0xCE, 0x00 /* (to 0x0133 state 143) */, - 0x08, /* fail */ -/* pos 0069: 18 */ 0xE5 /* 'e' -> */, -/* pos 006a: 19 */ 0xE3 /* 'c' -> */, -/* pos 006b: 20 */ 0xF4 /* 't' -> */, -/* pos 006c: 21 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0073 state 22) */, - 0x20 /* ' ' */, 0x14, 0x02 /* (to 0x0283 state 380) */, - 0x08, /* fail */ -/* pos 0073: 22 */ 0xEF /* 'o' -> */, -/* pos 0074: 23 */ 0xEE /* 'n' -> */, -/* pos 0075: 24 */ 0xBA /* ':' -> */, -/* pos 0076: 25 */ 0x00, 0x03 /* - terminal marker 3 - */, -/* pos 0078: 26 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x007F state 27) */, - 0x72 /* 'r' */, 0xE6, 0x01 /* (to 0x0261 state 355) */, - 0x08, /* fail */ -/* pos 007f: 27 */ 0xE7 /* 'g' -> */, -/* pos 0080: 28 */ 0xF2 /* 'r' -> */, -/* pos 0081: 29 */ 0xE1 /* 'a' -> */, -/* pos 0082: 30 */ 0xE4 /* 'd' -> */, -/* pos 0083: 31 */ 0xE5 /* 'e' -> */, -/* pos 0084: 32 */ 0xBA /* ':' -> */, -/* pos 0085: 33 */ 0x00, 0x04 /* - terminal marker 4 - */, -/* pos 0087: 34 */ 0xF2 /* 'r' -> */, -/* pos 0088: 35 */ 0xE9 /* 'i' -> */, -/* pos 0089: 36 */ 0xE7 /* 'g' -> */, -/* pos 008a: 37 */ 0xE9 /* 'i' -> */, -/* pos 008b: 38 */ 0xEE /* 'n' -> */, -/* pos 008c: 39 */ 0xBA /* ':' -> */, -/* pos 008d: 40 */ 0x00, 0x05 /* - terminal marker 5 - */, -/* pos 008f: 41 */ 0x8A /* '.' -> */, -/* pos 0090: 42 */ 0x00, 0x06 /* - terminal marker 6 - */, -/* pos 0092: 43 */ 0xF4 /* 't' -> */, -/* pos 0093: 44 */ 0xF0 /* 'p' -> */, -/* pos 0094: 45 */ 0xAF /* '/' -> */, -/* pos 0095: 46 */ 0xB1 /* '1' -> */, -/* pos 0096: 47 */ 0xAE /* '.' -> */, -/* pos 0097: 48 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x009E state 49) */, - 0x30 /* '0' */, 0xCF, 0x01 /* (to 0x0269 state 362) */, - 0x08, /* fail */ -/* pos 009e: 49 */ 0xA0 /* ' ' -> */, -/* pos 009f: 50 */ 0x00, 0x07 /* - terminal marker 7 - */, -/* pos 00a1: 51 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x00AE state 52) */, - 0x75 /* 'u' */, 0x7B, 0x00 /* (to 0x011F state 125) */, - 0x67 /* 'g' */, 0xD2, 0x00 /* (to 0x0179 state 178) */, - 0x6C /* 'l' */, 0xD3, 0x00 /* (to 0x017D state 181) */, - 0x08, /* fail */ -/* pos 00ae: 52 */ 0xE3 /* 'c' -> */, -/* pos 00af: 53 */ 0xE5 /* 'e' -> */, -/* pos 00b0: 54 */ 0xF0 /* 'p' -> */, -/* pos 00b1: 55 */ 0xF4 /* 't' -> */, -/* pos 00b2: 56 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x00B9 state 57) */, - 0x2D /* '-' */, 0x37, 0x00 /* (to 0x00EC state 87) */, - 0x08, /* fail */ -/* pos 00b9: 57 */ 0x00, 0x08 /* - terminal marker 8 - */, -/* pos 00bb: 58 */ 0xE6 /* 'f' -> */, -/* pos 00bc: 59 */ 0xAD /* '-' -> */, -/* pos 00bd: 60 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x00CA state 61) */, - 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x00E0 state 76) */, - 0x72 /* 'r' */, 0x1B, 0x01 /* (to 0x01DE state 255) */, - 0x75 /* 'u' */, 0x1F, 0x01 /* (to 0x01E5 state 261) */, - 0x08, /* fail */ -/* pos 00ca: 61 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x00D1 state 62) */, - 0x61 /* 'a' */, 0x0B, 0x01 /* (to 0x01D8 state 250) */, - 0x08, /* fail */ -/* pos 00d1: 62 */ 0xE4 /* 'd' -> */, -/* pos 00d2: 63 */ 0xE9 /* 'i' -> */, -/* pos 00d3: 64 */ 0xE6 /* 'f' -> */, -/* pos 00d4: 65 */ 0xE9 /* 'i' -> */, -/* pos 00d5: 66 */ 0xE5 /* 'e' -> */, -/* pos 00d6: 67 */ 0xE4 /* 'd' -> */, -/* pos 00d7: 68 */ 0xAD /* '-' -> */, -/* pos 00d8: 69 */ 0xF3 /* 's' -> */, -/* pos 00d9: 70 */ 0xE9 /* 'i' -> */, -/* pos 00da: 71 */ 0xEE /* 'n' -> */, -/* pos 00db: 72 */ 0xE3 /* 'c' -> */, -/* pos 00dc: 73 */ 0xE5 /* 'e' -> */, -/* pos 00dd: 74 */ 0xBA /* ':' -> */, -/* pos 00de: 75 */ 0x00, 0x09 /* - terminal marker 9 - */, -/* pos 00e0: 76 */ 0xEF /* 'o' -> */, -/* pos 00e1: 77 */ 0xEE /* 'n' -> */, -/* pos 00e2: 78 */ 0xE5 /* 'e' -> */, -/* pos 00e3: 79 */ 0xAD /* '-' -> */, -/* pos 00e4: 80 */ 0xED /* 'm' -> */, -/* pos 00e5: 81 */ 0xE1 /* 'a' -> */, -/* pos 00e6: 82 */ 0xF4 /* 't' -> */, -/* pos 00e7: 83 */ 0xE3 /* 'c' -> */, -/* pos 00e8: 84 */ 0xE8 /* 'h' -> */, -/* pos 00e9: 85 */ 0xBA /* ':' -> */, -/* pos 00ea: 86 */ 0x00, 0x0A /* - terminal marker 10 - */, -/* pos 00ec: 87 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x00F6 state 88) */, - 0x6C /* 'l' */, 0x11, 0x00 /* (to 0x0100 state 97) */, - 0x72 /* 'r' */, 0x7F, 0x00 /* (to 0x0171 state 171) */, - 0x08, /* fail */ -/* pos 00f6: 88 */ 0xEE /* 'n' -> */, -/* pos 00f7: 89 */ 0xE3 /* 'c' -> */, -/* pos 00f8: 90 */ 0xEF /* 'o' -> */, -/* pos 00f9: 91 */ 0xE4 /* 'd' -> */, -/* pos 00fa: 92 */ 0xE9 /* 'i' -> */, -/* pos 00fb: 93 */ 0xEE /* 'n' -> */, -/* pos 00fc: 94 */ 0xE7 /* 'g' -> */, -/* pos 00fd: 95 */ 0xBA /* ':' -> */, -/* pos 00fe: 96 */ 0x00, 0x0B /* - terminal marker 11 - */, -/* pos 0100: 97 */ 0xE1 /* 'a' -> */, -/* pos 0101: 98 */ 0xEE /* 'n' -> */, -/* pos 0102: 99 */ 0xE7 /* 'g' -> */, -/* pos 0103: 100 */ 0xF5 /* 'u' -> */, -/* pos 0104: 101 */ 0xE1 /* 'a' -> */, -/* pos 0105: 102 */ 0xE7 /* 'g' -> */, -/* pos 0106: 103 */ 0xE5 /* 'e' -> */, -/* pos 0107: 104 */ 0xBA /* ':' -> */, -/* pos 0108: 105 */ 0x00, 0x0C /* - terminal marker 12 - */, -/* pos 010a: 106 */ 0xE1 /* 'a' -> */, -/* pos 010b: 107 */ 0xE7 /* 'g' -> */, -/* pos 010c: 108 */ 0xED /* 'm' -> */, -/* pos 010d: 109 */ 0xE1 /* 'a' -> */, -/* pos 010e: 110 */ 0xBA /* ':' -> */, -/* pos 010f: 111 */ 0x00, 0x0D /* - terminal marker 13 - */, -/* pos 0111: 112 */ 0xE3 /* 'c' -> */, -/* pos 0112: 113 */ 0xE8 /* 'h' -> */, -/* pos 0113: 114 */ 0xE5 /* 'e' -> */, -/* pos 0114: 115 */ 0xAD /* '-' -> */, -/* pos 0115: 116 */ 0xE3 /* 'c' -> */, -/* pos 0116: 117 */ 0xEF /* 'o' -> */, -/* pos 0117: 118 */ 0xEE /* 'n' -> */, -/* pos 0118: 119 */ 0xF4 /* 't' -> */, -/* pos 0119: 120 */ 0xF2 /* 'r' -> */, -/* pos 011a: 121 */ 0xEF /* 'o' -> */, -/* pos 011b: 122 */ 0xEC /* 'l' -> */, -/* pos 011c: 123 */ 0xBA /* ':' -> */, -/* pos 011d: 124 */ 0x00, 0x0E /* - terminal marker 14 - */, -/* pos 011f: 125 */ 0xF4 /* 't' -> */, -/* pos 0120: 126 */ 0xE8 /* 'h' -> */, -/* pos 0121: 127 */ 0xEF /* 'o' -> */, -/* pos 0122: 128 */ 0xF2 /* 'r' -> */, -/* pos 0123: 129 */ 0xE9 /* 'i' -> */, -/* pos 0124: 130 */ 0xFA /* 'z' -> */, -/* pos 0125: 131 */ 0xE1 /* 'a' -> */, -/* pos 0126: 132 */ 0xF4 /* 't' -> */, -/* pos 0127: 133 */ 0xE9 /* 'i' -> */, -/* pos 0128: 134 */ 0xEF /* 'o' -> */, -/* pos 0129: 135 */ 0xEE /* 'n' -> */, -/* pos 012a: 136 */ 0xBA /* ':' -> */, -/* pos 012b: 137 */ 0x00, 0x0F /* - terminal marker 15 - */, -/* pos 012d: 138 */ 0xEB /* 'k' -> */, -/* pos 012e: 139 */ 0xE9 /* 'i' -> */, -/* pos 012f: 140 */ 0xE5 /* 'e' -> */, -/* pos 0130: 141 */ 0xBA /* ':' -> */, -/* pos 0131: 142 */ 0x00, 0x10 /* - terminal marker 16 - */, -/* pos 0133: 143 */ 0xE5 /* 'e' -> */, -/* pos 0134: 144 */ 0xEE /* 'n' -> */, -/* pos 0135: 145 */ 0xF4 /* 't' -> */, -/* pos 0136: 146 */ 0xAD /* '-' -> */, -/* pos 0137: 147 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0147 state 148) */, - 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0158 state 155) */, - 0x64 /* 'd' */, 0x46, 0x00 /* (to 0x0183 state 186) */, - 0x65 /* 'e' */, 0x50, 0x00 /* (to 0x0190 state 198) */, - 0x72 /* 'r' */, 0x69, 0x00 /* (to 0x01AC state 223) */, - 0x08, /* fail */ -/* pos 0147: 148 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0151 state 149) */, - 0x61 /* 'a' */, 0x50, 0x00 /* (to 0x019A state 207) */, - 0x6F /* 'o' */, 0x56, 0x00 /* (to 0x01A3 state 215) */, - 0x08, /* fail */ -/* pos 0151: 149 */ 0xEE /* 'n' -> */, -/* pos 0152: 150 */ 0xE7 /* 'g' -> */, -/* pos 0153: 151 */ 0xF4 /* 't' -> */, -/* pos 0154: 152 */ 0xE8 /* 'h' -> */, -/* pos 0155: 153 */ 0xBA /* ':' -> */, -/* pos 0156: 154 */ 0x00, 0x11 /* - terminal marker 17 - */, -/* pos 0158: 155 */ 0xF9 /* 'y' -> */, -/* pos 0159: 156 */ 0xF0 /* 'p' -> */, -/* pos 015a: 157 */ 0xE5 /* 'e' -> */, -/* pos 015b: 158 */ 0xBA /* ':' -> */, -/* pos 015c: 159 */ 0x00, 0x12 /* - terminal marker 18 - */, -/* pos 015e: 160 */ 0xE1 /* 'a' -> */, -/* pos 015f: 161 */ 0xF4 /* 't' -> */, -/* pos 0160: 162 */ 0xE5 /* 'e' -> */, -/* pos 0161: 163 */ 0xBA /* ':' -> */, -/* pos 0162: 164 */ 0x00, 0x13 /* - terminal marker 19 - */, -/* pos 0164: 165 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x016B state 166) */, - 0x65 /* 'e' */, 0xB6, 0x00 /* (to 0x021D state 304) */, - 0x08, /* fail */ -/* pos 016b: 166 */ 0xEE /* 'n' -> */, -/* pos 016c: 167 */ 0xE7 /* 'g' -> */, -/* pos 016d: 168 */ 0xE5 /* 'e' -> */, -/* pos 016e: 169 */ 0xBA /* ':' -> */, -/* pos 016f: 170 */ 0x00, 0x14 /* - terminal marker 20 - */, -/* pos 0171: 171 */ 0xE1 /* 'a' -> */, -/* pos 0172: 172 */ 0xEE /* 'n' -> */, -/* pos 0173: 173 */ 0xE7 /* 'g' -> */, -/* pos 0174: 174 */ 0xE5 /* 'e' -> */, -/* pos 0175: 175 */ 0xF3 /* 's' -> */, -/* pos 0176: 176 */ 0xBA /* ':' -> */, -/* pos 0177: 177 */ 0x00, 0x15 /* - terminal marker 21 - */, -/* pos 0179: 178 */ 0xE5 /* 'e' -> */, -/* pos 017a: 179 */ 0xBA /* ':' -> */, -/* pos 017b: 180 */ 0x00, 0x16 /* - terminal marker 22 - */, -/* pos 017d: 181 */ 0xEC /* 'l' -> */, -/* pos 017e: 182 */ 0xEF /* 'o' -> */, -/* pos 017f: 183 */ 0xF7 /* 'w' -> */, -/* pos 0180: 184 */ 0xBA /* ':' -> */, -/* pos 0181: 185 */ 0x00, 0x17 /* - terminal marker 23 - */, -/* pos 0183: 186 */ 0xE9 /* 'i' -> */, -/* pos 0184: 187 */ 0xF3 /* 's' -> */, -/* pos 0185: 188 */ 0xF0 /* 'p' -> */, -/* pos 0186: 189 */ 0xEF /* 'o' -> */, -/* pos 0187: 190 */ 0xF3 /* 's' -> */, -/* pos 0188: 191 */ 0xE9 /* 'i' -> */, -/* pos 0189: 192 */ 0xF4 /* 't' -> */, -/* pos 018a: 193 */ 0xE9 /* 'i' -> */, -/* pos 018b: 194 */ 0xEF /* 'o' -> */, -/* pos 018c: 195 */ 0xEE /* 'n' -> */, -/* pos 018d: 196 */ 0xBA /* ':' -> */, -/* pos 018e: 197 */ 0x00, 0x18 /* - terminal marker 24 - */, -/* pos 0190: 198 */ 0xEE /* 'n' -> */, -/* pos 0191: 199 */ 0xE3 /* 'c' -> */, -/* pos 0192: 200 */ 0xEF /* 'o' -> */, -/* pos 0193: 201 */ 0xE4 /* 'd' -> */, -/* pos 0194: 202 */ 0xE9 /* 'i' -> */, -/* pos 0195: 203 */ 0xEE /* 'n' -> */, -/* pos 0196: 204 */ 0xE7 /* 'g' -> */, -/* pos 0197: 205 */ 0xBA /* ':' -> */, -/* pos 0198: 206 */ 0x00, 0x19 /* - terminal marker 25 - */, -/* pos 019a: 207 */ 0xEE /* 'n' -> */, -/* pos 019b: 208 */ 0xE7 /* 'g' -> */, -/* pos 019c: 209 */ 0xF5 /* 'u' -> */, -/* pos 019d: 210 */ 0xE1 /* 'a' -> */, -/* pos 019e: 211 */ 0xE7 /* 'g' -> */, -/* pos 019f: 212 */ 0xE5 /* 'e' -> */, -/* pos 01a0: 213 */ 0xBA /* ':' -> */, -/* pos 01a1: 214 */ 0x00, 0x1A /* - terminal marker 26 - */, -/* pos 01a3: 215 */ 0xE3 /* 'c' -> */, -/* pos 01a4: 216 */ 0xE1 /* 'a' -> */, -/* pos 01a5: 217 */ 0xF4 /* 't' -> */, -/* pos 01a6: 218 */ 0xE9 /* 'i' -> */, -/* pos 01a7: 219 */ 0xEF /* 'o' -> */, -/* pos 01a8: 220 */ 0xEE /* 'n' -> */, -/* pos 01a9: 221 */ 0xBA /* ':' -> */, -/* pos 01aa: 222 */ 0x00, 0x1B /* - terminal marker 27 - */, -/* pos 01ac: 223 */ 0xE1 /* 'a' -> */, -/* pos 01ad: 224 */ 0xEE /* 'n' -> */, -/* pos 01ae: 225 */ 0xE7 /* 'g' -> */, -/* pos 01af: 226 */ 0xE5 /* 'e' -> */, -/* pos 01b0: 227 */ 0xBA /* ':' -> */, -/* pos 01b1: 228 */ 0x00, 0x1C /* - terminal marker 28 - */, -/* pos 01b3: 229 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x01BA state 230) */, - 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x01BF state 234) */, - 0x08, /* fail */ -/* pos 01ba: 230 */ 0xE1 /* 'a' -> */, -/* pos 01bb: 231 */ 0xE7 /* 'g' -> */, -/* pos 01bc: 232 */ 0xBA /* ':' -> */, -/* pos 01bd: 233 */ 0x00, 0x1D /* - terminal marker 29 - */, -/* pos 01bf: 234 */ 0xF0 /* 'p' -> */, -/* pos 01c0: 235 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x01C7 state 236) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x01CC state 240) */, - 0x08, /* fail */ -/* pos 01c7: 236 */ 0xE3 /* 'c' -> */, -/* pos 01c8: 237 */ 0xF4 /* 't' -> */, -/* pos 01c9: 238 */ 0xBA /* ':' -> */, -/* pos 01ca: 239 */ 0x00, 0x1E /* - terminal marker 30 - */, -/* pos 01cc: 240 */ 0xF2 /* 'r' -> */, -/* pos 01cd: 241 */ 0xE5 /* 'e' -> */, -/* pos 01ce: 242 */ 0xF3 /* 's' -> */, -/* pos 01cf: 243 */ 0xBA /* ':' -> */, -/* pos 01d0: 244 */ 0x00, 0x1F /* - terminal marker 31 - */, -/* pos 01d2: 245 */ 0xF2 /* 'r' -> */, -/* pos 01d3: 246 */ 0xEF /* 'o' -> */, -/* pos 01d4: 247 */ 0xED /* 'm' -> */, -/* pos 01d5: 248 */ 0xBA /* ':' -> */, -/* pos 01d6: 249 */ 0x00, 0x20 /* - terminal marker 32 - */, -/* pos 01d8: 250 */ 0xF4 /* 't' -> */, -/* pos 01d9: 251 */ 0xE3 /* 'c' -> */, -/* pos 01da: 252 */ 0xE8 /* 'h' -> */, -/* pos 01db: 253 */ 0xBA /* ':' -> */, -/* pos 01dc: 254 */ 0x00, 0x21 /* - terminal marker 33 - */, -/* pos 01de: 255 */ 0xE1 /* 'a' -> */, -/* pos 01df: 256 */ 0xEE /* 'n' -> */, -/* pos 01e0: 257 */ 0xE7 /* 'g' -> */, -/* pos 01e1: 258 */ 0xE5 /* 'e' -> */, -/* pos 01e2: 259 */ 0xBA /* ':' -> */, -/* pos 01e3: 260 */ 0x00, 0x22 /* - terminal marker 34 - */, -/* pos 01e5: 261 */ 0xEE /* 'n' -> */, -/* pos 01e6: 262 */ 0xED /* 'm' -> */, -/* pos 01e7: 263 */ 0xEF /* 'o' -> */, -/* pos 01e8: 264 */ 0xE4 /* 'd' -> */, -/* pos 01e9: 265 */ 0xE9 /* 'i' -> */, -/* pos 01ea: 266 */ 0xE6 /* 'f' -> */, -/* pos 01eb: 267 */ 0xE9 /* 'i' -> */, -/* pos 01ec: 268 */ 0xE5 /* 'e' -> */, -/* pos 01ed: 269 */ 0xE4 /* 'd' -> */, -/* pos 01ee: 270 */ 0xAD /* '-' -> */, -/* pos 01ef: 271 */ 0xF3 /* 's' -> */, -/* pos 01f0: 272 */ 0xE9 /* 'i' -> */, -/* pos 01f1: 273 */ 0xEE /* 'n' -> */, -/* pos 01f2: 274 */ 0xE3 /* 'c' -> */, -/* pos 01f3: 275 */ 0xE5 /* 'e' -> */, -/* pos 01f4: 276 */ 0xBA /* ':' -> */, -/* pos 01f5: 277 */ 0x00, 0x23 /* - terminal marker 35 - */, -/* pos 01f7: 278 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0201 state 279) */, - 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x020F state 292) */, - 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0214 state 296) */, - 0x08, /* fail */ -/* pos 0201: 279 */ 0xF3 /* 's' -> */, -/* pos 0202: 280 */ 0xF4 /* 't' -> */, -/* pos 0203: 281 */ 0xAD /* '-' -> */, -/* pos 0204: 282 */ 0xED /* 'm' -> */, -/* pos 0205: 283 */ 0xEF /* 'o' -> */, -/* pos 0206: 284 */ 0xE4 /* 'd' -> */, -/* pos 0207: 285 */ 0xE9 /* 'i' -> */, -/* pos 0208: 286 */ 0xE6 /* 'f' -> */, -/* pos 0209: 287 */ 0xE9 /* 'i' -> */, -/* pos 020a: 288 */ 0xE5 /* 'e' -> */, -/* pos 020b: 289 */ 0xE4 /* 'd' -> */, -/* pos 020c: 290 */ 0xBA /* ':' -> */, -/* pos 020d: 291 */ 0x00, 0x24 /* - terminal marker 36 - */, -/* pos 020f: 292 */ 0xEE /* 'n' -> */, -/* pos 0210: 293 */ 0xEB /* 'k' -> */, -/* pos 0211: 294 */ 0xBA /* ':' -> */, -/* pos 0212: 295 */ 0x00, 0x25 /* - terminal marker 37 - */, -/* pos 0214: 296 */ 0xE3 /* 'c' -> */, -/* pos 0215: 297 */ 0xE1 /* 'a' -> */, -/* pos 0216: 298 */ 0xF4 /* 't' -> */, -/* pos 0217: 299 */ 0xE9 /* 'i' -> */, -/* pos 0218: 300 */ 0xEF /* 'o' -> */, -/* pos 0219: 301 */ 0xEE /* 'n' -> */, -/* pos 021a: 302 */ 0xBA /* ':' -> */, -/* pos 021b: 303 */ 0x00, 0x26 /* - terminal marker 38 - */, -/* pos 021d: 304 */ 0x66 /* 'f' */, 0x07, 0x00 /* (to 0x0224 state 305) */, - 0x74 /* 't' */, 0x0B, 0x00 /* (to 0x022B state 311) */, - 0x08, /* fail */ -/* pos 0224: 305 */ 0xF2 /* 'r' -> */, -/* pos 0225: 306 */ 0xE5 /* 'e' -> */, -/* pos 0226: 307 */ 0xF3 /* 's' -> */, -/* pos 0227: 308 */ 0xE8 /* 'h' -> */, -/* pos 0228: 309 */ 0xBA /* ':' -> */, -/* pos 0229: 310 */ 0x00, 0x27 /* - terminal marker 39 - */, -/* pos 022b: 311 */ 0xF2 /* 'r' -> */, -/* pos 022c: 312 */ 0xF9 /* 'y' -> */, -/* pos 022d: 313 */ 0xAD /* '-' -> */, -/* pos 022e: 314 */ 0xE1 /* 'a' -> */, -/* pos 022f: 315 */ 0xE6 /* 'f' -> */, -/* pos 0230: 316 */ 0xF4 /* 't' -> */, -/* pos 0231: 317 */ 0xE5 /* 'e' -> */, -/* pos 0232: 318 */ 0xF2 /* 'r' -> */, -/* pos 0233: 319 */ 0xBA /* ':' -> */, -/* pos 0234: 320 */ 0x00, 0x28 /* - terminal marker 40 - */, -/* pos 0236: 321 */ 0xE5 /* 'e' -> */, -/* pos 0237: 322 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x023E state 323) */, - 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x0244 state 328) */, - 0x08, /* fail */ -/* pos 023e: 323 */ 0xF6 /* 'v' -> */, -/* pos 023f: 324 */ 0xE5 /* 'e' -> */, -/* pos 0240: 325 */ 0xF2 /* 'r' -> */, -/* pos 0241: 326 */ 0xBA /* ':' -> */, -/* pos 0242: 327 */ 0x00, 0x29 /* - terminal marker 41 - */, -/* pos 0244: 328 */ 0xAD /* '-' -> */, -/* pos 0245: 329 */ 0xE3 /* 'c' -> */, -/* pos 0246: 330 */ 0xEF /* 'o' -> */, -/* pos 0247: 331 */ 0xEF /* 'o' -> */, -/* pos 0248: 332 */ 0xEB /* 'k' -> */, -/* pos 0249: 333 */ 0xE9 /* 'i' -> */, -/* pos 024a: 334 */ 0xE5 /* 'e' -> */, -/* pos 024b: 335 */ 0xBA /* ':' -> */, -/* pos 024c: 336 */ 0x00, 0x2A /* - terminal marker 42 - */, -/* pos 024e: 337 */ 0xF2 /* 'r' -> */, -/* pos 024f: 338 */ 0xE1 /* 'a' -> */, -/* pos 0250: 339 */ 0xEE /* 'n' -> */, -/* pos 0251: 340 */ 0xF3 /* 's' -> */, -/* pos 0252: 341 */ 0xE6 /* 'f' -> */, -/* pos 0253: 342 */ 0xE5 /* 'e' -> */, -/* pos 0254: 343 */ 0xF2 /* 'r' -> */, -/* pos 0255: 344 */ 0xAD /* '-' -> */, -/* pos 0256: 345 */ 0xE5 /* 'e' -> */, -/* pos 0257: 346 */ 0xEE /* 'n' -> */, -/* pos 0258: 347 */ 0xE3 /* 'c' -> */, -/* pos 0259: 348 */ 0xEF /* 'o' -> */, -/* pos 025a: 349 */ 0xE4 /* 'd' -> */, -/* pos 025b: 350 */ 0xE9 /* 'i' -> */, -/* pos 025c: 351 */ 0xEE /* 'n' -> */, -/* pos 025d: 352 */ 0xE7 /* 'g' -> */, -/* pos 025e: 353 */ 0xBA /* ':' -> */, -/* pos 025f: 354 */ 0x00, 0x2B /* - terminal marker 43 - */, -/* pos 0261: 355 */ 0xE9 /* 'i' -> */, -/* pos 0262: 356 */ 0xAD /* '-' -> */, -/* pos 0263: 357 */ 0xE1 /* 'a' -> */, -/* pos 0264: 358 */ 0xF2 /* 'r' -> */, -/* pos 0265: 359 */ 0xE7 /* 'g' -> */, -/* pos 0266: 360 */ 0xF3 /* 's' -> */, -/* pos 0267: 361 */ 0x00, 0x2C /* - terminal marker 44 - */, -/* pos 0269: 362 */ 0xA0 /* ' ' -> */, -/* pos 026a: 363 */ 0x00, 0x2D /* - terminal marker 45 - */, -/* pos 026c: 364 */ 0xAD /* '-' -> */, -/* pos 026d: 365 */ 0x66 /* 'f' */, 0x07, 0x00 /* (to 0x0274 state 366) */, - 0x61 /* 'a' */, 0x1A, 0x00 /* (to 0x028A state 385) */, - 0x08, /* fail */ -/* pos 0274: 366 */ 0xEF /* 'o' -> */, -/* pos 0275: 367 */ 0xF2 /* 'r' -> */, -/* pos 0276: 368 */ 0xF7 /* 'w' -> */, -/* pos 0277: 369 */ 0xE1 /* 'a' -> */, -/* pos 0278: 370 */ 0xF2 /* 'r' -> */, -/* pos 0279: 371 */ 0xE4 /* 'd' -> */, -/* pos 027a: 372 */ 0xE5 /* 'e' -> */, -/* pos 027b: 373 */ 0xE4 /* 'd' -> */, -/* pos 027c: 374 */ 0xAD /* '-' -> */, -/* pos 027d: 375 */ 0xE6 /* 'f' -> */, -/* pos 027e: 376 */ 0xEF /* 'o' -> */, -/* pos 027f: 377 */ 0xF2 /* 'r' -> */, -/* pos 0280: 378 */ 0xBA /* ':' -> */, -/* pos 0281: 379 */ 0x00, 0x2E /* - terminal marker 46 - */, -/* pos 0283: 380 */ 0x00, 0x2F /* - terminal marker 47 - */, -/* pos 0285: 381 */ 0xE1 /* 'a' -> */, -/* pos 0286: 382 */ 0xE4 /* 'd' -> */, -/* pos 0287: 383 */ 0xA0 /* ' ' -> */, -/* pos 0288: 384 */ 0x00, 0x30 /* - terminal marker 48 - */, -/* pos 028a: 385 */ 0xF5 /* 'u' -> */, -/* pos 028b: 386 */ 0xF4 /* 't' -> */, -/* pos 028c: 387 */ 0xE8 /* 'h' -> */, -/* pos 028d: 388 */ 0xAD /* '-' -> */, -/* pos 028e: 389 */ 0xF4 /* 't' -> */, -/* pos 028f: 390 */ 0xEF /* 'o' -> */, -/* pos 0290: 391 */ 0xEB /* 'k' -> */, -/* pos 0291: 392 */ 0xE5 /* 'e' -> */, -/* pos 0292: 393 */ 0xEE /* 'n' -> */, -/* pos 0293: 394 */ 0xBA /* ':' -> */, -/* pos 0294: 395 */ 0x00, 0x31 /* - terminal marker 49 - */, -/* total size 662 bytes */ -#endif - -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) - /* 0: 0: get */ - /* 1: 1: post */ - /* 2: 2: options */ - /* 3: 3: host: */ - /* 4: 4: connection: */ - /* 5: 5: upgrade: */ - /* 6: 6: origin: */ - /* 7: 8: - */ - /* 8: 15: http/1.1 */ - /* 9: 17: accept: */ - /* 10: 18: access-control-request-headers: */ - /* 11: 19: if-modified-since: */ - /* 12: 20: if-none-match: */ - /* 13: 21: accept-encoding: */ - /* 14: 22: accept-language: */ - /* 15: 23: pragma: */ - /* 16: 24: cache-control: */ - /* 17: 25: authorization: */ - /* 18: 26: cookie: */ - /* 19: 27: content-length: */ - /* 20: 28: content-type: */ - /* 21: 29: date: */ - /* 22: 30: range: */ - /* 23: 31: referer: */ - /* 24: 40: accept-charset: */ - /* 25: 41: accept-ranges: */ - /* 26: 42: access-control-allow-origin: */ - /* 27: 43: age: */ - /* 28: 44: allow: */ - /* 29: 45: content-disposition: */ - /* 30: 46: content-encoding: */ - /* 31: 47: content-language: */ - /* 32: 48: content-location: */ - /* 33: 49: content-range: */ - /* 34: 50: etag: */ - /* 35: 51: expect: */ - /* 36: 52: expires: */ - /* 37: 53: from: */ - /* 38: 54: if-match: */ - /* 39: 55: if-range: */ - /* 40: 56: if-unmodified-since: */ - /* 41: 57: last-modified: */ - /* 42: 58: link: */ - /* 43: 59: location: */ - /* 44: 60: max-forwards: */ - /* 45: 61: proxy-authenticate: */ - /* 46: 62: proxy-authorization: */ - /* 47: 63: refresh: */ - /* 48: 64: retry-after: */ - /* 49: 65: server: */ - /* 50: 66: set-cookie: */ - /* 51: 67: strict-transport-security: */ - /* 52: 68: transfer-encoding: */ - /* 53: 69: user-agent: */ - /* 54: 70: vary: */ - /* 55: 71: via: */ - /* 56: 72: www-authenticate: */ - /* 57: 73: patch */ - /* 58: 74: put */ - /* 59: 75: delete */ - /* 60: 76: uri-args */ - /* 61: 77: proxy */ - /* 62: 78: x-real-ip: */ - /* 63: 79: http/1.0 */ - /* 64: 80: x-forwarded-for: */ - /* 65: 81: connect */ - /* 66: 82: head */ - /* 67: 83: te: */ - /* 68: 84: replay-nonce: */ - /* 69: 86: x-auth-token: */ - /* 70: 87: */ -/* pos 0000: 0 */ 0x67 /* 'g' */, 0x3D, 0x00 /* (to 0x003D state 1) */, - 0x70 /* 'p' */, 0x3F, 0x00 /* (to 0x0042 state 5) */, - 0x68 /* 'h' */, 0x4E, 0x00 /* (to 0x0054 state 10) */, - 0x63 /* 'c' */, 0x5A, 0x00 /* (to 0x0063 state 15) */, - 0x75 /* 'u' */, 0x7B, 0x00 /* (to 0x0087 state 26) */, - 0x6F /* 'o' */, 0x8A, 0x00 /* (to 0x0099 state 34) */, - 0x0D /* '.' */, 0x95, 0x00 /* (to 0x00A7 state 41) */, - 0x61 /* 'a' */, 0xA4, 0x00 /* (to 0x00B9 state 51) */, - 0x69 /* 'i' */, 0xC1, 0x00 /* (to 0x00D9 state 58) */, - 0x64 /* 'd' */, 0x6A, 0x01 /* (to 0x0185 state 160) */, - 0x72 /* 'r' */, 0x73, 0x01 /* (to 0x0191 state 165) */, - 0x65 /* 'e' */, 0xBF, 0x01 /* (to 0x01E0 state 229) */, - 0x66 /* 'f' */, 0xDB, 0x01 /* (to 0x01FF state 245) */, - 0x6C /* 'l' */, 0xFD, 0x01 /* (to 0x0224 state 278) */, - 0x73 /* 's' */, 0x42, 0x02 /* (to 0x026C state 321) */, - 0x74 /* 't' */, 0x5D, 0x02 /* (to 0x028A state 337) */, - 0x78 /* 'x' */, 0x7E, 0x02 /* (to 0x02AE state 364) */, - 0x6D /* 'm' */, 0xEF, 0x02 /* (to 0x0322 state 456) */, - 0x76 /* 'v' */, 0x48, 0x03 /* (to 0x037E state 531) */, - 0x77 /* 'w' */, 0x55, 0x03 /* (to 0x038E state 539) */, - 0x08, /* fail */ -/* pos 003d: 1 */ 0xE5 /* 'e' -> */, -/* pos 003e: 2 */ 0xF4 /* 't' -> */, -/* pos 003f: 3 */ 0xA0 /* ' ' -> */, -/* pos 0040: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, -/* pos 0042: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x004F state 6) */, - 0x72 /* 'r' */, 0xE6, 0x00 /* (to 0x012B state 106) */, - 0x61 /* 'a' */, 0x58, 0x03 /* (to 0x03A0 state 556) */, - 0x75 /* 'u' */, 0x5A, 0x03 /* (to 0x03A5 state 560) */, - 0x08, /* fail */ -/* pos 004f: 6 */ 0xF3 /* 's' -> */, -/* pos 0050: 7 */ 0xF4 /* 't' -> */, -/* pos 0051: 8 */ 0xA0 /* ' ' -> */, -/* pos 0052: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, -/* pos 0054: 10 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x005E state 11) */, - 0x74 /* 't' */, 0x53, 0x00 /* (to 0x00AA state 43) */, - 0x65 /* 'e' */, 0x70, 0x02 /* (to 0x02CA state 381) */, - 0x08, /* fail */ -/* pos 005e: 11 */ 0xF3 /* 's' -> */, -/* pos 005f: 12 */ 0xF4 /* 't' -> */, -/* pos 0060: 13 */ 0xBA /* ':' -> */, -/* pos 0061: 14 */ 0x00, 0x03 /* - terminal marker 3 - */, -/* pos 0063: 15 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x006A state 16) */, - 0x61 /* 'a' */, 0xD2, 0x00 /* (to 0x0138 state 112) */, - 0x08, /* fail */ -/* pos 006a: 16 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0071 state 17) */, - 0x6F /* 'o' */, 0xE7, 0x00 /* (to 0x0154 state 138) */, - 0x08, /* fail */ -/* pos 0071: 17 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0078 state 18) */, - 0x74 /* 't' */, 0xE6, 0x00 /* (to 0x015A state 143) */, - 0x08, /* fail */ -/* pos 0078: 18 */ 0xE5 /* 'e' -> */, -/* pos 0079: 19 */ 0xE3 /* 'c' -> */, -/* pos 007a: 20 */ 0xF4 /* 't' -> */, -/* pos 007b: 21 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0082 state 22) */, - 0x20 /* ' ' */, 0x4A, 0x02 /* (to 0x02C8 state 380) */, - 0x08, /* fail */ -/* pos 0082: 22 */ 0xEF /* 'o' -> */, -/* pos 0083: 23 */ 0xEE /* 'n' -> */, -/* pos 0084: 24 */ 0xBA /* ':' -> */, -/* pos 0085: 25 */ 0x00, 0x04 /* - terminal marker 4 - */, -/* pos 0087: 26 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x0091 state 27) */, - 0x72 /* 'r' */, 0x19, 0x02 /* (to 0x02A3 state 355) */, - 0x73 /* 's' */, 0xE6, 0x02 /* (to 0x0373 state 521) */, - 0x08, /* fail */ -/* pos 0091: 27 */ 0xE7 /* 'g' -> */, -/* pos 0092: 28 */ 0xF2 /* 'r' -> */, -/* pos 0093: 29 */ 0xE1 /* 'a' -> */, -/* pos 0094: 30 */ 0xE4 /* 'd' -> */, -/* pos 0095: 31 */ 0xE5 /* 'e' -> */, -/* pos 0096: 32 */ 0xBA /* ':' -> */, -/* pos 0097: 33 */ 0x00, 0x05 /* - terminal marker 5 - */, -/* pos 0099: 34 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x00A0 state 35) */, - 0x70 /* 'p' */, 0x3F, 0x02 /* (to 0x02DB state 396) */, - 0x08, /* fail */ -/* pos 00a0: 35 */ 0xE9 /* 'i' -> */, -/* pos 00a1: 36 */ 0xE7 /* 'g' -> */, -/* pos 00a2: 37 */ 0xE9 /* 'i' -> */, -/* pos 00a3: 38 */ 0xEE /* 'n' -> */, -/* pos 00a4: 39 */ 0xBA /* ':' -> */, -/* pos 00a5: 40 */ 0x00, 0x06 /* - terminal marker 6 - */, -/* pos 00a7: 41 */ 0x8A /* '.' -> */, -/* pos 00a8: 42 */ 0x00, 0x07 /* - terminal marker 7 - */, -/* pos 00aa: 43 */ 0xF4 /* 't' -> */, -/* pos 00ab: 44 */ 0xF0 /* 'p' -> */, -/* pos 00ac: 45 */ 0xAF /* '/' -> */, -/* pos 00ad: 46 */ 0xB1 /* '1' -> */, -/* pos 00ae: 47 */ 0xAE /* '.' -> */, -/* pos 00af: 48 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x00B6 state 49) */, - 0x30 /* '0' */, 0xF9, 0x01 /* (to 0x02AB state 362) */, - 0x08, /* fail */ -/* pos 00b6: 49 */ 0xA0 /* ' ' -> */, -/* pos 00b7: 50 */ 0x00, 0x08 /* - terminal marker 8 - */, -/* pos 00b9: 51 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x00C6 state 52) */, - 0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x0146 state 125) */, - 0x67 /* 'g' */, 0xE7, 0x00 /* (to 0x01A6 state 178) */, - 0x6C /* 'l' */, 0xE8, 0x00 /* (to 0x01AA state 181) */, - 0x08, /* fail */ -/* pos 00c6: 52 */ 0xE3 /* 'c' -> */, -/* pos 00c7: 53 */ 0xE5 /* 'e' -> */, -/* pos 00c8: 54 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x00CF state 55) */, - 0x73 /* 's' */, 0x18, 0x02 /* (to 0x02E3 state 403) */, - 0x08, /* fail */ -/* pos 00cf: 55 */ 0xF4 /* 't' -> */, -/* pos 00d0: 56 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x00D7 state 57) */, - 0x2D /* '-' */, 0x37, 0x00 /* (to 0x010A state 87) */, - 0x08, /* fail */ -/* pos 00d7: 57 */ 0x00, 0x09 /* - terminal marker 9 - */, -/* pos 00d9: 58 */ 0xE6 /* 'f' -> */, -/* pos 00da: 59 */ 0xAD /* '-' -> */, -/* pos 00db: 60 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x00E8 state 61) */, - 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x00FE state 76) */, - 0x72 /* 'r' */, 0x2A, 0x01 /* (to 0x020B state 255) */, - 0x75 /* 'u' */, 0x2E, 0x01 /* (to 0x0212 state 261) */, - 0x08, /* fail */ -/* pos 00e8: 61 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x00EF state 62) */, - 0x61 /* 'a' */, 0x1A, 0x01 /* (to 0x0205 state 250) */, - 0x08, /* fail */ -/* pos 00ef: 62 */ 0xE4 /* 'd' -> */, -/* pos 00f0: 63 */ 0xE9 /* 'i' -> */, -/* pos 00f1: 64 */ 0xE6 /* 'f' -> */, -/* pos 00f2: 65 */ 0xE9 /* 'i' -> */, -/* pos 00f3: 66 */ 0xE5 /* 'e' -> */, -/* pos 00f4: 67 */ 0xE4 /* 'd' -> */, -/* pos 00f5: 68 */ 0xAD /* '-' -> */, -/* pos 00f6: 69 */ 0xF3 /* 's' -> */, -/* pos 00f7: 70 */ 0xE9 /* 'i' -> */, -/* pos 00f8: 71 */ 0xEE /* 'n' -> */, -/* pos 00f9: 72 */ 0xE3 /* 'c' -> */, -/* pos 00fa: 73 */ 0xE5 /* 'e' -> */, -/* pos 00fb: 74 */ 0xBA /* ':' -> */, -/* pos 00fc: 75 */ 0x00, 0x0B /* - terminal marker 11 - */, -/* pos 00fe: 76 */ 0xEF /* 'o' -> */, -/* pos 00ff: 77 */ 0xEE /* 'n' -> */, -/* pos 0100: 78 */ 0xE5 /* 'e' -> */, -/* pos 0101: 79 */ 0xAD /* '-' -> */, -/* pos 0102: 80 */ 0xED /* 'm' -> */, -/* pos 0103: 81 */ 0xE1 /* 'a' -> */, -/* pos 0104: 82 */ 0xF4 /* 't' -> */, -/* pos 0105: 83 */ 0xE3 /* 'c' -> */, -/* pos 0106: 84 */ 0xE8 /* 'h' -> */, -/* pos 0107: 85 */ 0xBA /* ':' -> */, -/* pos 0108: 86 */ 0x00, 0x0C /* - terminal marker 12 - */, -/* pos 010a: 87 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x0117 state 88) */, - 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x0121 state 97) */, - 0x72 /* 'r' */, 0x8E, 0x00 /* (to 0x019E state 171) */, - 0x63 /* 'c' */, 0xF8, 0x01 /* (to 0x030B state 435) */, - 0x08, /* fail */ -/* pos 0117: 88 */ 0xEE /* 'n' -> */, -/* pos 0118: 89 */ 0xE3 /* 'c' -> */, -/* pos 0119: 90 */ 0xEF /* 'o' -> */, -/* pos 011a: 91 */ 0xE4 /* 'd' -> */, -/* pos 011b: 92 */ 0xE9 /* 'i' -> */, -/* pos 011c: 93 */ 0xEE /* 'n' -> */, -/* pos 011d: 94 */ 0xE7 /* 'g' -> */, -/* pos 011e: 95 */ 0xBA /* ':' -> */, -/* pos 011f: 96 */ 0x00, 0x0D /* - terminal marker 13 - */, -/* pos 0121: 97 */ 0xE1 /* 'a' -> */, -/* pos 0122: 98 */ 0xEE /* 'n' -> */, -/* pos 0123: 99 */ 0xE7 /* 'g' -> */, -/* pos 0124: 100 */ 0xF5 /* 'u' -> */, -/* pos 0125: 101 */ 0xE1 /* 'a' -> */, -/* pos 0126: 102 */ 0xE7 /* 'g' -> */, -/* pos 0127: 103 */ 0xE5 /* 'e' -> */, -/* pos 0128: 104 */ 0xBA /* ':' -> */, -/* pos 0129: 105 */ 0x00, 0x0E /* - terminal marker 14 - */, -/* pos 012b: 106 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0132 state 107) */, - 0x6F /* 'o' */, 0x02, 0x02 /* (to 0x0330 state 469) */, - 0x08, /* fail */ -/* pos 0132: 107 */ 0xE7 /* 'g' -> */, -/* pos 0133: 108 */ 0xED /* 'm' -> */, -/* pos 0134: 109 */ 0xE1 /* 'a' -> */, -/* pos 0135: 110 */ 0xBA /* ':' -> */, -/* pos 0136: 111 */ 0x00, 0x0F /* - terminal marker 15 - */, -/* pos 0138: 112 */ 0xE3 /* 'c' -> */, -/* pos 0139: 113 */ 0xE8 /* 'h' -> */, -/* pos 013a: 114 */ 0xE5 /* 'e' -> */, -/* pos 013b: 115 */ 0xAD /* '-' -> */, -/* pos 013c: 116 */ 0xE3 /* 'c' -> */, -/* pos 013d: 117 */ 0xEF /* 'o' -> */, -/* pos 013e: 118 */ 0xEE /* 'n' -> */, -/* pos 013f: 119 */ 0xF4 /* 't' -> */, -/* pos 0140: 120 */ 0xF2 /* 'r' -> */, -/* pos 0141: 121 */ 0xEF /* 'o' -> */, -/* pos 0142: 122 */ 0xEC /* 'l' -> */, -/* pos 0143: 123 */ 0xBA /* ':' -> */, -/* pos 0144: 124 */ 0x00, 0x10 /* - terminal marker 16 - */, -/* pos 0146: 125 */ 0xF4 /* 't' -> */, -/* pos 0147: 126 */ 0xE8 /* 'h' -> */, -/* pos 0148: 127 */ 0xEF /* 'o' -> */, -/* pos 0149: 128 */ 0xF2 /* 'r' -> */, -/* pos 014a: 129 */ 0xE9 /* 'i' -> */, -/* pos 014b: 130 */ 0xFA /* 'z' -> */, -/* pos 014c: 131 */ 0xE1 /* 'a' -> */, -/* pos 014d: 132 */ 0xF4 /* 't' -> */, -/* pos 014e: 133 */ 0xE9 /* 'i' -> */, -/* pos 014f: 134 */ 0xEF /* 'o' -> */, -/* pos 0150: 135 */ 0xEE /* 'n' -> */, -/* pos 0151: 136 */ 0xBA /* ':' -> */, -/* pos 0152: 137 */ 0x00, 0x11 /* - terminal marker 17 - */, -/* pos 0154: 138 */ 0xEB /* 'k' -> */, -/* pos 0155: 139 */ 0xE9 /* 'i' -> */, -/* pos 0156: 140 */ 0xE5 /* 'e' -> */, -/* pos 0157: 141 */ 0xBA /* ':' -> */, -/* pos 0158: 142 */ 0x00, 0x12 /* - terminal marker 18 - */, -/* pos 015a: 143 */ 0xE5 /* 'e' -> */, -/* pos 015b: 144 */ 0xEE /* 'n' -> */, -/* pos 015c: 145 */ 0xF4 /* 't' -> */, -/* pos 015d: 146 */ 0xAD /* '-' -> */, -/* pos 015e: 147 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x016E state 148) */, - 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x017F state 155) */, - 0x64 /* 'd' */, 0x4C, 0x00 /* (to 0x01B0 state 186) */, - 0x65 /* 'e' */, 0x56, 0x00 /* (to 0x01BD state 198) */, - 0x72 /* 'r' */, 0x6F, 0x00 /* (to 0x01D9 state 223) */, - 0x08, /* fail */ -/* pos 016e: 148 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0178 state 149) */, - 0x61 /* 'a' */, 0x56, 0x00 /* (to 0x01C7 state 207) */, - 0x6F /* 'o' */, 0x5C, 0x00 /* (to 0x01D0 state 215) */, - 0x08, /* fail */ -/* pos 0178: 149 */ 0xEE /* 'n' -> */, -/* pos 0179: 150 */ 0xE7 /* 'g' -> */, -/* pos 017a: 151 */ 0xF4 /* 't' -> */, -/* pos 017b: 152 */ 0xE8 /* 'h' -> */, -/* pos 017c: 153 */ 0xBA /* ':' -> */, -/* pos 017d: 154 */ 0x00, 0x13 /* - terminal marker 19 - */, -/* pos 017f: 155 */ 0xF9 /* 'y' -> */, -/* pos 0180: 156 */ 0xF0 /* 'p' -> */, -/* pos 0181: 157 */ 0xE5 /* 'e' -> */, -/* pos 0182: 158 */ 0xBA /* ':' -> */, -/* pos 0183: 159 */ 0x00, 0x14 /* - terminal marker 20 - */, -/* pos 0185: 160 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x018C state 161) */, - 0x65 /* 'e' */, 0x20, 0x02 /* (to 0x03A8 state 562) */, - 0x08, /* fail */ -/* pos 018c: 161 */ 0xF4 /* 't' -> */, -/* pos 018d: 162 */ 0xE5 /* 'e' -> */, -/* pos 018e: 163 */ 0xBA /* ':' -> */, -/* pos 018f: 164 */ 0x00, 0x15 /* - terminal marker 21 - */, -/* pos 0191: 165 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0198 state 166) */, - 0x65 /* 'e' */, 0xB6, 0x00 /* (to 0x024A state 304) */, - 0x08, /* fail */ -/* pos 0198: 166 */ 0xEE /* 'n' -> */, -/* pos 0199: 167 */ 0xE7 /* 'g' -> */, -/* pos 019a: 168 */ 0xE5 /* 'e' -> */, -/* pos 019b: 169 */ 0xBA /* ':' -> */, -/* pos 019c: 170 */ 0x00, 0x16 /* - terminal marker 22 - */, -/* pos 019e: 171 */ 0xE1 /* 'a' -> */, -/* pos 019f: 172 */ 0xEE /* 'n' -> */, -/* pos 01a0: 173 */ 0xE7 /* 'g' -> */, -/* pos 01a1: 174 */ 0xE5 /* 'e' -> */, -/* pos 01a2: 175 */ 0xF3 /* 's' -> */, -/* pos 01a3: 176 */ 0xBA /* ':' -> */, -/* pos 01a4: 177 */ 0x00, 0x19 /* - terminal marker 25 - */, -/* pos 01a6: 178 */ 0xE5 /* 'e' -> */, -/* pos 01a7: 179 */ 0xBA /* ':' -> */, -/* pos 01a8: 180 */ 0x00, 0x1B /* - terminal marker 27 - */, -/* pos 01aa: 181 */ 0xEC /* 'l' -> */, -/* pos 01ab: 182 */ 0xEF /* 'o' -> */, -/* pos 01ac: 183 */ 0xF7 /* 'w' -> */, -/* pos 01ad: 184 */ 0xBA /* ':' -> */, -/* pos 01ae: 185 */ 0x00, 0x1C /* - terminal marker 28 - */, -/* pos 01b0: 186 */ 0xE9 /* 'i' -> */, -/* pos 01b1: 187 */ 0xF3 /* 's' -> */, -/* pos 01b2: 188 */ 0xF0 /* 'p' -> */, -/* pos 01b3: 189 */ 0xEF /* 'o' -> */, -/* pos 01b4: 190 */ 0xF3 /* 's' -> */, -/* pos 01b5: 191 */ 0xE9 /* 'i' -> */, -/* pos 01b6: 192 */ 0xF4 /* 't' -> */, -/* pos 01b7: 193 */ 0xE9 /* 'i' -> */, -/* pos 01b8: 194 */ 0xEF /* 'o' -> */, -/* pos 01b9: 195 */ 0xEE /* 'n' -> */, -/* pos 01ba: 196 */ 0xBA /* ':' -> */, -/* pos 01bb: 197 */ 0x00, 0x1D /* - terminal marker 29 - */, -/* pos 01bd: 198 */ 0xEE /* 'n' -> */, -/* pos 01be: 199 */ 0xE3 /* 'c' -> */, -/* pos 01bf: 200 */ 0xEF /* 'o' -> */, -/* pos 01c0: 201 */ 0xE4 /* 'd' -> */, -/* pos 01c1: 202 */ 0xE9 /* 'i' -> */, -/* pos 01c2: 203 */ 0xEE /* 'n' -> */, -/* pos 01c3: 204 */ 0xE7 /* 'g' -> */, -/* pos 01c4: 205 */ 0xBA /* ':' -> */, -/* pos 01c5: 206 */ 0x00, 0x1E /* - terminal marker 30 - */, -/* pos 01c7: 207 */ 0xEE /* 'n' -> */, -/* pos 01c8: 208 */ 0xE7 /* 'g' -> */, -/* pos 01c9: 209 */ 0xF5 /* 'u' -> */, -/* pos 01ca: 210 */ 0xE1 /* 'a' -> */, -/* pos 01cb: 211 */ 0xE7 /* 'g' -> */, -/* pos 01cc: 212 */ 0xE5 /* 'e' -> */, -/* pos 01cd: 213 */ 0xBA /* ':' -> */, -/* pos 01ce: 214 */ 0x00, 0x1F /* - terminal marker 31 - */, -/* pos 01d0: 215 */ 0xE3 /* 'c' -> */, -/* pos 01d1: 216 */ 0xE1 /* 'a' -> */, -/* pos 01d2: 217 */ 0xF4 /* 't' -> */, -/* pos 01d3: 218 */ 0xE9 /* 'i' -> */, -/* pos 01d4: 219 */ 0xEF /* 'o' -> */, -/* pos 01d5: 220 */ 0xEE /* 'n' -> */, -/* pos 01d6: 221 */ 0xBA /* ':' -> */, -/* pos 01d7: 222 */ 0x00, 0x20 /* - terminal marker 32 - */, -/* pos 01d9: 223 */ 0xE1 /* 'a' -> */, -/* pos 01da: 224 */ 0xEE /* 'n' -> */, -/* pos 01db: 225 */ 0xE7 /* 'g' -> */, -/* pos 01dc: 226 */ 0xE5 /* 'e' -> */, -/* pos 01dd: 227 */ 0xBA /* ':' -> */, -/* pos 01de: 228 */ 0x00, 0x21 /* - terminal marker 33 - */, -/* pos 01e0: 229 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x01E7 state 230) */, - 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x01EC state 234) */, - 0x08, /* fail */ -/* pos 01e7: 230 */ 0xE1 /* 'a' -> */, -/* pos 01e8: 231 */ 0xE7 /* 'g' -> */, -/* pos 01e9: 232 */ 0xBA /* ':' -> */, -/* pos 01ea: 233 */ 0x00, 0x22 /* - terminal marker 34 - */, -/* pos 01ec: 234 */ 0xF0 /* 'p' -> */, -/* pos 01ed: 235 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x01F4 state 236) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x01F9 state 240) */, - 0x08, /* fail */ -/* pos 01f4: 236 */ 0xE3 /* 'c' -> */, -/* pos 01f5: 237 */ 0xF4 /* 't' -> */, -/* pos 01f6: 238 */ 0xBA /* ':' -> */, -/* pos 01f7: 239 */ 0x00, 0x23 /* - terminal marker 35 - */, -/* pos 01f9: 240 */ 0xF2 /* 'r' -> */, -/* pos 01fa: 241 */ 0xE5 /* 'e' -> */, -/* pos 01fb: 242 */ 0xF3 /* 's' -> */, -/* pos 01fc: 243 */ 0xBA /* ':' -> */, -/* pos 01fd: 244 */ 0x00, 0x24 /* - terminal marker 36 - */, -/* pos 01ff: 245 */ 0xF2 /* 'r' -> */, -/* pos 0200: 246 */ 0xEF /* 'o' -> */, -/* pos 0201: 247 */ 0xED /* 'm' -> */, -/* pos 0202: 248 */ 0xBA /* ':' -> */, -/* pos 0203: 249 */ 0x00, 0x25 /* - terminal marker 37 - */, -/* pos 0205: 250 */ 0xF4 /* 't' -> */, -/* pos 0206: 251 */ 0xE3 /* 'c' -> */, -/* pos 0207: 252 */ 0xE8 /* 'h' -> */, -/* pos 0208: 253 */ 0xBA /* ':' -> */, -/* pos 0209: 254 */ 0x00, 0x26 /* - terminal marker 38 - */, -/* pos 020b: 255 */ 0xE1 /* 'a' -> */, -/* pos 020c: 256 */ 0xEE /* 'n' -> */, -/* pos 020d: 257 */ 0xE7 /* 'g' -> */, -/* pos 020e: 258 */ 0xE5 /* 'e' -> */, -/* pos 020f: 259 */ 0xBA /* ':' -> */, -/* pos 0210: 260 */ 0x00, 0x27 /* - terminal marker 39 - */, -/* pos 0212: 261 */ 0xEE /* 'n' -> */, -/* pos 0213: 262 */ 0xED /* 'm' -> */, -/* pos 0214: 263 */ 0xEF /* 'o' -> */, -/* pos 0215: 264 */ 0xE4 /* 'd' -> */, -/* pos 0216: 265 */ 0xE9 /* 'i' -> */, -/* pos 0217: 266 */ 0xE6 /* 'f' -> */, -/* pos 0218: 267 */ 0xE9 /* 'i' -> */, -/* pos 0219: 268 */ 0xE5 /* 'e' -> */, -/* pos 021a: 269 */ 0xE4 /* 'd' -> */, -/* pos 021b: 270 */ 0xAD /* '-' -> */, -/* pos 021c: 271 */ 0xF3 /* 's' -> */, -/* pos 021d: 272 */ 0xE9 /* 'i' -> */, -/* pos 021e: 273 */ 0xEE /* 'n' -> */, -/* pos 021f: 274 */ 0xE3 /* 'c' -> */, -/* pos 0220: 275 */ 0xE5 /* 'e' -> */, -/* pos 0221: 276 */ 0xBA /* ':' -> */, -/* pos 0222: 277 */ 0x00, 0x28 /* - terminal marker 40 - */, -/* pos 0224: 278 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x022E state 279) */, - 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x023C state 292) */, - 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0241 state 296) */, - 0x08, /* fail */ -/* pos 022e: 279 */ 0xF3 /* 's' -> */, -/* pos 022f: 280 */ 0xF4 /* 't' -> */, -/* pos 0230: 281 */ 0xAD /* '-' -> */, -/* pos 0231: 282 */ 0xED /* 'm' -> */, -/* pos 0232: 283 */ 0xEF /* 'o' -> */, -/* pos 0233: 284 */ 0xE4 /* 'd' -> */, -/* pos 0234: 285 */ 0xE9 /* 'i' -> */, -/* pos 0235: 286 */ 0xE6 /* 'f' -> */, -/* pos 0236: 287 */ 0xE9 /* 'i' -> */, -/* pos 0237: 288 */ 0xE5 /* 'e' -> */, -/* pos 0238: 289 */ 0xE4 /* 'd' -> */, -/* pos 0239: 290 */ 0xBA /* ':' -> */, -/* pos 023a: 291 */ 0x00, 0x29 /* - terminal marker 41 - */, -/* pos 023c: 292 */ 0xEE /* 'n' -> */, -/* pos 023d: 293 */ 0xEB /* 'k' -> */, -/* pos 023e: 294 */ 0xBA /* ':' -> */, -/* pos 023f: 295 */ 0x00, 0x2A /* - terminal marker 42 - */, -/* pos 0241: 296 */ 0xE3 /* 'c' -> */, -/* pos 0242: 297 */ 0xE1 /* 'a' -> */, -/* pos 0243: 298 */ 0xF4 /* 't' -> */, -/* pos 0244: 299 */ 0xE9 /* 'i' -> */, -/* pos 0245: 300 */ 0xEF /* 'o' -> */, -/* pos 0246: 301 */ 0xEE /* 'n' -> */, -/* pos 0247: 302 */ 0xBA /* ':' -> */, -/* pos 0248: 303 */ 0x00, 0x2B /* - terminal marker 43 - */, -/* pos 024a: 304 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x0254 state 305) */, - 0x74 /* 't' */, 0x14, 0x00 /* (to 0x0261 state 311) */, - 0x70 /* 'p' */, 0x6C, 0x01 /* (to 0x03BC state 578) */, - 0x08, /* fail */ -/* pos 0254: 305 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x025B state 306) */, - 0x65 /* 'e' */, 0xAE, 0x00 /* (to 0x0305 state 430) */, - 0x08, /* fail */ -/* pos 025b: 306 */ 0xE5 /* 'e' -> */, -/* pos 025c: 307 */ 0xF3 /* 's' -> */, -/* pos 025d: 308 */ 0xE8 /* 'h' -> */, -/* pos 025e: 309 */ 0xBA /* ':' -> */, -/* pos 025f: 310 */ 0x00, 0x2F /* - terminal marker 47 - */, -/* pos 0261: 311 */ 0xF2 /* 'r' -> */, -/* pos 0262: 312 */ 0xF9 /* 'y' -> */, -/* pos 0263: 313 */ 0xAD /* '-' -> */, -/* pos 0264: 314 */ 0xE1 /* 'a' -> */, -/* pos 0265: 315 */ 0xE6 /* 'f' -> */, -/* pos 0266: 316 */ 0xF4 /* 't' -> */, -/* pos 0267: 317 */ 0xE5 /* 'e' -> */, -/* pos 0268: 318 */ 0xF2 /* 'r' -> */, -/* pos 0269: 319 */ 0xBA /* ':' -> */, -/* pos 026a: 320 */ 0x00, 0x30 /* - terminal marker 48 - */, -/* pos 026c: 321 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0273 state 322) */, - 0x74 /* 't' */, 0xEA, 0x00 /* (to 0x0359 state 496) */, - 0x08, /* fail */ -/* pos 0273: 322 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x027A state 323) */, - 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x0280 state 328) */, - 0x08, /* fail */ -/* pos 027a: 323 */ 0xF6 /* 'v' -> */, -/* pos 027b: 324 */ 0xE5 /* 'e' -> */, -/* pos 027c: 325 */ 0xF2 /* 'r' -> */, -/* pos 027d: 326 */ 0xBA /* ':' -> */, -/* pos 027e: 327 */ 0x00, 0x31 /* - terminal marker 49 - */, -/* pos 0280: 328 */ 0xAD /* '-' -> */, -/* pos 0281: 329 */ 0xE3 /* 'c' -> */, -/* pos 0282: 330 */ 0xEF /* 'o' -> */, -/* pos 0283: 331 */ 0xEF /* 'o' -> */, -/* pos 0284: 332 */ 0xEB /* 'k' -> */, -/* pos 0285: 333 */ 0xE9 /* 'i' -> */, -/* pos 0286: 334 */ 0xE5 /* 'e' -> */, -/* pos 0287: 335 */ 0xBA /* ':' -> */, -/* pos 0288: 336 */ 0x00, 0x32 /* - terminal marker 50 - */, -/* pos 028a: 337 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0291 state 338) */, - 0x65 /* 'e' */, 0x2C, 0x01 /* (to 0x03B9 state 576) */, - 0x08, /* fail */ -/* pos 0291: 338 */ 0xE1 /* 'a' -> */, -/* pos 0292: 339 */ 0xEE /* 'n' -> */, -/* pos 0293: 340 */ 0xF3 /* 's' -> */, -/* pos 0294: 341 */ 0xE6 /* 'f' -> */, -/* pos 0295: 342 */ 0xE5 /* 'e' -> */, -/* pos 0296: 343 */ 0xF2 /* 'r' -> */, -/* pos 0297: 344 */ 0xAD /* '-' -> */, -/* pos 0298: 345 */ 0xE5 /* 'e' -> */, -/* pos 0299: 346 */ 0xEE /* 'n' -> */, -/* pos 029a: 347 */ 0xE3 /* 'c' -> */, -/* pos 029b: 348 */ 0xEF /* 'o' -> */, -/* pos 029c: 349 */ 0xE4 /* 'd' -> */, -/* pos 029d: 350 */ 0xE9 /* 'i' -> */, -/* pos 029e: 351 */ 0xEE /* 'n' -> */, -/* pos 029f: 352 */ 0xE7 /* 'g' -> */, -/* pos 02a0: 353 */ 0xBA /* ':' -> */, -/* pos 02a1: 354 */ 0x00, 0x34 /* - terminal marker 52 - */, -/* pos 02a3: 355 */ 0xE9 /* 'i' -> */, -/* pos 02a4: 356 */ 0xAD /* '-' -> */, -/* pos 02a5: 357 */ 0xE1 /* 'a' -> */, -/* pos 02a6: 358 */ 0xF2 /* 'r' -> */, -/* pos 02a7: 359 */ 0xE7 /* 'g' -> */, -/* pos 02a8: 360 */ 0xF3 /* 's' -> */, -/* pos 02a9: 361 */ 0x00, 0x3C /* - terminal marker 60 - */, -/* pos 02ab: 362 */ 0xA0 /* ' ' -> */, -/* pos 02ac: 363 */ 0x00, 0x3F /* - terminal marker 63 - */, -/* pos 02ae: 364 */ 0xAD /* '-' -> */, -/* pos 02af: 365 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x02B9 state 366) */, - 0x61 /* 'a' */, 0x1D, 0x00 /* (to 0x02CF state 385) */, - 0x72 /* 'r' */, 0xFB, 0x00 /* (to 0x03B0 state 568) */, - 0x08, /* fail */ -/* pos 02b9: 366 */ 0xEF /* 'o' -> */, -/* pos 02ba: 367 */ 0xF2 /* 'r' -> */, -/* pos 02bb: 368 */ 0xF7 /* 'w' -> */, -/* pos 02bc: 369 */ 0xE1 /* 'a' -> */, -/* pos 02bd: 370 */ 0xF2 /* 'r' -> */, -/* pos 02be: 371 */ 0xE4 /* 'd' -> */, -/* pos 02bf: 372 */ 0xE5 /* 'e' -> */, -/* pos 02c0: 373 */ 0xE4 /* 'd' -> */, -/* pos 02c1: 374 */ 0xAD /* '-' -> */, -/* pos 02c2: 375 */ 0xE6 /* 'f' -> */, -/* pos 02c3: 376 */ 0xEF /* 'o' -> */, -/* pos 02c4: 377 */ 0xF2 /* 'r' -> */, -/* pos 02c5: 378 */ 0xBA /* ':' -> */, -/* pos 02c6: 379 */ 0x00, 0x40 /* - terminal marker 64 - */, -/* pos 02c8: 380 */ 0x00, 0x41 /* - terminal marker 65 - */, -/* pos 02ca: 381 */ 0xE1 /* 'a' -> */, -/* pos 02cb: 382 */ 0xE4 /* 'd' -> */, -/* pos 02cc: 383 */ 0xA0 /* ' ' -> */, -/* pos 02cd: 384 */ 0x00, 0x42 /* - terminal marker 66 - */, -/* pos 02cf: 385 */ 0xF5 /* 'u' -> */, -/* pos 02d0: 386 */ 0xF4 /* 't' -> */, -/* pos 02d1: 387 */ 0xE8 /* 'h' -> */, -/* pos 02d2: 388 */ 0xAD /* '-' -> */, -/* pos 02d3: 389 */ 0xF4 /* 't' -> */, -/* pos 02d4: 390 */ 0xEF /* 'o' -> */, -/* pos 02d5: 391 */ 0xEB /* 'k' -> */, -/* pos 02d6: 392 */ 0xE5 /* 'e' -> */, -/* pos 02d7: 393 */ 0xEE /* 'n' -> */, -/* pos 02d8: 394 */ 0xBA /* ':' -> */, -/* pos 02d9: 395 */ 0x00, 0x45 /* - terminal marker 69 - */, -/* pos 02db: 396 */ 0xF4 /* 't' -> */, -/* pos 02dc: 397 */ 0xE9 /* 'i' -> */, -/* pos 02dd: 398 */ 0xEF /* 'o' -> */, -/* pos 02de: 399 */ 0xEE /* 'n' -> */, -/* pos 02df: 400 */ 0xF3 /* 's' -> */, -/* pos 02e0: 401 */ 0xA0 /* ' ' -> */, -/* pos 02e1: 402 */ 0x00, 0x02 /* - terminal marker 2 - */, -/* pos 02e3: 403 */ 0xF3 /* 's' -> */, -/* pos 02e4: 404 */ 0xAD /* '-' -> */, -/* pos 02e5: 405 */ 0xE3 /* 'c' -> */, -/* pos 02e6: 406 */ 0xEF /* 'o' -> */, -/* pos 02e7: 407 */ 0xEE /* 'n' -> */, -/* pos 02e8: 408 */ 0xF4 /* 't' -> */, -/* pos 02e9: 409 */ 0xF2 /* 'r' -> */, -/* pos 02ea: 410 */ 0xEF /* 'o' -> */, -/* pos 02eb: 411 */ 0xEC /* 'l' -> */, -/* pos 02ec: 412 */ 0xAD /* '-' -> */, -/* pos 02ed: 413 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x02F4 state 414) */, - 0x61 /* 'a' */, 0x24, 0x00 /* (to 0x0314 state 443) */, - 0x08, /* fail */ -/* pos 02f4: 414 */ 0xE5 /* 'e' -> */, -/* pos 02f5: 415 */ 0xF1 /* 'q' -> */, -/* pos 02f6: 416 */ 0xF5 /* 'u' -> */, -/* pos 02f7: 417 */ 0xE5 /* 'e' -> */, -/* pos 02f8: 418 */ 0xF3 /* 's' -> */, -/* pos 02f9: 419 */ 0xF4 /* 't' -> */, -/* pos 02fa: 420 */ 0xAD /* '-' -> */, -/* pos 02fb: 421 */ 0xE8 /* 'h' -> */, -/* pos 02fc: 422 */ 0xE5 /* 'e' -> */, -/* pos 02fd: 423 */ 0xE1 /* 'a' -> */, -/* pos 02fe: 424 */ 0xE4 /* 'd' -> */, -/* pos 02ff: 425 */ 0xE5 /* 'e' -> */, -/* pos 0300: 426 */ 0xF2 /* 'r' -> */, -/* pos 0301: 427 */ 0xF3 /* 's' -> */, -/* pos 0302: 428 */ 0xBA /* ':' -> */, -/* pos 0303: 429 */ 0x00, 0x0A /* - terminal marker 10 - */, -/* pos 0305: 430 */ 0xF2 /* 'r' -> */, -/* pos 0306: 431 */ 0xE5 /* 'e' -> */, -/* pos 0307: 432 */ 0xF2 /* 'r' -> */, -/* pos 0308: 433 */ 0xBA /* ':' -> */, -/* pos 0309: 434 */ 0x00, 0x17 /* - terminal marker 23 - */, -/* pos 030b: 435 */ 0xE8 /* 'h' -> */, -/* pos 030c: 436 */ 0xE1 /* 'a' -> */, -/* pos 030d: 437 */ 0xF2 /* 'r' -> */, -/* pos 030e: 438 */ 0xF3 /* 's' -> */, -/* pos 030f: 439 */ 0xE5 /* 'e' -> */, -/* pos 0310: 440 */ 0xF4 /* 't' -> */, -/* pos 0311: 441 */ 0xBA /* ':' -> */, -/* pos 0312: 442 */ 0x00, 0x18 /* - terminal marker 24 - */, -/* pos 0314: 443 */ 0xEC /* 'l' -> */, -/* pos 0315: 444 */ 0xEC /* 'l' -> */, -/* pos 0316: 445 */ 0xEF /* 'o' -> */, -/* pos 0317: 446 */ 0xF7 /* 'w' -> */, -/* pos 0318: 447 */ 0xAD /* '-' -> */, -/* pos 0319: 448 */ 0xEF /* 'o' -> */, -/* pos 031a: 449 */ 0xF2 /* 'r' -> */, -/* pos 031b: 450 */ 0xE9 /* 'i' -> */, -/* pos 031c: 451 */ 0xE7 /* 'g' -> */, -/* pos 031d: 452 */ 0xE9 /* 'i' -> */, -/* pos 031e: 453 */ 0xEE /* 'n' -> */, -/* pos 031f: 454 */ 0xBA /* ':' -> */, -/* pos 0320: 455 */ 0x00, 0x1A /* - terminal marker 26 - */, -/* pos 0322: 456 */ 0xE1 /* 'a' -> */, -/* pos 0323: 457 */ 0xF8 /* 'x' -> */, -/* pos 0324: 458 */ 0xAD /* '-' -> */, -/* pos 0325: 459 */ 0xE6 /* 'f' -> */, -/* pos 0326: 460 */ 0xEF /* 'o' -> */, -/* pos 0327: 461 */ 0xF2 /* 'r' -> */, -/* pos 0328: 462 */ 0xF7 /* 'w' -> */, -/* pos 0329: 463 */ 0xE1 /* 'a' -> */, -/* pos 032a: 464 */ 0xF2 /* 'r' -> */, -/* pos 032b: 465 */ 0xE4 /* 'd' -> */, -/* pos 032c: 466 */ 0xF3 /* 's' -> */, -/* pos 032d: 467 */ 0xBA /* ':' -> */, -/* pos 032e: 468 */ 0x00, 0x2C /* - terminal marker 44 - */, -/* pos 0330: 469 */ 0xF8 /* 'x' -> */, -/* pos 0331: 470 */ 0xF9 /* 'y' -> */, -/* pos 0332: 471 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0339 state 472) */, - 0x20 /* ' ' */, 0x79, 0x00 /* (to 0x03AE state 567) */, - 0x08, /* fail */ -/* pos 0339: 472 */ 0xE1 /* 'a' -> */, -/* pos 033a: 473 */ 0xF5 /* 'u' -> */, -/* pos 033b: 474 */ 0xF4 /* 't' -> */, -/* pos 033c: 475 */ 0xE8 /* 'h' -> */, -/* pos 033d: 476 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0344 state 477) */, - 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x034E state 486) */, - 0x08, /* fail */ -/* pos 0344: 477 */ 0xEE /* 'n' -> */, -/* pos 0345: 478 */ 0xF4 /* 't' -> */, -/* pos 0346: 479 */ 0xE9 /* 'i' -> */, -/* pos 0347: 480 */ 0xE3 /* 'c' -> */, -/* pos 0348: 481 */ 0xE1 /* 'a' -> */, -/* pos 0349: 482 */ 0xF4 /* 't' -> */, -/* pos 034a: 483 */ 0xE5 /* 'e' -> */, -/* pos 034b: 484 */ 0xBA /* ':' -> */, -/* pos 034c: 485 */ 0x00, 0x2D /* - terminal marker 45 - */, -/* pos 034e: 486 */ 0xF2 /* 'r' -> */, -/* pos 034f: 487 */ 0xE9 /* 'i' -> */, -/* pos 0350: 488 */ 0xFA /* 'z' -> */, -/* pos 0351: 489 */ 0xE1 /* 'a' -> */, -/* pos 0352: 490 */ 0xF4 /* 't' -> */, -/* pos 0353: 491 */ 0xE9 /* 'i' -> */, -/* pos 0354: 492 */ 0xEF /* 'o' -> */, -/* pos 0355: 493 */ 0xEE /* 'n' -> */, -/* pos 0356: 494 */ 0xBA /* ':' -> */, -/* pos 0357: 495 */ 0x00, 0x2E /* - terminal marker 46 - */, -/* pos 0359: 496 */ 0xF2 /* 'r' -> */, -/* pos 035a: 497 */ 0xE9 /* 'i' -> */, -/* pos 035b: 498 */ 0xE3 /* 'c' -> */, -/* pos 035c: 499 */ 0xF4 /* 't' -> */, -/* pos 035d: 500 */ 0xAD /* '-' -> */, -/* pos 035e: 501 */ 0xF4 /* 't' -> */, -/* pos 035f: 502 */ 0xF2 /* 'r' -> */, -/* pos 0360: 503 */ 0xE1 /* 'a' -> */, -/* pos 0361: 504 */ 0xEE /* 'n' -> */, -/* pos 0362: 505 */ 0xF3 /* 's' -> */, -/* pos 0363: 506 */ 0xF0 /* 'p' -> */, -/* pos 0364: 507 */ 0xEF /* 'o' -> */, -/* pos 0365: 508 */ 0xF2 /* 'r' -> */, -/* pos 0366: 509 */ 0xF4 /* 't' -> */, -/* pos 0367: 510 */ 0xAD /* '-' -> */, -/* pos 0368: 511 */ 0xF3 /* 's' -> */, -/* pos 0369: 512 */ 0xE5 /* 'e' -> */, -/* pos 036a: 513 */ 0xE3 /* 'c' -> */, -/* pos 036b: 514 */ 0xF5 /* 'u' -> */, -/* pos 036c: 515 */ 0xF2 /* 'r' -> */, -/* pos 036d: 516 */ 0xE9 /* 'i' -> */, -/* pos 036e: 517 */ 0xF4 /* 't' -> */, -/* pos 036f: 518 */ 0xF9 /* 'y' -> */, -/* pos 0370: 519 */ 0xBA /* ':' -> */, -/* pos 0371: 520 */ 0x00, 0x33 /* - terminal marker 51 - */, -/* pos 0373: 521 */ 0xE5 /* 'e' -> */, -/* pos 0374: 522 */ 0xF2 /* 'r' -> */, -/* pos 0375: 523 */ 0xAD /* '-' -> */, -/* pos 0376: 524 */ 0xE1 /* 'a' -> */, -/* pos 0377: 525 */ 0xE7 /* 'g' -> */, -/* pos 0378: 526 */ 0xE5 /* 'e' -> */, -/* pos 0379: 527 */ 0xEE /* 'n' -> */, -/* pos 037a: 528 */ 0xF4 /* 't' -> */, -/* pos 037b: 529 */ 0xBA /* ':' -> */, -/* pos 037c: 530 */ 0x00, 0x35 /* - terminal marker 53 - */, -/* pos 037e: 531 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0385 state 532) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x038A state 536) */, - 0x08, /* fail */ -/* pos 0385: 532 */ 0xF2 /* 'r' -> */, -/* pos 0386: 533 */ 0xF9 /* 'y' -> */, -/* pos 0387: 534 */ 0xBA /* ':' -> */, -/* pos 0388: 535 */ 0x00, 0x36 /* - terminal marker 54 - */, -/* pos 038a: 536 */ 0xE1 /* 'a' -> */, -/* pos 038b: 537 */ 0xBA /* ':' -> */, -/* pos 038c: 538 */ 0x00, 0x37 /* - terminal marker 55 - */, -/* pos 038e: 539 */ 0xF7 /* 'w' -> */, -/* pos 038f: 540 */ 0xF7 /* 'w' -> */, -/* pos 0390: 541 */ 0xAD /* '-' -> */, -/* pos 0391: 542 */ 0xE1 /* 'a' -> */, -/* pos 0392: 543 */ 0xF5 /* 'u' -> */, -/* pos 0393: 544 */ 0xF4 /* 't' -> */, -/* pos 0394: 545 */ 0xE8 /* 'h' -> */, -/* pos 0395: 546 */ 0xE5 /* 'e' -> */, -/* pos 0396: 547 */ 0xEE /* 'n' -> */, -/* pos 0397: 548 */ 0xF4 /* 't' -> */, -/* pos 0398: 549 */ 0xE9 /* 'i' -> */, -/* pos 0399: 550 */ 0xE3 /* 'c' -> */, -/* pos 039a: 551 */ 0xE1 /* 'a' -> */, -/* pos 039b: 552 */ 0xF4 /* 't' -> */, -/* pos 039c: 553 */ 0xE5 /* 'e' -> */, -/* pos 039d: 554 */ 0xBA /* ':' -> */, -/* pos 039e: 555 */ 0x00, 0x38 /* - terminal marker 56 - */, -/* pos 03a0: 556 */ 0xF4 /* 't' -> */, -/* pos 03a1: 557 */ 0xE3 /* 'c' -> */, -/* pos 03a2: 558 */ 0xE8 /* 'h' -> */, -/* pos 03a3: 559 */ 0x00, 0x39 /* - terminal marker 57 - */, -/* pos 03a5: 560 */ 0xF4 /* 't' -> */, -/* pos 03a6: 561 */ 0x00, 0x3A /* - terminal marker 58 - */, -/* pos 03a8: 562 */ 0xEC /* 'l' -> */, -/* pos 03a9: 563 */ 0xE5 /* 'e' -> */, -/* pos 03aa: 564 */ 0xF4 /* 't' -> */, -/* pos 03ab: 565 */ 0xE5 /* 'e' -> */, -/* pos 03ac: 566 */ 0x00, 0x3B /* - terminal marker 59 - */, -/* pos 03ae: 567 */ 0x00, 0x3D /* - terminal marker 61 - */, -/* pos 03b0: 568 */ 0xE5 /* 'e' -> */, -/* pos 03b1: 569 */ 0xE1 /* 'a' -> */, -/* pos 03b2: 570 */ 0xEC /* 'l' -> */, -/* pos 03b3: 571 */ 0xAD /* '-' -> */, -/* pos 03b4: 572 */ 0xE9 /* 'i' -> */, -/* pos 03b5: 573 */ 0xF0 /* 'p' -> */, -/* pos 03b6: 574 */ 0xBA /* ':' -> */, -/* pos 03b7: 575 */ 0x00, 0x3E /* - terminal marker 62 - */, -/* pos 03b9: 576 */ 0xBA /* ':' -> */, -/* pos 03ba: 577 */ 0x00, 0x43 /* - terminal marker 67 - */, -/* pos 03bc: 578 */ 0xEC /* 'l' -> */, -/* pos 03bd: 579 */ 0xE1 /* 'a' -> */, -/* pos 03be: 580 */ 0xF9 /* 'y' -> */, -/* pos 03bf: 581 */ 0xAD /* '-' -> */, -/* pos 03c0: 582 */ 0xEE /* 'n' -> */, -/* pos 03c1: 583 */ 0xEF /* 'o' -> */, -/* pos 03c2: 584 */ 0xEE /* 'n' -> */, -/* pos 03c3: 585 */ 0xE3 /* 'c' -> */, -/* pos 03c4: 586 */ 0xE5 /* 'e' -> */, -/* pos 03c5: 587 */ 0xBA /* ':' -> */, -/* pos 03c6: 588 */ 0x00, 0x44 /* - terminal marker 68 - */, -/* total size 968 bytes */ -#endif - -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) - /* 0: 0: get */ - /* 1: 1: post */ - /* 2: 3: host: */ - /* 3: 4: connection: */ - /* 4: 5: upgrade: */ - /* 5: 6: origin: */ - /* 6: 7: sec-websocket-draft: */ - /* 7: 8: - */ - /* 8: 9: sec-websocket-extensions: */ - /* 9: 10: sec-websocket-key1: */ - /* 10: 11: sec-websocket-key2: */ - /* 11: 12: sec-websocket-protocol: */ - /* 12: 13: sec-websocket-accept: */ - /* 13: 14: sec-websocket-nonce: */ - /* 14: 15: http/1.1 */ - /* 15: 17: accept: */ - /* 16: 19: if-modified-since: */ - /* 17: 20: if-none-match: */ - /* 18: 21: accept-encoding: */ - /* 19: 22: accept-language: */ - /* 20: 23: pragma: */ - /* 21: 24: cache-control: */ - /* 22: 25: authorization: */ - /* 23: 26: cookie: */ - /* 24: 27: content-length: */ - /* 25: 28: content-type: */ - /* 26: 29: date: */ - /* 27: 30: range: */ - /* 28: 32: sec-websocket-key: */ - /* 29: 33: sec-websocket-version: */ - /* 30: 34: sec-websocket-origin: */ - /* 31: 41: accept-ranges: */ - /* 32: 43: age: */ - /* 33: 44: allow: */ - /* 34: 45: content-disposition: */ - /* 35: 46: content-encoding: */ - /* 36: 47: content-language: */ - /* 37: 48: content-location: */ - /* 38: 49: content-range: */ - /* 39: 50: etag: */ - /* 40: 51: expect: */ - /* 41: 52: expires: */ - /* 42: 53: from: */ - /* 43: 54: if-match: */ - /* 44: 55: if-range: */ - /* 45: 56: if-unmodified-since: */ - /* 46: 57: last-modified: */ - /* 47: 58: link: */ - /* 48: 59: location: */ - /* 49: 63: refresh: */ - /* 50: 64: retry-after: */ - /* 51: 65: server: */ - /* 52: 66: set-cookie: */ - /* 53: 68: transfer-encoding: */ - /* 54: 76: uri-args */ - /* 55: 79: http/1.0 */ - /* 56: 80: x-forwarded-for: */ - /* 57: 81: connect */ - /* 58: 82: head */ - /* 59: 86: x-auth-token: */ - /* 60: 87: */ -/* pos 0000: 0 */ 0x67 /* 'g' */, 0x3D, 0x00 /* (to 0x003D state 1) */, - 0x70 /* 'p' */, 0x3F, 0x00 /* (to 0x0042 state 5) */, - 0x68 /* 'h' */, 0x4E, 0x00 /* (to 0x0054 state 10) */, - 0x63 /* 'c' */, 0x5A, 0x00 /* (to 0x0063 state 15) */, - 0x75 /* 'u' */, 0x7B, 0x00 /* (to 0x0087 state 26) */, - 0x6F /* 'o' */, 0x8A, 0x00 /* (to 0x0099 state 34) */, - 0x0D /* '.' */, 0x95, 0x00 /* (to 0x00A7 state 41) */, - 0x61 /* 'a' */, 0xA4, 0x00 /* (to 0x00B9 state 51) */, - 0x69 /* 'i' */, 0xC1, 0x00 /* (to 0x00D9 state 58) */, - 0x64 /* 'd' */, 0x6A, 0x01 /* (to 0x0185 state 160) */, - 0x72 /* 'r' */, 0x73, 0x01 /* (to 0x0191 state 165) */, - 0x65 /* 'e' */, 0xBF, 0x01 /* (to 0x01E0 state 229) */, - 0x66 /* 'f' */, 0xDB, 0x01 /* (to 0x01FF state 245) */, - 0x6C /* 'l' */, 0xFD, 0x01 /* (to 0x0224 state 278) */, - 0x73 /* 's' */, 0x42, 0x02 /* (to 0x026C state 321) */, - 0x74 /* 't' */, 0x60, 0x02 /* (to 0x028D state 337) */, - 0x78 /* 'x' */, 0x81, 0x02 /* (to 0x02B1 state 364) */, - 0x6D /* 'm' */, 0xF2, 0x02 /* (to 0x0325 state 456) */, - 0x76 /* 'v' */, 0x4B, 0x03 /* (to 0x0381 state 531) */, - 0x77 /* 'w' */, 0x58, 0x03 /* (to 0x0391 state 539) */, - 0x08, /* fail */ -/* pos 003d: 1 */ 0xE5 /* 'e' -> */, -/* pos 003e: 2 */ 0xF4 /* 't' -> */, -/* pos 003f: 3 */ 0xA0 /* ' ' -> */, -/* pos 0040: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, -/* pos 0042: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x004F state 6) */, - 0x72 /* 'r' */, 0xE6, 0x00 /* (to 0x012B state 106) */, - 0x61 /* 'a' */, 0x5B, 0x03 /* (to 0x03A3 state 556) */, - 0x75 /* 'u' */, 0x5D, 0x03 /* (to 0x03A8 state 560) */, - 0x08, /* fail */ -/* pos 004f: 6 */ 0xF3 /* 's' -> */, -/* pos 0050: 7 */ 0xF4 /* 't' -> */, -/* pos 0051: 8 */ 0xA0 /* ' ' -> */, -/* pos 0052: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, -/* pos 0054: 10 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x005E state 11) */, - 0x74 /* 't' */, 0x53, 0x00 /* (to 0x00AA state 43) */, - 0x65 /* 'e' */, 0x73, 0x02 /* (to 0x02CD state 381) */, - 0x08, /* fail */ -/* pos 005e: 11 */ 0xF3 /* 's' -> */, -/* pos 005f: 12 */ 0xF4 /* 't' -> */, -/* pos 0060: 13 */ 0xBA /* ':' -> */, -/* pos 0061: 14 */ 0x00, 0x02 /* - terminal marker 2 - */, -/* pos 0063: 15 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x006A state 16) */, - 0x61 /* 'a' */, 0xD2, 0x00 /* (to 0x0138 state 112) */, - 0x08, /* fail */ -/* pos 006a: 16 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0071 state 17) */, - 0x6F /* 'o' */, 0xE7, 0x00 /* (to 0x0154 state 138) */, - 0x08, /* fail */ -/* pos 0071: 17 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0078 state 18) */, - 0x74 /* 't' */, 0xE6, 0x00 /* (to 0x015A state 143) */, - 0x08, /* fail */ -/* pos 0078: 18 */ 0xE5 /* 'e' -> */, -/* pos 0079: 19 */ 0xE3 /* 'c' -> */, -/* pos 007a: 20 */ 0xF4 /* 't' -> */, -/* pos 007b: 21 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0082 state 22) */, - 0x20 /* ' ' */, 0x4D, 0x02 /* (to 0x02CB state 380) */, - 0x08, /* fail */ -/* pos 0082: 22 */ 0xEF /* 'o' -> */, -/* pos 0083: 23 */ 0xEE /* 'n' -> */, -/* pos 0084: 24 */ 0xBA /* ':' -> */, -/* pos 0085: 25 */ 0x00, 0x03 /* - terminal marker 3 - */, -/* pos 0087: 26 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x0091 state 27) */, - 0x72 /* 'r' */, 0x1C, 0x02 /* (to 0x02A6 state 355) */, - 0x73 /* 's' */, 0xE9, 0x02 /* (to 0x0376 state 521) */, - 0x08, /* fail */ -/* pos 0091: 27 */ 0xE7 /* 'g' -> */, -/* pos 0092: 28 */ 0xF2 /* 'r' -> */, -/* pos 0093: 29 */ 0xE1 /* 'a' -> */, -/* pos 0094: 30 */ 0xE4 /* 'd' -> */, -/* pos 0095: 31 */ 0xE5 /* 'e' -> */, -/* pos 0096: 32 */ 0xBA /* ':' -> */, -/* pos 0097: 33 */ 0x00, 0x04 /* - terminal marker 4 - */, -/* pos 0099: 34 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x00A0 state 35) */, - 0x70 /* 'p' */, 0x42, 0x02 /* (to 0x02DE state 396) */, - 0x08, /* fail */ -/* pos 00a0: 35 */ 0xE9 /* 'i' -> */, -/* pos 00a1: 36 */ 0xE7 /* 'g' -> */, -/* pos 00a2: 37 */ 0xE9 /* 'i' -> */, -/* pos 00a3: 38 */ 0xEE /* 'n' -> */, -/* pos 00a4: 39 */ 0xBA /* ':' -> */, -/* pos 00a5: 40 */ 0x00, 0x05 /* - terminal marker 5 - */, -/* pos 00a7: 41 */ 0x8A /* '.' -> */, -/* pos 00a8: 42 */ 0x00, 0x07 /* - terminal marker 7 - */, -/* pos 00aa: 43 */ 0xF4 /* 't' -> */, -/* pos 00ab: 44 */ 0xF0 /* 'p' -> */, -/* pos 00ac: 45 */ 0xAF /* '/' -> */, -/* pos 00ad: 46 */ 0xB1 /* '1' -> */, -/* pos 00ae: 47 */ 0xAE /* '.' -> */, -/* pos 00af: 48 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x00B6 state 49) */, - 0x30 /* '0' */, 0xFC, 0x01 /* (to 0x02AE state 362) */, - 0x08, /* fail */ -/* pos 00b6: 49 */ 0xA0 /* ' ' -> */, -/* pos 00b7: 50 */ 0x00, 0x0E /* - terminal marker 14 - */, -/* pos 00b9: 51 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x00C6 state 52) */, - 0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x0146 state 125) */, - 0x67 /* 'g' */, 0xE7, 0x00 /* (to 0x01A6 state 178) */, - 0x6C /* 'l' */, 0xE8, 0x00 /* (to 0x01AA state 181) */, - 0x08, /* fail */ -/* pos 00c6: 52 */ 0xE3 /* 'c' -> */, -/* pos 00c7: 53 */ 0xE5 /* 'e' -> */, -/* pos 00c8: 54 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x00CF state 55) */, - 0x73 /* 's' */, 0x1B, 0x02 /* (to 0x02E6 state 403) */, - 0x08, /* fail */ -/* pos 00cf: 55 */ 0xF4 /* 't' -> */, -/* pos 00d0: 56 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x00D7 state 57) */, - 0x2D /* '-' */, 0x37, 0x00 /* (to 0x010A state 87) */, - 0x08, /* fail */ -/* pos 00d7: 57 */ 0x00, 0x0F /* - terminal marker 15 - */, -/* pos 00d9: 58 */ 0xE6 /* 'f' -> */, -/* pos 00da: 59 */ 0xAD /* '-' -> */, -/* pos 00db: 60 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x00E8 state 61) */, - 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x00FE state 76) */, - 0x72 /* 'r' */, 0x2A, 0x01 /* (to 0x020B state 255) */, - 0x75 /* 'u' */, 0x2E, 0x01 /* (to 0x0212 state 261) */, - 0x08, /* fail */ -/* pos 00e8: 61 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x00EF state 62) */, - 0x61 /* 'a' */, 0x1A, 0x01 /* (to 0x0205 state 250) */, - 0x08, /* fail */ -/* pos 00ef: 62 */ 0xE4 /* 'd' -> */, -/* pos 00f0: 63 */ 0xE9 /* 'i' -> */, -/* pos 00f1: 64 */ 0xE6 /* 'f' -> */, -/* pos 00f2: 65 */ 0xE9 /* 'i' -> */, -/* pos 00f3: 66 */ 0xE5 /* 'e' -> */, -/* pos 00f4: 67 */ 0xE4 /* 'd' -> */, -/* pos 00f5: 68 */ 0xAD /* '-' -> */, -/* pos 00f6: 69 */ 0xF3 /* 's' -> */, -/* pos 00f7: 70 */ 0xE9 /* 'i' -> */, -/* pos 00f8: 71 */ 0xEE /* 'n' -> */, -/* pos 00f9: 72 */ 0xE3 /* 'c' -> */, -/* pos 00fa: 73 */ 0xE5 /* 'e' -> */, -/* pos 00fb: 74 */ 0xBA /* ':' -> */, -/* pos 00fc: 75 */ 0x00, 0x10 /* - terminal marker 16 - */, -/* pos 00fe: 76 */ 0xEF /* 'o' -> */, -/* pos 00ff: 77 */ 0xEE /* 'n' -> */, -/* pos 0100: 78 */ 0xE5 /* 'e' -> */, -/* pos 0101: 79 */ 0xAD /* '-' -> */, -/* pos 0102: 80 */ 0xED /* 'm' -> */, -/* pos 0103: 81 */ 0xE1 /* 'a' -> */, -/* pos 0104: 82 */ 0xF4 /* 't' -> */, -/* pos 0105: 83 */ 0xE3 /* 'c' -> */, -/* pos 0106: 84 */ 0xE8 /* 'h' -> */, -/* pos 0107: 85 */ 0xBA /* ':' -> */, -/* pos 0108: 86 */ 0x00, 0x11 /* - terminal marker 17 - */, -/* pos 010a: 87 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x0117 state 88) */, - 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x0121 state 97) */, - 0x72 /* 'r' */, 0x8E, 0x00 /* (to 0x019E state 171) */, - 0x63 /* 'c' */, 0xFB, 0x01 /* (to 0x030E state 435) */, - 0x08, /* fail */ -/* pos 0117: 88 */ 0xEE /* 'n' -> */, -/* pos 0118: 89 */ 0xE3 /* 'c' -> */, -/* pos 0119: 90 */ 0xEF /* 'o' -> */, -/* pos 011a: 91 */ 0xE4 /* 'd' -> */, -/* pos 011b: 92 */ 0xE9 /* 'i' -> */, -/* pos 011c: 93 */ 0xEE /* 'n' -> */, -/* pos 011d: 94 */ 0xE7 /* 'g' -> */, -/* pos 011e: 95 */ 0xBA /* ':' -> */, -/* pos 011f: 96 */ 0x00, 0x12 /* - terminal marker 18 - */, -/* pos 0121: 97 */ 0xE1 /* 'a' -> */, -/* pos 0122: 98 */ 0xEE /* 'n' -> */, -/* pos 0123: 99 */ 0xE7 /* 'g' -> */, -/* pos 0124: 100 */ 0xF5 /* 'u' -> */, -/* pos 0125: 101 */ 0xE1 /* 'a' -> */, -/* pos 0126: 102 */ 0xE7 /* 'g' -> */, -/* pos 0127: 103 */ 0xE5 /* 'e' -> */, -/* pos 0128: 104 */ 0xBA /* ':' -> */, -/* pos 0129: 105 */ 0x00, 0x13 /* - terminal marker 19 - */, -/* pos 012b: 106 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0132 state 107) */, - 0x6F /* 'o' */, 0x05, 0x02 /* (to 0x0333 state 469) */, - 0x08, /* fail */ -/* pos 0132: 107 */ 0xE7 /* 'g' -> */, -/* pos 0133: 108 */ 0xED /* 'm' -> */, -/* pos 0134: 109 */ 0xE1 /* 'a' -> */, -/* pos 0135: 110 */ 0xBA /* ':' -> */, -/* pos 0136: 111 */ 0x00, 0x14 /* - terminal marker 20 - */, -/* pos 0138: 112 */ 0xE3 /* 'c' -> */, -/* pos 0139: 113 */ 0xE8 /* 'h' -> */, -/* pos 013a: 114 */ 0xE5 /* 'e' -> */, -/* pos 013b: 115 */ 0xAD /* '-' -> */, -/* pos 013c: 116 */ 0xE3 /* 'c' -> */, -/* pos 013d: 117 */ 0xEF /* 'o' -> */, -/* pos 013e: 118 */ 0xEE /* 'n' -> */, -/* pos 013f: 119 */ 0xF4 /* 't' -> */, -/* pos 0140: 120 */ 0xF2 /* 'r' -> */, -/* pos 0141: 121 */ 0xEF /* 'o' -> */, -/* pos 0142: 122 */ 0xEC /* 'l' -> */, -/* pos 0143: 123 */ 0xBA /* ':' -> */, -/* pos 0144: 124 */ 0x00, 0x15 /* - terminal marker 21 - */, -/* pos 0146: 125 */ 0xF4 /* 't' -> */, -/* pos 0147: 126 */ 0xE8 /* 'h' -> */, -/* pos 0148: 127 */ 0xEF /* 'o' -> */, -/* pos 0149: 128 */ 0xF2 /* 'r' -> */, -/* pos 014a: 129 */ 0xE9 /* 'i' -> */, -/* pos 014b: 130 */ 0xFA /* 'z' -> */, -/* pos 014c: 131 */ 0xE1 /* 'a' -> */, -/* pos 014d: 132 */ 0xF4 /* 't' -> */, -/* pos 014e: 133 */ 0xE9 /* 'i' -> */, -/* pos 014f: 134 */ 0xEF /* 'o' -> */, -/* pos 0150: 135 */ 0xEE /* 'n' -> */, -/* pos 0151: 136 */ 0xBA /* ':' -> */, -/* pos 0152: 137 */ 0x00, 0x16 /* - terminal marker 22 - */, -/* pos 0154: 138 */ 0xEB /* 'k' -> */, -/* pos 0155: 139 */ 0xE9 /* 'i' -> */, -/* pos 0156: 140 */ 0xE5 /* 'e' -> */, -/* pos 0157: 141 */ 0xBA /* ':' -> */, -/* pos 0158: 142 */ 0x00, 0x17 /* - terminal marker 23 - */, -/* pos 015a: 143 */ 0xE5 /* 'e' -> */, -/* pos 015b: 144 */ 0xEE /* 'n' -> */, -/* pos 015c: 145 */ 0xF4 /* 't' -> */, -/* pos 015d: 146 */ 0xAD /* '-' -> */, -/* pos 015e: 147 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x016E state 148) */, - 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x017F state 155) */, - 0x64 /* 'd' */, 0x4C, 0x00 /* (to 0x01B0 state 186) */, - 0x65 /* 'e' */, 0x56, 0x00 /* (to 0x01BD state 198) */, - 0x72 /* 'r' */, 0x6F, 0x00 /* (to 0x01D9 state 223) */, - 0x08, /* fail */ -/* pos 016e: 148 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0178 state 149) */, - 0x61 /* 'a' */, 0x56, 0x00 /* (to 0x01C7 state 207) */, - 0x6F /* 'o' */, 0x5C, 0x00 /* (to 0x01D0 state 215) */, - 0x08, /* fail */ -/* pos 0178: 149 */ 0xEE /* 'n' -> */, -/* pos 0179: 150 */ 0xE7 /* 'g' -> */, -/* pos 017a: 151 */ 0xF4 /* 't' -> */, -/* pos 017b: 152 */ 0xE8 /* 'h' -> */, -/* pos 017c: 153 */ 0xBA /* ':' -> */, -/* pos 017d: 154 */ 0x00, 0x18 /* - terminal marker 24 - */, -/* pos 017f: 155 */ 0xF9 /* 'y' -> */, -/* pos 0180: 156 */ 0xF0 /* 'p' -> */, -/* pos 0181: 157 */ 0xE5 /* 'e' -> */, -/* pos 0182: 158 */ 0xBA /* ':' -> */, -/* pos 0183: 159 */ 0x00, 0x19 /* - terminal marker 25 - */, -/* pos 0185: 160 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x018C state 161) */, - 0x65 /* 'e' */, 0x23, 0x02 /* (to 0x03AB state 562) */, - 0x08, /* fail */ -/* pos 018c: 161 */ 0xF4 /* 't' -> */, -/* pos 018d: 162 */ 0xE5 /* 'e' -> */, -/* pos 018e: 163 */ 0xBA /* ':' -> */, -/* pos 018f: 164 */ 0x00, 0x1A /* - terminal marker 26 - */, -/* pos 0191: 165 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0198 state 166) */, - 0x65 /* 'e' */, 0xB6, 0x00 /* (to 0x024A state 304) */, - 0x08, /* fail */ -/* pos 0198: 166 */ 0xEE /* 'n' -> */, -/* pos 0199: 167 */ 0xE7 /* 'g' -> */, -/* pos 019a: 168 */ 0xE5 /* 'e' -> */, -/* pos 019b: 169 */ 0xBA /* ':' -> */, -/* pos 019c: 170 */ 0x00, 0x1B /* - terminal marker 27 - */, -/* pos 019e: 171 */ 0xE1 /* 'a' -> */, -/* pos 019f: 172 */ 0xEE /* 'n' -> */, -/* pos 01a0: 173 */ 0xE7 /* 'g' -> */, -/* pos 01a1: 174 */ 0xE5 /* 'e' -> */, -/* pos 01a2: 175 */ 0xF3 /* 's' -> */, -/* pos 01a3: 176 */ 0xBA /* ':' -> */, -/* pos 01a4: 177 */ 0x00, 0x1F /* - terminal marker 31 - */, -/* pos 01a6: 178 */ 0xE5 /* 'e' -> */, -/* pos 01a7: 179 */ 0xBA /* ':' -> */, -/* pos 01a8: 180 */ 0x00, 0x20 /* - terminal marker 32 - */, -/* pos 01aa: 181 */ 0xEC /* 'l' -> */, -/* pos 01ab: 182 */ 0xEF /* 'o' -> */, -/* pos 01ac: 183 */ 0xF7 /* 'w' -> */, -/* pos 01ad: 184 */ 0xBA /* ':' -> */, -/* pos 01ae: 185 */ 0x00, 0x21 /* - terminal marker 33 - */, -/* pos 01b0: 186 */ 0xE9 /* 'i' -> */, -/* pos 01b1: 187 */ 0xF3 /* 's' -> */, -/* pos 01b2: 188 */ 0xF0 /* 'p' -> */, -/* pos 01b3: 189 */ 0xEF /* 'o' -> */, -/* pos 01b4: 190 */ 0xF3 /* 's' -> */, -/* pos 01b5: 191 */ 0xE9 /* 'i' -> */, -/* pos 01b6: 192 */ 0xF4 /* 't' -> */, -/* pos 01b7: 193 */ 0xE9 /* 'i' -> */, -/* pos 01b8: 194 */ 0xEF /* 'o' -> */, -/* pos 01b9: 195 */ 0xEE /* 'n' -> */, -/* pos 01ba: 196 */ 0xBA /* ':' -> */, -/* pos 01bb: 197 */ 0x00, 0x22 /* - terminal marker 34 - */, -/* pos 01bd: 198 */ 0xEE /* 'n' -> */, -/* pos 01be: 199 */ 0xE3 /* 'c' -> */, -/* pos 01bf: 200 */ 0xEF /* 'o' -> */, -/* pos 01c0: 201 */ 0xE4 /* 'd' -> */, -/* pos 01c1: 202 */ 0xE9 /* 'i' -> */, -/* pos 01c2: 203 */ 0xEE /* 'n' -> */, -/* pos 01c3: 204 */ 0xE7 /* 'g' -> */, -/* pos 01c4: 205 */ 0xBA /* ':' -> */, -/* pos 01c5: 206 */ 0x00, 0x23 /* - terminal marker 35 - */, -/* pos 01c7: 207 */ 0xEE /* 'n' -> */, -/* pos 01c8: 208 */ 0xE7 /* 'g' -> */, -/* pos 01c9: 209 */ 0xF5 /* 'u' -> */, -/* pos 01ca: 210 */ 0xE1 /* 'a' -> */, -/* pos 01cb: 211 */ 0xE7 /* 'g' -> */, -/* pos 01cc: 212 */ 0xE5 /* 'e' -> */, -/* pos 01cd: 213 */ 0xBA /* ':' -> */, -/* pos 01ce: 214 */ 0x00, 0x24 /* - terminal marker 36 - */, -/* pos 01d0: 215 */ 0xE3 /* 'c' -> */, -/* pos 01d1: 216 */ 0xE1 /* 'a' -> */, -/* pos 01d2: 217 */ 0xF4 /* 't' -> */, -/* pos 01d3: 218 */ 0xE9 /* 'i' -> */, -/* pos 01d4: 219 */ 0xEF /* 'o' -> */, -/* pos 01d5: 220 */ 0xEE /* 'n' -> */, -/* pos 01d6: 221 */ 0xBA /* ':' -> */, -/* pos 01d7: 222 */ 0x00, 0x25 /* - terminal marker 37 - */, -/* pos 01d9: 223 */ 0xE1 /* 'a' -> */, -/* pos 01da: 224 */ 0xEE /* 'n' -> */, -/* pos 01db: 225 */ 0xE7 /* 'g' -> */, -/* pos 01dc: 226 */ 0xE5 /* 'e' -> */, -/* pos 01dd: 227 */ 0xBA /* ':' -> */, -/* pos 01de: 228 */ 0x00, 0x26 /* - terminal marker 38 - */, -/* pos 01e0: 229 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x01E7 state 230) */, - 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x01EC state 234) */, - 0x08, /* fail */ -/* pos 01e7: 230 */ 0xE1 /* 'a' -> */, -/* pos 01e8: 231 */ 0xE7 /* 'g' -> */, -/* pos 01e9: 232 */ 0xBA /* ':' -> */, -/* pos 01ea: 233 */ 0x00, 0x27 /* - terminal marker 39 - */, -/* pos 01ec: 234 */ 0xF0 /* 'p' -> */, -/* pos 01ed: 235 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x01F4 state 236) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x01F9 state 240) */, - 0x08, /* fail */ -/* pos 01f4: 236 */ 0xE3 /* 'c' -> */, -/* pos 01f5: 237 */ 0xF4 /* 't' -> */, -/* pos 01f6: 238 */ 0xBA /* ':' -> */, -/* pos 01f7: 239 */ 0x00, 0x28 /* - terminal marker 40 - */, -/* pos 01f9: 240 */ 0xF2 /* 'r' -> */, -/* pos 01fa: 241 */ 0xE5 /* 'e' -> */, -/* pos 01fb: 242 */ 0xF3 /* 's' -> */, -/* pos 01fc: 243 */ 0xBA /* ':' -> */, -/* pos 01fd: 244 */ 0x00, 0x29 /* - terminal marker 41 - */, -/* pos 01ff: 245 */ 0xF2 /* 'r' -> */, -/* pos 0200: 246 */ 0xEF /* 'o' -> */, -/* pos 0201: 247 */ 0xED /* 'm' -> */, -/* pos 0202: 248 */ 0xBA /* ':' -> */, -/* pos 0203: 249 */ 0x00, 0x2A /* - terminal marker 42 - */, -/* pos 0205: 250 */ 0xF4 /* 't' -> */, -/* pos 0206: 251 */ 0xE3 /* 'c' -> */, -/* pos 0207: 252 */ 0xE8 /* 'h' -> */, -/* pos 0208: 253 */ 0xBA /* ':' -> */, -/* pos 0209: 254 */ 0x00, 0x2B /* - terminal marker 43 - */, -/* pos 020b: 255 */ 0xE1 /* 'a' -> */, -/* pos 020c: 256 */ 0xEE /* 'n' -> */, -/* pos 020d: 257 */ 0xE7 /* 'g' -> */, -/* pos 020e: 258 */ 0xE5 /* 'e' -> */, -/* pos 020f: 259 */ 0xBA /* ':' -> */, -/* pos 0210: 260 */ 0x00, 0x2C /* - terminal marker 44 - */, -/* pos 0212: 261 */ 0xEE /* 'n' -> */, -/* pos 0213: 262 */ 0xED /* 'm' -> */, -/* pos 0214: 263 */ 0xEF /* 'o' -> */, -/* pos 0215: 264 */ 0xE4 /* 'd' -> */, -/* pos 0216: 265 */ 0xE9 /* 'i' -> */, -/* pos 0217: 266 */ 0xE6 /* 'f' -> */, -/* pos 0218: 267 */ 0xE9 /* 'i' -> */, -/* pos 0219: 268 */ 0xE5 /* 'e' -> */, -/* pos 021a: 269 */ 0xE4 /* 'd' -> */, -/* pos 021b: 270 */ 0xAD /* '-' -> */, -/* pos 021c: 271 */ 0xF3 /* 's' -> */, -/* pos 021d: 272 */ 0xE9 /* 'i' -> */, -/* pos 021e: 273 */ 0xEE /* 'n' -> */, -/* pos 021f: 274 */ 0xE3 /* 'c' -> */, -/* pos 0220: 275 */ 0xE5 /* 'e' -> */, -/* pos 0221: 276 */ 0xBA /* ':' -> */, -/* pos 0222: 277 */ 0x00, 0x2D /* - terminal marker 45 - */, -/* pos 0224: 278 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x022E state 279) */, - 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x023C state 292) */, - 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0241 state 296) */, - 0x08, /* fail */ -/* pos 022e: 279 */ 0xF3 /* 's' -> */, -/* pos 022f: 280 */ 0xF4 /* 't' -> */, -/* pos 0230: 281 */ 0xAD /* '-' -> */, -/* pos 0231: 282 */ 0xED /* 'm' -> */, -/* pos 0232: 283 */ 0xEF /* 'o' -> */, -/* pos 0233: 284 */ 0xE4 /* 'd' -> */, -/* pos 0234: 285 */ 0xE9 /* 'i' -> */, -/* pos 0235: 286 */ 0xE6 /* 'f' -> */, -/* pos 0236: 287 */ 0xE9 /* 'i' -> */, -/* pos 0237: 288 */ 0xE5 /* 'e' -> */, -/* pos 0238: 289 */ 0xE4 /* 'd' -> */, -/* pos 0239: 290 */ 0xBA /* ':' -> */, -/* pos 023a: 291 */ 0x00, 0x2E /* - terminal marker 46 - */, -/* pos 023c: 292 */ 0xEE /* 'n' -> */, -/* pos 023d: 293 */ 0xEB /* 'k' -> */, -/* pos 023e: 294 */ 0xBA /* ':' -> */, -/* pos 023f: 295 */ 0x00, 0x2F /* - terminal marker 47 - */, -/* pos 0241: 296 */ 0xE3 /* 'c' -> */, -/* pos 0242: 297 */ 0xE1 /* 'a' -> */, -/* pos 0243: 298 */ 0xF4 /* 't' -> */, -/* pos 0244: 299 */ 0xE9 /* 'i' -> */, -/* pos 0245: 300 */ 0xEF /* 'o' -> */, -/* pos 0246: 301 */ 0xEE /* 'n' -> */, -/* pos 0247: 302 */ 0xBA /* ':' -> */, -/* pos 0248: 303 */ 0x00, 0x30 /* - terminal marker 48 - */, -/* pos 024a: 304 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x0254 state 305) */, - 0x74 /* 't' */, 0x14, 0x00 /* (to 0x0261 state 311) */, - 0x70 /* 'p' */, 0x6F, 0x01 /* (to 0x03BF state 578) */, - 0x08, /* fail */ -/* pos 0254: 305 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x025B state 306) */, - 0x65 /* 'e' */, 0xB1, 0x00 /* (to 0x0308 state 430) */, - 0x08, /* fail */ -/* pos 025b: 306 */ 0xE5 /* 'e' -> */, -/* pos 025c: 307 */ 0xF3 /* 's' -> */, -/* pos 025d: 308 */ 0xE8 /* 'h' -> */, -/* pos 025e: 309 */ 0xBA /* ':' -> */, -/* pos 025f: 310 */ 0x00, 0x31 /* - terminal marker 49 - */, -/* pos 0261: 311 */ 0xF2 /* 'r' -> */, -/* pos 0262: 312 */ 0xF9 /* 'y' -> */, -/* pos 0263: 313 */ 0xAD /* '-' -> */, -/* pos 0264: 314 */ 0xE1 /* 'a' -> */, -/* pos 0265: 315 */ 0xE6 /* 'f' -> */, -/* pos 0266: 316 */ 0xF4 /* 't' -> */, -/* pos 0267: 317 */ 0xE5 /* 'e' -> */, -/* pos 0268: 318 */ 0xF2 /* 'r' -> */, -/* pos 0269: 319 */ 0xBA /* ':' -> */, -/* pos 026a: 320 */ 0x00, 0x32 /* - terminal marker 50 - */, -/* pos 026c: 321 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0273 state 322) */, - 0x74 /* 't' */, 0xED, 0x00 /* (to 0x035C state 496) */, - 0x08, /* fail */ -/* pos 0273: 322 */ 0x72 /* 'r' */, 0x0A, 0x00 /* (to 0x027D state 323) */, - 0x74 /* 't' */, 0x0D, 0x00 /* (to 0x0283 state 328) */, - 0x63 /* 'c' */, 0x52, 0x01 /* (to 0x03CB state 589) */, - 0x08, /* fail */ -/* pos 027d: 323 */ 0xF6 /* 'v' -> */, -/* pos 027e: 324 */ 0xE5 /* 'e' -> */, -/* pos 027f: 325 */ 0xF2 /* 'r' -> */, -/* pos 0280: 326 */ 0xBA /* ':' -> */, -/* pos 0281: 327 */ 0x00, 0x33 /* - terminal marker 51 - */, -/* pos 0283: 328 */ 0xAD /* '-' -> */, -/* pos 0284: 329 */ 0xE3 /* 'c' -> */, -/* pos 0285: 330 */ 0xEF /* 'o' -> */, -/* pos 0286: 331 */ 0xEF /* 'o' -> */, -/* pos 0287: 332 */ 0xEB /* 'k' -> */, -/* pos 0288: 333 */ 0xE9 /* 'i' -> */, -/* pos 0289: 334 */ 0xE5 /* 'e' -> */, -/* pos 028a: 335 */ 0xBA /* ':' -> */, -/* pos 028b: 336 */ 0x00, 0x34 /* - terminal marker 52 - */, -/* pos 028d: 337 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0294 state 338) */, - 0x65 /* 'e' */, 0x2C, 0x01 /* (to 0x03BC state 576) */, - 0x08, /* fail */ -/* pos 0294: 338 */ 0xE1 /* 'a' -> */, -/* pos 0295: 339 */ 0xEE /* 'n' -> */, -/* pos 0296: 340 */ 0xF3 /* 's' -> */, -/* pos 0297: 341 */ 0xE6 /* 'f' -> */, -/* pos 0298: 342 */ 0xE5 /* 'e' -> */, -/* pos 0299: 343 */ 0xF2 /* 'r' -> */, -/* pos 029a: 344 */ 0xAD /* '-' -> */, -/* pos 029b: 345 */ 0xE5 /* 'e' -> */, -/* pos 029c: 346 */ 0xEE /* 'n' -> */, -/* pos 029d: 347 */ 0xE3 /* 'c' -> */, -/* pos 029e: 348 */ 0xEF /* 'o' -> */, -/* pos 029f: 349 */ 0xE4 /* 'd' -> */, -/* pos 02a0: 350 */ 0xE9 /* 'i' -> */, -/* pos 02a1: 351 */ 0xEE /* 'n' -> */, -/* pos 02a2: 352 */ 0xE7 /* 'g' -> */, -/* pos 02a3: 353 */ 0xBA /* ':' -> */, -/* pos 02a4: 354 */ 0x00, 0x35 /* - terminal marker 53 - */, -/* pos 02a6: 355 */ 0xE9 /* 'i' -> */, -/* pos 02a7: 356 */ 0xAD /* '-' -> */, -/* pos 02a8: 357 */ 0xE1 /* 'a' -> */, -/* pos 02a9: 358 */ 0xF2 /* 'r' -> */, -/* pos 02aa: 359 */ 0xE7 /* 'g' -> */, -/* pos 02ab: 360 */ 0xF3 /* 's' -> */, -/* pos 02ac: 361 */ 0x00, 0x36 /* - terminal marker 54 - */, -/* pos 02ae: 362 */ 0xA0 /* ' ' -> */, -/* pos 02af: 363 */ 0x00, 0x37 /* - terminal marker 55 - */, -/* pos 02b1: 364 */ 0xAD /* '-' -> */, -/* pos 02b2: 365 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x02BC state 366) */, - 0x61 /* 'a' */, 0x1D, 0x00 /* (to 0x02D2 state 385) */, - 0x72 /* 'r' */, 0xFB, 0x00 /* (to 0x03B3 state 568) */, - 0x08, /* fail */ -/* pos 02bc: 366 */ 0xEF /* 'o' -> */, -/* pos 02bd: 367 */ 0xF2 /* 'r' -> */, -/* pos 02be: 368 */ 0xF7 /* 'w' -> */, -/* pos 02bf: 369 */ 0xE1 /* 'a' -> */, -/* pos 02c0: 370 */ 0xF2 /* 'r' -> */, -/* pos 02c1: 371 */ 0xE4 /* 'd' -> */, -/* pos 02c2: 372 */ 0xE5 /* 'e' -> */, -/* pos 02c3: 373 */ 0xE4 /* 'd' -> */, -/* pos 02c4: 374 */ 0xAD /* '-' -> */, -/* pos 02c5: 375 */ 0xE6 /* 'f' -> */, -/* pos 02c6: 376 */ 0xEF /* 'o' -> */, -/* pos 02c7: 377 */ 0xF2 /* 'r' -> */, -/* pos 02c8: 378 */ 0xBA /* ':' -> */, -/* pos 02c9: 379 */ 0x00, 0x38 /* - terminal marker 56 - */, -/* pos 02cb: 380 */ 0x00, 0x39 /* - terminal marker 57 - */, -/* pos 02cd: 381 */ 0xE1 /* 'a' -> */, -/* pos 02ce: 382 */ 0xE4 /* 'd' -> */, -/* pos 02cf: 383 */ 0xA0 /* ' ' -> */, -/* pos 02d0: 384 */ 0x00, 0x3A /* - terminal marker 58 - */, -/* pos 02d2: 385 */ 0xF5 /* 'u' -> */, -/* pos 02d3: 386 */ 0xF4 /* 't' -> */, -/* pos 02d4: 387 */ 0xE8 /* 'h' -> */, -/* pos 02d5: 388 */ 0xAD /* '-' -> */, -/* pos 02d6: 389 */ 0xF4 /* 't' -> */, -/* pos 02d7: 390 */ 0xEF /* 'o' -> */, -/* pos 02d8: 391 */ 0xEB /* 'k' -> */, -/* pos 02d9: 392 */ 0xE5 /* 'e' -> */, -/* pos 02da: 393 */ 0xEE /* 'n' -> */, -/* pos 02db: 394 */ 0xBA /* ':' -> */, -/* pos 02dc: 395 */ 0x00, 0x3B /* - terminal marker 59 - */, -/* pos 02de: 396 */ 0xF4 /* 't' -> */, -/* pos 02df: 397 */ 0xE9 /* 'i' -> */, -/* pos 02e0: 398 */ 0xEF /* 'o' -> */, -/* pos 02e1: 399 */ 0xEE /* 'n' -> */, -/* pos 02e2: 400 */ 0xF3 /* 's' -> */, -/* pos 02e3: 401 */ 0xA0 /* ' ' -> */, -/* pos 02e4: 402 */ 0x00, 0x02 /* - terminal marker 2 - */, -/* pos 02e6: 403 */ 0xF3 /* 's' -> */, -/* pos 02e7: 404 */ 0xAD /* '-' -> */, -/* pos 02e8: 405 */ 0xE3 /* 'c' -> */, -/* pos 02e9: 406 */ 0xEF /* 'o' -> */, -/* pos 02ea: 407 */ 0xEE /* 'n' -> */, -/* pos 02eb: 408 */ 0xF4 /* 't' -> */, -/* pos 02ec: 409 */ 0xF2 /* 'r' -> */, -/* pos 02ed: 410 */ 0xEF /* 'o' -> */, -/* pos 02ee: 411 */ 0xEC /* 'l' -> */, -/* pos 02ef: 412 */ 0xAD /* '-' -> */, -/* pos 02f0: 413 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x02F7 state 414) */, - 0x61 /* 'a' */, 0x24, 0x00 /* (to 0x0317 state 443) */, - 0x08, /* fail */ -/* pos 02f7: 414 */ 0xE5 /* 'e' -> */, -/* pos 02f8: 415 */ 0xF1 /* 'q' -> */, -/* pos 02f9: 416 */ 0xF5 /* 'u' -> */, -/* pos 02fa: 417 */ 0xE5 /* 'e' -> */, -/* pos 02fb: 418 */ 0xF3 /* 's' -> */, -/* pos 02fc: 419 */ 0xF4 /* 't' -> */, -/* pos 02fd: 420 */ 0xAD /* '-' -> */, -/* pos 02fe: 421 */ 0xE8 /* 'h' -> */, -/* pos 02ff: 422 */ 0xE5 /* 'e' -> */, -/* pos 0300: 423 */ 0xE1 /* 'a' -> */, -/* pos 0301: 424 */ 0xE4 /* 'd' -> */, -/* pos 0302: 425 */ 0xE5 /* 'e' -> */, -/* pos 0303: 426 */ 0xF2 /* 'r' -> */, -/* pos 0304: 427 */ 0xF3 /* 's' -> */, -/* pos 0305: 428 */ 0xBA /* ':' -> */, -/* pos 0306: 429 */ 0x00, 0x0A /* - terminal marker 10 - */, -/* pos 0308: 430 */ 0xF2 /* 'r' -> */, -/* pos 0309: 431 */ 0xE5 /* 'e' -> */, -/* pos 030a: 432 */ 0xF2 /* 'r' -> */, -/* pos 030b: 433 */ 0xBA /* ':' -> */, -/* pos 030c: 434 */ 0x00, 0x17 /* - terminal marker 23 - */, -/* pos 030e: 435 */ 0xE8 /* 'h' -> */, -/* pos 030f: 436 */ 0xE1 /* 'a' -> */, -/* pos 0310: 437 */ 0xF2 /* 'r' -> */, -/* pos 0311: 438 */ 0xF3 /* 's' -> */, -/* pos 0312: 439 */ 0xE5 /* 'e' -> */, -/* pos 0313: 440 */ 0xF4 /* 't' -> */, -/* pos 0314: 441 */ 0xBA /* ':' -> */, -/* pos 0315: 442 */ 0x00, 0x18 /* - terminal marker 24 - */, -/* pos 0317: 443 */ 0xEC /* 'l' -> */, -/* pos 0318: 444 */ 0xEC /* 'l' -> */, -/* pos 0319: 445 */ 0xEF /* 'o' -> */, -/* pos 031a: 446 */ 0xF7 /* 'w' -> */, -/* pos 031b: 447 */ 0xAD /* '-' -> */, -/* pos 031c: 448 */ 0xEF /* 'o' -> */, -/* pos 031d: 449 */ 0xF2 /* 'r' -> */, -/* pos 031e: 450 */ 0xE9 /* 'i' -> */, -/* pos 031f: 451 */ 0xE7 /* 'g' -> */, -/* pos 0320: 452 */ 0xE9 /* 'i' -> */, -/* pos 0321: 453 */ 0xEE /* 'n' -> */, -/* pos 0322: 454 */ 0xBA /* ':' -> */, -/* pos 0323: 455 */ 0x00, 0x1A /* - terminal marker 26 - */, -/* pos 0325: 456 */ 0xE1 /* 'a' -> */, -/* pos 0326: 457 */ 0xF8 /* 'x' -> */, -/* pos 0327: 458 */ 0xAD /* '-' -> */, -/* pos 0328: 459 */ 0xE6 /* 'f' -> */, -/* pos 0329: 460 */ 0xEF /* 'o' -> */, -/* pos 032a: 461 */ 0xF2 /* 'r' -> */, -/* pos 032b: 462 */ 0xF7 /* 'w' -> */, -/* pos 032c: 463 */ 0xE1 /* 'a' -> */, -/* pos 032d: 464 */ 0xF2 /* 'r' -> */, -/* pos 032e: 465 */ 0xE4 /* 'd' -> */, -/* pos 032f: 466 */ 0xF3 /* 's' -> */, -/* pos 0330: 467 */ 0xBA /* ':' -> */, -/* pos 0331: 468 */ 0x00, 0x2C /* - terminal marker 44 - */, -/* pos 0333: 469 */ 0xF8 /* 'x' -> */, -/* pos 0334: 470 */ 0xF9 /* 'y' -> */, -/* pos 0335: 471 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x033C state 472) */, - 0x20 /* ' ' */, 0x79, 0x00 /* (to 0x03B1 state 567) */, - 0x08, /* fail */ -/* pos 033c: 472 */ 0xE1 /* 'a' -> */, -/* pos 033d: 473 */ 0xF5 /* 'u' -> */, -/* pos 033e: 474 */ 0xF4 /* 't' -> */, -/* pos 033f: 475 */ 0xE8 /* 'h' -> */, -/* pos 0340: 476 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0347 state 477) */, - 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x0351 state 486) */, - 0x08, /* fail */ -/* pos 0347: 477 */ 0xEE /* 'n' -> */, -/* pos 0348: 478 */ 0xF4 /* 't' -> */, -/* pos 0349: 479 */ 0xE9 /* 'i' -> */, -/* pos 034a: 480 */ 0xE3 /* 'c' -> */, -/* pos 034b: 481 */ 0xE1 /* 'a' -> */, -/* pos 034c: 482 */ 0xF4 /* 't' -> */, -/* pos 034d: 483 */ 0xE5 /* 'e' -> */, -/* pos 034e: 484 */ 0xBA /* ':' -> */, -/* pos 034f: 485 */ 0x00, 0x2D /* - terminal marker 45 - */, -/* pos 0351: 486 */ 0xF2 /* 'r' -> */, -/* pos 0352: 487 */ 0xE9 /* 'i' -> */, -/* pos 0353: 488 */ 0xFA /* 'z' -> */, -/* pos 0354: 489 */ 0xE1 /* 'a' -> */, -/* pos 0355: 490 */ 0xF4 /* 't' -> */, -/* pos 0356: 491 */ 0xE9 /* 'i' -> */, -/* pos 0357: 492 */ 0xEF /* 'o' -> */, -/* pos 0358: 493 */ 0xEE /* 'n' -> */, -/* pos 0359: 494 */ 0xBA /* ':' -> */, -/* pos 035a: 495 */ 0x00, 0x2E /* - terminal marker 46 - */, -/* pos 035c: 496 */ 0xF2 /* 'r' -> */, -/* pos 035d: 497 */ 0xE9 /* 'i' -> */, -/* pos 035e: 498 */ 0xE3 /* 'c' -> */, -/* pos 035f: 499 */ 0xF4 /* 't' -> */, -/* pos 0360: 500 */ 0xAD /* '-' -> */, -/* pos 0361: 501 */ 0xF4 /* 't' -> */, -/* pos 0362: 502 */ 0xF2 /* 'r' -> */, -/* pos 0363: 503 */ 0xE1 /* 'a' -> */, -/* pos 0364: 504 */ 0xEE /* 'n' -> */, -/* pos 0365: 505 */ 0xF3 /* 's' -> */, -/* pos 0366: 506 */ 0xF0 /* 'p' -> */, -/* pos 0367: 507 */ 0xEF /* 'o' -> */, -/* pos 0368: 508 */ 0xF2 /* 'r' -> */, -/* pos 0369: 509 */ 0xF4 /* 't' -> */, -/* pos 036a: 510 */ 0xAD /* '-' -> */, -/* pos 036b: 511 */ 0xF3 /* 's' -> */, -/* pos 036c: 512 */ 0xE5 /* 'e' -> */, -/* pos 036d: 513 */ 0xE3 /* 'c' -> */, -/* pos 036e: 514 */ 0xF5 /* 'u' -> */, -/* pos 036f: 515 */ 0xF2 /* 'r' -> */, -/* pos 0370: 516 */ 0xE9 /* 'i' -> */, -/* pos 0371: 517 */ 0xF4 /* 't' -> */, -/* pos 0372: 518 */ 0xF9 /* 'y' -> */, -/* pos 0373: 519 */ 0xBA /* ':' -> */, -/* pos 0374: 520 */ 0x00, 0x33 /* - terminal marker 51 - */, -/* pos 0376: 521 */ 0xE5 /* 'e' -> */, -/* pos 0377: 522 */ 0xF2 /* 'r' -> */, -/* pos 0378: 523 */ 0xAD /* '-' -> */, -/* pos 0379: 524 */ 0xE1 /* 'a' -> */, -/* pos 037a: 525 */ 0xE7 /* 'g' -> */, -/* pos 037b: 526 */ 0xE5 /* 'e' -> */, -/* pos 037c: 527 */ 0xEE /* 'n' -> */, -/* pos 037d: 528 */ 0xF4 /* 't' -> */, -/* pos 037e: 529 */ 0xBA /* ':' -> */, -/* pos 037f: 530 */ 0x00, 0x35 /* - terminal marker 53 - */, -/* pos 0381: 531 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0388 state 532) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x038D state 536) */, - 0x08, /* fail */ -/* pos 0388: 532 */ 0xF2 /* 'r' -> */, -/* pos 0389: 533 */ 0xF9 /* 'y' -> */, -/* pos 038a: 534 */ 0xBA /* ':' -> */, -/* pos 038b: 535 */ 0x00, 0x36 /* - terminal marker 54 - */, -/* pos 038d: 536 */ 0xE1 /* 'a' -> */, -/* pos 038e: 537 */ 0xBA /* ':' -> */, -/* pos 038f: 538 */ 0x00, 0x37 /* - terminal marker 55 - */, -/* pos 0391: 539 */ 0xF7 /* 'w' -> */, -/* pos 0392: 540 */ 0xF7 /* 'w' -> */, -/* pos 0393: 541 */ 0xAD /* '-' -> */, -/* pos 0394: 542 */ 0xE1 /* 'a' -> */, -/* pos 0395: 543 */ 0xF5 /* 'u' -> */, -/* pos 0396: 544 */ 0xF4 /* 't' -> */, -/* pos 0397: 545 */ 0xE8 /* 'h' -> */, -/* pos 0398: 546 */ 0xE5 /* 'e' -> */, -/* pos 0399: 547 */ 0xEE /* 'n' -> */, -/* pos 039a: 548 */ 0xF4 /* 't' -> */, -/* pos 039b: 549 */ 0xE9 /* 'i' -> */, -/* pos 039c: 550 */ 0xE3 /* 'c' -> */, -/* pos 039d: 551 */ 0xE1 /* 'a' -> */, -/* pos 039e: 552 */ 0xF4 /* 't' -> */, -/* pos 039f: 553 */ 0xE5 /* 'e' -> */, -/* pos 03a0: 554 */ 0xBA /* ':' -> */, -/* pos 03a1: 555 */ 0x00, 0x38 /* - terminal marker 56 - */, -/* pos 03a3: 556 */ 0xF4 /* 't' -> */, -/* pos 03a4: 557 */ 0xE3 /* 'c' -> */, -/* pos 03a5: 558 */ 0xE8 /* 'h' -> */, -/* pos 03a6: 559 */ 0x00, 0x39 /* - terminal marker 57 - */, -/* pos 03a8: 560 */ 0xF4 /* 't' -> */, -/* pos 03a9: 561 */ 0x00, 0x3A /* - terminal marker 58 - */, -/* pos 03ab: 562 */ 0xEC /* 'l' -> */, -/* pos 03ac: 563 */ 0xE5 /* 'e' -> */, -/* pos 03ad: 564 */ 0xF4 /* 't' -> */, -/* pos 03ae: 565 */ 0xE5 /* 'e' -> */, -/* pos 03af: 566 */ 0x00, 0x3B /* - terminal marker 59 - */, -/* pos 03b1: 567 */ 0x00, 0x3D /* - terminal marker 61 - */, -/* pos 03b3: 568 */ 0xE5 /* 'e' -> */, -/* pos 03b4: 569 */ 0xE1 /* 'a' -> */, -/* pos 03b5: 570 */ 0xEC /* 'l' -> */, -/* pos 03b6: 571 */ 0xAD /* '-' -> */, -/* pos 03b7: 572 */ 0xE9 /* 'i' -> */, -/* pos 03b8: 573 */ 0xF0 /* 'p' -> */, -/* pos 03b9: 574 */ 0xBA /* ':' -> */, -/* pos 03ba: 575 */ 0x00, 0x3E /* - terminal marker 62 - */, -/* pos 03bc: 576 */ 0xBA /* ':' -> */, -/* pos 03bd: 577 */ 0x00, 0x43 /* - terminal marker 67 - */, -/* pos 03bf: 578 */ 0xEC /* 'l' -> */, -/* pos 03c0: 579 */ 0xE1 /* 'a' -> */, -/* pos 03c1: 580 */ 0xF9 /* 'y' -> */, -/* pos 03c2: 581 */ 0xAD /* '-' -> */, -/* pos 03c3: 582 */ 0xEE /* 'n' -> */, -/* pos 03c4: 583 */ 0xEF /* 'o' -> */, -/* pos 03c5: 584 */ 0xEE /* 'n' -> */, -/* pos 03c6: 585 */ 0xE3 /* 'c' -> */, -/* pos 03c7: 586 */ 0xE5 /* 'e' -> */, -/* pos 03c8: 587 */ 0xBA /* ':' -> */, -/* pos 03c9: 588 */ 0x00, 0x44 /* - terminal marker 68 - */, -/* pos 03cb: 589 */ 0xAD /* '-' -> */, -/* pos 03cc: 590 */ 0xF7 /* 'w' -> */, -/* pos 03cd: 591 */ 0xE5 /* 'e' -> */, -/* pos 03ce: 592 */ 0xE2 /* 'b' -> */, -/* pos 03cf: 593 */ 0xF3 /* 's' -> */, -/* pos 03d0: 594 */ 0xEF /* 'o' -> */, -/* pos 03d1: 595 */ 0xE3 /* 'c' -> */, -/* pos 03d2: 596 */ 0xEB /* 'k' -> */, -/* pos 03d3: 597 */ 0xE5 /* 'e' -> */, -/* pos 03d4: 598 */ 0xF4 /* 't' -> */, -/* pos 03d5: 599 */ 0xAD /* '-' -> */, -/* pos 03d6: 600 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x03EF state 601) */, - 0x65 /* 'e' */, 0x1D, 0x00 /* (to 0x03F6 state 607) */, - 0x6B /* 'k' */, 0x26, 0x00 /* (to 0x0402 state 618) */, - 0x70 /* 'p' */, 0x35, 0x00 /* (to 0x0414 state 625) */, - 0x61 /* 'a' */, 0x3C, 0x00 /* (to 0x041E state 634) */, - 0x6E /* 'n' */, 0x41, 0x00 /* (to 0x0426 state 641) */, - 0x76 /* 'v' */, 0x47, 0x00 /* (to 0x042F state 648) */, - 0x6F /* 'o' */, 0x4D, 0x00 /* (to 0x0438 state 656) */, - 0x08, /* fail */ -/* pos 03ef: 601 */ 0xF2 /* 'r' -> */, -/* pos 03f0: 602 */ 0xE1 /* 'a' -> */, -/* pos 03f1: 603 */ 0xE6 /* 'f' -> */, -/* pos 03f2: 604 */ 0xF4 /* 't' -> */, -/* pos 03f3: 605 */ 0xBA /* ':' -> */, -/* pos 03f4: 606 */ 0x00, 0x06 /* - terminal marker 6 - */, -/* pos 03f6: 607 */ 0xF8 /* 'x' -> */, -/* pos 03f7: 608 */ 0xF4 /* 't' -> */, -/* pos 03f8: 609 */ 0xE5 /* 'e' -> */, -/* pos 03f9: 610 */ 0xEE /* 'n' -> */, -/* pos 03fa: 611 */ 0xF3 /* 's' -> */, -/* pos 03fb: 612 */ 0xE9 /* 'i' -> */, -/* pos 03fc: 613 */ 0xEF /* 'o' -> */, -/* pos 03fd: 614 */ 0xEE /* 'n' -> */, -/* pos 03fe: 615 */ 0xF3 /* 's' -> */, -/* pos 03ff: 616 */ 0xBA /* ':' -> */, -/* pos 0400: 617 */ 0x00, 0x08 /* - terminal marker 8 - */, -/* pos 0402: 618 */ 0xE5 /* 'e' -> */, -/* pos 0403: 619 */ 0xF9 /* 'y' -> */, -/* pos 0404: 620 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x040E state 621) */, - 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x0411 state 623) */, - 0x3A /* ':' */, 0x23, 0x00 /* (to 0x042D state 647) */, - 0x08, /* fail */ -/* pos 040e: 621 */ 0xBA /* ':' -> */, -/* pos 040f: 622 */ 0x00, 0x09 /* - terminal marker 9 - */, -/* pos 0411: 623 */ 0xBA /* ':' -> */, -/* pos 0412: 624 */ 0x00, 0x0A /* - terminal marker 10 - */, -/* pos 0414: 625 */ 0xF2 /* 'r' -> */, -/* pos 0415: 626 */ 0xEF /* 'o' -> */, -/* pos 0416: 627 */ 0xF4 /* 't' -> */, -/* pos 0417: 628 */ 0xEF /* 'o' -> */, -/* pos 0418: 629 */ 0xE3 /* 'c' -> */, -/* pos 0419: 630 */ 0xEF /* 'o' -> */, -/* pos 041a: 631 */ 0xEC /* 'l' -> */, -/* pos 041b: 632 */ 0xBA /* ':' -> */, -/* pos 041c: 633 */ 0x00, 0x0B /* - terminal marker 11 - */, -/* pos 041e: 634 */ 0xE3 /* 'c' -> */, -/* pos 041f: 635 */ 0xE3 /* 'c' -> */, -/* pos 0420: 636 */ 0xE5 /* 'e' -> */, -/* pos 0421: 637 */ 0xF0 /* 'p' -> */, -/* pos 0422: 638 */ 0xF4 /* 't' -> */, -/* pos 0423: 639 */ 0xBA /* ':' -> */, -/* pos 0424: 640 */ 0x00, 0x0C /* - terminal marker 12 - */, -/* pos 0426: 641 */ 0xEF /* 'o' -> */, -/* pos 0427: 642 */ 0xEE /* 'n' -> */, -/* pos 0428: 643 */ 0xE3 /* 'c' -> */, -/* pos 0429: 644 */ 0xE5 /* 'e' -> */, -/* pos 042a: 645 */ 0xBA /* ':' -> */, -/* pos 042b: 646 */ 0x00, 0x0D /* - terminal marker 13 - */, -/* pos 042d: 647 */ 0x00, 0x1C /* - terminal marker 28 - */, -/* pos 042f: 648 */ 0xE5 /* 'e' -> */, -/* pos 0430: 649 */ 0xF2 /* 'r' -> */, -/* pos 0431: 650 */ 0xF3 /* 's' -> */, -/* pos 0432: 651 */ 0xE9 /* 'i' -> */, -/* pos 0433: 652 */ 0xEF /* 'o' -> */, -/* pos 0434: 653 */ 0xEE /* 'n' -> */, -/* pos 0435: 654 */ 0xBA /* ':' -> */, -/* pos 0436: 655 */ 0x00, 0x1D /* - terminal marker 29 - */, -/* pos 0438: 656 */ 0xF2 /* 'r' -> */, -/* pos 0439: 657 */ 0xE9 /* 'i' -> */, -/* pos 043a: 658 */ 0xE7 /* 'g' -> */, -/* pos 043b: 659 */ 0xE9 /* 'i' -> */, -/* pos 043c: 660 */ 0xEE /* 'n' -> */, -/* pos 043d: 661 */ 0xBA /* ':' -> */, -/* pos 043e: 662 */ 0x00, 0x1E /* - terminal marker 30 - */, -/* total size 1088 bytes */ -#endif - -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) - /* 0: 0: get */ - /* 1: 1: post */ - /* 2: 2: options */ - /* 3: 3: host: */ - /* 4: 4: connection: */ - /* 5: 5: upgrade: */ - /* 6: 6: origin: */ - /* 7: 7: sec-websocket-draft: */ - /* 8: 8: - */ - /* 9: 9: sec-websocket-extensions: */ - /* 10: 10: sec-websocket-key1: */ - /* 11: 11: sec-websocket-key2: */ - /* 12: 12: sec-websocket-protocol: */ - /* 13: 13: sec-websocket-accept: */ - /* 14: 14: sec-websocket-nonce: */ - /* 15: 15: http/1.1 */ - /* 16: 17: accept: */ - /* 17: 18: access-control-request-headers: */ - /* 18: 19: if-modified-since: */ - /* 19: 20: if-none-match: */ - /* 20: 21: accept-encoding: */ - /* 21: 22: accept-language: */ - /* 22: 23: pragma: */ - /* 23: 24: cache-control: */ - /* 24: 25: authorization: */ - /* 25: 26: cookie: */ - /* 26: 27: content-length: */ - /* 27: 28: content-type: */ - /* 28: 29: date: */ - /* 29: 30: range: */ - /* 30: 31: referer: */ - /* 31: 32: sec-websocket-key: */ - /* 32: 33: sec-websocket-version: */ - /* 33: 34: sec-websocket-origin: */ - /* 34: 40: accept-charset: */ - /* 35: 41: accept-ranges: */ - /* 36: 42: access-control-allow-origin: */ - /* 37: 43: age: */ - /* 38: 44: allow: */ - /* 39: 45: content-disposition: */ - /* 40: 46: content-encoding: */ - /* 41: 47: content-language: */ - /* 42: 48: content-location: */ - /* 43: 49: content-range: */ - /* 44: 50: etag: */ - /* 45: 51: expect: */ - /* 46: 52: expires: */ - /* 47: 53: from: */ - /* 48: 54: if-match: */ - /* 49: 55: if-range: */ - /* 50: 56: if-unmodified-since: */ - /* 51: 57: last-modified: */ - /* 52: 58: link: */ - /* 53: 59: location: */ - /* 54: 60: max-forwards: */ - /* 55: 61: proxy-authenticate: */ - /* 56: 62: proxy-authorization: */ - /* 57: 63: refresh: */ - /* 58: 64: retry-after: */ - /* 59: 65: server: */ - /* 60: 66: set-cookie: */ - /* 61: 67: strict-transport-security: */ - /* 62: 68: transfer-encoding: */ - /* 63: 69: user-agent: */ - /* 64: 70: vary: */ - /* 65: 71: via: */ - /* 66: 72: www-authenticate: */ - /* 67: 73: patch */ - /* 68: 74: put */ - /* 69: 75: delete */ - /* 70: 76: uri-args */ - /* 71: 77: proxy */ - /* 72: 78: x-real-ip: */ - /* 73: 79: http/1.0 */ - /* 74: 80: x-forwarded-for: */ - /* 75: 81: connect */ - /* 76: 82: head */ - /* 77: 83: te: */ - /* 78: 84: replay-nonce: */ - /* 79: 86: x-auth-token: */ - /* 80: 87: */ -/* pos 0000: 0 */ 0x67 /* 'g' */, 0x3D, 0x00 /* (to 0x003D state 1) */, - 0x70 /* 'p' */, 0x3F, 0x00 /* (to 0x0042 state 5) */, - 0x68 /* 'h' */, 0x4E, 0x00 /* (to 0x0054 state 10) */, - 0x63 /* 'c' */, 0x5A, 0x00 /* (to 0x0063 state 15) */, - 0x75 /* 'u' */, 0x7B, 0x00 /* (to 0x0087 state 26) */, - 0x6F /* 'o' */, 0x8A, 0x00 /* (to 0x0099 state 34) */, - 0x0D /* '.' */, 0x95, 0x00 /* (to 0x00A7 state 41) */, - 0x61 /* 'a' */, 0xA4, 0x00 /* (to 0x00B9 state 51) */, - 0x69 /* 'i' */, 0xC1, 0x00 /* (to 0x00D9 state 58) */, - 0x64 /* 'd' */, 0x6A, 0x01 /* (to 0x0185 state 160) */, - 0x72 /* 'r' */, 0x73, 0x01 /* (to 0x0191 state 165) */, - 0x65 /* 'e' */, 0xBF, 0x01 /* (to 0x01E0 state 229) */, - 0x66 /* 'f' */, 0xDB, 0x01 /* (to 0x01FF state 245) */, - 0x6C /* 'l' */, 0xFD, 0x01 /* (to 0x0224 state 278) */, - 0x73 /* 's' */, 0x42, 0x02 /* (to 0x026C state 321) */, - 0x74 /* 't' */, 0x60, 0x02 /* (to 0x028D state 337) */, - 0x78 /* 'x' */, 0x81, 0x02 /* (to 0x02B1 state 364) */, - 0x6D /* 'm' */, 0xF2, 0x02 /* (to 0x0325 state 456) */, - 0x76 /* 'v' */, 0x4B, 0x03 /* (to 0x0381 state 531) */, - 0x77 /* 'w' */, 0x58, 0x03 /* (to 0x0391 state 539) */, - 0x08, /* fail */ -/* pos 003d: 1 */ 0xE5 /* 'e' -> */, -/* pos 003e: 2 */ 0xF4 /* 't' -> */, -/* pos 003f: 3 */ 0xA0 /* ' ' -> */, -/* pos 0040: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, -/* pos 0042: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x004F state 6) */, - 0x72 /* 'r' */, 0xE6, 0x00 /* (to 0x012B state 106) */, - 0x61 /* 'a' */, 0x5B, 0x03 /* (to 0x03A3 state 556) */, - 0x75 /* 'u' */, 0x5D, 0x03 /* (to 0x03A8 state 560) */, - 0x08, /* fail */ -/* pos 004f: 6 */ 0xF3 /* 's' -> */, -/* pos 0050: 7 */ 0xF4 /* 't' -> */, -/* pos 0051: 8 */ 0xA0 /* ' ' -> */, -/* pos 0052: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, -/* pos 0054: 10 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x005E state 11) */, - 0x74 /* 't' */, 0x53, 0x00 /* (to 0x00AA state 43) */, - 0x65 /* 'e' */, 0x73, 0x02 /* (to 0x02CD state 381) */, - 0x08, /* fail */ -/* pos 005e: 11 */ 0xF3 /* 's' -> */, -/* pos 005f: 12 */ 0xF4 /* 't' -> */, -/* pos 0060: 13 */ 0xBA /* ':' -> */, -/* pos 0061: 14 */ 0x00, 0x03 /* - terminal marker 3 - */, -/* pos 0063: 15 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x006A state 16) */, - 0x61 /* 'a' */, 0xD2, 0x00 /* (to 0x0138 state 112) */, - 0x08, /* fail */ -/* pos 006a: 16 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0071 state 17) */, - 0x6F /* 'o' */, 0xE7, 0x00 /* (to 0x0154 state 138) */, - 0x08, /* fail */ -/* pos 0071: 17 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0078 state 18) */, - 0x74 /* 't' */, 0xE6, 0x00 /* (to 0x015A state 143) */, - 0x08, /* fail */ -/* pos 0078: 18 */ 0xE5 /* 'e' -> */, -/* pos 0079: 19 */ 0xE3 /* 'c' -> */, -/* pos 007a: 20 */ 0xF4 /* 't' -> */, -/* pos 007b: 21 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0082 state 22) */, - 0x20 /* ' ' */, 0x4D, 0x02 /* (to 0x02CB state 380) */, - 0x08, /* fail */ -/* pos 0082: 22 */ 0xEF /* 'o' -> */, -/* pos 0083: 23 */ 0xEE /* 'n' -> */, -/* pos 0084: 24 */ 0xBA /* ':' -> */, -/* pos 0085: 25 */ 0x00, 0x04 /* - terminal marker 4 - */, -/* pos 0087: 26 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x0091 state 27) */, - 0x72 /* 'r' */, 0x1C, 0x02 /* (to 0x02A6 state 355) */, - 0x73 /* 's' */, 0xE9, 0x02 /* (to 0x0376 state 521) */, - 0x08, /* fail */ -/* pos 0091: 27 */ 0xE7 /* 'g' -> */, -/* pos 0092: 28 */ 0xF2 /* 'r' -> */, -/* pos 0093: 29 */ 0xE1 /* 'a' -> */, -/* pos 0094: 30 */ 0xE4 /* 'd' -> */, -/* pos 0095: 31 */ 0xE5 /* 'e' -> */, -/* pos 0096: 32 */ 0xBA /* ':' -> */, -/* pos 0097: 33 */ 0x00, 0x05 /* - terminal marker 5 - */, -/* pos 0099: 34 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x00A0 state 35) */, - 0x70 /* 'p' */, 0x42, 0x02 /* (to 0x02DE state 396) */, - 0x08, /* fail */ -/* pos 00a0: 35 */ 0xE9 /* 'i' -> */, -/* pos 00a1: 36 */ 0xE7 /* 'g' -> */, -/* pos 00a2: 37 */ 0xE9 /* 'i' -> */, -/* pos 00a3: 38 */ 0xEE /* 'n' -> */, -/* pos 00a4: 39 */ 0xBA /* ':' -> */, -/* pos 00a5: 40 */ 0x00, 0x06 /* - terminal marker 6 - */, -/* pos 00a7: 41 */ 0x8A /* '.' -> */, -/* pos 00a8: 42 */ 0x00, 0x08 /* - terminal marker 8 - */, -/* pos 00aa: 43 */ 0xF4 /* 't' -> */, -/* pos 00ab: 44 */ 0xF0 /* 'p' -> */, -/* pos 00ac: 45 */ 0xAF /* '/' -> */, -/* pos 00ad: 46 */ 0xB1 /* '1' -> */, -/* pos 00ae: 47 */ 0xAE /* '.' -> */, -/* pos 00af: 48 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x00B6 state 49) */, - 0x30 /* '0' */, 0xFC, 0x01 /* (to 0x02AE state 362) */, - 0x08, /* fail */ -/* pos 00b6: 49 */ 0xA0 /* ' ' -> */, -/* pos 00b7: 50 */ 0x00, 0x0F /* - terminal marker 15 - */, -/* pos 00b9: 51 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x00C6 state 52) */, - 0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x0146 state 125) */, - 0x67 /* 'g' */, 0xE7, 0x00 /* (to 0x01A6 state 178) */, - 0x6C /* 'l' */, 0xE8, 0x00 /* (to 0x01AA state 181) */, - 0x08, /* fail */ -/* pos 00c6: 52 */ 0xE3 /* 'c' -> */, -/* pos 00c7: 53 */ 0xE5 /* 'e' -> */, -/* pos 00c8: 54 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x00CF state 55) */, - 0x73 /* 's' */, 0x1B, 0x02 /* (to 0x02E6 state 403) */, - 0x08, /* fail */ -/* pos 00cf: 55 */ 0xF4 /* 't' -> */, -/* pos 00d0: 56 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x00D7 state 57) */, - 0x2D /* '-' */, 0x37, 0x00 /* (to 0x010A state 87) */, - 0x08, /* fail */ -/* pos 00d7: 57 */ 0x00, 0x10 /* - terminal marker 16 - */, -/* pos 00d9: 58 */ 0xE6 /* 'f' -> */, -/* pos 00da: 59 */ 0xAD /* '-' -> */, -/* pos 00db: 60 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x00E8 state 61) */, - 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x00FE state 76) */, - 0x72 /* 'r' */, 0x2A, 0x01 /* (to 0x020B state 255) */, - 0x75 /* 'u' */, 0x2E, 0x01 /* (to 0x0212 state 261) */, - 0x08, /* fail */ -/* pos 00e8: 61 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x00EF state 62) */, - 0x61 /* 'a' */, 0x1A, 0x01 /* (to 0x0205 state 250) */, - 0x08, /* fail */ -/* pos 00ef: 62 */ 0xE4 /* 'd' -> */, -/* pos 00f0: 63 */ 0xE9 /* 'i' -> */, -/* pos 00f1: 64 */ 0xE6 /* 'f' -> */, -/* pos 00f2: 65 */ 0xE9 /* 'i' -> */, -/* pos 00f3: 66 */ 0xE5 /* 'e' -> */, -/* pos 00f4: 67 */ 0xE4 /* 'd' -> */, -/* pos 00f5: 68 */ 0xAD /* '-' -> */, -/* pos 00f6: 69 */ 0xF3 /* 's' -> */, -/* pos 00f7: 70 */ 0xE9 /* 'i' -> */, -/* pos 00f8: 71 */ 0xEE /* 'n' -> */, -/* pos 00f9: 72 */ 0xE3 /* 'c' -> */, -/* pos 00fa: 73 */ 0xE5 /* 'e' -> */, -/* pos 00fb: 74 */ 0xBA /* ':' -> */, -/* pos 00fc: 75 */ 0x00, 0x12 /* - terminal marker 18 - */, -/* pos 00fe: 76 */ 0xEF /* 'o' -> */, -/* pos 00ff: 77 */ 0xEE /* 'n' -> */, -/* pos 0100: 78 */ 0xE5 /* 'e' -> */, -/* pos 0101: 79 */ 0xAD /* '-' -> */, -/* pos 0102: 80 */ 0xED /* 'm' -> */, -/* pos 0103: 81 */ 0xE1 /* 'a' -> */, -/* pos 0104: 82 */ 0xF4 /* 't' -> */, -/* pos 0105: 83 */ 0xE3 /* 'c' -> */, -/* pos 0106: 84 */ 0xE8 /* 'h' -> */, -/* pos 0107: 85 */ 0xBA /* ':' -> */, -/* pos 0108: 86 */ 0x00, 0x13 /* - terminal marker 19 - */, -/* pos 010a: 87 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x0117 state 88) */, - 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x0121 state 97) */, - 0x72 /* 'r' */, 0x8E, 0x00 /* (to 0x019E state 171) */, - 0x63 /* 'c' */, 0xFB, 0x01 /* (to 0x030E state 435) */, - 0x08, /* fail */ -/* pos 0117: 88 */ 0xEE /* 'n' -> */, -/* pos 0118: 89 */ 0xE3 /* 'c' -> */, -/* pos 0119: 90 */ 0xEF /* 'o' -> */, -/* pos 011a: 91 */ 0xE4 /* 'd' -> */, -/* pos 011b: 92 */ 0xE9 /* 'i' -> */, -/* pos 011c: 93 */ 0xEE /* 'n' -> */, -/* pos 011d: 94 */ 0xE7 /* 'g' -> */, -/* pos 011e: 95 */ 0xBA /* ':' -> */, -/* pos 011f: 96 */ 0x00, 0x14 /* - terminal marker 20 - */, -/* pos 0121: 97 */ 0xE1 /* 'a' -> */, -/* pos 0122: 98 */ 0xEE /* 'n' -> */, -/* pos 0123: 99 */ 0xE7 /* 'g' -> */, -/* pos 0124: 100 */ 0xF5 /* 'u' -> */, -/* pos 0125: 101 */ 0xE1 /* 'a' -> */, -/* pos 0126: 102 */ 0xE7 /* 'g' -> */, -/* pos 0127: 103 */ 0xE5 /* 'e' -> */, -/* pos 0128: 104 */ 0xBA /* ':' -> */, -/* pos 0129: 105 */ 0x00, 0x15 /* - terminal marker 21 - */, -/* pos 012b: 106 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0132 state 107) */, - 0x6F /* 'o' */, 0x05, 0x02 /* (to 0x0333 state 469) */, - 0x08, /* fail */ -/* pos 0132: 107 */ 0xE7 /* 'g' -> */, -/* pos 0133: 108 */ 0xED /* 'm' -> */, -/* pos 0134: 109 */ 0xE1 /* 'a' -> */, -/* pos 0135: 110 */ 0xBA /* ':' -> */, -/* pos 0136: 111 */ 0x00, 0x16 /* - terminal marker 22 - */, -/* pos 0138: 112 */ 0xE3 /* 'c' -> */, -/* pos 0139: 113 */ 0xE8 /* 'h' -> */, -/* pos 013a: 114 */ 0xE5 /* 'e' -> */, -/* pos 013b: 115 */ 0xAD /* '-' -> */, -/* pos 013c: 116 */ 0xE3 /* 'c' -> */, -/* pos 013d: 117 */ 0xEF /* 'o' -> */, -/* pos 013e: 118 */ 0xEE /* 'n' -> */, -/* pos 013f: 119 */ 0xF4 /* 't' -> */, -/* pos 0140: 120 */ 0xF2 /* 'r' -> */, -/* pos 0141: 121 */ 0xEF /* 'o' -> */, -/* pos 0142: 122 */ 0xEC /* 'l' -> */, -/* pos 0143: 123 */ 0xBA /* ':' -> */, -/* pos 0144: 124 */ 0x00, 0x17 /* - terminal marker 23 - */, -/* pos 0146: 125 */ 0xF4 /* 't' -> */, -/* pos 0147: 126 */ 0xE8 /* 'h' -> */, -/* pos 0148: 127 */ 0xEF /* 'o' -> */, -/* pos 0149: 128 */ 0xF2 /* 'r' -> */, -/* pos 014a: 129 */ 0xE9 /* 'i' -> */, -/* pos 014b: 130 */ 0xFA /* 'z' -> */, -/* pos 014c: 131 */ 0xE1 /* 'a' -> */, -/* pos 014d: 132 */ 0xF4 /* 't' -> */, -/* pos 014e: 133 */ 0xE9 /* 'i' -> */, -/* pos 014f: 134 */ 0xEF /* 'o' -> */, -/* pos 0150: 135 */ 0xEE /* 'n' -> */, -/* pos 0151: 136 */ 0xBA /* ':' -> */, -/* pos 0152: 137 */ 0x00, 0x18 /* - terminal marker 24 - */, -/* pos 0154: 138 */ 0xEB /* 'k' -> */, -/* pos 0155: 139 */ 0xE9 /* 'i' -> */, -/* pos 0156: 140 */ 0xE5 /* 'e' -> */, -/* pos 0157: 141 */ 0xBA /* ':' -> */, -/* pos 0158: 142 */ 0x00, 0x19 /* - terminal marker 25 - */, -/* pos 015a: 143 */ 0xE5 /* 'e' -> */, -/* pos 015b: 144 */ 0xEE /* 'n' -> */, -/* pos 015c: 145 */ 0xF4 /* 't' -> */, -/* pos 015d: 146 */ 0xAD /* '-' -> */, -/* pos 015e: 147 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x016E state 148) */, - 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x017F state 155) */, - 0x64 /* 'd' */, 0x4C, 0x00 /* (to 0x01B0 state 186) */, - 0x65 /* 'e' */, 0x56, 0x00 /* (to 0x01BD state 198) */, - 0x72 /* 'r' */, 0x6F, 0x00 /* (to 0x01D9 state 223) */, - 0x08, /* fail */ -/* pos 016e: 148 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0178 state 149) */, - 0x61 /* 'a' */, 0x56, 0x00 /* (to 0x01C7 state 207) */, - 0x6F /* 'o' */, 0x5C, 0x00 /* (to 0x01D0 state 215) */, - 0x08, /* fail */ -/* pos 0178: 149 */ 0xEE /* 'n' -> */, -/* pos 0179: 150 */ 0xE7 /* 'g' -> */, -/* pos 017a: 151 */ 0xF4 /* 't' -> */, -/* pos 017b: 152 */ 0xE8 /* 'h' -> */, -/* pos 017c: 153 */ 0xBA /* ':' -> */, -/* pos 017d: 154 */ 0x00, 0x1A /* - terminal marker 26 - */, -/* pos 017f: 155 */ 0xF9 /* 'y' -> */, -/* pos 0180: 156 */ 0xF0 /* 'p' -> */, -/* pos 0181: 157 */ 0xE5 /* 'e' -> */, -/* pos 0182: 158 */ 0xBA /* ':' -> */, -/* pos 0183: 159 */ 0x00, 0x1B /* - terminal marker 27 - */, -/* pos 0185: 160 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x018C state 161) */, - 0x65 /* 'e' */, 0x23, 0x02 /* (to 0x03AB state 562) */, - 0x08, /* fail */ -/* pos 018c: 161 */ 0xF4 /* 't' -> */, -/* pos 018d: 162 */ 0xE5 /* 'e' -> */, -/* pos 018e: 163 */ 0xBA /* ':' -> */, -/* pos 018f: 164 */ 0x00, 0x1C /* - terminal marker 28 - */, -/* pos 0191: 165 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0198 state 166) */, - 0x65 /* 'e' */, 0xB6, 0x00 /* (to 0x024A state 304) */, - 0x08, /* fail */ -/* pos 0198: 166 */ 0xEE /* 'n' -> */, -/* pos 0199: 167 */ 0xE7 /* 'g' -> */, -/* pos 019a: 168 */ 0xE5 /* 'e' -> */, -/* pos 019b: 169 */ 0xBA /* ':' -> */, -/* pos 019c: 170 */ 0x00, 0x1D /* - terminal marker 29 - */, -/* pos 019e: 171 */ 0xE1 /* 'a' -> */, -/* pos 019f: 172 */ 0xEE /* 'n' -> */, -/* pos 01a0: 173 */ 0xE7 /* 'g' -> */, -/* pos 01a1: 174 */ 0xE5 /* 'e' -> */, -/* pos 01a2: 175 */ 0xF3 /* 's' -> */, -/* pos 01a3: 176 */ 0xBA /* ':' -> */, -/* pos 01a4: 177 */ 0x00, 0x23 /* - terminal marker 35 - */, -/* pos 01a6: 178 */ 0xE5 /* 'e' -> */, -/* pos 01a7: 179 */ 0xBA /* ':' -> */, -/* pos 01a8: 180 */ 0x00, 0x25 /* - terminal marker 37 - */, -/* pos 01aa: 181 */ 0xEC /* 'l' -> */, -/* pos 01ab: 182 */ 0xEF /* 'o' -> */, -/* pos 01ac: 183 */ 0xF7 /* 'w' -> */, -/* pos 01ad: 184 */ 0xBA /* ':' -> */, -/* pos 01ae: 185 */ 0x00, 0x26 /* - terminal marker 38 - */, -/* pos 01b0: 186 */ 0xE9 /* 'i' -> */, -/* pos 01b1: 187 */ 0xF3 /* 's' -> */, -/* pos 01b2: 188 */ 0xF0 /* 'p' -> */, -/* pos 01b3: 189 */ 0xEF /* 'o' -> */, -/* pos 01b4: 190 */ 0xF3 /* 's' -> */, -/* pos 01b5: 191 */ 0xE9 /* 'i' -> */, -/* pos 01b6: 192 */ 0xF4 /* 't' -> */, -/* pos 01b7: 193 */ 0xE9 /* 'i' -> */, -/* pos 01b8: 194 */ 0xEF /* 'o' -> */, -/* pos 01b9: 195 */ 0xEE /* 'n' -> */, -/* pos 01ba: 196 */ 0xBA /* ':' -> */, -/* pos 01bb: 197 */ 0x00, 0x27 /* - terminal marker 39 - */, -/* pos 01bd: 198 */ 0xEE /* 'n' -> */, -/* pos 01be: 199 */ 0xE3 /* 'c' -> */, -/* pos 01bf: 200 */ 0xEF /* 'o' -> */, -/* pos 01c0: 201 */ 0xE4 /* 'd' -> */, -/* pos 01c1: 202 */ 0xE9 /* 'i' -> */, -/* pos 01c2: 203 */ 0xEE /* 'n' -> */, -/* pos 01c3: 204 */ 0xE7 /* 'g' -> */, -/* pos 01c4: 205 */ 0xBA /* ':' -> */, -/* pos 01c5: 206 */ 0x00, 0x28 /* - terminal marker 40 - */, -/* pos 01c7: 207 */ 0xEE /* 'n' -> */, -/* pos 01c8: 208 */ 0xE7 /* 'g' -> */, -/* pos 01c9: 209 */ 0xF5 /* 'u' -> */, -/* pos 01ca: 210 */ 0xE1 /* 'a' -> */, -/* pos 01cb: 211 */ 0xE7 /* 'g' -> */, -/* pos 01cc: 212 */ 0xE5 /* 'e' -> */, -/* pos 01cd: 213 */ 0xBA /* ':' -> */, -/* pos 01ce: 214 */ 0x00, 0x29 /* - terminal marker 41 - */, -/* pos 01d0: 215 */ 0xE3 /* 'c' -> */, -/* pos 01d1: 216 */ 0xE1 /* 'a' -> */, -/* pos 01d2: 217 */ 0xF4 /* 't' -> */, -/* pos 01d3: 218 */ 0xE9 /* 'i' -> */, -/* pos 01d4: 219 */ 0xEF /* 'o' -> */, -/* pos 01d5: 220 */ 0xEE /* 'n' -> */, -/* pos 01d6: 221 */ 0xBA /* ':' -> */, -/* pos 01d7: 222 */ 0x00, 0x2A /* - terminal marker 42 - */, -/* pos 01d9: 223 */ 0xE1 /* 'a' -> */, -/* pos 01da: 224 */ 0xEE /* 'n' -> */, -/* pos 01db: 225 */ 0xE7 /* 'g' -> */, -/* pos 01dc: 226 */ 0xE5 /* 'e' -> */, -/* pos 01dd: 227 */ 0xBA /* ':' -> */, -/* pos 01de: 228 */ 0x00, 0x2B /* - terminal marker 43 - */, -/* pos 01e0: 229 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x01E7 state 230) */, - 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x01EC state 234) */, - 0x08, /* fail */ -/* pos 01e7: 230 */ 0xE1 /* 'a' -> */, -/* pos 01e8: 231 */ 0xE7 /* 'g' -> */, -/* pos 01e9: 232 */ 0xBA /* ':' -> */, -/* pos 01ea: 233 */ 0x00, 0x2C /* - terminal marker 44 - */, -/* pos 01ec: 234 */ 0xF0 /* 'p' -> */, -/* pos 01ed: 235 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x01F4 state 236) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x01F9 state 240) */, - 0x08, /* fail */ -/* pos 01f4: 236 */ 0xE3 /* 'c' -> */, -/* pos 01f5: 237 */ 0xF4 /* 't' -> */, -/* pos 01f6: 238 */ 0xBA /* ':' -> */, -/* pos 01f7: 239 */ 0x00, 0x2D /* - terminal marker 45 - */, -/* pos 01f9: 240 */ 0xF2 /* 'r' -> */, -/* pos 01fa: 241 */ 0xE5 /* 'e' -> */, -/* pos 01fb: 242 */ 0xF3 /* 's' -> */, -/* pos 01fc: 243 */ 0xBA /* ':' -> */, -/* pos 01fd: 244 */ 0x00, 0x2E /* - terminal marker 46 - */, -/* pos 01ff: 245 */ 0xF2 /* 'r' -> */, -/* pos 0200: 246 */ 0xEF /* 'o' -> */, -/* pos 0201: 247 */ 0xED /* 'm' -> */, -/* pos 0202: 248 */ 0xBA /* ':' -> */, -/* pos 0203: 249 */ 0x00, 0x2F /* - terminal marker 47 - */, -/* pos 0205: 250 */ 0xF4 /* 't' -> */, -/* pos 0206: 251 */ 0xE3 /* 'c' -> */, -/* pos 0207: 252 */ 0xE8 /* 'h' -> */, -/* pos 0208: 253 */ 0xBA /* ':' -> */, -/* pos 0209: 254 */ 0x00, 0x30 /* - terminal marker 48 - */, -/* pos 020b: 255 */ 0xE1 /* 'a' -> */, -/* pos 020c: 256 */ 0xEE /* 'n' -> */, -/* pos 020d: 257 */ 0xE7 /* 'g' -> */, -/* pos 020e: 258 */ 0xE5 /* 'e' -> */, -/* pos 020f: 259 */ 0xBA /* ':' -> */, -/* pos 0210: 260 */ 0x00, 0x31 /* - terminal marker 49 - */, -/* pos 0212: 261 */ 0xEE /* 'n' -> */, -/* pos 0213: 262 */ 0xED /* 'm' -> */, -/* pos 0214: 263 */ 0xEF /* 'o' -> */, -/* pos 0215: 264 */ 0xE4 /* 'd' -> */, -/* pos 0216: 265 */ 0xE9 /* 'i' -> */, -/* pos 0217: 266 */ 0xE6 /* 'f' -> */, -/* pos 0218: 267 */ 0xE9 /* 'i' -> */, -/* pos 0219: 268 */ 0xE5 /* 'e' -> */, -/* pos 021a: 269 */ 0xE4 /* 'd' -> */, -/* pos 021b: 270 */ 0xAD /* '-' -> */, -/* pos 021c: 271 */ 0xF3 /* 's' -> */, -/* pos 021d: 272 */ 0xE9 /* 'i' -> */, -/* pos 021e: 273 */ 0xEE /* 'n' -> */, -/* pos 021f: 274 */ 0xE3 /* 'c' -> */, -/* pos 0220: 275 */ 0xE5 /* 'e' -> */, -/* pos 0221: 276 */ 0xBA /* ':' -> */, -/* pos 0222: 277 */ 0x00, 0x32 /* - terminal marker 50 - */, -/* pos 0224: 278 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x022E state 279) */, - 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x023C state 292) */, - 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0241 state 296) */, - 0x08, /* fail */ -/* pos 022e: 279 */ 0xF3 /* 's' -> */, -/* pos 022f: 280 */ 0xF4 /* 't' -> */, -/* pos 0230: 281 */ 0xAD /* '-' -> */, -/* pos 0231: 282 */ 0xED /* 'm' -> */, -/* pos 0232: 283 */ 0xEF /* 'o' -> */, -/* pos 0233: 284 */ 0xE4 /* 'd' -> */, -/* pos 0234: 285 */ 0xE9 /* 'i' -> */, -/* pos 0235: 286 */ 0xE6 /* 'f' -> */, -/* pos 0236: 287 */ 0xE9 /* 'i' -> */, -/* pos 0237: 288 */ 0xE5 /* 'e' -> */, -/* pos 0238: 289 */ 0xE4 /* 'd' -> */, -/* pos 0239: 290 */ 0xBA /* ':' -> */, -/* pos 023a: 291 */ 0x00, 0x33 /* - terminal marker 51 - */, -/* pos 023c: 292 */ 0xEE /* 'n' -> */, -/* pos 023d: 293 */ 0xEB /* 'k' -> */, -/* pos 023e: 294 */ 0xBA /* ':' -> */, -/* pos 023f: 295 */ 0x00, 0x34 /* - terminal marker 52 - */, -/* pos 0241: 296 */ 0xE3 /* 'c' -> */, -/* pos 0242: 297 */ 0xE1 /* 'a' -> */, -/* pos 0243: 298 */ 0xF4 /* 't' -> */, -/* pos 0244: 299 */ 0xE9 /* 'i' -> */, -/* pos 0245: 300 */ 0xEF /* 'o' -> */, -/* pos 0246: 301 */ 0xEE /* 'n' -> */, -/* pos 0247: 302 */ 0xBA /* ':' -> */, -/* pos 0248: 303 */ 0x00, 0x35 /* - terminal marker 53 - */, -/* pos 024a: 304 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x0254 state 305) */, - 0x74 /* 't' */, 0x14, 0x00 /* (to 0x0261 state 311) */, - 0x70 /* 'p' */, 0x6F, 0x01 /* (to 0x03BF state 578) */, - 0x08, /* fail */ -/* pos 0254: 305 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x025B state 306) */, - 0x65 /* 'e' */, 0xB1, 0x00 /* (to 0x0308 state 430) */, - 0x08, /* fail */ -/* pos 025b: 306 */ 0xE5 /* 'e' -> */, -/* pos 025c: 307 */ 0xF3 /* 's' -> */, -/* pos 025d: 308 */ 0xE8 /* 'h' -> */, -/* pos 025e: 309 */ 0xBA /* ':' -> */, -/* pos 025f: 310 */ 0x00, 0x39 /* - terminal marker 57 - */, -/* pos 0261: 311 */ 0xF2 /* 'r' -> */, -/* pos 0262: 312 */ 0xF9 /* 'y' -> */, -/* pos 0263: 313 */ 0xAD /* '-' -> */, -/* pos 0264: 314 */ 0xE1 /* 'a' -> */, -/* pos 0265: 315 */ 0xE6 /* 'f' -> */, -/* pos 0266: 316 */ 0xF4 /* 't' -> */, -/* pos 0267: 317 */ 0xE5 /* 'e' -> */, -/* pos 0268: 318 */ 0xF2 /* 'r' -> */, -/* pos 0269: 319 */ 0xBA /* ':' -> */, -/* pos 026a: 320 */ 0x00, 0x3A /* - terminal marker 58 - */, -/* pos 026c: 321 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0273 state 322) */, - 0x74 /* 't' */, 0xED, 0x00 /* (to 0x035C state 496) */, - 0x08, /* fail */ -/* pos 0273: 322 */ 0x72 /* 'r' */, 0x0A, 0x00 /* (to 0x027D state 323) */, - 0x74 /* 't' */, 0x0D, 0x00 /* (to 0x0283 state 328) */, - 0x63 /* 'c' */, 0x52, 0x01 /* (to 0x03CB state 589) */, - 0x08, /* fail */ -/* pos 027d: 323 */ 0xF6 /* 'v' -> */, -/* pos 027e: 324 */ 0xE5 /* 'e' -> */, -/* pos 027f: 325 */ 0xF2 /* 'r' -> */, -/* pos 0280: 326 */ 0xBA /* ':' -> */, -/* pos 0281: 327 */ 0x00, 0x3B /* - terminal marker 59 - */, -/* pos 0283: 328 */ 0xAD /* '-' -> */, -/* pos 0284: 329 */ 0xE3 /* 'c' -> */, -/* pos 0285: 330 */ 0xEF /* 'o' -> */, -/* pos 0286: 331 */ 0xEF /* 'o' -> */, -/* pos 0287: 332 */ 0xEB /* 'k' -> */, -/* pos 0288: 333 */ 0xE9 /* 'i' -> */, -/* pos 0289: 334 */ 0xE5 /* 'e' -> */, -/* pos 028a: 335 */ 0xBA /* ':' -> */, -/* pos 028b: 336 */ 0x00, 0x3C /* - terminal marker 60 - */, -/* pos 028d: 337 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0294 state 338) */, - 0x65 /* 'e' */, 0x2C, 0x01 /* (to 0x03BC state 576) */, - 0x08, /* fail */ -/* pos 0294: 338 */ 0xE1 /* 'a' -> */, -/* pos 0295: 339 */ 0xEE /* 'n' -> */, -/* pos 0296: 340 */ 0xF3 /* 's' -> */, -/* pos 0297: 341 */ 0xE6 /* 'f' -> */, -/* pos 0298: 342 */ 0xE5 /* 'e' -> */, -/* pos 0299: 343 */ 0xF2 /* 'r' -> */, -/* pos 029a: 344 */ 0xAD /* '-' -> */, -/* pos 029b: 345 */ 0xE5 /* 'e' -> */, -/* pos 029c: 346 */ 0xEE /* 'n' -> */, -/* pos 029d: 347 */ 0xE3 /* 'c' -> */, -/* pos 029e: 348 */ 0xEF /* 'o' -> */, -/* pos 029f: 349 */ 0xE4 /* 'd' -> */, -/* pos 02a0: 350 */ 0xE9 /* 'i' -> */, -/* pos 02a1: 351 */ 0xEE /* 'n' -> */, -/* pos 02a2: 352 */ 0xE7 /* 'g' -> */, -/* pos 02a3: 353 */ 0xBA /* ':' -> */, -/* pos 02a4: 354 */ 0x00, 0x3E /* - terminal marker 62 - */, -/* pos 02a6: 355 */ 0xE9 /* 'i' -> */, -/* pos 02a7: 356 */ 0xAD /* '-' -> */, -/* pos 02a8: 357 */ 0xE1 /* 'a' -> */, -/* pos 02a9: 358 */ 0xF2 /* 'r' -> */, -/* pos 02aa: 359 */ 0xE7 /* 'g' -> */, -/* pos 02ab: 360 */ 0xF3 /* 's' -> */, -/* pos 02ac: 361 */ 0x00, 0x46 /* - terminal marker 70 - */, -/* pos 02ae: 362 */ 0xA0 /* ' ' -> */, -/* pos 02af: 363 */ 0x00, 0x49 /* - terminal marker 73 - */, -/* pos 02b1: 364 */ 0xAD /* '-' -> */, -/* pos 02b2: 365 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x02BC state 366) */, - 0x61 /* 'a' */, 0x1D, 0x00 /* (to 0x02D2 state 385) */, - 0x72 /* 'r' */, 0xFB, 0x00 /* (to 0x03B3 state 568) */, - 0x08, /* fail */ -/* pos 02bc: 366 */ 0xEF /* 'o' -> */, -/* pos 02bd: 367 */ 0xF2 /* 'r' -> */, -/* pos 02be: 368 */ 0xF7 /* 'w' -> */, -/* pos 02bf: 369 */ 0xE1 /* 'a' -> */, -/* pos 02c0: 370 */ 0xF2 /* 'r' -> */, -/* pos 02c1: 371 */ 0xE4 /* 'd' -> */, -/* pos 02c2: 372 */ 0xE5 /* 'e' -> */, -/* pos 02c3: 373 */ 0xE4 /* 'd' -> */, -/* pos 02c4: 374 */ 0xAD /* '-' -> */, -/* pos 02c5: 375 */ 0xE6 /* 'f' -> */, -/* pos 02c6: 376 */ 0xEF /* 'o' -> */, -/* pos 02c7: 377 */ 0xF2 /* 'r' -> */, -/* pos 02c8: 378 */ 0xBA /* ':' -> */, -/* pos 02c9: 379 */ 0x00, 0x4A /* - terminal marker 74 - */, -/* pos 02cb: 380 */ 0x00, 0x4B /* - terminal marker 75 - */, -/* pos 02cd: 381 */ 0xE1 /* 'a' -> */, -/* pos 02ce: 382 */ 0xE4 /* 'd' -> */, -/* pos 02cf: 383 */ 0xA0 /* ' ' -> */, -/* pos 02d0: 384 */ 0x00, 0x4C /* - terminal marker 76 - */, -/* pos 02d2: 385 */ 0xF5 /* 'u' -> */, -/* pos 02d3: 386 */ 0xF4 /* 't' -> */, -/* pos 02d4: 387 */ 0xE8 /* 'h' -> */, -/* pos 02d5: 388 */ 0xAD /* '-' -> */, -/* pos 02d6: 389 */ 0xF4 /* 't' -> */, -/* pos 02d7: 390 */ 0xEF /* 'o' -> */, -/* pos 02d8: 391 */ 0xEB /* 'k' -> */, -/* pos 02d9: 392 */ 0xE5 /* 'e' -> */, -/* pos 02da: 393 */ 0xEE /* 'n' -> */, -/* pos 02db: 394 */ 0xBA /* ':' -> */, -/* pos 02dc: 395 */ 0x00, 0x4F /* - terminal marker 79 - */, -/* pos 02de: 396 */ 0xF4 /* 't' -> */, -/* pos 02df: 397 */ 0xE9 /* 'i' -> */, -/* pos 02e0: 398 */ 0xEF /* 'o' -> */, -/* pos 02e1: 399 */ 0xEE /* 'n' -> */, -/* pos 02e2: 400 */ 0xF3 /* 's' -> */, -/* pos 02e3: 401 */ 0xA0 /* ' ' -> */, -/* pos 02e4: 402 */ 0x00, 0x02 /* - terminal marker 2 - */, -/* pos 02e6: 403 */ 0xF3 /* 's' -> */, -/* pos 02e7: 404 */ 0xAD /* '-' -> */, -/* pos 02e8: 405 */ 0xE3 /* 'c' -> */, -/* pos 02e9: 406 */ 0xEF /* 'o' -> */, -/* pos 02ea: 407 */ 0xEE /* 'n' -> */, -/* pos 02eb: 408 */ 0xF4 /* 't' -> */, -/* pos 02ec: 409 */ 0xF2 /* 'r' -> */, -/* pos 02ed: 410 */ 0xEF /* 'o' -> */, -/* pos 02ee: 411 */ 0xEC /* 'l' -> */, -/* pos 02ef: 412 */ 0xAD /* '-' -> */, -/* pos 02f0: 413 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x02F7 state 414) */, - 0x61 /* 'a' */, 0x24, 0x00 /* (to 0x0317 state 443) */, - 0x08, /* fail */ -/* pos 02f7: 414 */ 0xE5 /* 'e' -> */, -/* pos 02f8: 415 */ 0xF1 /* 'q' -> */, -/* pos 02f9: 416 */ 0xF5 /* 'u' -> */, -/* pos 02fa: 417 */ 0xE5 /* 'e' -> */, -/* pos 02fb: 418 */ 0xF3 /* 's' -> */, -/* pos 02fc: 419 */ 0xF4 /* 't' -> */, -/* pos 02fd: 420 */ 0xAD /* '-' -> */, -/* pos 02fe: 421 */ 0xE8 /* 'h' -> */, -/* pos 02ff: 422 */ 0xE5 /* 'e' -> */, -/* pos 0300: 423 */ 0xE1 /* 'a' -> */, -/* pos 0301: 424 */ 0xE4 /* 'd' -> */, -/* pos 0302: 425 */ 0xE5 /* 'e' -> */, -/* pos 0303: 426 */ 0xF2 /* 'r' -> */, -/* pos 0304: 427 */ 0xF3 /* 's' -> */, -/* pos 0305: 428 */ 0xBA /* ':' -> */, -/* pos 0306: 429 */ 0x00, 0x11 /* - terminal marker 17 - */, -/* pos 0308: 430 */ 0xF2 /* 'r' -> */, -/* pos 0309: 431 */ 0xE5 /* 'e' -> */, -/* pos 030a: 432 */ 0xF2 /* 'r' -> */, -/* pos 030b: 433 */ 0xBA /* ':' -> */, -/* pos 030c: 434 */ 0x00, 0x1E /* - terminal marker 30 - */, -/* pos 030e: 435 */ 0xE8 /* 'h' -> */, -/* pos 030f: 436 */ 0xE1 /* 'a' -> */, -/* pos 0310: 437 */ 0xF2 /* 'r' -> */, -/* pos 0311: 438 */ 0xF3 /* 's' -> */, -/* pos 0312: 439 */ 0xE5 /* 'e' -> */, -/* pos 0313: 440 */ 0xF4 /* 't' -> */, -/* pos 0314: 441 */ 0xBA /* ':' -> */, -/* pos 0315: 442 */ 0x00, 0x22 /* - terminal marker 34 - */, -/* pos 0317: 443 */ 0xEC /* 'l' -> */, -/* pos 0318: 444 */ 0xEC /* 'l' -> */, -/* pos 0319: 445 */ 0xEF /* 'o' -> */, -/* pos 031a: 446 */ 0xF7 /* 'w' -> */, -/* pos 031b: 447 */ 0xAD /* '-' -> */, -/* pos 031c: 448 */ 0xEF /* 'o' -> */, -/* pos 031d: 449 */ 0xF2 /* 'r' -> */, -/* pos 031e: 450 */ 0xE9 /* 'i' -> */, -/* pos 031f: 451 */ 0xE7 /* 'g' -> */, -/* pos 0320: 452 */ 0xE9 /* 'i' -> */, -/* pos 0321: 453 */ 0xEE /* 'n' -> */, -/* pos 0322: 454 */ 0xBA /* ':' -> */, -/* pos 0323: 455 */ 0x00, 0x24 /* - terminal marker 36 - */, -/* pos 0325: 456 */ 0xE1 /* 'a' -> */, -/* pos 0326: 457 */ 0xF8 /* 'x' -> */, -/* pos 0327: 458 */ 0xAD /* '-' -> */, -/* pos 0328: 459 */ 0xE6 /* 'f' -> */, -/* pos 0329: 460 */ 0xEF /* 'o' -> */, -/* pos 032a: 461 */ 0xF2 /* 'r' -> */, -/* pos 032b: 462 */ 0xF7 /* 'w' -> */, -/* pos 032c: 463 */ 0xE1 /* 'a' -> */, -/* pos 032d: 464 */ 0xF2 /* 'r' -> */, -/* pos 032e: 465 */ 0xE4 /* 'd' -> */, -/* pos 032f: 466 */ 0xF3 /* 's' -> */, -/* pos 0330: 467 */ 0xBA /* ':' -> */, -/* pos 0331: 468 */ 0x00, 0x36 /* - terminal marker 54 - */, -/* pos 0333: 469 */ 0xF8 /* 'x' -> */, -/* pos 0334: 470 */ 0xF9 /* 'y' -> */, -/* pos 0335: 471 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x033C state 472) */, - 0x20 /* ' ' */, 0x79, 0x00 /* (to 0x03B1 state 567) */, - 0x08, /* fail */ -/* pos 033c: 472 */ 0xE1 /* 'a' -> */, -/* pos 033d: 473 */ 0xF5 /* 'u' -> */, -/* pos 033e: 474 */ 0xF4 /* 't' -> */, -/* pos 033f: 475 */ 0xE8 /* 'h' -> */, -/* pos 0340: 476 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0347 state 477) */, - 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x0351 state 486) */, - 0x08, /* fail */ -/* pos 0347: 477 */ 0xEE /* 'n' -> */, -/* pos 0348: 478 */ 0xF4 /* 't' -> */, -/* pos 0349: 479 */ 0xE9 /* 'i' -> */, -/* pos 034a: 480 */ 0xE3 /* 'c' -> */, -/* pos 034b: 481 */ 0xE1 /* 'a' -> */, -/* pos 034c: 482 */ 0xF4 /* 't' -> */, -/* pos 034d: 483 */ 0xE5 /* 'e' -> */, -/* pos 034e: 484 */ 0xBA /* ':' -> */, -/* pos 034f: 485 */ 0x00, 0x37 /* - terminal marker 55 - */, -/* pos 0351: 486 */ 0xF2 /* 'r' -> */, -/* pos 0352: 487 */ 0xE9 /* 'i' -> */, -/* pos 0353: 488 */ 0xFA /* 'z' -> */, -/* pos 0354: 489 */ 0xE1 /* 'a' -> */, -/* pos 0355: 490 */ 0xF4 /* 't' -> */, -/* pos 0356: 491 */ 0xE9 /* 'i' -> */, -/* pos 0357: 492 */ 0xEF /* 'o' -> */, -/* pos 0358: 493 */ 0xEE /* 'n' -> */, -/* pos 0359: 494 */ 0xBA /* ':' -> */, -/* pos 035a: 495 */ 0x00, 0x38 /* - terminal marker 56 - */, -/* pos 035c: 496 */ 0xF2 /* 'r' -> */, -/* pos 035d: 497 */ 0xE9 /* 'i' -> */, -/* pos 035e: 498 */ 0xE3 /* 'c' -> */, -/* pos 035f: 499 */ 0xF4 /* 't' -> */, -/* pos 0360: 500 */ 0xAD /* '-' -> */, -/* pos 0361: 501 */ 0xF4 /* 't' -> */, -/* pos 0362: 502 */ 0xF2 /* 'r' -> */, -/* pos 0363: 503 */ 0xE1 /* 'a' -> */, -/* pos 0364: 504 */ 0xEE /* 'n' -> */, -/* pos 0365: 505 */ 0xF3 /* 's' -> */, -/* pos 0366: 506 */ 0xF0 /* 'p' -> */, -/* pos 0367: 507 */ 0xEF /* 'o' -> */, -/* pos 0368: 508 */ 0xF2 /* 'r' -> */, -/* pos 0369: 509 */ 0xF4 /* 't' -> */, -/* pos 036a: 510 */ 0xAD /* '-' -> */, -/* pos 036b: 511 */ 0xF3 /* 's' -> */, -/* pos 036c: 512 */ 0xE5 /* 'e' -> */, -/* pos 036d: 513 */ 0xE3 /* 'c' -> */, -/* pos 036e: 514 */ 0xF5 /* 'u' -> */, -/* pos 036f: 515 */ 0xF2 /* 'r' -> */, -/* pos 0370: 516 */ 0xE9 /* 'i' -> */, -/* pos 0371: 517 */ 0xF4 /* 't' -> */, -/* pos 0372: 518 */ 0xF9 /* 'y' -> */, -/* pos 0373: 519 */ 0xBA /* ':' -> */, -/* pos 0374: 520 */ 0x00, 0x3D /* - terminal marker 61 - */, -/* pos 0376: 521 */ 0xE5 /* 'e' -> */, -/* pos 0377: 522 */ 0xF2 /* 'r' -> */, -/* pos 0378: 523 */ 0xAD /* '-' -> */, -/* pos 0379: 524 */ 0xE1 /* 'a' -> */, -/* pos 037a: 525 */ 0xE7 /* 'g' -> */, -/* pos 037b: 526 */ 0xE5 /* 'e' -> */, -/* pos 037c: 527 */ 0xEE /* 'n' -> */, -/* pos 037d: 528 */ 0xF4 /* 't' -> */, -/* pos 037e: 529 */ 0xBA /* ':' -> */, -/* pos 037f: 530 */ 0x00, 0x3F /* - terminal marker 63 - */, -/* pos 0381: 531 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0388 state 532) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x038D state 536) */, - 0x08, /* fail */ -/* pos 0388: 532 */ 0xF2 /* 'r' -> */, -/* pos 0389: 533 */ 0xF9 /* 'y' -> */, -/* pos 038a: 534 */ 0xBA /* ':' -> */, -/* pos 038b: 535 */ 0x00, 0x40 /* - terminal marker 64 - */, -/* pos 038d: 536 */ 0xE1 /* 'a' -> */, -/* pos 038e: 537 */ 0xBA /* ':' -> */, -/* pos 038f: 538 */ 0x00, 0x41 /* - terminal marker 65 - */, -/* pos 0391: 539 */ 0xF7 /* 'w' -> */, -/* pos 0392: 540 */ 0xF7 /* 'w' -> */, -/* pos 0393: 541 */ 0xAD /* '-' -> */, -/* pos 0394: 542 */ 0xE1 /* 'a' -> */, -/* pos 0395: 543 */ 0xF5 /* 'u' -> */, -/* pos 0396: 544 */ 0xF4 /* 't' -> */, -/* pos 0397: 545 */ 0xE8 /* 'h' -> */, -/* pos 0398: 546 */ 0xE5 /* 'e' -> */, -/* pos 0399: 547 */ 0xEE /* 'n' -> */, -/* pos 039a: 548 */ 0xF4 /* 't' -> */, -/* pos 039b: 549 */ 0xE9 /* 'i' -> */, -/* pos 039c: 550 */ 0xE3 /* 'c' -> */, -/* pos 039d: 551 */ 0xE1 /* 'a' -> */, -/* pos 039e: 552 */ 0xF4 /* 't' -> */, -/* pos 039f: 553 */ 0xE5 /* 'e' -> */, -/* pos 03a0: 554 */ 0xBA /* ':' -> */, -/* pos 03a1: 555 */ 0x00, 0x42 /* - terminal marker 66 - */, -/* pos 03a3: 556 */ 0xF4 /* 't' -> */, -/* pos 03a4: 557 */ 0xE3 /* 'c' -> */, -/* pos 03a5: 558 */ 0xE8 /* 'h' -> */, -/* pos 03a6: 559 */ 0x00, 0x43 /* - terminal marker 67 - */, -/* pos 03a8: 560 */ 0xF4 /* 't' -> */, -/* pos 03a9: 561 */ 0x00, 0x44 /* - terminal marker 68 - */, -/* pos 03ab: 562 */ 0xEC /* 'l' -> */, -/* pos 03ac: 563 */ 0xE5 /* 'e' -> */, -/* pos 03ad: 564 */ 0xF4 /* 't' -> */, -/* pos 03ae: 565 */ 0xE5 /* 'e' -> */, -/* pos 03af: 566 */ 0x00, 0x45 /* - terminal marker 69 - */, -/* pos 03b1: 567 */ 0x00, 0x47 /* - terminal marker 71 - */, -/* pos 03b3: 568 */ 0xE5 /* 'e' -> */, -/* pos 03b4: 569 */ 0xE1 /* 'a' -> */, -/* pos 03b5: 570 */ 0xEC /* 'l' -> */, -/* pos 03b6: 571 */ 0xAD /* '-' -> */, -/* pos 03b7: 572 */ 0xE9 /* 'i' -> */, -/* pos 03b8: 573 */ 0xF0 /* 'p' -> */, -/* pos 03b9: 574 */ 0xBA /* ':' -> */, -/* pos 03ba: 575 */ 0x00, 0x48 /* - terminal marker 72 - */, -/* pos 03bc: 576 */ 0xBA /* ':' -> */, -/* pos 03bd: 577 */ 0x00, 0x4D /* - terminal marker 77 - */, -/* pos 03bf: 578 */ 0xEC /* 'l' -> */, -/* pos 03c0: 579 */ 0xE1 /* 'a' -> */, -/* pos 03c1: 580 */ 0xF9 /* 'y' -> */, -/* pos 03c2: 581 */ 0xAD /* '-' -> */, -/* pos 03c3: 582 */ 0xEE /* 'n' -> */, -/* pos 03c4: 583 */ 0xEF /* 'o' -> */, -/* pos 03c5: 584 */ 0xEE /* 'n' -> */, -/* pos 03c6: 585 */ 0xE3 /* 'c' -> */, -/* pos 03c7: 586 */ 0xE5 /* 'e' -> */, -/* pos 03c8: 587 */ 0xBA /* ':' -> */, -/* pos 03c9: 588 */ 0x00, 0x4E /* - terminal marker 78 - */, -/* pos 03cb: 589 */ 0xAD /* '-' -> */, -/* pos 03cc: 590 */ 0xF7 /* 'w' -> */, -/* pos 03cd: 591 */ 0xE5 /* 'e' -> */, -/* pos 03ce: 592 */ 0xE2 /* 'b' -> */, -/* pos 03cf: 593 */ 0xF3 /* 's' -> */, -/* pos 03d0: 594 */ 0xEF /* 'o' -> */, -/* pos 03d1: 595 */ 0xE3 /* 'c' -> */, -/* pos 03d2: 596 */ 0xEB /* 'k' -> */, -/* pos 03d3: 597 */ 0xE5 /* 'e' -> */, -/* pos 03d4: 598 */ 0xF4 /* 't' -> */, -/* pos 03d5: 599 */ 0xAD /* '-' -> */, -/* pos 03d6: 600 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x03EF state 601) */, - 0x65 /* 'e' */, 0x1D, 0x00 /* (to 0x03F6 state 607) */, - 0x6B /* 'k' */, 0x26, 0x00 /* (to 0x0402 state 618) */, - 0x70 /* 'p' */, 0x35, 0x00 /* (to 0x0414 state 625) */, - 0x61 /* 'a' */, 0x3C, 0x00 /* (to 0x041E state 634) */, - 0x6E /* 'n' */, 0x41, 0x00 /* (to 0x0426 state 641) */, - 0x76 /* 'v' */, 0x47, 0x00 /* (to 0x042F state 648) */, - 0x6F /* 'o' */, 0x4D, 0x00 /* (to 0x0438 state 656) */, - 0x08, /* fail */ -/* pos 03ef: 601 */ 0xF2 /* 'r' -> */, -/* pos 03f0: 602 */ 0xE1 /* 'a' -> */, -/* pos 03f1: 603 */ 0xE6 /* 'f' -> */, -/* pos 03f2: 604 */ 0xF4 /* 't' -> */, -/* pos 03f3: 605 */ 0xBA /* ':' -> */, -/* pos 03f4: 606 */ 0x00, 0x07 /* - terminal marker 7 - */, -/* pos 03f6: 607 */ 0xF8 /* 'x' -> */, -/* pos 03f7: 608 */ 0xF4 /* 't' -> */, -/* pos 03f8: 609 */ 0xE5 /* 'e' -> */, -/* pos 03f9: 610 */ 0xEE /* 'n' -> */, -/* pos 03fa: 611 */ 0xF3 /* 's' -> */, -/* pos 03fb: 612 */ 0xE9 /* 'i' -> */, -/* pos 03fc: 613 */ 0xEF /* 'o' -> */, -/* pos 03fd: 614 */ 0xEE /* 'n' -> */, -/* pos 03fe: 615 */ 0xF3 /* 's' -> */, -/* pos 03ff: 616 */ 0xBA /* ':' -> */, -/* pos 0400: 617 */ 0x00, 0x09 /* - terminal marker 9 - */, -/* pos 0402: 618 */ 0xE5 /* 'e' -> */, -/* pos 0403: 619 */ 0xF9 /* 'y' -> */, -/* pos 0404: 620 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x040E state 621) */, - 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x0411 state 623) */, - 0x3A /* ':' */, 0x23, 0x00 /* (to 0x042D state 647) */, - 0x08, /* fail */ -/* pos 040e: 621 */ 0xBA /* ':' -> */, -/* pos 040f: 622 */ 0x00, 0x0A /* - terminal marker 10 - */, -/* pos 0411: 623 */ 0xBA /* ':' -> */, -/* pos 0412: 624 */ 0x00, 0x0B /* - terminal marker 11 - */, -/* pos 0414: 625 */ 0xF2 /* 'r' -> */, -/* pos 0415: 626 */ 0xEF /* 'o' -> */, -/* pos 0416: 627 */ 0xF4 /* 't' -> */, -/* pos 0417: 628 */ 0xEF /* 'o' -> */, -/* pos 0418: 629 */ 0xE3 /* 'c' -> */, -/* pos 0419: 630 */ 0xEF /* 'o' -> */, -/* pos 041a: 631 */ 0xEC /* 'l' -> */, -/* pos 041b: 632 */ 0xBA /* ':' -> */, -/* pos 041c: 633 */ 0x00, 0x0C /* - terminal marker 12 - */, -/* pos 041e: 634 */ 0xE3 /* 'c' -> */, -/* pos 041f: 635 */ 0xE3 /* 'c' -> */, -/* pos 0420: 636 */ 0xE5 /* 'e' -> */, -/* pos 0421: 637 */ 0xF0 /* 'p' -> */, -/* pos 0422: 638 */ 0xF4 /* 't' -> */, -/* pos 0423: 639 */ 0xBA /* ':' -> */, -/* pos 0424: 640 */ 0x00, 0x0D /* - terminal marker 13 - */, -/* pos 0426: 641 */ 0xEF /* 'o' -> */, -/* pos 0427: 642 */ 0xEE /* 'n' -> */, -/* pos 0428: 643 */ 0xE3 /* 'c' -> */, -/* pos 0429: 644 */ 0xE5 /* 'e' -> */, -/* pos 042a: 645 */ 0xBA /* ':' -> */, -/* pos 042b: 646 */ 0x00, 0x0E /* - terminal marker 14 - */, -/* pos 042d: 647 */ 0x00, 0x1F /* - terminal marker 31 - */, -/* pos 042f: 648 */ 0xE5 /* 'e' -> */, -/* pos 0430: 649 */ 0xF2 /* 'r' -> */, -/* pos 0431: 650 */ 0xF3 /* 's' -> */, -/* pos 0432: 651 */ 0xE9 /* 'i' -> */, -/* pos 0433: 652 */ 0xEF /* 'o' -> */, -/* pos 0434: 653 */ 0xEE /* 'n' -> */, -/* pos 0435: 654 */ 0xBA /* ':' -> */, -/* pos 0436: 655 */ 0x00, 0x20 /* - terminal marker 32 - */, -/* pos 0438: 656 */ 0xF2 /* 'r' -> */, -/* pos 0439: 657 */ 0xE9 /* 'i' -> */, -/* pos 043a: 658 */ 0xE7 /* 'g' -> */, -/* pos 043b: 659 */ 0xE9 /* 'i' -> */, -/* pos 043c: 660 */ 0xEE /* 'n' -> */, -/* pos 043d: 661 */ 0xBA /* ':' -> */, -/* pos 043e: 662 */ 0x00, 0x21 /* - terminal marker 33 - */, -/* total size 1088 bytes */ -#endif - -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) - /* 0: 0: get */ - /* 1: 1: post */ - /* 2: 3: host: */ - /* 3: 4: connection: */ - /* 4: 5: upgrade: */ - /* 5: 6: origin: */ - /* 6: 8: - */ - /* 7: 15: http/1.1 */ - /* 8: 16: http2-settings: */ - /* 9: 17: accept: */ - /* 10: 19: if-modified-since: */ - /* 11: 20: if-none-match: */ - /* 12: 21: accept-encoding: */ - /* 13: 22: accept-language: */ - /* 14: 23: pragma: */ - /* 15: 24: cache-control: */ - /* 16: 25: authorization: */ - /* 17: 26: cookie: */ - /* 18: 27: content-length: */ - /* 19: 28: content-type: */ - /* 20: 29: date: */ - /* 21: 30: range: */ - /* 22: 31: referer: */ - /* 23: 35: :authority */ - /* 24: 36: :method */ - /* 25: 37: :path */ - /* 26: 38: :scheme */ - /* 27: 39: :status */ - /* 28: 40: accept-charset: */ - /* 29: 41: accept-ranges: */ - /* 30: 42: access-control-allow-origin: */ - /* 31: 43: age: */ - /* 32: 44: allow: */ - /* 33: 45: content-disposition: */ - /* 34: 46: content-encoding: */ - /* 35: 47: content-language: */ - /* 36: 48: content-location: */ - /* 37: 49: content-range: */ - /* 38: 50: etag: */ - /* 39: 51: expect: */ - /* 40: 52: expires: */ - /* 41: 53: from: */ - /* 42: 54: if-match: */ - /* 43: 55: if-range: */ - /* 44: 56: if-unmodified-since: */ - /* 45: 57: last-modified: */ - /* 46: 58: link: */ - /* 47: 59: location: */ - /* 48: 60: max-forwards: */ - /* 49: 61: proxy-authenticate: */ - /* 50: 62: proxy-authorization: */ - /* 51: 63: refresh: */ - /* 52: 64: retry-after: */ - /* 53: 65: server: */ - /* 54: 66: set-cookie: */ - /* 55: 67: strict-transport-security: */ - /* 56: 68: transfer-encoding: */ - /* 57: 69: user-agent: */ - /* 58: 70: vary: */ - /* 59: 71: via: */ - /* 60: 72: www-authenticate: */ - /* 61: 76: uri-args */ - /* 62: 79: http/1.0 */ - /* 63: 80: x-forwarded-for: */ - /* 64: 81: connect */ - /* 65: 82: head */ - /* 66: 83: te: */ - /* 67: 84: replay-nonce: */ - /* 68: 85: :protocol */ - /* 69: 86: x-auth-token: */ - /* 70: 87: */ -/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */, - 0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */, - 0x68 /* 'h' */, 0x51, 0x00 /* (to 0x0057 state 10) */, - 0x63 /* 'c' */, 0x5D, 0x00 /* (to 0x0066 state 15) */, - 0x75 /* 'u' */, 0x7E, 0x00 /* (to 0x008A state 26) */, - 0x6F /* 'o' */, 0x8D, 0x00 /* (to 0x009C state 34) */, - 0x0D /* '.' */, 0x98, 0x00 /* (to 0x00AA state 41) */, - 0x61 /* 'a' */, 0xAD, 0x00 /* (to 0x00C2 state 51) */, - 0x69 /* 'i' */, 0xCA, 0x00 /* (to 0x00E2 state 58) */, - 0x64 /* 'd' */, 0x73, 0x01 /* (to 0x018E state 160) */, - 0x72 /* 'r' */, 0x7C, 0x01 /* (to 0x019A state 165) */, - 0x65 /* 'e' */, 0xC8, 0x01 /* (to 0x01E9 state 229) */, - 0x66 /* 'f' */, 0xE4, 0x01 /* (to 0x0208 state 245) */, - 0x6C /* 'l' */, 0x06, 0x02 /* (to 0x022D state 278) */, - 0x73 /* 's' */, 0x4B, 0x02 /* (to 0x0275 state 321) */, - 0x74 /* 't' */, 0x69, 0x02 /* (to 0x0296 state 337) */, - 0x78 /* 'x' */, 0x8A, 0x02 /* (to 0x02BA state 364) */, - 0x6D /* 'm' */, 0xFB, 0x02 /* (to 0x032E state 456) */, - 0x76 /* 'v' */, 0x54, 0x03 /* (to 0x038A state 531) */, - 0x77 /* 'w' */, 0x61, 0x03 /* (to 0x039A state 539) */, - 0x3A /* ':' */, 0x19, 0x04 /* (to 0x0455 state 674) */, - 0x08, /* fail */ -/* pos 0040: 1 */ 0xE5 /* 'e' -> */, -/* pos 0041: 2 */ 0xF4 /* 't' -> */, -/* pos 0042: 3 */ 0xA0 /* ' ' -> */, -/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, -/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */, - 0x72 /* 'r' */, 0xEC, 0x00 /* (to 0x0134 state 106) */, - 0x61 /* 'a' */, 0x61, 0x03 /* (to 0x03AC state 556) */, - 0x75 /* 'u' */, 0x63, 0x03 /* (to 0x03B1 state 560) */, - 0x08, /* fail */ -/* pos 0052: 6 */ 0xF3 /* 's' -> */, -/* pos 0053: 7 */ 0xF4 /* 't' -> */, -/* pos 0054: 8 */ 0xA0 /* ' ' -> */, -/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, -/* pos 0057: 10 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x0061 state 11) */, - 0x74 /* 't' */, 0x53, 0x00 /* (to 0x00AD state 43) */, - 0x65 /* 'e' */, 0x79, 0x02 /* (to 0x02D6 state 381) */, - 0x08, /* fail */ -/* pos 0061: 11 */ 0xF3 /* 's' -> */, -/* pos 0062: 12 */ 0xF4 /* 't' -> */, -/* pos 0063: 13 */ 0xBA /* ':' -> */, -/* pos 0064: 14 */ 0x00, 0x02 /* - terminal marker 2 - */, -/* pos 0066: 15 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x006D state 16) */, - 0x61 /* 'a' */, 0xD8, 0x00 /* (to 0x0141 state 112) */, - 0x08, /* fail */ -/* pos 006d: 16 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0074 state 17) */, - 0x6F /* 'o' */, 0xED, 0x00 /* (to 0x015D state 138) */, - 0x08, /* fail */ -/* pos 0074: 17 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x007B state 18) */, - 0x74 /* 't' */, 0xEC, 0x00 /* (to 0x0163 state 143) */, - 0x08, /* fail */ -/* pos 007b: 18 */ 0xE5 /* 'e' -> */, -/* pos 007c: 19 */ 0xE3 /* 'c' -> */, -/* pos 007d: 20 */ 0xF4 /* 't' -> */, -/* pos 007e: 21 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0085 state 22) */, - 0x20 /* ' ' */, 0x53, 0x02 /* (to 0x02D4 state 380) */, - 0x08, /* fail */ -/* pos 0085: 22 */ 0xEF /* 'o' -> */, -/* pos 0086: 23 */ 0xEE /* 'n' -> */, -/* pos 0087: 24 */ 0xBA /* ':' -> */, -/* pos 0088: 25 */ 0x00, 0x03 /* - terminal marker 3 - */, -/* pos 008a: 26 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x0094 state 27) */, - 0x72 /* 'r' */, 0x22, 0x02 /* (to 0x02AF state 355) */, - 0x73 /* 's' */, 0xEF, 0x02 /* (to 0x037F state 521) */, - 0x08, /* fail */ -/* pos 0094: 27 */ 0xE7 /* 'g' -> */, -/* pos 0095: 28 */ 0xF2 /* 'r' -> */, -/* pos 0096: 29 */ 0xE1 /* 'a' -> */, -/* pos 0097: 30 */ 0xE4 /* 'd' -> */, -/* pos 0098: 31 */ 0xE5 /* 'e' -> */, -/* pos 0099: 32 */ 0xBA /* ':' -> */, -/* pos 009a: 33 */ 0x00, 0x04 /* - terminal marker 4 - */, -/* pos 009c: 34 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x00A3 state 35) */, - 0x70 /* 'p' */, 0x48, 0x02 /* (to 0x02E7 state 396) */, - 0x08, /* fail */ -/* pos 00a3: 35 */ 0xE9 /* 'i' -> */, -/* pos 00a4: 36 */ 0xE7 /* 'g' -> */, -/* pos 00a5: 37 */ 0xE9 /* 'i' -> */, -/* pos 00a6: 38 */ 0xEE /* 'n' -> */, -/* pos 00a7: 39 */ 0xBA /* ':' -> */, -/* pos 00a8: 40 */ 0x00, 0x05 /* - terminal marker 5 - */, -/* pos 00aa: 41 */ 0x8A /* '.' -> */, -/* pos 00ab: 42 */ 0x00, 0x06 /* - terminal marker 6 - */, -/* pos 00ad: 43 */ 0xF4 /* 't' -> */, -/* pos 00ae: 44 */ 0xF0 /* 'p' -> */, -/* pos 00af: 45 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x00B6 state 46) */, - 0x32 /* '2' */, 0x97, 0x03 /* (to 0x0449 state 663) */, - 0x08, /* fail */ -/* pos 00b6: 46 */ 0xB1 /* '1' -> */, -/* pos 00b7: 47 */ 0xAE /* '.' -> */, -/* pos 00b8: 48 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x00BF state 49) */, - 0x30 /* '0' */, 0xFC, 0x01 /* (to 0x02B7 state 362) */, - 0x08, /* fail */ -/* pos 00bf: 49 */ 0xA0 /* ' ' -> */, -/* pos 00c0: 50 */ 0x00, 0x07 /* - terminal marker 7 - */, -/* pos 00c2: 51 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x00CF state 52) */, - 0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x014F state 125) */, - 0x67 /* 'g' */, 0xE7, 0x00 /* (to 0x01AF state 178) */, - 0x6C /* 'l' */, 0xE8, 0x00 /* (to 0x01B3 state 181) */, - 0x08, /* fail */ -/* pos 00cf: 52 */ 0xE3 /* 'c' -> */, -/* pos 00d0: 53 */ 0xE5 /* 'e' -> */, -/* pos 00d1: 54 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x00D8 state 55) */, - 0x73 /* 's' */, 0x1B, 0x02 /* (to 0x02EF state 403) */, - 0x08, /* fail */ -/* pos 00d8: 55 */ 0xF4 /* 't' -> */, -/* pos 00d9: 56 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x00E0 state 57) */, - 0x2D /* '-' */, 0x37, 0x00 /* (to 0x0113 state 87) */, - 0x08, /* fail */ -/* pos 00e0: 57 */ 0x00, 0x09 /* - terminal marker 9 - */, -/* pos 00e2: 58 */ 0xE6 /* 'f' -> */, -/* pos 00e3: 59 */ 0xAD /* '-' -> */, -/* pos 00e4: 60 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x00F1 state 61) */, - 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x0107 state 76) */, - 0x72 /* 'r' */, 0x2A, 0x01 /* (to 0x0214 state 255) */, - 0x75 /* 'u' */, 0x2E, 0x01 /* (to 0x021B state 261) */, - 0x08, /* fail */ -/* pos 00f1: 61 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x00F8 state 62) */, - 0x61 /* 'a' */, 0x1A, 0x01 /* (to 0x020E state 250) */, - 0x08, /* fail */ -/* pos 00f8: 62 */ 0xE4 /* 'd' -> */, -/* pos 00f9: 63 */ 0xE9 /* 'i' -> */, -/* pos 00fa: 64 */ 0xE6 /* 'f' -> */, -/* pos 00fb: 65 */ 0xE9 /* 'i' -> */, -/* pos 00fc: 66 */ 0xE5 /* 'e' -> */, -/* pos 00fd: 67 */ 0xE4 /* 'd' -> */, -/* pos 00fe: 68 */ 0xAD /* '-' -> */, -/* pos 00ff: 69 */ 0xF3 /* 's' -> */, -/* pos 0100: 70 */ 0xE9 /* 'i' -> */, -/* pos 0101: 71 */ 0xEE /* 'n' -> */, -/* pos 0102: 72 */ 0xE3 /* 'c' -> */, -/* pos 0103: 73 */ 0xE5 /* 'e' -> */, -/* pos 0104: 74 */ 0xBA /* ':' -> */, -/* pos 0105: 75 */ 0x00, 0x0A /* - terminal marker 10 - */, -/* pos 0107: 76 */ 0xEF /* 'o' -> */, -/* pos 0108: 77 */ 0xEE /* 'n' -> */, -/* pos 0109: 78 */ 0xE5 /* 'e' -> */, -/* pos 010a: 79 */ 0xAD /* '-' -> */, -/* pos 010b: 80 */ 0xED /* 'm' -> */, -/* pos 010c: 81 */ 0xE1 /* 'a' -> */, -/* pos 010d: 82 */ 0xF4 /* 't' -> */, -/* pos 010e: 83 */ 0xE3 /* 'c' -> */, -/* pos 010f: 84 */ 0xE8 /* 'h' -> */, -/* pos 0110: 85 */ 0xBA /* ':' -> */, -/* pos 0111: 86 */ 0x00, 0x0B /* - terminal marker 11 - */, -/* pos 0113: 87 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x0120 state 88) */, - 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x012A state 97) */, - 0x72 /* 'r' */, 0x8E, 0x00 /* (to 0x01A7 state 171) */, - 0x63 /* 'c' */, 0xFB, 0x01 /* (to 0x0317 state 435) */, - 0x08, /* fail */ -/* pos 0120: 88 */ 0xEE /* 'n' -> */, -/* pos 0121: 89 */ 0xE3 /* 'c' -> */, -/* pos 0122: 90 */ 0xEF /* 'o' -> */, -/* pos 0123: 91 */ 0xE4 /* 'd' -> */, -/* pos 0124: 92 */ 0xE9 /* 'i' -> */, -/* pos 0125: 93 */ 0xEE /* 'n' -> */, -/* pos 0126: 94 */ 0xE7 /* 'g' -> */, -/* pos 0127: 95 */ 0xBA /* ':' -> */, -/* pos 0128: 96 */ 0x00, 0x0C /* - terminal marker 12 - */, -/* pos 012a: 97 */ 0xE1 /* 'a' -> */, -/* pos 012b: 98 */ 0xEE /* 'n' -> */, -/* pos 012c: 99 */ 0xE7 /* 'g' -> */, -/* pos 012d: 100 */ 0xF5 /* 'u' -> */, -/* pos 012e: 101 */ 0xE1 /* 'a' -> */, -/* pos 012f: 102 */ 0xE7 /* 'g' -> */, -/* pos 0130: 103 */ 0xE5 /* 'e' -> */, -/* pos 0131: 104 */ 0xBA /* ':' -> */, -/* pos 0132: 105 */ 0x00, 0x0D /* - terminal marker 13 - */, -/* pos 0134: 106 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x013B state 107) */, - 0x6F /* 'o' */, 0x05, 0x02 /* (to 0x033C state 469) */, - 0x08, /* fail */ -/* pos 013b: 107 */ 0xE7 /* 'g' -> */, -/* pos 013c: 108 */ 0xED /* 'm' -> */, -/* pos 013d: 109 */ 0xE1 /* 'a' -> */, -/* pos 013e: 110 */ 0xBA /* ':' -> */, -/* pos 013f: 111 */ 0x00, 0x0E /* - terminal marker 14 - */, -/* pos 0141: 112 */ 0xE3 /* 'c' -> */, -/* pos 0142: 113 */ 0xE8 /* 'h' -> */, -/* pos 0143: 114 */ 0xE5 /* 'e' -> */, -/* pos 0144: 115 */ 0xAD /* '-' -> */, -/* pos 0145: 116 */ 0xE3 /* 'c' -> */, -/* pos 0146: 117 */ 0xEF /* 'o' -> */, -/* pos 0147: 118 */ 0xEE /* 'n' -> */, -/* pos 0148: 119 */ 0xF4 /* 't' -> */, -/* pos 0149: 120 */ 0xF2 /* 'r' -> */, -/* pos 014a: 121 */ 0xEF /* 'o' -> */, -/* pos 014b: 122 */ 0xEC /* 'l' -> */, -/* pos 014c: 123 */ 0xBA /* ':' -> */, -/* pos 014d: 124 */ 0x00, 0x0F /* - terminal marker 15 - */, -/* pos 014f: 125 */ 0xF4 /* 't' -> */, -/* pos 0150: 126 */ 0xE8 /* 'h' -> */, -/* pos 0151: 127 */ 0xEF /* 'o' -> */, -/* pos 0152: 128 */ 0xF2 /* 'r' -> */, -/* pos 0153: 129 */ 0xE9 /* 'i' -> */, -/* pos 0154: 130 */ 0xFA /* 'z' -> */, -/* pos 0155: 131 */ 0xE1 /* 'a' -> */, -/* pos 0156: 132 */ 0xF4 /* 't' -> */, -/* pos 0157: 133 */ 0xE9 /* 'i' -> */, -/* pos 0158: 134 */ 0xEF /* 'o' -> */, -/* pos 0159: 135 */ 0xEE /* 'n' -> */, -/* pos 015a: 136 */ 0xBA /* ':' -> */, -/* pos 015b: 137 */ 0x00, 0x10 /* - terminal marker 16 - */, -/* pos 015d: 138 */ 0xEB /* 'k' -> */, -/* pos 015e: 139 */ 0xE9 /* 'i' -> */, -/* pos 015f: 140 */ 0xE5 /* 'e' -> */, -/* pos 0160: 141 */ 0xBA /* ':' -> */, -/* pos 0161: 142 */ 0x00, 0x11 /* - terminal marker 17 - */, -/* pos 0163: 143 */ 0xE5 /* 'e' -> */, -/* pos 0164: 144 */ 0xEE /* 'n' -> */, -/* pos 0165: 145 */ 0xF4 /* 't' -> */, -/* pos 0166: 146 */ 0xAD /* '-' -> */, -/* pos 0167: 147 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0177 state 148) */, - 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0188 state 155) */, - 0x64 /* 'd' */, 0x4C, 0x00 /* (to 0x01B9 state 186) */, - 0x65 /* 'e' */, 0x56, 0x00 /* (to 0x01C6 state 198) */, - 0x72 /* 'r' */, 0x6F, 0x00 /* (to 0x01E2 state 223) */, - 0x08, /* fail */ -/* pos 0177: 148 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0181 state 149) */, - 0x61 /* 'a' */, 0x56, 0x00 /* (to 0x01D0 state 207) */, - 0x6F /* 'o' */, 0x5C, 0x00 /* (to 0x01D9 state 215) */, - 0x08, /* fail */ -/* pos 0181: 149 */ 0xEE /* 'n' -> */, -/* pos 0182: 150 */ 0xE7 /* 'g' -> */, -/* pos 0183: 151 */ 0xF4 /* 't' -> */, -/* pos 0184: 152 */ 0xE8 /* 'h' -> */, -/* pos 0185: 153 */ 0xBA /* ':' -> */, -/* pos 0186: 154 */ 0x00, 0x12 /* - terminal marker 18 - */, -/* pos 0188: 155 */ 0xF9 /* 'y' -> */, -/* pos 0189: 156 */ 0xF0 /* 'p' -> */, -/* pos 018a: 157 */ 0xE5 /* 'e' -> */, -/* pos 018b: 158 */ 0xBA /* ':' -> */, -/* pos 018c: 159 */ 0x00, 0x13 /* - terminal marker 19 - */, -/* pos 018e: 160 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0195 state 161) */, - 0x65 /* 'e' */, 0x23, 0x02 /* (to 0x03B4 state 562) */, - 0x08, /* fail */ -/* pos 0195: 161 */ 0xF4 /* 't' -> */, -/* pos 0196: 162 */ 0xE5 /* 'e' -> */, -/* pos 0197: 163 */ 0xBA /* ':' -> */, -/* pos 0198: 164 */ 0x00, 0x14 /* - terminal marker 20 - */, -/* pos 019a: 165 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01A1 state 166) */, - 0x65 /* 'e' */, 0xB6, 0x00 /* (to 0x0253 state 304) */, - 0x08, /* fail */ -/* pos 01a1: 166 */ 0xEE /* 'n' -> */, -/* pos 01a2: 167 */ 0xE7 /* 'g' -> */, -/* pos 01a3: 168 */ 0xE5 /* 'e' -> */, -/* pos 01a4: 169 */ 0xBA /* ':' -> */, -/* pos 01a5: 170 */ 0x00, 0x15 /* - terminal marker 21 - */, -/* pos 01a7: 171 */ 0xE1 /* 'a' -> */, -/* pos 01a8: 172 */ 0xEE /* 'n' -> */, -/* pos 01a9: 173 */ 0xE7 /* 'g' -> */, -/* pos 01aa: 174 */ 0xE5 /* 'e' -> */, -/* pos 01ab: 175 */ 0xF3 /* 's' -> */, -/* pos 01ac: 176 */ 0xBA /* ':' -> */, -/* pos 01ad: 177 */ 0x00, 0x1D /* - terminal marker 29 - */, -/* pos 01af: 178 */ 0xE5 /* 'e' -> */, -/* pos 01b0: 179 */ 0xBA /* ':' -> */, -/* pos 01b1: 180 */ 0x00, 0x1F /* - terminal marker 31 - */, -/* pos 01b3: 181 */ 0xEC /* 'l' -> */, -/* pos 01b4: 182 */ 0xEF /* 'o' -> */, -/* pos 01b5: 183 */ 0xF7 /* 'w' -> */, -/* pos 01b6: 184 */ 0xBA /* ':' -> */, -/* pos 01b7: 185 */ 0x00, 0x20 /* - terminal marker 32 - */, -/* pos 01b9: 186 */ 0xE9 /* 'i' -> */, -/* pos 01ba: 187 */ 0xF3 /* 's' -> */, -/* pos 01bb: 188 */ 0xF0 /* 'p' -> */, -/* pos 01bc: 189 */ 0xEF /* 'o' -> */, -/* pos 01bd: 190 */ 0xF3 /* 's' -> */, -/* pos 01be: 191 */ 0xE9 /* 'i' -> */, -/* pos 01bf: 192 */ 0xF4 /* 't' -> */, -/* pos 01c0: 193 */ 0xE9 /* 'i' -> */, -/* pos 01c1: 194 */ 0xEF /* 'o' -> */, -/* pos 01c2: 195 */ 0xEE /* 'n' -> */, -/* pos 01c3: 196 */ 0xBA /* ':' -> */, -/* pos 01c4: 197 */ 0x00, 0x21 /* - terminal marker 33 - */, -/* pos 01c6: 198 */ 0xEE /* 'n' -> */, -/* pos 01c7: 199 */ 0xE3 /* 'c' -> */, -/* pos 01c8: 200 */ 0xEF /* 'o' -> */, -/* pos 01c9: 201 */ 0xE4 /* 'd' -> */, -/* pos 01ca: 202 */ 0xE9 /* 'i' -> */, -/* pos 01cb: 203 */ 0xEE /* 'n' -> */, -/* pos 01cc: 204 */ 0xE7 /* 'g' -> */, -/* pos 01cd: 205 */ 0xBA /* ':' -> */, -/* pos 01ce: 206 */ 0x00, 0x22 /* - terminal marker 34 - */, -/* pos 01d0: 207 */ 0xEE /* 'n' -> */, -/* pos 01d1: 208 */ 0xE7 /* 'g' -> */, -/* pos 01d2: 209 */ 0xF5 /* 'u' -> */, -/* pos 01d3: 210 */ 0xE1 /* 'a' -> */, -/* pos 01d4: 211 */ 0xE7 /* 'g' -> */, -/* pos 01d5: 212 */ 0xE5 /* 'e' -> */, -/* pos 01d6: 213 */ 0xBA /* ':' -> */, -/* pos 01d7: 214 */ 0x00, 0x23 /* - terminal marker 35 - */, -/* pos 01d9: 215 */ 0xE3 /* 'c' -> */, -/* pos 01da: 216 */ 0xE1 /* 'a' -> */, -/* pos 01db: 217 */ 0xF4 /* 't' -> */, -/* pos 01dc: 218 */ 0xE9 /* 'i' -> */, -/* pos 01dd: 219 */ 0xEF /* 'o' -> */, -/* pos 01de: 220 */ 0xEE /* 'n' -> */, -/* pos 01df: 221 */ 0xBA /* ':' -> */, -/* pos 01e0: 222 */ 0x00, 0x24 /* - terminal marker 36 - */, -/* pos 01e2: 223 */ 0xE1 /* 'a' -> */, -/* pos 01e3: 224 */ 0xEE /* 'n' -> */, -/* pos 01e4: 225 */ 0xE7 /* 'g' -> */, -/* pos 01e5: 226 */ 0xE5 /* 'e' -> */, -/* pos 01e6: 227 */ 0xBA /* ':' -> */, -/* pos 01e7: 228 */ 0x00, 0x25 /* - terminal marker 37 - */, -/* pos 01e9: 229 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x01F0 state 230) */, - 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x01F5 state 234) */, - 0x08, /* fail */ -/* pos 01f0: 230 */ 0xE1 /* 'a' -> */, -/* pos 01f1: 231 */ 0xE7 /* 'g' -> */, -/* pos 01f2: 232 */ 0xBA /* ':' -> */, -/* pos 01f3: 233 */ 0x00, 0x26 /* - terminal marker 38 - */, -/* pos 01f5: 234 */ 0xF0 /* 'p' -> */, -/* pos 01f6: 235 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x01FD state 236) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0202 state 240) */, - 0x08, /* fail */ -/* pos 01fd: 236 */ 0xE3 /* 'c' -> */, -/* pos 01fe: 237 */ 0xF4 /* 't' -> */, -/* pos 01ff: 238 */ 0xBA /* ':' -> */, -/* pos 0200: 239 */ 0x00, 0x27 /* - terminal marker 39 - */, -/* pos 0202: 240 */ 0xF2 /* 'r' -> */, -/* pos 0203: 241 */ 0xE5 /* 'e' -> */, -/* pos 0204: 242 */ 0xF3 /* 's' -> */, -/* pos 0205: 243 */ 0xBA /* ':' -> */, -/* pos 0206: 244 */ 0x00, 0x28 /* - terminal marker 40 - */, -/* pos 0208: 245 */ 0xF2 /* 'r' -> */, -/* pos 0209: 246 */ 0xEF /* 'o' -> */, -/* pos 020a: 247 */ 0xED /* 'm' -> */, -/* pos 020b: 248 */ 0xBA /* ':' -> */, -/* pos 020c: 249 */ 0x00, 0x29 /* - terminal marker 41 - */, -/* pos 020e: 250 */ 0xF4 /* 't' -> */, -/* pos 020f: 251 */ 0xE3 /* 'c' -> */, -/* pos 0210: 252 */ 0xE8 /* 'h' -> */, -/* pos 0211: 253 */ 0xBA /* ':' -> */, -/* pos 0212: 254 */ 0x00, 0x2A /* - terminal marker 42 - */, -/* pos 0214: 255 */ 0xE1 /* 'a' -> */, -/* pos 0215: 256 */ 0xEE /* 'n' -> */, -/* pos 0216: 257 */ 0xE7 /* 'g' -> */, -/* pos 0217: 258 */ 0xE5 /* 'e' -> */, -/* pos 0218: 259 */ 0xBA /* ':' -> */, -/* pos 0219: 260 */ 0x00, 0x2B /* - terminal marker 43 - */, -/* pos 021b: 261 */ 0xEE /* 'n' -> */, -/* pos 021c: 262 */ 0xED /* 'm' -> */, -/* pos 021d: 263 */ 0xEF /* 'o' -> */, -/* pos 021e: 264 */ 0xE4 /* 'd' -> */, -/* pos 021f: 265 */ 0xE9 /* 'i' -> */, -/* pos 0220: 266 */ 0xE6 /* 'f' -> */, -/* pos 0221: 267 */ 0xE9 /* 'i' -> */, -/* pos 0222: 268 */ 0xE5 /* 'e' -> */, -/* pos 0223: 269 */ 0xE4 /* 'd' -> */, -/* pos 0224: 270 */ 0xAD /* '-' -> */, -/* pos 0225: 271 */ 0xF3 /* 's' -> */, -/* pos 0226: 272 */ 0xE9 /* 'i' -> */, -/* pos 0227: 273 */ 0xEE /* 'n' -> */, -/* pos 0228: 274 */ 0xE3 /* 'c' -> */, -/* pos 0229: 275 */ 0xE5 /* 'e' -> */, -/* pos 022a: 276 */ 0xBA /* ':' -> */, -/* pos 022b: 277 */ 0x00, 0x2C /* - terminal marker 44 - */, -/* pos 022d: 278 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0237 state 279) */, - 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x0245 state 292) */, - 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x024A state 296) */, - 0x08, /* fail */ -/* pos 0237: 279 */ 0xF3 /* 's' -> */, -/* pos 0238: 280 */ 0xF4 /* 't' -> */, -/* pos 0239: 281 */ 0xAD /* '-' -> */, -/* pos 023a: 282 */ 0xED /* 'm' -> */, -/* pos 023b: 283 */ 0xEF /* 'o' -> */, -/* pos 023c: 284 */ 0xE4 /* 'd' -> */, -/* pos 023d: 285 */ 0xE9 /* 'i' -> */, -/* pos 023e: 286 */ 0xE6 /* 'f' -> */, -/* pos 023f: 287 */ 0xE9 /* 'i' -> */, -/* pos 0240: 288 */ 0xE5 /* 'e' -> */, -/* pos 0241: 289 */ 0xE4 /* 'd' -> */, -/* pos 0242: 290 */ 0xBA /* ':' -> */, -/* pos 0243: 291 */ 0x00, 0x2D /* - terminal marker 45 - */, -/* pos 0245: 292 */ 0xEE /* 'n' -> */, -/* pos 0246: 293 */ 0xEB /* 'k' -> */, -/* pos 0247: 294 */ 0xBA /* ':' -> */, -/* pos 0248: 295 */ 0x00, 0x2E /* - terminal marker 46 - */, -/* pos 024a: 296 */ 0xE3 /* 'c' -> */, -/* pos 024b: 297 */ 0xE1 /* 'a' -> */, -/* pos 024c: 298 */ 0xF4 /* 't' -> */, -/* pos 024d: 299 */ 0xE9 /* 'i' -> */, -/* pos 024e: 300 */ 0xEF /* 'o' -> */, -/* pos 024f: 301 */ 0xEE /* 'n' -> */, -/* pos 0250: 302 */ 0xBA /* ':' -> */, -/* pos 0251: 303 */ 0x00, 0x2F /* - terminal marker 47 - */, -/* pos 0253: 304 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x025D state 305) */, - 0x74 /* 't' */, 0x14, 0x00 /* (to 0x026A state 311) */, - 0x70 /* 'p' */, 0x6F, 0x01 /* (to 0x03C8 state 578) */, - 0x08, /* fail */ -/* pos 025d: 305 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0264 state 306) */, - 0x65 /* 'e' */, 0xB1, 0x00 /* (to 0x0311 state 430) */, - 0x08, /* fail */ -/* pos 0264: 306 */ 0xE5 /* 'e' -> */, -/* pos 0265: 307 */ 0xF3 /* 's' -> */, -/* pos 0266: 308 */ 0xE8 /* 'h' -> */, -/* pos 0267: 309 */ 0xBA /* ':' -> */, -/* pos 0268: 310 */ 0x00, 0x33 /* - terminal marker 51 - */, -/* pos 026a: 311 */ 0xF2 /* 'r' -> */, -/* pos 026b: 312 */ 0xF9 /* 'y' -> */, -/* pos 026c: 313 */ 0xAD /* '-' -> */, -/* pos 026d: 314 */ 0xE1 /* 'a' -> */, -/* pos 026e: 315 */ 0xE6 /* 'f' -> */, -/* pos 026f: 316 */ 0xF4 /* 't' -> */, -/* pos 0270: 317 */ 0xE5 /* 'e' -> */, -/* pos 0271: 318 */ 0xF2 /* 'r' -> */, -/* pos 0272: 319 */ 0xBA /* ':' -> */, -/* pos 0273: 320 */ 0x00, 0x34 /* - terminal marker 52 - */, -/* pos 0275: 321 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x027C state 322) */, - 0x74 /* 't' */, 0xED, 0x00 /* (to 0x0365 state 496) */, - 0x08, /* fail */ -/* pos 027c: 322 */ 0x72 /* 'r' */, 0x0A, 0x00 /* (to 0x0286 state 323) */, - 0x74 /* 't' */, 0x0D, 0x00 /* (to 0x028C state 328) */, - 0x63 /* 'c' */, 0x52, 0x01 /* (to 0x03D4 state 589) */, - 0x08, /* fail */ -/* pos 0286: 323 */ 0xF6 /* 'v' -> */, -/* pos 0287: 324 */ 0xE5 /* 'e' -> */, -/* pos 0288: 325 */ 0xF2 /* 'r' -> */, -/* pos 0289: 326 */ 0xBA /* ':' -> */, -/* pos 028a: 327 */ 0x00, 0x35 /* - terminal marker 53 - */, -/* pos 028c: 328 */ 0xAD /* '-' -> */, -/* pos 028d: 329 */ 0xE3 /* 'c' -> */, -/* pos 028e: 330 */ 0xEF /* 'o' -> */, -/* pos 028f: 331 */ 0xEF /* 'o' -> */, -/* pos 0290: 332 */ 0xEB /* 'k' -> */, -/* pos 0291: 333 */ 0xE9 /* 'i' -> */, -/* pos 0292: 334 */ 0xE5 /* 'e' -> */, -/* pos 0293: 335 */ 0xBA /* ':' -> */, -/* pos 0294: 336 */ 0x00, 0x36 /* - terminal marker 54 - */, -/* pos 0296: 337 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x029D state 338) */, - 0x65 /* 'e' */, 0x2C, 0x01 /* (to 0x03C5 state 576) */, - 0x08, /* fail */ -/* pos 029d: 338 */ 0xE1 /* 'a' -> */, -/* pos 029e: 339 */ 0xEE /* 'n' -> */, -/* pos 029f: 340 */ 0xF3 /* 's' -> */, -/* pos 02a0: 341 */ 0xE6 /* 'f' -> */, -/* pos 02a1: 342 */ 0xE5 /* 'e' -> */, -/* pos 02a2: 343 */ 0xF2 /* 'r' -> */, -/* pos 02a3: 344 */ 0xAD /* '-' -> */, -/* pos 02a4: 345 */ 0xE5 /* 'e' -> */, -/* pos 02a5: 346 */ 0xEE /* 'n' -> */, -/* pos 02a6: 347 */ 0xE3 /* 'c' -> */, -/* pos 02a7: 348 */ 0xEF /* 'o' -> */, -/* pos 02a8: 349 */ 0xE4 /* 'd' -> */, -/* pos 02a9: 350 */ 0xE9 /* 'i' -> */, -/* pos 02aa: 351 */ 0xEE /* 'n' -> */, -/* pos 02ab: 352 */ 0xE7 /* 'g' -> */, -/* pos 02ac: 353 */ 0xBA /* ':' -> */, -/* pos 02ad: 354 */ 0x00, 0x38 /* - terminal marker 56 - */, -/* pos 02af: 355 */ 0xE9 /* 'i' -> */, -/* pos 02b0: 356 */ 0xAD /* '-' -> */, -/* pos 02b1: 357 */ 0xE1 /* 'a' -> */, -/* pos 02b2: 358 */ 0xF2 /* 'r' -> */, -/* pos 02b3: 359 */ 0xE7 /* 'g' -> */, -/* pos 02b4: 360 */ 0xF3 /* 's' -> */, -/* pos 02b5: 361 */ 0x00, 0x3D /* - terminal marker 61 - */, -/* pos 02b7: 362 */ 0xA0 /* ' ' -> */, -/* pos 02b8: 363 */ 0x00, 0x3E /* - terminal marker 62 - */, -/* pos 02ba: 364 */ 0xAD /* '-' -> */, -/* pos 02bb: 365 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x02C5 state 366) */, - 0x61 /* 'a' */, 0x1D, 0x00 /* (to 0x02DB state 385) */, - 0x72 /* 'r' */, 0xFB, 0x00 /* (to 0x03BC state 568) */, - 0x08, /* fail */ -/* pos 02c5: 366 */ 0xEF /* 'o' -> */, -/* pos 02c6: 367 */ 0xF2 /* 'r' -> */, -/* pos 02c7: 368 */ 0xF7 /* 'w' -> */, -/* pos 02c8: 369 */ 0xE1 /* 'a' -> */, -/* pos 02c9: 370 */ 0xF2 /* 'r' -> */, -/* pos 02ca: 371 */ 0xE4 /* 'd' -> */, -/* pos 02cb: 372 */ 0xE5 /* 'e' -> */, -/* pos 02cc: 373 */ 0xE4 /* 'd' -> */, -/* pos 02cd: 374 */ 0xAD /* '-' -> */, -/* pos 02ce: 375 */ 0xE6 /* 'f' -> */, -/* pos 02cf: 376 */ 0xEF /* 'o' -> */, -/* pos 02d0: 377 */ 0xF2 /* 'r' -> */, -/* pos 02d1: 378 */ 0xBA /* ':' -> */, -/* pos 02d2: 379 */ 0x00, 0x3F /* - terminal marker 63 - */, -/* pos 02d4: 380 */ 0x00, 0x40 /* - terminal marker 64 - */, -/* pos 02d6: 381 */ 0xE1 /* 'a' -> */, -/* pos 02d7: 382 */ 0xE4 /* 'd' -> */, -/* pos 02d8: 383 */ 0xA0 /* ' ' -> */, -/* pos 02d9: 384 */ 0x00, 0x41 /* - terminal marker 65 - */, -/* pos 02db: 385 */ 0xF5 /* 'u' -> */, -/* pos 02dc: 386 */ 0xF4 /* 't' -> */, -/* pos 02dd: 387 */ 0xE8 /* 'h' -> */, -/* pos 02de: 388 */ 0xAD /* '-' -> */, -/* pos 02df: 389 */ 0xF4 /* 't' -> */, -/* pos 02e0: 390 */ 0xEF /* 'o' -> */, -/* pos 02e1: 391 */ 0xEB /* 'k' -> */, -/* pos 02e2: 392 */ 0xE5 /* 'e' -> */, -/* pos 02e3: 393 */ 0xEE /* 'n' -> */, -/* pos 02e4: 394 */ 0xBA /* ':' -> */, -/* pos 02e5: 395 */ 0x00, 0x45 /* - terminal marker 69 - */, -/* pos 02e7: 396 */ 0xF4 /* 't' -> */, -/* pos 02e8: 397 */ 0xE9 /* 'i' -> */, -/* pos 02e9: 398 */ 0xEF /* 'o' -> */, -/* pos 02ea: 399 */ 0xEE /* 'n' -> */, -/* pos 02eb: 400 */ 0xF3 /* 's' -> */, -/* pos 02ec: 401 */ 0xA0 /* ' ' -> */, -/* pos 02ed: 402 */ 0x00, 0x02 /* - terminal marker 2 - */, -/* pos 02ef: 403 */ 0xF3 /* 's' -> */, -/* pos 02f0: 404 */ 0xAD /* '-' -> */, -/* pos 02f1: 405 */ 0xE3 /* 'c' -> */, -/* pos 02f2: 406 */ 0xEF /* 'o' -> */, -/* pos 02f3: 407 */ 0xEE /* 'n' -> */, -/* pos 02f4: 408 */ 0xF4 /* 't' -> */, -/* pos 02f5: 409 */ 0xF2 /* 'r' -> */, -/* pos 02f6: 410 */ 0xEF /* 'o' -> */, -/* pos 02f7: 411 */ 0xEC /* 'l' -> */, -/* pos 02f8: 412 */ 0xAD /* '-' -> */, -/* pos 02f9: 413 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0300 state 414) */, - 0x61 /* 'a' */, 0x24, 0x00 /* (to 0x0320 state 443) */, - 0x08, /* fail */ -/* pos 0300: 414 */ 0xE5 /* 'e' -> */, -/* pos 0301: 415 */ 0xF1 /* 'q' -> */, -/* pos 0302: 416 */ 0xF5 /* 'u' -> */, -/* pos 0303: 417 */ 0xE5 /* 'e' -> */, -/* pos 0304: 418 */ 0xF3 /* 's' -> */, -/* pos 0305: 419 */ 0xF4 /* 't' -> */, -/* pos 0306: 420 */ 0xAD /* '-' -> */, -/* pos 0307: 421 */ 0xE8 /* 'h' -> */, -/* pos 0308: 422 */ 0xE5 /* 'e' -> */, -/* pos 0309: 423 */ 0xE1 /* 'a' -> */, -/* pos 030a: 424 */ 0xE4 /* 'd' -> */, -/* pos 030b: 425 */ 0xE5 /* 'e' -> */, -/* pos 030c: 426 */ 0xF2 /* 'r' -> */, -/* pos 030d: 427 */ 0xF3 /* 's' -> */, -/* pos 030e: 428 */ 0xBA /* ':' -> */, -/* pos 030f: 429 */ 0x00, 0x11 /* - terminal marker 17 - */, -/* pos 0311: 430 */ 0xF2 /* 'r' -> */, -/* pos 0312: 431 */ 0xE5 /* 'e' -> */, -/* pos 0313: 432 */ 0xF2 /* 'r' -> */, -/* pos 0314: 433 */ 0xBA /* ':' -> */, -/* pos 0315: 434 */ 0x00, 0x16 /* - terminal marker 22 - */, -/* pos 0317: 435 */ 0xE8 /* 'h' -> */, -/* pos 0318: 436 */ 0xE1 /* 'a' -> */, -/* pos 0319: 437 */ 0xF2 /* 'r' -> */, -/* pos 031a: 438 */ 0xF3 /* 's' -> */, -/* pos 031b: 439 */ 0xE5 /* 'e' -> */, -/* pos 031c: 440 */ 0xF4 /* 't' -> */, -/* pos 031d: 441 */ 0xBA /* ':' -> */, -/* pos 031e: 442 */ 0x00, 0x1C /* - terminal marker 28 - */, -/* pos 0320: 443 */ 0xEC /* 'l' -> */, -/* pos 0321: 444 */ 0xEC /* 'l' -> */, -/* pos 0322: 445 */ 0xEF /* 'o' -> */, -/* pos 0323: 446 */ 0xF7 /* 'w' -> */, -/* pos 0324: 447 */ 0xAD /* '-' -> */, -/* pos 0325: 448 */ 0xEF /* 'o' -> */, -/* pos 0326: 449 */ 0xF2 /* 'r' -> */, -/* pos 0327: 450 */ 0xE9 /* 'i' -> */, -/* pos 0328: 451 */ 0xE7 /* 'g' -> */, -/* pos 0329: 452 */ 0xE9 /* 'i' -> */, -/* pos 032a: 453 */ 0xEE /* 'n' -> */, -/* pos 032b: 454 */ 0xBA /* ':' -> */, -/* pos 032c: 455 */ 0x00, 0x1E /* - terminal marker 30 - */, -/* pos 032e: 456 */ 0xE1 /* 'a' -> */, -/* pos 032f: 457 */ 0xF8 /* 'x' -> */, -/* pos 0330: 458 */ 0xAD /* '-' -> */, -/* pos 0331: 459 */ 0xE6 /* 'f' -> */, -/* pos 0332: 460 */ 0xEF /* 'o' -> */, -/* pos 0333: 461 */ 0xF2 /* 'r' -> */, -/* pos 0334: 462 */ 0xF7 /* 'w' -> */, -/* pos 0335: 463 */ 0xE1 /* 'a' -> */, -/* pos 0336: 464 */ 0xF2 /* 'r' -> */, -/* pos 0337: 465 */ 0xE4 /* 'd' -> */, -/* pos 0338: 466 */ 0xF3 /* 's' -> */, -/* pos 0339: 467 */ 0xBA /* ':' -> */, -/* pos 033a: 468 */ 0x00, 0x30 /* - terminal marker 48 - */, -/* pos 033c: 469 */ 0xF8 /* 'x' -> */, -/* pos 033d: 470 */ 0xF9 /* 'y' -> */, -/* pos 033e: 471 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0345 state 472) */, - 0x20 /* ' ' */, 0x79, 0x00 /* (to 0x03BA state 567) */, - 0x08, /* fail */ -/* pos 0345: 472 */ 0xE1 /* 'a' -> */, -/* pos 0346: 473 */ 0xF5 /* 'u' -> */, -/* pos 0347: 474 */ 0xF4 /* 't' -> */, -/* pos 0348: 475 */ 0xE8 /* 'h' -> */, -/* pos 0349: 476 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0350 state 477) */, - 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x035A state 486) */, - 0x08, /* fail */ -/* pos 0350: 477 */ 0xEE /* 'n' -> */, -/* pos 0351: 478 */ 0xF4 /* 't' -> */, -/* pos 0352: 479 */ 0xE9 /* 'i' -> */, -/* pos 0353: 480 */ 0xE3 /* 'c' -> */, -/* pos 0354: 481 */ 0xE1 /* 'a' -> */, -/* pos 0355: 482 */ 0xF4 /* 't' -> */, -/* pos 0356: 483 */ 0xE5 /* 'e' -> */, -/* pos 0357: 484 */ 0xBA /* ':' -> */, -/* pos 0358: 485 */ 0x00, 0x31 /* - terminal marker 49 - */, -/* pos 035a: 486 */ 0xF2 /* 'r' -> */, -/* pos 035b: 487 */ 0xE9 /* 'i' -> */, -/* pos 035c: 488 */ 0xFA /* 'z' -> */, -/* pos 035d: 489 */ 0xE1 /* 'a' -> */, -/* pos 035e: 490 */ 0xF4 /* 't' -> */, -/* pos 035f: 491 */ 0xE9 /* 'i' -> */, -/* pos 0360: 492 */ 0xEF /* 'o' -> */, -/* pos 0361: 493 */ 0xEE /* 'n' -> */, -/* pos 0362: 494 */ 0xBA /* ':' -> */, -/* pos 0363: 495 */ 0x00, 0x32 /* - terminal marker 50 - */, -/* pos 0365: 496 */ 0xF2 /* 'r' -> */, -/* pos 0366: 497 */ 0xE9 /* 'i' -> */, -/* pos 0367: 498 */ 0xE3 /* 'c' -> */, -/* pos 0368: 499 */ 0xF4 /* 't' -> */, -/* pos 0369: 500 */ 0xAD /* '-' -> */, -/* pos 036a: 501 */ 0xF4 /* 't' -> */, -/* pos 036b: 502 */ 0xF2 /* 'r' -> */, -/* pos 036c: 503 */ 0xE1 /* 'a' -> */, -/* pos 036d: 504 */ 0xEE /* 'n' -> */, -/* pos 036e: 505 */ 0xF3 /* 's' -> */, -/* pos 036f: 506 */ 0xF0 /* 'p' -> */, -/* pos 0370: 507 */ 0xEF /* 'o' -> */, -/* pos 0371: 508 */ 0xF2 /* 'r' -> */, -/* pos 0372: 509 */ 0xF4 /* 't' -> */, -/* pos 0373: 510 */ 0xAD /* '-' -> */, -/* pos 0374: 511 */ 0xF3 /* 's' -> */, -/* pos 0375: 512 */ 0xE5 /* 'e' -> */, -/* pos 0376: 513 */ 0xE3 /* 'c' -> */, -/* pos 0377: 514 */ 0xF5 /* 'u' -> */, -/* pos 0378: 515 */ 0xF2 /* 'r' -> */, -/* pos 0379: 516 */ 0xE9 /* 'i' -> */, -/* pos 037a: 517 */ 0xF4 /* 't' -> */, -/* pos 037b: 518 */ 0xF9 /* 'y' -> */, -/* pos 037c: 519 */ 0xBA /* ':' -> */, -/* pos 037d: 520 */ 0x00, 0x37 /* - terminal marker 55 - */, -/* pos 037f: 521 */ 0xE5 /* 'e' -> */, -/* pos 0380: 522 */ 0xF2 /* 'r' -> */, -/* pos 0381: 523 */ 0xAD /* '-' -> */, -/* pos 0382: 524 */ 0xE1 /* 'a' -> */, -/* pos 0383: 525 */ 0xE7 /* 'g' -> */, -/* pos 0384: 526 */ 0xE5 /* 'e' -> */, -/* pos 0385: 527 */ 0xEE /* 'n' -> */, -/* pos 0386: 528 */ 0xF4 /* 't' -> */, -/* pos 0387: 529 */ 0xBA /* ':' -> */, -/* pos 0388: 530 */ 0x00, 0x39 /* - terminal marker 57 - */, -/* pos 038a: 531 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0391 state 532) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0396 state 536) */, - 0x08, /* fail */ -/* pos 0391: 532 */ 0xF2 /* 'r' -> */, -/* pos 0392: 533 */ 0xF9 /* 'y' -> */, -/* pos 0393: 534 */ 0xBA /* ':' -> */, -/* pos 0394: 535 */ 0x00, 0x3A /* - terminal marker 58 - */, -/* pos 0396: 536 */ 0xE1 /* 'a' -> */, -/* pos 0397: 537 */ 0xBA /* ':' -> */, -/* pos 0398: 538 */ 0x00, 0x3B /* - terminal marker 59 - */, -/* pos 039a: 539 */ 0xF7 /* 'w' -> */, -/* pos 039b: 540 */ 0xF7 /* 'w' -> */, -/* pos 039c: 541 */ 0xAD /* '-' -> */, -/* pos 039d: 542 */ 0xE1 /* 'a' -> */, -/* pos 039e: 543 */ 0xF5 /* 'u' -> */, -/* pos 039f: 544 */ 0xF4 /* 't' -> */, -/* pos 03a0: 545 */ 0xE8 /* 'h' -> */, -/* pos 03a1: 546 */ 0xE5 /* 'e' -> */, -/* pos 03a2: 547 */ 0xEE /* 'n' -> */, -/* pos 03a3: 548 */ 0xF4 /* 't' -> */, -/* pos 03a4: 549 */ 0xE9 /* 'i' -> */, -/* pos 03a5: 550 */ 0xE3 /* 'c' -> */, -/* pos 03a6: 551 */ 0xE1 /* 'a' -> */, -/* pos 03a7: 552 */ 0xF4 /* 't' -> */, -/* pos 03a8: 553 */ 0xE5 /* 'e' -> */, -/* pos 03a9: 554 */ 0xBA /* ':' -> */, -/* pos 03aa: 555 */ 0x00, 0x3C /* - terminal marker 60 - */, -/* pos 03ac: 556 */ 0xF4 /* 't' -> */, -/* pos 03ad: 557 */ 0xE3 /* 'c' -> */, -/* pos 03ae: 558 */ 0xE8 /* 'h' -> */, -/* pos 03af: 559 */ 0x00, 0x43 /* - terminal marker 67 - */, -/* pos 03b1: 560 */ 0xF4 /* 't' -> */, -/* pos 03b2: 561 */ 0x00, 0x44 /* - terminal marker 68 - */, -/* pos 03b4: 562 */ 0xEC /* 'l' -> */, -/* pos 03b5: 563 */ 0xE5 /* 'e' -> */, -/* pos 03b6: 564 */ 0xF4 /* 't' -> */, -/* pos 03b7: 565 */ 0xE5 /* 'e' -> */, -/* pos 03b8: 566 */ 0x00, 0x45 /* - terminal marker 69 - */, -/* pos 03ba: 567 */ 0x00, 0x47 /* - terminal marker 71 - */, -/* pos 03bc: 568 */ 0xE5 /* 'e' -> */, -/* pos 03bd: 569 */ 0xE1 /* 'a' -> */, -/* pos 03be: 570 */ 0xEC /* 'l' -> */, -/* pos 03bf: 571 */ 0xAD /* '-' -> */, -/* pos 03c0: 572 */ 0xE9 /* 'i' -> */, -/* pos 03c1: 573 */ 0xF0 /* 'p' -> */, -/* pos 03c2: 574 */ 0xBA /* ':' -> */, -/* pos 03c3: 575 */ 0x00, 0x48 /* - terminal marker 72 - */, -/* pos 03c5: 576 */ 0xBA /* ':' -> */, -/* pos 03c6: 577 */ 0x00, 0x42 /* - terminal marker 66 - */, -/* pos 03c8: 578 */ 0xEC /* 'l' -> */, -/* pos 03c9: 579 */ 0xE1 /* 'a' -> */, -/* pos 03ca: 580 */ 0xF9 /* 'y' -> */, -/* pos 03cb: 581 */ 0xAD /* '-' -> */, -/* pos 03cc: 582 */ 0xEE /* 'n' -> */, -/* pos 03cd: 583 */ 0xEF /* 'o' -> */, -/* pos 03ce: 584 */ 0xEE /* 'n' -> */, -/* pos 03cf: 585 */ 0xE3 /* 'c' -> */, -/* pos 03d0: 586 */ 0xE5 /* 'e' -> */, -/* pos 03d1: 587 */ 0xBA /* ':' -> */, -/* pos 03d2: 588 */ 0x00, 0x43 /* - terminal marker 67 - */, -/* pos 03d4: 589 */ 0xAD /* '-' -> */, -/* pos 03d5: 590 */ 0xF7 /* 'w' -> */, -/* pos 03d6: 591 */ 0xE5 /* 'e' -> */, -/* pos 03d7: 592 */ 0xE2 /* 'b' -> */, -/* pos 03d8: 593 */ 0xF3 /* 's' -> */, -/* pos 03d9: 594 */ 0xEF /* 'o' -> */, -/* pos 03da: 595 */ 0xE3 /* 'c' -> */, -/* pos 03db: 596 */ 0xEB /* 'k' -> */, -/* pos 03dc: 597 */ 0xE5 /* 'e' -> */, -/* pos 03dd: 598 */ 0xF4 /* 't' -> */, -/* pos 03de: 599 */ 0xAD /* '-' -> */, -/* pos 03df: 600 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x03F8 state 601) */, - 0x65 /* 'e' */, 0x1D, 0x00 /* (to 0x03FF state 607) */, - 0x6B /* 'k' */, 0x26, 0x00 /* (to 0x040B state 618) */, - 0x70 /* 'p' */, 0x35, 0x00 /* (to 0x041D state 625) */, - 0x61 /* 'a' */, 0x3C, 0x00 /* (to 0x0427 state 634) */, - 0x6E /* 'n' */, 0x41, 0x00 /* (to 0x042F state 641) */, - 0x76 /* 'v' */, 0x47, 0x00 /* (to 0x0438 state 648) */, - 0x6F /* 'o' */, 0x4D, 0x00 /* (to 0x0441 state 656) */, - 0x08, /* fail */ -/* pos 03f8: 601 */ 0xF2 /* 'r' -> */, -/* pos 03f9: 602 */ 0xE1 /* 'a' -> */, -/* pos 03fa: 603 */ 0xE6 /* 'f' -> */, -/* pos 03fb: 604 */ 0xF4 /* 't' -> */, -/* pos 03fc: 605 */ 0xBA /* ':' -> */, -/* pos 03fd: 606 */ 0x00, 0x07 /* - terminal marker 7 - */, -/* pos 03ff: 607 */ 0xF8 /* 'x' -> */, -/* pos 0400: 608 */ 0xF4 /* 't' -> */, -/* pos 0401: 609 */ 0xE5 /* 'e' -> */, -/* pos 0402: 610 */ 0xEE /* 'n' -> */, -/* pos 0403: 611 */ 0xF3 /* 's' -> */, -/* pos 0404: 612 */ 0xE9 /* 'i' -> */, -/* pos 0405: 613 */ 0xEF /* 'o' -> */, -/* pos 0406: 614 */ 0xEE /* 'n' -> */, -/* pos 0407: 615 */ 0xF3 /* 's' -> */, -/* pos 0408: 616 */ 0xBA /* ':' -> */, -/* pos 0409: 617 */ 0x00, 0x09 /* - terminal marker 9 - */, -/* pos 040b: 618 */ 0xE5 /* 'e' -> */, -/* pos 040c: 619 */ 0xF9 /* 'y' -> */, -/* pos 040d: 620 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0417 state 621) */, - 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x041A state 623) */, - 0x3A /* ':' */, 0x23, 0x00 /* (to 0x0436 state 647) */, - 0x08, /* fail */ -/* pos 0417: 621 */ 0xBA /* ':' -> */, -/* pos 0418: 622 */ 0x00, 0x0A /* - terminal marker 10 - */, -/* pos 041a: 623 */ 0xBA /* ':' -> */, -/* pos 041b: 624 */ 0x00, 0x0B /* - terminal marker 11 - */, -/* pos 041d: 625 */ 0xF2 /* 'r' -> */, -/* pos 041e: 626 */ 0xEF /* 'o' -> */, -/* pos 041f: 627 */ 0xF4 /* 't' -> */, -/* pos 0420: 628 */ 0xEF /* 'o' -> */, -/* pos 0421: 629 */ 0xE3 /* 'c' -> */, -/* pos 0422: 630 */ 0xEF /* 'o' -> */, -/* pos 0423: 631 */ 0xEC /* 'l' -> */, -/* pos 0424: 632 */ 0xBA /* ':' -> */, -/* pos 0425: 633 */ 0x00, 0x0C /* - terminal marker 12 - */, -/* pos 0427: 634 */ 0xE3 /* 'c' -> */, -/* pos 0428: 635 */ 0xE3 /* 'c' -> */, -/* pos 0429: 636 */ 0xE5 /* 'e' -> */, -/* pos 042a: 637 */ 0xF0 /* 'p' -> */, -/* pos 042b: 638 */ 0xF4 /* 't' -> */, -/* pos 042c: 639 */ 0xBA /* ':' -> */, -/* pos 042d: 640 */ 0x00, 0x0D /* - terminal marker 13 - */, -/* pos 042f: 641 */ 0xEF /* 'o' -> */, -/* pos 0430: 642 */ 0xEE /* 'n' -> */, -/* pos 0431: 643 */ 0xE3 /* 'c' -> */, -/* pos 0432: 644 */ 0xE5 /* 'e' -> */, -/* pos 0433: 645 */ 0xBA /* ':' -> */, -/* pos 0434: 646 */ 0x00, 0x0E /* - terminal marker 14 - */, -/* pos 0436: 647 */ 0x00, 0x1F /* - terminal marker 31 - */, -/* pos 0438: 648 */ 0xE5 /* 'e' -> */, -/* pos 0439: 649 */ 0xF2 /* 'r' -> */, -/* pos 043a: 650 */ 0xF3 /* 's' -> */, -/* pos 043b: 651 */ 0xE9 /* 'i' -> */, -/* pos 043c: 652 */ 0xEF /* 'o' -> */, -/* pos 043d: 653 */ 0xEE /* 'n' -> */, -/* pos 043e: 654 */ 0xBA /* ':' -> */, -/* pos 043f: 655 */ 0x00, 0x20 /* - terminal marker 32 - */, -/* pos 0441: 656 */ 0xF2 /* 'r' -> */, -/* pos 0442: 657 */ 0xE9 /* 'i' -> */, -/* pos 0443: 658 */ 0xE7 /* 'g' -> */, -/* pos 0444: 659 */ 0xE9 /* 'i' -> */, -/* pos 0445: 660 */ 0xEE /* 'n' -> */, -/* pos 0446: 661 */ 0xBA /* ':' -> */, -/* pos 0447: 662 */ 0x00, 0x21 /* - terminal marker 33 - */, -/* pos 0449: 663 */ 0xAD /* '-' -> */, -/* pos 044a: 664 */ 0xF3 /* 's' -> */, -/* pos 044b: 665 */ 0xE5 /* 'e' -> */, -/* pos 044c: 666 */ 0xF4 /* 't' -> */, -/* pos 044d: 667 */ 0xF4 /* 't' -> */, -/* pos 044e: 668 */ 0xE9 /* 'i' -> */, -/* pos 044f: 669 */ 0xEE /* 'n' -> */, -/* pos 0450: 670 */ 0xE7 /* 'g' -> */, -/* pos 0451: 671 */ 0xF3 /* 's' -> */, -/* pos 0452: 672 */ 0xBA /* ':' -> */, -/* pos 0453: 673 */ 0x00, 0x08 /* - terminal marker 8 - */, -/* pos 0455: 674 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0462 state 675) */, - 0x6D /* 'm' */, 0x14, 0x00 /* (to 0x046C state 684) */, - 0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0473 state 690) */, - 0x73 /* 's' */, 0x20, 0x00 /* (to 0x047E state 694) */, - 0x08, /* fail */ -/* pos 0462: 675 */ 0xF5 /* 'u' -> */, -/* pos 0463: 676 */ 0xF4 /* 't' -> */, -/* pos 0464: 677 */ 0xE8 /* 'h' -> */, -/* pos 0465: 678 */ 0xEF /* 'o' -> */, -/* pos 0466: 679 */ 0xF2 /* 'r' -> */, -/* pos 0467: 680 */ 0xE9 /* 'i' -> */, -/* pos 0468: 681 */ 0xF4 /* 't' -> */, -/* pos 0469: 682 */ 0xF9 /* 'y' -> */, -/* pos 046a: 683 */ 0x00, 0x17 /* - terminal marker 23 - */, -/* pos 046c: 684 */ 0xE5 /* 'e' -> */, -/* pos 046d: 685 */ 0xF4 /* 't' -> */, -/* pos 046e: 686 */ 0xE8 /* 'h' -> */, -/* pos 046f: 687 */ 0xEF /* 'o' -> */, -/* pos 0470: 688 */ 0xE4 /* 'd' -> */, -/* pos 0471: 689 */ 0x00, 0x18 /* - terminal marker 24 - */, -/* pos 0473: 690 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x047A state 691) */, - 0x72 /* 'r' */, 0x1B, 0x00 /* (to 0x0491 state 705) */, - 0x08, /* fail */ -/* pos 047a: 691 */ 0xF4 /* 't' -> */, -/* pos 047b: 692 */ 0xE8 /* 'h' -> */, -/* pos 047c: 693 */ 0x00, 0x19 /* - terminal marker 25 - */, -/* pos 047e: 694 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x0485 state 695) */, - 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x048B state 700) */, - 0x08, /* fail */ -/* pos 0485: 695 */ 0xE8 /* 'h' -> */, -/* pos 0486: 696 */ 0xE5 /* 'e' -> */, -/* pos 0487: 697 */ 0xED /* 'm' -> */, -/* pos 0488: 698 */ 0xE5 /* 'e' -> */, -/* pos 0489: 699 */ 0x00, 0x1A /* - terminal marker 26 - */, -/* pos 048b: 700 */ 0xE1 /* 'a' -> */, -/* pos 048c: 701 */ 0xF4 /* 't' -> */, -/* pos 048d: 702 */ 0xF5 /* 'u' -> */, -/* pos 048e: 703 */ 0xF3 /* 's' -> */, -/* pos 048f: 704 */ 0x00, 0x1B /* - terminal marker 27 - */, -/* pos 0491: 705 */ 0xEF /* 'o' -> */, -/* pos 0492: 706 */ 0xF4 /* 't' -> */, -/* pos 0493: 707 */ 0xEF /* 'o' -> */, -/* pos 0494: 708 */ 0xE3 /* 'c' -> */, -/* pos 0495: 709 */ 0xEF /* 'o' -> */, -/* pos 0496: 710 */ 0xEC /* 'l' -> */, -/* pos 0497: 711 */ 0x00, 0x44 /* - terminal marker 68 - */, -/* total size 1177 bytes */ -#endif - -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) - /* 0: 0: get */ - /* 1: 1: post */ - /* 2: 2: options */ - /* 3: 3: host: */ - /* 4: 4: connection: */ - /* 5: 5: upgrade: */ - /* 6: 6: origin: */ - /* 7: 8: - */ - /* 8: 15: http/1.1 */ - /* 9: 16: http2-settings: */ - /* 10: 17: accept: */ - /* 11: 18: access-control-request-headers: */ - /* 12: 19: if-modified-since: */ - /* 13: 20: if-none-match: */ - /* 14: 21: accept-encoding: */ - /* 15: 22: accept-language: */ - /* 16: 23: pragma: */ - /* 17: 24: cache-control: */ - /* 18: 25: authorization: */ - /* 19: 26: cookie: */ - /* 20: 27: content-length: */ - /* 21: 28: content-type: */ - /* 22: 29: date: */ - /* 23: 30: range: */ - /* 24: 31: referer: */ - /* 25: 35: :authority */ - /* 26: 36: :method */ - /* 27: 37: :path */ - /* 28: 38: :scheme */ - /* 29: 39: :status */ - /* 30: 40: accept-charset: */ - /* 31: 41: accept-ranges: */ - /* 32: 42: access-control-allow-origin: */ - /* 33: 43: age: */ - /* 34: 44: allow: */ - /* 35: 45: content-disposition: */ - /* 36: 46: content-encoding: */ - /* 37: 47: content-language: */ - /* 38: 48: content-location: */ - /* 39: 49: content-range: */ - /* 40: 50: etag: */ - /* 41: 51: expect: */ - /* 42: 52: expires: */ - /* 43: 53: from: */ - /* 44: 54: if-match: */ - /* 45: 55: if-range: */ - /* 46: 56: if-unmodified-since: */ - /* 47: 57: last-modified: */ - /* 48: 58: link: */ - /* 49: 59: location: */ - /* 50: 60: max-forwards: */ - /* 51: 61: proxy-authenticate: */ - /* 52: 62: proxy-authorization: */ - /* 53: 63: refresh: */ - /* 54: 64: retry-after: */ - /* 55: 65: server: */ - /* 56: 66: set-cookie: */ - /* 57: 67: strict-transport-security: */ - /* 58: 68: transfer-encoding: */ - /* 59: 69: user-agent: */ - /* 60: 70: vary: */ - /* 61: 71: via: */ - /* 62: 72: www-authenticate: */ - /* 63: 73: patch */ - /* 64: 74: put */ - /* 65: 75: delete */ - /* 66: 76: uri-args */ - /* 67: 77: proxy */ - /* 68: 78: x-real-ip: */ - /* 69: 79: http/1.0 */ - /* 70: 80: x-forwarded-for: */ - /* 71: 81: connect */ - /* 72: 82: head */ - /* 73: 83: te: */ - /* 74: 84: replay-nonce: */ - /* 75: 85: :protocol */ - /* 76: 86: x-auth-token: */ - /* 77: 87: */ -/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */, - 0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */, - 0x68 /* 'h' */, 0x51, 0x00 /* (to 0x0057 state 10) */, - 0x63 /* 'c' */, 0x5D, 0x00 /* (to 0x0066 state 15) */, - 0x75 /* 'u' */, 0x7E, 0x00 /* (to 0x008A state 26) */, - 0x6F /* 'o' */, 0x8D, 0x00 /* (to 0x009C state 34) */, - 0x0D /* '.' */, 0x98, 0x00 /* (to 0x00AA state 41) */, - 0x61 /* 'a' */, 0xAD, 0x00 /* (to 0x00C2 state 51) */, - 0x69 /* 'i' */, 0xCA, 0x00 /* (to 0x00E2 state 58) */, - 0x64 /* 'd' */, 0x73, 0x01 /* (to 0x018E state 160) */, - 0x72 /* 'r' */, 0x7C, 0x01 /* (to 0x019A state 165) */, - 0x65 /* 'e' */, 0xC8, 0x01 /* (to 0x01E9 state 229) */, - 0x66 /* 'f' */, 0xE4, 0x01 /* (to 0x0208 state 245) */, - 0x6C /* 'l' */, 0x06, 0x02 /* (to 0x022D state 278) */, - 0x73 /* 's' */, 0x4B, 0x02 /* (to 0x0275 state 321) */, - 0x74 /* 't' */, 0x69, 0x02 /* (to 0x0296 state 337) */, - 0x78 /* 'x' */, 0x8A, 0x02 /* (to 0x02BA state 364) */, - 0x6D /* 'm' */, 0xFB, 0x02 /* (to 0x032E state 456) */, - 0x76 /* 'v' */, 0x54, 0x03 /* (to 0x038A state 531) */, - 0x77 /* 'w' */, 0x61, 0x03 /* (to 0x039A state 539) */, - 0x3A /* ':' */, 0x19, 0x04 /* (to 0x0455 state 674) */, - 0x08, /* fail */ -/* pos 0040: 1 */ 0xE5 /* 'e' -> */, -/* pos 0041: 2 */ 0xF4 /* 't' -> */, -/* pos 0042: 3 */ 0xA0 /* ' ' -> */, -/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, -/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */, - 0x72 /* 'r' */, 0xEC, 0x00 /* (to 0x0134 state 106) */, - 0x61 /* 'a' */, 0x61, 0x03 /* (to 0x03AC state 556) */, - 0x75 /* 'u' */, 0x63, 0x03 /* (to 0x03B1 state 560) */, - 0x08, /* fail */ -/* pos 0052: 6 */ 0xF3 /* 's' -> */, -/* pos 0053: 7 */ 0xF4 /* 't' -> */, -/* pos 0054: 8 */ 0xA0 /* ' ' -> */, -/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, -/* pos 0057: 10 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x0061 state 11) */, - 0x74 /* 't' */, 0x53, 0x00 /* (to 0x00AD state 43) */, - 0x65 /* 'e' */, 0x79, 0x02 /* (to 0x02D6 state 381) */, - 0x08, /* fail */ -/* pos 0061: 11 */ 0xF3 /* 's' -> */, -/* pos 0062: 12 */ 0xF4 /* 't' -> */, -/* pos 0063: 13 */ 0xBA /* ':' -> */, -/* pos 0064: 14 */ 0x00, 0x03 /* - terminal marker 3 - */, -/* pos 0066: 15 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x006D state 16) */, - 0x61 /* 'a' */, 0xD8, 0x00 /* (to 0x0141 state 112) */, - 0x08, /* fail */ -/* pos 006d: 16 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0074 state 17) */, - 0x6F /* 'o' */, 0xED, 0x00 /* (to 0x015D state 138) */, - 0x08, /* fail */ -/* pos 0074: 17 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x007B state 18) */, - 0x74 /* 't' */, 0xEC, 0x00 /* (to 0x0163 state 143) */, - 0x08, /* fail */ -/* pos 007b: 18 */ 0xE5 /* 'e' -> */, -/* pos 007c: 19 */ 0xE3 /* 'c' -> */, -/* pos 007d: 20 */ 0xF4 /* 't' -> */, -/* pos 007e: 21 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0085 state 22) */, - 0x20 /* ' ' */, 0x53, 0x02 /* (to 0x02D4 state 380) */, - 0x08, /* fail */ -/* pos 0085: 22 */ 0xEF /* 'o' -> */, -/* pos 0086: 23 */ 0xEE /* 'n' -> */, -/* pos 0087: 24 */ 0xBA /* ':' -> */, -/* pos 0088: 25 */ 0x00, 0x04 /* - terminal marker 4 - */, -/* pos 008a: 26 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x0094 state 27) */, - 0x72 /* 'r' */, 0x22, 0x02 /* (to 0x02AF state 355) */, - 0x73 /* 's' */, 0xEF, 0x02 /* (to 0x037F state 521) */, - 0x08, /* fail */ -/* pos 0094: 27 */ 0xE7 /* 'g' -> */, -/* pos 0095: 28 */ 0xF2 /* 'r' -> */, -/* pos 0096: 29 */ 0xE1 /* 'a' -> */, -/* pos 0097: 30 */ 0xE4 /* 'd' -> */, -/* pos 0098: 31 */ 0xE5 /* 'e' -> */, -/* pos 0099: 32 */ 0xBA /* ':' -> */, -/* pos 009a: 33 */ 0x00, 0x05 /* - terminal marker 5 - */, -/* pos 009c: 34 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x00A3 state 35) */, - 0x70 /* 'p' */, 0x48, 0x02 /* (to 0x02E7 state 396) */, - 0x08, /* fail */ -/* pos 00a3: 35 */ 0xE9 /* 'i' -> */, -/* pos 00a4: 36 */ 0xE7 /* 'g' -> */, -/* pos 00a5: 37 */ 0xE9 /* 'i' -> */, -/* pos 00a6: 38 */ 0xEE /* 'n' -> */, -/* pos 00a7: 39 */ 0xBA /* ':' -> */, -/* pos 00a8: 40 */ 0x00, 0x06 /* - terminal marker 6 - */, -/* pos 00aa: 41 */ 0x8A /* '.' -> */, -/* pos 00ab: 42 */ 0x00, 0x07 /* - terminal marker 7 - */, -/* pos 00ad: 43 */ 0xF4 /* 't' -> */, -/* pos 00ae: 44 */ 0xF0 /* 'p' -> */, -/* pos 00af: 45 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x00B6 state 46) */, - 0x32 /* '2' */, 0x97, 0x03 /* (to 0x0449 state 663) */, - 0x08, /* fail */ -/* pos 00b6: 46 */ 0xB1 /* '1' -> */, -/* pos 00b7: 47 */ 0xAE /* '.' -> */, -/* pos 00b8: 48 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x00BF state 49) */, - 0x30 /* '0' */, 0xFC, 0x01 /* (to 0x02B7 state 362) */, - 0x08, /* fail */ -/* pos 00bf: 49 */ 0xA0 /* ' ' -> */, -/* pos 00c0: 50 */ 0x00, 0x08 /* - terminal marker 8 - */, -/* pos 00c2: 51 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x00CF state 52) */, - 0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x014F state 125) */, - 0x67 /* 'g' */, 0xE7, 0x00 /* (to 0x01AF state 178) */, - 0x6C /* 'l' */, 0xE8, 0x00 /* (to 0x01B3 state 181) */, - 0x08, /* fail */ -/* pos 00cf: 52 */ 0xE3 /* 'c' -> */, -/* pos 00d0: 53 */ 0xE5 /* 'e' -> */, -/* pos 00d1: 54 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x00D8 state 55) */, - 0x73 /* 's' */, 0x1B, 0x02 /* (to 0x02EF state 403) */, - 0x08, /* fail */ -/* pos 00d8: 55 */ 0xF4 /* 't' -> */, -/* pos 00d9: 56 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x00E0 state 57) */, - 0x2D /* '-' */, 0x37, 0x00 /* (to 0x0113 state 87) */, - 0x08, /* fail */ -/* pos 00e0: 57 */ 0x00, 0x0A /* - terminal marker 10 - */, -/* pos 00e2: 58 */ 0xE6 /* 'f' -> */, -/* pos 00e3: 59 */ 0xAD /* '-' -> */, -/* pos 00e4: 60 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x00F1 state 61) */, - 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x0107 state 76) */, - 0x72 /* 'r' */, 0x2A, 0x01 /* (to 0x0214 state 255) */, - 0x75 /* 'u' */, 0x2E, 0x01 /* (to 0x021B state 261) */, - 0x08, /* fail */ -/* pos 00f1: 61 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x00F8 state 62) */, - 0x61 /* 'a' */, 0x1A, 0x01 /* (to 0x020E state 250) */, - 0x08, /* fail */ -/* pos 00f8: 62 */ 0xE4 /* 'd' -> */, -/* pos 00f9: 63 */ 0xE9 /* 'i' -> */, -/* pos 00fa: 64 */ 0xE6 /* 'f' -> */, -/* pos 00fb: 65 */ 0xE9 /* 'i' -> */, -/* pos 00fc: 66 */ 0xE5 /* 'e' -> */, -/* pos 00fd: 67 */ 0xE4 /* 'd' -> */, -/* pos 00fe: 68 */ 0xAD /* '-' -> */, -/* pos 00ff: 69 */ 0xF3 /* 's' -> */, -/* pos 0100: 70 */ 0xE9 /* 'i' -> */, -/* pos 0101: 71 */ 0xEE /* 'n' -> */, -/* pos 0102: 72 */ 0xE3 /* 'c' -> */, -/* pos 0103: 73 */ 0xE5 /* 'e' -> */, -/* pos 0104: 74 */ 0xBA /* ':' -> */, -/* pos 0105: 75 */ 0x00, 0x0C /* - terminal marker 12 - */, -/* pos 0107: 76 */ 0xEF /* 'o' -> */, -/* pos 0108: 77 */ 0xEE /* 'n' -> */, -/* pos 0109: 78 */ 0xE5 /* 'e' -> */, -/* pos 010a: 79 */ 0xAD /* '-' -> */, -/* pos 010b: 80 */ 0xED /* 'm' -> */, -/* pos 010c: 81 */ 0xE1 /* 'a' -> */, -/* pos 010d: 82 */ 0xF4 /* 't' -> */, -/* pos 010e: 83 */ 0xE3 /* 'c' -> */, -/* pos 010f: 84 */ 0xE8 /* 'h' -> */, -/* pos 0110: 85 */ 0xBA /* ':' -> */, -/* pos 0111: 86 */ 0x00, 0x0D /* - terminal marker 13 - */, -/* pos 0113: 87 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x0120 state 88) */, - 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x012A state 97) */, - 0x72 /* 'r' */, 0x8E, 0x00 /* (to 0x01A7 state 171) */, - 0x63 /* 'c' */, 0xFB, 0x01 /* (to 0x0317 state 435) */, - 0x08, /* fail */ -/* pos 0120: 88 */ 0xEE /* 'n' -> */, -/* pos 0121: 89 */ 0xE3 /* 'c' -> */, -/* pos 0122: 90 */ 0xEF /* 'o' -> */, -/* pos 0123: 91 */ 0xE4 /* 'd' -> */, -/* pos 0124: 92 */ 0xE9 /* 'i' -> */, -/* pos 0125: 93 */ 0xEE /* 'n' -> */, -/* pos 0126: 94 */ 0xE7 /* 'g' -> */, -/* pos 0127: 95 */ 0xBA /* ':' -> */, -/* pos 0128: 96 */ 0x00, 0x0E /* - terminal marker 14 - */, -/* pos 012a: 97 */ 0xE1 /* 'a' -> */, -/* pos 012b: 98 */ 0xEE /* 'n' -> */, -/* pos 012c: 99 */ 0xE7 /* 'g' -> */, -/* pos 012d: 100 */ 0xF5 /* 'u' -> */, -/* pos 012e: 101 */ 0xE1 /* 'a' -> */, -/* pos 012f: 102 */ 0xE7 /* 'g' -> */, -/* pos 0130: 103 */ 0xE5 /* 'e' -> */, -/* pos 0131: 104 */ 0xBA /* ':' -> */, -/* pos 0132: 105 */ 0x00, 0x0F /* - terminal marker 15 - */, -/* pos 0134: 106 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x013B state 107) */, - 0x6F /* 'o' */, 0x05, 0x02 /* (to 0x033C state 469) */, - 0x08, /* fail */ -/* pos 013b: 107 */ 0xE7 /* 'g' -> */, -/* pos 013c: 108 */ 0xED /* 'm' -> */, -/* pos 013d: 109 */ 0xE1 /* 'a' -> */, -/* pos 013e: 110 */ 0xBA /* ':' -> */, -/* pos 013f: 111 */ 0x00, 0x10 /* - terminal marker 16 - */, -/* pos 0141: 112 */ 0xE3 /* 'c' -> */, -/* pos 0142: 113 */ 0xE8 /* 'h' -> */, -/* pos 0143: 114 */ 0xE5 /* 'e' -> */, -/* pos 0144: 115 */ 0xAD /* '-' -> */, -/* pos 0145: 116 */ 0xE3 /* 'c' -> */, -/* pos 0146: 117 */ 0xEF /* 'o' -> */, -/* pos 0147: 118 */ 0xEE /* 'n' -> */, -/* pos 0148: 119 */ 0xF4 /* 't' -> */, -/* pos 0149: 120 */ 0xF2 /* 'r' -> */, -/* pos 014a: 121 */ 0xEF /* 'o' -> */, -/* pos 014b: 122 */ 0xEC /* 'l' -> */, -/* pos 014c: 123 */ 0xBA /* ':' -> */, -/* pos 014d: 124 */ 0x00, 0x11 /* - terminal marker 17 - */, -/* pos 014f: 125 */ 0xF4 /* 't' -> */, -/* pos 0150: 126 */ 0xE8 /* 'h' -> */, -/* pos 0151: 127 */ 0xEF /* 'o' -> */, -/* pos 0152: 128 */ 0xF2 /* 'r' -> */, -/* pos 0153: 129 */ 0xE9 /* 'i' -> */, -/* pos 0154: 130 */ 0xFA /* 'z' -> */, -/* pos 0155: 131 */ 0xE1 /* 'a' -> */, -/* pos 0156: 132 */ 0xF4 /* 't' -> */, -/* pos 0157: 133 */ 0xE9 /* 'i' -> */, -/* pos 0158: 134 */ 0xEF /* 'o' -> */, -/* pos 0159: 135 */ 0xEE /* 'n' -> */, -/* pos 015a: 136 */ 0xBA /* ':' -> */, -/* pos 015b: 137 */ 0x00, 0x12 /* - terminal marker 18 - */, -/* pos 015d: 138 */ 0xEB /* 'k' -> */, -/* pos 015e: 139 */ 0xE9 /* 'i' -> */, -/* pos 015f: 140 */ 0xE5 /* 'e' -> */, -/* pos 0160: 141 */ 0xBA /* ':' -> */, -/* pos 0161: 142 */ 0x00, 0x13 /* - terminal marker 19 - */, -/* pos 0163: 143 */ 0xE5 /* 'e' -> */, -/* pos 0164: 144 */ 0xEE /* 'n' -> */, -/* pos 0165: 145 */ 0xF4 /* 't' -> */, -/* pos 0166: 146 */ 0xAD /* '-' -> */, -/* pos 0167: 147 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0177 state 148) */, - 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0188 state 155) */, - 0x64 /* 'd' */, 0x4C, 0x00 /* (to 0x01B9 state 186) */, - 0x65 /* 'e' */, 0x56, 0x00 /* (to 0x01C6 state 198) */, - 0x72 /* 'r' */, 0x6F, 0x00 /* (to 0x01E2 state 223) */, - 0x08, /* fail */ -/* pos 0177: 148 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0181 state 149) */, - 0x61 /* 'a' */, 0x56, 0x00 /* (to 0x01D0 state 207) */, - 0x6F /* 'o' */, 0x5C, 0x00 /* (to 0x01D9 state 215) */, - 0x08, /* fail */ -/* pos 0181: 149 */ 0xEE /* 'n' -> */, -/* pos 0182: 150 */ 0xE7 /* 'g' -> */, -/* pos 0183: 151 */ 0xF4 /* 't' -> */, -/* pos 0184: 152 */ 0xE8 /* 'h' -> */, -/* pos 0185: 153 */ 0xBA /* ':' -> */, -/* pos 0186: 154 */ 0x00, 0x14 /* - terminal marker 20 - */, -/* pos 0188: 155 */ 0xF9 /* 'y' -> */, -/* pos 0189: 156 */ 0xF0 /* 'p' -> */, -/* pos 018a: 157 */ 0xE5 /* 'e' -> */, -/* pos 018b: 158 */ 0xBA /* ':' -> */, -/* pos 018c: 159 */ 0x00, 0x15 /* - terminal marker 21 - */, -/* pos 018e: 160 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0195 state 161) */, - 0x65 /* 'e' */, 0x23, 0x02 /* (to 0x03B4 state 562) */, - 0x08, /* fail */ -/* pos 0195: 161 */ 0xF4 /* 't' -> */, -/* pos 0196: 162 */ 0xE5 /* 'e' -> */, -/* pos 0197: 163 */ 0xBA /* ':' -> */, -/* pos 0198: 164 */ 0x00, 0x16 /* - terminal marker 22 - */, -/* pos 019a: 165 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01A1 state 166) */, - 0x65 /* 'e' */, 0xB6, 0x00 /* (to 0x0253 state 304) */, - 0x08, /* fail */ -/* pos 01a1: 166 */ 0xEE /* 'n' -> */, -/* pos 01a2: 167 */ 0xE7 /* 'g' -> */, -/* pos 01a3: 168 */ 0xE5 /* 'e' -> */, -/* pos 01a4: 169 */ 0xBA /* ':' -> */, -/* pos 01a5: 170 */ 0x00, 0x17 /* - terminal marker 23 - */, -/* pos 01a7: 171 */ 0xE1 /* 'a' -> */, -/* pos 01a8: 172 */ 0xEE /* 'n' -> */, -/* pos 01a9: 173 */ 0xE7 /* 'g' -> */, -/* pos 01aa: 174 */ 0xE5 /* 'e' -> */, -/* pos 01ab: 175 */ 0xF3 /* 's' -> */, -/* pos 01ac: 176 */ 0xBA /* ':' -> */, -/* pos 01ad: 177 */ 0x00, 0x1F /* - terminal marker 31 - */, -/* pos 01af: 178 */ 0xE5 /* 'e' -> */, -/* pos 01b0: 179 */ 0xBA /* ':' -> */, -/* pos 01b1: 180 */ 0x00, 0x21 /* - terminal marker 33 - */, -/* pos 01b3: 181 */ 0xEC /* 'l' -> */, -/* pos 01b4: 182 */ 0xEF /* 'o' -> */, -/* pos 01b5: 183 */ 0xF7 /* 'w' -> */, -/* pos 01b6: 184 */ 0xBA /* ':' -> */, -/* pos 01b7: 185 */ 0x00, 0x22 /* - terminal marker 34 - */, -/* pos 01b9: 186 */ 0xE9 /* 'i' -> */, -/* pos 01ba: 187 */ 0xF3 /* 's' -> */, -/* pos 01bb: 188 */ 0xF0 /* 'p' -> */, -/* pos 01bc: 189 */ 0xEF /* 'o' -> */, -/* pos 01bd: 190 */ 0xF3 /* 's' -> */, -/* pos 01be: 191 */ 0xE9 /* 'i' -> */, -/* pos 01bf: 192 */ 0xF4 /* 't' -> */, -/* pos 01c0: 193 */ 0xE9 /* 'i' -> */, -/* pos 01c1: 194 */ 0xEF /* 'o' -> */, -/* pos 01c2: 195 */ 0xEE /* 'n' -> */, -/* pos 01c3: 196 */ 0xBA /* ':' -> */, -/* pos 01c4: 197 */ 0x00, 0x23 /* - terminal marker 35 - */, -/* pos 01c6: 198 */ 0xEE /* 'n' -> */, -/* pos 01c7: 199 */ 0xE3 /* 'c' -> */, -/* pos 01c8: 200 */ 0xEF /* 'o' -> */, -/* pos 01c9: 201 */ 0xE4 /* 'd' -> */, -/* pos 01ca: 202 */ 0xE9 /* 'i' -> */, -/* pos 01cb: 203 */ 0xEE /* 'n' -> */, -/* pos 01cc: 204 */ 0xE7 /* 'g' -> */, -/* pos 01cd: 205 */ 0xBA /* ':' -> */, -/* pos 01ce: 206 */ 0x00, 0x24 /* - terminal marker 36 - */, -/* pos 01d0: 207 */ 0xEE /* 'n' -> */, -/* pos 01d1: 208 */ 0xE7 /* 'g' -> */, -/* pos 01d2: 209 */ 0xF5 /* 'u' -> */, -/* pos 01d3: 210 */ 0xE1 /* 'a' -> */, -/* pos 01d4: 211 */ 0xE7 /* 'g' -> */, -/* pos 01d5: 212 */ 0xE5 /* 'e' -> */, -/* pos 01d6: 213 */ 0xBA /* ':' -> */, -/* pos 01d7: 214 */ 0x00, 0x25 /* - terminal marker 37 - */, -/* pos 01d9: 215 */ 0xE3 /* 'c' -> */, -/* pos 01da: 216 */ 0xE1 /* 'a' -> */, -/* pos 01db: 217 */ 0xF4 /* 't' -> */, -/* pos 01dc: 218 */ 0xE9 /* 'i' -> */, -/* pos 01dd: 219 */ 0xEF /* 'o' -> */, -/* pos 01de: 220 */ 0xEE /* 'n' -> */, -/* pos 01df: 221 */ 0xBA /* ':' -> */, -/* pos 01e0: 222 */ 0x00, 0x26 /* - terminal marker 38 - */, -/* pos 01e2: 223 */ 0xE1 /* 'a' -> */, -/* pos 01e3: 224 */ 0xEE /* 'n' -> */, -/* pos 01e4: 225 */ 0xE7 /* 'g' -> */, -/* pos 01e5: 226 */ 0xE5 /* 'e' -> */, -/* pos 01e6: 227 */ 0xBA /* ':' -> */, -/* pos 01e7: 228 */ 0x00, 0x27 /* - terminal marker 39 - */, -/* pos 01e9: 229 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x01F0 state 230) */, - 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x01F5 state 234) */, - 0x08, /* fail */ -/* pos 01f0: 230 */ 0xE1 /* 'a' -> */, -/* pos 01f1: 231 */ 0xE7 /* 'g' -> */, -/* pos 01f2: 232 */ 0xBA /* ':' -> */, -/* pos 01f3: 233 */ 0x00, 0x28 /* - terminal marker 40 - */, -/* pos 01f5: 234 */ 0xF0 /* 'p' -> */, -/* pos 01f6: 235 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x01FD state 236) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0202 state 240) */, - 0x08, /* fail */ -/* pos 01fd: 236 */ 0xE3 /* 'c' -> */, -/* pos 01fe: 237 */ 0xF4 /* 't' -> */, -/* pos 01ff: 238 */ 0xBA /* ':' -> */, -/* pos 0200: 239 */ 0x00, 0x29 /* - terminal marker 41 - */, -/* pos 0202: 240 */ 0xF2 /* 'r' -> */, -/* pos 0203: 241 */ 0xE5 /* 'e' -> */, -/* pos 0204: 242 */ 0xF3 /* 's' -> */, -/* pos 0205: 243 */ 0xBA /* ':' -> */, -/* pos 0206: 244 */ 0x00, 0x2A /* - terminal marker 42 - */, -/* pos 0208: 245 */ 0xF2 /* 'r' -> */, -/* pos 0209: 246 */ 0xEF /* 'o' -> */, -/* pos 020a: 247 */ 0xED /* 'm' -> */, -/* pos 020b: 248 */ 0xBA /* ':' -> */, -/* pos 020c: 249 */ 0x00, 0x2B /* - terminal marker 43 - */, -/* pos 020e: 250 */ 0xF4 /* 't' -> */, -/* pos 020f: 251 */ 0xE3 /* 'c' -> */, -/* pos 0210: 252 */ 0xE8 /* 'h' -> */, -/* pos 0211: 253 */ 0xBA /* ':' -> */, -/* pos 0212: 254 */ 0x00, 0x2C /* - terminal marker 44 - */, -/* pos 0214: 255 */ 0xE1 /* 'a' -> */, -/* pos 0215: 256 */ 0xEE /* 'n' -> */, -/* pos 0216: 257 */ 0xE7 /* 'g' -> */, -/* pos 0217: 258 */ 0xE5 /* 'e' -> */, -/* pos 0218: 259 */ 0xBA /* ':' -> */, -/* pos 0219: 260 */ 0x00, 0x2D /* - terminal marker 45 - */, -/* pos 021b: 261 */ 0xEE /* 'n' -> */, -/* pos 021c: 262 */ 0xED /* 'm' -> */, -/* pos 021d: 263 */ 0xEF /* 'o' -> */, -/* pos 021e: 264 */ 0xE4 /* 'd' -> */, -/* pos 021f: 265 */ 0xE9 /* 'i' -> */, -/* pos 0220: 266 */ 0xE6 /* 'f' -> */, -/* pos 0221: 267 */ 0xE9 /* 'i' -> */, -/* pos 0222: 268 */ 0xE5 /* 'e' -> */, -/* pos 0223: 269 */ 0xE4 /* 'd' -> */, -/* pos 0224: 270 */ 0xAD /* '-' -> */, -/* pos 0225: 271 */ 0xF3 /* 's' -> */, -/* pos 0226: 272 */ 0xE9 /* 'i' -> */, -/* pos 0227: 273 */ 0xEE /* 'n' -> */, -/* pos 0228: 274 */ 0xE3 /* 'c' -> */, -/* pos 0229: 275 */ 0xE5 /* 'e' -> */, -/* pos 022a: 276 */ 0xBA /* ':' -> */, -/* pos 022b: 277 */ 0x00, 0x2E /* - terminal marker 46 - */, -/* pos 022d: 278 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0237 state 279) */, - 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x0245 state 292) */, - 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x024A state 296) */, - 0x08, /* fail */ -/* pos 0237: 279 */ 0xF3 /* 's' -> */, -/* pos 0238: 280 */ 0xF4 /* 't' -> */, -/* pos 0239: 281 */ 0xAD /* '-' -> */, -/* pos 023a: 282 */ 0xED /* 'm' -> */, -/* pos 023b: 283 */ 0xEF /* 'o' -> */, -/* pos 023c: 284 */ 0xE4 /* 'd' -> */, -/* pos 023d: 285 */ 0xE9 /* 'i' -> */, -/* pos 023e: 286 */ 0xE6 /* 'f' -> */, -/* pos 023f: 287 */ 0xE9 /* 'i' -> */, -/* pos 0240: 288 */ 0xE5 /* 'e' -> */, -/* pos 0241: 289 */ 0xE4 /* 'd' -> */, -/* pos 0242: 290 */ 0xBA /* ':' -> */, -/* pos 0243: 291 */ 0x00, 0x2F /* - terminal marker 47 - */, -/* pos 0245: 292 */ 0xEE /* 'n' -> */, -/* pos 0246: 293 */ 0xEB /* 'k' -> */, -/* pos 0247: 294 */ 0xBA /* ':' -> */, -/* pos 0248: 295 */ 0x00, 0x30 /* - terminal marker 48 - */, -/* pos 024a: 296 */ 0xE3 /* 'c' -> */, -/* pos 024b: 297 */ 0xE1 /* 'a' -> */, -/* pos 024c: 298 */ 0xF4 /* 't' -> */, -/* pos 024d: 299 */ 0xE9 /* 'i' -> */, -/* pos 024e: 300 */ 0xEF /* 'o' -> */, -/* pos 024f: 301 */ 0xEE /* 'n' -> */, -/* pos 0250: 302 */ 0xBA /* ':' -> */, -/* pos 0251: 303 */ 0x00, 0x31 /* - terminal marker 49 - */, -/* pos 0253: 304 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x025D state 305) */, - 0x74 /* 't' */, 0x14, 0x00 /* (to 0x026A state 311) */, - 0x70 /* 'p' */, 0x6F, 0x01 /* (to 0x03C8 state 578) */, - 0x08, /* fail */ -/* pos 025d: 305 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0264 state 306) */, - 0x65 /* 'e' */, 0xB1, 0x00 /* (to 0x0311 state 430) */, - 0x08, /* fail */ -/* pos 0264: 306 */ 0xE5 /* 'e' -> */, -/* pos 0265: 307 */ 0xF3 /* 's' -> */, -/* pos 0266: 308 */ 0xE8 /* 'h' -> */, -/* pos 0267: 309 */ 0xBA /* ':' -> */, -/* pos 0268: 310 */ 0x00, 0x35 /* - terminal marker 53 - */, -/* pos 026a: 311 */ 0xF2 /* 'r' -> */, -/* pos 026b: 312 */ 0xF9 /* 'y' -> */, -/* pos 026c: 313 */ 0xAD /* '-' -> */, -/* pos 026d: 314 */ 0xE1 /* 'a' -> */, -/* pos 026e: 315 */ 0xE6 /* 'f' -> */, -/* pos 026f: 316 */ 0xF4 /* 't' -> */, -/* pos 0270: 317 */ 0xE5 /* 'e' -> */, -/* pos 0271: 318 */ 0xF2 /* 'r' -> */, -/* pos 0272: 319 */ 0xBA /* ':' -> */, -/* pos 0273: 320 */ 0x00, 0x36 /* - terminal marker 54 - */, -/* pos 0275: 321 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x027C state 322) */, - 0x74 /* 't' */, 0xED, 0x00 /* (to 0x0365 state 496) */, - 0x08, /* fail */ -/* pos 027c: 322 */ 0x72 /* 'r' */, 0x0A, 0x00 /* (to 0x0286 state 323) */, - 0x74 /* 't' */, 0x0D, 0x00 /* (to 0x028C state 328) */, - 0x63 /* 'c' */, 0x52, 0x01 /* (to 0x03D4 state 589) */, - 0x08, /* fail */ -/* pos 0286: 323 */ 0xF6 /* 'v' -> */, -/* pos 0287: 324 */ 0xE5 /* 'e' -> */, -/* pos 0288: 325 */ 0xF2 /* 'r' -> */, -/* pos 0289: 326 */ 0xBA /* ':' -> */, -/* pos 028a: 327 */ 0x00, 0x37 /* - terminal marker 55 - */, -/* pos 028c: 328 */ 0xAD /* '-' -> */, -/* pos 028d: 329 */ 0xE3 /* 'c' -> */, -/* pos 028e: 330 */ 0xEF /* 'o' -> */, -/* pos 028f: 331 */ 0xEF /* 'o' -> */, -/* pos 0290: 332 */ 0xEB /* 'k' -> */, -/* pos 0291: 333 */ 0xE9 /* 'i' -> */, -/* pos 0292: 334 */ 0xE5 /* 'e' -> */, -/* pos 0293: 335 */ 0xBA /* ':' -> */, -/* pos 0294: 336 */ 0x00, 0x38 /* - terminal marker 56 - */, -/* pos 0296: 337 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x029D state 338) */, - 0x65 /* 'e' */, 0x2C, 0x01 /* (to 0x03C5 state 576) */, - 0x08, /* fail */ -/* pos 029d: 338 */ 0xE1 /* 'a' -> */, -/* pos 029e: 339 */ 0xEE /* 'n' -> */, -/* pos 029f: 340 */ 0xF3 /* 's' -> */, -/* pos 02a0: 341 */ 0xE6 /* 'f' -> */, -/* pos 02a1: 342 */ 0xE5 /* 'e' -> */, -/* pos 02a2: 343 */ 0xF2 /* 'r' -> */, -/* pos 02a3: 344 */ 0xAD /* '-' -> */, -/* pos 02a4: 345 */ 0xE5 /* 'e' -> */, -/* pos 02a5: 346 */ 0xEE /* 'n' -> */, -/* pos 02a6: 347 */ 0xE3 /* 'c' -> */, -/* pos 02a7: 348 */ 0xEF /* 'o' -> */, -/* pos 02a8: 349 */ 0xE4 /* 'd' -> */, -/* pos 02a9: 350 */ 0xE9 /* 'i' -> */, -/* pos 02aa: 351 */ 0xEE /* 'n' -> */, -/* pos 02ab: 352 */ 0xE7 /* 'g' -> */, -/* pos 02ac: 353 */ 0xBA /* ':' -> */, -/* pos 02ad: 354 */ 0x00, 0x3A /* - terminal marker 58 - */, -/* pos 02af: 355 */ 0xE9 /* 'i' -> */, -/* pos 02b0: 356 */ 0xAD /* '-' -> */, -/* pos 02b1: 357 */ 0xE1 /* 'a' -> */, -/* pos 02b2: 358 */ 0xF2 /* 'r' -> */, -/* pos 02b3: 359 */ 0xE7 /* 'g' -> */, -/* pos 02b4: 360 */ 0xF3 /* 's' -> */, -/* pos 02b5: 361 */ 0x00, 0x42 /* - terminal marker 66 - */, -/* pos 02b7: 362 */ 0xA0 /* ' ' -> */, -/* pos 02b8: 363 */ 0x00, 0x45 /* - terminal marker 69 - */, -/* pos 02ba: 364 */ 0xAD /* '-' -> */, -/* pos 02bb: 365 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x02C5 state 366) */, - 0x61 /* 'a' */, 0x1D, 0x00 /* (to 0x02DB state 385) */, - 0x72 /* 'r' */, 0xFB, 0x00 /* (to 0x03BC state 568) */, - 0x08, /* fail */ -/* pos 02c5: 366 */ 0xEF /* 'o' -> */, -/* pos 02c6: 367 */ 0xF2 /* 'r' -> */, -/* pos 02c7: 368 */ 0xF7 /* 'w' -> */, -/* pos 02c8: 369 */ 0xE1 /* 'a' -> */, -/* pos 02c9: 370 */ 0xF2 /* 'r' -> */, -/* pos 02ca: 371 */ 0xE4 /* 'd' -> */, -/* pos 02cb: 372 */ 0xE5 /* 'e' -> */, -/* pos 02cc: 373 */ 0xE4 /* 'd' -> */, -/* pos 02cd: 374 */ 0xAD /* '-' -> */, -/* pos 02ce: 375 */ 0xE6 /* 'f' -> */, -/* pos 02cf: 376 */ 0xEF /* 'o' -> */, -/* pos 02d0: 377 */ 0xF2 /* 'r' -> */, -/* pos 02d1: 378 */ 0xBA /* ':' -> */, -/* pos 02d2: 379 */ 0x00, 0x46 /* - terminal marker 70 - */, -/* pos 02d4: 380 */ 0x00, 0x47 /* - terminal marker 71 - */, -/* pos 02d6: 381 */ 0xE1 /* 'a' -> */, -/* pos 02d7: 382 */ 0xE4 /* 'd' -> */, -/* pos 02d8: 383 */ 0xA0 /* ' ' -> */, -/* pos 02d9: 384 */ 0x00, 0x48 /* - terminal marker 72 - */, -/* pos 02db: 385 */ 0xF5 /* 'u' -> */, -/* pos 02dc: 386 */ 0xF4 /* 't' -> */, -/* pos 02dd: 387 */ 0xE8 /* 'h' -> */, -/* pos 02de: 388 */ 0xAD /* '-' -> */, -/* pos 02df: 389 */ 0xF4 /* 't' -> */, -/* pos 02e0: 390 */ 0xEF /* 'o' -> */, -/* pos 02e1: 391 */ 0xEB /* 'k' -> */, -/* pos 02e2: 392 */ 0xE5 /* 'e' -> */, -/* pos 02e3: 393 */ 0xEE /* 'n' -> */, -/* pos 02e4: 394 */ 0xBA /* ':' -> */, -/* pos 02e5: 395 */ 0x00, 0x4C /* - terminal marker 76 - */, -/* pos 02e7: 396 */ 0xF4 /* 't' -> */, -/* pos 02e8: 397 */ 0xE9 /* 'i' -> */, -/* pos 02e9: 398 */ 0xEF /* 'o' -> */, -/* pos 02ea: 399 */ 0xEE /* 'n' -> */, -/* pos 02eb: 400 */ 0xF3 /* 's' -> */, -/* pos 02ec: 401 */ 0xA0 /* ' ' -> */, -/* pos 02ed: 402 */ 0x00, 0x02 /* - terminal marker 2 - */, -/* pos 02ef: 403 */ 0xF3 /* 's' -> */, -/* pos 02f0: 404 */ 0xAD /* '-' -> */, -/* pos 02f1: 405 */ 0xE3 /* 'c' -> */, -/* pos 02f2: 406 */ 0xEF /* 'o' -> */, -/* pos 02f3: 407 */ 0xEE /* 'n' -> */, -/* pos 02f4: 408 */ 0xF4 /* 't' -> */, -/* pos 02f5: 409 */ 0xF2 /* 'r' -> */, -/* pos 02f6: 410 */ 0xEF /* 'o' -> */, -/* pos 02f7: 411 */ 0xEC /* 'l' -> */, -/* pos 02f8: 412 */ 0xAD /* '-' -> */, -/* pos 02f9: 413 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0300 state 414) */, - 0x61 /* 'a' */, 0x24, 0x00 /* (to 0x0320 state 443) */, - 0x08, /* fail */ -/* pos 0300: 414 */ 0xE5 /* 'e' -> */, -/* pos 0301: 415 */ 0xF1 /* 'q' -> */, -/* pos 0302: 416 */ 0xF5 /* 'u' -> */, -/* pos 0303: 417 */ 0xE5 /* 'e' -> */, -/* pos 0304: 418 */ 0xF3 /* 's' -> */, -/* pos 0305: 419 */ 0xF4 /* 't' -> */, -/* pos 0306: 420 */ 0xAD /* '-' -> */, -/* pos 0307: 421 */ 0xE8 /* 'h' -> */, -/* pos 0308: 422 */ 0xE5 /* 'e' -> */, -/* pos 0309: 423 */ 0xE1 /* 'a' -> */, -/* pos 030a: 424 */ 0xE4 /* 'd' -> */, -/* pos 030b: 425 */ 0xE5 /* 'e' -> */, -/* pos 030c: 426 */ 0xF2 /* 'r' -> */, -/* pos 030d: 427 */ 0xF3 /* 's' -> */, -/* pos 030e: 428 */ 0xBA /* ':' -> */, -/* pos 030f: 429 */ 0x00, 0x0B /* - terminal marker 11 - */, -/* pos 0311: 430 */ 0xF2 /* 'r' -> */, -/* pos 0312: 431 */ 0xE5 /* 'e' -> */, -/* pos 0313: 432 */ 0xF2 /* 'r' -> */, -/* pos 0314: 433 */ 0xBA /* ':' -> */, -/* pos 0315: 434 */ 0x00, 0x18 /* - terminal marker 24 - */, -/* pos 0317: 435 */ 0xE8 /* 'h' -> */, -/* pos 0318: 436 */ 0xE1 /* 'a' -> */, -/* pos 0319: 437 */ 0xF2 /* 'r' -> */, -/* pos 031a: 438 */ 0xF3 /* 's' -> */, -/* pos 031b: 439 */ 0xE5 /* 'e' -> */, -/* pos 031c: 440 */ 0xF4 /* 't' -> */, -/* pos 031d: 441 */ 0xBA /* ':' -> */, -/* pos 031e: 442 */ 0x00, 0x1E /* - terminal marker 30 - */, -/* pos 0320: 443 */ 0xEC /* 'l' -> */, -/* pos 0321: 444 */ 0xEC /* 'l' -> */, -/* pos 0322: 445 */ 0xEF /* 'o' -> */, -/* pos 0323: 446 */ 0xF7 /* 'w' -> */, -/* pos 0324: 447 */ 0xAD /* '-' -> */, -/* pos 0325: 448 */ 0xEF /* 'o' -> */, -/* pos 0326: 449 */ 0xF2 /* 'r' -> */, -/* pos 0327: 450 */ 0xE9 /* 'i' -> */, -/* pos 0328: 451 */ 0xE7 /* 'g' -> */, -/* pos 0329: 452 */ 0xE9 /* 'i' -> */, -/* pos 032a: 453 */ 0xEE /* 'n' -> */, -/* pos 032b: 454 */ 0xBA /* ':' -> */, -/* pos 032c: 455 */ 0x00, 0x20 /* - terminal marker 32 - */, -/* pos 032e: 456 */ 0xE1 /* 'a' -> */, -/* pos 032f: 457 */ 0xF8 /* 'x' -> */, -/* pos 0330: 458 */ 0xAD /* '-' -> */, -/* pos 0331: 459 */ 0xE6 /* 'f' -> */, -/* pos 0332: 460 */ 0xEF /* 'o' -> */, -/* pos 0333: 461 */ 0xF2 /* 'r' -> */, -/* pos 0334: 462 */ 0xF7 /* 'w' -> */, -/* pos 0335: 463 */ 0xE1 /* 'a' -> */, -/* pos 0336: 464 */ 0xF2 /* 'r' -> */, -/* pos 0337: 465 */ 0xE4 /* 'd' -> */, -/* pos 0338: 466 */ 0xF3 /* 's' -> */, -/* pos 0339: 467 */ 0xBA /* ':' -> */, -/* pos 033a: 468 */ 0x00, 0x32 /* - terminal marker 50 - */, -/* pos 033c: 469 */ 0xF8 /* 'x' -> */, -/* pos 033d: 470 */ 0xF9 /* 'y' -> */, -/* pos 033e: 471 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0345 state 472) */, - 0x20 /* ' ' */, 0x79, 0x00 /* (to 0x03BA state 567) */, - 0x08, /* fail */ -/* pos 0345: 472 */ 0xE1 /* 'a' -> */, -/* pos 0346: 473 */ 0xF5 /* 'u' -> */, -/* pos 0347: 474 */ 0xF4 /* 't' -> */, -/* pos 0348: 475 */ 0xE8 /* 'h' -> */, -/* pos 0349: 476 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0350 state 477) */, - 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x035A state 486) */, - 0x08, /* fail */ -/* pos 0350: 477 */ 0xEE /* 'n' -> */, -/* pos 0351: 478 */ 0xF4 /* 't' -> */, -/* pos 0352: 479 */ 0xE9 /* 'i' -> */, -/* pos 0353: 480 */ 0xE3 /* 'c' -> */, -/* pos 0354: 481 */ 0xE1 /* 'a' -> */, -/* pos 0355: 482 */ 0xF4 /* 't' -> */, -/* pos 0356: 483 */ 0xE5 /* 'e' -> */, -/* pos 0357: 484 */ 0xBA /* ':' -> */, -/* pos 0358: 485 */ 0x00, 0x33 /* - terminal marker 51 - */, -/* pos 035a: 486 */ 0xF2 /* 'r' -> */, -/* pos 035b: 487 */ 0xE9 /* 'i' -> */, -/* pos 035c: 488 */ 0xFA /* 'z' -> */, -/* pos 035d: 489 */ 0xE1 /* 'a' -> */, -/* pos 035e: 490 */ 0xF4 /* 't' -> */, -/* pos 035f: 491 */ 0xE9 /* 'i' -> */, -/* pos 0360: 492 */ 0xEF /* 'o' -> */, -/* pos 0361: 493 */ 0xEE /* 'n' -> */, -/* pos 0362: 494 */ 0xBA /* ':' -> */, -/* pos 0363: 495 */ 0x00, 0x34 /* - terminal marker 52 - */, -/* pos 0365: 496 */ 0xF2 /* 'r' -> */, -/* pos 0366: 497 */ 0xE9 /* 'i' -> */, -/* pos 0367: 498 */ 0xE3 /* 'c' -> */, -/* pos 0368: 499 */ 0xF4 /* 't' -> */, -/* pos 0369: 500 */ 0xAD /* '-' -> */, -/* pos 036a: 501 */ 0xF4 /* 't' -> */, -/* pos 036b: 502 */ 0xF2 /* 'r' -> */, -/* pos 036c: 503 */ 0xE1 /* 'a' -> */, -/* pos 036d: 504 */ 0xEE /* 'n' -> */, -/* pos 036e: 505 */ 0xF3 /* 's' -> */, -/* pos 036f: 506 */ 0xF0 /* 'p' -> */, -/* pos 0370: 507 */ 0xEF /* 'o' -> */, -/* pos 0371: 508 */ 0xF2 /* 'r' -> */, -/* pos 0372: 509 */ 0xF4 /* 't' -> */, -/* pos 0373: 510 */ 0xAD /* '-' -> */, -/* pos 0374: 511 */ 0xF3 /* 's' -> */, -/* pos 0375: 512 */ 0xE5 /* 'e' -> */, -/* pos 0376: 513 */ 0xE3 /* 'c' -> */, -/* pos 0377: 514 */ 0xF5 /* 'u' -> */, -/* pos 0378: 515 */ 0xF2 /* 'r' -> */, -/* pos 0379: 516 */ 0xE9 /* 'i' -> */, -/* pos 037a: 517 */ 0xF4 /* 't' -> */, -/* pos 037b: 518 */ 0xF9 /* 'y' -> */, -/* pos 037c: 519 */ 0xBA /* ':' -> */, -/* pos 037d: 520 */ 0x00, 0x39 /* - terminal marker 57 - */, -/* pos 037f: 521 */ 0xE5 /* 'e' -> */, -/* pos 0380: 522 */ 0xF2 /* 'r' -> */, -/* pos 0381: 523 */ 0xAD /* '-' -> */, -/* pos 0382: 524 */ 0xE1 /* 'a' -> */, -/* pos 0383: 525 */ 0xE7 /* 'g' -> */, -/* pos 0384: 526 */ 0xE5 /* 'e' -> */, -/* pos 0385: 527 */ 0xEE /* 'n' -> */, -/* pos 0386: 528 */ 0xF4 /* 't' -> */, -/* pos 0387: 529 */ 0xBA /* ':' -> */, -/* pos 0388: 530 */ 0x00, 0x3B /* - terminal marker 59 - */, -/* pos 038a: 531 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0391 state 532) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0396 state 536) */, - 0x08, /* fail */ -/* pos 0391: 532 */ 0xF2 /* 'r' -> */, -/* pos 0392: 533 */ 0xF9 /* 'y' -> */, -/* pos 0393: 534 */ 0xBA /* ':' -> */, -/* pos 0394: 535 */ 0x00, 0x3C /* - terminal marker 60 - */, -/* pos 0396: 536 */ 0xE1 /* 'a' -> */, -/* pos 0397: 537 */ 0xBA /* ':' -> */, -/* pos 0398: 538 */ 0x00, 0x3D /* - terminal marker 61 - */, -/* pos 039a: 539 */ 0xF7 /* 'w' -> */, -/* pos 039b: 540 */ 0xF7 /* 'w' -> */, -/* pos 039c: 541 */ 0xAD /* '-' -> */, -/* pos 039d: 542 */ 0xE1 /* 'a' -> */, -/* pos 039e: 543 */ 0xF5 /* 'u' -> */, -/* pos 039f: 544 */ 0xF4 /* 't' -> */, -/* pos 03a0: 545 */ 0xE8 /* 'h' -> */, -/* pos 03a1: 546 */ 0xE5 /* 'e' -> */, -/* pos 03a2: 547 */ 0xEE /* 'n' -> */, -/* pos 03a3: 548 */ 0xF4 /* 't' -> */, -/* pos 03a4: 549 */ 0xE9 /* 'i' -> */, -/* pos 03a5: 550 */ 0xE3 /* 'c' -> */, -/* pos 03a6: 551 */ 0xE1 /* 'a' -> */, -/* pos 03a7: 552 */ 0xF4 /* 't' -> */, -/* pos 03a8: 553 */ 0xE5 /* 'e' -> */, -/* pos 03a9: 554 */ 0xBA /* ':' -> */, -/* pos 03aa: 555 */ 0x00, 0x3E /* - terminal marker 62 - */, -/* pos 03ac: 556 */ 0xF4 /* 't' -> */, -/* pos 03ad: 557 */ 0xE3 /* 'c' -> */, -/* pos 03ae: 558 */ 0xE8 /* 'h' -> */, -/* pos 03af: 559 */ 0x00, 0x3F /* - terminal marker 63 - */, -/* pos 03b1: 560 */ 0xF4 /* 't' -> */, -/* pos 03b2: 561 */ 0x00, 0x40 /* - terminal marker 64 - */, -/* pos 03b4: 562 */ 0xEC /* 'l' -> */, -/* pos 03b5: 563 */ 0xE5 /* 'e' -> */, -/* pos 03b6: 564 */ 0xF4 /* 't' -> */, -/* pos 03b7: 565 */ 0xE5 /* 'e' -> */, -/* pos 03b8: 566 */ 0x00, 0x41 /* - terminal marker 65 - */, -/* pos 03ba: 567 */ 0x00, 0x43 /* - terminal marker 67 - */, -/* pos 03bc: 568 */ 0xE5 /* 'e' -> */, -/* pos 03bd: 569 */ 0xE1 /* 'a' -> */, -/* pos 03be: 570 */ 0xEC /* 'l' -> */, -/* pos 03bf: 571 */ 0xAD /* '-' -> */, -/* pos 03c0: 572 */ 0xE9 /* 'i' -> */, -/* pos 03c1: 573 */ 0xF0 /* 'p' -> */, -/* pos 03c2: 574 */ 0xBA /* ':' -> */, -/* pos 03c3: 575 */ 0x00, 0x44 /* - terminal marker 68 - */, -/* pos 03c5: 576 */ 0xBA /* ':' -> */, -/* pos 03c6: 577 */ 0x00, 0x49 /* - terminal marker 73 - */, -/* pos 03c8: 578 */ 0xEC /* 'l' -> */, -/* pos 03c9: 579 */ 0xE1 /* 'a' -> */, -/* pos 03ca: 580 */ 0xF9 /* 'y' -> */, -/* pos 03cb: 581 */ 0xAD /* '-' -> */, -/* pos 03cc: 582 */ 0xEE /* 'n' -> */, -/* pos 03cd: 583 */ 0xEF /* 'o' -> */, -/* pos 03ce: 584 */ 0xEE /* 'n' -> */, -/* pos 03cf: 585 */ 0xE3 /* 'c' -> */, -/* pos 03d0: 586 */ 0xE5 /* 'e' -> */, -/* pos 03d1: 587 */ 0xBA /* ':' -> */, -/* pos 03d2: 588 */ 0x00, 0x4A /* - terminal marker 74 - */, -/* pos 03d4: 589 */ 0xAD /* '-' -> */, -/* pos 03d5: 590 */ 0xF7 /* 'w' -> */, -/* pos 03d6: 591 */ 0xE5 /* 'e' -> */, -/* pos 03d7: 592 */ 0xE2 /* 'b' -> */, -/* pos 03d8: 593 */ 0xF3 /* 's' -> */, -/* pos 03d9: 594 */ 0xEF /* 'o' -> */, -/* pos 03da: 595 */ 0xE3 /* 'c' -> */, -/* pos 03db: 596 */ 0xEB /* 'k' -> */, -/* pos 03dc: 597 */ 0xE5 /* 'e' -> */, -/* pos 03dd: 598 */ 0xF4 /* 't' -> */, -/* pos 03de: 599 */ 0xAD /* '-' -> */, -/* pos 03df: 600 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x03F8 state 601) */, - 0x65 /* 'e' */, 0x1D, 0x00 /* (to 0x03FF state 607) */, - 0x6B /* 'k' */, 0x26, 0x00 /* (to 0x040B state 618) */, - 0x70 /* 'p' */, 0x35, 0x00 /* (to 0x041D state 625) */, - 0x61 /* 'a' */, 0x3C, 0x00 /* (to 0x0427 state 634) */, - 0x6E /* 'n' */, 0x41, 0x00 /* (to 0x042F state 641) */, - 0x76 /* 'v' */, 0x47, 0x00 /* (to 0x0438 state 648) */, - 0x6F /* 'o' */, 0x4D, 0x00 /* (to 0x0441 state 656) */, - 0x08, /* fail */ -/* pos 03f8: 601 */ 0xF2 /* 'r' -> */, -/* pos 03f9: 602 */ 0xE1 /* 'a' -> */, -/* pos 03fa: 603 */ 0xE6 /* 'f' -> */, -/* pos 03fb: 604 */ 0xF4 /* 't' -> */, -/* pos 03fc: 605 */ 0xBA /* ':' -> */, -/* pos 03fd: 606 */ 0x00, 0x07 /* - terminal marker 7 - */, -/* pos 03ff: 607 */ 0xF8 /* 'x' -> */, -/* pos 0400: 608 */ 0xF4 /* 't' -> */, -/* pos 0401: 609 */ 0xE5 /* 'e' -> */, -/* pos 0402: 610 */ 0xEE /* 'n' -> */, -/* pos 0403: 611 */ 0xF3 /* 's' -> */, -/* pos 0404: 612 */ 0xE9 /* 'i' -> */, -/* pos 0405: 613 */ 0xEF /* 'o' -> */, -/* pos 0406: 614 */ 0xEE /* 'n' -> */, -/* pos 0407: 615 */ 0xF3 /* 's' -> */, -/* pos 0408: 616 */ 0xBA /* ':' -> */, -/* pos 0409: 617 */ 0x00, 0x09 /* - terminal marker 9 - */, -/* pos 040b: 618 */ 0xE5 /* 'e' -> */, -/* pos 040c: 619 */ 0xF9 /* 'y' -> */, -/* pos 040d: 620 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0417 state 621) */, - 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x041A state 623) */, - 0x3A /* ':' */, 0x23, 0x00 /* (to 0x0436 state 647) */, - 0x08, /* fail */ -/* pos 0417: 621 */ 0xBA /* ':' -> */, -/* pos 0418: 622 */ 0x00, 0x0A /* - terminal marker 10 - */, -/* pos 041a: 623 */ 0xBA /* ':' -> */, -/* pos 041b: 624 */ 0x00, 0x0B /* - terminal marker 11 - */, -/* pos 041d: 625 */ 0xF2 /* 'r' -> */, -/* pos 041e: 626 */ 0xEF /* 'o' -> */, -/* pos 041f: 627 */ 0xF4 /* 't' -> */, -/* pos 0420: 628 */ 0xEF /* 'o' -> */, -/* pos 0421: 629 */ 0xE3 /* 'c' -> */, -/* pos 0422: 630 */ 0xEF /* 'o' -> */, -/* pos 0423: 631 */ 0xEC /* 'l' -> */, -/* pos 0424: 632 */ 0xBA /* ':' -> */, -/* pos 0425: 633 */ 0x00, 0x0C /* - terminal marker 12 - */, -/* pos 0427: 634 */ 0xE3 /* 'c' -> */, -/* pos 0428: 635 */ 0xE3 /* 'c' -> */, -/* pos 0429: 636 */ 0xE5 /* 'e' -> */, -/* pos 042a: 637 */ 0xF0 /* 'p' -> */, -/* pos 042b: 638 */ 0xF4 /* 't' -> */, -/* pos 042c: 639 */ 0xBA /* ':' -> */, -/* pos 042d: 640 */ 0x00, 0x0D /* - terminal marker 13 - */, -/* pos 042f: 641 */ 0xEF /* 'o' -> */, -/* pos 0430: 642 */ 0xEE /* 'n' -> */, -/* pos 0431: 643 */ 0xE3 /* 'c' -> */, -/* pos 0432: 644 */ 0xE5 /* 'e' -> */, -/* pos 0433: 645 */ 0xBA /* ':' -> */, -/* pos 0434: 646 */ 0x00, 0x0E /* - terminal marker 14 - */, -/* pos 0436: 647 */ 0x00, 0x1F /* - terminal marker 31 - */, -/* pos 0438: 648 */ 0xE5 /* 'e' -> */, -/* pos 0439: 649 */ 0xF2 /* 'r' -> */, -/* pos 043a: 650 */ 0xF3 /* 's' -> */, -/* pos 043b: 651 */ 0xE9 /* 'i' -> */, -/* pos 043c: 652 */ 0xEF /* 'o' -> */, -/* pos 043d: 653 */ 0xEE /* 'n' -> */, -/* pos 043e: 654 */ 0xBA /* ':' -> */, -/* pos 043f: 655 */ 0x00, 0x20 /* - terminal marker 32 - */, -/* pos 0441: 656 */ 0xF2 /* 'r' -> */, -/* pos 0442: 657 */ 0xE9 /* 'i' -> */, -/* pos 0443: 658 */ 0xE7 /* 'g' -> */, -/* pos 0444: 659 */ 0xE9 /* 'i' -> */, -/* pos 0445: 660 */ 0xEE /* 'n' -> */, -/* pos 0446: 661 */ 0xBA /* ':' -> */, -/* pos 0447: 662 */ 0x00, 0x21 /* - terminal marker 33 - */, -/* pos 0449: 663 */ 0xAD /* '-' -> */, -/* pos 044a: 664 */ 0xF3 /* 's' -> */, -/* pos 044b: 665 */ 0xE5 /* 'e' -> */, -/* pos 044c: 666 */ 0xF4 /* 't' -> */, -/* pos 044d: 667 */ 0xF4 /* 't' -> */, -/* pos 044e: 668 */ 0xE9 /* 'i' -> */, -/* pos 044f: 669 */ 0xEE /* 'n' -> */, -/* pos 0450: 670 */ 0xE7 /* 'g' -> */, -/* pos 0451: 671 */ 0xF3 /* 's' -> */, -/* pos 0452: 672 */ 0xBA /* ':' -> */, -/* pos 0453: 673 */ 0x00, 0x09 /* - terminal marker 9 - */, -/* pos 0455: 674 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0462 state 675) */, - 0x6D /* 'm' */, 0x14, 0x00 /* (to 0x046C state 684) */, - 0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0473 state 690) */, - 0x73 /* 's' */, 0x20, 0x00 /* (to 0x047E state 694) */, - 0x08, /* fail */ -/* pos 0462: 675 */ 0xF5 /* 'u' -> */, -/* pos 0463: 676 */ 0xF4 /* 't' -> */, -/* pos 0464: 677 */ 0xE8 /* 'h' -> */, -/* pos 0465: 678 */ 0xEF /* 'o' -> */, -/* pos 0466: 679 */ 0xF2 /* 'r' -> */, -/* pos 0467: 680 */ 0xE9 /* 'i' -> */, -/* pos 0468: 681 */ 0xF4 /* 't' -> */, -/* pos 0469: 682 */ 0xF9 /* 'y' -> */, -/* pos 046a: 683 */ 0x00, 0x19 /* - terminal marker 25 - */, -/* pos 046c: 684 */ 0xE5 /* 'e' -> */, -/* pos 046d: 685 */ 0xF4 /* 't' -> */, -/* pos 046e: 686 */ 0xE8 /* 'h' -> */, -/* pos 046f: 687 */ 0xEF /* 'o' -> */, -/* pos 0470: 688 */ 0xE4 /* 'd' -> */, -/* pos 0471: 689 */ 0x00, 0x1A /* - terminal marker 26 - */, -/* pos 0473: 690 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x047A state 691) */, - 0x72 /* 'r' */, 0x1B, 0x00 /* (to 0x0491 state 705) */, - 0x08, /* fail */ -/* pos 047a: 691 */ 0xF4 /* 't' -> */, -/* pos 047b: 692 */ 0xE8 /* 'h' -> */, -/* pos 047c: 693 */ 0x00, 0x1B /* - terminal marker 27 - */, -/* pos 047e: 694 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x0485 state 695) */, - 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x048B state 700) */, - 0x08, /* fail */ -/* pos 0485: 695 */ 0xE8 /* 'h' -> */, -/* pos 0486: 696 */ 0xE5 /* 'e' -> */, -/* pos 0487: 697 */ 0xED /* 'm' -> */, -/* pos 0488: 698 */ 0xE5 /* 'e' -> */, -/* pos 0489: 699 */ 0x00, 0x1C /* - terminal marker 28 - */, -/* pos 048b: 700 */ 0xE1 /* 'a' -> */, -/* pos 048c: 701 */ 0xF4 /* 't' -> */, -/* pos 048d: 702 */ 0xF5 /* 'u' -> */, -/* pos 048e: 703 */ 0xF3 /* 's' -> */, -/* pos 048f: 704 */ 0x00, 0x1D /* - terminal marker 29 - */, -/* pos 0491: 705 */ 0xEF /* 'o' -> */, -/* pos 0492: 706 */ 0xF4 /* 't' -> */, -/* pos 0493: 707 */ 0xEF /* 'o' -> */, -/* pos 0494: 708 */ 0xE3 /* 'c' -> */, -/* pos 0495: 709 */ 0xEF /* 'o' -> */, -/* pos 0496: 710 */ 0xEC /* 'l' -> */, -/* pos 0497: 711 */ 0x00, 0x4B /* - terminal marker 75 - */, -/* total size 1177 bytes */ -#endif - -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) - /* 0: 0: get */ - /* 1: 1: post */ - /* 2: 3: host: */ - /* 3: 4: connection: */ - /* 4: 5: upgrade: */ - /* 5: 6: origin: */ - /* 6: 7: sec-websocket-draft: */ - /* 7: 8: - */ - /* 8: 9: sec-websocket-extensions: */ - /* 9: 10: sec-websocket-key1: */ - /* 10: 11: sec-websocket-key2: */ - /* 11: 12: sec-websocket-protocol: */ - /* 12: 13: sec-websocket-accept: */ - /* 13: 14: sec-websocket-nonce: */ - /* 14: 15: http/1.1 */ - /* 15: 16: http2-settings: */ - /* 16: 17: accept: */ - /* 17: 19: if-modified-since: */ - /* 18: 20: if-none-match: */ - /* 19: 21: accept-encoding: */ - /* 20: 22: accept-language: */ - /* 21: 23: pragma: */ - /* 22: 24: cache-control: */ - /* 23: 25: authorization: */ - /* 24: 26: cookie: */ - /* 25: 27: content-length: */ - /* 26: 28: content-type: */ - /* 27: 29: date: */ - /* 28: 30: range: */ - /* 29: 31: referer: */ - /* 30: 32: sec-websocket-key: */ - /* 31: 33: sec-websocket-version: */ - /* 32: 34: sec-websocket-origin: */ - /* 33: 35: :authority */ - /* 34: 36: :method */ - /* 35: 37: :path */ - /* 36: 38: :scheme */ - /* 37: 39: :status */ - /* 38: 40: accept-charset: */ - /* 39: 41: accept-ranges: */ - /* 40: 42: access-control-allow-origin: */ - /* 41: 43: age: */ - /* 42: 44: allow: */ - /* 43: 45: content-disposition: */ - /* 44: 46: content-encoding: */ - /* 45: 47: content-language: */ - /* 46: 48: content-location: */ - /* 47: 49: content-range: */ - /* 48: 50: etag: */ - /* 49: 51: expect: */ - /* 50: 52: expires: */ - /* 51: 53: from: */ - /* 52: 54: if-match: */ - /* 53: 55: if-range: */ - /* 54: 56: if-unmodified-since: */ - /* 55: 57: last-modified: */ - /* 56: 58: link: */ - /* 57: 59: location: */ - /* 58: 60: max-forwards: */ - /* 59: 61: proxy-authenticate: */ - /* 60: 62: proxy-authorization: */ - /* 61: 63: refresh: */ - /* 62: 64: retry-after: */ - /* 63: 65: server: */ - /* 64: 66: set-cookie: */ - /* 65: 67: strict-transport-security: */ - /* 66: 68: transfer-encoding: */ - /* 67: 69: user-agent: */ - /* 68: 70: vary: */ - /* 69: 71: via: */ - /* 70: 72: www-authenticate: */ - /* 71: 76: uri-args */ - /* 72: 79: http/1.0 */ - /* 73: 80: x-forwarded-for: */ - /* 74: 81: connect */ - /* 75: 82: head */ - /* 76: 83: te: */ - /* 77: 84: replay-nonce: */ - /* 78: 85: :protocol */ - /* 79: 86: x-auth-token: */ - /* 80: 87: */ -/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */, - 0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */, - 0x68 /* 'h' */, 0x51, 0x00 /* (to 0x0057 state 10) */, - 0x63 /* 'c' */, 0x5D, 0x00 /* (to 0x0066 state 15) */, - 0x75 /* 'u' */, 0x7E, 0x00 /* (to 0x008A state 26) */, - 0x6F /* 'o' */, 0x8D, 0x00 /* (to 0x009C state 34) */, - 0x0D /* '.' */, 0x98, 0x00 /* (to 0x00AA state 41) */, - 0x61 /* 'a' */, 0xAD, 0x00 /* (to 0x00C2 state 51) */, - 0x69 /* 'i' */, 0xCA, 0x00 /* (to 0x00E2 state 58) */, - 0x64 /* 'd' */, 0x73, 0x01 /* (to 0x018E state 160) */, - 0x72 /* 'r' */, 0x7C, 0x01 /* (to 0x019A state 165) */, - 0x65 /* 'e' */, 0xC8, 0x01 /* (to 0x01E9 state 229) */, - 0x66 /* 'f' */, 0xE4, 0x01 /* (to 0x0208 state 245) */, - 0x6C /* 'l' */, 0x06, 0x02 /* (to 0x022D state 278) */, - 0x73 /* 's' */, 0x4B, 0x02 /* (to 0x0275 state 321) */, - 0x74 /* 't' */, 0x69, 0x02 /* (to 0x0296 state 337) */, - 0x78 /* 'x' */, 0x8A, 0x02 /* (to 0x02BA state 364) */, - 0x6D /* 'm' */, 0xFB, 0x02 /* (to 0x032E state 456) */, - 0x76 /* 'v' */, 0x54, 0x03 /* (to 0x038A state 531) */, - 0x77 /* 'w' */, 0x61, 0x03 /* (to 0x039A state 539) */, - 0x3A /* ':' */, 0x19, 0x04 /* (to 0x0455 state 674) */, - 0x08, /* fail */ -/* pos 0040: 1 */ 0xE5 /* 'e' -> */, -/* pos 0041: 2 */ 0xF4 /* 't' -> */, -/* pos 0042: 3 */ 0xA0 /* ' ' -> */, -/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, -/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */, - 0x72 /* 'r' */, 0xEC, 0x00 /* (to 0x0134 state 106) */, - 0x61 /* 'a' */, 0x61, 0x03 /* (to 0x03AC state 556) */, - 0x75 /* 'u' */, 0x63, 0x03 /* (to 0x03B1 state 560) */, - 0x08, /* fail */ -/* pos 0052: 6 */ 0xF3 /* 's' -> */, -/* pos 0053: 7 */ 0xF4 /* 't' -> */, -/* pos 0054: 8 */ 0xA0 /* ' ' -> */, -/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, -/* pos 0057: 10 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x0061 state 11) */, - 0x74 /* 't' */, 0x53, 0x00 /* (to 0x00AD state 43) */, - 0x65 /* 'e' */, 0x79, 0x02 /* (to 0x02D6 state 381) */, - 0x08, /* fail */ -/* pos 0061: 11 */ 0xF3 /* 's' -> */, -/* pos 0062: 12 */ 0xF4 /* 't' -> */, -/* pos 0063: 13 */ 0xBA /* ':' -> */, -/* pos 0064: 14 */ 0x00, 0x02 /* - terminal marker 2 - */, -/* pos 0066: 15 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x006D state 16) */, - 0x61 /* 'a' */, 0xD8, 0x00 /* (to 0x0141 state 112) */, - 0x08, /* fail */ -/* pos 006d: 16 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0074 state 17) */, - 0x6F /* 'o' */, 0xED, 0x00 /* (to 0x015D state 138) */, - 0x08, /* fail */ -/* pos 0074: 17 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x007B state 18) */, - 0x74 /* 't' */, 0xEC, 0x00 /* (to 0x0163 state 143) */, - 0x08, /* fail */ -/* pos 007b: 18 */ 0xE5 /* 'e' -> */, -/* pos 007c: 19 */ 0xE3 /* 'c' -> */, -/* pos 007d: 20 */ 0xF4 /* 't' -> */, -/* pos 007e: 21 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0085 state 22) */, - 0x20 /* ' ' */, 0x53, 0x02 /* (to 0x02D4 state 380) */, - 0x08, /* fail */ -/* pos 0085: 22 */ 0xEF /* 'o' -> */, -/* pos 0086: 23 */ 0xEE /* 'n' -> */, -/* pos 0087: 24 */ 0xBA /* ':' -> */, -/* pos 0088: 25 */ 0x00, 0x03 /* - terminal marker 3 - */, -/* pos 008a: 26 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x0094 state 27) */, - 0x72 /* 'r' */, 0x22, 0x02 /* (to 0x02AF state 355) */, - 0x73 /* 's' */, 0xEF, 0x02 /* (to 0x037F state 521) */, - 0x08, /* fail */ -/* pos 0094: 27 */ 0xE7 /* 'g' -> */, -/* pos 0095: 28 */ 0xF2 /* 'r' -> */, -/* pos 0096: 29 */ 0xE1 /* 'a' -> */, -/* pos 0097: 30 */ 0xE4 /* 'd' -> */, -/* pos 0098: 31 */ 0xE5 /* 'e' -> */, -/* pos 0099: 32 */ 0xBA /* ':' -> */, -/* pos 009a: 33 */ 0x00, 0x04 /* - terminal marker 4 - */, -/* pos 009c: 34 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x00A3 state 35) */, - 0x70 /* 'p' */, 0x48, 0x02 /* (to 0x02E7 state 396) */, - 0x08, /* fail */ -/* pos 00a3: 35 */ 0xE9 /* 'i' -> */, -/* pos 00a4: 36 */ 0xE7 /* 'g' -> */, -/* pos 00a5: 37 */ 0xE9 /* 'i' -> */, -/* pos 00a6: 38 */ 0xEE /* 'n' -> */, -/* pos 00a7: 39 */ 0xBA /* ':' -> */, -/* pos 00a8: 40 */ 0x00, 0x05 /* - terminal marker 5 - */, -/* pos 00aa: 41 */ 0x8A /* '.' -> */, -/* pos 00ab: 42 */ 0x00, 0x07 /* - terminal marker 7 - */, -/* pos 00ad: 43 */ 0xF4 /* 't' -> */, -/* pos 00ae: 44 */ 0xF0 /* 'p' -> */, -/* pos 00af: 45 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x00B6 state 46) */, - 0x32 /* '2' */, 0x97, 0x03 /* (to 0x0449 state 663) */, - 0x08, /* fail */ -/* pos 00b6: 46 */ 0xB1 /* '1' -> */, -/* pos 00b7: 47 */ 0xAE /* '.' -> */, -/* pos 00b8: 48 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x00BF state 49) */, - 0x30 /* '0' */, 0xFC, 0x01 /* (to 0x02B7 state 362) */, - 0x08, /* fail */ -/* pos 00bf: 49 */ 0xA0 /* ' ' -> */, -/* pos 00c0: 50 */ 0x00, 0x0E /* - terminal marker 14 - */, -/* pos 00c2: 51 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x00CF state 52) */, - 0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x014F state 125) */, - 0x67 /* 'g' */, 0xE7, 0x00 /* (to 0x01AF state 178) */, - 0x6C /* 'l' */, 0xE8, 0x00 /* (to 0x01B3 state 181) */, - 0x08, /* fail */ -/* pos 00cf: 52 */ 0xE3 /* 'c' -> */, -/* pos 00d0: 53 */ 0xE5 /* 'e' -> */, -/* pos 00d1: 54 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x00D8 state 55) */, - 0x73 /* 's' */, 0x1B, 0x02 /* (to 0x02EF state 403) */, - 0x08, /* fail */ -/* pos 00d8: 55 */ 0xF4 /* 't' -> */, -/* pos 00d9: 56 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x00E0 state 57) */, - 0x2D /* '-' */, 0x37, 0x00 /* (to 0x0113 state 87) */, - 0x08, /* fail */ -/* pos 00e0: 57 */ 0x00, 0x10 /* - terminal marker 16 - */, -/* pos 00e2: 58 */ 0xE6 /* 'f' -> */, -/* pos 00e3: 59 */ 0xAD /* '-' -> */, -/* pos 00e4: 60 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x00F1 state 61) */, - 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x0107 state 76) */, - 0x72 /* 'r' */, 0x2A, 0x01 /* (to 0x0214 state 255) */, - 0x75 /* 'u' */, 0x2E, 0x01 /* (to 0x021B state 261) */, - 0x08, /* fail */ -/* pos 00f1: 61 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x00F8 state 62) */, - 0x61 /* 'a' */, 0x1A, 0x01 /* (to 0x020E state 250) */, - 0x08, /* fail */ -/* pos 00f8: 62 */ 0xE4 /* 'd' -> */, -/* pos 00f9: 63 */ 0xE9 /* 'i' -> */, -/* pos 00fa: 64 */ 0xE6 /* 'f' -> */, -/* pos 00fb: 65 */ 0xE9 /* 'i' -> */, -/* pos 00fc: 66 */ 0xE5 /* 'e' -> */, -/* pos 00fd: 67 */ 0xE4 /* 'd' -> */, -/* pos 00fe: 68 */ 0xAD /* '-' -> */, -/* pos 00ff: 69 */ 0xF3 /* 's' -> */, -/* pos 0100: 70 */ 0xE9 /* 'i' -> */, -/* pos 0101: 71 */ 0xEE /* 'n' -> */, -/* pos 0102: 72 */ 0xE3 /* 'c' -> */, -/* pos 0103: 73 */ 0xE5 /* 'e' -> */, -/* pos 0104: 74 */ 0xBA /* ':' -> */, -/* pos 0105: 75 */ 0x00, 0x11 /* - terminal marker 17 - */, -/* pos 0107: 76 */ 0xEF /* 'o' -> */, -/* pos 0108: 77 */ 0xEE /* 'n' -> */, -/* pos 0109: 78 */ 0xE5 /* 'e' -> */, -/* pos 010a: 79 */ 0xAD /* '-' -> */, -/* pos 010b: 80 */ 0xED /* 'm' -> */, -/* pos 010c: 81 */ 0xE1 /* 'a' -> */, -/* pos 010d: 82 */ 0xF4 /* 't' -> */, -/* pos 010e: 83 */ 0xE3 /* 'c' -> */, -/* pos 010f: 84 */ 0xE8 /* 'h' -> */, -/* pos 0110: 85 */ 0xBA /* ':' -> */, -/* pos 0111: 86 */ 0x00, 0x12 /* - terminal marker 18 - */, -/* pos 0113: 87 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x0120 state 88) */, - 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x012A state 97) */, - 0x72 /* 'r' */, 0x8E, 0x00 /* (to 0x01A7 state 171) */, - 0x63 /* 'c' */, 0xFB, 0x01 /* (to 0x0317 state 435) */, - 0x08, /* fail */ -/* pos 0120: 88 */ 0xEE /* 'n' -> */, -/* pos 0121: 89 */ 0xE3 /* 'c' -> */, -/* pos 0122: 90 */ 0xEF /* 'o' -> */, -/* pos 0123: 91 */ 0xE4 /* 'd' -> */, -/* pos 0124: 92 */ 0xE9 /* 'i' -> */, -/* pos 0125: 93 */ 0xEE /* 'n' -> */, -/* pos 0126: 94 */ 0xE7 /* 'g' -> */, -/* pos 0127: 95 */ 0xBA /* ':' -> */, -/* pos 0128: 96 */ 0x00, 0x13 /* - terminal marker 19 - */, -/* pos 012a: 97 */ 0xE1 /* 'a' -> */, -/* pos 012b: 98 */ 0xEE /* 'n' -> */, -/* pos 012c: 99 */ 0xE7 /* 'g' -> */, -/* pos 012d: 100 */ 0xF5 /* 'u' -> */, -/* pos 012e: 101 */ 0xE1 /* 'a' -> */, -/* pos 012f: 102 */ 0xE7 /* 'g' -> */, -/* pos 0130: 103 */ 0xE5 /* 'e' -> */, -/* pos 0131: 104 */ 0xBA /* ':' -> */, -/* pos 0132: 105 */ 0x00, 0x14 /* - terminal marker 20 - */, -/* pos 0134: 106 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x013B state 107) */, - 0x6F /* 'o' */, 0x05, 0x02 /* (to 0x033C state 469) */, - 0x08, /* fail */ -/* pos 013b: 107 */ 0xE7 /* 'g' -> */, -/* pos 013c: 108 */ 0xED /* 'm' -> */, -/* pos 013d: 109 */ 0xE1 /* 'a' -> */, -/* pos 013e: 110 */ 0xBA /* ':' -> */, -/* pos 013f: 111 */ 0x00, 0x15 /* - terminal marker 21 - */, -/* pos 0141: 112 */ 0xE3 /* 'c' -> */, -/* pos 0142: 113 */ 0xE8 /* 'h' -> */, -/* pos 0143: 114 */ 0xE5 /* 'e' -> */, -/* pos 0144: 115 */ 0xAD /* '-' -> */, -/* pos 0145: 116 */ 0xE3 /* 'c' -> */, -/* pos 0146: 117 */ 0xEF /* 'o' -> */, -/* pos 0147: 118 */ 0xEE /* 'n' -> */, -/* pos 0148: 119 */ 0xF4 /* 't' -> */, -/* pos 0149: 120 */ 0xF2 /* 'r' -> */, -/* pos 014a: 121 */ 0xEF /* 'o' -> */, -/* pos 014b: 122 */ 0xEC /* 'l' -> */, -/* pos 014c: 123 */ 0xBA /* ':' -> */, -/* pos 014d: 124 */ 0x00, 0x16 /* - terminal marker 22 - */, -/* pos 014f: 125 */ 0xF4 /* 't' -> */, -/* pos 0150: 126 */ 0xE8 /* 'h' -> */, -/* pos 0151: 127 */ 0xEF /* 'o' -> */, -/* pos 0152: 128 */ 0xF2 /* 'r' -> */, -/* pos 0153: 129 */ 0xE9 /* 'i' -> */, -/* pos 0154: 130 */ 0xFA /* 'z' -> */, -/* pos 0155: 131 */ 0xE1 /* 'a' -> */, -/* pos 0156: 132 */ 0xF4 /* 't' -> */, -/* pos 0157: 133 */ 0xE9 /* 'i' -> */, -/* pos 0158: 134 */ 0xEF /* 'o' -> */, -/* pos 0159: 135 */ 0xEE /* 'n' -> */, -/* pos 015a: 136 */ 0xBA /* ':' -> */, -/* pos 015b: 137 */ 0x00, 0x17 /* - terminal marker 23 - */, -/* pos 015d: 138 */ 0xEB /* 'k' -> */, -/* pos 015e: 139 */ 0xE9 /* 'i' -> */, -/* pos 015f: 140 */ 0xE5 /* 'e' -> */, -/* pos 0160: 141 */ 0xBA /* ':' -> */, -/* pos 0161: 142 */ 0x00, 0x18 /* - terminal marker 24 - */, -/* pos 0163: 143 */ 0xE5 /* 'e' -> */, -/* pos 0164: 144 */ 0xEE /* 'n' -> */, -/* pos 0165: 145 */ 0xF4 /* 't' -> */, -/* pos 0166: 146 */ 0xAD /* '-' -> */, -/* pos 0167: 147 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0177 state 148) */, - 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0188 state 155) */, - 0x64 /* 'd' */, 0x4C, 0x00 /* (to 0x01B9 state 186) */, - 0x65 /* 'e' */, 0x56, 0x00 /* (to 0x01C6 state 198) */, - 0x72 /* 'r' */, 0x6F, 0x00 /* (to 0x01E2 state 223) */, - 0x08, /* fail */ -/* pos 0177: 148 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0181 state 149) */, - 0x61 /* 'a' */, 0x56, 0x00 /* (to 0x01D0 state 207) */, - 0x6F /* 'o' */, 0x5C, 0x00 /* (to 0x01D9 state 215) */, - 0x08, /* fail */ -/* pos 0181: 149 */ 0xEE /* 'n' -> */, -/* pos 0182: 150 */ 0xE7 /* 'g' -> */, -/* pos 0183: 151 */ 0xF4 /* 't' -> */, -/* pos 0184: 152 */ 0xE8 /* 'h' -> */, -/* pos 0185: 153 */ 0xBA /* ':' -> */, -/* pos 0186: 154 */ 0x00, 0x19 /* - terminal marker 25 - */, -/* pos 0188: 155 */ 0xF9 /* 'y' -> */, -/* pos 0189: 156 */ 0xF0 /* 'p' -> */, -/* pos 018a: 157 */ 0xE5 /* 'e' -> */, -/* pos 018b: 158 */ 0xBA /* ':' -> */, -/* pos 018c: 159 */ 0x00, 0x1A /* - terminal marker 26 - */, -/* pos 018e: 160 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0195 state 161) */, - 0x65 /* 'e' */, 0x23, 0x02 /* (to 0x03B4 state 562) */, - 0x08, /* fail */ -/* pos 0195: 161 */ 0xF4 /* 't' -> */, -/* pos 0196: 162 */ 0xE5 /* 'e' -> */, -/* pos 0197: 163 */ 0xBA /* ':' -> */, -/* pos 0198: 164 */ 0x00, 0x1B /* - terminal marker 27 - */, -/* pos 019a: 165 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01A1 state 166) */, - 0x65 /* 'e' */, 0xB6, 0x00 /* (to 0x0253 state 304) */, - 0x08, /* fail */ -/* pos 01a1: 166 */ 0xEE /* 'n' -> */, -/* pos 01a2: 167 */ 0xE7 /* 'g' -> */, -/* pos 01a3: 168 */ 0xE5 /* 'e' -> */, -/* pos 01a4: 169 */ 0xBA /* ':' -> */, -/* pos 01a5: 170 */ 0x00, 0x1C /* - terminal marker 28 - */, -/* pos 01a7: 171 */ 0xE1 /* 'a' -> */, -/* pos 01a8: 172 */ 0xEE /* 'n' -> */, -/* pos 01a9: 173 */ 0xE7 /* 'g' -> */, -/* pos 01aa: 174 */ 0xE5 /* 'e' -> */, -/* pos 01ab: 175 */ 0xF3 /* 's' -> */, -/* pos 01ac: 176 */ 0xBA /* ':' -> */, -/* pos 01ad: 177 */ 0x00, 0x27 /* - terminal marker 39 - */, -/* pos 01af: 178 */ 0xE5 /* 'e' -> */, -/* pos 01b0: 179 */ 0xBA /* ':' -> */, -/* pos 01b1: 180 */ 0x00, 0x29 /* - terminal marker 41 - */, -/* pos 01b3: 181 */ 0xEC /* 'l' -> */, -/* pos 01b4: 182 */ 0xEF /* 'o' -> */, -/* pos 01b5: 183 */ 0xF7 /* 'w' -> */, -/* pos 01b6: 184 */ 0xBA /* ':' -> */, -/* pos 01b7: 185 */ 0x00, 0x2A /* - terminal marker 42 - */, -/* pos 01b9: 186 */ 0xE9 /* 'i' -> */, -/* pos 01ba: 187 */ 0xF3 /* 's' -> */, -/* pos 01bb: 188 */ 0xF0 /* 'p' -> */, -/* pos 01bc: 189 */ 0xEF /* 'o' -> */, -/* pos 01bd: 190 */ 0xF3 /* 's' -> */, -/* pos 01be: 191 */ 0xE9 /* 'i' -> */, -/* pos 01bf: 192 */ 0xF4 /* 't' -> */, -/* pos 01c0: 193 */ 0xE9 /* 'i' -> */, -/* pos 01c1: 194 */ 0xEF /* 'o' -> */, -/* pos 01c2: 195 */ 0xEE /* 'n' -> */, -/* pos 01c3: 196 */ 0xBA /* ':' -> */, -/* pos 01c4: 197 */ 0x00, 0x2B /* - terminal marker 43 - */, -/* pos 01c6: 198 */ 0xEE /* 'n' -> */, -/* pos 01c7: 199 */ 0xE3 /* 'c' -> */, -/* pos 01c8: 200 */ 0xEF /* 'o' -> */, -/* pos 01c9: 201 */ 0xE4 /* 'd' -> */, -/* pos 01ca: 202 */ 0xE9 /* 'i' -> */, -/* pos 01cb: 203 */ 0xEE /* 'n' -> */, -/* pos 01cc: 204 */ 0xE7 /* 'g' -> */, -/* pos 01cd: 205 */ 0xBA /* ':' -> */, -/* pos 01ce: 206 */ 0x00, 0x2C /* - terminal marker 44 - */, -/* pos 01d0: 207 */ 0xEE /* 'n' -> */, -/* pos 01d1: 208 */ 0xE7 /* 'g' -> */, -/* pos 01d2: 209 */ 0xF5 /* 'u' -> */, -/* pos 01d3: 210 */ 0xE1 /* 'a' -> */, -/* pos 01d4: 211 */ 0xE7 /* 'g' -> */, -/* pos 01d5: 212 */ 0xE5 /* 'e' -> */, -/* pos 01d6: 213 */ 0xBA /* ':' -> */, -/* pos 01d7: 214 */ 0x00, 0x2D /* - terminal marker 45 - */, -/* pos 01d9: 215 */ 0xE3 /* 'c' -> */, -/* pos 01da: 216 */ 0xE1 /* 'a' -> */, -/* pos 01db: 217 */ 0xF4 /* 't' -> */, -/* pos 01dc: 218 */ 0xE9 /* 'i' -> */, -/* pos 01dd: 219 */ 0xEF /* 'o' -> */, -/* pos 01de: 220 */ 0xEE /* 'n' -> */, -/* pos 01df: 221 */ 0xBA /* ':' -> */, -/* pos 01e0: 222 */ 0x00, 0x2E /* - terminal marker 46 - */, -/* pos 01e2: 223 */ 0xE1 /* 'a' -> */, -/* pos 01e3: 224 */ 0xEE /* 'n' -> */, -/* pos 01e4: 225 */ 0xE7 /* 'g' -> */, -/* pos 01e5: 226 */ 0xE5 /* 'e' -> */, -/* pos 01e6: 227 */ 0xBA /* ':' -> */, -/* pos 01e7: 228 */ 0x00, 0x2F /* - terminal marker 47 - */, -/* pos 01e9: 229 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x01F0 state 230) */, - 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x01F5 state 234) */, - 0x08, /* fail */ -/* pos 01f0: 230 */ 0xE1 /* 'a' -> */, -/* pos 01f1: 231 */ 0xE7 /* 'g' -> */, -/* pos 01f2: 232 */ 0xBA /* ':' -> */, -/* pos 01f3: 233 */ 0x00, 0x30 /* - terminal marker 48 - */, -/* pos 01f5: 234 */ 0xF0 /* 'p' -> */, -/* pos 01f6: 235 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x01FD state 236) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0202 state 240) */, - 0x08, /* fail */ -/* pos 01fd: 236 */ 0xE3 /* 'c' -> */, -/* pos 01fe: 237 */ 0xF4 /* 't' -> */, -/* pos 01ff: 238 */ 0xBA /* ':' -> */, -/* pos 0200: 239 */ 0x00, 0x31 /* - terminal marker 49 - */, -/* pos 0202: 240 */ 0xF2 /* 'r' -> */, -/* pos 0203: 241 */ 0xE5 /* 'e' -> */, -/* pos 0204: 242 */ 0xF3 /* 's' -> */, -/* pos 0205: 243 */ 0xBA /* ':' -> */, -/* pos 0206: 244 */ 0x00, 0x32 /* - terminal marker 50 - */, -/* pos 0208: 245 */ 0xF2 /* 'r' -> */, -/* pos 0209: 246 */ 0xEF /* 'o' -> */, -/* pos 020a: 247 */ 0xED /* 'm' -> */, -/* pos 020b: 248 */ 0xBA /* ':' -> */, -/* pos 020c: 249 */ 0x00, 0x33 /* - terminal marker 51 - */, -/* pos 020e: 250 */ 0xF4 /* 't' -> */, -/* pos 020f: 251 */ 0xE3 /* 'c' -> */, -/* pos 0210: 252 */ 0xE8 /* 'h' -> */, -/* pos 0211: 253 */ 0xBA /* ':' -> */, -/* pos 0212: 254 */ 0x00, 0x34 /* - terminal marker 52 - */, -/* pos 0214: 255 */ 0xE1 /* 'a' -> */, -/* pos 0215: 256 */ 0xEE /* 'n' -> */, -/* pos 0216: 257 */ 0xE7 /* 'g' -> */, -/* pos 0217: 258 */ 0xE5 /* 'e' -> */, -/* pos 0218: 259 */ 0xBA /* ':' -> */, -/* pos 0219: 260 */ 0x00, 0x35 /* - terminal marker 53 - */, -/* pos 021b: 261 */ 0xEE /* 'n' -> */, -/* pos 021c: 262 */ 0xED /* 'm' -> */, -/* pos 021d: 263 */ 0xEF /* 'o' -> */, -/* pos 021e: 264 */ 0xE4 /* 'd' -> */, -/* pos 021f: 265 */ 0xE9 /* 'i' -> */, -/* pos 0220: 266 */ 0xE6 /* 'f' -> */, -/* pos 0221: 267 */ 0xE9 /* 'i' -> */, -/* pos 0222: 268 */ 0xE5 /* 'e' -> */, -/* pos 0223: 269 */ 0xE4 /* 'd' -> */, -/* pos 0224: 270 */ 0xAD /* '-' -> */, -/* pos 0225: 271 */ 0xF3 /* 's' -> */, -/* pos 0226: 272 */ 0xE9 /* 'i' -> */, -/* pos 0227: 273 */ 0xEE /* 'n' -> */, -/* pos 0228: 274 */ 0xE3 /* 'c' -> */, -/* pos 0229: 275 */ 0xE5 /* 'e' -> */, -/* pos 022a: 276 */ 0xBA /* ':' -> */, -/* pos 022b: 277 */ 0x00, 0x36 /* - terminal marker 54 - */, -/* pos 022d: 278 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0237 state 279) */, - 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x0245 state 292) */, - 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x024A state 296) */, - 0x08, /* fail */ -/* pos 0237: 279 */ 0xF3 /* 's' -> */, -/* pos 0238: 280 */ 0xF4 /* 't' -> */, -/* pos 0239: 281 */ 0xAD /* '-' -> */, -/* pos 023a: 282 */ 0xED /* 'm' -> */, -/* pos 023b: 283 */ 0xEF /* 'o' -> */, -/* pos 023c: 284 */ 0xE4 /* 'd' -> */, -/* pos 023d: 285 */ 0xE9 /* 'i' -> */, -/* pos 023e: 286 */ 0xE6 /* 'f' -> */, -/* pos 023f: 287 */ 0xE9 /* 'i' -> */, -/* pos 0240: 288 */ 0xE5 /* 'e' -> */, -/* pos 0241: 289 */ 0xE4 /* 'd' -> */, -/* pos 0242: 290 */ 0xBA /* ':' -> */, -/* pos 0243: 291 */ 0x00, 0x37 /* - terminal marker 55 - */, -/* pos 0245: 292 */ 0xEE /* 'n' -> */, -/* pos 0246: 293 */ 0xEB /* 'k' -> */, -/* pos 0247: 294 */ 0xBA /* ':' -> */, -/* pos 0248: 295 */ 0x00, 0x38 /* - terminal marker 56 - */, -/* pos 024a: 296 */ 0xE3 /* 'c' -> */, -/* pos 024b: 297 */ 0xE1 /* 'a' -> */, -/* pos 024c: 298 */ 0xF4 /* 't' -> */, -/* pos 024d: 299 */ 0xE9 /* 'i' -> */, -/* pos 024e: 300 */ 0xEF /* 'o' -> */, -/* pos 024f: 301 */ 0xEE /* 'n' -> */, -/* pos 0250: 302 */ 0xBA /* ':' -> */, -/* pos 0251: 303 */ 0x00, 0x39 /* - terminal marker 57 - */, -/* pos 0253: 304 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x025D state 305) */, - 0x74 /* 't' */, 0x14, 0x00 /* (to 0x026A state 311) */, - 0x70 /* 'p' */, 0x6F, 0x01 /* (to 0x03C8 state 578) */, - 0x08, /* fail */ -/* pos 025d: 305 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0264 state 306) */, - 0x65 /* 'e' */, 0xB1, 0x00 /* (to 0x0311 state 430) */, - 0x08, /* fail */ -/* pos 0264: 306 */ 0xE5 /* 'e' -> */, -/* pos 0265: 307 */ 0xF3 /* 's' -> */, -/* pos 0266: 308 */ 0xE8 /* 'h' -> */, -/* pos 0267: 309 */ 0xBA /* ':' -> */, -/* pos 0268: 310 */ 0x00, 0x3D /* - terminal marker 61 - */, -/* pos 026a: 311 */ 0xF2 /* 'r' -> */, -/* pos 026b: 312 */ 0xF9 /* 'y' -> */, -/* pos 026c: 313 */ 0xAD /* '-' -> */, -/* pos 026d: 314 */ 0xE1 /* 'a' -> */, -/* pos 026e: 315 */ 0xE6 /* 'f' -> */, -/* pos 026f: 316 */ 0xF4 /* 't' -> */, -/* pos 0270: 317 */ 0xE5 /* 'e' -> */, -/* pos 0271: 318 */ 0xF2 /* 'r' -> */, -/* pos 0272: 319 */ 0xBA /* ':' -> */, -/* pos 0273: 320 */ 0x00, 0x3E /* - terminal marker 62 - */, -/* pos 0275: 321 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x027C state 322) */, - 0x74 /* 't' */, 0xED, 0x00 /* (to 0x0365 state 496) */, - 0x08, /* fail */ -/* pos 027c: 322 */ 0x72 /* 'r' */, 0x0A, 0x00 /* (to 0x0286 state 323) */, - 0x74 /* 't' */, 0x0D, 0x00 /* (to 0x028C state 328) */, - 0x63 /* 'c' */, 0x52, 0x01 /* (to 0x03D4 state 589) */, - 0x08, /* fail */ -/* pos 0286: 323 */ 0xF6 /* 'v' -> */, -/* pos 0287: 324 */ 0xE5 /* 'e' -> */, -/* pos 0288: 325 */ 0xF2 /* 'r' -> */, -/* pos 0289: 326 */ 0xBA /* ':' -> */, -/* pos 028a: 327 */ 0x00, 0x3F /* - terminal marker 63 - */, -/* pos 028c: 328 */ 0xAD /* '-' -> */, -/* pos 028d: 329 */ 0xE3 /* 'c' -> */, -/* pos 028e: 330 */ 0xEF /* 'o' -> */, -/* pos 028f: 331 */ 0xEF /* 'o' -> */, -/* pos 0290: 332 */ 0xEB /* 'k' -> */, -/* pos 0291: 333 */ 0xE9 /* 'i' -> */, -/* pos 0292: 334 */ 0xE5 /* 'e' -> */, -/* pos 0293: 335 */ 0xBA /* ':' -> */, -/* pos 0294: 336 */ 0x00, 0x40 /* - terminal marker 64 - */, -/* pos 0296: 337 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x029D state 338) */, - 0x65 /* 'e' */, 0x2C, 0x01 /* (to 0x03C5 state 576) */, - 0x08, /* fail */ -/* pos 029d: 338 */ 0xE1 /* 'a' -> */, -/* pos 029e: 339 */ 0xEE /* 'n' -> */, -/* pos 029f: 340 */ 0xF3 /* 's' -> */, -/* pos 02a0: 341 */ 0xE6 /* 'f' -> */, -/* pos 02a1: 342 */ 0xE5 /* 'e' -> */, -/* pos 02a2: 343 */ 0xF2 /* 'r' -> */, -/* pos 02a3: 344 */ 0xAD /* '-' -> */, -/* pos 02a4: 345 */ 0xE5 /* 'e' -> */, -/* pos 02a5: 346 */ 0xEE /* 'n' -> */, -/* pos 02a6: 347 */ 0xE3 /* 'c' -> */, -/* pos 02a7: 348 */ 0xEF /* 'o' -> */, -/* pos 02a8: 349 */ 0xE4 /* 'd' -> */, -/* pos 02a9: 350 */ 0xE9 /* 'i' -> */, -/* pos 02aa: 351 */ 0xEE /* 'n' -> */, -/* pos 02ab: 352 */ 0xE7 /* 'g' -> */, -/* pos 02ac: 353 */ 0xBA /* ':' -> */, -/* pos 02ad: 354 */ 0x00, 0x42 /* - terminal marker 66 - */, -/* pos 02af: 355 */ 0xE9 /* 'i' -> */, -/* pos 02b0: 356 */ 0xAD /* '-' -> */, -/* pos 02b1: 357 */ 0xE1 /* 'a' -> */, -/* pos 02b2: 358 */ 0xF2 /* 'r' -> */, -/* pos 02b3: 359 */ 0xE7 /* 'g' -> */, -/* pos 02b4: 360 */ 0xF3 /* 's' -> */, -/* pos 02b5: 361 */ 0x00, 0x47 /* - terminal marker 71 - */, -/* pos 02b7: 362 */ 0xA0 /* ' ' -> */, -/* pos 02b8: 363 */ 0x00, 0x48 /* - terminal marker 72 - */, -/* pos 02ba: 364 */ 0xAD /* '-' -> */, -/* pos 02bb: 365 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x02C5 state 366) */, - 0x61 /* 'a' */, 0x1D, 0x00 /* (to 0x02DB state 385) */, - 0x72 /* 'r' */, 0xFB, 0x00 /* (to 0x03BC state 568) */, - 0x08, /* fail */ -/* pos 02c5: 366 */ 0xEF /* 'o' -> */, -/* pos 02c6: 367 */ 0xF2 /* 'r' -> */, -/* pos 02c7: 368 */ 0xF7 /* 'w' -> */, -/* pos 02c8: 369 */ 0xE1 /* 'a' -> */, -/* pos 02c9: 370 */ 0xF2 /* 'r' -> */, -/* pos 02ca: 371 */ 0xE4 /* 'd' -> */, -/* pos 02cb: 372 */ 0xE5 /* 'e' -> */, -/* pos 02cc: 373 */ 0xE4 /* 'd' -> */, -/* pos 02cd: 374 */ 0xAD /* '-' -> */, -/* pos 02ce: 375 */ 0xE6 /* 'f' -> */, -/* pos 02cf: 376 */ 0xEF /* 'o' -> */, -/* pos 02d0: 377 */ 0xF2 /* 'r' -> */, -/* pos 02d1: 378 */ 0xBA /* ':' -> */, -/* pos 02d2: 379 */ 0x00, 0x49 /* - terminal marker 73 - */, -/* pos 02d4: 380 */ 0x00, 0x4A /* - terminal marker 74 - */, -/* pos 02d6: 381 */ 0xE1 /* 'a' -> */, -/* pos 02d7: 382 */ 0xE4 /* 'd' -> */, -/* pos 02d8: 383 */ 0xA0 /* ' ' -> */, -/* pos 02d9: 384 */ 0x00, 0x4B /* - terminal marker 75 - */, -/* pos 02db: 385 */ 0xF5 /* 'u' -> */, -/* pos 02dc: 386 */ 0xF4 /* 't' -> */, -/* pos 02dd: 387 */ 0xE8 /* 'h' -> */, -/* pos 02de: 388 */ 0xAD /* '-' -> */, -/* pos 02df: 389 */ 0xF4 /* 't' -> */, -/* pos 02e0: 390 */ 0xEF /* 'o' -> */, -/* pos 02e1: 391 */ 0xEB /* 'k' -> */, -/* pos 02e2: 392 */ 0xE5 /* 'e' -> */, -/* pos 02e3: 393 */ 0xEE /* 'n' -> */, -/* pos 02e4: 394 */ 0xBA /* ':' -> */, -/* pos 02e5: 395 */ 0x00, 0x4F /* - terminal marker 79 - */, -/* pos 02e7: 396 */ 0xF4 /* 't' -> */, -/* pos 02e8: 397 */ 0xE9 /* 'i' -> */, -/* pos 02e9: 398 */ 0xEF /* 'o' -> */, -/* pos 02ea: 399 */ 0xEE /* 'n' -> */, -/* pos 02eb: 400 */ 0xF3 /* 's' -> */, -/* pos 02ec: 401 */ 0xA0 /* ' ' -> */, -/* pos 02ed: 402 */ 0x00, 0x02 /* - terminal marker 2 - */, -/* pos 02ef: 403 */ 0xF3 /* 's' -> */, -/* pos 02f0: 404 */ 0xAD /* '-' -> */, -/* pos 02f1: 405 */ 0xE3 /* 'c' -> */, -/* pos 02f2: 406 */ 0xEF /* 'o' -> */, -/* pos 02f3: 407 */ 0xEE /* 'n' -> */, -/* pos 02f4: 408 */ 0xF4 /* 't' -> */, -/* pos 02f5: 409 */ 0xF2 /* 'r' -> */, -/* pos 02f6: 410 */ 0xEF /* 'o' -> */, -/* pos 02f7: 411 */ 0xEC /* 'l' -> */, -/* pos 02f8: 412 */ 0xAD /* '-' -> */, -/* pos 02f9: 413 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0300 state 414) */, - 0x61 /* 'a' */, 0x24, 0x00 /* (to 0x0320 state 443) */, - 0x08, /* fail */ -/* pos 0300: 414 */ 0xE5 /* 'e' -> */, -/* pos 0301: 415 */ 0xF1 /* 'q' -> */, -/* pos 0302: 416 */ 0xF5 /* 'u' -> */, -/* pos 0303: 417 */ 0xE5 /* 'e' -> */, -/* pos 0304: 418 */ 0xF3 /* 's' -> */, -/* pos 0305: 419 */ 0xF4 /* 't' -> */, -/* pos 0306: 420 */ 0xAD /* '-' -> */, -/* pos 0307: 421 */ 0xE8 /* 'h' -> */, -/* pos 0308: 422 */ 0xE5 /* 'e' -> */, -/* pos 0309: 423 */ 0xE1 /* 'a' -> */, -/* pos 030a: 424 */ 0xE4 /* 'd' -> */, -/* pos 030b: 425 */ 0xE5 /* 'e' -> */, -/* pos 030c: 426 */ 0xF2 /* 'r' -> */, -/* pos 030d: 427 */ 0xF3 /* 's' -> */, -/* pos 030e: 428 */ 0xBA /* ':' -> */, -/* pos 030f: 429 */ 0x00, 0x0B /* - terminal marker 11 - */, -/* pos 0311: 430 */ 0xF2 /* 'r' -> */, -/* pos 0312: 431 */ 0xE5 /* 'e' -> */, -/* pos 0313: 432 */ 0xF2 /* 'r' -> */, -/* pos 0314: 433 */ 0xBA /* ':' -> */, -/* pos 0315: 434 */ 0x00, 0x1D /* - terminal marker 29 - */, -/* pos 0317: 435 */ 0xE8 /* 'h' -> */, -/* pos 0318: 436 */ 0xE1 /* 'a' -> */, -/* pos 0319: 437 */ 0xF2 /* 'r' -> */, -/* pos 031a: 438 */ 0xF3 /* 's' -> */, -/* pos 031b: 439 */ 0xE5 /* 'e' -> */, -/* pos 031c: 440 */ 0xF4 /* 't' -> */, -/* pos 031d: 441 */ 0xBA /* ':' -> */, -/* pos 031e: 442 */ 0x00, 0x26 /* - terminal marker 38 - */, -/* pos 0320: 443 */ 0xEC /* 'l' -> */, -/* pos 0321: 444 */ 0xEC /* 'l' -> */, -/* pos 0322: 445 */ 0xEF /* 'o' -> */, -/* pos 0323: 446 */ 0xF7 /* 'w' -> */, -/* pos 0324: 447 */ 0xAD /* '-' -> */, -/* pos 0325: 448 */ 0xEF /* 'o' -> */, -/* pos 0326: 449 */ 0xF2 /* 'r' -> */, -/* pos 0327: 450 */ 0xE9 /* 'i' -> */, -/* pos 0328: 451 */ 0xE7 /* 'g' -> */, -/* pos 0329: 452 */ 0xE9 /* 'i' -> */, -/* pos 032a: 453 */ 0xEE /* 'n' -> */, -/* pos 032b: 454 */ 0xBA /* ':' -> */, -/* pos 032c: 455 */ 0x00, 0x28 /* - terminal marker 40 - */, -/* pos 032e: 456 */ 0xE1 /* 'a' -> */, -/* pos 032f: 457 */ 0xF8 /* 'x' -> */, -/* pos 0330: 458 */ 0xAD /* '-' -> */, -/* pos 0331: 459 */ 0xE6 /* 'f' -> */, -/* pos 0332: 460 */ 0xEF /* 'o' -> */, -/* pos 0333: 461 */ 0xF2 /* 'r' -> */, -/* pos 0334: 462 */ 0xF7 /* 'w' -> */, -/* pos 0335: 463 */ 0xE1 /* 'a' -> */, -/* pos 0336: 464 */ 0xF2 /* 'r' -> */, -/* pos 0337: 465 */ 0xE4 /* 'd' -> */, -/* pos 0338: 466 */ 0xF3 /* 's' -> */, -/* pos 0339: 467 */ 0xBA /* ':' -> */, -/* pos 033a: 468 */ 0x00, 0x3A /* - terminal marker 58 - */, -/* pos 033c: 469 */ 0xF8 /* 'x' -> */, -/* pos 033d: 470 */ 0xF9 /* 'y' -> */, -/* pos 033e: 471 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0345 state 472) */, - 0x20 /* ' ' */, 0x79, 0x00 /* (to 0x03BA state 567) */, - 0x08, /* fail */ -/* pos 0345: 472 */ 0xE1 /* 'a' -> */, -/* pos 0346: 473 */ 0xF5 /* 'u' -> */, -/* pos 0347: 474 */ 0xF4 /* 't' -> */, -/* pos 0348: 475 */ 0xE8 /* 'h' -> */, -/* pos 0349: 476 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0350 state 477) */, - 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x035A state 486) */, - 0x08, /* fail */ -/* pos 0350: 477 */ 0xEE /* 'n' -> */, -/* pos 0351: 478 */ 0xF4 /* 't' -> */, -/* pos 0352: 479 */ 0xE9 /* 'i' -> */, -/* pos 0353: 480 */ 0xE3 /* 'c' -> */, -/* pos 0354: 481 */ 0xE1 /* 'a' -> */, -/* pos 0355: 482 */ 0xF4 /* 't' -> */, -/* pos 0356: 483 */ 0xE5 /* 'e' -> */, -/* pos 0357: 484 */ 0xBA /* ':' -> */, -/* pos 0358: 485 */ 0x00, 0x3B /* - terminal marker 59 - */, -/* pos 035a: 486 */ 0xF2 /* 'r' -> */, -/* pos 035b: 487 */ 0xE9 /* 'i' -> */, -/* pos 035c: 488 */ 0xFA /* 'z' -> */, -/* pos 035d: 489 */ 0xE1 /* 'a' -> */, -/* pos 035e: 490 */ 0xF4 /* 't' -> */, -/* pos 035f: 491 */ 0xE9 /* 'i' -> */, -/* pos 0360: 492 */ 0xEF /* 'o' -> */, -/* pos 0361: 493 */ 0xEE /* 'n' -> */, -/* pos 0362: 494 */ 0xBA /* ':' -> */, -/* pos 0363: 495 */ 0x00, 0x3C /* - terminal marker 60 - */, -/* pos 0365: 496 */ 0xF2 /* 'r' -> */, -/* pos 0366: 497 */ 0xE9 /* 'i' -> */, -/* pos 0367: 498 */ 0xE3 /* 'c' -> */, -/* pos 0368: 499 */ 0xF4 /* 't' -> */, -/* pos 0369: 500 */ 0xAD /* '-' -> */, -/* pos 036a: 501 */ 0xF4 /* 't' -> */, -/* pos 036b: 502 */ 0xF2 /* 'r' -> */, -/* pos 036c: 503 */ 0xE1 /* 'a' -> */, -/* pos 036d: 504 */ 0xEE /* 'n' -> */, -/* pos 036e: 505 */ 0xF3 /* 's' -> */, -/* pos 036f: 506 */ 0xF0 /* 'p' -> */, -/* pos 0370: 507 */ 0xEF /* 'o' -> */, -/* pos 0371: 508 */ 0xF2 /* 'r' -> */, -/* pos 0372: 509 */ 0xF4 /* 't' -> */, -/* pos 0373: 510 */ 0xAD /* '-' -> */, -/* pos 0374: 511 */ 0xF3 /* 's' -> */, -/* pos 0375: 512 */ 0xE5 /* 'e' -> */, -/* pos 0376: 513 */ 0xE3 /* 'c' -> */, -/* pos 0377: 514 */ 0xF5 /* 'u' -> */, -/* pos 0378: 515 */ 0xF2 /* 'r' -> */, -/* pos 0379: 516 */ 0xE9 /* 'i' -> */, -/* pos 037a: 517 */ 0xF4 /* 't' -> */, -/* pos 037b: 518 */ 0xF9 /* 'y' -> */, -/* pos 037c: 519 */ 0xBA /* ':' -> */, -/* pos 037d: 520 */ 0x00, 0x41 /* - terminal marker 65 - */, -/* pos 037f: 521 */ 0xE5 /* 'e' -> */, -/* pos 0380: 522 */ 0xF2 /* 'r' -> */, -/* pos 0381: 523 */ 0xAD /* '-' -> */, -/* pos 0382: 524 */ 0xE1 /* 'a' -> */, -/* pos 0383: 525 */ 0xE7 /* 'g' -> */, -/* pos 0384: 526 */ 0xE5 /* 'e' -> */, -/* pos 0385: 527 */ 0xEE /* 'n' -> */, -/* pos 0386: 528 */ 0xF4 /* 't' -> */, -/* pos 0387: 529 */ 0xBA /* ':' -> */, -/* pos 0388: 530 */ 0x00, 0x43 /* - terminal marker 67 - */, -/* pos 038a: 531 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0391 state 532) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0396 state 536) */, - 0x08, /* fail */ -/* pos 0391: 532 */ 0xF2 /* 'r' -> */, -/* pos 0392: 533 */ 0xF9 /* 'y' -> */, -/* pos 0393: 534 */ 0xBA /* ':' -> */, -/* pos 0394: 535 */ 0x00, 0x44 /* - terminal marker 68 - */, -/* pos 0396: 536 */ 0xE1 /* 'a' -> */, -/* pos 0397: 537 */ 0xBA /* ':' -> */, -/* pos 0398: 538 */ 0x00, 0x45 /* - terminal marker 69 - */, -/* pos 039a: 539 */ 0xF7 /* 'w' -> */, -/* pos 039b: 540 */ 0xF7 /* 'w' -> */, -/* pos 039c: 541 */ 0xAD /* '-' -> */, -/* pos 039d: 542 */ 0xE1 /* 'a' -> */, -/* pos 039e: 543 */ 0xF5 /* 'u' -> */, -/* pos 039f: 544 */ 0xF4 /* 't' -> */, -/* pos 03a0: 545 */ 0xE8 /* 'h' -> */, -/* pos 03a1: 546 */ 0xE5 /* 'e' -> */, -/* pos 03a2: 547 */ 0xEE /* 'n' -> */, -/* pos 03a3: 548 */ 0xF4 /* 't' -> */, -/* pos 03a4: 549 */ 0xE9 /* 'i' -> */, -/* pos 03a5: 550 */ 0xE3 /* 'c' -> */, -/* pos 03a6: 551 */ 0xE1 /* 'a' -> */, -/* pos 03a7: 552 */ 0xF4 /* 't' -> */, -/* pos 03a8: 553 */ 0xE5 /* 'e' -> */, -/* pos 03a9: 554 */ 0xBA /* ':' -> */, -/* pos 03aa: 555 */ 0x00, 0x46 /* - terminal marker 70 - */, -/* pos 03ac: 556 */ 0xF4 /* 't' -> */, -/* pos 03ad: 557 */ 0xE3 /* 'c' -> */, -/* pos 03ae: 558 */ 0xE8 /* 'h' -> */, -/* pos 03af: 559 */ 0x00, 0x3F /* - terminal marker 63 - */, -/* pos 03b1: 560 */ 0xF4 /* 't' -> */, -/* pos 03b2: 561 */ 0x00, 0x40 /* - terminal marker 64 - */, -/* pos 03b4: 562 */ 0xEC /* 'l' -> */, -/* pos 03b5: 563 */ 0xE5 /* 'e' -> */, -/* pos 03b6: 564 */ 0xF4 /* 't' -> */, -/* pos 03b7: 565 */ 0xE5 /* 'e' -> */, -/* pos 03b8: 566 */ 0x00, 0x41 /* - terminal marker 65 - */, -/* pos 03ba: 567 */ 0x00, 0x43 /* - terminal marker 67 - */, -/* pos 03bc: 568 */ 0xE5 /* 'e' -> */, -/* pos 03bd: 569 */ 0xE1 /* 'a' -> */, -/* pos 03be: 570 */ 0xEC /* 'l' -> */, -/* pos 03bf: 571 */ 0xAD /* '-' -> */, -/* pos 03c0: 572 */ 0xE9 /* 'i' -> */, -/* pos 03c1: 573 */ 0xF0 /* 'p' -> */, -/* pos 03c2: 574 */ 0xBA /* ':' -> */, -/* pos 03c3: 575 */ 0x00, 0x44 /* - terminal marker 68 - */, -/* pos 03c5: 576 */ 0xBA /* ':' -> */, -/* pos 03c6: 577 */ 0x00, 0x4C /* - terminal marker 76 - */, -/* pos 03c8: 578 */ 0xEC /* 'l' -> */, -/* pos 03c9: 579 */ 0xE1 /* 'a' -> */, -/* pos 03ca: 580 */ 0xF9 /* 'y' -> */, -/* pos 03cb: 581 */ 0xAD /* '-' -> */, -/* pos 03cc: 582 */ 0xEE /* 'n' -> */, -/* pos 03cd: 583 */ 0xEF /* 'o' -> */, -/* pos 03ce: 584 */ 0xEE /* 'n' -> */, -/* pos 03cf: 585 */ 0xE3 /* 'c' -> */, -/* pos 03d0: 586 */ 0xE5 /* 'e' -> */, -/* pos 03d1: 587 */ 0xBA /* ':' -> */, -/* pos 03d2: 588 */ 0x00, 0x4D /* - terminal marker 77 - */, -/* pos 03d4: 589 */ 0xAD /* '-' -> */, -/* pos 03d5: 590 */ 0xF7 /* 'w' -> */, -/* pos 03d6: 591 */ 0xE5 /* 'e' -> */, -/* pos 03d7: 592 */ 0xE2 /* 'b' -> */, -/* pos 03d8: 593 */ 0xF3 /* 's' -> */, -/* pos 03d9: 594 */ 0xEF /* 'o' -> */, -/* pos 03da: 595 */ 0xE3 /* 'c' -> */, -/* pos 03db: 596 */ 0xEB /* 'k' -> */, -/* pos 03dc: 597 */ 0xE5 /* 'e' -> */, -/* pos 03dd: 598 */ 0xF4 /* 't' -> */, -/* pos 03de: 599 */ 0xAD /* '-' -> */, -/* pos 03df: 600 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x03F8 state 601) */, - 0x65 /* 'e' */, 0x1D, 0x00 /* (to 0x03FF state 607) */, - 0x6B /* 'k' */, 0x26, 0x00 /* (to 0x040B state 618) */, - 0x70 /* 'p' */, 0x35, 0x00 /* (to 0x041D state 625) */, - 0x61 /* 'a' */, 0x3C, 0x00 /* (to 0x0427 state 634) */, - 0x6E /* 'n' */, 0x41, 0x00 /* (to 0x042F state 641) */, - 0x76 /* 'v' */, 0x47, 0x00 /* (to 0x0438 state 648) */, - 0x6F /* 'o' */, 0x4D, 0x00 /* (to 0x0441 state 656) */, - 0x08, /* fail */ -/* pos 03f8: 601 */ 0xF2 /* 'r' -> */, -/* pos 03f9: 602 */ 0xE1 /* 'a' -> */, -/* pos 03fa: 603 */ 0xE6 /* 'f' -> */, -/* pos 03fb: 604 */ 0xF4 /* 't' -> */, -/* pos 03fc: 605 */ 0xBA /* ':' -> */, -/* pos 03fd: 606 */ 0x00, 0x06 /* - terminal marker 6 - */, -/* pos 03ff: 607 */ 0xF8 /* 'x' -> */, -/* pos 0400: 608 */ 0xF4 /* 't' -> */, -/* pos 0401: 609 */ 0xE5 /* 'e' -> */, -/* pos 0402: 610 */ 0xEE /* 'n' -> */, -/* pos 0403: 611 */ 0xF3 /* 's' -> */, -/* pos 0404: 612 */ 0xE9 /* 'i' -> */, -/* pos 0405: 613 */ 0xEF /* 'o' -> */, -/* pos 0406: 614 */ 0xEE /* 'n' -> */, -/* pos 0407: 615 */ 0xF3 /* 's' -> */, -/* pos 0408: 616 */ 0xBA /* ':' -> */, -/* pos 0409: 617 */ 0x00, 0x08 /* - terminal marker 8 - */, -/* pos 040b: 618 */ 0xE5 /* 'e' -> */, -/* pos 040c: 619 */ 0xF9 /* 'y' -> */, -/* pos 040d: 620 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0417 state 621) */, - 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x041A state 623) */, - 0x3A /* ':' */, 0x23, 0x00 /* (to 0x0436 state 647) */, - 0x08, /* fail */ -/* pos 0417: 621 */ 0xBA /* ':' -> */, -/* pos 0418: 622 */ 0x00, 0x09 /* - terminal marker 9 - */, -/* pos 041a: 623 */ 0xBA /* ':' -> */, -/* pos 041b: 624 */ 0x00, 0x0A /* - terminal marker 10 - */, -/* pos 041d: 625 */ 0xF2 /* 'r' -> */, -/* pos 041e: 626 */ 0xEF /* 'o' -> */, -/* pos 041f: 627 */ 0xF4 /* 't' -> */, -/* pos 0420: 628 */ 0xEF /* 'o' -> */, -/* pos 0421: 629 */ 0xE3 /* 'c' -> */, -/* pos 0422: 630 */ 0xEF /* 'o' -> */, -/* pos 0423: 631 */ 0xEC /* 'l' -> */, -/* pos 0424: 632 */ 0xBA /* ':' -> */, -/* pos 0425: 633 */ 0x00, 0x0B /* - terminal marker 11 - */, -/* pos 0427: 634 */ 0xE3 /* 'c' -> */, -/* pos 0428: 635 */ 0xE3 /* 'c' -> */, -/* pos 0429: 636 */ 0xE5 /* 'e' -> */, -/* pos 042a: 637 */ 0xF0 /* 'p' -> */, -/* pos 042b: 638 */ 0xF4 /* 't' -> */, -/* pos 042c: 639 */ 0xBA /* ':' -> */, -/* pos 042d: 640 */ 0x00, 0x0C /* - terminal marker 12 - */, -/* pos 042f: 641 */ 0xEF /* 'o' -> */, -/* pos 0430: 642 */ 0xEE /* 'n' -> */, -/* pos 0431: 643 */ 0xE3 /* 'c' -> */, -/* pos 0432: 644 */ 0xE5 /* 'e' -> */, -/* pos 0433: 645 */ 0xBA /* ':' -> */, -/* pos 0434: 646 */ 0x00, 0x0D /* - terminal marker 13 - */, -/* pos 0436: 647 */ 0x00, 0x1E /* - terminal marker 30 - */, -/* pos 0438: 648 */ 0xE5 /* 'e' -> */, -/* pos 0439: 649 */ 0xF2 /* 'r' -> */, -/* pos 043a: 650 */ 0xF3 /* 's' -> */, -/* pos 043b: 651 */ 0xE9 /* 'i' -> */, -/* pos 043c: 652 */ 0xEF /* 'o' -> */, -/* pos 043d: 653 */ 0xEE /* 'n' -> */, -/* pos 043e: 654 */ 0xBA /* ':' -> */, -/* pos 043f: 655 */ 0x00, 0x1F /* - terminal marker 31 - */, -/* pos 0441: 656 */ 0xF2 /* 'r' -> */, -/* pos 0442: 657 */ 0xE9 /* 'i' -> */, -/* pos 0443: 658 */ 0xE7 /* 'g' -> */, -/* pos 0444: 659 */ 0xE9 /* 'i' -> */, -/* pos 0445: 660 */ 0xEE /* 'n' -> */, -/* pos 0446: 661 */ 0xBA /* ':' -> */, -/* pos 0447: 662 */ 0x00, 0x20 /* - terminal marker 32 - */, -/* pos 0449: 663 */ 0xAD /* '-' -> */, -/* pos 044a: 664 */ 0xF3 /* 's' -> */, -/* pos 044b: 665 */ 0xE5 /* 'e' -> */, -/* pos 044c: 666 */ 0xF4 /* 't' -> */, -/* pos 044d: 667 */ 0xF4 /* 't' -> */, -/* pos 044e: 668 */ 0xE9 /* 'i' -> */, -/* pos 044f: 669 */ 0xEE /* 'n' -> */, -/* pos 0450: 670 */ 0xE7 /* 'g' -> */, -/* pos 0451: 671 */ 0xF3 /* 's' -> */, -/* pos 0452: 672 */ 0xBA /* ':' -> */, -/* pos 0453: 673 */ 0x00, 0x0F /* - terminal marker 15 - */, -/* pos 0455: 674 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0462 state 675) */, - 0x6D /* 'm' */, 0x14, 0x00 /* (to 0x046C state 684) */, - 0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0473 state 690) */, - 0x73 /* 's' */, 0x20, 0x00 /* (to 0x047E state 694) */, - 0x08, /* fail */ -/* pos 0462: 675 */ 0xF5 /* 'u' -> */, -/* pos 0463: 676 */ 0xF4 /* 't' -> */, -/* pos 0464: 677 */ 0xE8 /* 'h' -> */, -/* pos 0465: 678 */ 0xEF /* 'o' -> */, -/* pos 0466: 679 */ 0xF2 /* 'r' -> */, -/* pos 0467: 680 */ 0xE9 /* 'i' -> */, -/* pos 0468: 681 */ 0xF4 /* 't' -> */, -/* pos 0469: 682 */ 0xF9 /* 'y' -> */, -/* pos 046a: 683 */ 0x00, 0x21 /* - terminal marker 33 - */, -/* pos 046c: 684 */ 0xE5 /* 'e' -> */, -/* pos 046d: 685 */ 0xF4 /* 't' -> */, -/* pos 046e: 686 */ 0xE8 /* 'h' -> */, -/* pos 046f: 687 */ 0xEF /* 'o' -> */, -/* pos 0470: 688 */ 0xE4 /* 'd' -> */, -/* pos 0471: 689 */ 0x00, 0x22 /* - terminal marker 34 - */, -/* pos 0473: 690 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x047A state 691) */, - 0x72 /* 'r' */, 0x1B, 0x00 /* (to 0x0491 state 705) */, - 0x08, /* fail */ -/* pos 047a: 691 */ 0xF4 /* 't' -> */, -/* pos 047b: 692 */ 0xE8 /* 'h' -> */, -/* pos 047c: 693 */ 0x00, 0x23 /* - terminal marker 35 - */, -/* pos 047e: 694 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x0485 state 695) */, - 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x048B state 700) */, - 0x08, /* fail */ -/* pos 0485: 695 */ 0xE8 /* 'h' -> */, -/* pos 0486: 696 */ 0xE5 /* 'e' -> */, -/* pos 0487: 697 */ 0xED /* 'm' -> */, -/* pos 0488: 698 */ 0xE5 /* 'e' -> */, -/* pos 0489: 699 */ 0x00, 0x24 /* - terminal marker 36 - */, -/* pos 048b: 700 */ 0xE1 /* 'a' -> */, -/* pos 048c: 701 */ 0xF4 /* 't' -> */, -/* pos 048d: 702 */ 0xF5 /* 'u' -> */, -/* pos 048e: 703 */ 0xF3 /* 's' -> */, -/* pos 048f: 704 */ 0x00, 0x25 /* - terminal marker 37 - */, -/* pos 0491: 705 */ 0xEF /* 'o' -> */, -/* pos 0492: 706 */ 0xF4 /* 't' -> */, -/* pos 0493: 707 */ 0xEF /* 'o' -> */, -/* pos 0494: 708 */ 0xE3 /* 'c' -> */, -/* pos 0495: 709 */ 0xEF /* 'o' -> */, -/* pos 0496: 710 */ 0xEC /* 'l' -> */, -/* pos 0497: 711 */ 0x00, 0x4E /* - terminal marker 78 - */, -/* total size 1177 bytes */ -#endif - -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) - /* 0: 0: get */ - /* 1: 1: post */ - /* 2: 2: options */ - /* 3: 3: host: */ - /* 4: 4: connection: */ - /* 5: 5: upgrade: */ - /* 6: 6: origin: */ - /* 7: 7: sec-websocket-draft: */ - /* 8: 8: - */ - /* 9: 9: sec-websocket-extensions: */ - /* 10: 10: sec-websocket-key1: */ - /* 11: 11: sec-websocket-key2: */ - /* 12: 12: sec-websocket-protocol: */ - /* 13: 13: sec-websocket-accept: */ - /* 14: 14: sec-websocket-nonce: */ - /* 15: 15: http/1.1 */ - /* 16: 16: http2-settings: */ - /* 17: 17: accept: */ - /* 18: 18: access-control-request-headers: */ - /* 19: 19: if-modified-since: */ - /* 20: 20: if-none-match: */ - /* 21: 21: accept-encoding: */ - /* 22: 22: accept-language: */ - /* 23: 23: pragma: */ - /* 24: 24: cache-control: */ - /* 25: 25: authorization: */ - /* 26: 26: cookie: */ - /* 27: 27: content-length: */ - /* 28: 28: content-type: */ - /* 29: 29: date: */ - /* 30: 30: range: */ - /* 31: 31: referer: */ - /* 32: 32: sec-websocket-key: */ - /* 33: 33: sec-websocket-version: */ - /* 34: 34: sec-websocket-origin: */ - /* 35: 35: :authority */ - /* 36: 36: :method */ - /* 37: 37: :path */ - /* 38: 38: :scheme */ - /* 39: 39: :status */ - /* 40: 40: accept-charset: */ - /* 41: 41: accept-ranges: */ - /* 42: 42: access-control-allow-origin: */ - /* 43: 43: age: */ - /* 44: 44: allow: */ - /* 45: 45: content-disposition: */ - /* 46: 46: content-encoding: */ - /* 47: 47: content-language: */ - /* 48: 48: content-location: */ - /* 49: 49: content-range: */ - /* 50: 50: etag: */ - /* 51: 51: expect: */ - /* 52: 52: expires: */ - /* 53: 53: from: */ - /* 54: 54: if-match: */ - /* 55: 55: if-range: */ - /* 56: 56: if-unmodified-since: */ - /* 57: 57: last-modified: */ - /* 58: 58: link: */ - /* 59: 59: location: */ - /* 60: 60: max-forwards: */ - /* 61: 61: proxy-authenticate: */ - /* 62: 62: proxy-authorization: */ - /* 63: 63: refresh: */ - /* 64: 64: retry-after: */ - /* 65: 65: server: */ - /* 66: 66: set-cookie: */ - /* 67: 67: strict-transport-security: */ - /* 68: 68: transfer-encoding: */ - /* 69: 69: user-agent: */ - /* 70: 70: vary: */ - /* 71: 71: via: */ - /* 72: 72: www-authenticate: */ - /* 73: 73: patch */ - /* 74: 74: put */ - /* 75: 75: delete */ - /* 76: 76: uri-args */ - /* 77: 77: proxy */ - /* 78: 78: x-real-ip: */ - /* 79: 79: http/1.0 */ - /* 80: 80: x-forwarded-for: */ - /* 81: 81: connect */ - /* 82: 82: head */ - /* 83: 83: te: */ - /* 84: 84: replay-nonce: */ - /* 85: 85: :protocol */ - /* 86: 86: x-auth-token: */ - /* 87: 87: */ -/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */, - 0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */, - 0x68 /* 'h' */, 0x51, 0x00 /* (to 0x0057 state 10) */, - 0x63 /* 'c' */, 0x5D, 0x00 /* (to 0x0066 state 15) */, - 0x75 /* 'u' */, 0x7E, 0x00 /* (to 0x008A state 26) */, - 0x6F /* 'o' */, 0x8D, 0x00 /* (to 0x009C state 34) */, - 0x0D /* '.' */, 0x98, 0x00 /* (to 0x00AA state 41) */, - 0x61 /* 'a' */, 0xAD, 0x00 /* (to 0x00C2 state 51) */, - 0x69 /* 'i' */, 0xCA, 0x00 /* (to 0x00E2 state 58) */, - 0x64 /* 'd' */, 0x73, 0x01 /* (to 0x018E state 160) */, - 0x72 /* 'r' */, 0x7C, 0x01 /* (to 0x019A state 165) */, - 0x65 /* 'e' */, 0xC8, 0x01 /* (to 0x01E9 state 229) */, - 0x66 /* 'f' */, 0xE4, 0x01 /* (to 0x0208 state 245) */, - 0x6C /* 'l' */, 0x06, 0x02 /* (to 0x022D state 278) */, - 0x73 /* 's' */, 0x4B, 0x02 /* (to 0x0275 state 321) */, - 0x74 /* 't' */, 0x69, 0x02 /* (to 0x0296 state 337) */, - 0x78 /* 'x' */, 0x8A, 0x02 /* (to 0x02BA state 364) */, - 0x6D /* 'm' */, 0xFB, 0x02 /* (to 0x032E state 456) */, - 0x76 /* 'v' */, 0x54, 0x03 /* (to 0x038A state 531) */, - 0x77 /* 'w' */, 0x61, 0x03 /* (to 0x039A state 539) */, - 0x3A /* ':' */, 0x19, 0x04 /* (to 0x0455 state 674) */, - 0x08, /* fail */ -/* pos 0040: 1 */ 0xE5 /* 'e' -> */, -/* pos 0041: 2 */ 0xF4 /* 't' -> */, -/* pos 0042: 3 */ 0xA0 /* ' ' -> */, -/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, -/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */, - 0x72 /* 'r' */, 0xEC, 0x00 /* (to 0x0134 state 106) */, - 0x61 /* 'a' */, 0x61, 0x03 /* (to 0x03AC state 556) */, - 0x75 /* 'u' */, 0x63, 0x03 /* (to 0x03B1 state 560) */, - 0x08, /* fail */ -/* pos 0052: 6 */ 0xF3 /* 's' -> */, -/* pos 0053: 7 */ 0xF4 /* 't' -> */, -/* pos 0054: 8 */ 0xA0 /* ' ' -> */, -/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, -/* pos 0057: 10 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x0061 state 11) */, - 0x74 /* 't' */, 0x53, 0x00 /* (to 0x00AD state 43) */, - 0x65 /* 'e' */, 0x79, 0x02 /* (to 0x02D6 state 381) */, - 0x08, /* fail */ -/* pos 0061: 11 */ 0xF3 /* 's' -> */, -/* pos 0062: 12 */ 0xF4 /* 't' -> */, -/* pos 0063: 13 */ 0xBA /* ':' -> */, -/* pos 0064: 14 */ 0x00, 0x03 /* - terminal marker 3 - */, -/* pos 0066: 15 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x006D state 16) */, - 0x61 /* 'a' */, 0xD8, 0x00 /* (to 0x0141 state 112) */, - 0x08, /* fail */ -/* pos 006d: 16 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0074 state 17) */, - 0x6F /* 'o' */, 0xED, 0x00 /* (to 0x015D state 138) */, - 0x08, /* fail */ -/* pos 0074: 17 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x007B state 18) */, - 0x74 /* 't' */, 0xEC, 0x00 /* (to 0x0163 state 143) */, - 0x08, /* fail */ -/* pos 007b: 18 */ 0xE5 /* 'e' -> */, -/* pos 007c: 19 */ 0xE3 /* 'c' -> */, -/* pos 007d: 20 */ 0xF4 /* 't' -> */, -/* pos 007e: 21 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0085 state 22) */, - 0x20 /* ' ' */, 0x53, 0x02 /* (to 0x02D4 state 380) */, - 0x08, /* fail */ -/* pos 0085: 22 */ 0xEF /* 'o' -> */, -/* pos 0086: 23 */ 0xEE /* 'n' -> */, -/* pos 0087: 24 */ 0xBA /* ':' -> */, -/* pos 0088: 25 */ 0x00, 0x04 /* - terminal marker 4 - */, -/* pos 008a: 26 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x0094 state 27) */, - 0x72 /* 'r' */, 0x22, 0x02 /* (to 0x02AF state 355) */, - 0x73 /* 's' */, 0xEF, 0x02 /* (to 0x037F state 521) */, - 0x08, /* fail */ -/* pos 0094: 27 */ 0xE7 /* 'g' -> */, -/* pos 0095: 28 */ 0xF2 /* 'r' -> */, -/* pos 0096: 29 */ 0xE1 /* 'a' -> */, -/* pos 0097: 30 */ 0xE4 /* 'd' -> */, -/* pos 0098: 31 */ 0xE5 /* 'e' -> */, -/* pos 0099: 32 */ 0xBA /* ':' -> */, -/* pos 009a: 33 */ 0x00, 0x05 /* - terminal marker 5 - */, -/* pos 009c: 34 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x00A3 state 35) */, - 0x70 /* 'p' */, 0x48, 0x02 /* (to 0x02E7 state 396) */, - 0x08, /* fail */ -/* pos 00a3: 35 */ 0xE9 /* 'i' -> */, -/* pos 00a4: 36 */ 0xE7 /* 'g' -> */, -/* pos 00a5: 37 */ 0xE9 /* 'i' -> */, -/* pos 00a6: 38 */ 0xEE /* 'n' -> */, -/* pos 00a7: 39 */ 0xBA /* ':' -> */, -/* pos 00a8: 40 */ 0x00, 0x06 /* - terminal marker 6 - */, -/* pos 00aa: 41 */ 0x8A /* '.' -> */, -/* pos 00ab: 42 */ 0x00, 0x08 /* - terminal marker 8 - */, -/* pos 00ad: 43 */ 0xF4 /* 't' -> */, -/* pos 00ae: 44 */ 0xF0 /* 'p' -> */, -/* pos 00af: 45 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x00B6 state 46) */, - 0x32 /* '2' */, 0x97, 0x03 /* (to 0x0449 state 663) */, - 0x08, /* fail */ -/* pos 00b6: 46 */ 0xB1 /* '1' -> */, -/* pos 00b7: 47 */ 0xAE /* '.' -> */, -/* pos 00b8: 48 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x00BF state 49) */, - 0x30 /* '0' */, 0xFC, 0x01 /* (to 0x02B7 state 362) */, - 0x08, /* fail */ -/* pos 00bf: 49 */ 0xA0 /* ' ' -> */, -/* pos 00c0: 50 */ 0x00, 0x0F /* - terminal marker 15 - */, -/* pos 00c2: 51 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x00CF state 52) */, - 0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x014F state 125) */, - 0x67 /* 'g' */, 0xE7, 0x00 /* (to 0x01AF state 178) */, - 0x6C /* 'l' */, 0xE8, 0x00 /* (to 0x01B3 state 181) */, - 0x08, /* fail */ -/* pos 00cf: 52 */ 0xE3 /* 'c' -> */, -/* pos 00d0: 53 */ 0xE5 /* 'e' -> */, -/* pos 00d1: 54 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x00D8 state 55) */, - 0x73 /* 's' */, 0x1B, 0x02 /* (to 0x02EF state 403) */, - 0x08, /* fail */ -/* pos 00d8: 55 */ 0xF4 /* 't' -> */, -/* pos 00d9: 56 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x00E0 state 57) */, - 0x2D /* '-' */, 0x37, 0x00 /* (to 0x0113 state 87) */, - 0x08, /* fail */ -/* pos 00e0: 57 */ 0x00, 0x11 /* - terminal marker 17 - */, -/* pos 00e2: 58 */ 0xE6 /* 'f' -> */, -/* pos 00e3: 59 */ 0xAD /* '-' -> */, -/* pos 00e4: 60 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x00F1 state 61) */, - 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x0107 state 76) */, - 0x72 /* 'r' */, 0x2A, 0x01 /* (to 0x0214 state 255) */, - 0x75 /* 'u' */, 0x2E, 0x01 /* (to 0x021B state 261) */, - 0x08, /* fail */ -/* pos 00f1: 61 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x00F8 state 62) */, - 0x61 /* 'a' */, 0x1A, 0x01 /* (to 0x020E state 250) */, - 0x08, /* fail */ -/* pos 00f8: 62 */ 0xE4 /* 'd' -> */, -/* pos 00f9: 63 */ 0xE9 /* 'i' -> */, -/* pos 00fa: 64 */ 0xE6 /* 'f' -> */, -/* pos 00fb: 65 */ 0xE9 /* 'i' -> */, -/* pos 00fc: 66 */ 0xE5 /* 'e' -> */, -/* pos 00fd: 67 */ 0xE4 /* 'd' -> */, -/* pos 00fe: 68 */ 0xAD /* '-' -> */, -/* pos 00ff: 69 */ 0xF3 /* 's' -> */, -/* pos 0100: 70 */ 0xE9 /* 'i' -> */, -/* pos 0101: 71 */ 0xEE /* 'n' -> */, -/* pos 0102: 72 */ 0xE3 /* 'c' -> */, -/* pos 0103: 73 */ 0xE5 /* 'e' -> */, -/* pos 0104: 74 */ 0xBA /* ':' -> */, -/* pos 0105: 75 */ 0x00, 0x13 /* - terminal marker 19 - */, -/* pos 0107: 76 */ 0xEF /* 'o' -> */, -/* pos 0108: 77 */ 0xEE /* 'n' -> */, -/* pos 0109: 78 */ 0xE5 /* 'e' -> */, -/* pos 010a: 79 */ 0xAD /* '-' -> */, -/* pos 010b: 80 */ 0xED /* 'm' -> */, -/* pos 010c: 81 */ 0xE1 /* 'a' -> */, -/* pos 010d: 82 */ 0xF4 /* 't' -> */, -/* pos 010e: 83 */ 0xE3 /* 'c' -> */, -/* pos 010f: 84 */ 0xE8 /* 'h' -> */, -/* pos 0110: 85 */ 0xBA /* ':' -> */, -/* pos 0111: 86 */ 0x00, 0x14 /* - terminal marker 20 - */, -/* pos 0113: 87 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x0120 state 88) */, - 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x012A state 97) */, - 0x72 /* 'r' */, 0x8E, 0x00 /* (to 0x01A7 state 171) */, - 0x63 /* 'c' */, 0xFB, 0x01 /* (to 0x0317 state 435) */, - 0x08, /* fail */ -/* pos 0120: 88 */ 0xEE /* 'n' -> */, -/* pos 0121: 89 */ 0xE3 /* 'c' -> */, -/* pos 0122: 90 */ 0xEF /* 'o' -> */, -/* pos 0123: 91 */ 0xE4 /* 'd' -> */, -/* pos 0124: 92 */ 0xE9 /* 'i' -> */, -/* pos 0125: 93 */ 0xEE /* 'n' -> */, -/* pos 0126: 94 */ 0xE7 /* 'g' -> */, -/* pos 0127: 95 */ 0xBA /* ':' -> */, -/* pos 0128: 96 */ 0x00, 0x15 /* - terminal marker 21 - */, -/* pos 012a: 97 */ 0xE1 /* 'a' -> */, -/* pos 012b: 98 */ 0xEE /* 'n' -> */, -/* pos 012c: 99 */ 0xE7 /* 'g' -> */, -/* pos 012d: 100 */ 0xF5 /* 'u' -> */, -/* pos 012e: 101 */ 0xE1 /* 'a' -> */, -/* pos 012f: 102 */ 0xE7 /* 'g' -> */, -/* pos 0130: 103 */ 0xE5 /* 'e' -> */, -/* pos 0131: 104 */ 0xBA /* ':' -> */, -/* pos 0132: 105 */ 0x00, 0x16 /* - terminal marker 22 - */, -/* pos 0134: 106 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x013B state 107) */, - 0x6F /* 'o' */, 0x05, 0x02 /* (to 0x033C state 469) */, - 0x08, /* fail */ -/* pos 013b: 107 */ 0xE7 /* 'g' -> */, -/* pos 013c: 108 */ 0xED /* 'm' -> */, -/* pos 013d: 109 */ 0xE1 /* 'a' -> */, -/* pos 013e: 110 */ 0xBA /* ':' -> */, -/* pos 013f: 111 */ 0x00, 0x17 /* - terminal marker 23 - */, -/* pos 0141: 112 */ 0xE3 /* 'c' -> */, -/* pos 0142: 113 */ 0xE8 /* 'h' -> */, -/* pos 0143: 114 */ 0xE5 /* 'e' -> */, -/* pos 0144: 115 */ 0xAD /* '-' -> */, -/* pos 0145: 116 */ 0xE3 /* 'c' -> */, -/* pos 0146: 117 */ 0xEF /* 'o' -> */, -/* pos 0147: 118 */ 0xEE /* 'n' -> */, -/* pos 0148: 119 */ 0xF4 /* 't' -> */, -/* pos 0149: 120 */ 0xF2 /* 'r' -> */, -/* pos 014a: 121 */ 0xEF /* 'o' -> */, -/* pos 014b: 122 */ 0xEC /* 'l' -> */, -/* pos 014c: 123 */ 0xBA /* ':' -> */, -/* pos 014d: 124 */ 0x00, 0x18 /* - terminal marker 24 - */, -/* pos 014f: 125 */ 0xF4 /* 't' -> */, -/* pos 0150: 126 */ 0xE8 /* 'h' -> */, -/* pos 0151: 127 */ 0xEF /* 'o' -> */, -/* pos 0152: 128 */ 0xF2 /* 'r' -> */, -/* pos 0153: 129 */ 0xE9 /* 'i' -> */, -/* pos 0154: 130 */ 0xFA /* 'z' -> */, -/* pos 0155: 131 */ 0xE1 /* 'a' -> */, -/* pos 0156: 132 */ 0xF4 /* 't' -> */, -/* pos 0157: 133 */ 0xE9 /* 'i' -> */, -/* pos 0158: 134 */ 0xEF /* 'o' -> */, -/* pos 0159: 135 */ 0xEE /* 'n' -> */, -/* pos 015a: 136 */ 0xBA /* ':' -> */, -/* pos 015b: 137 */ 0x00, 0x19 /* - terminal marker 25 - */, -/* pos 015d: 138 */ 0xEB /* 'k' -> */, -/* pos 015e: 139 */ 0xE9 /* 'i' -> */, -/* pos 015f: 140 */ 0xE5 /* 'e' -> */, -/* pos 0160: 141 */ 0xBA /* ':' -> */, -/* pos 0161: 142 */ 0x00, 0x1A /* - terminal marker 26 - */, -/* pos 0163: 143 */ 0xE5 /* 'e' -> */, -/* pos 0164: 144 */ 0xEE /* 'n' -> */, -/* pos 0165: 145 */ 0xF4 /* 't' -> */, -/* pos 0166: 146 */ 0xAD /* '-' -> */, -/* pos 0167: 147 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0177 state 148) */, - 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0188 state 155) */, - 0x64 /* 'd' */, 0x4C, 0x00 /* (to 0x01B9 state 186) */, - 0x65 /* 'e' */, 0x56, 0x00 /* (to 0x01C6 state 198) */, - 0x72 /* 'r' */, 0x6F, 0x00 /* (to 0x01E2 state 223) */, - 0x08, /* fail */ -/* pos 0177: 148 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0181 state 149) */, - 0x61 /* 'a' */, 0x56, 0x00 /* (to 0x01D0 state 207) */, - 0x6F /* 'o' */, 0x5C, 0x00 /* (to 0x01D9 state 215) */, - 0x08, /* fail */ -/* pos 0181: 149 */ 0xEE /* 'n' -> */, -/* pos 0182: 150 */ 0xE7 /* 'g' -> */, -/* pos 0183: 151 */ 0xF4 /* 't' -> */, -/* pos 0184: 152 */ 0xE8 /* 'h' -> */, -/* pos 0185: 153 */ 0xBA /* ':' -> */, -/* pos 0186: 154 */ 0x00, 0x1B /* - terminal marker 27 - */, -/* pos 0188: 155 */ 0xF9 /* 'y' -> */, -/* pos 0189: 156 */ 0xF0 /* 'p' -> */, -/* pos 018a: 157 */ 0xE5 /* 'e' -> */, -/* pos 018b: 158 */ 0xBA /* ':' -> */, -/* pos 018c: 159 */ 0x00, 0x1C /* - terminal marker 28 - */, -/* pos 018e: 160 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0195 state 161) */, - 0x65 /* 'e' */, 0x23, 0x02 /* (to 0x03B4 state 562) */, - 0x08, /* fail */ -/* pos 0195: 161 */ 0xF4 /* 't' -> */, -/* pos 0196: 162 */ 0xE5 /* 'e' -> */, -/* pos 0197: 163 */ 0xBA /* ':' -> */, -/* pos 0198: 164 */ 0x00, 0x1D /* - terminal marker 29 - */, -/* pos 019a: 165 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01A1 state 166) */, - 0x65 /* 'e' */, 0xB6, 0x00 /* (to 0x0253 state 304) */, - 0x08, /* fail */ -/* pos 01a1: 166 */ 0xEE /* 'n' -> */, -/* pos 01a2: 167 */ 0xE7 /* 'g' -> */, -/* pos 01a3: 168 */ 0xE5 /* 'e' -> */, -/* pos 01a4: 169 */ 0xBA /* ':' -> */, -/* pos 01a5: 170 */ 0x00, 0x1E /* - terminal marker 30 - */, -/* pos 01a7: 171 */ 0xE1 /* 'a' -> */, -/* pos 01a8: 172 */ 0xEE /* 'n' -> */, -/* pos 01a9: 173 */ 0xE7 /* 'g' -> */, -/* pos 01aa: 174 */ 0xE5 /* 'e' -> */, -/* pos 01ab: 175 */ 0xF3 /* 's' -> */, -/* pos 01ac: 176 */ 0xBA /* ':' -> */, -/* pos 01ad: 177 */ 0x00, 0x29 /* - terminal marker 41 - */, -/* pos 01af: 178 */ 0xE5 /* 'e' -> */, -/* pos 01b0: 179 */ 0xBA /* ':' -> */, -/* pos 01b1: 180 */ 0x00, 0x2B /* - terminal marker 43 - */, -/* pos 01b3: 181 */ 0xEC /* 'l' -> */, -/* pos 01b4: 182 */ 0xEF /* 'o' -> */, -/* pos 01b5: 183 */ 0xF7 /* 'w' -> */, -/* pos 01b6: 184 */ 0xBA /* ':' -> */, -/* pos 01b7: 185 */ 0x00, 0x2C /* - terminal marker 44 - */, -/* pos 01b9: 186 */ 0xE9 /* 'i' -> */, -/* pos 01ba: 187 */ 0xF3 /* 's' -> */, -/* pos 01bb: 188 */ 0xF0 /* 'p' -> */, -/* pos 01bc: 189 */ 0xEF /* 'o' -> */, -/* pos 01bd: 190 */ 0xF3 /* 's' -> */, -/* pos 01be: 191 */ 0xE9 /* 'i' -> */, -/* pos 01bf: 192 */ 0xF4 /* 't' -> */, -/* pos 01c0: 193 */ 0xE9 /* 'i' -> */, -/* pos 01c1: 194 */ 0xEF /* 'o' -> */, -/* pos 01c2: 195 */ 0xEE /* 'n' -> */, -/* pos 01c3: 196 */ 0xBA /* ':' -> */, -/* pos 01c4: 197 */ 0x00, 0x2D /* - terminal marker 45 - */, -/* pos 01c6: 198 */ 0xEE /* 'n' -> */, -/* pos 01c7: 199 */ 0xE3 /* 'c' -> */, -/* pos 01c8: 200 */ 0xEF /* 'o' -> */, -/* pos 01c9: 201 */ 0xE4 /* 'd' -> */, -/* pos 01ca: 202 */ 0xE9 /* 'i' -> */, -/* pos 01cb: 203 */ 0xEE /* 'n' -> */, -/* pos 01cc: 204 */ 0xE7 /* 'g' -> */, -/* pos 01cd: 205 */ 0xBA /* ':' -> */, -/* pos 01ce: 206 */ 0x00, 0x2E /* - terminal marker 46 - */, -/* pos 01d0: 207 */ 0xEE /* 'n' -> */, -/* pos 01d1: 208 */ 0xE7 /* 'g' -> */, -/* pos 01d2: 209 */ 0xF5 /* 'u' -> */, -/* pos 01d3: 210 */ 0xE1 /* 'a' -> */, -/* pos 01d4: 211 */ 0xE7 /* 'g' -> */, -/* pos 01d5: 212 */ 0xE5 /* 'e' -> */, -/* pos 01d6: 213 */ 0xBA /* ':' -> */, -/* pos 01d7: 214 */ 0x00, 0x2F /* - terminal marker 47 - */, -/* pos 01d9: 215 */ 0xE3 /* 'c' -> */, -/* pos 01da: 216 */ 0xE1 /* 'a' -> */, -/* pos 01db: 217 */ 0xF4 /* 't' -> */, -/* pos 01dc: 218 */ 0xE9 /* 'i' -> */, -/* pos 01dd: 219 */ 0xEF /* 'o' -> */, -/* pos 01de: 220 */ 0xEE /* 'n' -> */, -/* pos 01df: 221 */ 0xBA /* ':' -> */, -/* pos 01e0: 222 */ 0x00, 0x30 /* - terminal marker 48 - */, -/* pos 01e2: 223 */ 0xE1 /* 'a' -> */, -/* pos 01e3: 224 */ 0xEE /* 'n' -> */, -/* pos 01e4: 225 */ 0xE7 /* 'g' -> */, -/* pos 01e5: 226 */ 0xE5 /* 'e' -> */, -/* pos 01e6: 227 */ 0xBA /* ':' -> */, -/* pos 01e7: 228 */ 0x00, 0x31 /* - terminal marker 49 - */, -/* pos 01e9: 229 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x01F0 state 230) */, - 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x01F5 state 234) */, - 0x08, /* fail */ -/* pos 01f0: 230 */ 0xE1 /* 'a' -> */, -/* pos 01f1: 231 */ 0xE7 /* 'g' -> */, -/* pos 01f2: 232 */ 0xBA /* ':' -> */, -/* pos 01f3: 233 */ 0x00, 0x32 /* - terminal marker 50 - */, -/* pos 01f5: 234 */ 0xF0 /* 'p' -> */, -/* pos 01f6: 235 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x01FD state 236) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0202 state 240) */, - 0x08, /* fail */ -/* pos 01fd: 236 */ 0xE3 /* 'c' -> */, -/* pos 01fe: 237 */ 0xF4 /* 't' -> */, -/* pos 01ff: 238 */ 0xBA /* ':' -> */, -/* pos 0200: 239 */ 0x00, 0x33 /* - terminal marker 51 - */, -/* pos 0202: 240 */ 0xF2 /* 'r' -> */, -/* pos 0203: 241 */ 0xE5 /* 'e' -> */, -/* pos 0204: 242 */ 0xF3 /* 's' -> */, -/* pos 0205: 243 */ 0xBA /* ':' -> */, -/* pos 0206: 244 */ 0x00, 0x34 /* - terminal marker 52 - */, -/* pos 0208: 245 */ 0xF2 /* 'r' -> */, -/* pos 0209: 246 */ 0xEF /* 'o' -> */, -/* pos 020a: 247 */ 0xED /* 'm' -> */, -/* pos 020b: 248 */ 0xBA /* ':' -> */, -/* pos 020c: 249 */ 0x00, 0x35 /* - terminal marker 53 - */, -/* pos 020e: 250 */ 0xF4 /* 't' -> */, -/* pos 020f: 251 */ 0xE3 /* 'c' -> */, -/* pos 0210: 252 */ 0xE8 /* 'h' -> */, -/* pos 0211: 253 */ 0xBA /* ':' -> */, -/* pos 0212: 254 */ 0x00, 0x36 /* - terminal marker 54 - */, -/* pos 0214: 255 */ 0xE1 /* 'a' -> */, -/* pos 0215: 256 */ 0xEE /* 'n' -> */, -/* pos 0216: 257 */ 0xE7 /* 'g' -> */, -/* pos 0217: 258 */ 0xE5 /* 'e' -> */, -/* pos 0218: 259 */ 0xBA /* ':' -> */, -/* pos 0219: 260 */ 0x00, 0x37 /* - terminal marker 55 - */, -/* pos 021b: 261 */ 0xEE /* 'n' -> */, -/* pos 021c: 262 */ 0xED /* 'm' -> */, -/* pos 021d: 263 */ 0xEF /* 'o' -> */, -/* pos 021e: 264 */ 0xE4 /* 'd' -> */, -/* pos 021f: 265 */ 0xE9 /* 'i' -> */, -/* pos 0220: 266 */ 0xE6 /* 'f' -> */, -/* pos 0221: 267 */ 0xE9 /* 'i' -> */, -/* pos 0222: 268 */ 0xE5 /* 'e' -> */, -/* pos 0223: 269 */ 0xE4 /* 'd' -> */, -/* pos 0224: 270 */ 0xAD /* '-' -> */, -/* pos 0225: 271 */ 0xF3 /* 's' -> */, -/* pos 0226: 272 */ 0xE9 /* 'i' -> */, -/* pos 0227: 273 */ 0xEE /* 'n' -> */, -/* pos 0228: 274 */ 0xE3 /* 'c' -> */, -/* pos 0229: 275 */ 0xE5 /* 'e' -> */, -/* pos 022a: 276 */ 0xBA /* ':' -> */, -/* pos 022b: 277 */ 0x00, 0x38 /* - terminal marker 56 - */, -/* pos 022d: 278 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0237 state 279) */, - 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x0245 state 292) */, - 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x024A state 296) */, - 0x08, /* fail */ -/* pos 0237: 279 */ 0xF3 /* 's' -> */, -/* pos 0238: 280 */ 0xF4 /* 't' -> */, -/* pos 0239: 281 */ 0xAD /* '-' -> */, -/* pos 023a: 282 */ 0xED /* 'm' -> */, -/* pos 023b: 283 */ 0xEF /* 'o' -> */, -/* pos 023c: 284 */ 0xE4 /* 'd' -> */, -/* pos 023d: 285 */ 0xE9 /* 'i' -> */, -/* pos 023e: 286 */ 0xE6 /* 'f' -> */, -/* pos 023f: 287 */ 0xE9 /* 'i' -> */, -/* pos 0240: 288 */ 0xE5 /* 'e' -> */, -/* pos 0241: 289 */ 0xE4 /* 'd' -> */, -/* pos 0242: 290 */ 0xBA /* ':' -> */, -/* pos 0243: 291 */ 0x00, 0x39 /* - terminal marker 57 - */, -/* pos 0245: 292 */ 0xEE /* 'n' -> */, -/* pos 0246: 293 */ 0xEB /* 'k' -> */, -/* pos 0247: 294 */ 0xBA /* ':' -> */, -/* pos 0248: 295 */ 0x00, 0x3A /* - terminal marker 58 - */, -/* pos 024a: 296 */ 0xE3 /* 'c' -> */, -/* pos 024b: 297 */ 0xE1 /* 'a' -> */, -/* pos 024c: 298 */ 0xF4 /* 't' -> */, -/* pos 024d: 299 */ 0xE9 /* 'i' -> */, -/* pos 024e: 300 */ 0xEF /* 'o' -> */, -/* pos 024f: 301 */ 0xEE /* 'n' -> */, -/* pos 0250: 302 */ 0xBA /* ':' -> */, -/* pos 0251: 303 */ 0x00, 0x3B /* - terminal marker 59 - */, -/* pos 0253: 304 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x025D state 305) */, - 0x74 /* 't' */, 0x14, 0x00 /* (to 0x026A state 311) */, - 0x70 /* 'p' */, 0x6F, 0x01 /* (to 0x03C8 state 578) */, - 0x08, /* fail */ -/* pos 025d: 305 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0264 state 306) */, - 0x65 /* 'e' */, 0xB1, 0x00 /* (to 0x0311 state 430) */, - 0x08, /* fail */ -/* pos 0264: 306 */ 0xE5 /* 'e' -> */, -/* pos 0265: 307 */ 0xF3 /* 's' -> */, -/* pos 0266: 308 */ 0xE8 /* 'h' -> */, -/* pos 0267: 309 */ 0xBA /* ':' -> */, -/* pos 0268: 310 */ 0x00, 0x3F /* - terminal marker 63 - */, -/* pos 026a: 311 */ 0xF2 /* 'r' -> */, -/* pos 026b: 312 */ 0xF9 /* 'y' -> */, -/* pos 026c: 313 */ 0xAD /* '-' -> */, -/* pos 026d: 314 */ 0xE1 /* 'a' -> */, -/* pos 026e: 315 */ 0xE6 /* 'f' -> */, -/* pos 026f: 316 */ 0xF4 /* 't' -> */, -/* pos 0270: 317 */ 0xE5 /* 'e' -> */, -/* pos 0271: 318 */ 0xF2 /* 'r' -> */, -/* pos 0272: 319 */ 0xBA /* ':' -> */, -/* pos 0273: 320 */ 0x00, 0x40 /* - terminal marker 64 - */, -/* pos 0275: 321 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x027C state 322) */, - 0x74 /* 't' */, 0xED, 0x00 /* (to 0x0365 state 496) */, - 0x08, /* fail */ -/* pos 027c: 322 */ 0x72 /* 'r' */, 0x0A, 0x00 /* (to 0x0286 state 323) */, - 0x74 /* 't' */, 0x0D, 0x00 /* (to 0x028C state 328) */, - 0x63 /* 'c' */, 0x52, 0x01 /* (to 0x03D4 state 589) */, - 0x08, /* fail */ -/* pos 0286: 323 */ 0xF6 /* 'v' -> */, -/* pos 0287: 324 */ 0xE5 /* 'e' -> */, -/* pos 0288: 325 */ 0xF2 /* 'r' -> */, -/* pos 0289: 326 */ 0xBA /* ':' -> */, -/* pos 028a: 327 */ 0x00, 0x41 /* - terminal marker 65 - */, -/* pos 028c: 328 */ 0xAD /* '-' -> */, -/* pos 028d: 329 */ 0xE3 /* 'c' -> */, -/* pos 028e: 330 */ 0xEF /* 'o' -> */, -/* pos 028f: 331 */ 0xEF /* 'o' -> */, -/* pos 0290: 332 */ 0xEB /* 'k' -> */, -/* pos 0291: 333 */ 0xE9 /* 'i' -> */, -/* pos 0292: 334 */ 0xE5 /* 'e' -> */, -/* pos 0293: 335 */ 0xBA /* ':' -> */, -/* pos 0294: 336 */ 0x00, 0x42 /* - terminal marker 66 - */, -/* pos 0296: 337 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x029D state 338) */, - 0x65 /* 'e' */, 0x2C, 0x01 /* (to 0x03C5 state 576) */, - 0x08, /* fail */ -/* pos 029d: 338 */ 0xE1 /* 'a' -> */, -/* pos 029e: 339 */ 0xEE /* 'n' -> */, -/* pos 029f: 340 */ 0xF3 /* 's' -> */, -/* pos 02a0: 341 */ 0xE6 /* 'f' -> */, -/* pos 02a1: 342 */ 0xE5 /* 'e' -> */, -/* pos 02a2: 343 */ 0xF2 /* 'r' -> */, -/* pos 02a3: 344 */ 0xAD /* '-' -> */, -/* pos 02a4: 345 */ 0xE5 /* 'e' -> */, -/* pos 02a5: 346 */ 0xEE /* 'n' -> */, -/* pos 02a6: 347 */ 0xE3 /* 'c' -> */, -/* pos 02a7: 348 */ 0xEF /* 'o' -> */, -/* pos 02a8: 349 */ 0xE4 /* 'd' -> */, -/* pos 02a9: 350 */ 0xE9 /* 'i' -> */, -/* pos 02aa: 351 */ 0xEE /* 'n' -> */, -/* pos 02ab: 352 */ 0xE7 /* 'g' -> */, -/* pos 02ac: 353 */ 0xBA /* ':' -> */, -/* pos 02ad: 354 */ 0x00, 0x44 /* - terminal marker 68 - */, -/* pos 02af: 355 */ 0xE9 /* 'i' -> */, -/* pos 02b0: 356 */ 0xAD /* '-' -> */, -/* pos 02b1: 357 */ 0xE1 /* 'a' -> */, -/* pos 02b2: 358 */ 0xF2 /* 'r' -> */, -/* pos 02b3: 359 */ 0xE7 /* 'g' -> */, -/* pos 02b4: 360 */ 0xF3 /* 's' -> */, -/* pos 02b5: 361 */ 0x00, 0x4C /* - terminal marker 76 - */, -/* pos 02b7: 362 */ 0xA0 /* ' ' -> */, -/* pos 02b8: 363 */ 0x00, 0x4F /* - terminal marker 79 - */, -/* pos 02ba: 364 */ 0xAD /* '-' -> */, -/* pos 02bb: 365 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x02C5 state 366) */, - 0x61 /* 'a' */, 0x1D, 0x00 /* (to 0x02DB state 385) */, - 0x72 /* 'r' */, 0xFB, 0x00 /* (to 0x03BC state 568) */, - 0x08, /* fail */ -/* pos 02c5: 366 */ 0xEF /* 'o' -> */, -/* pos 02c6: 367 */ 0xF2 /* 'r' -> */, -/* pos 02c7: 368 */ 0xF7 /* 'w' -> */, -/* pos 02c8: 369 */ 0xE1 /* 'a' -> */, -/* pos 02c9: 370 */ 0xF2 /* 'r' -> */, -/* pos 02ca: 371 */ 0xE4 /* 'd' -> */, -/* pos 02cb: 372 */ 0xE5 /* 'e' -> */, -/* pos 02cc: 373 */ 0xE4 /* 'd' -> */, -/* pos 02cd: 374 */ 0xAD /* '-' -> */, -/* pos 02ce: 375 */ 0xE6 /* 'f' -> */, -/* pos 02cf: 376 */ 0xEF /* 'o' -> */, -/* pos 02d0: 377 */ 0xF2 /* 'r' -> */, -/* pos 02d1: 378 */ 0xBA /* ':' -> */, -/* pos 02d2: 379 */ 0x00, 0x50 /* - terminal marker 80 - */, -/* pos 02d4: 380 */ 0x00, 0x51 /* - terminal marker 81 - */, -/* pos 02d6: 381 */ 0xE1 /* 'a' -> */, -/* pos 02d7: 382 */ 0xE4 /* 'd' -> */, -/* pos 02d8: 383 */ 0xA0 /* ' ' -> */, -/* pos 02d9: 384 */ 0x00, 0x52 /* - terminal marker 82 - */, -/* pos 02db: 385 */ 0xF5 /* 'u' -> */, -/* pos 02dc: 386 */ 0xF4 /* 't' -> */, -/* pos 02dd: 387 */ 0xE8 /* 'h' -> */, -/* pos 02de: 388 */ 0xAD /* '-' -> */, -/* pos 02df: 389 */ 0xF4 /* 't' -> */, -/* pos 02e0: 390 */ 0xEF /* 'o' -> */, -/* pos 02e1: 391 */ 0xEB /* 'k' -> */, -/* pos 02e2: 392 */ 0xE5 /* 'e' -> */, -/* pos 02e3: 393 */ 0xEE /* 'n' -> */, -/* pos 02e4: 394 */ 0xBA /* ':' -> */, -/* pos 02e5: 395 */ 0x00, 0x56 /* - terminal marker 86 - */, -/* pos 02e7: 396 */ 0xF4 /* 't' -> */, -/* pos 02e8: 397 */ 0xE9 /* 'i' -> */, -/* pos 02e9: 398 */ 0xEF /* 'o' -> */, -/* pos 02ea: 399 */ 0xEE /* 'n' -> */, -/* pos 02eb: 400 */ 0xF3 /* 's' -> */, -/* pos 02ec: 401 */ 0xA0 /* ' ' -> */, -/* pos 02ed: 402 */ 0x00, 0x02 /* - terminal marker 2 - */, -/* pos 02ef: 403 */ 0xF3 /* 's' -> */, -/* pos 02f0: 404 */ 0xAD /* '-' -> */, -/* pos 02f1: 405 */ 0xE3 /* 'c' -> */, -/* pos 02f2: 406 */ 0xEF /* 'o' -> */, -/* pos 02f3: 407 */ 0xEE /* 'n' -> */, -/* pos 02f4: 408 */ 0xF4 /* 't' -> */, -/* pos 02f5: 409 */ 0xF2 /* 'r' -> */, -/* pos 02f6: 410 */ 0xEF /* 'o' -> */, -/* pos 02f7: 411 */ 0xEC /* 'l' -> */, -/* pos 02f8: 412 */ 0xAD /* '-' -> */, -/* pos 02f9: 413 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0300 state 414) */, - 0x61 /* 'a' */, 0x24, 0x00 /* (to 0x0320 state 443) */, - 0x08, /* fail */ -/* pos 0300: 414 */ 0xE5 /* 'e' -> */, -/* pos 0301: 415 */ 0xF1 /* 'q' -> */, -/* pos 0302: 416 */ 0xF5 /* 'u' -> */, -/* pos 0303: 417 */ 0xE5 /* 'e' -> */, -/* pos 0304: 418 */ 0xF3 /* 's' -> */, -/* pos 0305: 419 */ 0xF4 /* 't' -> */, -/* pos 0306: 420 */ 0xAD /* '-' -> */, -/* pos 0307: 421 */ 0xE8 /* 'h' -> */, -/* pos 0308: 422 */ 0xE5 /* 'e' -> */, -/* pos 0309: 423 */ 0xE1 /* 'a' -> */, -/* pos 030a: 424 */ 0xE4 /* 'd' -> */, -/* pos 030b: 425 */ 0xE5 /* 'e' -> */, -/* pos 030c: 426 */ 0xF2 /* 'r' -> */, -/* pos 030d: 427 */ 0xF3 /* 's' -> */, -/* pos 030e: 428 */ 0xBA /* ':' -> */, -/* pos 030f: 429 */ 0x00, 0x12 /* - terminal marker 18 - */, -/* pos 0311: 430 */ 0xF2 /* 'r' -> */, -/* pos 0312: 431 */ 0xE5 /* 'e' -> */, -/* pos 0313: 432 */ 0xF2 /* 'r' -> */, -/* pos 0314: 433 */ 0xBA /* ':' -> */, -/* pos 0315: 434 */ 0x00, 0x1F /* - terminal marker 31 - */, -/* pos 0317: 435 */ 0xE8 /* 'h' -> */, -/* pos 0318: 436 */ 0xE1 /* 'a' -> */, -/* pos 0319: 437 */ 0xF2 /* 'r' -> */, -/* pos 031a: 438 */ 0xF3 /* 's' -> */, -/* pos 031b: 439 */ 0xE5 /* 'e' -> */, -/* pos 031c: 440 */ 0xF4 /* 't' -> */, -/* pos 031d: 441 */ 0xBA /* ':' -> */, -/* pos 031e: 442 */ 0x00, 0x28 /* - terminal marker 40 - */, -/* pos 0320: 443 */ 0xEC /* 'l' -> */, -/* pos 0321: 444 */ 0xEC /* 'l' -> */, -/* pos 0322: 445 */ 0xEF /* 'o' -> */, -/* pos 0323: 446 */ 0xF7 /* 'w' -> */, -/* pos 0324: 447 */ 0xAD /* '-' -> */, -/* pos 0325: 448 */ 0xEF /* 'o' -> */, -/* pos 0326: 449 */ 0xF2 /* 'r' -> */, -/* pos 0327: 450 */ 0xE9 /* 'i' -> */, -/* pos 0328: 451 */ 0xE7 /* 'g' -> */, -/* pos 0329: 452 */ 0xE9 /* 'i' -> */, -/* pos 032a: 453 */ 0xEE /* 'n' -> */, -/* pos 032b: 454 */ 0xBA /* ':' -> */, -/* pos 032c: 455 */ 0x00, 0x2A /* - terminal marker 42 - */, -/* pos 032e: 456 */ 0xE1 /* 'a' -> */, -/* pos 032f: 457 */ 0xF8 /* 'x' -> */, -/* pos 0330: 458 */ 0xAD /* '-' -> */, -/* pos 0331: 459 */ 0xE6 /* 'f' -> */, -/* pos 0332: 460 */ 0xEF /* 'o' -> */, -/* pos 0333: 461 */ 0xF2 /* 'r' -> */, -/* pos 0334: 462 */ 0xF7 /* 'w' -> */, -/* pos 0335: 463 */ 0xE1 /* 'a' -> */, -/* pos 0336: 464 */ 0xF2 /* 'r' -> */, -/* pos 0337: 465 */ 0xE4 /* 'd' -> */, -/* pos 0338: 466 */ 0xF3 /* 's' -> */, -/* pos 0339: 467 */ 0xBA /* ':' -> */, -/* pos 033a: 468 */ 0x00, 0x3C /* - terminal marker 60 - */, -/* pos 033c: 469 */ 0xF8 /* 'x' -> */, -/* pos 033d: 470 */ 0xF9 /* 'y' -> */, -/* pos 033e: 471 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0345 state 472) */, - 0x20 /* ' ' */, 0x79, 0x00 /* (to 0x03BA state 567) */, - 0x08, /* fail */ -/* pos 0345: 472 */ 0xE1 /* 'a' -> */, -/* pos 0346: 473 */ 0xF5 /* 'u' -> */, -/* pos 0347: 474 */ 0xF4 /* 't' -> */, -/* pos 0348: 475 */ 0xE8 /* 'h' -> */, -/* pos 0349: 476 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0350 state 477) */, - 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x035A state 486) */, - 0x08, /* fail */ -/* pos 0350: 477 */ 0xEE /* 'n' -> */, -/* pos 0351: 478 */ 0xF4 /* 't' -> */, -/* pos 0352: 479 */ 0xE9 /* 'i' -> */, -/* pos 0353: 480 */ 0xE3 /* 'c' -> */, -/* pos 0354: 481 */ 0xE1 /* 'a' -> */, -/* pos 0355: 482 */ 0xF4 /* 't' -> */, -/* pos 0356: 483 */ 0xE5 /* 'e' -> */, -/* pos 0357: 484 */ 0xBA /* ':' -> */, -/* pos 0358: 485 */ 0x00, 0x3D /* - terminal marker 61 - */, -/* pos 035a: 486 */ 0xF2 /* 'r' -> */, -/* pos 035b: 487 */ 0xE9 /* 'i' -> */, -/* pos 035c: 488 */ 0xFA /* 'z' -> */, -/* pos 035d: 489 */ 0xE1 /* 'a' -> */, -/* pos 035e: 490 */ 0xF4 /* 't' -> */, -/* pos 035f: 491 */ 0xE9 /* 'i' -> */, -/* pos 0360: 492 */ 0xEF /* 'o' -> */, -/* pos 0361: 493 */ 0xEE /* 'n' -> */, -/* pos 0362: 494 */ 0xBA /* ':' -> */, -/* pos 0363: 495 */ 0x00, 0x3E /* - terminal marker 62 - */, -/* pos 0365: 496 */ 0xF2 /* 'r' -> */, -/* pos 0366: 497 */ 0xE9 /* 'i' -> */, -/* pos 0367: 498 */ 0xE3 /* 'c' -> */, -/* pos 0368: 499 */ 0xF4 /* 't' -> */, -/* pos 0369: 500 */ 0xAD /* '-' -> */, -/* pos 036a: 501 */ 0xF4 /* 't' -> */, -/* pos 036b: 502 */ 0xF2 /* 'r' -> */, -/* pos 036c: 503 */ 0xE1 /* 'a' -> */, -/* pos 036d: 504 */ 0xEE /* 'n' -> */, -/* pos 036e: 505 */ 0xF3 /* 's' -> */, -/* pos 036f: 506 */ 0xF0 /* 'p' -> */, -/* pos 0370: 507 */ 0xEF /* 'o' -> */, -/* pos 0371: 508 */ 0xF2 /* 'r' -> */, -/* pos 0372: 509 */ 0xF4 /* 't' -> */, -/* pos 0373: 510 */ 0xAD /* '-' -> */, -/* pos 0374: 511 */ 0xF3 /* 's' -> */, -/* pos 0375: 512 */ 0xE5 /* 'e' -> */, -/* pos 0376: 513 */ 0xE3 /* 'c' -> */, -/* pos 0377: 514 */ 0xF5 /* 'u' -> */, -/* pos 0378: 515 */ 0xF2 /* 'r' -> */, -/* pos 0379: 516 */ 0xE9 /* 'i' -> */, -/* pos 037a: 517 */ 0xF4 /* 't' -> */, -/* pos 037b: 518 */ 0xF9 /* 'y' -> */, -/* pos 037c: 519 */ 0xBA /* ':' -> */, -/* pos 037d: 520 */ 0x00, 0x43 /* - terminal marker 67 - */, -/* pos 037f: 521 */ 0xE5 /* 'e' -> */, -/* pos 0380: 522 */ 0xF2 /* 'r' -> */, -/* pos 0381: 523 */ 0xAD /* '-' -> */, -/* pos 0382: 524 */ 0xE1 /* 'a' -> */, -/* pos 0383: 525 */ 0xE7 /* 'g' -> */, -/* pos 0384: 526 */ 0xE5 /* 'e' -> */, -/* pos 0385: 527 */ 0xEE /* 'n' -> */, -/* pos 0386: 528 */ 0xF4 /* 't' -> */, -/* pos 0387: 529 */ 0xBA /* ':' -> */, -/* pos 0388: 530 */ 0x00, 0x45 /* - terminal marker 69 - */, -/* pos 038a: 531 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0391 state 532) */, - 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0396 state 536) */, - 0x08, /* fail */ -/* pos 0391: 532 */ 0xF2 /* 'r' -> */, -/* pos 0392: 533 */ 0xF9 /* 'y' -> */, -/* pos 0393: 534 */ 0xBA /* ':' -> */, -/* pos 0394: 535 */ 0x00, 0x46 /* - terminal marker 70 - */, -/* pos 0396: 536 */ 0xE1 /* 'a' -> */, -/* pos 0397: 537 */ 0xBA /* ':' -> */, -/* pos 0398: 538 */ 0x00, 0x47 /* - terminal marker 71 - */, -/* pos 039a: 539 */ 0xF7 /* 'w' -> */, -/* pos 039b: 540 */ 0xF7 /* 'w' -> */, -/* pos 039c: 541 */ 0xAD /* '-' -> */, -/* pos 039d: 542 */ 0xE1 /* 'a' -> */, -/* pos 039e: 543 */ 0xF5 /* 'u' -> */, -/* pos 039f: 544 */ 0xF4 /* 't' -> */, -/* pos 03a0: 545 */ 0xE8 /* 'h' -> */, -/* pos 03a1: 546 */ 0xE5 /* 'e' -> */, -/* pos 03a2: 547 */ 0xEE /* 'n' -> */, -/* pos 03a3: 548 */ 0xF4 /* 't' -> */, -/* pos 03a4: 549 */ 0xE9 /* 'i' -> */, -/* pos 03a5: 550 */ 0xE3 /* 'c' -> */, -/* pos 03a6: 551 */ 0xE1 /* 'a' -> */, -/* pos 03a7: 552 */ 0xF4 /* 't' -> */, -/* pos 03a8: 553 */ 0xE5 /* 'e' -> */, -/* pos 03a9: 554 */ 0xBA /* ':' -> */, -/* pos 03aa: 555 */ 0x00, 0x48 /* - terminal marker 72 - */, -/* pos 03ac: 556 */ 0xF4 /* 't' -> */, -/* pos 03ad: 557 */ 0xE3 /* 'c' -> */, -/* pos 03ae: 558 */ 0xE8 /* 'h' -> */, -/* pos 03af: 559 */ 0x00, 0x49 /* - terminal marker 73 - */, -/* pos 03b1: 560 */ 0xF4 /* 't' -> */, -/* pos 03b2: 561 */ 0x00, 0x4A /* - terminal marker 74 - */, -/* pos 03b4: 562 */ 0xEC /* 'l' -> */, -/* pos 03b5: 563 */ 0xE5 /* 'e' -> */, -/* pos 03b6: 564 */ 0xF4 /* 't' -> */, -/* pos 03b7: 565 */ 0xE5 /* 'e' -> */, -/* pos 03b8: 566 */ 0x00, 0x4B /* - terminal marker 75 - */, -/* pos 03ba: 567 */ 0x00, 0x4D /* - terminal marker 77 - */, -/* pos 03bc: 568 */ 0xE5 /* 'e' -> */, -/* pos 03bd: 569 */ 0xE1 /* 'a' -> */, -/* pos 03be: 570 */ 0xEC /* 'l' -> */, -/* pos 03bf: 571 */ 0xAD /* '-' -> */, -/* pos 03c0: 572 */ 0xE9 /* 'i' -> */, -/* pos 03c1: 573 */ 0xF0 /* 'p' -> */, -/* pos 03c2: 574 */ 0xBA /* ':' -> */, -/* pos 03c3: 575 */ 0x00, 0x4E /* - terminal marker 78 - */, -/* pos 03c5: 576 */ 0xBA /* ':' -> */, -/* pos 03c6: 577 */ 0x00, 0x53 /* - terminal marker 83 - */, -/* pos 03c8: 578 */ 0xEC /* 'l' -> */, -/* pos 03c9: 579 */ 0xE1 /* 'a' -> */, -/* pos 03ca: 580 */ 0xF9 /* 'y' -> */, -/* pos 03cb: 581 */ 0xAD /* '-' -> */, -/* pos 03cc: 582 */ 0xEE /* 'n' -> */, -/* pos 03cd: 583 */ 0xEF /* 'o' -> */, -/* pos 03ce: 584 */ 0xEE /* 'n' -> */, -/* pos 03cf: 585 */ 0xE3 /* 'c' -> */, -/* pos 03d0: 586 */ 0xE5 /* 'e' -> */, -/* pos 03d1: 587 */ 0xBA /* ':' -> */, -/* pos 03d2: 588 */ 0x00, 0x54 /* - terminal marker 84 - */, -/* pos 03d4: 589 */ 0xAD /* '-' -> */, -/* pos 03d5: 590 */ 0xF7 /* 'w' -> */, -/* pos 03d6: 591 */ 0xE5 /* 'e' -> */, -/* pos 03d7: 592 */ 0xE2 /* 'b' -> */, -/* pos 03d8: 593 */ 0xF3 /* 's' -> */, -/* pos 03d9: 594 */ 0xEF /* 'o' -> */, -/* pos 03da: 595 */ 0xE3 /* 'c' -> */, -/* pos 03db: 596 */ 0xEB /* 'k' -> */, -/* pos 03dc: 597 */ 0xE5 /* 'e' -> */, -/* pos 03dd: 598 */ 0xF4 /* 't' -> */, -/* pos 03de: 599 */ 0xAD /* '-' -> */, -/* pos 03df: 600 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x03F8 state 601) */, - 0x65 /* 'e' */, 0x1D, 0x00 /* (to 0x03FF state 607) */, - 0x6B /* 'k' */, 0x26, 0x00 /* (to 0x040B state 618) */, - 0x70 /* 'p' */, 0x35, 0x00 /* (to 0x041D state 625) */, - 0x61 /* 'a' */, 0x3C, 0x00 /* (to 0x0427 state 634) */, - 0x6E /* 'n' */, 0x41, 0x00 /* (to 0x042F state 641) */, - 0x76 /* 'v' */, 0x47, 0x00 /* (to 0x0438 state 648) */, - 0x6F /* 'o' */, 0x4D, 0x00 /* (to 0x0441 state 656) */, - 0x08, /* fail */ -/* pos 03f8: 601 */ 0xF2 /* 'r' -> */, -/* pos 03f9: 602 */ 0xE1 /* 'a' -> */, -/* pos 03fa: 603 */ 0xE6 /* 'f' -> */, -/* pos 03fb: 604 */ 0xF4 /* 't' -> */, -/* pos 03fc: 605 */ 0xBA /* ':' -> */, -/* pos 03fd: 606 */ 0x00, 0x07 /* - terminal marker 7 - */, -/* pos 03ff: 607 */ 0xF8 /* 'x' -> */, -/* pos 0400: 608 */ 0xF4 /* 't' -> */, -/* pos 0401: 609 */ 0xE5 /* 'e' -> */, -/* pos 0402: 610 */ 0xEE /* 'n' -> */, -/* pos 0403: 611 */ 0xF3 /* 's' -> */, -/* pos 0404: 612 */ 0xE9 /* 'i' -> */, -/* pos 0405: 613 */ 0xEF /* 'o' -> */, -/* pos 0406: 614 */ 0xEE /* 'n' -> */, -/* pos 0407: 615 */ 0xF3 /* 's' -> */, -/* pos 0408: 616 */ 0xBA /* ':' -> */, -/* pos 0409: 617 */ 0x00, 0x09 /* - terminal marker 9 - */, -/* pos 040b: 618 */ 0xE5 /* 'e' -> */, -/* pos 040c: 619 */ 0xF9 /* 'y' -> */, -/* pos 040d: 620 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0417 state 621) */, - 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x041A state 623) */, - 0x3A /* ':' */, 0x23, 0x00 /* (to 0x0436 state 647) */, - 0x08, /* fail */ -/* pos 0417: 621 */ 0xBA /* ':' -> */, -/* pos 0418: 622 */ 0x00, 0x0A /* - terminal marker 10 - */, -/* pos 041a: 623 */ 0xBA /* ':' -> */, -/* pos 041b: 624 */ 0x00, 0x0B /* - terminal marker 11 - */, -/* pos 041d: 625 */ 0xF2 /* 'r' -> */, -/* pos 041e: 626 */ 0xEF /* 'o' -> */, -/* pos 041f: 627 */ 0xF4 /* 't' -> */, -/* pos 0420: 628 */ 0xEF /* 'o' -> */, -/* pos 0421: 629 */ 0xE3 /* 'c' -> */, -/* pos 0422: 630 */ 0xEF /* 'o' -> */, -/* pos 0423: 631 */ 0xEC /* 'l' -> */, -/* pos 0424: 632 */ 0xBA /* ':' -> */, -/* pos 0425: 633 */ 0x00, 0x0C /* - terminal marker 12 - */, -/* pos 0427: 634 */ 0xE3 /* 'c' -> */, -/* pos 0428: 635 */ 0xE3 /* 'c' -> */, -/* pos 0429: 636 */ 0xE5 /* 'e' -> */, -/* pos 042a: 637 */ 0xF0 /* 'p' -> */, -/* pos 042b: 638 */ 0xF4 /* 't' -> */, -/* pos 042c: 639 */ 0xBA /* ':' -> */, -/* pos 042d: 640 */ 0x00, 0x0D /* - terminal marker 13 - */, -/* pos 042f: 641 */ 0xEF /* 'o' -> */, -/* pos 0430: 642 */ 0xEE /* 'n' -> */, -/* pos 0431: 643 */ 0xE3 /* 'c' -> */, -/* pos 0432: 644 */ 0xE5 /* 'e' -> */, -/* pos 0433: 645 */ 0xBA /* ':' -> */, -/* pos 0434: 646 */ 0x00, 0x0E /* - terminal marker 14 - */, -/* pos 0436: 647 */ 0x00, 0x20 /* - terminal marker 32 - */, -/* pos 0438: 648 */ 0xE5 /* 'e' -> */, -/* pos 0439: 649 */ 0xF2 /* 'r' -> */, -/* pos 043a: 650 */ 0xF3 /* 's' -> */, -/* pos 043b: 651 */ 0xE9 /* 'i' -> */, -/* pos 043c: 652 */ 0xEF /* 'o' -> */, -/* pos 043d: 653 */ 0xEE /* 'n' -> */, -/* pos 043e: 654 */ 0xBA /* ':' -> */, -/* pos 043f: 655 */ 0x00, 0x21 /* - terminal marker 33 - */, -/* pos 0441: 656 */ 0xF2 /* 'r' -> */, -/* pos 0442: 657 */ 0xE9 /* 'i' -> */, -/* pos 0443: 658 */ 0xE7 /* 'g' -> */, -/* pos 0444: 659 */ 0xE9 /* 'i' -> */, -/* pos 0445: 660 */ 0xEE /* 'n' -> */, -/* pos 0446: 661 */ 0xBA /* ':' -> */, -/* pos 0447: 662 */ 0x00, 0x22 /* - terminal marker 34 - */, -/* pos 0449: 663 */ 0xAD /* '-' -> */, -/* pos 044a: 664 */ 0xF3 /* 's' -> */, -/* pos 044b: 665 */ 0xE5 /* 'e' -> */, -/* pos 044c: 666 */ 0xF4 /* 't' -> */, -/* pos 044d: 667 */ 0xF4 /* 't' -> */, -/* pos 044e: 668 */ 0xE9 /* 'i' -> */, -/* pos 044f: 669 */ 0xEE /* 'n' -> */, -/* pos 0450: 670 */ 0xE7 /* 'g' -> */, -/* pos 0451: 671 */ 0xF3 /* 's' -> */, -/* pos 0452: 672 */ 0xBA /* ':' -> */, -/* pos 0453: 673 */ 0x00, 0x10 /* - terminal marker 16 - */, -/* pos 0455: 674 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0462 state 675) */, - 0x6D /* 'm' */, 0x14, 0x00 /* (to 0x046C state 684) */, - 0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0473 state 690) */, - 0x73 /* 's' */, 0x20, 0x00 /* (to 0x047E state 694) */, - 0x08, /* fail */ -/* pos 0462: 675 */ 0xF5 /* 'u' -> */, -/* pos 0463: 676 */ 0xF4 /* 't' -> */, -/* pos 0464: 677 */ 0xE8 /* 'h' -> */, -/* pos 0465: 678 */ 0xEF /* 'o' -> */, -/* pos 0466: 679 */ 0xF2 /* 'r' -> */, -/* pos 0467: 680 */ 0xE9 /* 'i' -> */, -/* pos 0468: 681 */ 0xF4 /* 't' -> */, -/* pos 0469: 682 */ 0xF9 /* 'y' -> */, -/* pos 046a: 683 */ 0x00, 0x23 /* - terminal marker 35 - */, -/* pos 046c: 684 */ 0xE5 /* 'e' -> */, -/* pos 046d: 685 */ 0xF4 /* 't' -> */, -/* pos 046e: 686 */ 0xE8 /* 'h' -> */, -/* pos 046f: 687 */ 0xEF /* 'o' -> */, -/* pos 0470: 688 */ 0xE4 /* 'd' -> */, -/* pos 0471: 689 */ 0x00, 0x24 /* - terminal marker 36 - */, -/* pos 0473: 690 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x047A state 691) */, - 0x72 /* 'r' */, 0x1B, 0x00 /* (to 0x0491 state 705) */, - 0x08, /* fail */ -/* pos 047a: 691 */ 0xF4 /* 't' -> */, -/* pos 047b: 692 */ 0xE8 /* 'h' -> */, -/* pos 047c: 693 */ 0x00, 0x25 /* - terminal marker 37 - */, -/* pos 047e: 694 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x0485 state 695) */, - 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x048B state 700) */, - 0x08, /* fail */ -/* pos 0485: 695 */ 0xE8 /* 'h' -> */, -/* pos 0486: 696 */ 0xE5 /* 'e' -> */, -/* pos 0487: 697 */ 0xED /* 'm' -> */, -/* pos 0488: 698 */ 0xE5 /* 'e' -> */, -/* pos 0489: 699 */ 0x00, 0x26 /* - terminal marker 38 - */, -/* pos 048b: 700 */ 0xE1 /* 'a' -> */, -/* pos 048c: 701 */ 0xF4 /* 't' -> */, -/* pos 048d: 702 */ 0xF5 /* 'u' -> */, -/* pos 048e: 703 */ 0xF3 /* 's' -> */, -/* pos 048f: 704 */ 0x00, 0x27 /* - terminal marker 39 - */, -/* pos 0491: 705 */ 0xEF /* 'o' -> */, -/* pos 0492: 706 */ 0xF4 /* 't' -> */, -/* pos 0493: 707 */ 0xEF /* 'o' -> */, -/* pos 0494: 708 */ 0xE3 /* 'c' -> */, -/* pos 0495: 709 */ 0xEF /* 'o' -> */, -/* pos 0496: 710 */ 0xEC /* 'l' -> */, -/* pos 0497: 711 */ 0x00, 0x55 /* - terminal marker 85 - */, -/* total size 1177 bytes */ -#endif - - -/* -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x10,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x03,0x00,0x80,0x0f,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x07,0x00,0x00,0x3e,0x00,0x00,0x00,0x80,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x03,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) -static uint8_t lws_header_implies_psuedoheader_map[] = { - 0x07,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,0x0e,0x04,0x00,0x00,0x00,0x00,0x00, -}; -#endif -*/ diff -Nru libwebsockets-4.0.20/lib/roles/http/lextable-strings.h libwebsockets-2.4.2/lib/roles/http/lextable-strings.h --- libwebsockets-4.0.20/lib/roles/http/lextable-strings.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/lextable-strings.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -/* set of parsable strings -- ALL LOWER CASE */ - -static const char * const set[] = { - "get ", - "post ", -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - "options ", -#endif - "host:", - "connection:", - "upgrade:", - "origin:", -#if defined(LWS_ROLE_WS) - "sec-websocket-draft:", -#endif - "\x0d\x0a", - -#if defined(LWS_ROLE_WS) - "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) - "http2-settings:", -#endif - - "accept:", -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - "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) - "referer:", -#endif -#if defined(LWS_ROLE_WS) - "sec-websocket-key:", - "sec-websocket-version:", - "sec-websocket-origin:", -#endif -#if defined(LWS_ROLE_H2) - ":authority", - ":method", - ":path", - ":scheme", - ":status", -#endif -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - "accept-charset:", -#endif - "accept-ranges:", -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - "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) - "max-forwards:", - "proxy-authenticate:", - "proxy-authorization:", -#endif - "refresh:", - "retry-after:", - "server:", - "set-cookie:", -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - "strict-transport-security:", -#endif - "transfer-encoding:", -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - "user-agent:", - "vary:", - "via:", - "www-authenticate:", -#endif -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - "patch", - "put", - "delete", -#endif - - "uri-args", /* fake header used for uri-only storage */ - -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - "proxy ", - "x-real-ip:", -#endif - "http/1.0 ", - - "x-forwarded-for:", - "connect ", - "head ", -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) - "te:", /* http/2 wants it to reject it */ - "replay-nonce:", /* ACME */ -#endif -#if defined(LWS_ROLE_H2) - ":protocol", /* defined in mcmanus-httpbis-h2-ws-02 */ -#endif - - "x-auth-token:", - - "", /* not matchable */ - -}; diff -Nru libwebsockets-4.0.20/lib/roles/http/minilex.c libwebsockets-2.4.2/lib/roles/http/minilex.c --- libwebsockets-4.0.20/lib/roles/http/minilex.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/minilex.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,488 +0,0 @@ -/* - * minilex.c - * - * High efficiency lexical state parser - * - * Copyright (C)2011-2020 Andy Green - * - * 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 -#include -#include - -/* 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 */, 0x04 /* <- 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)); - - printf("#if %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++) { - - printf("#if %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"); -} diff -Nru libwebsockets-4.0.20/lib/roles/http/parsers.c libwebsockets-2.4.2/lib/roles/http/parsers.c --- libwebsockets-4.0.20/lib/roles/http/parsers.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/parsers.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1431 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 const unsigned char lextable_h1[] = { - #include "lextable.h" -}; - -#define FAIL_CHAR 0x08 - -#if defined(LWS_WITH_CUSTOM_HEADERS) - -#define UHO_NLEN 0 -#define UHO_VLEN 2 -#define UHO_LL 4 -#define UHO_NAME 8 - -#endif - -static struct allocated_headers * -_lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size) -{ - struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct"); - - if (!ah) - return NULL; - - ah->data = lws_malloc(data_size, "ah data"); - if (!ah->data) { - lws_free(ah); - - return NULL; - } - ah->next = pt->http.ah_list; - pt->http.ah_list = ah; - ah->data_length = data_size; - pt->http.ah_pool_length++; - - lwsl_info("%s: created ah %p (size %d): pool length %u\n", __func__, - ah, (int)data_size, (unsigned int)pt->http.ah_pool_length); - - return ah; -} - -int -_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah) -{ - lws_start_foreach_llp(struct allocated_headers **, a, pt->http.ah_list) { - if ((*a) == ah) { - *a = ah->next; - pt->http.ah_pool_length--; - lwsl_info("%s: freed ah %p : pool length %u\n", - __func__, ah, - (unsigned int)pt->http.ah_pool_length); - if (ah->data) - lws_free(ah->data); - lws_free(ah); - - return 0; - } - } lws_end_foreach_llp(a, next); - - return 1; -} - -void -_lws_header_table_reset(struct allocated_headers *ah) -{ - /* init the ah to reflect no headers or data have appeared yet */ - memset(ah->frag_index, 0, sizeof(ah->frag_index)); - memset(ah->frags, 0, sizeof(ah->frags)); - ah->nfrag = 0; - ah->pos = 0; - ah->http_response = 0; - ah->parser_state = WSI_TOKEN_NAME_PART; - ah->lextable_pos = 0; - ah->unk_pos = 0; -#if defined(LWS_WITH_CUSTOM_HEADERS) - ah->unk_ll_head = 0; - ah->unk_ll_tail = 0; -#endif -} - -// doesn't scrub the ah rxbuffer by default, parent must do if needed - -void -__lws_header_table_reset(struct lws *wsi, int autoservice) -{ - struct allocated_headers *ah = wsi->http.ah; - struct lws_context_per_thread *pt; - struct lws_pollfd *pfd; - - /* if we have the idea we're resetting 'our' ah, must be bound to one */ - assert(ah); - /* ah also concurs with ownership */ - assert(ah->wsi == wsi); - - _lws_header_table_reset(ah); - - /* since we will restart the ah, our new headers are not completed */ - wsi->hdr_parsing_completed = 0; - - /* while we hold the ah, keep a timeout on the wsi */ - __lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH, - wsi->vhost->timeout_secs_ah_idle); - - time(&ah->assigned); - - if (wsi->position_in_fds_table != LWS_NO_FDS_POS && - lws_buflist_next_segment_len(&wsi->buflist, NULL) && - autoservice) { - lwsl_debug("%s: service on readbuf ah\n", __func__); - - pt = &wsi->context->pt[(int)wsi->tsi]; - /* - * Unlike a normal connect, we have the headers already - * (or the first part of them anyway) - */ - pfd = &pt->fds[wsi->position_in_fds_table]; - pfd->revents |= LWS_POLLIN; - lwsl_err("%s: calling service\n", __func__); - lws_service_fd_tsi(wsi->context, pfd, wsi->tsi); - } -} - -void -lws_header_table_reset(struct lws *wsi, int autoservice) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - lws_pt_lock(pt, __func__); - - __lws_header_table_reset(wsi, autoservice); - - lws_pt_unlock(pt); -} - -static void -_lws_header_ensure_we_are_on_waiting_list(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct lws_pollargs pa; - struct lws **pwsi = &pt->http.ah_wait_list; - - while (*pwsi) { - if (*pwsi == wsi) - return; - pwsi = &(*pwsi)->http.ah_wait_list; - } - - lwsl_info("%s: wsi: %p\n", __func__, wsi); - wsi->http.ah_wait_list = pt->http.ah_wait_list; - pt->http.ah_wait_list = wsi; - pt->http.ah_wait_list_length++; - - /* we cannot accept input then */ - - _lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa); -} - -static int -__lws_remove_from_ah_waiting_list(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct lws **pwsi =&pt->http.ah_wait_list; - - while (*pwsi) { - if (*pwsi == wsi) { - lwsl_info("%s: wsi %p\n", __func__, wsi); - /* point prev guy to our next */ - *pwsi = wsi->http.ah_wait_list; - /* we shouldn't point anywhere now */ - wsi->http.ah_wait_list = NULL; - pt->http.ah_wait_list_length--; - - return 1; - } - pwsi = &(*pwsi)->http.ah_wait_list; - } - - return 0; -} - -int LWS_WARN_UNUSED_RESULT -lws_header_table_attach(struct lws *wsi, int autoservice) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - struct lws_pollargs pa; - int n; - -#if defined(LWS_ROLE_MQTT) && defined(LWS_WITH_CLIENT) - if (lwsi_role_mqtt(wsi)) - goto connect_via_info2; -#endif - - lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__, - (void *)wsi, (void *)wsi->http.ah, wsi->tsi, - pt->http.ah_count_in_use); - - if (!lwsi_role_http(wsi)) { - lwsl_err("%s: bad role %s\n", __func__, wsi->role_ops->name); - assert(0); - return -1; - } - - lws_pt_lock(pt, __func__); - - /* if we are already bound to one, just clear it down */ - if (wsi->http.ah) { - lwsl_info("%s: cleardown\n", __func__); - goto reset; - } - - n = pt->http.ah_count_in_use == context->max_http_header_pool; -#if defined(LWS_WITH_PEER_LIMITS) - if (!n) { - n = lws_peer_confirm_ah_attach_ok(context, wsi->peer); - if (n) - lws_stats_bump(pt, LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1); - } -#endif - if (n) { - /* - * Pool is either all busy, or we don't want to give this - * particular guy an ah right now... - * - * Make sure we are on the waiting list, and return that we - * weren't able to provide the ah - */ - _lws_header_ensure_we_are_on_waiting_list(wsi); - - goto bail; - } - - __lws_remove_from_ah_waiting_list(wsi); - - wsi->http.ah = _lws_create_ah(pt, context->max_http_header_data); - if (!wsi->http.ah) { /* we could not create an ah */ - _lws_header_ensure_we_are_on_waiting_list(wsi); - - goto bail; - } - - wsi->http.ah->in_use = 1; - wsi->http.ah->wsi = wsi; /* mark our owner */ - pt->http.ah_count_in_use++; - -#if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || \ - defined(LWS_ROLE_H2)) - lws_context_lock(context, "ah attach"); /* <========================= */ - if (wsi->peer) - wsi->peer->http.count_ah++; - lws_context_unlock(context); /* ====================================> */ -#endif - - _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa); - - lwsl_info("%s: did attach wsi %p: ah %p: count %d (on exit)\n", __func__, - (void *)wsi, (void *)wsi->http.ah, pt->http.ah_count_in_use); - -reset: - __lws_header_table_reset(wsi, autoservice); - - lws_pt_unlock(pt); - -#if defined(LWS_WITH_CLIENT) -#if defined(LWS_ROLE_MQTT) -connect_via_info2: -#endif - if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) - if (!lws_http_client_connect_via_info2(wsi)) - /* our client connect has failed, the wsi - * has been closed - */ - return -1; -#endif - - return 0; - -bail: - lws_pt_unlock(pt); - - return 1; -} - -int __lws_header_table_detach(struct lws *wsi, int autoservice) -{ - struct lws_context *context = wsi->context; - struct allocated_headers *ah = wsi->http.ah; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - struct lws_pollargs pa; - struct lws **pwsi, **pwsi_eligible; - time_t now; - - __lws_remove_from_ah_waiting_list(wsi); - - if (!ah) - return 0; - - lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__, - (void *)wsi, (void *)ah, wsi->tsi, - pt->http.ah_count_in_use); - - /* we did have an ah attached */ - time(&now); - if (ah->assigned && now - ah->assigned > 3) { - /* - * we're detaching the ah, but it was held an - * unreasonably long time - */ - lwsl_debug("%s: wsi %p: ah held %ds, role/state 0x%lx 0x%x," - "\n", __func__, wsi, (int)(now - ah->assigned), - (unsigned long)lwsi_role(wsi), lwsi_state(wsi)); - } - - ah->assigned = 0; - - /* if we think we're detaching one, there should be one in use */ - assert(pt->http.ah_count_in_use > 0); - /* and this specific one should have been in use */ - assert(ah->in_use); - memset(&wsi->http.ah, 0, sizeof(wsi->http.ah)); - -#if defined(LWS_WITH_PEER_LIMITS) - if (ah->wsi) - lws_peer_track_ah_detach(context, wsi->peer); -#endif - ah->wsi = NULL; /* no owner */ - wsi->http.ah = NULL; - - pwsi = &pt->http.ah_wait_list; - - /* oh there is nobody on the waiting list... leave the ah unattached */ - if (!*pwsi) - goto nobody_usable_waiting; - - /* - * at least one wsi on the same tsi is waiting, give it to oldest guy - * who is allowed to take it (if any) - */ - lwsl_info("pt wait list %p\n", *pwsi); - wsi = NULL; - pwsi_eligible = NULL; - - while (*pwsi) { -#if defined(LWS_WITH_PEER_LIMITS) - /* are we willing to give this guy an ah? */ - if (!lws_peer_confirm_ah_attach_ok(context, (*pwsi)->peer)) -#endif - { - wsi = *pwsi; - pwsi_eligible = pwsi; - } -#if defined(LWS_WITH_PEER_LIMITS) - else - if (!(*pwsi)->http.ah_wait_list) - lws_stats_bump(pt, - LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1); -#endif - pwsi = &(*pwsi)->http.ah_wait_list; - } - - if (!wsi) /* everybody waiting already has too many ah... */ - goto nobody_usable_waiting; - - lwsl_info("%s: transferring ah to last eligible wsi in wait list " - "%p (wsistate 0x%lx)\n", __func__, wsi, - (unsigned long)wsi->wsistate); - - wsi->http.ah = ah; - ah->wsi = wsi; /* new owner */ - - __lws_header_table_reset(wsi, autoservice); -#if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || \ - defined(LWS_ROLE_H2)) - lws_context_lock(context, "ah detach"); /* <========================= */ - if (wsi->peer) - wsi->peer->http.count_ah++; - lws_context_unlock(context); /* ====================================> */ -#endif - - /* clients acquire the ah and then insert themselves in fds table... */ - if (wsi->position_in_fds_table != LWS_NO_FDS_POS) { - lwsl_info("%s: Enabling %p POLLIN\n", __func__, wsi); - - /* he has been stuck waiting for an ah, but now his wait is - * over, let him progress */ - - _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa); - } - - /* point prev guy to next guy in list instead */ - *pwsi_eligible = wsi->http.ah_wait_list; - /* the guy who got one is out of the list */ - wsi->http.ah_wait_list = NULL; - pt->http.ah_wait_list_length--; - -#if defined(LWS_WITH_CLIENT) - if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) { - lws_pt_unlock(pt); - - if (!lws_http_client_connect_via_info2(wsi)) { - /* our client connect has failed, the wsi - * has been closed - */ - - return -1; - } - return 0; - } -#endif - - assert(!!pt->http.ah_wait_list_length == - !!(lws_intptr_t)pt->http.ah_wait_list); -bail: - lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__, - (void *)wsi, (void *)ah, pt->tid, pt->http.ah_count_in_use); - - return 0; - -nobody_usable_waiting: - lwsl_info("%s: nobody usable waiting\n", __func__); - _lws_destroy_ah(pt, ah); - pt->http.ah_count_in_use--; - - goto bail; -} - -int lws_header_table_detach(struct lws *wsi, int autoservice) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - int n; - - lws_pt_lock(pt, __func__); - n = __lws_header_table_detach(wsi, autoservice); - lws_pt_unlock(pt); - - return n; -} - -int -lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx) -{ - int n; - - if (!wsi->http.ah) - return 0; - - n = wsi->http.ah->frag_index[h]; - if (!n) - return 0; - do { - if (!frag_idx) - return wsi->http.ah->frags[n].len; - n = wsi->http.ah->frags[n].nfrag; - } while (frag_idx-- && n); - - return 0; -} - -int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h) -{ - int n; - int len = 0; - - if (!wsi->http.ah) - return 0; - - n = wsi->http.ah->frag_index[h]; - if (!n) - return 0; - do { - len += wsi->http.ah->frags[n].len; - n = wsi->http.ah->frags[n].nfrag; - - if (n && h != WSI_TOKEN_HTTP_COOKIE) - ++len; - - } while (n); - - return len; -} - -int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len, - enum lws_token_indexes h, int frag_idx) -{ - int n = 0; - int f; - - if (!wsi->http.ah) - return -1; - - f = wsi->http.ah->frag_index[h]; - - if (!f) - return -1; - - while (n < frag_idx) { - f = wsi->http.ah->frags[f].nfrag; - if (!f) - return -1; - n++; - } - - if (wsi->http.ah->frags[f].len >= len) - return -1; - - memcpy(dst, wsi->http.ah->data + wsi->http.ah->frags[f].offset, - wsi->http.ah->frags[f].len); - dst[wsi->http.ah->frags[f].len] = '\0'; - - return wsi->http.ah->frags[f].len; -} - -int lws_hdr_copy(struct lws *wsi, char *dst, int len, - enum lws_token_indexes h) -{ - int toklen = lws_hdr_total_length(wsi, h); - int n; - int comma; - - *dst = '\0'; - if (!toklen) - return 0; - - if (toklen >= len) - return -1; - - if (!wsi->http.ah) - return -1; - - n = wsi->http.ah->frag_index[h]; - if (!n) - return 0; - - do { - comma = (wsi->http.ah->frags[n].nfrag && - h != WSI_TOKEN_HTTP_COOKIE) ? 1 : 0; - - if (wsi->http.ah->frags[n].len + comma >= len) - return -1; - strncpy(dst, &wsi->http.ah->data[wsi->http.ah->frags[n].offset], - wsi->http.ah->frags[n].len); - dst += wsi->http.ah->frags[n].len; - len -= wsi->http.ah->frags[n].len; - n = wsi->http.ah->frags[n].nfrag; - - if (comma) - *dst++ = ','; - - } while (n); - *dst = '\0'; - - return toklen; -} - -#if defined(LWS_WITH_CUSTOM_HEADERS) -int -lws_hdr_custom_length(struct lws *wsi, const char *name, int nlen) -{ - ah_data_idx_t ll; - - if (!wsi->http.ah || wsi->mux_substream) - return -1; - - ll = wsi->http.ah->unk_ll_head; - while (ll) { - if (ll >= wsi->http.ah->data_length) - return -1; - if (nlen == lws_ser_ru16be( - (uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]) && - !strncmp(name, &wsi->http.ah->data[ll + UHO_NAME], nlen)) - return lws_ser_ru16be( - (uint8_t *)&wsi->http.ah->data[ll + UHO_VLEN]); - - ll = lws_ser_ru32be((uint8_t *)&wsi->http.ah->data[ll + UHO_LL]); - } - - return -1; -} - -int -lws_hdr_custom_copy(struct lws *wsi, char *dst, int len, const char *name, - int nlen) -{ - ah_data_idx_t ll; - int n; - - if (!wsi->http.ah || wsi->mux_substream) - return -1; - - *dst = '\0'; - - ll = wsi->http.ah->unk_ll_head; - while (ll) { - if (ll >= wsi->http.ah->data_length) - return -1; - if (nlen == lws_ser_ru16be( - (uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]) && - !strncmp(name, &wsi->http.ah->data[ll + UHO_NAME], nlen)) { - n = lws_ser_ru16be( - (uint8_t *)&wsi->http.ah->data[ll + UHO_VLEN]); - if (n + 1 > len) - return -1; - strncpy(dst, &wsi->http.ah->data[ll + UHO_NAME + nlen], n); - dst[n] = '\0'; - - return n; - } - ll = lws_ser_ru32be((uint8_t *)&wsi->http.ah->data[ll + UHO_LL]); - } - - return -1; -} -#endif - -char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h) -{ - int n; - - if (!wsi->http.ah) - return NULL; - - n = wsi->http.ah->frag_index[h]; - if (!n) - return NULL; - - return wsi->http.ah->data + wsi->http.ah->frags[n].offset; -} - -static int LWS_WARN_UNUSED_RESULT -lws_pos_in_bounds(struct lws *wsi) -{ - if (!wsi->http.ah) - return -1; - - if (wsi->http.ah->pos < - (unsigned int)wsi->context->max_http_header_data) - return 0; - - if ((int)wsi->http.ah->pos >= wsi->context->max_http_header_data - 1) { - lwsl_err("Ran out of header data space\n"); - return 1; - } - - /* - * with these tests everywhere, it should never be able to exceed - * the limit, only meet it - */ - lwsl_err("%s: pos %ld, limit %ld\n", __func__, - (unsigned long)wsi->http.ah->pos, - (unsigned long)wsi->context->max_http_header_data); - assert(0); - - return 1; -} - -int LWS_WARN_UNUSED_RESULT -lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s) -{ - if (!*s) { - /* - * If we get an empty string, then remove any entry for the - * header - */ - wsi->http.ah->frag_index[h] = 0; - - return 0; - } - - wsi->http.ah->nfrag++; - if (wsi->http.ah->nfrag == LWS_ARRAY_SIZE(wsi->http.ah->frags)) { - lwsl_warn("More hdr frags than we can deal with, dropping\n"); - return -1; - } - - wsi->http.ah->frag_index[h] = wsi->http.ah->nfrag; - - wsi->http.ah->frags[wsi->http.ah->nfrag].offset = wsi->http.ah->pos; - wsi->http.ah->frags[wsi->http.ah->nfrag].len = 0; - wsi->http.ah->frags[wsi->http.ah->nfrag].nfrag = 0; - - do { - if (lws_pos_in_bounds(wsi)) - return -1; - - wsi->http.ah->data[wsi->http.ah->pos++] = *s; - if (*s) - wsi->http.ah->frags[wsi->http.ah->nfrag].len++; - } while (*s++); - - return 0; -} - -static int LWS_WARN_UNUSED_RESULT -issue_char(struct lws *wsi, unsigned char c) -{ - unsigned short frag_len; - - if (lws_pos_in_bounds(wsi)) - return -1; - - frag_len = wsi->http.ah->frags[wsi->http.ah->nfrag].len; - /* - * If we haven't hit the token limit, just copy the character into - * the header - */ - if (!wsi->http.ah->current_token_limit || - frag_len < wsi->http.ah->current_token_limit) { - wsi->http.ah->data[wsi->http.ah->pos++] = c; - if (c) - wsi->http.ah->frags[wsi->http.ah->nfrag].len++; - return 0; - } - - /* Insert a null character when we *hit* the limit: */ - if (frag_len == wsi->http.ah->current_token_limit) { - if (lws_pos_in_bounds(wsi)) - return -1; - - wsi->http.ah->data[wsi->http.ah->pos++] = '\0'; - lwsl_warn("header %li exceeds limit %ld\n", - (long)wsi->http.ah->parser_state, - (long)wsi->http.ah->current_token_limit); - } - - return 1; -} - -int -lws_parse_urldecode(struct lws *wsi, uint8_t *_c) -{ - struct allocated_headers *ah = wsi->http.ah; - unsigned int enc = 0; - uint8_t c = *_c; - - // lwsl_notice("ah->ups %d\n", ah->ups); - - /* - * PRIORITY 1 - * special URI processing... convert %xx - */ - switch (ah->ues) { - case URIES_IDLE: - if (c == '%') { - ah->ues = URIES_SEEN_PERCENT; - goto swallow; - } - break; - case URIES_SEEN_PERCENT: - if (char_to_hex(c) < 0) - /* illegal post-% char */ - goto forbid; - - ah->esc_stash = c; - ah->ues = URIES_SEEN_PERCENT_H1; - goto swallow; - - case URIES_SEEN_PERCENT_H1: - if (char_to_hex(c) < 0) - /* illegal post-% char */ - goto forbid; - - *_c = (char_to_hex(ah->esc_stash) << 4) | - char_to_hex(c); - c = *_c; - enc = 1; - ah->ues = URIES_IDLE; - break; - } - - /* - * PRIORITY 2 - * special URI processing... - * convert /.. or /... or /../ etc to / - * convert /./ to / - * convert // or /// etc to / - * leave /.dir or whatever alone - */ - - switch (ah->ups) { - case URIPS_IDLE: - if (!c) - return -1; - /* genuine delimiter */ - if ((c == '&' || c == ';') && !enc) { - if (issue_char(wsi, '\0') < 0) - return -1; - /* link to next fragment */ - ah->frags[ah->nfrag].nfrag = ah->nfrag + 1; - ah->nfrag++; - if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags)) - goto excessive; - /* start next fragment after the & */ - ah->post_literal_equal = 0; - ah->frags[ah->nfrag].offset = ++ah->pos; - ah->frags[ah->nfrag].len = 0; - ah->frags[ah->nfrag].nfrag = 0; - goto swallow; - } - /* uriencoded = in the name part, disallow */ - if (c == '=' && enc && - ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] && - !ah->post_literal_equal) { - c = '_'; - *_c =c; - } - - /* after the real =, we don't care how many = */ - if (c == '=' && !enc) - ah->post_literal_equal = 1; - - /* + to space */ - if (c == '+' && !enc) { - c = ' '; - *_c = c; - } - /* issue the first / always */ - if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) - ah->ups = URIPS_SEEN_SLASH; - break; - case URIPS_SEEN_SLASH: - /* swallow subsequent slashes */ - if (c == '/') - goto swallow; - /* track and swallow the first . after / */ - if (c == '.') { - ah->ups = URIPS_SEEN_SLASH_DOT; - goto swallow; - } - ah->ups = URIPS_IDLE; - break; - case URIPS_SEEN_SLASH_DOT: - /* swallow second . */ - if (c == '.') { - ah->ups = URIPS_SEEN_SLASH_DOT_DOT; - goto swallow; - } - /* change /./ to / */ - if (c == '/') { - ah->ups = URIPS_SEEN_SLASH; - goto swallow; - } - /* it was like /.dir ... regurgitate the . */ - ah->ups = URIPS_IDLE; - if (issue_char(wsi, '.') < 0) - return -1; - break; - - case URIPS_SEEN_SLASH_DOT_DOT: - - /* /../ or /..[End of URI] --> backup to last / */ - if (c == '/' || c == '?') { - /* - * back up one dir level if possible - * safe against header fragmentation because - * the method URI can only be in 1 fragment - */ - if (ah->frags[ah->nfrag].len > 2) { - ah->pos--; - ah->frags[ah->nfrag].len--; - do { - ah->pos--; - ah->frags[ah->nfrag].len--; - } while (ah->frags[ah->nfrag].len > 1 && - ah->data[ah->pos] != '/'); - } - ah->ups = URIPS_SEEN_SLASH; - if (ah->frags[ah->nfrag].len > 1) - break; - goto swallow; - } - - /* /..[^/] ... regurgitate and allow */ - - if (issue_char(wsi, '.') < 0) - return -1; - if (issue_char(wsi, '.') < 0) - return -1; - ah->ups = URIPS_IDLE; - break; - } - - if (c == '?' && !enc && - !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI args */ - if (ah->ues != URIES_IDLE) - goto forbid; - - /* seal off uri header */ - if (issue_char(wsi, '\0') < 0) - return -1; - - /* move to using WSI_TOKEN_HTTP_URI_ARGS */ - ah->nfrag++; - if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags)) - goto excessive; - ah->frags[ah->nfrag].offset = ++ah->pos; - ah->frags[ah->nfrag].len = 0; - ah->frags[ah->nfrag].nfrag = 0; - - ah->post_literal_equal = 0; - ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag; - ah->ups = URIPS_IDLE; - goto swallow; - } - - return LPUR_CONTINUE; - -swallow: - return LPUR_SWALLOW; - -forbid: - return LPUR_FORBID; - -excessive: - return LPUR_EXCESSIVE; -} - -static const unsigned char methods[] = { - WSI_TOKEN_GET_URI, - WSI_TOKEN_POST_URI, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - WSI_TOKEN_OPTIONS_URI, - WSI_TOKEN_PUT_URI, - WSI_TOKEN_PATCH_URI, - WSI_TOKEN_DELETE_URI, -#endif - WSI_TOKEN_CONNECT, - WSI_TOKEN_HEAD_URI, -}; - -/* - * possible returns:, -1 fail, 0 ok or 2, transition to raw - */ - -lws_parser_return_t LWS_WARN_UNUSED_RESULT -lws_parse(struct lws *wsi, unsigned char *buf, int *len) -{ - struct allocated_headers *ah = wsi->http.ah; - struct lws_context *context = wsi->context; - unsigned int n, m; - unsigned char c; - int r, pos; - - assert(wsi->http.ah); - - do { - (*len)--; - c = *buf++; - - switch (ah->parser_state) { -#if defined(LWS_WITH_CUSTOM_HEADERS) - case WSI_TOKEN_UNKNOWN_VALUE_PART: - - if (c == '\r') - break; - if (c == '\n') { - lws_ser_wu16be((uint8_t *)&ah->data[ah->unk_pos + 2], - ah->pos - ah->unk_value_pos); - ah->parser_state = WSI_TOKEN_NAME_PART; - ah->unk_pos = 0; - ah->lextable_pos = 0; - break; - } - - /* trim leading whitespace */ - if (ah->pos != ah->unk_value_pos || - (c != ' ' && c != '\t')) { - - if (lws_pos_in_bounds(wsi)) - return LPR_FAIL; - - ah->data[ah->pos++] = c; - } - pos = ah->lextable_pos; - break; -#endif - default: - - lwsl_parser("WSI_TOK_(%d) '%c'\n", ah->parser_state, c); - - /* collect into malloc'd buffers */ - /* optional initial space swallow */ - if (!ah->frags[ah->frag_index[ah->parser_state]].len && - c == ' ') - break; - - for (m = 0; m < LWS_ARRAY_SIZE(methods); m++) - if (ah->parser_state == methods[m]) - break; - if (m == LWS_ARRAY_SIZE(methods)) - /* it was not any of the methods */ - goto check_eol; - - /* special URI processing... end at space */ - - if (c == ' ') { - /* enforce starting with / */ - if (!ah->frags[ah->nfrag].len) - if (issue_char(wsi, '/') < 0) - return LPR_FAIL; - - if (ah->ups == URIPS_SEEN_SLASH_DOT_DOT) { - /* - * back up one dir level if possible - * safe against header fragmentation - * because the method URI can only be - * in 1 fragment - */ - if (ah->frags[ah->nfrag].len > 2) { - ah->pos--; - ah->frags[ah->nfrag].len--; - do { - ah->pos--; - ah->frags[ah->nfrag].len--; - } while (ah->frags[ah->nfrag].len > 1 && - ah->data[ah->pos] != '/'); - } - } - - /* begin parsing HTTP version: */ - if (issue_char(wsi, '\0') < 0) - return LPR_FAIL; - ah->parser_state = WSI_TOKEN_HTTP; - goto start_fragment; - } - - r = lws_parse_urldecode(wsi, &c); - switch (r) { - case LPUR_CONTINUE: - break; - case LPUR_SWALLOW: - goto swallow; - case LPUR_FORBID: - goto forbid; - case LPUR_EXCESSIVE: - goto excessive; - default: - return LPR_FAIL; - } -check_eol: - /* bail at EOL */ - if (ah->parser_state != WSI_TOKEN_CHALLENGE && - c == '\x0d') { - if (ah->ues != URIES_IDLE) - goto forbid; - - c = '\0'; - ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR; - lwsl_parser("*\n"); - } - - n = issue_char(wsi, c); - if ((int)n < 0) - return LPR_FAIL; - if (n > 0) - ah->parser_state = WSI_TOKEN_SKIPPING; - -swallow: - /* per-protocol end of headers management */ - - if (ah->parser_state == WSI_TOKEN_CHALLENGE) - goto set_parsing_complete; - break; - - /* collecting and checking a name part */ - case WSI_TOKEN_NAME_PART: - lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X " - "(role=0x%lx) " - "wsi->lextable_pos=%d\n", c, c, - (unsigned long)lwsi_role(wsi), - ah->lextable_pos); - - if (c >= 'A' && c <= 'Z') - c += 'a' - 'A'; - /* - * ...in case it's an unknown header, speculatively - * store it as the name comes in. If we recognize it as - * a known header, we'll snip this. - */ - - if (!wsi->mux_substream && !ah->unk_pos) { - ah->unk_pos = ah->pos; -#if defined(LWS_WITH_CUSTOM_HEADERS) - /* - * Prepare new unknown header linked-list entry - * - * - 16-bit BE: name part length - * - 16-bit BE: value part length - * - 32-bit BE: data offset of next, or 0 - */ - for (n = 0; n < 8; n++) - if (!lws_pos_in_bounds(wsi)) - ah->data[ah->pos++] = 0; -#endif - } - - if (lws_pos_in_bounds(wsi)) - return LPR_FAIL; - - ah->data[ah->pos++] = c; - pos = ah->lextable_pos; - -#if defined(LWS_WITH_CUSTOM_HEADERS) - if (!wsi->mux_substream && pos < 0 && c == ':') { -#if defined(_DEBUG) - char dotstar[64]; - int uhlen; -#endif - - /* - * process unknown headers - * - * register us in the unknown hdr ll - */ - - if (!ah->unk_ll_head) - ah->unk_ll_head = ah->unk_pos; - - if (ah->unk_ll_tail) - lws_ser_wu32be( - (uint8_t *)&ah->data[ah->unk_ll_tail + UHO_LL], - ah->unk_pos); - - ah->unk_ll_tail = ah->unk_pos; - -#if defined(_DEBUG) - uhlen = ah->pos - (ah->unk_pos + UHO_NAME); - lws_strnncpy(dotstar, - &ah->data[ah->unk_pos + UHO_NAME], - uhlen, sizeof(dotstar)); - lwsl_debug("%s: unk header %d '%s'\n", - __func__, - ah->pos - (ah->unk_pos + UHO_NAME), - dotstar); -#endif - - /* set the unknown header name part length */ - - lws_ser_wu16be((uint8_t *)&ah->data[ah->unk_pos], - (ah->pos - ah->unk_pos) - UHO_NAME); - - ah->unk_value_pos = ah->pos; - - /* - * collect whatever's coming for the unknown header - * argument until the next CRLF - */ - ah->parser_state = WSI_TOKEN_UNKNOWN_VALUE_PART; - break; - } -#endif - if (pos < 0) - break; - - while (1) { - if (lextable_h1[pos] & (1 << 7)) { - /* 1-byte, fail on mismatch */ - if ((lextable_h1[pos] & 0x7f) != c) { -nope: - ah->lextable_pos = -1; - break; - } - /* fall thru */ - pos++; - if (lextable_h1[pos] == FAIL_CHAR) - goto nope; - - ah->lextable_pos = pos; - break; - } - - if (lextable_h1[pos] == FAIL_CHAR) - goto nope; - - /* b7 = 0, end or 3-byte */ - if (lextable_h1[pos] < FAIL_CHAR) { - if (!wsi->mux_substream) { - /* - * We hit a terminal marker, so - * we recognized this header... - * drop the speculative name - * part storage - */ - ah->pos = ah->unk_pos; - ah->unk_pos = 0; - } - - ah->lextable_pos = pos; - break; - } - - if (lextable_h1[pos] == c) { /* goto */ - ah->lextable_pos = pos + - (lextable_h1[pos + 1]) + - (lextable_h1[pos + 2] << 8); - break; - } - - /* fall thru goto */ - pos += 3; - /* continue */ - } - - /* - * If it's h1, server needs to be on the look out for - * unknown methods... - */ - if (ah->lextable_pos < 0 && lwsi_role_h1(wsi) && - lwsi_role_server(wsi)) { - /* - * this is not a header we know about... did - * we get a valid method (GET, POST etc) - * already, or is this the bogus method? - */ - for (m = 0; m < LWS_ARRAY_SIZE(methods); m++) - if (ah->frag_index[methods[m]]) { - /* - * already had the method - */ -#if !defined(LWS_WITH_CUSTOM_HEADERS) - ah->parser_state = WSI_TOKEN_SKIPPING; -#endif - if (wsi->mux_substream) - ah->parser_state = WSI_TOKEN_SKIPPING; - break; - } - - if (m != LWS_ARRAY_SIZE(methods)) { -#if defined(LWS_WITH_CUSTOM_HEADERS) - /* - * We have the method, this is just an - * unknown header then - */ - if (!wsi->mux_substream) - goto unknown_hdr; - else - break; -#else - break; -#endif - } - /* - * ...it's an unknown http method from a client - * in fact, it cannot be valid http. - * - * Are we set up to transition to another role - * in these cases? - */ - if (lws_check_opt(wsi->vhost->options, - LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG)) { - lwsl_notice("%s: http fail fallback\n", - __func__); - /* transition to other role */ - return LPR_DO_FALLBACK; - } - - lwsl_info("Unknown method - dropping\n"); - goto forbid; - } - if (ah->lextable_pos < 0) { -#if defined(LWS_WITH_CUSTOM_HEADERS) - if (!wsi->mux_substream) - goto unknown_hdr; -#endif - /* - * ...otherwise for a client, let him ignore - * unknown headers coming from the server - */ - ah->parser_state = WSI_TOKEN_SKIPPING; - break; - } - - if (lextable_h1[ah->lextable_pos] < FAIL_CHAR) { - /* terminal state */ - - n = ((unsigned int)lextable_h1[ah->lextable_pos] << 8) | - lextable_h1[ah->lextable_pos + 1]; - - lwsl_parser("known hdr %d\n", n); - for (m = 0; m < LWS_ARRAY_SIZE(methods); m++) - if (n == methods[m] && - ah->frag_index[methods[m]]) { - lwsl_warn("Duplicated method\n"); - return LPR_FAIL; - } - - /* - * WSORIGIN is protocol equiv to ORIGIN, - * JWebSocket likes to send it, map to ORIGIN - */ -#if defined(LWS_ROLE_WS) - if (n == WSI_TOKEN_SWORIGIN) - n = WSI_TOKEN_ORIGIN; -#endif - - ah->parser_state = (enum lws_token_indexes) - (WSI_TOKEN_GET_URI + n); - ah->ups = URIPS_IDLE; - - if (context->token_limits) - ah->current_token_limit = context-> - token_limits->token_limit[ - ah->parser_state]; - else - ah->current_token_limit = - wsi->context->max_http_header_data; - - if (ah->parser_state == WSI_TOKEN_CHALLENGE) - goto set_parsing_complete; - - goto start_fragment; - } - break; - -#if defined(LWS_WITH_CUSTOM_HEADERS) -unknown_hdr: - //ah->parser_state = WSI_TOKEN_SKIPPING; - //break; - if (!wsi->mux_substream) - break; -#endif - -start_fragment: - ah->nfrag++; -excessive: - if (ah->nfrag == LWS_ARRAY_SIZE(ah->frags)) { - lwsl_warn("More hdr frags than we can deal with\n"); - return LPR_FAIL; - } - - ah->frags[ah->nfrag].offset = ah->pos; - ah->frags[ah->nfrag].len = 0; - ah->frags[ah->nfrag].nfrag = 0; - ah->frags[ah->nfrag].flags = 2; - - n = ah->frag_index[ah->parser_state]; - if (!n) { /* first fragment */ - ah->frag_index[ah->parser_state] = ah->nfrag; - ah->hdr_token_idx = ah->parser_state; - break; - } - /* continuation */ - while (ah->frags[n].nfrag) - n = ah->frags[n].nfrag; - ah->frags[n].nfrag = ah->nfrag; - - if (issue_char(wsi, ' ') < 0) - return LPR_FAIL; - break; - - /* skipping arg part of a name we didn't recognize */ - case WSI_TOKEN_SKIPPING: - lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c); - - if (c == '\x0d') - ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR; - break; - - case WSI_TOKEN_SKIPPING_SAW_CR: - lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c); - if (ah->ues != URIES_IDLE) - goto forbid; - if (c == '\x0a') { - ah->parser_state = WSI_TOKEN_NAME_PART; - ah->unk_pos = ah->lextable_pos = 0; - } else - ah->parser_state = WSI_TOKEN_SKIPPING; - break; - /* we're done, ignore anything else */ - - case WSI_PARSING_COMPLETE: - lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c); - break; - } - - } while (*len); - - return LPR_OK; - -set_parsing_complete: - if (ah->ues != URIES_IDLE) - goto forbid; - - if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) { -#if defined(LWS_ROLE_WS) - if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION)) - wsi->rx_frame_type = /* temp for ws version index */ - atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION)); - - lwsl_parser("v%02d hdrs done\n", wsi->rx_frame_type); -#endif - } - ah->parser_state = WSI_PARSING_COMPLETE; - wsi->hdr_parsing_completed = 1; - - return LPR_OK; - -forbid: - lwsl_info(" forbidding on uri sanitation\n"); -#if defined(LWS_WITH_SERVER) - lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); -#endif - - return LPR_FORBIDDEN; -} - diff -Nru libwebsockets-4.0.20/lib/roles/http/private-lib-roles-http.h libwebsockets-2.4.2/lib/roles/http/private-lib-roles-http.h --- libwebsockets-4.0.20/lib/roles/http/private-lib-roles-http.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/private-lib-roles-http.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,331 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - #include - #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 - -/* - * 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 -#ifdef LWS_WITH_CGI - struct lws_cgi *cgi; /* wsi being cgi master 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; -}; - - -#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_client_create_tls(struct lws *wsi, const char **pcce, int do_c1); diff -Nru libwebsockets-4.0.20/lib/roles/http/server/access-log.c libwebsockets-2.4.2/lib/roles/http/server/access-log.c --- libwebsockets-4.0.20/lib/roles/http/server/access-log.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/server/access-log.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,195 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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]; - time_t t = time(NULL); - struct lws *nwsi; - const char *me; - int l = 256, m; - struct tm *tmp; - - if (!wsi->vhost) - return; - - /* only worry about preparing it if we store it */ - if (wsi->vhost->log_fd == (int)LWS_INVALID_FILE) - return; - - if (wsi->access_log_pending) - lws_access_log(wsi); - - wsi->http.access_log.header_log = lws_malloc(l, "access log"); - if (!wsi->http.access_log.header_log) - return; - - tmp = localtime(&t); - if (tmp) - strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", tmp); - else - strcpy(da, "01/Jan/1970:00:00:00 +0000"); - - if (wsi->mux_substream) - me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD); - else - 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, m); - uri[m] = '\0'; - - nwsi = lws_get_network_wsi(wsi); - - lws_snprintf(wsi->http.access_log.header_log, l, - "%s - - [%s] \"%s %s %s\"", - nwsi->simple_ip[0] ? nwsi->simple_ip : "unknown", 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(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(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->vhost) - return 0; - - if (wsi->vhost->log_fd == (int)LWS_INVALID_FILE) - return 0; - - if (!wsi->access_log_pending) - return 0; - - if (!wsi->http.access_log.header_log) - return 0; - - if (!p) - p = ""; - - if (!p1) - p1 = ""; - - /* - * We do this in two parts to restrict an oversize referrer such that - * we will always have space left to append an empty useragent, while - * maintaining the structure of the log text - */ - l = lws_snprintf(ass, sizeof(ass) - 7, "%s %d %lu \"%s", - wsi->http.access_log.header_log, - wsi->http.access_log.response, - wsi->http.access_log.sent, p1); - if (strlen(p) > sizeof(ass) - 6 - l) { - p[sizeof(ass) - 6 - l] = '\0'; - l--; - } - l += lws_snprintf(ass + l, sizeof(ass) - 1 - l, "\" \"%s\"\n", p); - - ass[sizeof(ass) - 1] = '\0'; - - if (write(wsi->vhost->log_fd, ass, l) != l) - lwsl_err("Failed to write log\n"); - - if (wsi->http.access_log.header_log) { - lws_free(wsi->http.access_log.header_log); - wsi->http.access_log.header_log = NULL; - } - if (wsi->http.access_log.user_agent) { - lws_free(wsi->http.access_log.user_agent); - wsi->http.access_log.user_agent = NULL; - } - if (wsi->http.access_log.referrer) { - lws_free(wsi->http.access_log.referrer); - wsi->http.access_log.referrer = NULL; - } - wsi->access_log_pending = 0; - - return 0; -} - diff -Nru libwebsockets-4.0.20/lib/roles/http/server/fops-zip.c libwebsockets-2.4.2/lib/roles/http/server/fops-zip.c --- libwebsockets-4.0.20/lib/roles/http/server/fops-zip.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/server/fops-zip.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,654 +0,0 @@ -/* - * 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 - * - * 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 -#else -#include -#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, -}; - -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, 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, - 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, - 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, 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, 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, m + 1); - - /* open the zip file itself using the incoming fops, not fops_zip */ - - priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops, rp, NULL, &local_flags); - if (!priv->zip_fop_fd) { - lwsl_err("unable to open zip %s\n", 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 += offset_from_cur_pos; - - return 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 > priv->hdr.comp_size - - (cur - priv->content_start)) - rlen = priv->hdr.comp_size - - (priv->hdr.comp_size - - 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 > priv->hdr.uncomp_size - (cur - priv->content_start)) - len = priv->hdr.comp_size - (priv->hdr.comp_size - - priv->content_start); - - if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd, - amount, buf, len)) - return LWS_FZ_ERR_READ_CONTENT; - - 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, -}; diff -Nru libwebsockets-4.0.20/lib/roles/http/server/lejp-conf.c libwebsockets-2.4.2/lib/roles/http/server/lejp-conf.c --- libwebsockets-4.0.20/lib/roles/http/server/lejp-conf.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/server/lejp-conf.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1061 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -#ifndef _WIN32 -/* this is needed for Travis CI */ -#include -#endif - -#define ESC_INSTALL_DATADIR "_lws_ddir_" - -static const char * const paths_global[] = { - "global.uid", - "global.gid", - "global.username", - "global.groupname", - "global.count-threads", - "global.init-ssl", - "global.server-string", - "global.plugin-dir", - "global.ws-pingpong-secs", - "global.timeout-secs", - "global.reject-service-keywords[].*", - "global.reject-service-keywords[]", - "global.default-alpn", - "global.ip-limit-ah", - "global.ip-limit-wsi", -}; - -enum lejp_global_paths { - LEJPGP_UID, - LEJPGP_GID, - LEJPGP_USERNAME, - LEJPGP_GROUPNAME, - LEJPGP_COUNT_THREADS, - LWJPGP_INIT_SSL, - LEJPGP_SERVER_STRING, - LEJPGP_PLUGIN_DIR, - LWJPGP_PINGPONG_SECS, - LWJPGP_TIMEOUT_SECS, - LWJPGP_REJECT_SERVICE_KEYWORDS_NAME, - LWJPGP_REJECT_SERVICE_KEYWORDS, - LWJPGP_DEFAULT_ALPN, - LWJPGP_IP_LIMIT_AH, - LWJPGP_IP_LIMIT_WSI, -}; - -static const char * const paths_vhosts[] = { - "vhosts[]", - "vhosts[].mounts[]", - "vhosts[].name", - "vhosts[].port", - "vhosts[].interface", - "vhosts[].unix-socket", - "vhosts[].unix-socket-perms", - "vhosts[].sts", - "vhosts[].host-ssl-key", - "vhosts[].host-ssl-cert", - "vhosts[].host-ssl-ca", - "vhosts[].access-log", - "vhosts[].mounts[].mountpoint", - "vhosts[].mounts[].origin", - "vhosts[].mounts[].protocol", - "vhosts[].mounts[].default", - "vhosts[].mounts[].auth-mask", - "vhosts[].mounts[].cgi-timeout", - "vhosts[].mounts[].cgi-env[].*", - "vhosts[].mounts[].cache-max-age", - "vhosts[].mounts[].cache-reuse", - "vhosts[].mounts[].cache-revalidate", - "vhosts[].mounts[].basic-auth", - "vhosts[].mounts[].cache-intermediaries", - "vhosts[].mounts[].extra-mimetypes.*", - "vhosts[].mounts[].interpret.*", - "vhosts[].ws-protocols[].*.*", - "vhosts[].ws-protocols[].*", - "vhosts[].ws-protocols[]", - "vhosts[].keepalive_timeout", - "vhosts[].enable-client-ssl", - "vhosts[].ciphers", - "vhosts[].ecdh-curve", - "vhosts[].noipv6", - "vhosts[].ipv6only", - "vhosts[].ssl-option-set", - "vhosts[].ssl-option-clear", - "vhosts[].mounts[].pmo[].*", - "vhosts[].headers[].*", - "vhosts[].headers[]", - "vhosts[].client-ssl-key", - "vhosts[].client-ssl-cert", - "vhosts[].client-ssl-ca", - "vhosts[].client-ssl-ciphers", - "vhosts[].onlyraw", - "vhosts[].client-cert-required", - "vhosts[].ignore-missing-cert", - "vhosts[].error-document-404", - "vhosts[].alpn", - "vhosts[].ssl-client-option-set", - "vhosts[].ssl-client-option-clear", - "vhosts[].tls13-ciphers", - "vhosts[].client-tls13-ciphers", - "vhosts[].strict-host-check", - - "vhosts[].listen-accept-role", - "vhosts[].listen-accept-protocol", - "vhosts[].apply-listen-accept", /* deprecates "onlyraw" */ - "vhosts[].fallback-listen-accept", - "vhosts[].allow-non-tls", - "vhosts[].redirect-http", - "vhosts[].allow-http-on-https", - - "vhosts[].disable-no-protocol-ws-upgrades", - "vhosts[].h2-half-closed-long-poll", -}; - -enum lejp_vhost_paths { - LEJPVP, - LEJPVP_MOUNTS, - LEJPVP_NAME, - LEJPVP_PORT, - LEJPVP_INTERFACE, - LEJPVP_UNIXSKT, - LEJPVP_UNIXSKT_PERMS, - LEJPVP_STS, - LEJPVP_HOST_SSL_KEY, - LEJPVP_HOST_SSL_CERT, - LEJPVP_HOST_SSL_CA, - LEJPVP_ACCESS_LOG, - LEJPVP_MOUNTPOINT, - LEJPVP_ORIGIN, - LEJPVP_MOUNT_PROTOCOL, - LEJPVP_DEFAULT, - LEJPVP_DEFAULT_AUTH_MASK, - LEJPVP_CGI_TIMEOUT, - LEJPVP_CGI_ENV, - LEJPVP_MOUNT_CACHE_MAX_AGE, - LEJPVP_MOUNT_CACHE_REUSE, - LEJPVP_MOUNT_CACHE_REVALIDATE, - LEJPVP_MOUNT_BASIC_AUTH, - LEJPVP_MOUNT_CACHE_INTERMEDIARIES, - LEJPVP_MOUNT_EXTRA_MIMETYPES, - LEJPVP_MOUNT_INTERPRET, - LEJPVP_PROTOCOL_NAME_OPT, - LEJPVP_PROTOCOL_NAME, - LEJPVP_PROTOCOL, - LEJPVP_KEEPALIVE_TIMEOUT, - LEJPVP_ENABLE_CLIENT_SSL, - LEJPVP_CIPHERS, - LEJPVP_ECDH_CURVE, - LEJPVP_NOIPV6, - LEJPVP_IPV6ONLY, - LEJPVP_SSL_OPTION_SET, - LEJPVP_SSL_OPTION_CLEAR, - LEJPVP_PMO, - LEJPVP_HEADERS_NAME, - LEJPVP_HEADERS, - LEJPVP_CLIENT_SSL_KEY, - LEJPVP_CLIENT_SSL_CERT, - LEJPVP_CLIENT_SSL_CA, - LEJPVP_CLIENT_CIPHERS, - LEJPVP_FLAG_ONLYRAW, - LEJPVP_FLAG_CLIENT_CERT_REQUIRED, - LEJPVP_IGNORE_MISSING_CERT, - LEJPVP_ERROR_DOCUMENT_404, - LEJPVP_ALPN, - LEJPVP_SSL_CLIENT_OPTION_SET, - LEJPVP_SSL_CLIENT_OPTION_CLEAR, - LEJPVP_TLS13_CIPHERS, - LEJPVP_CLIENT_TLS13_CIPHERS, - LEJPVP_FLAG_STRICT_HOST_CHECK, - - LEJPVP_LISTEN_ACCEPT_ROLE, - LEJPVP_LISTEN_ACCEPT_PROTOCOL, - LEJPVP_FLAG_APPLY_LISTEN_ACCEPT, - LEJPVP_FLAG_FALLBACK_LISTEN_ACCEPT, - LEJPVP_FLAG_ALLOW_NON_TLS, - LEJPVP_FLAG_REDIRECT_HTTP, - LEJPVP_FLAG_ALLOW_HTTP_ON_HTTPS, - - LEJPVP_FLAG_DISABLE_NO_PROTOCOL_WS_UPGRADES, - LEJPVP_FLAG_H2_HALF_CLOSED_LONG_POLL, -}; - -#define MAX_PLUGIN_DIRS 10 - -struct jpargs { - struct lws_context_creation_info *info; - struct lws_context *context; - const struct lws_protocols *protocols; - const struct lws_protocols **pprotocols; - const struct lws_extension *extensions; - char *p, *end, valid; - struct lws_http_mount *head, *last; - - struct lws_protocol_vhost_options *pvo; - struct lws_protocol_vhost_options *pvo_em; - struct lws_protocol_vhost_options *pvo_int; - struct lws_http_mount m; - const char **plugin_dirs; - int count_plugin_dirs; - - unsigned int reject_ws_with_no_protocol:1; - unsigned int enable_client_ssl:1; - unsigned int fresh_mount:1; - unsigned int any_vhosts:1; - unsigned int chunk:1; -}; - -static void * -lwsws_align(struct jpargs *a) -{ - if ((lws_intptr_t)(a->p) & 15) - a->p += 16 - ((lws_intptr_t)(a->p) & 15); - - a->chunk = 0; - - return a->p; -} - -static int -arg_to_bool(const char *s) -{ - static const char * const on[] = { "on", "yes", "true" }; - int n = atoi(s); - - if (n) - return 1; - - for (n = 0; n < (int)LWS_ARRAY_SIZE(on); n++) - if (!strcasecmp(s, on[n])) - return 1; - - return 0; -} - -static void -set_reset_flag(uint64_t *p, const char *state, uint64_t flag) -{ - if (arg_to_bool(state)) - *p |= flag; - else - *p &= ~(flag); -} - -static signed char -lejp_globals_cb(struct lejp_ctx *ctx, char reason) -{ - struct jpargs *a = (struct jpargs *)ctx->user; - struct lws_protocol_vhost_options *rej; - int n; - - /* we only match on the prepared path strings */ - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - /* this catches, eg, vhosts[].headers[].xxx */ - if (reason == LEJPCB_VAL_STR_END && - ctx->path_match == LWJPGP_REJECT_SERVICE_KEYWORDS_NAME + 1) { - rej = lwsws_align(a); - a->p += sizeof(*rej); - - n = lejp_get_wildcard(ctx, 0, a->p, lws_ptr_diff(a->end, a->p)); - rej->next = a->info->reject_service_keywords; - a->info->reject_service_keywords = rej; - rej->name = a->p; - lwsl_notice(" adding rej %s=%s\n", a->p, ctx->buf); - a->p += n - 1; - *(a->p++) = '\0'; - rej->value = a->p; - rej->options = NULL; - goto dostring; - } - - switch (ctx->path_match - 1) { - case LEJPGP_UID: - a->info->uid = atoi(ctx->buf); - return 0; - case LEJPGP_GID: - a->info->gid = atoi(ctx->buf); - return 0; - case LEJPGP_USERNAME: - a->info->username = a->p; - break; - case LEJPGP_GROUPNAME: - a->info->groupname = a->p; - break; - case LEJPGP_COUNT_THREADS: - a->info->count_threads = atoi(ctx->buf); - return 0; - case LWJPGP_INIT_SSL: - if (arg_to_bool(ctx->buf)) - a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - return 0; - case LEJPGP_SERVER_STRING: -#if defined(LWS_WITH_SERVER) - a->info->server_string = a->p; -#endif - break; - case LEJPGP_PLUGIN_DIR: - if (a->count_plugin_dirs == MAX_PLUGIN_DIRS - 1) { - lwsl_err("Too many plugin dirs\n"); - return -1; - } - a->plugin_dirs[a->count_plugin_dirs++] = a->p; - break; - - case LWJPGP_PINGPONG_SECS: - a->info->ws_ping_pong_interval = atoi(ctx->buf); - return 0; - - case LWJPGP_TIMEOUT_SECS: - a->info->timeout_secs = atoi(ctx->buf); - return 0; - - case LWJPGP_DEFAULT_ALPN: - a->info->alpn = a->p; - break; - - case LWJPGP_IP_LIMIT_AH: - a->info->ip_limit_ah = atoi(ctx->buf); - return 0; - - case LWJPGP_IP_LIMIT_WSI: - a->info->ip_limit_wsi = atoi(ctx->buf); - return 0; - - default: - return 0; - } - -dostring: - a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf); - *(a->p)++ = '\0'; - - return 0; -} - -static signed char -lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) -{ - struct jpargs *a = (struct jpargs *)ctx->user; - struct lws_protocol_vhost_options *pvo, *mp_cgienv, *headers; - struct lws_http_mount *m; - char *p, *p1; - int n; - -#if 0 - lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match); - for (n = 0; n < ctx->wildcount; n++) - lwsl_notice(" %d\n", ctx->wild[n]); -#endif - - if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) { - uint32_t i[4]; -#if defined(LWS_WITH_SERVER) - const char *ss; -#endif - - /* set the defaults for this vhost */ - a->reject_ws_with_no_protocol = 0; - a->valid = 1; - a->head = NULL; - a->last = NULL; - - i[0] = a->info->count_threads; - i[1] = a->info->options & ( - LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME | - LWS_SERVER_OPTION_LIBUV | - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN | - LWS_SERVER_OPTION_LIBEVENT | - LWS_SERVER_OPTION_LIBEV - ); -#if defined(LWS_WITH_SERVER) - ss = a->info->server_string; -#endif - i[2] = a->info->ws_ping_pong_interval; - i[3] = a->info->timeout_secs; - - memset(a->info, 0, sizeof(*a->info)); - - a->info->count_threads = i[0]; - a->info->options = i[1]; -#if defined(LWS_WITH_SERVER) - a->info->server_string = ss; -#endif - a->info->ws_ping_pong_interval = i[2]; - a->info->timeout_secs = i[3]; - - a->info->protocols = a->protocols; - a->info->pprotocols = a->pprotocols; - a->info->extensions = a->extensions; -#if defined(LWS_WITH_TLS) - a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:" - "ECDHE-RSA-AES256-GCM-SHA384:" - "DHE-RSA-AES256-GCM-SHA384:" - "ECDHE-RSA-AES256-SHA384:" - "HIGH:!aNULL:!eNULL:!EXPORT:" - "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:" - "!SHA1:!DHE-RSA-AES128-GCM-SHA256:" - "!DHE-RSA-AES128-SHA256:" - "!AES128-GCM-SHA256:" - "!AES128-SHA256:" - "!DHE-RSA-AES256-SHA256:" - "!AES256-GCM-SHA384:" - "!AES256-SHA256"; -#endif - a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:" - "ECDHE-RSA-AES256-GCM-SHA384:" - "DHE-RSA-AES256-GCM-SHA384:" - "ECDHE-RSA-AES256-SHA384:" - "HIGH:!aNULL:!eNULL:!EXPORT:" - "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:" - "!SHA1:!DHE-RSA-AES128-GCM-SHA256:" - "!DHE-RSA-AES128-SHA256:" - "!AES128-GCM-SHA256:" - "!AES128-SHA256:" - "!DHE-RSA-AES256-SHA256:" - "!AES256-GCM-SHA384:" - "!AES256-SHA256"; - a->info->keepalive_timeout = 5; - } - - if (reason == LEJPCB_OBJECT_START && - ctx->path_match == LEJPVP_MOUNTS + 1) { - a->fresh_mount = 1; - memset(&a->m, 0, sizeof(a->m)); - } - - /* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */ - if (reason == LEJPCB_OBJECT_START && - ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) { - a->pvo = lwsws_align(a); - a->p += sizeof(*a->pvo); - - n = lejp_get_wildcard(ctx, 0, a->p, lws_ptr_diff(a->end, a->p)); - /* ie, enable this protocol, no options yet */ - a->pvo->next = a->info->pvo; - a->info->pvo = a->pvo; - a->pvo->name = a->p; - lwsl_info(" adding protocol %s\n", a->p); - a->p += n; - a->pvo->value = a->p; - a->pvo->options = NULL; - goto dostring; - } - - /* this catches, eg, vhosts[].headers[].xxx */ - if ((reason == LEJPCB_VAL_STR_END || reason == LEJPCB_VAL_STR_CHUNK) && - ctx->path_match == LEJPVP_HEADERS_NAME + 1) { - - if (!a->chunk) { - headers = lwsws_align(a); - a->p += sizeof(*headers); - - n = lejp_get_wildcard(ctx, 0, a->p, - lws_ptr_diff(a->end, a->p)); - /* ie, add this header */ - headers->next = a->info->headers; - a->info->headers = headers; - headers->name = a->p; - - lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf); - a->p += n - 1; - *(a->p++) = ':'; - if (a->p < a->end) - *(a->p++) = '\0'; - else - *(a->p - 1) = '\0'; - headers->value = a->p; - headers->options = NULL; - } - a->chunk = reason == LEJPCB_VAL_STR_CHUNK; - goto dostring; - } - - if (reason == LEJPCB_OBJECT_END && - (ctx->path_match == LEJPVP + 1 || !ctx->path[0]) && - a->valid) { - - struct lws_vhost *vhost; - - //lwsl_notice("%s\n", ctx->path); - if (!a->info->port && - !(a->info->options & LWS_SERVER_OPTION_UNIX_SOCK)) { - lwsl_err("Port required (eg, 443)\n"); - return 1; - } - a->valid = 0; - a->info->mounts = a->head; - - vhost = lws_create_vhost(a->context, a->info); - if (!vhost) { - lwsl_err("Failed to create vhost %s\n", - a->info->vhost_name); - return 1; - } - a->any_vhosts = 1; - - if (a->reject_ws_with_no_protocol) { - a->reject_ws_with_no_protocol = 0; - - vhost->default_protocol_index = 255; - } - -#if defined(LWS_WITH_TLS) - if (a->enable_client_ssl) { - const char *cert_filepath = - a->info->client_ssl_cert_filepath; - const char *private_key_filepath = - a->info->client_ssl_private_key_filepath; - const char *ca_filepath = - a->info->client_ssl_ca_filepath; - const char *cipher_list = - a->info->client_ssl_cipher_list; - - memset(a->info, 0, sizeof(*a->info)); - a->info->client_ssl_cert_filepath = cert_filepath; - a->info->client_ssl_private_key_filepath = - private_key_filepath; - a->info->client_ssl_ca_filepath = ca_filepath; - a->info->client_ssl_cipher_list = cipher_list; - a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - lws_init_vhost_client_ssl(a->info, vhost); - } -#endif - - return 0; - } - - if (reason == LEJPCB_OBJECT_END && - ctx->path_match == LEJPVP_MOUNTS + 1) { - static const char * const mount_protocols[] = { - "http://", - "https://", - "file://", - "cgi://", - ">http://", - ">https://", - "callback://", - "gzip://", - }; - - if (!a->fresh_mount) - return 0; - - if (!a->m.mountpoint || !a->m.origin) { - lwsl_err("mountpoint and origin required\n"); - return 1; - } - lwsl_debug("adding mount %s\n", a->m.mountpoint); - m = lwsws_align(a); - memcpy(m, &a->m, sizeof(*m)); - if (a->last) - a->last->mount_next = m; - - for (n = 0; n < (int)LWS_ARRAY_SIZE(mount_protocols); n++) - if (!strncmp(a->m.origin, mount_protocols[n], - strlen(mount_protocols[n]))) { - lwsl_info("----%s\n", a->m.origin); - m->origin_protocol = n; - m->origin = a->m.origin + - strlen(mount_protocols[n]); - break; - } - - if (n == (int)LWS_ARRAY_SIZE(mount_protocols)) { - lwsl_err("unsupported protocol:// %s\n", a->m.origin); - return 1; - } - - a->p += sizeof(*m); - if (!a->head) - a->head = m; - - a->last = m; - a->fresh_mount = 0; - } - - /* we only match on the prepared path strings */ - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - switch (ctx->path_match - 1) { - case LEJPVP_NAME: - a->info->vhost_name = a->p; - break; - case LEJPVP_PORT: - a->info->port = atoi(ctx->buf); - return 0; - case LEJPVP_INTERFACE: - a->info->iface = a->p; - break; - case LEJPVP_UNIXSKT: - if (arg_to_bool(ctx->buf)) - a->info->options |= LWS_SERVER_OPTION_UNIX_SOCK; - else - a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK); - return 0; - case LEJPVP_UNIXSKT_PERMS: - a->info->unix_socket_perms = a->p; - break; - case LEJPVP_STS: - if (arg_to_bool(ctx->buf)) - a->info->options |= LWS_SERVER_OPTION_STS; - else - a->info->options &= ~(LWS_SERVER_OPTION_STS); - return 0; - case LEJPVP_HOST_SSL_KEY: - a->info->ssl_private_key_filepath = a->p; - break; - case LEJPVP_HOST_SSL_CERT: - a->info->ssl_cert_filepath = a->p; - break; - case LEJPVP_HOST_SSL_CA: - a->info->ssl_ca_filepath = a->p; - break; - case LEJPVP_ACCESS_LOG: - a->info->log_filepath = a->p; - break; - case LEJPVP_MOUNTPOINT: - a->m.mountpoint = a->p; - a->m.mountpoint_len = (unsigned char)strlen(ctx->buf); - break; - case LEJPVP_ORIGIN: - if (!strncmp(ctx->buf, "callback://", 11)) - a->m.protocol = a->p + 11; - - if (!a->m.origin) - a->m.origin = a->p; - break; - case LEJPVP_DEFAULT: - a->m.def = a->p; - break; - case LEJPVP_DEFAULT_AUTH_MASK: - a->m.auth_mask = atoi(ctx->buf); - return 0; - case LEJPVP_MOUNT_CACHE_MAX_AGE: - a->m.cache_max_age = atoi(ctx->buf); - return 0; - case LEJPVP_MOUNT_CACHE_REUSE: - a->m.cache_reusable = arg_to_bool(ctx->buf); - return 0; - case LEJPVP_MOUNT_CACHE_REVALIDATE: - a->m.cache_revalidate = arg_to_bool(ctx->buf); - return 0; - case LEJPVP_MOUNT_CACHE_INTERMEDIARIES: - a->m.cache_intermediaries = arg_to_bool(ctx->buf);; - return 0; - case LEJPVP_MOUNT_BASIC_AUTH: -#if defined(LWS_WITH_HTTP_BASIC_AUTH) - a->m.basic_auth_login_file = a->p; -#endif - break; - case LEJPVP_CGI_TIMEOUT: - a->m.cgi_timeout = atoi(ctx->buf); - return 0; - case LEJPVP_KEEPALIVE_TIMEOUT: - a->info->keepalive_timeout = atoi(ctx->buf); - return 0; -#if defined(LWS_WITH_TLS) - case LEJPVP_CLIENT_CIPHERS: - a->info->client_ssl_cipher_list = a->p; - break; -#endif - case LEJPVP_CIPHERS: - a->info->ssl_cipher_list = a->p; - break; - case LEJPVP_TLS13_CIPHERS: - a->info->tls1_3_plus_cipher_list = a->p; - break; - case LEJPVP_CLIENT_TLS13_CIPHERS: - a->info->client_tls_1_3_plus_cipher_list = a->p; - break; - - case LEJPVP_ECDH_CURVE: - a->info->ecdh_curve = a->p; - break; - case LEJPVP_PMO: - case LEJPVP_CGI_ENV: - mp_cgienv = lwsws_align(a); - a->p += sizeof(*a->m.cgienv); - - mp_cgienv->next = a->m.cgienv; - a->m.cgienv = mp_cgienv; - - n = lejp_get_wildcard(ctx, 0, a->p, lws_ptr_diff(a->end, a->p)); - mp_cgienv->name = a->p; - a->p += n; - mp_cgienv->value = a->p; - mp_cgienv->options = NULL; - //lwsl_notice(" adding pmo / cgi-env '%s' = '%s'\n", - // mp_cgienv->name, mp_cgienv->value); - goto dostring; - - case LEJPVP_PROTOCOL_NAME_OPT: - /* this catches, eg, - * vhosts[].ws-protocols[].xxx-protocol.yyy-option - * ie, these are options attached to a protocol with { } - */ - pvo = lwsws_align(a); - a->p += sizeof(*a->pvo); - - n = lejp_get_wildcard(ctx, 1, a->p, lws_ptr_diff(a->end, a->p)); - /* ie, enable this protocol, no options yet */ - pvo->next = a->pvo->options; - a->pvo->options = pvo; - pvo->name = a->p; - a->p += n; - pvo->value = a->p; - pvo->options = NULL; - break; - - case LEJPVP_MOUNT_EXTRA_MIMETYPES: - a->pvo_em = lwsws_align(a); - a->p += sizeof(*a->pvo_em); - - n = lejp_get_wildcard(ctx, 0, a->p, lws_ptr_diff(a->end, a->p)); - /* ie, enable this protocol, no options yet */ - a->pvo_em->next = a->m.extra_mimetypes; - a->m.extra_mimetypes = a->pvo_em; - a->pvo_em->name = a->p; - lwsl_notice(" + extra-mimetypes %s -> %s\n", a->p, ctx->buf); - a->p += n; - a->pvo_em->value = a->p; - a->pvo_em->options = NULL; - break; - - case LEJPVP_MOUNT_INTERPRET: - a->pvo_int = lwsws_align(a); - a->p += sizeof(*a->pvo_int); - - n = lejp_get_wildcard(ctx, 0, a->p, lws_ptr_diff(a->end, a->p)); - /* ie, enable this protocol, no options yet */ - a->pvo_int->next = a->m.interpret; - a->m.interpret = a->pvo_int; - a->pvo_int->name = a->p; - lwsl_notice(" adding interpret %s -> %s\n", a->p, - ctx->buf); - a->p += n; - a->pvo_int->value = a->p; - a->pvo_int->options = NULL; - break; - - case LEJPVP_ENABLE_CLIENT_SSL: - a->enable_client_ssl = arg_to_bool(ctx->buf); - return 0; -#if defined(LWS_WITH_TLS) - case LEJPVP_CLIENT_SSL_KEY: - a->info->client_ssl_private_key_filepath = a->p; - break; - case LEJPVP_CLIENT_SSL_CERT: - a->info->client_ssl_cert_filepath = a->p; - break; - case LEJPVP_CLIENT_SSL_CA: - a->info->client_ssl_ca_filepath = a->p; - break; -#endif - - case LEJPVP_NOIPV6: - set_reset_flag(&a->info->options, ctx->buf, - LWS_SERVER_OPTION_DISABLE_IPV6); - return 0; - - case LEJPVP_FLAG_ONLYRAW: - set_reset_flag(&a->info->options, ctx->buf, - LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG); - return 0; - - case LEJPVP_IPV6ONLY: - a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY; - set_reset_flag(&a->info->options, ctx->buf, - LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE); - return 0; - - case LEJPVP_FLAG_CLIENT_CERT_REQUIRED: - if (arg_to_bool(ctx->buf)) - a->info->options |= - LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT; - return 0; - - case LEJPVP_IGNORE_MISSING_CERT: - set_reset_flag(&a->info->options, ctx->buf, - LWS_SERVER_OPTION_IGNORE_MISSING_CERT); - return 0; - - case LEJPVP_FLAG_STRICT_HOST_CHECK: - set_reset_flag(&a->info->options, ctx->buf, - LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK); - return 0; - - case LEJPVP_ERROR_DOCUMENT_404: - a->info->error_document_404 = a->p; - break; - - case LEJPVP_SSL_OPTION_SET: - a->info->ssl_options_set |= atol(ctx->buf); - return 0; - case LEJPVP_SSL_OPTION_CLEAR: - a->info->ssl_options_clear |= atol(ctx->buf); - return 0; - - case LEJPVP_SSL_CLIENT_OPTION_SET: - a->info->ssl_client_options_set |= atol(ctx->buf); - return 0; - case LEJPVP_SSL_CLIENT_OPTION_CLEAR: - a->info->ssl_client_options_clear |= atol(ctx->buf); - return 0; - - case LEJPVP_ALPN: - a->info->alpn = a->p; - break; - - case LEJPVP_LISTEN_ACCEPT_ROLE: - a->info->listen_accept_role = a->p; - break; - case LEJPVP_LISTEN_ACCEPT_PROTOCOL: - a->info->listen_accept_protocol = a->p; - break; - - case LEJPVP_FLAG_APPLY_LISTEN_ACCEPT: - set_reset_flag(&a->info->options, ctx->buf, - LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG); - return 0; - case LEJPVP_FLAG_FALLBACK_LISTEN_ACCEPT: - lwsl_notice("vh %s: LEJPVP_FLAG_FALLBACK_LISTEN_ACCEPT: %s\n", - a->info->vhost_name, ctx->buf); - set_reset_flag(&a->info->options, ctx->buf, - LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG); - return 0; - case LEJPVP_FLAG_ALLOW_NON_TLS: - set_reset_flag(&a->info->options, ctx->buf, - LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT); - return 0; - case LEJPVP_FLAG_REDIRECT_HTTP: - set_reset_flag(&a->info->options, ctx->buf, - LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS); - return 0; - case LEJPVP_FLAG_ALLOW_HTTP_ON_HTTPS: - set_reset_flag(&a->info->options, ctx->buf, - LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER); - return 0; - - case LEJPVP_FLAG_DISABLE_NO_PROTOCOL_WS_UPGRADES: - a->reject_ws_with_no_protocol = 1; - return 0; - - case LEJPVP_FLAG_H2_HALF_CLOSED_LONG_POLL: - set_reset_flag(&a->info->options, ctx->buf, - LWS_SERVER_OPTION_VH_H2_HALF_CLOSED_LONG_POLL); - return 0; - - default: - return 0; - } - -dostring: - p = ctx->buf; - p[LEJP_STRING_CHUNK] = '\0'; - p1 = strstr(p, ESC_INSTALL_DATADIR); - if (p1) { - n = lws_ptr_diff(p1, p); - if (n > a->end - a->p) - n = lws_ptr_diff(a->end, a->p); - lws_strncpy(a->p, p, n + 1); - a->p += n; - a->p += lws_snprintf(a->p, a->end - a->p, "%s", - LWS_INSTALL_DATADIR); - p += n + strlen(ESC_INSTALL_DATADIR); - } - - a->p += lws_snprintf(a->p, a->end - a->p, "%s", p); - if (reason == LEJPCB_VAL_STR_END) - *(a->p)++ = '\0'; - - return 0; -} - -/* - * returns 0 = OK, 1 = can't open, 2 = parsing error - */ - -static int -lwsws_get_config(void *user, const char *f, const char * const *paths, - int count_paths, lejp_callback cb) -{ - unsigned char buf[128]; - struct lejp_ctx ctx; - int n, m = 0, fd; - - fd = lws_open(f, O_RDONLY); - if (fd < 0) { - lwsl_err("Cannot open %s\n", f); - return 2; - } - lwsl_info("%s: %s\n", __func__, f); - lejp_construct(&ctx, cb, user, paths, count_paths); - - do { - n = read(fd, buf, sizeof(buf)); - if (!n) - break; - - m = (int)(signed char)lejp_parse(&ctx, buf, n); - } while (m == LEJP_CONTINUE); - - close(fd); - n = ctx.line; - lejp_destruct(&ctx); - - if (m < 0) { - lwsl_err("%s(%u): parsing error %d: %s\n", f, n, m, - lejp_error_to_string(m)); - return 2; - } - - return 0; -} - -struct lws_dir_args { - void *user; - const char * const *paths; - int count_paths; - lejp_callback cb; -}; - -static int -lwsws_get_config_d_cb(const char *dirpath, void *user, - struct lws_dir_entry *lde) -{ - struct lws_dir_args *da = (struct lws_dir_args *)user; - char path[256]; - - if (lde->type != LDOT_FILE && lde->type != LDOT_UNKNOWN /* ZFS */) - return 0; - - lws_snprintf(path, sizeof(path) - 1, "%s/%s", dirpath, lde->name); - - return lwsws_get_config(da->user, path, da->paths, - da->count_paths, da->cb); -} - -int -lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d, - char **cs, int *len) -{ - struct lws_dir_args da; - struct jpargs a; - const char * const *old = info->plugin_dirs; - char dd[128]; - - memset(&a, 0, sizeof(a)); - - a.info = info; - a.p = *cs; - a.end = (a.p + *len) - 1; - a.valid = 0; - - lwsws_align(&a); - info->plugin_dirs = (void *)a.p; - a.plugin_dirs = (void *)a.p; /* writeable version */ - a.p += MAX_PLUGIN_DIRS * sizeof(void *); - - /* copy any default paths */ - - while (old && *old) { - a.plugin_dirs[a.count_plugin_dirs++] = *old; - old++; - } - - lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d); - if (lwsws_get_config(&a, dd, paths_global, - LWS_ARRAY_SIZE(paths_global), lejp_globals_cb) > 1) - return 1; - lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d); - - da.user = &a; - da.paths = paths_global; - da.count_paths = LWS_ARRAY_SIZE(paths_global), - da.cb = lejp_globals_cb; - - if (lws_dir(dd, &da, lwsws_get_config_d_cb) > 1) - return 1; - - a.plugin_dirs[a.count_plugin_dirs] = NULL; - - *cs = a.p; - *len = lws_ptr_diff(a.end, a.p); - - return 0; -} - -int -lwsws_get_config_vhosts(struct lws_context *context, - struct lws_context_creation_info *info, const char *d, - char **cs, int *len) -{ - struct lws_dir_args da; - struct jpargs a; - char dd[128]; - - memset(&a, 0, sizeof(a)); - - a.info = info; - a.p = *cs; - a.end = a.p + *len; - a.valid = 0; - a.context = context; - a.protocols = info->protocols; - a.pprotocols = info->pprotocols; - a.extensions = info->extensions; - - lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d); - if (lwsws_get_config(&a, dd, paths_vhosts, - LWS_ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1) - return 1; - lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d); - - da.user = &a; - da.paths = paths_vhosts; - da.count_paths = LWS_ARRAY_SIZE(paths_vhosts), - da.cb = lejp_vhosts_cb; - - if (lws_dir(dd, &da, lwsws_get_config_d_cb) > 1) - return 1; - - *cs = a.p; - *len = lws_ptr_diff(a.end, a.p); - - if (!a.any_vhosts) { - lwsl_err("Need at least one vhost\n"); - return 1; - } - -// lws_finalize_startup(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/roles/http/server/lws-spa.c libwebsockets-2.4.2/lib/roles/http/server/lws-spa.c --- libwebsockets-4.0.20/lib/roles/http/server/lws-spa.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/server/lws-spa.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,708 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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, 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++] = 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, 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 += '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 &= ~(1 << n); - continue; - } - - if (c != mp_hdrs[n].hdr[s->mp]) { - /* mismatched a char */ - s->matchable &= ~(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 += hit + 1; - 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 ^= 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_IGNORE1; - 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 (!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, 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, len + 1, - spa->i.ac_chunk_size); - if (!spa->params[n]) - return -1; - - memcpy(spa->params[n], *buf, 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, spa->i.max_storage, - i->ac_chunk_size); - else - spa->storage = lws_malloc(spa->i.max_storage, "spa"); - - if (!spa->storage) - goto bail2; - - spa->end = spa->storage + i->max_storage - 1; - - if (i->count_params) { - if (i->ac) - spa->params = lwsac_use_zero(i->ac, - sizeof(char *) * i->count_params, i->ac_chunk_size); - else - spa->params = lws_zalloc(sizeof(char *) * i->count_params, - "spa params"); - if (!spa->params) - goto bail3; - } - - spa->s = lws_urldecode_s_create(spa, wsi, spa->storage, 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) * i->count_params, i->ac_chunk_size); - else - spa->param_length = lws_zalloc(sizeof(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; -} diff -Nru libwebsockets-4.0.20/lib/roles/http/server/ranges.c libwebsockets-2.4.2/lib/roles/http/server/ranges.c --- libwebsockets-4.0.20/lib/roles/http/server/ranges.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/server/ranges.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,214 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 = (rp->start * 10) + (c - '0'); - rp->start_valid = 1; - break; - - case LWSRS_ENDING: - if (c == ',' || c == '\0') { - rp->state = LWSRS_FIRST; - if (c == ',') - rp->pos++; - - /* - * By the end of this, start and end are - * always valid if the range still is - */ - - if (!rp->start_valid) { /* eg, -500 */ - if (rp->end > rp->extent) - rp->end = rp->extent; - - rp->start = rp->extent - rp->end; - rp->end = rp->extent - 1; - } else - if (!rp->end_valid) - rp->end = rp->extent - 1; - - rp->did_try = 1; - - /* end must be >= start or ignore it */ - if (rp->end < rp->start) { - if (c == ',') - break; - rp->state = LWSRS_COMPLETED; - return 0; - } - - return 1; /* issue range */ - } - - if (!(c >= '0' && c <= '9')) { - rp->state = LWSRS_SYNTAX; - return 0; - } - rp->end = (rp->end * 10) + (c - '0'); - rp->end_valid = 1; - break; - } - - rp->pos++; - } -} - -void -lws_ranges_reset(struct lws_range_parsing *rp) -{ - rp->pos = 0; - rp->ctr = 0; - rp->start = 0; - rp->end = 0; - rp->start_valid = 0; - rp->end_valid = 0; - rp->state = LWSRS_BYTES_EQ; -} - -/* - * returns count of valid ranges - */ -int -lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp, - unsigned long long extent) -{ - rp->agg = 0; - rp->send_ctr = 0; - rp->inside = 0; - rp->count_ranges = 0; - rp->did_try = 0; - lws_ranges_reset(rp); - rp->state = LWSRS_COMPLETED; - - rp->extent = extent; - - if (lws_hdr_copy(wsi, (char *)rp->buf, sizeof(rp->buf), - WSI_TOKEN_HTTP_RANGE) <= 0) - return 0; - - rp->state = LWSRS_BYTES_EQ; - - while (lws_ranges_next(rp)) { - rp->count_ranges++; - rp->agg += rp->end - rp->start + 1; - } - - lwsl_debug("%s: count %d\n", __func__, rp->count_ranges); - lws_ranges_reset(rp); - - if (rp->did_try && !rp->count_ranges) - return -1; /* "not satisfiable */ - - lws_ranges_next(rp); - - return rp->count_ranges; -} diff -Nru libwebsockets-4.0.20/lib/roles/http/server/rewrite.c libwebsockets-2.4.2/lib/roles/http/server/rewrite.c --- libwebsockets-4.0.20/lib/roles/http/server/rewrite.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/server/rewrite.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -#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 diff -Nru libwebsockets-4.0.20/lib/roles/http/server/server.c libwebsockets-2.4.2/lib/roles/http/server/server.c --- libwebsockets-4.0.20/lib/roles/http/server/server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/http/server/server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,3162 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -const char * const method_names[] = { - "GET", "POST", -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - "OPTIONS", "PUT", "PATCH", "DELETE", -#endif - "CONNECT", "HEAD", -#ifdef LWS_WITH_HTTP2 - ":path", -#endif - }; - -#if defined(LWS_WITH_FILE_OPS) -static const char * const intermediates[] = { "private", "public" }; -#endif - -/* - * return 0: all done - * 1: nonfatal error - * <0: fatal error - * - * REQUIRES CONTEXT LOCK HELD - */ - -#if defined(LWS_WITH_SERVER) -int -_lws_vhost_init_server(const struct lws_context_creation_info *info, - struct lws_vhost *vhost) -{ - int n, opt = 1, limit = 1; - lws_sockfd_type sockfd; - struct lws_vhost *vh; - struct lws *wsi; - int m = 0, is; - - (void)method_names; - (void)opt; - - if (info) { - vhost->iface = info->iface; - vhost->listen_port = info->port; - } - - /* set up our external listening socket we serve on */ - - if (vhost->listen_port == CONTEXT_PORT_NO_LISTEN || - vhost->listen_port == CONTEXT_PORT_NO_LISTEN_SERVER) - return 0; - - vh = vhost->context->vhost_list; - while (vh) { - if (vh->listen_port == vhost->listen_port) { - if (((!vhost->iface && !vh->iface) || - (vhost->iface && vh->iface && - !strcmp(vhost->iface, vh->iface))) && - vh->lserv_wsi - ) { - lwsl_notice(" using listen skt from vhost %s\n", - vh->name); - return 0; - } - } - vh = vh->vhost_next; - } - - if (vhost->iface) { - /* - * let's check before we do anything else about the disposition - * of the interface he wants to bind to... - */ - is = lws_socket_bind(vhost, LWS_SOCK_INVALID, vhost->listen_port, - vhost->iface, 1); - lwsl_debug("initial if check says %d\n", is); - - if (is == LWS_ITOSA_BUSY) - /* treat as fatal */ - return -1; - -deal: - - lws_start_foreach_llp(struct lws_vhost **, pv, - vhost->context->no_listener_vhost_list) { - if (is >= LWS_ITOSA_USABLE && *pv == vhost) { - /* on the list and shouldn't be: remove it */ - lwsl_debug("deferred iface: removing vh %s\n", - (*pv)->name); - *pv = vhost->no_listener_vhost_list; - vhost->no_listener_vhost_list = NULL; - goto done_list; - } - if (is < LWS_ITOSA_USABLE && *pv == vhost) - goto done_list; - } lws_end_foreach_llp(pv, no_listener_vhost_list); - - /* not on the list... */ - - if (is < LWS_ITOSA_USABLE) { - - /* ... but needs to be: so add it */ - - lwsl_debug("deferred iface: adding vh %s\n", vhost->name); - vhost->no_listener_vhost_list = - vhost->context->no_listener_vhost_list; - vhost->context->no_listener_vhost_list = vhost; - } - -done_list: - - switch (is) { - default: - break; - case LWS_ITOSA_NOT_EXIST: - /* can't add it */ - if (info) /* first time */ - lwsl_err("VH %s: iface %s port %d DOESN'T EXIST\n", - vhost->name, vhost->iface, vhost->listen_port); - else - return -1; - return (info->options & LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) == - LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND? - -1 : 1; - case LWS_ITOSA_NOT_USABLE: - /* can't add it */ - if (info) /* first time */ - lwsl_err("VH %s: iface %s port %d NOT USABLE\n", - vhost->name, vhost->iface, vhost->listen_port); - else - return -1; - return (info->options & LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) == - LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND? - -1 : 1; - } - } - - (void)n; -#if defined(__linux__) -#ifdef LWS_WITH_UNIX_SOCK - /* - * A Unix domain sockets cannot be bound for several times, even if we set - * the SO_REUSE* options on. - * However, fortunately, each thread is able to independently listen when - * running on a reasonably new Linux kernel. So we can safely assume - * creating just one listening socket for a multi-threaded environment won't - * fail in most cases. - */ - if (!LWS_UNIX_SOCK_ENABLED(vhost)) -#endif - limit = vhost->context->count_threads; -#endif - - for (m = 0; m < limit; m++) { -#ifdef LWS_WITH_UNIX_SOCK - if (LWS_UNIX_SOCK_ENABLED(vhost)) - sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - else -#endif -#ifdef LWS_WITH_IPV6 - if (LWS_IPV6_ENABLED(vhost)) - sockfd = socket(AF_INET6, SOCK_STREAM, 0); - else -#endif - sockfd = socket(AF_INET, SOCK_STREAM, 0); - - if (sockfd == LWS_SOCK_INVALID) { - lwsl_err("ERROR opening socket\n"); - return 1; - } -#if !defined(LWS_PLAT_FREERTOS) -#if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE) - /* - * only accept that we are the only listener on the port - * https://msdn.microsoft.com/zh-tw/library/ - * windows/desktop/ms740621(v=vs.85).aspx - * - * for lws, to match Linux, we default to exclusive listen - */ - if (!lws_check_opt(vhost->options, - LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) { - if (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, - (const void *)&opt, sizeof(opt)) < 0) { - lwsl_err("reuseaddr failed\n"); - compatible_close(sockfd); - return -1; - } - } else -#endif - - /* - * allow us to restart even if old sockets in TIME_WAIT - */ - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, - (const void *)&opt, sizeof(opt)) < 0) { - lwsl_err("reuseaddr failed\n"); - compatible_close(sockfd); - return -1; - } - -#if defined(LWS_WITH_IPV6) && defined(IPV6_V6ONLY) - if (LWS_IPV6_ENABLED(vhost) && - vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) { - int value = (vhost->options & - LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE) ? 1 : 0; - if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, - (const void*)&value, sizeof(value)) < 0) { - compatible_close(sockfd); - return -1; - } - } -#endif - -#if defined(__linux__) && defined(SO_REUSEPORT) - /* keep coverity happy */ -#if LWS_MAX_SMP > 1 - n = 1; -#else - n = lws_check_opt(vhost->options, - LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE); -#endif - if (n && vhost->context->count_threads > 1) - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, - (const void *)&opt, sizeof(opt)) < 0) { - compatible_close(sockfd); - return -1; - } -#endif -#endif - lws_plat_set_socket_options(vhost, sockfd, 0); - - is = lws_socket_bind(vhost, sockfd, vhost->listen_port, - vhost->iface, 1); - if (is == LWS_ITOSA_BUSY) { - /* treat as fatal */ - compatible_close(sockfd); - - return -1; - } - - /* - * There is a race where the network device may come up and then - * go away and fail here. So correctly handle unexpected failure - * here despite we earlier confirmed it. - */ - if (is < 0) { - lwsl_info("%s: lws_socket_bind says %d\n", __func__, is); - compatible_close(sockfd); - goto deal; - } - - wsi = lws_zalloc(sizeof(struct lws), "listen wsi"); - if (wsi == NULL) { - lwsl_err("Out of mem\n"); - goto bail; - } - -#ifdef LWS_WITH_UNIX_SOCK - if (!LWS_UNIX_SOCK_ENABLED(vhost)) -#endif - { - wsi->unix_skt = 1; - vhost->listen_port = is; - - lwsl_debug("%s: lws_socket_bind says %d\n", __func__, is); - } - - wsi->context = vhost->context; - wsi->desc.sockfd = sockfd; - lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_listen); - wsi->protocol = vhost->protocols; - wsi->tsi = m; - lws_vhost_bind_wsi(vhost, wsi); - wsi->listener = 1; - - if (wsi->context->event_loop_ops->init_vhost_listen_wsi) - wsi->context->event_loop_ops->init_vhost_listen_wsi(wsi); - - if (__insert_wsi_socket_into_fds(vhost->context, wsi)) { - lwsl_notice("inserting wsi socket into fds failed\n"); - goto bail; - } - - vhost->context->count_wsi_allocated++; - vhost->lserv_wsi = wsi; - - n = listen(wsi->desc.sockfd, LWS_SOMAXCONN); - if (n < 0) { - lwsl_err("listen failed with error %d\n", LWS_ERRNO); - vhost->lserv_wsi = NULL; - vhost->context->count_wsi_allocated--; - __remove_wsi_socket_from_fds(wsi); - goto bail; - } - } /* for each thread able to independently listen */ - - if (!lws_check_opt(vhost->context->options, - LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) { -#ifdef LWS_WITH_UNIX_SOCK - if (LWS_UNIX_SOCK_ENABLED(vhost)) - lwsl_info(" Listening on \"%s\"\n", vhost->iface); - else -#endif - lwsl_info(" Listening on port %d\n", vhost->listen_port); - } - - // info->port = vhost->listen_port; - - return 0; - -bail: - compatible_close(sockfd); - - return -1; -} -#endif - -struct lws_vhost * -lws_select_vhost(struct lws_context *context, int port, const char *servername) -{ - struct lws_vhost *vhost = context->vhost_list; - const char *p; - int n, colon; - - n = (int)strlen(servername); - colon = n; - p = strchr(servername, ':'); - if (p) - colon = lws_ptr_diff(p, servername); - - /* Priotity 1: first try exact matches */ - - while (vhost) { - if (port == vhost->listen_port && - !strncmp(vhost->name, servername, colon)) { - lwsl_info("SNI: Found: %s\n", servername); - return vhost; - } - vhost = vhost->vhost_next; - } - - /* - * Priority 2: if no exact matches, try matching *.vhost-name - * unintentional matches are possible but resolve to x.com for *.x.com - * which is reasonable. If exact match exists we already chose it and - * never reach here. SSL will still fail it if the cert doesn't allow - * *.x.com. - */ - vhost = context->vhost_list; - while (vhost) { - int m = (int)strlen(vhost->name); - if (port && port == vhost->listen_port && - m <= (colon - 2) && - servername[colon - m - 1] == '.' && - !strncmp(vhost->name, servername + colon - m, m)) { - lwsl_info("SNI: Found %s on wildcard: %s\n", - servername, vhost->name); - return vhost; - } - vhost = vhost->vhost_next; - } - - /* Priority 3: match the first vhost on our port */ - - vhost = context->vhost_list; - while (vhost) { - if (port && port == vhost->listen_port) { - lwsl_info("%s: vhost match to %s based on port %d\n", - __func__, vhost->name, port); - return vhost; - } - vhost = vhost->vhost_next; - } - - /* no match */ - - return NULL; -} - -static const struct lws_mimetype { - const char *extension; - const char *mimetype; -} server_mimetypes[] = { - { ".html", "text/html" }, - { ".htm", "text/html" }, - { ".js", "text/javascript" }, - { ".css", "text/css" }, - { ".png", "image/png" }, - { ".jpg", "image/jpeg" }, - { ".jpeg", "image/jpeg" }, - { ".ico", "image/x-icon" }, - { ".gif", "image/gif" }, - { ".svg", "image/svg+xml" }, - { ".ttf", "application/x-font-ttf" }, - { ".otf", "application/font-woff" }, - { ".woff", "application/font-woff" }, - { ".woff2", "application/font-woff2" }, - { ".gz", "application/gzip" }, - { ".txt", "text/plain" }, - { ".xml", "application/xml" }, - { ".json", "application/json" }, -}; - -const char * -lws_get_mimetype(const char *file, const struct lws_http_mount *m) -{ - const struct lws_protocol_vhost_options *pvo; - size_t n = strlen(file), len, i; - const char *fallback_mimetype = NULL; - const struct lws_mimetype *mt; - - /* prioritize user-defined mimetypes */ - for (pvo = m ? m->extra_mimetypes : NULL; pvo; pvo = pvo->next) { - /* ie, match anything */ - if (!fallback_mimetype && pvo->name[0] == '*') { - fallback_mimetype = pvo->value; - continue; - } - - len = strlen(pvo->name); - if (n > len && !strcasecmp(&file[n - len], pvo->name)) { - lwsl_info("%s: match to user mimetype: %s\n", __func__, - pvo->value); - return pvo->value; - } - } - - /* fallback to server-defined mimetypes */ - for (i = 0; i < LWS_ARRAY_SIZE(server_mimetypes); ++i) { - mt = &server_mimetypes[i]; - - len = strlen(mt->extension); - if (n > len && !strcasecmp(&file[n - len], mt->extension)) { - lwsl_info("%s: match to server mimetype: %s\n", __func__, - mt->mimetype); - return mt->mimetype; - } - } - - /* fallback to '*' if defined */ - if (fallback_mimetype) { - lwsl_info("%s: match to any mimetype: %s\n", __func__, - fallback_mimetype); - return fallback_mimetype; - } - - return NULL; -} - -#if defined(LWS_WITH_FILE_OPS) -static lws_fop_flags_t -lws_vfs_prepare_flags(struct lws *wsi) -{ - lws_fop_flags_t f = 0; - - if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING)) - return f; - - if (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING), - "gzip")) { - lwsl_info("client indicates GZIP is acceptable\n"); - f |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP; - } - - return f; -} - -static int -lws_http_serve(struct lws *wsi, char *uri, const char *origin, - const struct lws_http_mount *m) -{ - const struct lws_protocol_vhost_options *pvo = m->interpret; - struct lws_process_html_args args; - const char *mimetype; -#if !defined(_WIN32_WCE) - const struct lws_plat_file_ops *fops; - const char *vpath; - lws_fop_flags_t fflags = LWS_O_RDONLY; -#if defined(WIN32) && defined(LWS_HAVE__STAT32I64) - struct _stat32i64 st; -#else - struct stat st; -#endif - int spin = 0; -#endif - char path[256], sym[2048]; - unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p; - unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE; -#if !defined(WIN32) && !defined(LWS_PLAT_FREERTOS) - size_t len; -#endif - int n; - - wsi->handling_404 = 0; - if (!wsi->vhost) - return -1; - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - if (wsi->vhost->http.error_document_404 && - !strcmp(uri, wsi->vhost->http.error_document_404)) - wsi->handling_404 = 1; -#endif - - lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri); - -#if !defined(_WIN32_WCE) - - fflags |= lws_vfs_prepare_flags(wsi); - - do { - spin++; - fops = lws_vfs_select_fops(wsi->context->fops, path, &vpath); - - if (wsi->http.fop_fd) - lws_vfs_file_close(&wsi->http.fop_fd); - - wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops, - path, vpath, &fflags); - if (!wsi->http.fop_fd) { - lwsl_info("%s: Unable to open '%s': errno %d\n", - __func__, path, errno); - - return 1; - } - - /* if it can't be statted, don't try */ - if (fflags & LWS_FOP_FLAG_VIRTUAL) - break; -#if defined(LWS_PLAT_FREERTOS) - break; -#endif -#if !defined(WIN32) - if (fstat(wsi->http.fop_fd->fd, &st)) { - lwsl_info("unable to stat %s\n", path); - goto notfound; - } -#else -#if defined(LWS_HAVE__STAT32I64) - if (_stat32i64(path, &st)) { - lwsl_info("unable to stat %s\n", path); - goto notfound; - } -#else - if (stat(path, &st)) { - lwsl_info("unable to stat %s\n", path); - goto notfound; - } -#endif -#endif - - wsi->http.fop_fd->mod_time = (uint32_t)st.st_mtime; - fflags |= LWS_FOP_FLAG_MOD_TIME_VALID; - -#if !defined(WIN32) && !defined(LWS_PLAT_FREERTOS) - if ((S_IFMT & st.st_mode) == S_IFLNK) { - len = readlink(path, sym, sizeof(sym) - 1); - if (len) { - lwsl_err("Failed to read link %s\n", path); - goto notfound; - } - sym[len] = '\0'; - lwsl_debug("symlink %s -> %s\n", path, sym); - lws_snprintf(path, sizeof(path) - 1, "%s", sym); - } -#endif - if ((S_IFMT & st.st_mode) == S_IFDIR) { - lwsl_debug("default filename append to dir\n"); - lws_snprintf(path, sizeof(path) - 1, "%s/%s/%s", - origin, uri, m->def ? m->def : "index.html"); - } - - } while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5); - - if (spin == 5) - lwsl_err("symlink loop %s \n", path); - - n = sprintf(sym, "%08llX%08lX", - (unsigned long long)lws_vfs_get_length(wsi->http.fop_fd), - (unsigned long)lws_vfs_get_mod_time(wsi->http.fop_fd)); - - /* disable ranges if IF_RANGE token invalid */ - - if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_RANGE)) - if (strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_RANGE))) - /* differs - defeat Range: */ - wsi->http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0; - - if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) { - /* - * he thinks he has some version of it already, - * check if the tag matches - */ - if (!strcmp(sym, lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_IF_NONE_MATCH))) { - - char cache_control[50], *cc = "no-store"; - int cclen = 8; - - lwsl_debug("%s: ETAG match %s %s\n", __func__, - uri, origin); - - /* we don't need to send the payload */ - if (lws_add_http_header_status(wsi, - HTTP_STATUS_NOT_MODIFIED, &p, end)) { - lwsl_err("%s: failed adding not modified\n", - __func__); - return -1; - } - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_ETAG, - (unsigned char *)sym, n, &p, end)) - return -1; - - /* but we still need to send cache control... */ - - if (m->cache_max_age && m->cache_reusable) { - if (!m->cache_revalidate) { - cc = cache_control; - cclen = sprintf(cache_control, - "%s, max-age=%u", - intermediates[wsi->cache_intermediaries], - m->cache_max_age); - } else { - cc = cache_control; - cclen = sprintf(cache_control, - "must-revalidate, %s, max-age=%u", - intermediates[wsi->cache_intermediaries], - m->cache_max_age); - } - } - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CACHE_CONTROL, - (unsigned char *)cc, cclen, &p, end)) - return -1; - - if (lws_finalize_http_header(wsi, &p, end)) - return -1; - - n = lws_write(wsi, start, p - start, - LWS_WRITE_HTTP_HEADERS | - LWS_WRITE_H2_STREAM_END); - if (n != (p - start)) { - lwsl_err("_write returned %d from %ld\n", n, - (long)(p - start)); - return -1; - } - - lws_vfs_file_close(&wsi->http.fop_fd); - - if (lws_http_transaction_completed(wsi)) - return -1; - - return 0; - } - } - - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG, - (unsigned char *)sym, n, &p, end)) - return -1; -#endif - - mimetype = lws_get_mimetype(path, m); - if (!mimetype) { - lwsl_info("unknown mimetype for %s\n", path); - if (lws_return_http_status(wsi, - HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL) || - lws_http_transaction_completed(wsi)) - return -1; - - return 0; - } - if (!mimetype[0]) - lwsl_debug("sending no mimetype for %s\n", path); - - wsi->sending_chunked = 0; - wsi->interpreting = 0; - - /* - * check if this is in the list of file suffixes to be interpreted by - * a protocol - */ - while (pvo) { - n = (int)strlen(path); - if (n > (int)strlen(pvo->name) && - !strcmp(&path[n - strlen(pvo->name)], pvo->name)) { - wsi->interpreting = 1; - if (!wsi->mux_substream) - wsi->sending_chunked = 1; - - wsi->protocol_interpret_idx = (char)( - lws_vhost_name_to_protocol(wsi->vhost, - pvo->value) - - &lws_get_vhost(wsi)->protocols[0]); - - lwsl_debug("want %s interpreted by %s (pcol is %s)\n", path, - wsi->vhost->protocols[ - (int)wsi->protocol_interpret_idx].name, - wsi->protocol->name); - if (lws_bind_protocol(wsi, &wsi->vhost->protocols[ - (int)wsi->protocol_interpret_idx], __func__)) - return -1; - - if (lws_ensure_user_space(wsi)) - return -1; - break; - } - pvo = pvo->next; - } - - if (wsi->sending_chunked) { - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_TRANSFER_ENCODING, - (unsigned char *)"chunked", 7, - &p, end)) - return -1; - } - - if (m->protocol) { - const struct lws_protocols *pp = lws_vhost_name_to_protocol( - wsi->vhost, m->protocol); - - if (lws_bind_protocol(wsi, pp, __func__)) - return -1; - args.p = (char *)p; - args.max_len = lws_ptr_diff(end, p); - if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS, - wsi->user_space, &args, 0)) - return -1; - p = (unsigned char *)args.p; - } - - *p = '\0'; - n = lws_serve_http_file(wsi, path, mimetype, (char *)start, - lws_ptr_diff(p, start)); - - if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi))) - return -1; /* error or can't reuse connection: close the socket */ - - return 0; - -notfound: - - return 1; -} -#endif - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) -const struct lws_http_mount * -lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len) -{ - const struct lws_http_mount *hm, *hit = NULL; - int best = 0; - - hm = wsi->vhost->http.mount_list; - while (hm) { - if (uri_len >= hm->mountpoint_len && - !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) && - (uri_ptr[hm->mountpoint_len] == '\0' || - uri_ptr[hm->mountpoint_len] == '/' || - hm->mountpoint_len == 1) - ) { - if (hm->origin_protocol == LWSMPRO_CALLBACK || - ((hm->origin_protocol == LWSMPRO_CGI || - lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) || - lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) || - lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_URI) || -#if defined(LWS_ROLE_H2) - (wsi->mux_substream && - lws_hdr_total_length(wsi, - WSI_TOKEN_HTTP_COLON_PATH)) || -#endif - hm->protocol) && - hm->mountpoint_len > best)) { - best = hm->mountpoint_len; - hit = hm; - } - } - hm = hm->mount_next; - } - - return hit; -} -#endif - -#if defined(LWS_WITH_HTTP_BASIC_AUTH) && !defined(LWS_PLAT_FREERTOS) && defined(LWS_WITH_FILE_OPS) -static int -lws_find_string_in_file(const char *filename, const char *string, int stringlen) -{ - char buf[128]; - int fd, match = 0, pos = 0, n = 0, hit = 0; - - fd = lws_open(filename, O_RDONLY); - if (fd < 0) { - lwsl_err("can't open auth file: %s\n", filename); - return 0; - } - - while (1) { - if (pos == n) { - n = read(fd, buf, sizeof(buf)); - if (n <= 0) { - if (match == stringlen) - hit = 1; - break; - } - pos = 0; - } - - if (match == stringlen) { - if (buf[pos] == '\r' || buf[pos] == '\n') { - hit = 1; - break; - } - match = 0; - } - - if (buf[pos] == string[match]) - match++; - else - match = 0; - - pos++; - } - - close(fd); - - return hit; -} -#endif - -#if defined(LWS_WITH_HTTP_BASIC_AUTH) - -int -lws_unauthorised_basic_auth(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - unsigned char *start = pt->serv_buf + LWS_PRE, - *p = start, *end = p + 2048; - char buf[64]; - int n; - - /* no auth... tell him it is required */ - - if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end)) - return -1; - - n = lws_snprintf(buf, sizeof(buf), "Basic realm=\"lwsws\""); - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_WWW_AUTHENTICATE, - (unsigned char *)buf, n, &p, end)) - return -1; - - if (lws_add_http_header_content_length(wsi, 0, &p, end)) - return -1; - - if (lws_finalize_http_header(wsi, &p, end)) - return -1; - - n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS | - LWS_WRITE_H2_STREAM_END); - if (n < 0) - return -1; - - return lws_http_transaction_completed(wsi); - -} - -#endif - -int lws_clean_url(char *p) -{ - if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') { - p += 4; - if (*p == 's') - p++; - if (*p == ':') { - p++; - if (*p == '/') - p++; - } - } - - while (*p) { - if (p[0] == '/' && p[1] == '/') { - char *p1 = p; - while (*p1) { - *p1 = p1[1]; - p1++; - } - continue; - } - p++; - } - - return 0; -} - -static const unsigned char methods[] = { - WSI_TOKEN_GET_URI, - WSI_TOKEN_POST_URI, -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - WSI_TOKEN_OPTIONS_URI, - WSI_TOKEN_PUT_URI, - WSI_TOKEN_PATCH_URI, - WSI_TOKEN_DELETE_URI, -#endif - WSI_TOKEN_CONNECT, - WSI_TOKEN_HEAD_URI, -#ifdef LWS_WITH_HTTP2 - WSI_TOKEN_HTTP_COLON_PATH, -#endif -}; - -int -lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len) -{ - int n, count = 0; - - for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++) - if (lws_hdr_total_length(wsi, methods[n])) - count++; - if (!count) { - lwsl_warn("Missing URI in HTTP request\n"); - return -1; - } - - if (count != 1 && - !((wsi->mux_substream || wsi->h2_stream_carries_ws) -#if defined(LWS_ROLE_H2) - && - lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH) -#endif - )) { - lwsl_warn("multiple methods?\n"); - return -1; - } - - for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++) - if (lws_hdr_total_length(wsi, methods[n])) { - *puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]); - *puri_len = lws_hdr_total_length(wsi, methods[n]); - return n; - } - - return -1; -} - -#if defined(LWS_WITH_HTTP_BASIC_AUTH) - -enum lws_check_basic_auth_results -lws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file, - unsigned int auth_mode) -{ -#if defined(LWS_WITH_FILE_OPS) - char b64[160], plain[(sizeof(b64) * 3) / 4], *pcolon; - int m, ml, fi, bar; - - if (!basic_auth_login_file && auth_mode == LWSAUTHM_DEFAULT) - return LCBA_CONTINUE; - - /* Did he send auth? */ - ml = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION); - if (!ml) - return LCBA_FAILED_AUTH; - - /* Disallow fragmentation monkey business */ - - fi = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_AUTHORIZATION]; - if (wsi->http.ah->frags[fi].nfrag) { - lwsl_err("fragmented basic auth header not allowed\n"); - return LCBA_FAILED_AUTH; - } - - m = lws_hdr_copy(wsi, b64, sizeof(b64), - WSI_TOKEN_HTTP_AUTHORIZATION); - if (m < 7) { - lwsl_err("b64 auth too long\n"); - return LCBA_END_TRANSACTION; - } - - b64[5] = '\0'; - if (strcasecmp(b64, "Basic")) { - lwsl_err("auth missing basic: %s\n", b64); - return LCBA_END_TRANSACTION; - } - - /* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */ - - m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain) - 1); - if (m < 0) { - lwsl_err("plain auth too long\n"); - return LCBA_END_TRANSACTION; - } - - plain[m] = '\0'; - pcolon = strchr(plain, ':'); - if (!pcolon) { - lwsl_err("basic auth format broken\n"); - return LCBA_END_TRANSACTION; - } - - switch (auth_mode) { - case LWSAUTHM_DEFAULT: - if (lws_find_string_in_file(basic_auth_login_file, plain, m)) - break; - lwsl_err("%s: basic auth lookup failed\n", __func__); - return LCBA_FAILED_AUTH; - - case LWSAUTHM_BASIC_AUTH_CALLBACK: - bar = wsi->protocol->callback(wsi, - LWS_CALLBACK_VERIFY_BASIC_AUTHORIZATION, - wsi->user_space, plain, m); - if (!bar) - return LCBA_FAILED_AUTH; - break; - default: - /* Invalid auth mode so lets fail all authentication attempts */ - return LCBA_FAILED_AUTH; - } - - /* - * Rewrite WSI_TOKEN_HTTP_AUTHORIZATION so it is just the - * authorized username - */ - - *pcolon = '\0'; - wsi->http.ah->frags[fi].len = lws_ptr_diff(pcolon, plain); - pcolon = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION); - strncpy(pcolon, plain, ml - 1); - pcolon[ml - 1] = '\0'; - lwsl_info("%s: basic auth accepted for %s\n", __func__, - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION)); - - return LCBA_CONTINUE; -#else - return LCBA_FAILED_AUTH; -#endif -} - -#endif - -#if defined(LWS_WITH_HTTP_PROXY) -/* - * Set up an onward http proxy connection according to the mount this - * uri falls under. Notice this can also be starting the proxying of what was - * originally an incoming h1 upgrade, or an h2 ws "upgrade". - */ -int -lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit, - char *uri_ptr, char ws) -{ - char ads[96], rpath[256], host[96], *pcolon, *pslash, unix_skt = 0; - struct lws_client_connect_info i; - struct lws *cwsi; - int n, na; - -#if defined(LWS_ROLE_WS) - if (ws) - /* - * Neither our inbound ws upgrade request side, nor our onward - * ws client connection on our side can bind to the actual - * protocol that only the remote inbound side and the remote - * onward side understand. - * - * Instead these are both bound to our built-in "lws-ws-proxy" - * protocol, which understands how to proxy between the two - * sides. - * - * We bind the parent, inbound part here and our side of the - * onward client connection is bound to the same handler using - * the .local_protocol_name. - */ - lws_bind_protocol(wsi, &lws_ws_proxy, __func__); -#endif - memset(&i, 0, sizeof(i)); - i.context = lws_get_context(wsi); - - if (hit->origin[0] == '+') - unix_skt = 1; - - pcolon = strchr(hit->origin, ':'); - pslash = strchr(hit->origin, '/'); - if (!pslash) { - lwsl_err("Proxy mount origin '%s' must have /\n", hit->origin); - return -1; - } - - if (unix_skt) { - if (!pcolon) { - lwsl_err("Proxy mount origin for unix skt must " - "have address delimited by :\n"); - - return -1; - } - n = lws_ptr_diff(pcolon, hit->origin); - pslash = pcolon; - } else { - if (pcolon > pslash) - pcolon = NULL; - - if (pcolon) - n = (int)(pcolon - hit->origin); - else - n = (int)(pslash - hit->origin); - - if (n >= (int)sizeof(ads) - 2) - n = sizeof(ads) - 2; - } - - memcpy(ads, hit->origin, n); - ads[n] = '\0'; - - i.address = ads; - i.port = 80; - if (hit->origin_protocol == LWSMPRO_HTTPS) { - i.port = 443; - i.ssl_connection = 1; - } - if (pcolon) - i.port = atoi(pcolon + 1); - - n = lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s", - pslash + 1, uri_ptr + hit->mountpoint_len) - 2; - lws_clean_url(rpath); - na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS); - if (na) { - char *p; - - if (!n) /* don't start with the ?... use the first / if so */ - n++; - - p = rpath + n; - - if (na >= (int)sizeof(rpath) - n - 2) { - lwsl_info("%s: query string %d longer " - "than we can handle\n", __func__, - na); - - return -1; - } - - *p++ = '?'; - if (lws_hdr_copy(wsi, p, - (int)(&rpath[sizeof(rpath) - 1] - p), - WSI_TOKEN_HTTP_URI_ARGS) > 0) - while (na--) { - if (*p == '\0') - *p = '&'; - p++; - } - *p = '\0'; - } - - i.path = rpath; - - /* incoming may be h1 or h2... if he sends h1 HOST, use that - * directly, otherwise we must convert h2 :authority to h1 - * host */ - - i.host = NULL; -#if defined(LWS_ROLE_H2) - n = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY); - if (n > 0) - i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY); - else -#endif - { - n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST); - if (n > 0) { - i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST); - } - } - -#if 0 - if (i.address[0] != '+' || - !lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)) - i.host = i.address; - else - i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST); -#endif - i.origin = NULL; - if (!ws) { - if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI) -#if defined(LWS_WITH_HTTP2) - || ( - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) && - !strcmp(lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_COLON_METHOD), "post") - ) -#endif - ) - i.method = "POST"; - else - i.method = "GET"; - } - - if (i.host) - lws_snprintf(host, sizeof(host), "%s:%u", i.host, - wsi->vhost->listen_port); - else - lws_snprintf(host, sizeof(host), "%s:%d", i.address, i.port); - - i.host = host; - - i.alpn = "http/1.1"; - i.parent_wsi = wsi; - i.pwsi = &cwsi; -#if defined(LWS_ROLE_WS) - i.protocol = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL); - if (ws) - i.local_protocol_name = "lws-ws-proxy"; -#endif - -// i.uri_replace_from = hit->origin; -// i.uri_replace_to = hit->mountpoint; - - lwsl_info("proxying to %s port %d url %s, ssl %d, from %s, to %s\n", - i.address, i.port, i.path, i.ssl_connection, - i.uri_replace_from, i.uri_replace_to); - - if (!lws_client_connect_via_info(&i)) { - lwsl_err("proxy connect fail\n"); - - /* - * ... we can't do the proxy action, but we can - * cleanly return him a 503 and a description - */ - - lws_return_http_status(wsi, - HTTP_STATUS_SERVICE_UNAVAILABLE, - "

Service Temporarily Unavailable

" - "The server is temporarily unable to service " - "your request due to maintenance downtime or " - "capacity problems. Please try again later."); - - return 1; - } - - lwsl_info("%s: setting proxy clientside on %p (parent %p)\n", - __func__, cwsi, lws_get_parent(cwsi)); - - cwsi->http.proxy_clientside = 1; - if (ws) { - wsi->proxied_ws_parent = 1; - cwsi->h1_ws_proxied = 1; - if (i.protocol) { - lwsl_debug("%s: (requesting '%s')\n", - __func__, i.protocol); - } - } - - return 0; -} -#endif - - -static const char * const oprot[] = { - "http://", "https://" -}; - - -static int -lws_http_redirect_hit(struct lws_context_per_thread *pt, struct lws *wsi, - const struct lws_http_mount *hit, char *uri_ptr, - int uri_len, int *h) -{ - char *s; - int n; - - *h = 0; - s = uri_ptr + hit->mountpoint_len; - - /* - * if we have a mountpoint like https://xxx.com/yyy - * there is an implied / at the end for our purposes since - * we can only mount on a "directory". - * - * But if we just go with that, the browser cannot understand - * that he is actually looking down one "directory level", so - * even though we give him /yyy/abc.html he acts like the - * current directory level is /. So relative urls like "x.png" - * wrongly look outside the mountpoint. - * - * Therefore if we didn't come in on a url with an explicit - * / at the end, we must redirect to add it so the browser - * understands he is one "directory level" down. - */ - if ((hit->mountpoint_len > 1 || - (hit->origin_protocol == LWSMPRO_REDIR_HTTP || - hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) && - (*s != '/' || - (hit->origin_protocol == LWSMPRO_REDIR_HTTP || - hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) && - (hit->origin_protocol != LWSMPRO_CGI && - hit->origin_protocol != LWSMPRO_CALLBACK)) { - unsigned char *start = pt->serv_buf + LWS_PRE, *p = start, - *end = p + wsi->context->pt_serv_buf_size - - LWS_PRE - 512; - - *h = 1; - - lwsl_info("Doing 301 '%s' org %s\n", s, hit->origin); - - /* > at start indicates deal with by redirect */ - if (hit->origin_protocol == LWSMPRO_REDIR_HTTP || - hit->origin_protocol == LWSMPRO_REDIR_HTTPS) - n = lws_snprintf((char *)end, 256, "%s%s", - oprot[hit->origin_protocol & 1], - hit->origin); - else { - if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { -#if defined(LWS_ROLE_H2) - if (!lws_hdr_total_length(wsi, - WSI_TOKEN_HTTP_COLON_AUTHORITY)) -#endif - goto bail_nuke_ah; -#if defined(LWS_ROLE_H2) - n = lws_snprintf((char *)end, 256, - "%s%s%s/", oprot[!!lws_is_ssl(wsi)], - lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_COLON_AUTHORITY), - uri_ptr); -#else - ; -#endif - } else - n = lws_snprintf((char *)end, 256, - "%s%s%s/", oprot[!!lws_is_ssl(wsi)], - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), - uri_ptr); - } - - lws_clean_url((char *)end); - n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, - end, n, &p, end); - if ((int)n < 0) - goto bail_nuke_ah; - - return lws_http_transaction_completed(wsi); - } - - return 0; - -bail_nuke_ah: - lws_header_table_detach(wsi, 1); - - return 1; -} - -int -lws_http_action(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - int uri_len = 0, meth, m, http_version_len, ha; - const struct lws_http_mount *hit = NULL; - enum http_version request_version; - struct lws_process_html_args args; - enum http_conn_type conn_type; - char content_length_str[32]; - char http_version_str[12]; - char *uri_ptr = NULL, *s; - char http_conn_str[25]; - unsigned int n; - - meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len); - if (meth < 0 || meth >= (int)LWS_ARRAY_SIZE(method_names)) - goto bail_nuke_ah; - - /* we insist on absolute paths */ - - if (!uri_ptr || uri_ptr[0] != '/') { - lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); - - goto bail_nuke_ah; - } - - lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth], - meth, uri_ptr); - - if (wsi->role_ops && wsi->role_ops->check_upgrades) - switch (wsi->role_ops->check_upgrades(wsi)) { - case LWS_UPG_RET_DONE: - return 0; - case LWS_UPG_RET_CONTINUE: - break; - case LWS_UPG_RET_BAIL: - goto bail_nuke_ah; - } - - if (lws_ensure_user_space(wsi)) - goto bail_nuke_ah; - - /* HTTP header had a content length? */ - - wsi->http.rx_content_length = 0; - wsi->http.content_length_explicitly_zero = 0; - if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - || - lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) || - lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI) -#endif - ) - wsi->http.rx_content_length = 100 * 1024 * 1024; - - if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) && - lws_hdr_copy(wsi, content_length_str, - sizeof(content_length_str) - 1, - WSI_TOKEN_HTTP_CONTENT_LENGTH) > 0) { - wsi->http.rx_content_remain = wsi->http.rx_content_length = - atoll(content_length_str); - if (!wsi->http.rx_content_length) { - wsi->http.content_length_explicitly_zero = 1; - lwsl_debug("%s: explicit 0 content-length\n", __func__); - } - } - - if (wsi->mux_substream) { - wsi->http.request_version = HTTP_VERSION_2; - } else { - /* http_version? Default to 1.0, override with token: */ - request_version = HTTP_VERSION_1_0; - - /* Works for single digit HTTP versions. : */ - http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP); - if (http_version_len > 7 && - lws_hdr_copy(wsi, http_version_str, - sizeof(http_version_str) - 1, - WSI_TOKEN_HTTP) > 0 && - http_version_str[5] == '1' && http_version_str[7] == '1') - request_version = HTTP_VERSION_1_1; - - wsi->http.request_version = request_version; - - /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */ - if (request_version == HTTP_VERSION_1_1) - conn_type = HTTP_CONNECTION_KEEP_ALIVE; - else - conn_type = HTTP_CONNECTION_CLOSE; - - /* Override default if http "Connection:" header: */ - if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION) && - lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1, - WSI_TOKEN_CONNECTION) > 0) { - http_conn_str[sizeof(http_conn_str) - 1] = '\0'; - if (!strcasecmp(http_conn_str, "keep-alive")) - conn_type = HTTP_CONNECTION_KEEP_ALIVE; - else - if (!strcasecmp(http_conn_str, "close")) - conn_type = HTTP_CONNECTION_CLOSE; - } - wsi->http.conn_type = conn_type; - } - - n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION, - wsi->user_space, uri_ptr, uri_len); - if (n) { - lwsl_info("LWS_CALLBACK_HTTP closing\n"); - - return 1; - } - /* - * if there is content supposed to be coming, - * put a timeout on it having arrived - */ - if (!wsi->mux_stream_immortal) - lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, - wsi->context->timeout_secs); -#ifdef LWS_WITH_TLS - if (wsi->tls.redirect_to_https) { - /* - * we accepted http:// only so we could redirect to - * https://, so issue the redirect. Create the redirection - * URI from the host: header and ignore the path part - */ - unsigned char *start = pt->serv_buf + LWS_PRE, *p = start, - *end = p + wsi->context->pt_serv_buf_size - LWS_PRE; - - n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST); - if (!n || n > 128) - goto bail_nuke_ah; - - p += lws_snprintf((char *)p, lws_ptr_diff(end, p), "https://"); - memcpy(p, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), n); - p += n; - *p++ = '/'; - *p = '\0'; - n = lws_ptr_diff(p, start); - - p += LWS_PRE; - n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, - start, n, &p, end); - if ((int)n < 0) - goto bail_nuke_ah; - - return lws_http_transaction_completed(wsi); - } -#endif - -#ifdef LWS_WITH_ACCESS_LOG - lws_prepare_access_log_info(wsi, uri_ptr, uri_len, meth); -#endif - - /* can we serve it from the mount list? */ - - hit = lws_find_mount(wsi, uri_ptr, uri_len); - if (!hit) { - /* deferred cleanup and reset to protocols[0] */ - - lwsl_info("no hit\n"); - - if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0], - "no mount hit")) - return 1; - - lwsi_set_state(wsi, LRS_DOING_TRANSACTION); - - m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, - wsi->user_space, uri_ptr, uri_len); - - goto after; - } - - s = uri_ptr + hit->mountpoint_len; - n = lws_http_redirect_hit(pt, wsi, hit, uri_ptr, uri_len, &ha); - if (ha) - return n; - -#if defined(LWS_WITH_HTTP_BASIC_AUTH) - - /* basic auth? */ - - switch (lws_check_basic_auth(wsi, hit->basic_auth_login_file, - hit->auth_mask & AUTH_MODE_MASK)) { - case LCBA_CONTINUE: - break; - case LCBA_FAILED_AUTH: - return lws_unauthorised_basic_auth(wsi); - case LCBA_END_TRANSACTION: - lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); - return lws_http_transaction_completed(wsi); - } -#endif - -#if defined(LWS_WITH_HTTP_PROXY) - /* - * The mount is a reverse proxy? - */ - - // if (hit) - // lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol); - //else - // lwsl_notice("%s: no hit\n", __func__); - - if (hit->origin_protocol == LWSMPRO_HTTPS || - hit->origin_protocol == LWSMPRO_HTTP) { - n = lws_http_proxy_start(wsi, hit, uri_ptr, 0); - // lwsl_notice("proxy start says %d\n", n); - if (n) - return n; - - goto deal_body; - } -#endif - - /* - * A particular protocol callback is mounted here? - * - * For the duration of this http transaction, bind us to the - * associated protocol - */ - if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) { - const struct lws_protocols *pp; - const char *name = hit->origin; - if (hit->protocol) - name = hit->protocol; - - pp = lws_vhost_name_to_protocol(wsi->vhost, name); - if (!pp) { - lwsl_err("Unable to find plugin '%s'\n", - hit->origin); - return 1; - } - - if (lws_bind_protocol(wsi, pp, "http action CALLBACK bind")) - return 1; - - lwsl_debug("%s: %s, checking access rights for mask 0x%x\n", - __func__, hit->origin, hit->auth_mask); - - args.p = uri_ptr; - args.len = uri_len; - args.max_len = hit->auth_mask & ~AUTH_MODE_MASK; - args.final = 0; /* used to signal callback dealt with it */ - args.chunked = 0; - - n = wsi->protocol->callback(wsi, - LWS_CALLBACK_CHECK_ACCESS_RIGHTS, - wsi->user_space, &args, 0); - if (n) { - lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED, - NULL); - goto bail_nuke_ah; - } - if (args.final) /* callback completely handled it well */ - return 0; - - if (hit->cgienv && wsi->protocol->callback(wsi, - LWS_CALLBACK_HTTP_PMO, - wsi->user_space, (void *)hit->cgienv, 0)) - return 1; - - if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) { - m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, - wsi->user_space, - uri_ptr + hit->mountpoint_len, - uri_len - hit->mountpoint_len); - goto after; - } - } - -#ifdef LWS_WITH_CGI - /* did we hit something with a cgi:// origin? */ - if (hit->origin_protocol == LWSMPRO_CGI) { - const char *cmd[] = { - NULL, /* replace with cgi path */ - NULL - }; - - lwsl_debug("%s: cgi\n", __func__); - cmd[0] = hit->origin; - - n = 5; - if (hit->cgi_timeout) - n = hit->cgi_timeout; - - n = lws_cgi(wsi, cmd, hit->mountpoint_len, n, - hit->cgienv); - if (n) { - lwsl_err("%s: cgi failed\n", __func__); - return -1; - } - - goto deal_body; - } -#endif - - n = uri_len - lws_ptr_diff(s, uri_ptr); - if (s[0] == '\0' || (n == 1 && s[n - 1] == '/')) - s = (char *)hit->def; - if (!s) - s = "index.html"; - - wsi->cache_secs = hit->cache_max_age; - wsi->cache_reuse = hit->cache_reusable; - wsi->cache_revalidate = hit->cache_revalidate; - wsi->cache_intermediaries = hit->cache_intermediaries; - - m = 1; -#if defined(LWS_WITH_FILE_OPS) - if (hit->origin_protocol == LWSMPRO_FILE) - m = lws_http_serve(wsi, s, hit->origin, hit); - - if (m > 0) -#endif - { - /* - * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL); - */ - if (hit->protocol) { - const struct lws_protocols *pp = - lws_vhost_name_to_protocol( - wsi->vhost, hit->protocol); - - lwsi_set_state(wsi, LRS_DOING_TRANSACTION); - - if (lws_bind_protocol(wsi, pp, "http_action HTTP")) - return 1; - - m = pp->callback(wsi, LWS_CALLBACK_HTTP, - wsi->user_space, - uri_ptr + hit->mountpoint_len, - uri_len - hit->mountpoint_len); - } else - m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, - wsi->user_space, uri_ptr, uri_len); - } - -after: - if (m) { - lwsl_info("LWS_CALLBACK_HTTP closing\n"); - - return 1; - } - -#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY) -deal_body: -#endif - /* - * If we're not issuing a file, check for content_length or - * HTTP keep-alive. No keep-alive header allocation for - * ISSUING_FILE, as this uses HTTP/1.0. - * - * In any case, return 0 and let lws_read decide how to - * proceed based on state - */ - if (lwsi_state(wsi) == LRS_ISSUING_FILE) - return 0; - - /* Prepare to read body if we have a content length: */ - lwsl_debug("wsi->http.rx_content_length %lld %d %d\n", - (long long)wsi->http.rx_content_length, - wsi->upgraded_to_http2, wsi->mux_substream); - - if (wsi->http.content_length_explicitly_zero && - lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) { - - /* - * POST with an explicit content-length of zero - * - * If we don't give the user code the empty HTTP_BODY callback, - * he may become confused to hear the HTTP_BODY_COMPLETION (due - * to, eg, instantiation of lws_spa never happened). - * - * HTTP_BODY_COMPLETION is responsible for sending the result - * status code and result body if any, and to do the transaction - * complete processing. - */ - if (wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_BODY, - wsi->user_space, NULL, 0)) - return 1; - if (wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_BODY_COMPLETION, - wsi->user_space, NULL, 0)) - return 1; - - return 0; - } - - if (wsi->http.rx_content_length <= 0) - return 0; - - if (lwsi_state(wsi) != LRS_DISCARD_BODY) { - lwsi_set_state(wsi, LRS_BODY); - lwsl_info("%s: %p: LRS_BODY state set (0x%x)\n", __func__, wsi, - (int)wsi->wsistate); - } - wsi->http.rx_content_remain = wsi->http.rx_content_length; - - /* - * At this point we have transitioned from deferred - * action to expecting BODY on the stream wsi, if it's - * in a bundle like h2. So if the stream wsi has its - * own buflist, we need to deal with that first. - */ - - while (1) { - struct lws_tokens ebuf; - int m; - - ebuf.len = (int)lws_buflist_next_segment_len(&wsi->buflist, - &ebuf.token); - if (!ebuf.len) - break; - - lwsl_debug("%s: consuming %d\n", __func__, (int)ebuf.len); - m = lws_read_h1(wsi, ebuf.token, ebuf.len); - if (m < 0) - return -1; - - if (lws_buflist_aware_finished_consuming(wsi, &ebuf, m, 1, - __func__)) - return -1; - } - - return 0; - -bail_nuke_ah: - lws_header_table_detach(wsi, 1); - - return 1; -} - -int -lws_confirm_host_header(struct lws *wsi) -{ - struct lws_tokenize ts; - lws_tokenize_elem e; - int port = 80, n; - char buf[128]; - - /* - * this vhost wants us to validate what the - * client sent against our vhost name - */ - - if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { - lwsl_info("%s: missing host on upgrade\n", __func__); - - return 1; - } - -#if defined(LWS_WITH_TLS) - if (wsi->tls.ssl) - port = 443; -#endif - - lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_DOT_NONTERM /* server.com */| - LWS_TOKENIZE_F_NO_FLOATS /* 1.server.com */| - LWS_TOKENIZE_F_MINUS_NONTERM /* a-b.com */); - n = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_HOST); - if (n <= 0) { - lwsl_info("%s: missing or oversize host header\n", __func__); - return 1; - } - ts.len = n; - - if (lws_tokenize(&ts) != LWS_TOKZE_TOKEN) - goto bad_format; - - if (strncmp(ts.token, wsi->vhost->name, ts.token_len)) { - buf[(ts.token - buf) + ts.token_len] = '\0'; - lwsl_info("%s: '%s' in host hdr but vhost name %s\n", - __func__, ts.token, wsi->vhost->name); - return 1; - } - - e = lws_tokenize(&ts); - if (e == LWS_TOKZE_DELIMITER && ts.token[0] == ':') { - if (lws_tokenize(&ts) != LWS_TOKZE_INTEGER) - goto bad_format; - else - port = atoi(ts.token); - } else - if (e != LWS_TOKZE_ENDED) - goto bad_format; - - if (wsi->vhost->listen_port != port) { - lwsl_info("%s: host port %d mismatches vhost port %d\n", - __func__, port, wsi->vhost->listen_port); - return 1; - } - - lwsl_debug("%s: host header OK\n", __func__); - - return 0; - -bad_format: - lwsl_info("%s: bad host header format\n", __func__); - - return 1; -} - -#if defined(LWS_WITH_SERVER) -int -lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen) -{ - const struct lws_role_ops *role = &role_ops_raw_skt; - const struct lws_protocols *p1, *protocol = - &wsi->vhost->protocols[wsi->vhost->raw_protocol_index]; - char ipbuf[64]; - int n; - - if (wsi->vhost->listen_accept_role && - lws_role_by_name(wsi->vhost->listen_accept_role)) - role = lws_role_by_name(wsi->vhost->listen_accept_role); - - if (wsi->vhost->listen_accept_protocol) { - p1 = lws_vhost_name_to_protocol(wsi->vhost, - wsi->vhost->listen_accept_protocol); - if (p1) - protocol = p1; - } - - lws_bind_protocol(wsi, protocol, __func__); - - lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, role); - - lws_header_table_detach(wsi, 0); - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED; - if (wsi->role_ops->adoption_cb[1]) - n = wsi->role_ops->adoption_cb[1]; - - ipbuf[0] = '\0'; -#if !defined(LWS_PLAT_OPTEE) - lws_get_peer_simple(wsi, ipbuf, sizeof(ipbuf)); -#endif - - lwsl_notice("%s: vh %s, peer: %s, role %s, " - "protocol %s, cb %d, ah %p\n", __func__, wsi->vhost->name, - ipbuf, role->name, protocol->name, n, wsi->http.ah); - - if ((wsi->protocol->callback)(wsi, n, wsi->user_space, NULL, 0)) - return 1; - - n = LWS_CALLBACK_RAW_RX; - if (wsi->role_ops->rx_cb[lwsi_role_server(wsi)]) - n = wsi->role_ops->rx_cb[lwsi_role_server(wsi)]; - if (wsi->protocol->callback(wsi, n, wsi->user_space, obuf, olen)) - return 1; - - return 0; -} - -int -lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len) -{ - struct lws_context *context = lws_get_context(wsi); - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; -#if defined(LWS_WITH_HTTP2) - struct allocated_headers *ah; -#endif - unsigned char *obuf = *buf; -#if defined(LWS_WITH_HTTP2) - char tbuf[128], *p; -#endif - size_t olen = len; - int n = 0, m, i; - - if (len >= 10000000) { - lwsl_err("%s: assert: len %ld\n", __func__, (long)len); - assert(0); - } - - if (!wsi->http.ah) { - lwsl_err("%s: assert: NULL ah\n", __func__); - assert(0); - } - - while (len) { - if (!lwsi_role_server(wsi) || !lwsi_role_http(wsi)) { - lwsl_err("%s: bad wsi role 0x%x\n", __func__, - (int)lwsi_role(wsi)); - goto bail_nuke_ah; - } - - i = (int)len; - m = lws_parse(wsi, *buf, &i); - lwsl_info("%s: parsed count %d\n", __func__, (int)len - i); - (*buf) += (int)len - i; - len = i; - - if (m == LPR_DO_FALLBACK) { - - /* - * http parser went off the rails and - * LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ - * ACCEPT_CONFIG is set on this vhost. - * - * We are transitioning from http with an AH, to - * a backup role (raw-skt, by default). Drop - * the ah, bind to the role with mode as - * ESTABLISHED. - */ -raw_transition: - - if (lws_http_to_fallback(wsi, obuf, olen)) { - lwsl_info("%s: fallback -> close\n", __func__); - goto bail_nuke_ah; - } - - (*buf) = obuf + olen; - - return 0; - } - if (m) { - lwsl_info("lws_parse failed\n"); - goto bail_nuke_ah; - } - - if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE) - continue; - - lwsl_parser("%s: lws_parse sees parsing complete\n", __func__); - - /* select vhost */ - - if (wsi->vhost->listen_port && - lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { - struct lws_vhost *vhost = lws_select_vhost( - context, wsi->vhost->listen_port, - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); - - if (vhost) - lws_vhost_bind_wsi(vhost, wsi); - } else - lwsl_info("no host\n"); - - if (!lwsi_role_h2(wsi) || !lwsi_role_server(wsi)) { -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.h1_trans++; -#endif - if (!wsi->conn_stat_done) { -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.h1_conn++; -#endif - wsi->conn_stat_done = 1; - } - } - - /* check for unwelcome guests */ -#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) - if (wsi->context->reject_service_keywords) { - const struct lws_protocol_vhost_options *rej = - wsi->context->reject_service_keywords; - char ua[384], *msg = NULL; - - if (lws_hdr_copy(wsi, ua, sizeof(ua) - 1, - WSI_TOKEN_HTTP_USER_AGENT) > 0) { -#ifdef LWS_WITH_ACCESS_LOG - char *uri_ptr = NULL; - int meth, uri_len; -#endif - ua[sizeof(ua) - 1] = '\0'; - while (rej) { - if (!strstr(ua, rej->name)) { - rej = rej->next; - continue; - } - - msg = strchr(rej->value, ' '); - if (msg) - msg++; - lws_return_http_status(wsi, - atoi(rej->value), msg); -#ifdef LWS_WITH_ACCESS_LOG - meth = lws_http_get_uri_and_method(wsi, - &uri_ptr, &uri_len); - if (meth >= 0) - lws_prepare_access_log_info(wsi, - uri_ptr, uri_len, meth); - - /* wsi close will do the log */ -#endif -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.rejected++; -#endif - /* - * We don't want anything from - * this rejected guy. Follow - * the close flow, not the - * transaction complete flow. - */ - goto bail_nuke_ah; - } - } - } -#endif - /* - * So he may have come to us requesting one or another kind - * of upgrade from http... but we may want to redirect him at - * http level. In that case, we need to check the redirect - * situation even though he's not actually wanting http and - * prioritize returning that if there is one. - */ - - { - const struct lws_http_mount *hit = NULL; - int uri_len = 0, ha, n; - char *uri_ptr = NULL; - - n = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len); - if (n >= 0) { - hit = lws_find_mount(wsi, uri_ptr, uri_len); - if (hit) { - n = lws_http_redirect_hit(pt, wsi, hit, uri_ptr, - uri_len, &ha); - if (ha) - return n; - } - } - } - - - - if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) { - lwsl_info("Changing to RAW mode\n"); - m = 0; - goto raw_transition; - } - - lwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT); - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) { - - const char *up = lws_hdr_simple_ptr(wsi, - WSI_TOKEN_UPGRADE); - - if (strcasecmp(up, "websocket") && - strcasecmp(up, "h2c")) { - lwsl_info("Unknown upgrade '%s'\n", up); - - if (lws_return_http_status(wsi, - HTTP_STATUS_FORBIDDEN, NULL) || - lws_http_transaction_completed(wsi)) - goto bail_nuke_ah; - } - - n = user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_HTTP_CONFIRM_UPGRADE, - wsi->user_space, (char *)up, 0); - - /* just hang up? */ - - if (n < 0) - goto bail_nuke_ah; - - /* callback returned headers already, do t_c? */ - - if (n > 0) { - if (lws_http_transaction_completed(wsi)) - goto bail_nuke_ah; - - /* continue on */ - - return 0; - } - - /* callback said 0, it was allowed */ - - if (wsi->vhost->options & - LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK && - lws_confirm_host_header(wsi)) - goto bail_nuke_ah; - - if (!strcasecmp(up, "websocket")) { -#if defined(LWS_ROLE_WS) -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.ws_upg++; -#endif - lwsl_info("Upgrade to ws\n"); - goto upgrade_ws; -#endif - } -#if defined(LWS_WITH_HTTP2) - if (!strcasecmp(up, "h2c")) { -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.h2_upg++; -#endif - lwsl_info("Upgrade to h2c\n"); - goto upgrade_h2c; - } -#endif - } - - /* no upgrade ack... he remained as HTTP */ - - lwsl_info("%s: %p: No upgrade\n", __func__, wsi); - - lwsi_set_state(wsi, LRS_ESTABLISHED); -#if defined(LWS_WITH_FILE_OPS) - wsi->http.fop_fd = NULL; -#endif - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - lws_http_compression_validate(wsi); -#endif - - lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi, - (void *)wsi->http.ah); - - n = lws_http_action(wsi); - - return n; - -#if defined(LWS_WITH_HTTP2) -upgrade_h2c: - if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) { - lwsl_info("missing http2_settings\n"); - goto bail_nuke_ah; - } - - lwsl_info("h2c upgrade...\n"); - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS); - /* convert the peer's HTTP-Settings */ - n = lws_b64_decode_string(p, tbuf, sizeof(tbuf)); - if (n < 0) { - lwsl_parser("HTTP2_SETTINGS too long\n"); - return 1; - } - - wsi->upgraded_to_http2 = 1; - - /* adopt the header info */ - - ah = wsi->http.ah; - lws_role_transition(wsi, LWSIFR_SERVER, LRS_H2_AWAIT_PREFACE, - &role_ops_h2); - - /* http2 union member has http union struct at start */ - wsi->http.ah = ah; - - if (!wsi->h2.h2n) { - wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n), "h2n"); - if (!wsi->h2.h2n) - return 1; - } - - lws_h2_init(wsi); - - /* HTTP2 union */ - - lws_h2_settings(wsi, &wsi->h2.h2n->peer_set, (uint8_t *)tbuf, n); - - lws_hpack_dynamic_size(wsi, wsi->h2.h2n->peer_set.s[ - H2SET_HEADER_TABLE_SIZE]); - - strcpy(tbuf, "HTTP/1.1 101 Switching Protocols\x0d\x0a" - "Connection: Upgrade\x0d\x0a" - "Upgrade: h2c\x0d\x0a\x0d\x0a"); - m = (int)strlen(tbuf); - n = lws_issue_raw(wsi, (unsigned char *)tbuf, m); - if (n != m) { - lwsl_debug("http2 switch: ERROR writing to socket\n"); - return 1; - } - - return 0; -#endif -#if defined(LWS_ROLE_WS) -upgrade_ws: - if (lws_process_ws_upgrade(wsi)) - goto bail_nuke_ah; - - return 0; -#endif - } /* while all chars are handled */ - - return 0; - -bail_nuke_ah: - /* drop the header info */ - lws_header_table_detach(wsi, 1); - - return 1; -} -#endif - -int LWS_WARN_UNUSED_RESULT -lws_http_transaction_completed(struct lws *wsi) -{ - int n; - - if (wsi->http.cgi_transaction_complete) - return 0; - - if (lws_has_buffered_out(wsi) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - || wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more -#endif - ) { - /* - * ...so he tried to send something large as the http reply, - * it went as a partial, but he immediately said the - * transaction was completed. - * - * Defer the transaction completed until the last part of the - * partial is sent. - */ - lwsl_debug("%s: %p: deferring due to partial\n", __func__, wsi); - wsi->http.deferred_transaction_completed = 1; - lws_callback_on_writable(wsi); - - return 0; - } - /* - * Are we finishing the transaction before we have consumed any body? - * - * For h1 this would kill keepalive pipelining, and for h2, considering - * it can extend over multiple DATA frames, it would kill the network - * connection. - */ - if (wsi->http.rx_content_length && wsi->http.rx_content_remain) { - /* - * are we already in LRS_DISCARD_BODY and didn't clear the - * remaining before trying to complete the transaction again? - */ - if (lwsi_state(wsi) == LRS_DISCARD_BODY) - return -1; - /* - * let's defer transaction completed processing until we - * discarded the remaining body - */ - lwsi_set_state(wsi, LRS_DISCARD_BODY); - - return 0; - } - - lwsl_info("%s: wsi %p\n", __func__, wsi); - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - lws_http_compression_destroy(wsi); -#endif - lws_access_log(wsi); - - if (!wsi->hdr_parsing_completed -#if defined(LWS_WITH_CGI) - && !wsi->http.cgi -#endif - ) { - char peer[64]; - -#if !defined(LWS_PLAT_OPTEE) - lws_get_peer_simple(wsi, peer, sizeof(peer) - 1); -#else - peer[0] = '\0'; -#endif - peer[sizeof(peer) - 1] = '\0'; - lwsl_notice("%s: (from %s) ignoring, ah parsing incomplete\n", - __func__, peer); - return 0; - } - -#if defined(LWS_WITH_CGI) - if (wsi->http.cgi) { - lwsl_debug("%s: cleaning cgi\n", __func__); - wsi->http.cgi_transaction_complete = 1; - lws_cgi_remove_and_kill(wsi); - lws_spawn_piped_destroy(&wsi->http.cgi->lsp); - - lws_free_set_NULL(wsi->http.cgi); - wsi->http.cgi_transaction_complete = 0; - } -#endif - - /* if we can't go back to accept new headers, drop the connection */ - if (wsi->mux_substream) - return 1; - - if (wsi->seen_zero_length_recv) - return 1; - - if (wsi->http.conn_type != HTTP_CONNECTION_KEEP_ALIVE) { - lwsl_info("%s: %p: close connection\n", __func__, wsi); - return 1; - } - - if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0], __func__)) - return 1; - - /* - * otherwise set ourselves up ready to go again, but because we have no - * idea about the wsi writability, we make put it in a holding state - * until we can verify POLLOUT. The part of this that confirms POLLOUT - * with no partials is in lws_server_socket_service() below. - */ - lwsl_debug("%s: %p: setting DEF_ACT from 0x%x: %p\n", __func__, - wsi, (int)wsi->wsistate, wsi->buflist); - lwsi_set_state(wsi, LRS_DEFERRING_ACTION); - wsi->http.tx_content_length = 0; - wsi->http.tx_content_remain = 0; - wsi->hdr_parsing_completed = 0; - wsi->sending_chunked = 0; -#ifdef LWS_WITH_ACCESS_LOG - wsi->http.access_log.sent = 0; -#endif -#if defined(LWS_WITH_FILE_OPS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)) - if (lwsi_role_http(wsi) && lwsi_role_server(wsi) && - wsi->http.fop_fd != NULL) - lws_vfs_file_close(&wsi->http.fop_fd); -#endif - - n = NO_PENDING_TIMEOUT; - if (wsi->vhost->keepalive_timeout) - n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE; - lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout); - - /* - * We already know we are on http1.1 / keepalive and the next thing - * coming will be another header set. - * - * If there is no pending rx and we still have the ah, drop it and - * reacquire a new ah when the new headers start to arrive. (Otherwise - * we needlessly hog an ah indefinitely.) - * - * However if there is pending rx and we know from the keepalive state - * that is already at least the start of another header set, simply - * reset the existing header table and keep it. - */ - if (wsi->http.ah) { - // lws_buflist_describe(&wsi->buflist, wsi, __func__); - if (!lws_buflist_next_segment_len(&wsi->buflist, NULL)) { - lwsl_debug("%s: %p: nothing in buflist, detaching ah\n", - __func__, wsi); - lws_header_table_detach(wsi, 1); -#ifdef LWS_WITH_TLS - /* - * additionally... if we are hogging an SSL instance - * with no pending pipelined headers (or ah now), and - * SSL is scarce, drop this connection without waiting - */ - - if (wsi->vhost->tls.use_ssl && - wsi->context->simultaneous_ssl_restriction && - wsi->context->simultaneous_ssl == - wsi->context->simultaneous_ssl_restriction) { - lwsl_info("%s: simultaneous_ssl_restriction\n", - __func__); - return 1; - } -#endif - } else { - lwsl_info("%s: %p: resetting/keeping ah as pipeline\n", - __func__, wsi); - lws_header_table_reset(wsi, 0); - /* - * If we kept the ah, we should restrict the amount - * of time we are willing to keep it. Otherwise it - * will be bound the whole time the connection remains - * open. - */ - lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH, - wsi->vhost->keepalive_timeout); - } - /* If we're (re)starting on headers, need other implied init */ - if (wsi->http.ah) - wsi->http.ah->ues = URIES_IDLE; - - //lwsi_set_state(wsi, LRS_ESTABLISHED); // !!! - } else - if (lws_buflist_next_segment_len(&wsi->buflist, NULL)) - if (lws_header_table_attach(wsi, 0)) - lwsl_debug("acquired ah\n"); - - lwsl_debug("%s: %p: keep-alive await new transaction (state 0x%x)\n", - __func__, wsi, (int)wsi->wsistate); - lws_callback_on_writable(wsi); - - return 0; -} - -#if defined(LWS_WITH_FILE_OPS) -int -lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, - const char *other_headers, int other_headers_len) -{ - struct lws_context *context = lws_get_context(wsi); - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - unsigned char *response = pt->serv_buf + LWS_PRE; -#if defined(LWS_WITH_RANGES) - struct lws_range_parsing *rp = &wsi->http.range; -#endif - int ret = 0, cclen = 8, n = HTTP_STATUS_OK; - char cache_control[50], *cc = "no-store"; - lws_fop_flags_t fflags = LWS_O_RDONLY; - const struct lws_plat_file_ops *fops; - lws_filepos_t total_content_length; - unsigned char *p = response; - unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE; - const char *vpath; -#if defined(LWS_WITH_RANGES) - int ranges; -#endif - - if (wsi->handling_404) - n = HTTP_STATUS_NOT_FOUND; - - /* - * We either call the platform fops .open with first arg platform fops, - * or we call fops_zip .open with first arg platform fops, and fops_zip - * open will decide whether to switch to fops_zip or stay with fops_def. - * - * If wsi->http.fop_fd is already set, the caller already opened it - */ - if (!wsi->http.fop_fd) { - fops = lws_vfs_select_fops(wsi->context->fops, file, &vpath); - fflags |= lws_vfs_prepare_flags(wsi); - wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops, - file, vpath, &fflags); - if (!wsi->http.fop_fd) { - lwsl_info("%s: Unable to open: '%s': errno %d\n", - __func__, file, errno); - if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, - NULL)) - return -1; - return !wsi->mux_substream; - } - } - - /* - * Caution... wsi->http.fop_fd is live from here - */ - - wsi->http.filelen = lws_vfs_get_length(wsi->http.fop_fd); - total_content_length = wsi->http.filelen; - -#if defined(LWS_WITH_RANGES) - ranges = lws_ranges_init(wsi, rp, wsi->http.filelen); - - lwsl_debug("Range count %d\n", ranges); - /* - * no ranges -> 200; - * 1 range -> 206 + Content-Type: normal; Content-Range; - * more -> 206 + Content-Type: multipart/byteranges - * Repeat the true Content-Type in each multipart header - * along with Content-Range - */ - if (ranges < 0) { - /* it means he expressed a range in Range:, but it was illegal */ - lws_return_http_status(wsi, - HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, NULL); - if (lws_http_transaction_completed(wsi)) - goto bail; /* <0 means just hang up */ - - lws_vfs_file_close(&wsi->http.fop_fd); - - return 0; /* == 0 means we did the transaction complete */ - } - if (ranges) - n = HTTP_STATUS_PARTIAL_CONTENT; -#endif - - if (lws_add_http_header_status(wsi, n, &p, end)) - goto bail; - - if ((wsi->http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | - LWS_FOP_FLAG_COMPR_IS_GZIP)) == - (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) { - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_ENCODING, - (unsigned char *)"gzip", 4, &p, end)) - goto bail; - lwsl_info("file is being provided in gzip\n"); - } -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - else { - /* - * if we know its very compressible, and we can use - * compression, then use the most preferred compression - * method that the client said he will accept - */ - - if (!wsi->interpreting && ( - !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 ( -#if defined(LWS_WITH_RANGES) - ranges < 2 && -#endif - content_type && content_type[0]) - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_TYPE, - (unsigned char *)content_type, - (int)strlen(content_type), - &p, end)) - goto bail; - -#if defined(LWS_WITH_RANGES) - if (ranges >= 2) { /* multipart byteranges */ - lws_strncpy(wsi->http.multipart_content_type, content_type, - sizeof(wsi->http.multipart_content_type)); - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_TYPE, - (unsigned char *) - "multipart/byteranges; " - "boundary=_lws", - 20, &p, end)) - goto bail; - - /* - * our overall content length has to include - * - * - (n + 1) x "_lws\r\n" - * - n x Content-Type: xxx/xxx\r\n - * - n x Content-Range: bytes xxx-yyy/zzz\r\n - * - n x /r/n - * - the actual payloads (aggregated in rp->agg) - * - * Precompute it for the main response header - */ - - total_content_length = (lws_filepos_t)rp->agg + - 6 /* final _lws\r\n */; - - lws_ranges_reset(rp); - while (lws_ranges_next(rp)) { - n = lws_snprintf(cache_control, sizeof(cache_control), - "bytes %llu-%llu/%llu", - rp->start, rp->end, rp->extent); - - total_content_length += - 6 /* header _lws\r\n */ + - /* Content-Type: xxx/xxx\r\n */ - 14 + strlen(content_type) + 2 + - /* Content-Range: xxxx\r\n */ - 15 + n + 2 + - 2; /* /r/n */ - } - - lws_ranges_reset(rp); - lws_ranges_next(rp); - } - - if (ranges == 1) { - total_content_length = (lws_filepos_t)rp->agg; - n = lws_snprintf(cache_control, sizeof(cache_control), - "bytes %llu-%llu/%llu", - rp->start, rp->end, rp->extent); - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_RANGE, - (unsigned char *)cache_control, - n, &p, end)) - goto bail; - } - - wsi->http.range.inside = 0; - - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES, - (unsigned char *)"bytes", 5, &p, end)) - goto bail; -#endif - - if (!wsi->mux_substream) { - /* for http/1.1 ... */ - if (!wsi->sending_chunked -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - && !wsi->http.lcs -#endif - ) { - /* ... if not already using chunked and not using an - * http compression translation, then send the naive - * content length - */ - if (lws_add_http_header_content_length(wsi, - total_content_length, &p, end)) - goto bail; - } else { - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - if (wsi->http.lcs) { - - /* ...otherwise, for http 1 it must go chunked. - * For the compression case, the reason is we - * compress on the fly and do not know the - * compressed content-length until it has all - * been sent. Http/1.1 pipelining must be able - * to know where the transaction boundaries are - * ... so chunking... - */ - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_TRANSFER_ENCODING, - (unsigned char *)"chunked", 7, - &p, end)) - goto bail; - - /* - * ...this is fun, isn't it :-) For h1 that is - * using an http compression translation, the - * compressor must chunk its output privately. - * - * h2 doesn't need (or support) any of this - * crap. - */ - lwsl_debug("setting chunking\n"); - wsi->http.comp_ctx.chunking = 1; - } -#endif - } - } - - if (wsi->cache_secs && wsi->cache_reuse) { - if (!wsi->cache_revalidate) { - cc = cache_control; - cclen = sprintf(cache_control, "%s, max-age=%u", - intermediates[wsi->cache_intermediaries], - wsi->cache_secs); - } else { - cc = cache_control; - cclen = sprintf(cache_control, - "must-revalidate, %s, max-age=%u", - intermediates[wsi->cache_intermediaries], - wsi->cache_secs); - - } - } - - /* Only add cache control if its not specified by any other_headers. */ - if (!other_headers || - (!strstr(other_headers, "cache-control") && - !strstr(other_headers, "Cache-Control"))) { - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CACHE_CONTROL, - (unsigned char *)cc, cclen, &p, end)) - goto bail; - } - - if (other_headers) { - if ((end - p) < other_headers_len) - goto bail; - memcpy(p, other_headers, other_headers_len); - p += other_headers_len; - } - - if (lws_finalize_http_header(wsi, &p, end)) - goto bail; - - ret = lws_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS); - if (ret != (p - response)) { - lwsl_err("_write returned %d from %ld\n", ret, - (long)(p - response)); - goto bail; - } - - wsi->http.filepos = 0; - lwsi_set_state(wsi, LRS_ISSUING_FILE); - - if (lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_URI)) { - /* we do not emit the body */ - lws_vfs_file_close(&wsi->http.fop_fd); - if (lws_http_transaction_completed(wsi)) - goto bail; - - return 0; - } - - lws_callback_on_writable(wsi); - - return 0; - -bail: - lws_vfs_file_close(&wsi->http.fop_fd); - - return -1; -} -#endif - -#if defined(LWS_WITH_FILE_OPS) - -int lws_serve_http_file_fragment(struct lws *wsi) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - struct lws_process_html_args args; - lws_filepos_t amount, poss; - unsigned char *p, *pstart; -#if defined(LWS_WITH_RANGES) - unsigned char finished = 0; -#endif - int n, m; - - lwsl_debug("wsi->mux_substream %d\n", wsi->mux_substream); - - do { - - /* priority 1: buffered output */ - - if (lws_has_buffered_out(wsi)) { - if (lws_issue_raw(wsi, NULL, 0) < 0) { - lwsl_info("%s: closing\n", __func__); - goto file_had_it; - } - break; - } - - /* priority 2: buffered pre-compression-transform */ - -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - if (wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more) { - enum lws_write_protocol wp = LWS_WRITE_HTTP; - - lwsl_info("%s: completing comp partial (buflist %p, may %d)\n", - __func__, wsi->http.comp_ctx.buflist_comp, - wsi->http.comp_ctx.may_have_more); - - if (wsi->role_ops->write_role_protocol(wsi, NULL, 0, &wp) < 0) { - lwsl_info("%s signalling to close\n", __func__); - goto file_had_it; - } - lws_callback_on_writable(wsi); - - break; - } -#endif - - if (wsi->http.filepos == wsi->http.filelen) - goto all_sent; - - n = 0; - p = pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH; - -#if defined(LWS_WITH_RANGES) - if (wsi->http.range.count_ranges && !wsi->http.range.inside) { - - lwsl_notice("%s: doing range start %llu\n", __func__, - wsi->http.range.start); - - if ((long long)lws_vfs_file_seek_cur(wsi->http.fop_fd, - wsi->http.range.start - - wsi->http.filepos) < 0) - goto file_had_it; - - wsi->http.filepos = wsi->http.range.start; - - if (wsi->http.range.count_ranges > 1) { - n = lws_snprintf((char *)p, - context->pt_serv_buf_size - - LWS_H2_FRAME_HEADER_LENGTH, - "_lws\x0d\x0a" - "Content-Type: %s\x0d\x0a" - "Content-Range: bytes " - "%llu-%llu/%llu\x0d\x0a" - "\x0d\x0a", - wsi->http.multipart_content_type, - wsi->http.range.start, - wsi->http.range.end, - wsi->http.range.extent); - p += n; - } - - wsi->http.range.budget = wsi->http.range.end - - wsi->http.range.start + 1; - wsi->http.range.inside = 1; - } -#endif - - poss = context->pt_serv_buf_size - n - - LWS_H2_FRAME_HEADER_LENGTH; - - if (wsi->http.tx_content_length) - if (poss > wsi->http.tx_content_remain) - poss = wsi->http.tx_content_remain; - - /* - * If there is a hint about how much we will do well to send at - * one time, restrict ourselves to only trying to send that. - */ - if (wsi->protocol->tx_packet_size && - poss > wsi->protocol->tx_packet_size) - poss = wsi->protocol->tx_packet_size; - - if (wsi->role_ops->tx_credit) { - lws_filepos_t txc = - wsi->role_ops->tx_credit(wsi, LWSTXCR_US_TO_PEER, 0); - - if (!txc) { - /* - * We shouldn't've been able to get the - * WRITEABLE if we are skint - */ - lwsl_notice("%s: %p: no tx credit\n", __func__, - wsi); - - return 0; - } - if (txc < poss) - poss = txc; - - /* - * Tracking consumption of the actual payload amount - * will be handled when the role data frame is sent... - */ - } - -#if defined(LWS_WITH_RANGES) - if (wsi->http.range.count_ranges) { - if (wsi->http.range.count_ranges > 1) - poss -= 7; /* allow for final boundary */ - if (poss > wsi->http.range.budget) - poss = wsi->http.range.budget; - } -#endif - if (wsi->sending_chunked) { - /* we need to drop the chunk size in here */ - p += 10; - /* allow for the chunk to grow by 128 in translation */ - poss -= 10 + 128; - } - - amount = 0; - if (lws_vfs_file_read(wsi->http.fop_fd, &amount, p, poss) < 0) - goto file_had_it; /* caller will close */ - - if (wsi->sending_chunked) - n = (int)amount; - else - n = lws_ptr_diff(p, pstart) + (int)amount; - - lwsl_debug("%s: sending %d\n", __func__, n); - - if (n) { - lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, - context->timeout_secs); - - if (wsi->interpreting) { - args.p = (char *)p; - args.len = n; - args.max_len = (unsigned int)poss + 128; - args.final = wsi->http.filepos + n == - wsi->http.filelen; - args.chunked = wsi->sending_chunked; - if (user_callback_handle_rxflow( - wsi->vhost->protocols[ - (int)wsi->protocol_interpret_idx].callback, - wsi, LWS_CALLBACK_PROCESS_HTML, - wsi->user_space, &args, 0) < 0) - goto file_had_it; - n = args.len; - p = (unsigned char *)args.p; - } else - p = pstart; - -#if defined(LWS_WITH_RANGES) - if (wsi->http.range.send_ctr + 1 == - wsi->http.range.count_ranges && // last range - wsi->http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart) - wsi->http.range.budget - amount == 0) {// final part - n += lws_snprintf((char *)pstart + n, 6, - "_lws\x0d\x0a"); // append trailing boundary - lwsl_debug("added trailing boundary\n"); - } -#endif - m = lws_write(wsi, p, n, wsi->http.filepos + amount == - wsi->http.filelen ? - LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP); - if (m < 0) - goto file_had_it; - - wsi->http.filepos += amount; - -#if defined(LWS_WITH_RANGES) - if (wsi->http.range.count_ranges >= 1) { - wsi->http.range.budget -= amount; - if (wsi->http.range.budget == 0) { - lwsl_notice("range budget exhausted\n"); - wsi->http.range.inside = 0; - wsi->http.range.send_ctr++; - - if (lws_ranges_next(&wsi->http.range) < 1) { - finished = 1; - goto all_sent; - } - } - } -#endif - - if (m != n) { - /* adjust for what was not sent */ - if (lws_vfs_file_seek_cur(wsi->http.fop_fd, - m - n) == - (lws_fileofs_t)-1) - goto file_had_it; - } - } - -all_sent: - if ((!lws_has_buffered_out(wsi) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - && !wsi->http.comp_ctx.buflist_comp && - !wsi->http.comp_ctx.may_have_more -#endif - ) && (wsi->http.filepos >= wsi->http.filelen -#if defined(LWS_WITH_RANGES) - || finished) -#else - ) -#endif - ) { - lwsi_set_state(wsi, LRS_ESTABLISHED); - /* we might be in keepalive, so close it off here */ - lws_vfs_file_close(&wsi->http.fop_fd); - - lwsl_debug("file completed\n"); - - if (wsi->protocol->callback && - user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION, - wsi->user_space, NULL, 0) < 0) { - /* - * For http/1.x, the choices from - * transaction_completed are either - * 0 to use the connection for pipelined - * or nonzero to hang it up. - * - * However for http/2. while we are - * still interested in hanging up the - * nwsi if there was a network-level - * fatal error, simply completing the - * transaction is a matter of the stream - * state, not the root connection at the - * network level - */ - if (wsi->mux_substream) - return 1; - else - return -1; - } - - return 1; /* >0 indicates completed */ - } - /* - * while(1) here causes us to spam the whole file contents into - * a hugely bloated output buffer if it ever can't send the - * whole chunk... - */ - } while (!lws_send_pipe_choked(wsi)); - - lws_callback_on_writable(wsi); - - return 0; /* indicates further processing must be done */ - -file_had_it: - lws_vfs_file_close(&wsi->http.fop_fd); - - return -1; -} - -#endif - -#if defined(LWS_WITH_SERVER) -void -lws_server_get_canonical_hostname(struct lws_context *context, - const struct lws_context_creation_info *info) -{ - if (lws_check_opt(info->options, - LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)) - return; -#if !defined(LWS_PLAT_FREERTOS) - /* find canonical hostname */ - gethostname((char *)context->canonical_hostname, - sizeof(context->canonical_hostname) - 1); - - lwsl_info(" canonical_hostname = %s\n", context->canonical_hostname); -#else - (void)context; -#endif -} -#endif - -int -lws_chunked_html_process(struct lws_process_html_args *args, - struct lws_process_html_state *s) -{ - char *sp, buffer[32]; - const char *pc; - int old_len, n; - - /* do replacements */ - sp = args->p; - old_len = args->len; - args->len = 0; - s->start = sp; - while (sp < args->p + old_len) { - - if (args->len + 7 >= args->max_len) { - lwsl_err("Used up interpret padding\n"); - return -1; - } - - if ((!s->pos && *sp == '$') || s->pos) { - int hits = 0, hit = 0; - - if (!s->pos) - s->start = sp; - s->swallow[s->pos++] = *sp; - if (s->pos == sizeof(s->swallow) - 1) - goto skip; - for (n = 0; n < s->count_vars; n++) - if (!strncmp(s->swallow, s->vars[n], s->pos)) { - hits++; - hit = n; - } - if (!hits) { -skip: - s->swallow[s->pos] = '\0'; - memcpy(s->start, s->swallow, s->pos); - args->len++; - s->pos = 0; - sp = s->start + 1; - continue; - } - if (hits == 1 && s->pos == (int)strlen(s->vars[hit])) { - pc = s->replace(s->data, hit); - if (!pc) - pc = "NULL"; - n = (int)strlen(pc); - s->swallow[s->pos] = '\0'; - if (n != s->pos) { - memmove(s->start + n, s->start + s->pos, - old_len - (sp - args->p) - 1); - old_len += (n - s->pos) + 1; - } - memcpy(s->start, pc, n); - args->len++; - sp = s->start + 1; - - s->pos = 0; - } - sp++; - continue; - } - - args->len++; - sp++; - } - - if (args->chunked) { - /* no space left for final chunk trailer */ - if (args->final && args->len + 7 >= args->max_len) - return -1; - - n = sprintf(buffer, "%X\x0d\x0a", args->len); - - args->p -= n; - memcpy(args->p, buffer, n); - args->len += n; - - if (args->final) { - sp = args->p + args->len; - *sp++ = '\x0d'; - *sp++ = '\x0a'; - *sp++ = '0'; - *sp++ = '\x0d'; - *sp++ = '\x0a'; - *sp++ = '\x0d'; - *sp++ = '\x0a'; - args->len += 7; - } else { - sp = args->p + args->len; - *sp++ = '\x0d'; - *sp++ = '\x0a'; - args->len += 2; - } - } - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/roles/listen/ops-listen.c libwebsockets-2.4.2/lib/roles/listen/ops-listen.c --- libwebsockets-4.0.20/lib/roles/listen/ops-listen.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/listen/ops-listen.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,209 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -static int -rops_handle_POLLIN_listen(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_pollfd *pollfd) -{ - struct lws_context *context = wsi->context; - lws_sockfd_type accept_fd = LWS_SOCK_INVALID; - lws_sock_file_fd_type fd; - struct sockaddr_storage cli_addr; - socklen_t clilen; - - memset(&cli_addr, 0, sizeof(cli_addr)); - - /* if our vhost is going down, ignore it */ - - if (wsi->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->vhost->tls.use_ssl && - context->simultaneous_ssl_restriction && - context->simultaneous_ssl == - context->simultaneous_ssl_restriction) - /* - * no... ignore it, he won't come again until - * we are below the simultaneous_ssl_restriction - * limit and POLLIN is enabled on him again - */ - break; -#endif - /* listen socket got an unencrypted connection... */ - - clilen = sizeof(cli_addr); - - /* - * We cannot identify the peer who is in the listen - * socket connect queue before we accept it; even if - * we could, not accepting it due to PEER_LIMITS would - * block the connect queue for other legit peers. - */ - - accept_fd = accept((int)pollfd->fd, - (struct sockaddr *)&cli_addr, &clilen); - if (accept_fd == LWS_SOCK_INVALID) { - if (LWS_ERRNO == LWS_EAGAIN || - LWS_ERRNO == LWS_EWOULDBLOCK) { - break; - } - lwsl_err("accept: %s\n", strerror(LWS_ERRNO)); - return LWS_HPI_RET_HANDLED; - } - - if (context->being_destroyed) { - compatible_close(accept_fd); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - lws_plat_set_socket_options(wsi->vhost, accept_fd, 0); - -#if defined(LWS_WITH_IPV6) - lwsl_debug("accepted new conn port %u on fd=%d\n", - ((cli_addr.ss_family == AF_INET6) ? - ntohs(((struct sockaddr_in6 *) &cli_addr)->sin6_port) : - ntohs(((struct sockaddr_in *) &cli_addr)->sin_port)), - accept_fd); -#else - { - struct sockaddr_in sain; - - memcpy(&sain, &cli_addr, sizeof(sain)); - lwsl_debug("accepted new conn port %u on fd=%d\n", - ntohs(sain.sin_port), - accept_fd); - } -#endif - - /* - * look at who we connected to and give user code a - * chance to reject based on client IP. There's no - * protocol selected yet so we issue this to - * protocols[0] - */ - if ((wsi->vhost->protocols[0].callback)(wsi, - LWS_CALLBACK_FILTER_NETWORK_CONNECTION, - NULL, - (void *)(lws_intptr_t)accept_fd, 0)) { - lwsl_debug("Callback denied net connection\n"); - compatible_close(accept_fd); - return LWS_HPI_RET_HANDLED; - } - - if (!(wsi->vhost->options & - LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG)) - opts |= LWS_ADOPT_HTTP; - -#if defined(LWS_WITH_TLS) - if (!wsi->vhost->tls.use_ssl) -#endif - opts &= ~LWS_ADOPT_ALLOW_SSL; - - fd.sockfd = accept_fd; - cwsi = lws_adopt_descriptor_vhost(wsi->vhost, opts, fd, - NULL, NULL); - if (!cwsi) { - lwsl_info("%s: vh %s: adopt failed\n", __func__, - wsi->vhost->name); - - /* already closed cleanly as necessary */ - return LWS_HPI_RET_WSI_ALREADY_DIED; - } -/* - if (lws_server_socket_service_ssl(cwsi, accept_fd)) { - lws_close_free_wsi(cwsi, LWS_CLOSE_STATUS_NOSTATUS, - "listen svc fail"); - return LWS_HPI_RET_WSI_ALREADY_DIED; - } - - lwsl_info("%s: new wsi %p: wsistate 0x%lx, role_ops %s\n", - __func__, 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; -} - -const struct lws_role_ops role_ops_listen = { - /* role name */ "listen", - /* alpn id */ NULL, - /* check_upgrades */ NULL, - /* pt_init_destroy */ NULL, - /* init_vhost */ NULL, - /* destroy_vhost */ NULL, - /* service_flag_pending */ NULL, - /* handle_POLLIN */ rops_handle_POLLIN_listen, - /* handle_POLLOUT */ rops_handle_POLLOUT_listen, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ NULL, - /* tx_credit */ NULL, - /* write_role_protocol */ NULL, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ NULL, - /* close_via_role_protocol */ NULL, - /* close_role */ NULL, - /* close_kill_connection */ NULL, - /* destroy_role */ NULL, - /* adoption_bind */ NULL, - /* client_bind */ NULL, - /* issue_keepalive */ NULL, - /* 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, -}; diff -Nru libwebsockets-4.0.20/lib/roles/mqtt/client/client-mqtt.c libwebsockets-2.4.2/lib/roles/mqtt/client/client-mqtt.c --- libwebsockets-4.0.20/lib/roles/mqtt/client/client-mqtt.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/mqtt/client/client-mqtt.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,392 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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->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 = 23; - - if (len > 23) /* 3.1.3.1-5: Server MUST... between 1 and 23 chars... */ - return 1; - - *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'; - } - - lws_mqtt_str_advance(*ms, (uint16_t)len); - - 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, 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[0]) - c->conn_flags = LMQCFT_CLEAN_START; - - c->keep_alive_secs = cp->keep_alive; - - 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 |= (cp->will_param.qos << 3) & LMQCFT_WILL_QOS_MASK; - c->conn_flags |= (!!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 (cp->password) { - c->password = - lws_mqtt_str_create_cstr_dup(cp->password, 0); - if (!c->password) - goto oom4; - c->conn_flags |= LMQCFT_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->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - int n = 0, m = 0; - struct lws_tokens ebuf; - int buffered = 0; - char 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 */ - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (context->detailed_latency_cb) { - wsi->detlat.type = LDLT_TLS_NEG_CLIENT; - wsi->detlat.latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE] = - lws_now_usecs() - - wsi->detlat.earliest_write_req_pre_write; - wsi->detlat.latencies[LAT_DUR_USERCB] = 0; - lws_det_lat_cb(wsi->context, &wsi->detlat); - } -#endif -#if 0 - if (wsi->client_h2_alpn) { - /* - * We connected to the server and set up tls, and - * negotiated "h2". - * - * So this is it, we are an h2 master client connection - * now, not an h1 client connection. - */ -#if defined(LWS_WITH_TLS) - lws_tls_server_conn_alpn(wsi); -#endif - - /* send the H2 preface to legitimize the connection */ - if (lws_h2_issue_preface(wsi)) { - cce = "error sending h2 preface"; - goto bail3; - } - - break; - } -#endif - - /* 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, - context->timeout_secs); - - /* fallthru */ - - case LRS_MQTTC_IDLE: - /* - * we should be ready to send out MQTT CONNECT - */ - lwsl_info("%s: wsi %p: Transport established, send out CONNECT\n", - __func__, 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 = wsi->context->pt_serv_buf_size; - - if ((unsigned int)ebuf.len > wsi->context->pt_serv_buf_size) - ebuf.len = wsi->context->pt_serv_buf_size; - - if ((int)pending > ebuf.len) - pending = ebuf.len; - - ebuf.len = lws_ssl_capable_read(wsi, ebuf.token, - pending ? (int)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, 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; -} diff -Nru libwebsockets-4.0.20/lib/roles/mqtt/client/client-mqtt-handshake.c libwebsockets-2.4.2/lib/roles/mqtt/client/client-mqtt-handshake.c --- libwebsockets-4.0.20/lib/roles/mqtt/client/client-mqtt-handshake.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/mqtt/client/client-mqtt-handshake.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,182 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * Sakthi Kannan - * - * 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 - -#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, - 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 += c->username->len + 2; - if (c->conn_flags & LMQCFT_PASSWORD) - len += (c->password ? c->password->len : 0) + 2; - } - if (c->conn_flags & LMQCFT_WILL_FLAG && c->will.topic) { - len += c->will.topic->len + 2; - len += (c->will.message ? c->will.message->len : 0) + 2; - } - 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++ = 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_CLEAN_START) == 0) { - *p++ = 0; /* no properties */ - break; - } - 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.topic->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(p, start), - LWS_WRITE_BINARY) != lws_ptr_diff(p, start)) { - lwsl_notice("%s: write failed\n", __func__); - - return NULL; - } - - return wsi; -} diff -Nru libwebsockets-4.0.20/lib/roles/mqtt/mqtt.c libwebsockets-2.4.2/lib/roles/mqtt/mqtt.c --- libwebsockets-4.0.20/lib/roles/mqtt/mqtt.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/mqtt/mqtt.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2129 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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 - * - * Control Packet structure - * - * - Always: 2+ byte: Fixed Hdr - * - Required in some: variable: Variable Hdr + [(CONNECT)Will Props] + Props - * - Required in some: variable: Payload - * - * For CONNECT, the props if present MUST be in the order [MQTT-3.1.3-1] - * - * - Client Identifier - * - Will Properties - * - Will Topic - * - Will Payload - * - User Name - * - Password - */ - -#include "private-lib-core.h" -/* #include "lws-mqtt.h" */ - -#include -#include -#include -#include - -typedef enum { - LMQPRS_AWAITING_CONNECT, - -} lws_mqtt_protocol_server_connstate_t; - -const char * const reason_names_g1[] = { - "Success / Normal disconnection / QoS0", - "QoS1", - "QoS2", - "Disconnect Will", - "No matching subscriber", - "No subscription existed", - "Continue authentication", - "Re-authenticate" -}; - -const char * const reason_names_g2[] = { - "Unspecified error", - "Malformed packet", - "Protocol error", - "Implementation specific error", - "Unsupported protocol", - "Client ID invalid", - "Bad credentials", - "Not Authorized", - "Server Unavailable", - "Server Busy", - "Banned", - "Server Shutting Down", - "Bad Authentication Method", - "Keepalive Timeout", - "Session taken over", - "Topic Filter Invalid", - "Packet ID in use", - "Packet ID not found", - "Max RX Exceeded", - "Topic Alias Invalid", - "Packet too large", - "Ratelimit", - "Quota Exceeded", - "Administrative Action", - "Payload format invalid", - "Retain not supported", - "QoS not supported", - "Use another server", - "Server Moved", - "Shared subscriptions not supported", - "Connection rate exceeded", - "Maximum Connect Time", - "Subscription IDs not supported", - "Wildcard subscriptions not supported" -}; - -#define LMQCP_WILL_PROPERTIES 0 - -/* For each property, a bitmap describing which commands it is valid for */ - -static const uint16_t property_valid[] = { - [LMQPROP_PAYLOAD_FORMAT_INDICATOR] = (1 << LMQCP_PUBLISH) | - (1 << LMQCP_WILL_PROPERTIES), - [LMQPROP_MESSAGE_EXPIRY_INTERVAL] = (1 << LMQCP_PUBLISH) | - (1 << LMQCP_WILL_PROPERTIES), - [LMQPROP_CONTENT_TYPE] = (1 << LMQCP_PUBLISH) | - (1 << LMQCP_WILL_PROPERTIES), - [LMQPROP_RESPONSE_TOPIC] = (1 << LMQCP_PUBLISH) | - (1 << LMQCP_WILL_PROPERTIES), - [LMQPROP_CORRELATION_DATA] = (1 << LMQCP_PUBLISH) | - (1 << LMQCP_WILL_PROPERTIES), - [LMQPROP_SUBSCRIPTION_IDENTIFIER] = (1 << LMQCP_PUBLISH) | - (1 << LMQCP_CTOS_SUBSCRIBE), - [LMQPROP_SESSION_EXPIRY_INTERVAL] = (1 << LMQCP_CTOS_CONNECT) | - (1 << LMQCP_STOC_CONNACK) | - (1 << LMQCP_DISCONNECT), - [LMQPROP_ASSIGNED_CLIENT_IDENTIFIER] = (1 << LMQCP_STOC_CONNACK), - [LMQPROP_SERVER_KEEP_ALIVE] = (1 << LMQCP_STOC_CONNACK), - [LMQPROP_AUTHENTICATION_METHOD] = (1 << LMQCP_CTOS_CONNECT) | - (1 << LMQCP_STOC_CONNACK) | - (1 << LMQCP_AUTH), - [LMQPROP_AUTHENTICATION_DATA] = (1 << LMQCP_CTOS_CONNECT) | - (1 << LMQCP_STOC_CONNACK) | - (1 << LMQCP_AUTH), - [LMQPROP_REQUEST_PROBLEM_INFORMATION] = (1 << LMQCP_CTOS_CONNECT), - [LMQPROP_WILL_DELAY_INTERVAL] = (1 << LMQCP_WILL_PROPERTIES), - [LMQPROP_REQUEST_RESPONSE_INFORMATION] = (1 << LMQCP_CTOS_CONNECT), - [LMQPROP_RESPONSE_INFORMATION] = (1 << LMQCP_STOC_CONNACK), - [LMQPROP_SERVER_REFERENCE] = (1 << LMQCP_STOC_CONNACK) | - (1 << LMQCP_DISCONNECT), - [LMQPROP_REASON_STRING] = (1 << LMQCP_STOC_CONNACK) | - (1 << LMQCP_PUBACK) | - (1 << LMQCP_PUBREC) | - (1 << LMQCP_PUBREL) | - (1 << LMQCP_PUBCOMP) | - (1 << LMQCP_STOC_SUBACK) | - (1 << LMQCP_STOC_UNSUBACK) | - (1 << LMQCP_DISCONNECT) | - (1 << LMQCP_AUTH), - [LMQPROP_RECEIVE_MAXIMUM] = (1 << LMQCP_CTOS_CONNECT) | - (1 << LMQCP_STOC_CONNACK), - [LMQPROP_TOPIC_ALIAS_MAXIMUM] = (1 << LMQCP_CTOS_CONNECT) | - (1 << LMQCP_STOC_CONNACK), - [LMQPROP_TOPIC_ALIAS] = (1 << LMQCP_PUBLISH), - [LMQPROP_MAXIMUM_QOS] = (1 << LMQCP_STOC_CONNACK), - [LMQPROP_RETAIN_AVAILABLE] = (1 << LMQCP_STOC_CONNACK), - [LMQPROP_USER_PROPERTY] = (1 << LMQCP_CTOS_CONNECT) | - (1 << LMQCP_STOC_CONNACK) | - (1 << LMQCP_PUBLISH) | - (1 << LMQCP_WILL_PROPERTIES) | - (1 << LMQCP_PUBACK) | - (1 << LMQCP_PUBREC) | - (1 << LMQCP_PUBREL) | - (1 << LMQCP_PUBCOMP) | - (1 << LMQCP_CTOS_SUBSCRIBE) | - (1 << LMQCP_STOC_SUBACK) | - (1 << LMQCP_CTOS_UNSUBSCRIBE) | - (1 << LMQCP_STOC_UNSUBACK) | - (1 << LMQCP_DISCONNECT) | - (1 << LMQCP_AUTH), - [LMQPROP_MAXIMUM_PACKET_SIZE] = (1 << LMQCP_CTOS_CONNECT) | - (1 << LMQCP_STOC_CONNACK), - [LMQPROP_WILDCARD_SUBSCRIPTION_AVAIL] = (1 << LMQCP_STOC_CONNACK), - [LMQPROP_SUBSCRIPTION_IDENTIFIER_AVAIL] = (1 << LMQCP_STOC_CONNACK), - [LMQPROP_SHARED_SUBSCRIPTION_AVAIL] = (1 << LMQCP_STOC_CONNACK) -}; - - -/* - * For each command index, maps flags, id, qos and payload legality - * notice in most cases PUBLISH requires further processing - */ -static const uint8_t map_flags[] = { - [LMQCP_RESERVED] = 0x00, - [LMQCP_CTOS_CONNECT] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PAYLOAD | - LMQCP_LUT_FLAG_PACKET_ID_NONE | 0x00, - [LMQCP_STOC_CONNACK] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PACKET_ID_NONE | 0x00, - [LMQCP_PUBLISH] = LMQCP_LUT_FLAG_PAYLOAD | /* option */ - LMQCP_LUT_FLAG_PACKET_ID_QOS12 | 0x00, - [LMQCP_PUBACK] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PACKET_ID_HAS | 0x00, - [LMQCP_PUBREC] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PACKET_ID_HAS | 0x00, - [LMQCP_PUBREL] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PACKET_ID_HAS | 0x02, - [LMQCP_PUBCOMP] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PACKET_ID_HAS | 0x00, - [LMQCP_CTOS_SUBSCRIBE] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PAYLOAD | - LMQCP_LUT_FLAG_PACKET_ID_HAS | 0x02, - [LMQCP_STOC_SUBACK] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PAYLOAD | - LMQCP_LUT_FLAG_PACKET_ID_HAS | 0x00, - [LMQCP_CTOS_UNSUBSCRIBE] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PAYLOAD | - LMQCP_LUT_FLAG_PACKET_ID_HAS | 0x02, - [LMQCP_STOC_UNSUBACK] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PAYLOAD | - LMQCP_LUT_FLAG_PACKET_ID_NONE | 0x00, - [LMQCP_CTOS_PINGREQ] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PACKET_ID_NONE | 0x00, - [LMQCP_STOC_PINGRESP] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PACKET_ID_NONE | 0x00, - [LMQCP_DISCONNECT] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PACKET_ID_NONE | 0x00, - [LMQCP_AUTH] = LMQCP_LUT_FLAG_RESERVED_FLAGS | - LMQCP_LUT_FLAG_PACKET_ID_NONE | 0x00, -}; - -void -lws_mqttc_state_transition(lws_mqttc_t *c, lwsgs_mqtt_states_t s) -{ - lwsl_debug("%s: ep %p: state %d -> %d\n", __func__, c, c->estate, s); - c->estate = s; -} - -static int -lws_mqtt_pconsume(lws_mqtt_parser_t *par, int consumed) -{ - par->consumed += consumed; - - if (par->consumed > par->props_len) - return -1; - - /* more properties coming */ - - if (par->consumed < par->props_len) { - par->state = LMQCPP_PROP_ID_VBI; - return 0; - } - - /* properties finished: are we headed for payload or idle? */ - - if ((map_flags[ctl_pkt_type(par)] & LMQCP_LUT_FLAG_PAYLOAD) && - /* A PUBLISH packet MUST NOT contain a Packet Identifier if - * its QoS value is set to 0 [MQTT-2.2.1-2]. */ - (ctl_pkt_type(par) != LMQCP_PUBLISH || - (par->packet_type_flags & 6))) { - par->state = LMQCPP_PAYLOAD; - return 0; - } - - par->state = LMQCPP_IDLE; - - return 0; -} - -static int -lws_mqtt_set_client_established(struct lws *wsi) -{ - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED, - &role_ops_mqtt); - - if (user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_MQTT_CLIENT_ESTABLISHED, - wsi->user_space, NULL, 0) < 0) { - lwsl_err("%s: MQTT_ESTABLISHED failed\n", __func__); - - return -1; - } - /* - * If we made a new connection and got the ACK, our connection is - * definitely working in both directions at the moment - */ - lws_validity_confirmed(wsi); - - /* clear connection timeout */ - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - return 0; -} - -lws_mqtt_subs_t * -lws_mqtt_find_sub(struct _lws_mqtt_related *mqtt, const char *topic) -{ - lws_mqtt_subs_t *s = mqtt->subs_head; - - while (s) { - if (!strcmp((const char *)s->topic, topic)) - return s; - s = s->next; - } - - return NULL; -} - -static lws_mqtt_subs_t * -lws_mqtt_create_sub(struct _lws_mqtt_related *mqtt, const char *topic) -{ - lws_mqtt_subs_t *mysub; - - mysub = lws_malloc(sizeof(*mysub) + strlen(topic) + 1, "sub"); - if (!mysub) - return NULL; - - mysub->next = mqtt->subs_head; - mqtt->subs_head = mysub; - memcpy(mysub->topic, topic, strlen(topic) + 1); - mysub->ref_count = 1; - - lwsl_info("%s: Created mysub %p for wsi->mqtt %p\n", - __func__, mysub, mqtt); - - return mysub; -} - -static int -lws_mqtt_client_remove_subs(struct _lws_mqtt_related *mqtt) -{ - lws_mqtt_subs_t *s = mqtt->subs_head; - lws_mqtt_subs_t *temp = NULL; - - - lwsl_info("%s: Called to remove subs from wsi->mqtt %p\n", - __func__, mqtt); - - while (s && s->next) { - if (s->next->ref_count == 0) - break; - s = s->next; - } - - if (s && s->next) { - temp = s->next; - lwsl_info("%s: Removing sub %p from wsi->mqtt %p\n", - __func__, temp, mqtt); - s->next = temp->next; - lws_free(temp); - return 0; - } - return 1; -} - -int -_lws_mqtt_rx_parser(struct lws *wsi, lws_mqtt_parser_t *par, - const uint8_t *buf, size_t len) -{ - struct lws *w; - int n; - - if (par->flag_pending_send_reason_close) - return 0; - - /* - * Stateful, fragmentation-immune parser - * - * Notice that len can always be 1 if under attack, even over tls if - * the server is compromised or malicious. - */ - - while (len) { - lwsl_debug("%s: %d, len = %d\n", __func__, par->state, (int)len); - switch (par->state) { - case LMQCPP_IDLE: - par->packet_type_flags = *buf++; - len--; - -#if defined(LWS_WITH_CLIENT) - /* - * The case where we sent the connect, but we received - * something else before any CONNACK - */ - if (lwsi_state(wsi) == LRS_MQTTC_AWAIT_CONNACK && - par->packet_type_flags >> 4 != LMQCP_STOC_CONNACK) { - lwsl_notice("%s: server sent non-CONNACK\n", - __func__); - goto send_protocol_error_and_close; - } -#endif /* LWS_WITH_CLIENT */ - - n = map_flags[par->packet_type_flags >> 4]; - /* - * Where a flag bit is marked as “Reserved”, it is - * reserved for future use and MUST be set to the value - * listed [MQTT-2.1.3-1]. - */ - if ((n & LMQCP_LUT_FLAG_RESERVED_FLAGS) && - ((par->packet_type_flags & 0x0f) != (n & 0x0f))) { - lwsl_notice("%s: wsi %p: bad flags, 0x%02x mask 0x%02x (len %d)\n", - __func__, wsi, par->packet_type_flags, n, (int)len + 1); - lwsl_hexdump_err(buf - 1, len + 1); - goto send_protocol_error_and_close; - } - - lwsl_debug("%s: received pkt type 0x%x / flags 0x%x\n", - __func__, par->packet_type_flags >> 4, - par->packet_type_flags & 0xf); - - /* allows us to know if a property that can only be - * given once, appears twice */ - memset(par->props_seen, 0, sizeof(par->props_seen)); - par->state = par->packet_type_flags & 0xf0; - break; - - case LMQCPP_CONNECT_PACKET: - lwsl_debug("%s: received CONNECT pkt\n", __func__); - par->state = LMQCPP_CONNECT_REMAINING_LEN_VBI; - lws_mqtt_vbi_init(&par->vbit); - break; - - case LMQCPP_CONNECT_REMAINING_LEN_VBI: - switch (lws_mqtt_vbi_r(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - par->cpkt_remlen = par->vbit.value; - n = map_flags[ctl_pkt_type(par)]; - lws_mqtt_str_init(&par->s_temp, par->temp, - sizeof(par->temp), 0); - par->state = LMQCPP_CONNECT_VH_PNAME; - break; - default: - lwsl_notice("%s: bad vbi\n", __func__); - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_CONNECT_VH_PNAME: - switch (lws_mqtt_str_parse(&par->s_temp, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - if (par->s_temp.len != 4 || - memcmp(par->s_temp.buf, "MQTT", - par->s_temp.len)) { - lwsl_notice("%s: protocol name: %.*s\n", - __func__, par->s_temp.len, - par->s_temp.buf); - goto send_unsupp_connack_and_close; - } - par->state = LMQCPP_CONNECT_VH_PVERSION; - break; - default: - lwsl_notice("%s: bad protocol name\n", __func__); - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_CONNECT_VH_PVERSION: - par->conn_protocol_version = *buf++; - len--; - if (par->conn_protocol_version != 5) { - lwsl_info("%s: unsupported MQTT version %d\n", - __func__, par->conn_protocol_version); - goto send_unsupp_connack_and_close; - } - par->state = LMQCPP_CONNECT_VH_FLAGS; - break; - - case LMQCPP_CONNECT_VH_FLAGS: - par->cpkt_flags = *buf++; - len--; - if (par->cpkt_flags & 1) { - /* - * The Server MUST validate that the reserved - * flag in the CONNECT packet is set to 0 - * [MQTT-3.1.2-3]. - */ - par->reason = LMQCP_REASON_MALFORMED_PACKET; - goto send_reason_and_close; - } - /* - * conn_flags specifies the Will Properties that should - * appear in the payload section - */ - lws_mqtt_2byte_init(&par->vbit); - par->state = LMQCPP_CONNECT_VH_KEEPALIVE; - break; - - case LMQCPP_CONNECT_VH_KEEPALIVE: - switch (lws_mqtt_vbi_r(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - par->keepalive = (uint16_t)par->vbit.value; - lws_mqtt_vbi_init(&par->vbit); - par->state = LMQCPP_CONNECT_VH_PROPERTIES_VBI_LEN; - break; - default: - lwsl_notice("%s: ka bad vbi\n", __func__); - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_PINGRESP_ZERO: - len--; - /* second byte of PINGRESP must be zero */ - if (*buf++) - goto send_protocol_error_and_close; - goto cmd_completion; - - case LMQCPP_CONNECT_VH_PROPERTIES_VBI_LEN: - switch (lws_mqtt_vbi_r(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - /* reset consumption counter */ - par->consumed = 0; - par->props_len = par->vbit.value; - lws_mqtt_vbi_init(&par->vbit); - par->state = LMQCPP_PROP_ID_VBI; - break; - default: - lwsl_notice("%s: connpr bad vbi\n", __func__); - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_PUBLISH_PACKET: - if (lwsi_role_client(wsi) && wsi->mqtt->inside_subscribe) { - lwsl_notice("%s: Topic rx before subscribing\n", - __func__); - goto send_protocol_error_and_close; - } - lwsl_info("%s: received PUBLISH pkt\n", __func__); - par->state = LMQCPP_PUBLISH_REMAINING_LEN_VBI; - lws_mqtt_vbi_init(&par->vbit); - break; - case LMQCPP_PUBLISH_REMAINING_LEN_VBI: - switch (lws_mqtt_vbi_r(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - par->cpkt_remlen = par->vbit.value; - lwsl_debug("%s: PUBLISH pkt len = %d\n", - __func__, (int)par->cpkt_remlen); - /* Move on to PUBLISH's variable header */ - par->state = LMQCPP_PUBLISH_VH_TOPIC; - break; - default: - lwsl_notice("%s: pubrem bad vbi\n", __func__); - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_PUBLISH_VH_TOPIC: - { - lws_mqtt_publish_param_t *pub = NULL; - - if (len < 2) { - lwsl_notice("%s: topic too short\n", __func__); - return -1; - } - - /* Topic len */ - par->n = lws_ser_ru16be(buf); - buf += 2; - len -= 2; - - if (len < par->n) {/* the way this is written... */ - lwsl_notice("%s: len breakage\n", __func__); - return -1; - } - - /* Invalid topic len */ - if (par->n == 0) { - lwsl_notice("%s: zero topic len\n", __func__); - par->reason = LMQCP_REASON_MALFORMED_PACKET; - goto send_reason_and_close; - } - lwsl_debug("%s: PUBLISH topic len %d\n", - __func__, (int)par->n); - assert(!wsi->mqtt->rx_cpkt_param); - wsi->mqtt->rx_cpkt_param = lws_zalloc( - sizeof(lws_mqtt_publish_param_t), "rx pub param"); - if (!wsi->mqtt->rx_cpkt_param) - goto oom; - pub = (lws_mqtt_publish_param_t *)wsi->mqtt->rx_cpkt_param; - - pub->topic_len = par->n; - - /* Topic Name */ - pub->topic = (char *)lws_zalloc((size_t)pub->topic_len + 1, - "rx publish topic"); - if (!pub->topic) - goto oom; - lws_strncpy(pub->topic, (const char *)buf, - (size_t)pub->topic_len + 1); - buf += pub->topic_len; - len -= pub->topic_len; - - /* Extract QoS Level from Fixed Header Flags */ - pub->qos = (lws_mqtt_qos_levels_t) - ((par->packet_type_flags >> 1) & 0x3); - - pub->payload_pos = 0; - - pub->payload_len = par->cpkt_remlen - - (2 + pub->topic_len + ((pub->qos) ? 2 : 0)); - - switch (pub->qos) { - case QOS0: - par->state = LMQCPP_PAYLOAD; - if (pub->payload_len == 0) - goto cmd_completion; - - break; - case QOS1: - case QOS2: - par->state = LMQCPP_PUBLISH_VH_PKT_ID; - break; - default: - par->reason = LMQCP_REASON_MALFORMED_PACKET; - lws_free_set_NULL(pub->topic); - lws_free_set_NULL(wsi->mqtt->rx_cpkt_param); - goto send_reason_and_close; - } - break; - } - case LMQCPP_PUBLISH_VH_PKT_ID: - { - lws_mqtt_publish_param_t *pub = - (lws_mqtt_publish_param_t *)wsi->mqtt->rx_cpkt_param; - - if (len < 2) { - lwsl_notice("%s: len breakage 2\n", __func__); - return -1; - } - - par->cpkt_id = lws_ser_ru16be(buf); - buf += 2; - len -= 2; - wsi->mqtt->ack_pkt_id = par->cpkt_id; - lwsl_debug("%s: Packet ID %d\n", - __func__, (int)par->cpkt_id); - par->state = LMQCPP_PAYLOAD; - pub->payload_pos = 0; - pub->payload_len = par->cpkt_remlen - - (2 + pub->topic_len + ((pub->qos) ? 2 : 0)); - if (pub->payload_len == 0) - goto cmd_completion; - - break; - } - case LMQCPP_PAYLOAD: - { - lws_mqtt_publish_param_t *pub = - (lws_mqtt_publish_param_t *)wsi->mqtt->rx_cpkt_param; - if (pub == NULL) { - lwsl_err("%s: Uninitialized pub_param\n", - __func__); - goto send_protocol_error_and_close; - } - - pub->payload = buf; - goto cmd_completion; - } - - case LMQCPP_CONNACK_PACKET: - if (!lwsi_role_client(wsi)) { - lwsl_err("%s: CONNACK is only Server to Client", - __func__); - goto send_unsupp_connack_and_close; - } - - lwsl_debug("%s: received CONNACK pkt\n", __func__); - lws_mqtt_vbi_init(&par->vbit); - switch (lws_mqtt_vbi_r(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - par->cpkt_remlen = par->vbit.value; - lwsl_debug("%s: CONNACK pkt len = %d\n", - __func__, (int)par->cpkt_remlen); - if (par->cpkt_remlen != 2) - goto send_protocol_error_and_close; - - par->state = LMQCPP_CONNACK_VH_FLAGS; - break; - default: - lwsl_notice("%s: connack bad vbi\n", __func__); - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_CONNACK_VH_FLAGS: - { - lws_mqttc_t *c = &wsi->mqtt->client; - par->cpkt_flags = *buf++; - len--; - - if (par->cpkt_flags & ~LMQCFT_SESSION_PRESENT) { - /* - * Byte 1 is the "Connect Acknowledge - * Flags". Bits 7-1 are reserved and - * MUST be set to 0. - */ - par->reason = LMQCP_REASON_MALFORMED_PACKET; - goto send_reason_and_close; - } - /* - * If the Server accepts a connection with - * CleanSession set to 1, the Server MUST set - * Session Present to 0 in the CONNACK packet - * in addition to setting a zero return code - * in the CONNACK packet [MQTT-3.2.2-1]. If - * the Server accepts a connection with - * CleanSession set to 0, the value set in - * Session Present depends on whether the - * Server already has stored Session state for - * the supplied client ID. If the Server has - * stored Session state, it MUST set - * SessionPresent to 1 in the CONNACK packet - * [MQTT-3.2.2-2]. If the Server does not have - * stored Session state, it MUST set Session - * Present to 0 in the CONNACK packet. This is - * in addition to setting a zero return code - * in the CONNACK packet [MQTT-3.2.2-3]. - */ - if ((c->conn_flags & LMQCFT_CLEAN_START) && - (par->cpkt_flags & LMQCFT_SESSION_PRESENT)) - goto send_protocol_error_and_close; - - wsi->mqtt->session_resumed = (par->cpkt_flags & - LMQCFT_SESSION_PRESENT); - - /* Move on to Connect Return Code */ - par->state = LMQCPP_CONNACK_VH_RETURN_CODE; - break; - } - case LMQCPP_CONNACK_VH_RETURN_CODE: - par->conn_rc = *buf++; - len--; - /* - * If a server sends a CONNACK packet containing a - * non-zero return code it MUST then close the Network - * Connection [MQTT-3.2.2-5] - */ - switch (par->conn_rc) { - case 0: - goto cmd_completion; - case 1: - case 2: - case 3: - case 4: - case 5: - par->reason = LMQCP_REASON_UNSUPPORTED_PROTOCOL + - par->conn_rc - 1; - goto send_reason_and_close; - default: - lwsl_notice("%s: bad connack retcode\n", __func__); - goto send_protocol_error_and_close; - } - break; - - /* SUBACK */ - case LMQCPP_SUBACK_PACKET: - if (!lwsi_role_client(wsi)) { - lwsl_err("%s: SUBACK is only Server to Client", - __func__); - goto send_unsupp_connack_and_close; - } - - lwsl_debug("%s: received SUBACK pkt\n", __func__); - lws_mqtt_vbi_init(&par->vbit); - switch (lws_mqtt_vbi_r(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - par->cpkt_remlen = par->vbit.value; - lwsl_debug("%s: SUBACK pkt len = %d\n", - __func__, (int)par->cpkt_remlen); - if (par->cpkt_remlen <= 2) - goto send_protocol_error_and_close; - par->state = LMQCPP_SUBACK_VH_PKT_ID; - break; - default: - lwsl_notice("%s: suback bad vbi\n", __func__); - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_SUBACK_VH_PKT_ID: - - if (len < 2) { - lwsl_notice("%s: len breakage 4\n", __func__); - return -1; - } - - par->cpkt_id = lws_ser_ru16be(buf); - wsi->mqtt->ack_pkt_id = par->cpkt_id; - buf += 2; - len -= 2; - par->cpkt_remlen -= 2; - par->n = 0; - par->state = LMQCPP_SUBACK_PAYLOAD; - *par->temp = 0; - break; - - case LMQCPP_SUBACK_PAYLOAD: - { - lws_mqtt_qos_levels_t qos = (lws_mqtt_qos_levels_t)*buf++; - - len--; - switch (qos) { - case QOS0: - case QOS1: - case QOS2: - break; - case FAILURE_QOS_LEVEL: - goto send_protocol_error_and_close; - - default: - par->reason = LMQCP_REASON_MALFORMED_PACKET; - goto send_reason_and_close; - } - - if (++(par->n) == par->cpkt_remlen) { - par->n = 0; - goto cmd_completion; - } - - break; - } - - /* UNSUBACK */ - case LMQCPP_UNSUBACK_PACKET: - if (!lwsi_role_client(wsi)) { - lwsl_err("%s: UNSUBACK is only Server to Client", - __func__); - goto send_unsupp_connack_and_close; - } - - lwsl_debug("%s: received UNSUBACK pkt\n", __func__); - lws_mqtt_vbi_init(&par->vbit); - switch (lws_mqtt_vbi_r(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - par->cpkt_remlen = par->vbit.value; - lwsl_debug("%s: UNSUBACK pkt len = %d\n", - __func__, (int)par->cpkt_remlen); - if (par->cpkt_remlen < 2) - goto send_protocol_error_and_close; - par->state = LMQCPP_UNSUBACK_VH_PKT_ID; - break; - default: - lwsl_notice("%s: unsuback bad vbi\n", __func__); - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_UNSUBACK_VH_PKT_ID: - - if (len < 2) { - lwsl_notice("%s: len breakage 3\n", __func__); - return -1; - } - - par->cpkt_id = lws_ser_ru16be(buf); - wsi->mqtt->ack_pkt_id = par->cpkt_id; - buf += 2; - len -= 2; - par->cpkt_remlen -= 2; - par->n = 0; - - goto cmd_completion; - - case LMQCPP_PUBACK_PACKET: - lws_mqtt_vbi_init(&par->vbit); - switch (lws_mqtt_vbi_r(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - par->cpkt_remlen = par->vbit.value; - lwsl_info("%s: PUBACK pkt len = %d\n", __func__, - (int)par->cpkt_remlen); - /* - * must be 4 or more, with special case that 2 - * means success with no reason code or props - */ - if (par->cpkt_remlen <= 1 || - par->cpkt_remlen == 3) - goto send_protocol_error_and_close; - - par->state = LMQCPP_PUBACK_VH_PKT_ID; - par->fixed_seen[2] = par->fixed_seen[3] = 0; - par->fixed = 0; - par->n = 0; - break; - default: - lwsl_notice("%s: puback bad vbi\n", __func__); - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_PUBACK_VH_PKT_ID: - /* - * There are 3 fixed bytes and then a VBI for the - * property section length - */ - par->fixed_seen[par->fixed++] = *buf++; - if (len < par->cpkt_remlen - par->n) { - lwsl_notice("%s: len breakage 4\n", __func__); - return -1; - } - len--; - par->n++; - if (par->fixed == 2) - par->cpkt_id = lws_ser_ru16be(par->fixed_seen); - - if (par->fixed == 3) { - lws_mqtt_vbi_init(&par->vbit); - par->props_consumed = 0; - par->state = LMQCPP_PUBACK_PROPERTIES_LEN_VBI; - } - /* length of 2 is truncated packet and we completed it */ - if (par->cpkt_remlen == par->fixed) - goto cmd_completion; - break; - - case LMQCPP_PUBACK_PROPERTIES_LEN_VBI: - switch (lws_mqtt_vbi_r(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - par->props_len = par->vbit.value; - lwsl_info("%s: PUBACK props len = %d\n", - __func__, (int)par->cpkt_remlen); - /* - * If there are no properties, this is a - * command completion event in itself - */ - if (!par->props_len) - goto cmd_completion; - - /* - * Otherwise consume the properties before - * completing the command - */ - lws_mqtt_vbi_init(&par->vbit); - par->state = LMQCPP_PUBACK_VH_PKT_ID; - break; - default: - lwsl_notice("%s: puback pr bad vbi\n", __func__); - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_EAT_PROPERTIES_AND_COMPLETE: - /* - * TODO: stash the props - */ - par->props_consumed++; - len--; - buf++; - if (par->props_len != par->props_consumed) - break; - -cmd_completion: - /* - * We come here when we understood we just processed - * the last byte of a command packet, regardless of the - * packet type - */ - par->state = LMQCPP_IDLE; - - switch (par->packet_type_flags >> 4) { - case LMQCP_STOC_CONNACK: - lwsl_info("%s: cmd_completion: CONNACK\n", - __func__); - - /* - * Getting the CONNACK means we are the first, - * the nwsi, and we succeeded to create a new - * network connection ourselves. - * - * Since others may join us sharing the nwsi, - * and we may close while they still want to use - * it, our wsi lifecycle alone can no longer - * define the lifecycle of the nwsi... it means - * we need to do a "magic trick" and instead of - * being both the nwsi and act like a child - * stream, create a new wsi to take over the - * nwsi duties and turn our wsi into a child of - * the nwsi with its own lifecycle. - * - * The nwsi gets a mostly empty wsi->nwsi used - * to track already-subscribed topics globally - * for the connection. - */ - - /* we were under SENT_CLIENT_HANDSHAKE timeout */ - lws_set_timeout(wsi, 0, 0); - - w = lws_create_new_server_wsi(wsi->vhost, - wsi->tsi); - if (!w) { - lwsl_notice("%s: sid 1 migrate failed\n", - __func__); - return -1; - } - - wsi->mux.highest_sid = 1; - lws_wsi_mux_insert(w, wsi, wsi->mux.highest_sid++); - - wsi->mux_substream = 1; - w->mux_substream = 1; - w->client_mux_substream = 1; - wsi->client_mux_migrated = 1; - wsi->told_user_closed = 1; /* don't tell nwsi closed */ - - lwsi_set_state(w, LRS_ESTABLISHED); - lwsi_set_state(wsi, LRS_ESTABLISHED); - lwsi_set_role(w, lwsi_role(wsi)); - -#if defined(LWS_WITH_CLIENT) - w->flags = wsi->flags; -#endif - - w->mqtt = wsi->mqtt; - wsi->mqtt = lws_zalloc(sizeof(*wsi->mqtt), "nwsi mqtt"); - if (!wsi->mqtt) - return -1; - w->mqtt->wsi = w; - w->protocol = wsi->protocol; - if (w->user_space && - !w->user_space_externally_allocated) - lws_free_set_NULL(w->user_space); - w->user_space = wsi->user_space; - wsi->user_space = NULL; - w->user_space_externally_allocated = - wsi->user_space_externally_allocated; - if (lws_ensure_user_space(w)) - goto bail1; - w->opaque_user_data = wsi->opaque_user_data; - wsi->opaque_user_data = NULL; - w->stash = wsi->stash; - wsi->stash = NULL; - - lws_mux_mark_immortal(w); - - lwsl_notice("%s: migrated nwsi %p to sid 1 %p\n", - __func__, wsi, w); - - #if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.h2_subs++; - #endif - - /* - * It was the last thing we were waiting for - * before we can be fully ESTABLISHED - */ - if (lws_mqtt_set_client_established(w)) { - lwsl_notice("%s: set EST fail\n", __func__); - return -1; - } - - /* get the ball rolling */ - lws_validity_confirmed(wsi); - - /* well, add the queued guys as children */ - lws_wsi_mux_apply_queue(wsi); - break; - -bail1: - /* undo the insert */ - wsi->mux.child_list = w->mux.sibling_list; - wsi->mux.child_count--; - - w->context->count_wsi_allocated--; - - if (w->user_space) - lws_free_set_NULL(w->user_space); - w->vhost->protocols[0].callback(w, - LWS_CALLBACK_WSI_DESTROY, - NULL, NULL, 0); - lws_vhost_unbind_wsi(w); - lws_free(w); - - return 0; - - case LMQCP_PUBACK: - lwsl_info("%s: cmd_completion: PUBACK\n", - __func__); - - /* - * Figure out which child asked for this - */ - - n = 0; - lws_start_foreach_ll(struct lws *, w, - wsi->mux.child_list) { - if (w->mqtt->unacked_publish && - w->mqtt->ack_pkt_id == par->cpkt_id) { - char requested_close = 0; - - w->mqtt->unacked_publish = 0; - if (user_callback_handle_rxflow( - w->protocol->callback, - w, LWS_CALLBACK_MQTT_ACK, - w->user_space, NULL, 0) < 0) { - lwsl_info("%s: MQTT_ACK requests close\n", - __func__); - requested_close = 1; - } - n = 1; - - /* - * We got an assertive PUBACK, - * no need for ACK timeout wait - * any more - */ - lws_sul_schedule(lws_get_context(w), 0, - &w->mqtt->sul_qos1_puback_wait, NULL, - LWS_SET_TIMER_USEC_CANCEL); - - if (requested_close) { - __lws_close_free_wsi(w, - 0, "ack cb"); - break; - } - - break; - } - } lws_end_foreach_ll(w, mux.sibling_list); - - if (!n) { - lwsl_err("%s: unsolicited PUBACK\n", - __func__); - return -1; - } - - /* - * If we published something and it was acked, - * our connection is definitely working in both - * directions at the moment. - */ - lws_validity_confirmed(wsi); - break; - - case LMQCP_STOC_PINGRESP: - lwsl_info("%s: cmd_completion: PINGRESP\n", - __func__); - /* - * If we asked for a PINGRESP and it came, - * our connection is definitely working in both - * directions at the moment. - */ - lws_validity_confirmed(wsi); - break; - - case LMQCP_STOC_SUBACK: - lwsl_info("%s: cmd_completion: SUBACK\n", - __func__); - - /* - * Figure out which child asked for this - */ - - n = 0; - lws_start_foreach_ll(struct lws *, w, - wsi->mux.child_list) { - if (w->mqtt->inside_subscribe && - w->mqtt->ack_pkt_id == par->cpkt_id) { - w->mqtt->inside_subscribe = 0; - if (user_callback_handle_rxflow( - w->protocol->callback, - w, LWS_CALLBACK_MQTT_SUBSCRIBED, - w->user_space, NULL, 0) < 0) { - lwsl_err("%s: MQTT_SUBSCRIBE failed\n", - __func__); - return -1; - } - n = 1; - break; - } - } lws_end_foreach_ll(w, mux.sibling_list); - - if (!n) { - lwsl_err("%s: unsolicited SUBACK\n", - __func__); - return -1; - } - - /* - * If we subscribed to something and SUBACK came, - * our connection is definitely working in both - * directions at the moment. - */ - lws_validity_confirmed(wsi); - - break; - - case LMQCP_STOC_UNSUBACK: - { - char requested_close = 0; - lwsl_info("%s: cmd_completion: UNSUBACK\n", - __func__); - /* - * Figure out which child asked for this - */ - n = 0; - lws_start_foreach_ll(struct lws *, w, - wsi->mux.child_list) { - if (w->mqtt->inside_unsubscribe && - w->mqtt->ack_pkt_id == par->cpkt_id) { - struct lws *nwsi = lws_get_network_wsi(w); - - /* - * No more subscribers left, - * remove the topic from nwsi - */ - lws_mqtt_client_remove_subs(nwsi->mqtt); - - w->mqtt->inside_unsubscribe = 0; - if (user_callback_handle_rxflow( - w->protocol->callback, - w, LWS_CALLBACK_MQTT_UNSUBSCRIBED, - w->user_space, NULL, 0) < 0) { - lwsl_info("%s: MQTT_UNSUBACK requests close\n", - __func__); - requested_close = 1; - } - n = 1; - - if (requested_close) { - __lws_close_free_wsi(w, - 0, "unsub ack cb"); - break; - } - break; - } - } lws_end_foreach_ll(w, mux.sibling_list); - - if (!n) { - lwsl_err("%s: unsolicited UNSUBACK\n", - __func__); - return -1; - } - - - /* - * If we unsubscribed to something and - * UNSUBACK came, our connection is - * definitely working in both - * directions at the moment. - */ - lws_validity_confirmed(wsi); - - break; - } - case LMQCP_PUBLISH: - { - lws_mqtt_publish_param_t *pub = - (lws_mqtt_publish_param_t *) - wsi->mqtt->rx_cpkt_param; - size_t chunk; - - if (pub == NULL) { - lwsl_notice("%s: no pub\n", __func__); - return -1; - } - - /* - * RX PUBLISH is delivered to any children that - * registered for the related topic - */ - - n = wsi->role_ops->rx_cb[lwsi_role_server(wsi)]; - - chunk = pub->payload_len - pub->payload_pos; - if (chunk > len) - chunk = len; - - lws_start_foreach_ll(struct lws *, w, - wsi->mux.child_list) { - if (lws_mqtt_find_sub(w->mqtt, - pub->topic)) - if (w->protocol->callback( - w, n, - w->user_space, - (void *)pub, - chunk)) - return 1; - } lws_end_foreach_ll(w, mux.sibling_list); - - - pub->payload_pos += (uint32_t)chunk; - len -= chunk; - buf += chunk; - - lwsl_debug("%s: post pos %d, plen %d, len %d\n", - __func__, (int)pub->payload_pos, - (int)pub->payload_len, (int)len); - - if (pub->payload_pos != pub->payload_len) { - /* - * More chunks of the payload pending, - * blocking this connection from doing - * anything else - */ - par->state = LMQCPP_PAYLOAD; - break; - } - - /* For QOS>0, send out PUBACK */ - if (pub->qos) { - wsi->mqtt->send_puback = 1; - lws_callback_on_writable(wsi); - } - - par->payload_consumed = 0; - lws_free_set_NULL(pub->topic); - lws_free_set_NULL(wsi->mqtt->rx_cpkt_param); - - break; - } - default: - break; - } - - break; - - - case LMQCPP_PROP_ID_VBI: - switch (lws_mqtt_vbi_r(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - par->consumed += par->vbit.consumed; - if (par->vbit.value > - LWS_ARRAY_SIZE(property_valid)) { - lwsl_notice("%s: undef prop id 0x%x\n", - __func__, (int)par->vbit.value); - goto send_protocol_error_and_close; - } - if (!(property_valid[par->vbit.value] & - (1 << ctl_pkt_type(par)))) { - lwsl_notice("%s: prop id 0x%x invalid for" - " control pkt %d\n", __func__, - (int)par->vbit.value, - ctl_pkt_type(par)); - goto send_protocol_error_and_close; - } - par->prop_id = par->vbit.value; - par->flag_prop_multi = - par->props_seen[par->prop_id >> 3] & - (1 << (par->prop_id & 7)); - par->props_seen[par->prop_id >> 3] |= - (1 << (par->prop_id & 7)); - /* - * even if it's not a vbi property arg, - * .consumed of this will be zero the first time - */ - lws_mqtt_vbi_init(&par->vbit); - /* - * if it's a string, next state must set the - * destination and size limit itself. But - * resetting it generically here lets it use - * lws_mqtt_str_first() to understand it's the - * first time around. - */ - lws_mqtt_str_init(&par->s_temp, NULL, 0, 0); - - /* property arg state enums are so encoded */ - par->state = 0x100 | par->vbit.value; - break; - default: - lwsl_notice("%s: prop id bad vbi\n", __func__); - goto send_protocol_error_and_close; - } - break; - - /* - * All possible property payloads... restricting which ones - * can appear in which control packets is already done above - * in LMQCPP_PROP_ID_VBI - */ - - case LMQCPP_PROP_REQUEST_PROBLEM_INFO_1BYTE: - case LMQCPP_PROP_REQUEST_REPSONSE_INFO_1BYTE: - case LMQCPP_PROP_MAXIMUM_QOS_1BYTE: - case LMQCPP_PROP_RETAIN_AVAILABLE_1BYTE: - case LMQCPP_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE_1BYTE: - case LMQCPP_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE_1BYTE: - case LMQCPP_PROP_SHARED_SUBSCRIPTION_AVAILABLE_1BYTE: - case LMQCPP_PROP_PAYLOAD_FORMAT_INDICATOR_1BYTE: /* 3.3.2.3.2 */ - if (par->flag_prop_multi) - goto singular_prop_seen_twice; - par->payload_format = *buf++; - len--; - if (lws_mqtt_pconsume(par, 1)) - goto send_protocol_error_and_close; - break; - - case LMQCPP_PROP_MAXIMUM_PACKET_SIZE_4BYTE: - case LMQCPP_PROP_WILL_DELAY_INTERVAL_4BYTE: - case LMQCPP_PROP_SESSION_EXPIRY_INTERVAL_4BYTE: - case LMQCPP_PROP_MSG_EXPIRY_INTERVAL_4BYTE: - if (par->flag_prop_multi) - goto singular_prop_seen_twice; - - if (lws_mqtt_mb_first(&par->vbit)) - lws_mqtt_4byte_init(&par->vbit); - - switch (lws_mqtt_mb_parse(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - if (lws_mqtt_pconsume(par, par->vbit.consumed)) - goto send_protocol_error_and_close; - break; - default: - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_PROP_SERVER_KEEPALIVE_2BYTE: - case LMQCPP_PROP_RECEIVE_MAXIMUM_2BYTE: - case LMQCPP_PROP_TOPIC_MAXIMUM_2BYTE: - case LMQCPP_PROP_TOPIC_ALIAS_2BYTE: - if (par->flag_prop_multi) - goto singular_prop_seen_twice; - - if (lws_mqtt_mb_first(&par->vbit)) - lws_mqtt_2byte_init(&par->vbit); - - switch (lws_mqtt_mb_parse(&par->vbit, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - if (lws_mqtt_pconsume(par, par->vbit.consumed)) - goto send_protocol_error_and_close; - break; - default: - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_PROP_ASSIGNED_CLIENTID_UTF8S: - case LMQCPP_PROP_AUTH_METHOD_UTF8S: - case LMQCPP_PROP_USER_PROPERTY_NAME_UTF8S: - case LMQCPP_PROP_USER_PROPERTY_VALUE_UTF8S: - case LMQCPP_PROP_RESPONSE_INFO_UTF8S: - case LMQCPP_PROP_SERVER_REFERENCE_UTF8S: - case LMQCPP_PROP_REASON_STRING_UTF8S: - case LMQCPP_PROP_RESPONSE_TOPIC_UTF8S: - case LMQCPP_PROP_CONTENT_TYPE_UTF8S: - if (par->flag_prop_multi) - goto singular_prop_seen_twice; - - if (lws_mqtt_str_first(&par->s_temp)) - lws_mqtt_str_init(&par->s_temp, par->temp, - sizeof(par->temp), 0); - - switch (lws_mqtt_str_parse(&par->s_temp, &buf, &len)) { - case LMSPR_NEED_MORE: - break; - case LMSPR_COMPLETED: - if (lws_mqtt_pconsume(par, par->s_temp.len)) - goto send_protocol_error_and_close; - break; - - default: - lwsl_info("%s: bad protocol name\n", __func__); - goto send_protocol_error_and_close; - } - break; - - case LMQCPP_PROP_SUBSCRIPTION_ID_VBI: - - case LMQCPP_PROP_CORRELATION_BINDATA: - case LMQCPP_PROP_AUTH_DATA_BINDATA: - - /* TODO */ - lwsl_err("%s: Unimplemented packet state 0x%x\n", - __func__, par->state); - return -1; - } - } - - return 0; - -oom: - lwsl_err("%s: OOM!\n", __func__); - goto send_protocol_error_and_close; - -singular_prop_seen_twice: - lwsl_info("%s: property appears twice\n", __func__); - -send_protocol_error_and_close: - lwsl_notice("%s: peac\n", __func__); - par->reason = LMQCP_REASON_PROTOCOL_ERROR; - -send_reason_and_close: - lwsl_notice("%s: srac\n", __func__); - par->flag_pending_send_reason_close = 1; - goto ask; - -send_unsupp_connack_and_close: - lwsl_notice("%s: unsupac\n", __func__); - par->reason = LMQCP_REASON_UNSUPPORTED_PROTOCOL; - par->flag_pending_send_connack_close = 1; - -ask: - /* Should we ask for clients? */ - lws_callback_on_writable(wsi); - - return -1; -} - -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) -{ - lws_mqtt_fixed_hdr_t hdr; - - hdr.bits = 0; - hdr.flags.ctrl_pkt_type = (uint8_t) ctrl_pkt_type; - - switch(ctrl_pkt_type) { - case LMQCP_PUBLISH: - hdr.flags.dup = !!dup; - /* - * A PUBLISH Packet MUST NOT have both QoS bits set to - * 1. If a Server or Client receives a PUBLISH Packet - * which has both QoS bits set to 1 it MUST close the - * Network Connection [MQTT-3.3.1-4]. - */ - if (qos >= RESERVED_QOS_LEVEL) { - lwsl_err("%s: Unsupport QoS level 0x%x\n", - __func__, qos); - return -1; - } - hdr.flags.qos = (uint8_t)qos; - hdr.flags.retain = !!retain; - break; - - case LMQCP_CTOS_CONNECT: - case LMQCP_STOC_CONNACK: - case LMQCP_PUBACK: - case LMQCP_PUBREC: - case LMQCP_PUBCOMP: - case LMQCP_STOC_SUBACK: - case LMQCP_STOC_UNSUBACK: - case LMQCP_CTOS_PINGREQ: - case LMQCP_STOC_PINGRESP: - case LMQCP_DISCONNECT: - case LMQCP_AUTH: - hdr.bits &= 0xf0; - break; - - /* - * Bits 3,2,1 and 0 of the fixed header of the PUBREL, - * SUBSCRIBE, UNSUBSCRIBE Control Packets are reserved and - * MUST be set to 0,0,1 and 0 respectively. The Server MUST - * treat any other value as malformed and close the Network - * Connection [MQTT-3.6.1-1], [MQTT-3.8.1-1], [MQTT-3.10.1-1]. - */ - case LMQCP_PUBREL: - case LMQCP_CTOS_SUBSCRIBE: - case LMQCP_CTOS_UNSUBSCRIBE: - hdr.bits |= 0x02; - break; - - default: - return -1; - } - - *p = hdr.bits; - - return 0; -} - -/* - * This fires if the wsi did a PUBLISH under QoS1, but no PUBACK came before - * the timeout period - */ - -static void -lws_mqtt_publish_resend(struct lws_sorted_usec_list *sul) -{ - struct _lws_mqtt_related *mqtt = lws_container_of(sul, - struct _lws_mqtt_related, sul_qos1_puback_wait); - - lwsl_notice("%s: wsi %p\n", __func__, mqtt->wsi); - - if (mqtt->wsi->protocol->callback(mqtt->wsi, LWS_CALLBACK_MQTT_RESEND, - mqtt->wsi->user_space, NULL, 0)) - lws_set_timeout(mqtt->wsi, 1, LWS_TO_KILL_ASYNC); -} - -int -lws_mqtt_client_send_publish(struct lws *wsi, lws_mqtt_publish_param_t *pub, - const void *buf, uint32_t len, int is_complete) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - uint8_t *b = (uint8_t *)pt->serv_buf, *start, *p; - struct lws *nwsi = lws_get_network_wsi(wsi); - lws_mqtt_str_t mqtt_vh_payload; - uint32_t vh_len, rem_len; - - assert(pub->topic); - - lwsl_debug("%s: len = %d, is_complete = %d\n", - __func__, (int)len, (int)is_complete); - - if (lwsi_state(wsi) != LRS_ESTABLISHED) { - lwsl_err("%s: wsi %p: unknown state 0x%x\n", __func__, wsi, - lwsi_state(wsi)); - assert(0); - return 1; - } - - if (wsi->mqtt->inside_payload) { - /* - * Headers are filled, we are sending - * the payload - a buffer with LWS_PRE - * in front it. - */ - start = (uint8_t *)buf; - p = start + len; - if (is_complete) - wsi->mqtt->inside_payload = 0; - goto do_write; - } - - start = b + LWS_PRE; - p = start; - /* - * Fill headers and the first chunk of the - * payload (if any) - */ - if (lws_mqtt_fill_fixed_header(p++, LMQCP_PUBLISH, - 0, pub->qos, 0)) { - lwsl_err("%s: Failed to fill fixed header\n", __func__); - return 1; - } - - /* - * Topic len field + Topic len + Packet ID - * (for QOS>0) + Payload len - */ - vh_len = 2 + pub->topic_len + ((pub->qos) ? 2 : 0); - rem_len = vh_len + pub->payload_len; - lwsl_debug("%s: Remaining len = %d\n", __func__, (int) rem_len); - - /* Will the chunk of payload fit? */ - if ((vh_len + len) >= - (wsi->context->pt_serv_buf_size - LWS_PRE)) { - lwsl_err("%s: Payload is too big\n", __func__); - return 1; - } - - p += lws_mqtt_vbi_encode(rem_len, p); - - /* Topic's Len */ - lws_ser_wu16be(p, pub->topic_len); - p += 2; - - /* - * Init lws_mqtt_str for "MQTT Variable - * Headers + payload" (only the supplied - * chuncked payload) - */ - lws_mqtt_str_init(&mqtt_vh_payload, (uint8_t *)p, - (pub->topic_len + ((pub->qos) ? 2 : 0) + len), - 0); - - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - lws_strncpy((char *)p, pub->topic, (size_t)pub->topic_len+1); - if (lws_mqtt_str_advance(&mqtt_vh_payload, pub->topic_len)) { - lwsl_err("%s: a\n", __func__); - return 1; - } - - /* Packet ID */ - if (pub->qos != QOS0) { - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - wsi->mqtt->ack_pkt_id = pub->packet_id = ++nwsi->mqtt->pkt_id; - lwsl_debug("%s: pkt_id = %d\n", __func__, - (int)wsi->mqtt->ack_pkt_id); - lws_ser_wu16be(p, pub->packet_id); - if (lws_mqtt_str_advance(&mqtt_vh_payload, 2)) { - lwsl_err("%s: b\n", __func__); - return 1; - } - } - /* - * A non-empty Payload is expected and a chunk - * is present - */ - if (pub->payload_len && len) { - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - memcpy(p, buf, len); - if (lws_mqtt_str_advance(&mqtt_vh_payload, len)) - return 1; - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - } - - if (!is_complete) - nwsi->mqtt->inside_payload = wsi->mqtt->inside_payload = 1; - -do_write: - - // lwsl_hexdump_err(start, lws_ptr_diff(p, start)); - - if (lws_write(nwsi, start, lws_ptr_diff(p, start), LWS_WRITE_BINARY) != - lws_ptr_diff(p, start)) { - lwsl_err("%s: write failed\n", __func__); - return 1; - } - - if (!is_complete) { - /* still some more chunks to come... */ - lws_callback_on_writable(wsi); - - return 0; - } - - wsi->mqtt->inside_payload = nwsi->mqtt->inside_payload = 0; - - if (pub->qos != QOS0) - wsi->mqtt->unacked_publish = 1; - - /* this was the last part of the publish message */ - - if (pub->qos == QOS0) { - /* - * There won't be any real PUBACK, act like we got one - * so the user callback logic is the same for QoS0 or - * QoS1 - */ - if (wsi->protocol->callback(wsi, LWS_CALLBACK_MQTT_ACK, - wsi->user_space, NULL, 0)) { - lwsl_err("%s: ACK callback exited\n", __func__); - return 1; - } - - return 0; - } - - /* For QoS1, if no PUBACK coming after 3s, we must RETRY the publish */ - - wsi->mqtt->sul_qos1_puback_wait.cb = lws_mqtt_publish_resend; - __lws_sul_insert(&pt->pt_sul_owner, &wsi->mqtt->sul_qos1_puback_wait, - 3 * LWS_USEC_PER_SEC); - - return 0; -} - -int -lws_mqtt_client_send_subcribe(struct lws *wsi, lws_mqtt_subscribe_param_t *sub) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - uint8_t *b = (uint8_t *)pt->serv_buf + LWS_PRE, *start = b, *p = start; - struct lws *nwsi = lws_get_network_wsi(wsi); - lws_mqtt_str_t mqtt_vh_payload; - uint8_t exists[8], extant; - lws_mqtt_subs_t *mysub; - uint32_t rem_len; -#if defined(_DEBUG) - uint32_t tops; -#endif - uint32_t n; - - assert(sub->num_topics); - assert(sub->num_topics < sizeof(exists)); - - switch (lwsi_state(wsi)) { - case LRS_ESTABLISHED: /* Protocol connection established */ - if (lws_mqtt_fill_fixed_header(p++, LMQCP_CTOS_SUBSCRIBE, - 0, 0, 0)) { - lwsl_err("%s: Failed to fill fixed header\n", __func__); - return 1; - } - - /* - * The stream wants to subscribe to one or more topic, but - * the shared nwsi may already be subscribed to some or all of - * them from interactions with other streams. For those cases, - * we filter them from the list the child wants until we just - * have ones that are new to the nwsi. If nothing left, we just - * synthesize the callback to the child as if SUBACK had come - * and we're done, otherwise just ask the server for topics that - * are new to the wsi. - */ - - extant = 0; - memset(&exists, 0, sizeof(exists)); - for (n = 0; n < sub->num_topics; n++) { - lwsl_info("%s: Subscribing to topic[%d] = \"%s\"\n", - __func__, (int)n, sub->topic[n].name); - - mysub = lws_mqtt_find_sub(nwsi->mqtt, sub->topic[n].name); - if (mysub && mysub->ref_count) { - mysub->ref_count++; /* another stream using it */ - exists[n] = 1; - extant++; - } - - /* - * Attach the topic we're subscribing to, to wsi->mqtt - */ - if (!lws_mqtt_create_sub(wsi->mqtt, sub->topic[n].name)) { - lwsl_err("%s: create sub fail\n", __func__); - return 1; - } - } - - if (extant == sub->num_topics) { - /* - * It turns out there's nothing to do here, the nwsi has - * already subscribed to all the topics this stream - * wanted. Just tell it it can have them. - */ - lwsl_notice("%s: all topics already subscribed\n", __func__); - if (user_callback_handle_rxflow( - wsi->protocol->callback, - wsi, LWS_CALLBACK_MQTT_SUBSCRIBED, - wsi->user_space, NULL, 0) < 0) { - lwsl_err("%s: MQTT_SUBSCRIBE failed\n", - __func__); - return -1; - } - - return 0; - } - -#if defined(_DEBUG) - /* - * zero or more of the topics already existed, but not all, - * so we must go to the server with a filtered list of the - * new ones only - */ - - tops = sub->num_topics - extant; -#endif - - /* - * Pid + (Topic len field + Topic len + Req. QoS) x Num of Topics - */ - rem_len = 2; - for (n = 0; n < sub->num_topics; n++) - if (!exists[n]) - rem_len += (2 + (uint32_t)strlen(sub->topic[n].name) + (uint32_t)1); - - wsi->mqtt->sub_size = rem_len; - -#if defined(_DEBUG) - lwsl_debug("%s: Number of topics = %d, Remaining len = %d\n", - __func__, (int)tops, (int)rem_len); -#endif - - p += lws_mqtt_vbi_encode(rem_len, p); - - if ((rem_len + lws_ptr_diff(p, start)) >= - wsi->context->pt_serv_buf_size) { - lwsl_err("%s: Payload is too big\n", __func__); - return 1; - } - - /* Init lws_mqtt_str */ - lws_mqtt_str_init(&mqtt_vh_payload, (uint8_t *)p, rem_len, 0); - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - - /* Packet ID */ - wsi->mqtt->ack_pkt_id = ++nwsi->mqtt->pkt_id; - lwsl_debug("%s: pkt_id = %d\n", __func__, - (int)wsi->mqtt->ack_pkt_id); - lws_ser_wu16be(p, wsi->mqtt->ack_pkt_id); - - if (lws_mqtt_str_advance(&mqtt_vh_payload, 2)) - return 1; - - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - - for (n = 0; n < sub->num_topics; n++) { - lwsl_info("%s: topics[%d] = %s\n", __func__, - (int)n, sub->topic[n].name); - - /* if the nwsi already has it, don't ask server for it */ - if (exists[n]) { - lwsl_info("%s: topics[%d] \"%s\" exists in nwsi\n", - __func__, (int)n, sub->topic[n].name); - continue; - } - - /* - * Attach the topic we're subscribing to, to nwsi->mqtt - * so we know the nwsi itself has a subscription to it - */ - - if (!lws_mqtt_create_sub(nwsi->mqtt, sub->topic[n].name)) - return 1; - - /* Topic's Len */ - lws_ser_wu16be(p, (uint16_t)strlen(sub->topic[n].name)); - if (lws_mqtt_str_advance(&mqtt_vh_payload, 2)) - return 1; - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - - /* Topic Name */ - lws_strncpy((char *)p, sub->topic[n].name, - strlen(sub->topic[n].name) + 1); - if (lws_mqtt_str_advance(&mqtt_vh_payload, - (int)strlen(sub->topic[n].name))) - return 1; - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - - /* QoS */ - *p = sub->topic[n].qos; - if (lws_mqtt_str_advance(&mqtt_vh_payload, 1)) - return 1; - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - } - break; - - default: - return 1; - } - - if (lws_write(nwsi, start, lws_ptr_diff(p, start), LWS_WRITE_BINARY) != - lws_ptr_diff(p, start)) - return 1; - - wsi->mqtt->inside_subscribe = 1; - - return 0; -} - -int -lws_mqtt_client_send_unsubcribe(struct lws *wsi, - const lws_mqtt_subscribe_param_t *unsub) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - uint8_t *b = (uint8_t *)pt->serv_buf + LWS_PRE, *start = b, *p = start; - struct lws *nwsi = lws_get_network_wsi(wsi); - lws_mqtt_str_t mqtt_vh_payload; - uint8_t send_unsub[8], orphaned; - uint32_t rem_len, n; - lws_mqtt_subs_t *mysub; -#if defined(_DEBUG) - uint32_t tops; -#endif - - lwsl_info("%s: Enter\n", __func__); - - switch (lwsi_state(wsi)) { - case LRS_ESTABLISHED: /* Protocol connection established */ - orphaned = 0; - memset(&send_unsub, 0, sizeof(send_unsub)); - for (n = 0; n < unsub->num_topics; n++) { - mysub = lws_mqtt_find_sub(nwsi->mqtt, - unsub->topic[n].name); - assert(mysub); - - if (--mysub->ref_count == 0) { - lwsl_notice("%s: Need to send UNSUB\n", __func__); - send_unsub[n] = 1; - orphaned++; - } - } - - if (!orphaned) { - /* - * The nwsi still has other subscribers bound to the - * topics. - * - * So, don't send UNSUB to server, and just fake the - * UNSUB ACK event for the guy going away. - */ - lwsl_notice("%s: unsubscribed!\n", __func__); - if (user_callback_handle_rxflow( - wsi->protocol->callback, - wsi, LWS_CALLBACK_MQTT_UNSUBSCRIBED, - wsi->user_space, NULL, 0) < 0) { - /* - * We can't directly close here, because the - * caller still has the wsi. Inform the - * caller that we want to close - */ - - return 1; - } - - return 0; - } -#if defined(_DEBUG) - /* - * one or more of the topics needs to be unsubscribed - * from, so we must go to the server with a filtered - * list of the new ones only - */ - - tops = orphaned; -#endif - - if (lws_mqtt_fill_fixed_header(p++, LMQCP_CTOS_UNSUBSCRIBE, - 0, 0, 0)) { - lwsl_err("%s: Failed to fill fixed header\n", __func__); - return 1; - } - - /* - * Pid + (Topic len field + Topic len) x Num of Topics - */ - rem_len = 2; - for (n = 0; n < unsub->num_topics; n++) - if (send_unsub[n]) - rem_len += (2 + (uint32_t)strlen(unsub->topic[n].name)); - - wsi->mqtt->sub_size = rem_len; - - lwsl_debug("%s: Number of topics = %d, Remaining len = %d\n", - __func__, (int)tops, (int)rem_len); - - p += lws_mqtt_vbi_encode(rem_len, p); - - if ((rem_len + lws_ptr_diff(p, start)) >= - wsi->context->pt_serv_buf_size) { - lwsl_err("%s: Payload is too big\n", __func__); - return 1; - } - - /* Init lws_mqtt_str */ - lws_mqtt_str_init(&mqtt_vh_payload, (uint8_t *)p, rem_len, 0); - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - - /* Packet ID */ - wsi->mqtt->ack_pkt_id = ++nwsi->mqtt->pkt_id; - lwsl_debug("%s: pkt_id = %d\n", __func__, - (int)wsi->mqtt->ack_pkt_id); - lws_ser_wu16be(p, wsi->mqtt->ack_pkt_id); - - if (lws_mqtt_str_advance(&mqtt_vh_payload, 2)) - return 1; - - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - - for (n = 0; n < unsub->num_topics; n++) { - lwsl_info("%s: topics[%d] = %s\n", __func__, - (int)n, unsub->topic[n].name); - - /* - * Subscriber still bound to it, don't UBSUB - * from the server - */ - if (!send_unsub[n]) - continue; - - /* Topic's Len */ - lws_ser_wu16be(p, (uint16_t)strlen(unsub->topic[n].name)); - if (lws_mqtt_str_advance(&mqtt_vh_payload, 2)) - return 1; - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - - /* Topic Name */ - lws_strncpy((char *)p, unsub->topic[n].name, - strlen(unsub->topic[n].name) + 1); - if (lws_mqtt_str_advance(&mqtt_vh_payload, - (int)strlen(unsub->topic[n].name))) - return 1; - p = lws_mqtt_str_next(&mqtt_vh_payload, NULL); - } - break; - - default: - return 1; - } - - if (lws_write(nwsi, start, lws_ptr_diff(p, start), LWS_WRITE_BINARY) != - lws_ptr_diff(p, start)) - return 1; - - wsi->mqtt->inside_unsubscribe = 1; - - return 0; -} - -/* - * This is called when child streams bind to an already-existing and compatible - * MQTT stream - */ - -struct lws * -lws_wsi_mqtt_adopt(struct lws *parent_wsi, struct lws *wsi) -{ - /* no more children allowed by parent? */ - - if (parent_wsi->mux.child_count + 1 > LWS_MQTT_MAX_CHILDREN) { - lwsl_err("%s: reached concurrent stream limit\n", __func__); - return NULL; - } - -#if defined(LWS_WITH_CLIENT) - wsi->client_mux_substream = 1; -#endif - - lws_wsi_mux_insert(wsi, parent_wsi, wsi->mux.my_sid); - - if (lws_ensure_user_space(wsi)) - goto bail1; - - lws_mqtt_set_client_established(wsi); - lws_callback_on_writable(wsi); - -#if defined(LWS_WITH_SERVER_STATUS) - wsi->vhost->conn_stats.mqtt_subs++; -#endif - - return wsi; - -bail1: - /* undo the insert */ - parent_wsi->mux.child_list = wsi->mux.sibling_list; - parent_wsi->mux.child_count--; - - if (wsi->user_space) - lws_free_set_NULL(wsi->user_space); - - wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_DESTROY, NULL, NULL, 0); - lws_free(wsi); - - return NULL; -} - diff -Nru libwebsockets-4.0.20/lib/roles/mqtt/ops-mqtt.c libwebsockets-2.4.2/lib/roles/mqtt/ops-mqtt.c --- libwebsockets-4.0.20/lib/roles/mqtt/ops-mqtt.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/mqtt/ops-mqtt.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,603 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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->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 = wsi->context->pt_serv_buf_size; - - if ((unsigned int)ebuf.len > wsi->context->pt_serv_buf_size) - ebuf.len = wsi->context->pt_serv_buf_size; - - if ((int)pending > ebuf.len) - pending = ebuf.len; - - ebuf.len = lws_ssl_capable_read(wsi, ebuf.token, - pending ? (int)pending : - 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, ebuf.len); - if (n < 0) { - lwsl_notice("%s: lws_read_mqtt returned %d\n", - __func__, n); - /* we closed wsi */ - n = 0; - 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 = lws_ssl_pending(wsi); - if (pending) { - pending = pending > wsi->context->pt_serv_buf_size ? - wsi->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: %p flow buf: drained\n", __func__, 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->protocol, __func__); - else - /* this is the only time he will transition */ - lws_bind_protocol(wsi, - &wsi->vhost->protocols[wsi->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 - - 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; - } - - 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 %p (wsistate 0x%x)\n", __func__, 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->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 %p\n", __func__, 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_insert(&pt->pt_sul_owner, &wsi->mqtt->sul_qos1_puback_wait, - LWS_SET_TIMER_USEC_CANCEL); - - 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: %p (wsistate 0x%x)\n", __func__, 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(" wsi: %p, his parent %p: child list %p, siblings:\n", wsi, - 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 %p: parent %p: first child %p\n", wsi, - wsi->mux.parent_wsi, wsi->mux.child_list); - - if (wsi->mux.child_list && lwsl_visible(LLL_INFO)) { - lwsl_info(" parent %p: closing children: list:\n", wsi); - lws_wsi_mux_dump_children(wsi); - } - - lws_wsi_mux_close_children(wsi, 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; -} - - -struct lws_role_ops role_ops_mqtt = { - /* role name */ "mqtt", - /* alpn id */ "x-amzn-mqtt-ca", /* "mqtt/3.1.1" */ - /* check_upgrades */ NULL, - /* pt_init_destroy */ NULL, - /* init_vhost */ NULL, - /* destroy_vhost */ NULL, - /* service_flag_pending */ NULL, - .handle_POLLIN = rops_handle_POLLIN_mqtt, - .handle_POLLOUT = rops_handle_POLLOUT_mqtt, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ rops_callback_on_writable_mqtt, - /* tx_credit */ NULL, - .write_role_protocol = NULL, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ NULL, - /* close_via_role_protocol */ NULL, - .close_role = rops_close_role_mqtt, - .close_kill_connection = rops_close_kill_connection_mqtt, - /* destroy_role */ NULL, -#if 0 /* defined(LWS_WITH_SERVER) */ - /* adoption_bind */ rops_adoption_bind_mqtt, -#else - NULL, -#endif -#if defined(LWS_WITH_CLIENT) - .client_bind = rops_client_bind_mqtt, - .issue_keepalive = rops_issue_keepalive_mqtt, -#else - .client_bind = NULL, - .issue_keepalive = NULL, -#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, -}; diff -Nru libwebsockets-4.0.20/lib/roles/mqtt/primitives.c libwebsockets-2.4.2/lib/roles/mqtt/primitives.c --- libwebsockets-4.0.20/lib/roles/mqtt/primitives.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/mqtt/primitives.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,326 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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 "lws-mqtt.h" */ - -#include -#include -#include -#include - - -/* - * 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 += (u & 0x7f) << multiplier; - 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 = 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 += n; - s->len += 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 = (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 = s->len - s->pos; - - if (span > *len) - span = (uint16_t)*len; - - memcpy(s->buf + s->pos, *in, span); - *in += span; - s->pos += span; - } - - *len -= *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); -} - diff -Nru libwebsockets-4.0.20/lib/roles/mqtt/private-lib-roles-mqtt.h libwebsockets-2.4.2/lib/roles/mqtt/private-lib-roles-mqtt.h --- libwebsockets-4.0.20/lib/roles/mqtt/private-lib-roles-mqtt.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/mqtt/private-lib-roles-mqtt.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,396 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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) - -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_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 struct lws_mqtt_subs { - struct lws_mqtt_subs *next; - - uint8_t ref_count; /* number of children referencing */ - - /* 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; - uint8_t conn_flags; -} lws_mqttc_t; - -struct _lws_mqtt_related { - lws_mqttc_t client; - lws_sorted_usec_list_t sul_qos1_puback_wait; /* QoS1 puback 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 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 send_puback:1; - uint8_t unacked_publish:1; - - uint8_t done_subscribe: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); - -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); - -#endif /* _PRIVATE_LIB_ROLES_MQTT */ - diff -Nru libwebsockets-4.0.20/lib/roles/pipe/ops-pipe.c libwebsockets-2.4.2/lib/roles/pipe/ops-pipe.c --- libwebsockets-4.0.20/lib/roles/pipe/ops-pipe.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/pipe/ops-pipe.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,105 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -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; - if (eventfd_read(wsi->desc.sockfd, &value) < 0) - 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 = read(wsi->desc.sockfd, s, sizeof(s)); - (void)n; - if (n < 0) - return LWS_HPI_RET_PLEASE_CLOSE_ME; -#endif - -#if defined(LWS_WITH_THREADPOOL) - /* - * 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 - - /* - * 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; -} - -const struct lws_role_ops role_ops_pipe = { - /* role name */ "pipe", - /* alpn id */ NULL, - /* check_upgrades */ NULL, - /* pt_init_destroy */ NULL, - /* init_vhost */ NULL, - /* destroy_vhost */ NULL, - /* service_flag_pending */ NULL, - /* handle_POLLIN */ rops_handle_POLLIN_pipe, - /* handle_POLLOUT */ NULL, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ NULL, - /* tx_credit */ NULL, - /* write_role_protocol */ NULL, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ NULL, - /* close_via_role_protocol */ NULL, - /* close_role */ NULL, - /* close_kill_connection */ NULL, - /* destroy_role */ NULL, - /* adoption_bind */ NULL, - /* client_bind */ NULL, - /* issue_keepalive */ NULL, - /* 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 */ 1, -}; diff -Nru libwebsockets-4.0.20/lib/roles/private-lib-roles.h libwebsockets-2.4.2/lib/roles/private-lib-roles.h --- libwebsockets-4.0.20/lib/roles/private-lib-roles.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/private-lib-roles.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,352 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 & 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 & (~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 - */ -struct lws_context_per_thread; -struct lws_role_ops { - const char *name; - const char *alpn; - /* - * After http headers have parsed, this is the last chance for a role - * to upgrade the connection to something else using the headers. - * ws-over-h2 is upgraded from h2 like this. - */ - int (*check_upgrades)(struct lws *wsi); - /* role-specific context init during context creation */ - int (*pt_init_destroy)(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 */ - int (*init_vhost)(struct lws_vhost *vh, - const struct lws_context_creation_info *info); - /* role-specific per-vhost destructor during vhost destroy */ - int (*destroy_vhost)(struct lws_vhost *vh); - /* chance for the role to force POLLIN without network activity */ - int (*service_flag_pending)(struct lws_context *context, int tsi); - /* an fd using this role has POLLIN signalled */ - int (*handle_POLLIN)(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_pollfd *pollfd); - /* an fd using the role wanted a POLLOUT callback and now has it */ - int (*handle_POLLOUT)(struct lws *wsi); - /* perform user pollout */ - int (*perform_user_POLLOUT)(struct lws *wsi); - /* do effective callback on writeable */ - int (*callback_on_writable)(struct lws *wsi); - /* connection-specific tx credit in bytes */ - int (*tx_credit)(struct lws *wsi, char peer_to_us, int add); - /* role-specific write formatting */ - int (*write_role_protocol)(struct lws *wsi, unsigned char *buf, - size_t len, enum lws_write_protocol *wp); - - /* get encapsulation parent */ - struct lws * (*encapsulation_parent)(struct lws *wsi); - - /* role-specific destructor */ - int (*alpn_negotiated)(struct lws *wsi, const char *alpn); - - /* chance for the role to handle close in the protocol */ - int (*close_via_role_protocol)(struct lws *wsi, - enum lws_close_status reason); - /* role-specific close processing */ - int (*close_role)(struct lws_context_per_thread *pt, struct lws *wsi); - /* role-specific connection close processing */ - int (*close_kill_connection)(struct lws *wsi, - enum lws_close_status reason); - /* role-specific destructor */ - int (*destroy_role)(struct lws *wsi); - - /* role-specific socket-adopt */ - int (*adoption_bind)(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 */ - int (*client_bind)(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 */ - int (*issue_keepalive)(struct lws *wsi, int isvalid); - - /* - * the callback reasons for adoption for client, server - * (just client applies if no concept of client or server) - */ - uint16_t adoption_cb[2]; - /* - * the callback reasons for adoption for client, server - * (just client applies if no concept of client or server) - */ - uint16_t rx_cb[2]; - /* - * the callback reasons for WRITEABLE for client, server - * (just client applies if no concept of client or server) - */ - uint16_t writeable_cb[2]; - /* - * the callback reasons for CLOSE for client, server - * (just client applies if no concept of client or server) - */ - uint16_t close_cb[2]; - /* - * the callback reasons for protocol bind for client, server - * (just client applies if no concept of client or server) - */ - uint16_t protocol_bind_cb[2]; - /* - * the callback reasons for protocol unbind for client, server - * (just client applies if no concept of client or server) - */ - uint16_t protocol_unbind_cb[2]; - - unsigned int file_handle:1; /* role operates on files not sockets */ -}; - -/* core roles */ -extern const struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, - role_ops_listen, role_ops_pipe; - -/* bring in role private declarations */ - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - #include "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 -}; - -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); diff -Nru libwebsockets-4.0.20/lib/roles/raw-file/ops-raw-file.c libwebsockets-2.4.2/lib/roles/raw-file/ops-raw-file.c --- libwebsockets-4.0.20/lib/roles/raw-file/ops-raw-file.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/raw-file/ops-raw-file.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,117 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -static int -rops_handle_POLLIN_raw_file(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_pollfd *pollfd) -{ - int n; - - if (pollfd->revents & LWS_POLLOUT) { - n = lws_callback_as_writeable(wsi); - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { - lwsl_info("failed at set pollfd\n"); - return LWS_HPI_RET_WSI_ALREADY_DIED; - } - if (n) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - if (pollfd->revents & LWS_POLLIN) { - if (user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_RAW_RX_FILE, - wsi->user_space, NULL, 0)) { - lwsl_debug("raw rx callback closed it\n"); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - } - - if (pollfd->revents & LWS_POLLHUP) - 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->vhost->default_protocol_index >= - wsi->vhost->count_protocols) - return 0; - - wsi->protocol = &wsi->vhost->protocols[ - wsi->vhost->default_protocol_index]; - } - - return 1; /* bound */ -} - -const struct lws_role_ops role_ops_raw_file = { - /* role name */ "raw-file", - /* alpn id */ NULL, - /* check_upgrades */ NULL, - /* pt_init_destroy */ NULL, - /* init_vhost */ NULL, - /* destroy_vhost */ NULL, - /* service_flag_pending */ NULL, - /* handle_POLLIN */ rops_handle_POLLIN_raw_file, - /* handle_POLLOUT */ NULL, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ NULL, - /* tx_credit */ NULL, - /* write_role_protocol */ NULL, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ NULL, - /* close_via_role_protocol */ NULL, - /* close_role */ NULL, - /* close_kill_connection */ NULL, - /* destroy_role */ NULL, - /* adoption_bind */ rops_adoption_bind_raw_file, - /* client_bind */ NULL, - /* issue_keepalive */ NULL, - /* 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, -}; diff -Nru libwebsockets-4.0.20/lib/roles/raw-proxy/ops-raw-proxy.c libwebsockets-2.4.2/lib/roles/raw-proxy/ops-raw-proxy.c --- libwebsockets-4.0.20/lib/roles/raw-proxy/ops-raw-proxy.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/raw-proxy/ops-raw-proxy.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,230 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -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 ((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->protocol->callback, - wsi, lwsi_role_client(wsi) ? - LWS_CALLBACK_RAW_PROXY_CLI_RX : - LWS_CALLBACK_RAW_PROXY_SRV_RX, - wsi->user_space, ebuf.token, - 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_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->protocol, __func__); - else - /* this is the only time he will transition */ - lws_bind_protocol(wsi, - &wsi->vhost->protocols[wsi->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; -} - -const struct lws_role_ops role_ops_raw_proxy = { - /* role name */ "raw-proxy", - /* alpn id */ NULL, - /* check_upgrades */ NULL, - /* pt_init_destroy */ NULL, - /* init_vhost */ NULL, - /* destroy_vhost */ NULL, - /* service_flag_pending */ NULL, - /* handle_POLLIN */ rops_handle_POLLIN_raw_proxy, - /* handle_POLLOUT */ rops_handle_POLLOUT_raw_proxy, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ NULL, - /* tx_credit */ NULL, - /* write_role_protocol */ NULL, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ NULL, - /* close_via_role_protocol */ NULL, - /* close_role */ NULL, - /* close_kill_connection */ NULL, - /* destroy_role */ NULL, - /* adoption_bind */ rops_adoption_bind_raw_proxy, - /* client_bind */ rops_client_bind_raw_proxy, - /* issue_keepalive */ NULL, - /* 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, -}; diff -Nru libwebsockets-4.0.20/lib/roles/raw-proxy/private-lib-roles-raw-proxy.h libwebsockets-2.4.2/lib/roles/raw-proxy/private-lib-roles-raw-proxy.h --- libwebsockets-4.0.20/lib/roles/raw-proxy/private-lib-roles-raw-proxy.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/raw-proxy/private-lib-roles-raw-proxy.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 diff -Nru libwebsockets-4.0.20/lib/roles/raw-skt/ops-raw-skt.c libwebsockets-2.4.2/lib/roles/raw-skt/ops-raw-skt.c --- libwebsockets-4.0.20/lib/roles/raw-skt/ops-raw-skt.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/raw-skt/ops-raw-skt.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,335 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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 - -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_debug("%s: %p: wsistate 0x%x\n", __func__, wsi, - (int)wsi->wsistate); - - if (lwsi_state(wsi) != LRS_SSL_INIT) - if (lws_server_socket_service_ssl(wsi, - LWS_SOCK_INVALID)) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - - return LWS_HPI_RET_HANDLED; - } -#endif - - if ((pollfd->revents & pollfd->events & LWS_POLLIN) && - !(wsi->favoured_pollin && - (pollfd->revents & pollfd->events & LWS_POLLOUT))) { - - lwsl_debug("%s: POLLIN: wsi %p, state 0x%x\n", __func__, - wsi, 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; - -#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 = 0; - - buffered = lws_buflist_aware_read(pt, wsi, &ebuf, 1, __func__); - 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; - } - -#if defined(LWS_WITH_UDP) - if (wsi->context->udp_loss_sim_rx_pc) { - uint16_t u16; - /* - * We should randomly drop some of these - */ - - if (lws_get_random(wsi->context, &u16, 2) == 2 && - ((u16 * 100) / 0xffff) <= - wsi->context->udp_loss_sim_rx_pc) { - lwsl_warn("%s: dropping udp rx\n", __func__); - /* pretend it was handled */ - n = ebuf.len; - goto post_rx; - } - } -#endif - - n = user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_RAW_RX, - wsi->user_space, ebuf.token, - ebuf.len); -#if defined(LWS_WITH_UDP) || defined(LWS_WITH_SOCKS5) -post_rx: -#endif - if (n < 0) { - lwsl_info("LWS_CALLBACK_RAW_RX_fail\n"); - 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 && - !lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL)) - return LWS_HPI_RET_WSI_ALREADY_DIED; -#endif - - /* one shot */ - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { - lwsl_notice("%s a\n", __func__); - goto fail; - } - - /* clear back-to-back write detection */ - wsi->could_have_pending = 0; - - lws_stats_bump(pt, LWSSTATS_C_WRITEABLE_CB, 1); -#if defined(LWS_WITH_STATS) - if (wsi->active_writable_req_us) { - uint64_t ul = lws_now_usecs() - - wsi->active_writable_req_us; - - lws_stats_bump(pt, LWSSTATS_US_WRITABLE_DELAY_AVG, ul); - lws_stats_max(pt, - LWSSTATS_US_WORST_WRITABLE_DELAY, ul); - wsi->active_writable_req_us = 0; - } -#endif - n = user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_RAW_WRITEABLE, - wsi->user_space, NULL, 0); - if (n < 0) { - lwsl_info("writeable_fail\n"); - goto fail; - } - - return LWS_HPI_RET_HANDLED; - -fail: - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "raw svc fail"); - - return LWS_HPI_RET_WSI_ALREADY_DIED; -} - -#if defined(LWS_WITH_SERVER) -static int -rops_adoption_bind_raw_skt(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_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, 0, (type & LWS_ADOPT_ALLOW_SSL) ? LRS_SSL_INIT : - LRS_ESTABLISHED, &role_ops_raw_skt); - - if (vh_prot_name) - lws_bind_protocol(wsi, wsi->protocol, __func__); - else - /* this is the only time he will transition */ - lws_bind_protocol(wsi, - &wsi->vhost->protocols[wsi->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 - -const struct lws_role_ops role_ops_raw_skt = { - /* role name */ "raw-skt", - /* alpn id */ NULL, - /* check_upgrades */ NULL, - /* pt_init_destroy */ NULL, - /* init_vhost */ NULL, - /* destroy_vhost */ NULL, - /* service_flag_pending */ NULL, - /* handle_POLLIN */ rops_handle_POLLIN_raw_skt, - /* handle_POLLOUT */ NULL, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ NULL, - /* tx_credit */ NULL, - /* write_role_protocol */ NULL, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ NULL, - /* close_via_role_protocol */ NULL, - /* close_role */ NULL, - /* close_kill_connection */ NULL, - /* destroy_role */ NULL, -#if defined(LWS_WITH_SERVER) - /* adoption_bind */ rops_adoption_bind_raw_skt, -#else - NULL, -#endif -#if defined(LWS_WITH_CLIENT) - /* client_bind */ rops_client_bind_raw_skt, -#else - NULL, -#endif - /* issue_keepalive */ NULL, - /* 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, -}; diff -Nru libwebsockets-4.0.20/lib/roles/README.md libwebsockets-2.4.2/lib/roles/README.md --- libwebsockets-4.0.20/lib/roles/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,161 +0,0 @@ -## 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 - - - proxy support, 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()`. - diff -Nru libwebsockets-4.0.20/lib/roles/ws/client-parser-ws.c libwebsockets-2.4.2/lib/roles/ws/client-parser-ws.c --- libwebsockets-4.0.20/lib/roles/ws/client-parser-ws.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/ws/client-parser-ws.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,692 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_debug("%s: doing draining flow\n", __func__); - - goto drain_extension; - } -#endif - - if (wsi->socket_is_permanently_unusable) - return -1; - - 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->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_info("disordered continuation\n"); - 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: - lwsl_info("illegal opcode\n"); - 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) { - lwsl_info("illegal rsv bits set\n"); - return -1; - } - wsi->ws->final = !!((c >> 7) & 1); - lwsl_ext("%s: This RX frame Final %d\n", __func__, - wsi->ws->final); - - if (wsi->ws->owed_a_fin && - (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME || - wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) { - lwsl_info("hey you owed us a FIN\n"); - 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_info("control msg can't be fragmented\n"); - 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_err("unknown spec version %02d\n", - wsi->ws->ietf_spec_revision); - break; - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN: - - wsi->ws->this_frame_masked = !!(c & 0x80); - - 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 = 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_warn("b63 of length must be zero\n"); - /* 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_debug("%s: spilling as we have the whole frame\n", - __func__); - goto spill; - } - - /* - * if there's no protocol max frame size given, we are - * supposed to default to context->pt_serv_buf_size - */ - if (!wsi->protocol->rx_buffer_size && - wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size) - break; - - if (wsi->protocol->rx_buffer_size && - wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size) - break; - - /* spill because we filled our rx buffer */ - - lwsl_debug("%s: spilling as we filled our rx buffer\n", - __func__); -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->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_parser("seen server's close ack\n"); - return -1; - } - - lwsl_parser("client sees server close len = %d\n", - (int)wsi->ws->rx_ubuf_head); - if (wsi->ws->rx_ubuf_head >= 2) { - close_code = (pp[0] << 8) | pp[1]; - if (close_code < 1000 || - close_code == 1004 || - close_code == 1005 || - close_code == 1006 || - close_code == 1012 || - close_code == 1013 || - close_code == 1014 || - close_code == 1015 || - (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->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 = - wsi->ws->rx_ubuf_head; - - lwsl_info("%s: scheduling return close as ack\n", - __func__); - __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_info("received %d byte ping, sending pong\n", - (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->ping_pending_flag) { - /* - * there is already a pending ping payload - * we should just log and drop - */ - lwsl_parser("DROP PING since one pending\n"); - goto ping_drop; - } - - /* control packets can only be < 128 bytes long */ - if (wsi->ws->rx_ubuf_head > 128 - 3) { - lwsl_parser("DROP PING payload too large\n"); - goto ping_drop; - } - - /* stash the pong payload */ - memcpy(wsi->ws->ping_payload_buf + LWS_PRE, - &wsi->ws->rx_ubuf[LWS_PRE], - wsi->ws->rx_ubuf_head); - - wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head; - wsi->ws->ping_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_info("%s: client %p received pong\n", __func__, wsi); - lwsl_hexdump(&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_ext("Unhandled ext opc 0x%x\n", 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 = wsi->ws->rx_ubuf_head; - - /* for the non-pm-deflate case */ - - pmdrx.eb_out = pmdrx.eb_in; - - lwsl_debug("%s: starting disbursal of %d deframed rx\n", - __func__, (int)wsi->ws->rx_ubuf_head); - -#if !defined(LWS_WITHOUT_EXTENSIONS) -drain_extension: -#endif - do { - - // lwsl_notice("%s: pmdrx.eb_in.len: %d\n", __func__, - // (int)pmdrx.eb_in.len); - - n = PMDR_DID_NOTHING; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - lwsl_ext("%s: +++ passing %d %p to ext\n", __func__, - pmdrx.eb_in.len, pmdrx.eb_in.token); - - n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, - &pmdrx, 0); - lwsl_ext("Ext RX returned %d\n", n); - if (n < 0) { - wsi->socket_is_permanently_unusable = 1; - return -1; - } - if (n == PMDR_DID_NOTHING) - break; -#endif - lwsl_ext("%s: post inflate ebuf in len %d / out len %d\n", - __func__, pmdrx.eb_in.len, pmdrx.eb_out.len); - -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (rx_draining_ext && !pmdrx.eb_out.len) { - lwsl_debug(" --- ending drain on 0 read result\n"); - goto already_done; - } - - if (n == PMDR_HAS_PENDING) { /* 1 means stuff to drain */ - /* extension had more... main loop will come back */ - lwsl_ext("%s: adding to draining ext list\n", - __func__); - lws_add_wsi_to_draining_ext_list(wsi); - } else { - lwsl_ext("%s: removing from draining ext list\n", - __func__); - 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, - 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_info("FINAL utf8 error\n"); - lws_close_reason(wsi, - LWS_CLOSE_STATUS_INVALID_PAYLOAD, - (uint8_t *)"partial utf8", 12); -utf8_fail: - lwsl_info("utf8 error\n"); - lwsl_hexdump_info(pmdrx.eb_out.token, - 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->protocol->callback) - goto already_done; - - if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG) - lwsl_info("Client doing pong callback\n"); - -#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_UNKNOWN -#endif - ) - pmdrx.eb_in.len -= pmdrx.eb_out.len; - - m = wsi->protocol->callback(wsi, - (enum lws_callback_reasons)callback_action, - wsi->user_space, pmdrx.eb_out.token, - pmdrx.eb_out.len); - - wsi->ws->first_fragment = 0; - - lwsl_debug("%s: bulk ws rx: inp used %d, output %d\n", - __func__, (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_err("client rx illegal state\n"); - return 1; - } - - return 0; - -illegal_ctl_length: - lwsl_warn("Control frame asking for extended length is illegal\n"); - - /* kill the connection */ - return -1; -} - - diff -Nru libwebsockets-4.0.20/lib/roles/ws/client-ws.c libwebsockets-2.4.2/lib/roles/ws/client-ws.c --- libwebsockets-4.0.20/lib/roles/ws/client-ws.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/ws/client-ws.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,679 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -/* - * In-place str to lower case - */ - -static void -strtolower(char *s) -{ - while (*s) { -#ifdef LWS_PLAT_OPTEE - int tolower_optee(int c); - *s = tolower_optee((int)*s); -#else - *s = tolower((int)*s); -#endif - s++; - } -} - -int -lws_create_client_ws_object(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_notice("OOM\n"); - return 1; - } - - /* -1 means just use latest supported */ - if (i->ietf_version_or_minus_one != -1 && - i->ietf_version_or_minus_one) - v = i->ietf_version_or_minus_one; - - wsi->ws->ietf_spec_revision = v; - - return 0; -} - -#if defined(LWS_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_debug("%s: hs client feels it has %d in\n", __func__, (int)len); - - while (len) { - /* - * we were accepting input but now we stopped doing so - */ - if (lws_is_flowcontrolled(wsi)) { - lwsl_debug("%s: caching %ld\n", __func__, (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, (int)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_info("%s: trimming inside rxflow cache\n", - __func__); - *buf = bufin; - } else - *buf += len; - - return 0; - } -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->rx_draining_ext) { - int m; - - lwsl_info("%s: draining ext\n", __func__); - if (lwsi_role_client(wsi)) - m = lws_ws_client_rx_sm(wsi, 0); - else - m = lws_ws_rx_sm(wsi, 0, 0); - if (m < 0) - return -1; - continue; - } -#endif - /* - * caller will account for buflist usage by studying what - * happened to *buf - */ - - if (lws_ws_client_rx_sm(wsi, *(*buf)++)) { - lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n", - __func__, (int)len); - return -1; - } - len--; - } - // lwsl_notice("%s: finished with %ld\n", __func__, (long)len); - - return 0; -} -#endif - -char * -lws_generate_client_ws_handshake(struct lws *wsi, char *p, 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->context, hash, 16) != 16) { - lwsl_err("Unable to read from random dev %s\n", - 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->vhost->ws.extensions; - while (ext && ext->callback) { - - n = wsi->vhost->protocols[0].callback(wsi, - LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, - wsi->user_space, (char *)ext->name, 0); - - /* - * zero return from callback means go ahead and allow - * the extension, it's what we get if the callback is - * unhandled - */ - - if (n) { - ext++; - continue; - } - - /* apply it */ - - if (ext_count) - *p++ = ','; - else - p += sprintf(p, "Sec-WebSocket-Extensions: "); - p += sprintf(p, "%s", ext->client_offer); - ext_count++; - - ext++; - } - if (ext_count) - p += sprintf(p, "\x0d\x0a"); -#endif - - if (wsi->ws->ietf_spec_revision) - p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a", - wsi->ws->ietf_spec_revision); - - /* prepare the expected server accept response */ - - key_b64[39] = '\0'; /* enforce composed length below buf sizeof */ - n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", - key_b64); - - lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash); - - lws_b64_encode_string(hash, 20, - wsi->http.ah->initial_handshake_hash_base64, - sizeof(wsi->http.ah->initial_handshake_hash_base64)); - - return p; -} - -int -lws_client_ws_upgrade(struct lws *wsi, const char **cce) -{ - struct lws_context *context = wsi->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->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 defined(LWS_WITH_DETAILED_LATENCY) - wsi->detlat.earliest_write_req = 0; - wsi->detlat.earliest_write_req_pre_write = 0; -#endif - - if (wsi->client_mux_substream) {/* !!! client ws-over-h2 not there yet */ - lwsl_warn("%s: client ws-over-h2 upgrade not supported yet\n", - __func__); - *cce = "HS: h2 / ws upgrade unsupported"; - goto bail3; - } - - if (wsi->http.ah->http_response == 401) { - lwsl_warn( - "lws_client_handshake: got bad HTTP response '%d'\n", - wsi->http.ah->http_response); - *cce = "HS: ws upgrade unauthorized"; - goto bail3; - } - - if (wsi->http.ah->http_response != 101) { - lwsl_warn( - "lws_client_handshake: got bad HTTP response '%d'\n", - wsi->http.ah->http_response); - *cce = "HS: ws upgrade response not 101"; - goto bail3; - } - - if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) { - lwsl_info("no ACCEPT\n"); - *cce = "HS: ACCEPT missing"; - goto bail3; - } - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE); - if (!p) { - lwsl_info("no UPGRADE\n"); - *cce = "HS: UPGRADE missing"; - goto bail3; - } - strtolower(p); - if (strcmp(p, "websocket")) { - lwsl_warn( - "lws_client_handshake: got bad Upgrade header '%s'\n", p); - *cce = "HS: Upgrade to something other than websocket"; - goto bail3; - } - - /* 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 = 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_info("%s: malfored connection '%s'\n", - __func__, 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_parser("lws_client_int_s_hs: no protocol list\n"); - } else - lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", 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_info("%s: WSI_TOKEN_PROTOCOL is null\n", __func__); - /* - * no protocol name to work from, if we don't already have one - * default to first protocol - */ - - if (wsi->protocol) { - p = (char *)wsi->protocol->name; - goto identify_protocol; - } - - /* no choice but to use the default protocol */ - - n = 0; - wsi->protocol = &wsi->vhost->protocols[0]; - goto check_extensions; - } - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL); - len = (int)strlen(p); - - while (pc && *pc && !okay) { - if (!strncmp(pc, p, len) && - (pc[len] == ',' || pc[len] == '\0')) { - okay = 1; - continue; - } - while (*pc && *pc++ != ',') - ; - while (*pc == ' ') - pc++; - } - - if (!okay) { - lwsl_info("%s: got bad protocol %s\n", __func__, 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->protocol = NULL; - - while (n < wsi->vhost->count_protocols) { - if (!wsi->protocol && - strcmp(p, wsi->vhost->protocols[n].name) == 0) { - wsi->protocol = &wsi->vhost->protocols[n]; - break; - } - n++; - } - - if (n == wsi->vhost->count_protocols) { /* no match */ - /* if server, that's already fatal */ - if (!lwsi_role_client(wsi)) { - lwsl_info("%s: fail protocol %s\n", __func__, p); - *cce = "HS: Cannot match protocol"; - goto bail2; - } - - /* for client, find the index of our pre-bound protocol */ - - n = 0; - while (wsi->vhost->protocols[n].callback) { - if (wsi->protocol && strcmp(wsi->protocol->name, - wsi->vhost->protocols[n].name) == 0) { - wsi->protocol = &wsi->vhost->protocols[n]; - break; - } - n++; - } - - if (!wsi->vhost->protocols[n].callback) { - if (wsi->protocol) - lwsl_err("Failed to match protocol %s\n", - wsi->protocol->name); - else - lwsl_err("No protocol on client\n"); - *cce = "ws protocol no match"; - goto bail2; - } - } - - lwsl_debug("Selected protocol %s\n", wsi->protocol->name); - -check_extensions: - /* - * stitch protocol choice into the vh protocol linked list - * We always insert ourselves at the start of the list - * - * X <-> B - * X <-> pAn <-> pB - */ - - lws_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_ext("no client extensions allowed by server\n"); - goto check_accept; - } - - /* - * break down the list of server accepted extensions - * and go through matching them or identifying bogons - */ - - if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size, - WSI_TOKEN_EXTENSIONS) < 0) { - lwsl_warn("ext list from server failed to copy\n"); - *cce = "HS: EXT: list too big"; - goto bail2; - } - - c = sb; - n = 0; - ignore = 0; - a = NULL; - while (more) { - - if (*c && (*c != ',' && *c != '\t')) { - if (*c == ';') { - ignore = 1; - if (!a) - a = c + 1; - } - if (ignore || *c == ' ') { - c++; - continue; - } - - ext_name[n] = *c++; - if (n < (int)sizeof(ext_name) - 1) - n++; - continue; - } - ext_name[n] = '\0'; - ignore = 0; - if (!*c) - more = 0; - else { - c++; - if (!n) - continue; - } - - /* check we actually support it */ - - lwsl_notice("checking client ext %s\n", ext_name); - - n = 0; - ext = wsi->vhost->ws.extensions; - while (ext && ext->callback) { - if (strcmp(ext_name, ext->name)) { - ext++; - continue; - } - - n = 1; - lwsl_notice("instantiating client ext %s\n", ext_name); - - /* instantiate the extension on this conn */ - - wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext; - - /* allow him to construct his ext instance */ - - if (ext->callback(lws_get_context(wsi), ext, wsi, - LWS_EXT_CB_CLIENT_CONSTRUCT, - (void *)&wsi->ws->act_ext_user[ - wsi->ws->count_act_ext], - (void *)&opts, 0)) { - lwsl_info(" ext %s failed construction\n", - ext_name); - ext++; - continue; - } - - /* - * allow the user code to override ext defaults if it - * wants to - */ - ext_name[0] = '\0'; - if (user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_WS_EXT_DEFAULTS, - (char *)ext->name, ext_name, - sizeof(ext_name))) { - *cce = "HS: EXT: failed setting defaults"; - goto bail2; - } - - if (ext_name[0] && - lws_ext_parse_options(ext, wsi, - wsi->ws->act_ext_user[ - wsi->ws->count_act_ext], - opts, ext_name, - (int)strlen(ext_name))) { - lwsl_err("%s: unable to parse user defaults '%s'", - __func__, ext_name); - *cce = "HS: EXT: failed parsing defaults"; - goto bail2; - } - - /* - * give the extension the server options - */ - if (a && lws_ext_parse_options(ext, wsi, - wsi->ws->act_ext_user[ - wsi->ws->count_act_ext], - opts, a, lws_ptr_diff(c, a))) { - lwsl_err("%s: unable to parse remote def '%s'", - __func__, a); - *cce = "HS: EXT: failed parsing options"; - goto bail2; - } - - if (ext->callback(lws_get_context(wsi), ext, wsi, - LWS_EXT_CB_OPTION_CONFIRM, - wsi->ws->act_ext_user[wsi->ws->count_act_ext], - NULL, 0)) { - lwsl_err("%s: ext %s rejects server options %s", - __func__, ext->name, a); - *cce = "HS: EXT: Rejects server options"; - goto bail2; - } - - wsi->ws->count_act_ext++; - - ext++; - } - - if (n == 0) { - lwsl_warn("Unknown ext '%s'!\n", ext_name); - *cce = "HS: EXT: unknown ext"; - goto bail2; - } - - a = NULL; - n = 0; - } - -check_accept: -#endif - - /* - * Confirm his accept token is the one we precomputed - */ - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT); - if (strcmp(p, wsi->http.ah->initial_handshake_hash_base64)) { - lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p, - wsi->http.ah->initial_handshake_hash_base64); - *cce = "HS: Accept hash wrong"; - goto bail2; - } - - /* allocate the per-connection user memory (if any) */ - if (lws_ensure_user_space(wsi)) { - lwsl_err("Problem allocating wsi user mem\n"); - *cce = "HS: OOM"; - goto bail2; - } - - /* - * we seem to be good to go, give client last chance to check - * headers and OK it - */ - if (wsi->protocol->callback(wsi, - LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, - wsi->user_space, NULL, 0)) { - *cce = "HS: Rejected by filter cb"; - goto bail2; - } - - /* clear his proxy connection timeout */ - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - /* free up his parsing allocations */ - lws_header_table_detach(wsi, 0); - - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED, &role_ops_ws); - lws_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->protocol->rx_buffer_size; - if (!n) - n = context->pt_serv_buf_size; - n += LWS_PRE; - wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, - "client frame buffer"); - if (!wsi->ws->rx_ubuf) { - lwsl_err("Out of Mem allocating rx buffer %d\n", n); - *cce = "HS: OOM"; - goto bail2; - } - wsi->ws->rx_ubuf_alloc = n; - - lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name); - - /* call him back to inform him he is up */ - - if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED, - wsi->user_space, NULL, 0)) { - *cce = "HS: Rejected at CLIENT_ESTABLISHED"; - goto bail3; - } - - return 0; - -bail3: - return 3; - -bail2: - return 2; -} diff -Nru libwebsockets-4.0.20/lib/roles/ws/ext/extension.c libwebsockets-2.4.2/lib/roles/ws/ext/extension.c --- libwebsockets-4.0.20/lib/roles/ws/ext/extension.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/ws/ext/extension.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,383 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE); -} - -enum lws_ext_option_parser_states { - LEAPS_SEEK_NAME, - LEAPS_EAT_NAME, - LEAPS_SEEK_VAL, - LEAPS_EAT_DEC, - LEAPS_SEEK_ARG_TERM -}; - -int -lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi, - void *ext_user, const struct lws_ext_options *opts, - const char *in, int len) -{ - enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME; - unsigned int match_map = 0, n, m, w = 0, count_options = 0, - pending_close_quote = 0; - struct lws_ext_option_arg oa; - - oa.option_name = NULL; - - while (opts[count_options].name) - count_options++; - while (len) { - lwsl_ext("'%c' %d", *in, leap); - switch (leap) { - case LEAPS_SEEK_NAME: - if (*in == ' ') - break; - if (*in == ',') { - len = 1; - break; - } - match_map = (1 << count_options) - 1; - leap = LEAPS_EAT_NAME; - w = 0; - - /* fallthru */ - - case LEAPS_EAT_NAME: - oa.start = NULL; - oa.len = 0; - m = match_map; - n = 0; - pending_close_quote = 0; - while (m) { - if (!(m & 1)) { - m >>= 1; - n++; - continue; - } - lwsl_ext(" m=%d, n=%d, w=%d\n", m, n, w); - - if (*in == opts[n].name[w]) { - if (!opts[n].name[w + 1]) { - oa.option_index = n; - lwsl_ext("hit %d\n", - oa.option_index); - leap = LEAPS_SEEK_VAL; - if (len == 1) - goto set_arg; - break; - } - } else { - match_map &= ~(1 << n); - if (!match_map) { - lwsl_ext("empty match map\n"); - return -1; - } - } - - m >>= 1; - n++; - } - w++; - break; - case LEAPS_SEEK_VAL: - if (*in == ' ') - break; - if (*in == ',') { - len = 1; - break; - } - if (*in == ';' || len == 1) { /* ie,nonoptional */ - if (opts[oa.option_index].type == EXTARG_DEC) - return -1; - leap = LEAPS_SEEK_NAME; - goto set_arg; - } - if (*in == '=') { - w = 0; - pending_close_quote = 0; - if (opts[oa.option_index].type == EXTARG_NONE) - return -1; - - leap = LEAPS_EAT_DEC; - break; - } - return -1; - - case LEAPS_EAT_DEC: - if (*in >= '0' && *in <= '9') { - if (!w) - oa.start = in; - w++; - if (len != 1) - break; - } - if (!w && *in =='"') { - pending_close_quote = 1; - break; - } - if (!w) - return -1; - if (pending_close_quote && *in != '"' && len != 1) - return -1; - leap = LEAPS_SEEK_ARG_TERM; - if (oa.start) - oa.len = 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, reason, wsi->ws->act_ext_user[n], arg, len); - if (m < 0) { - lwsl_ext("Ext '%s' failed to handle callback %d!\n", - 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->vhost || !wsi->ws) - return 0; - - ext = wsi->vhost->ws.extensions; - - while (ext && ext->callback && !handled) { - m = ext->callback(context, ext, wsi, reason, - (void *)(lws_intptr_t)n, arg, len); - if (m < 0) { - lwsl_ext("Ext '%s' failed to handle callback %d!\n", - wsi->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, ebuf.len); - if (n < 0) { - lwsl_info("closing from ext access\n"); - return -1; - } - - /* always either sent it all or privately buffered */ - if (wsi->ws->clean_buffer) - len = n; - - lwsl_ext("%s: written %d bytes to client\n", - __func__, 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_debug("choked\n"); - - /* - * Yes, he's choked. Don't spill the rest now get a callback - * when he is ready to send and take care of it there - */ - lws_callback_on_writable(wsi); - wsi->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->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->context, - wsi->ws->active_extensions[idx], wsi, - LWS_EXT_CB_NAMED_OPTION_SET, wsi->ws->act_ext_user[idx], - &oa, 0); -} diff -Nru libwebsockets-4.0.20/lib/roles/ws/ext/extension-permessage-deflate.c libwebsockets-2.4.2/lib/roles/ws/ext/extension-permessage-deflate.c --- libwebsockets-4.0.20/lib/roles/ws/ext/extension-permessage-deflate.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/ws/ext/extension-permessage-deflate.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,549 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 -#include -#include - -#define LWS_ZLIB_MEMLEVEL 8 - -const struct lws_ext_options lws_ext_pm_deflate_options[] = { - /* public RFC7692 settings */ - { "server_no_context_takeover", EXTARG_NONE }, - { "client_no_context_takeover", EXTARG_NONE }, - { "server_max_window_bits", EXTARG_OPT_DEC }, - { "client_max_window_bits", EXTARG_OPT_DEC }, - /* ones only user code can set */ - { "rx_buf_size", EXTARG_DEC }, - { "tx_buf_size", EXTARG_DEC }, - { "compression_level", EXTARG_DEC }, - { "mem_level", EXTARG_DEC }, - { NULL, 0 }, /* sentinel */ -}; - -static void -lws_extension_pmdeflate_restrict_args(struct lws *wsi, - struct lws_ext_pm_deflate_priv *priv) -{ - int n, extra; - - /* cap the RX buf at the nearest power of 2 to protocol rx buf */ - - n = wsi->context->pt_serv_buf_size; - if (wsi->protocol->rx_buffer_size) - n = (int)wsi->protocol->rx_buffer_size; - - extra = 7; - while (n >= 1 << (extra + 1)) - extra++; - - if (extra < priv->args[PMD_RX_BUF_PWR2]) { - priv->args[PMD_RX_BUF_PWR2] = extra; - lwsl_info(" Capping pmd rx to %d\n", 1 << extra); - } -} - -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_ext("%s: named option set: %s\n", __func__, - 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_ext("%s: option set: idx %d, %s, len %d\n", __func__, - oa->option_index, oa->start, oa->len); - if (oa->start) - priv->args[oa->option_index] = atoi(oa->start); - else - priv->args[oa->option_index] = 1; - - if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8) - priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9; - - lws_extension_pmdeflate_restrict_args(wsi, priv); - break; - - case LWS_EXT_CB_OPTION_CONFIRM: - if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 || - priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 || - priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 || - priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15) - return -1; - break; - - case LWS_EXT_CB_CLIENT_CONSTRUCT: - case LWS_EXT_CB_CONSTRUCT: - - n = context->pt_serv_buf_size; - if (wsi->protocol->rx_buffer_size) - n = (int)wsi->protocol->rx_buffer_size; - - if (n < 128) { - lwsl_info(" permessage-deflate requires the protocol " - "(%s) to have an RX buffer >= 128\n", - wsi->protocol->name); - return -1; - } - - /* fill in **user */ - priv = lws_zalloc(sizeof(*priv), "pmd priv"); - *((void **)user) = priv; - lwsl_ext("%s: LWS_EXT_CB_*CONSTRUCT\n", __func__); - memset(priv, 0, sizeof(*priv)); - - /* fill in pointer to options list */ - if (in) - *((const struct lws_ext_options **)in) = - lws_ext_pm_deflate_options; - - /* fallthru */ - - case LWS_EXT_CB_OPTION_DEFAULT: - - /* set the public, RFC7692 defaults... */ - - priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0, - priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0; - priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15; - priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15; - - /* ...and the ones the user code can override */ - - priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */ - priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */ - priv->args[PMD_COMP_LEVEL] = 1; - priv->args[PMD_MEM_LEVEL] = 8; - - lws_extension_pmdeflate_restrict_args(wsi, priv); - break; - - case LWS_EXT_CB_DESTROY: - lwsl_ext("%s: LWS_EXT_CB_DESTROY\n", __func__); - lws_free(priv->buf_rx_inflated); - lws_free(priv->buf_tx_deflated); - if (priv->rx_init) - (void)inflateEnd(&priv->rx); - if (priv->tx_init) - (void)deflateEnd(&priv->tx); - lws_free(priv); - - return ret; - - - case LWS_EXT_CB_PAYLOAD_RX: - /* - * ie, we are INFLATING - */ - lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n", - __func__, pmdrx->eb_in.len, priv->rx.avail_in); - - /* if this frame is not marked as compressed, we ignore it */ - - if (!(wsi->ws->rsv_first_msg & 0x40) || (wsi->ws->opcode & 8)) - return PMDR_DID_NOTHING; - - /* - * 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_ext("%s: LWS_EXT_CB_PAYLOAD_RX: in %d, " - "existing avail in %d, pkt fin: %d\n", __func__, - 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_err("%s: iniflateInit failed\n", __func__); - return PMDR_FAILED; - } - priv->rx_init = 1; - if (!priv->buf_rx_inflated) - priv->buf_rx_inflated = lws_malloc( - LWS_PRE + 7 + 5 + - (1 << priv->args[PMD_RX_BUF_PWR2]), - "pmd rx inflate buf"); - if (!priv->buf_rx_inflated) { - lwsl_err("%s: OOM\n", __func__); - return 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_warn("%s: priv->rx.avail_in %d while getting new in\n", - __func__, 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 = 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 = 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_ext("%s: trailer apply 1\n", __func__); - 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_ext("inflate ret %d, avi %d, avo %d, wsifinal %d\n", 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_err("%s: zlib error inflate %d: \"%s\"\n", - __func__, n, priv->rx.msg); - return PMDR_FAILED; - } - - /* - * track how much input was used, and advance it - */ - - pmdrx->eb_in.token = pmdrx->eb_in.token + - (pmdrx->eb_in.len - priv->rx.avail_in); - pmdrx->eb_in.len = priv->rx.avail_in; - - lwsl_debug("%s: %d %d %d %d %d\n", __func__, - 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_ext("%s: RX trailer apply 2\n", __func__); - - /* 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_ext("RX trailer infl ret %d, avi %d, avo %d\n", - n, priv->rx.avail_in, priv->rx.avail_out); - switch (n) { - case Z_NEED_DICT: - case Z_STREAM_ERROR: - case Z_DATA_ERROR: - case Z_MEM_ERROR: - lwsl_info("zlib error inflate %d: %s\n", - n, priv->rx.msg); - return -1; - } - - 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 += pmdrx->eb_out.len; - - lwsl_ext(" %s: RX leaving with new effbuff len %d, " - "rx.avail_in=%d, TOTAL RX since FIN %lu\n", - __func__, pmdrx->eb_out.len, priv->rx.avail_in, - (unsigned long)priv->count_rx_between_fin); - - if (was_fin) { - lwsl_ext("%s: was_fin\n", __func__); - priv->count_rx_between_fin = 0; - if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) { - lwsl_ext("PMD_SERVER_NO_CONTEXT_TAKEOVER\n"); - (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->vhost->listen_port <= 0)], - priv->args[PMD_MEM_LEVEL], - Z_DEFAULT_STRATEGY); - if (n != Z_OK) { - lwsl_ext("inflateInit2 failed %d\n", n); - return PMDR_FAILED; - } - priv->tx_init = 1; - } - - if (!priv->buf_tx_deflated) - priv->buf_tx_deflated = lws_malloc(LWS_PRE + 7 + 5 + - (1 << priv->args[PMD_TX_BUF_PWR2]), - "pmd tx deflate buf"); - if (!priv->buf_tx_deflated) { - lwsl_err("%s: OOM\n", __func__); - return 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 += pmdrx->eb_in.len; - lwsl_ext("%s: TX: eb_in length %d, " - "TOTAL TX since FIN: %d\n", __func__, - pmdrx->eb_in.len, - (int)priv->count_tx_between_fin); - priv->tx.next_in = (unsigned char *)pmdrx->eb_in.token; - priv->tx.avail_in = 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 = 1 << priv->args[PMD_TX_BUF_PWR2]; - - pen = penbits = 0; - deflatePending(&priv->tx, &pen, &penbits); - pen |= penbits; - - if (!priv->tx.avail_in && (len & LWS_WRITE_NO_FIN)) { - lwsl_ext("%s: no available in, pen: %u\n", __func__, pen); - - if (!pen) - return PMDR_DID_NOTHING; - } - - m = Z_NO_FLUSH; - if (!(len & LWS_WRITE_NO_FIN)) { - lwsl_ext("%s: deflate with SYNC_FLUSH, pkt len %d\n", - __func__, (int)wsi->ws->rx_packet_length); - m = Z_SYNC_FLUSH; - } - - n = deflate(&priv->tx, m); - if (n == Z_STREAM_ERROR) { - lwsl_notice("%s: Z_STREAM_ERROR\n", __func__); - return PMDR_FAILED; - } - - pen = (!priv->tx.avail_out) && n != Z_STREAM_END; - - lwsl_ext("%s: deflate ret %d, len 0x%x\n", __func__, 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_err("%s: FAIL want to trim out length %d\n", - __func__, (int)pmdrx->eb_out.len); - assert(0); - } - - if (!(len & LWS_WRITE_NO_FIN) && - m == Z_SYNC_FLUSH && - !pen && - pmdrx->eb_out.len >= 4) { - // lwsl_err("%s: Trimming 4 from end of write\n", __func__); - 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 + - (pmdrx->eb_in.len - priv->tx.avail_in); - pmdrx->eb_in.len = priv->tx.avail_in; - - priv->compressed_out = 1; - pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out, - pmdrx->eb_out.token); - - lwsl_ext(" TX rewritten with new eb_in len %d, " - "eb_out len %d, deflatePending %d\n", - 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 = ((*pmdrx->eb_in.token) & ~0xf) | - (priv->tx_first_frame_type & 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_ext("%s: PRESEND compressed: ws frame 0x%02X, len %d\n", - __func__, ((*pmdrx->eb_in.token) & 0xff), - pmdrx->eb_in.len); - - if (((*pmdrx->eb_in.token) & 0x80) && /* fin */ - priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) { - lwsl_debug("PMD_CLIENT_NO_CONTEXT_TAKEOVER\n"); - (void)deflateEnd(&priv->tx); - priv->tx_init = 0; - } - - break; - - default: - break; - } - - return 0; -} - diff -Nru libwebsockets-4.0.20/lib/roles/ws/ext/extension-permessage-deflate.h libwebsockets-2.4.2/lib/roles/ws/ext/extension-permessage-deflate.h --- libwebsockets-4.0.20/lib/roles/ws/ext/extension-permessage-deflate.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/ws/ext/extension-permessage-deflate.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 -#else -#include -#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; -}; - diff -Nru libwebsockets-4.0.20/lib/roles/ws/ops-ws.c libwebsockets-2.4.2/lib/roles/ws/ops-ws.c --- libwebsockets-4.0.20/lib/roles/ws/ops-ws.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/ws/ops-ws.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2077 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); } - -/* - * client-parser.c: lws_ws_client_rx_sm() needs to be roughly kept in - * sync with changes here, esp related to ext draining - */ - -int -lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c) -{ - int callback_action = LWS_CALLBACK_RECEIVE; - struct lws_ext_pm_deflate_rx_ebufs pmdrx; - unsigned short close_code; - unsigned char *pp; - int ret = 0; - int n = 0; -#if !defined(LWS_WITHOUT_EXTENSIONS) - int rx_draining_ext = 0; - int lin; -#endif - - pmdrx.eb_in.token = NULL; - pmdrx.eb_in.len = 0; - pmdrx.eb_out.token = NULL; - pmdrx.eb_out.len = 0; - - if (wsi->socket_is_permanently_unusable) - return -1; - - switch (wsi->lws_rx_parse_state) { - case LWS_RXPS_NEW: -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->rx_draining_ext) { - pmdrx.eb_in.token = NULL; - pmdrx.eb_in.len = 0; - pmdrx.eb_out.token = NULL; - pmdrx.eb_out.len = 0; - lws_remove_wsi_from_draining_ext_list(wsi); - rx_draining_ext = 1; - lwsl_debug("%s: doing draining flow\n", __func__); - - goto drain_extension; - } -#endif - switch (wsi->ws->ietf_spec_revision) { - case 13: - /* - * no prepended frame key any more - */ - wsi->ws->all_zero_nonce = 1; - goto handle_first; - - default: - lwsl_warn("lws_ws_rx_sm: unknown spec version %d\n", - wsi->ws->ietf_spec_revision); - break; - } - break; - case LWS_RXPS_04_mask_1: - wsi->ws->mask[1] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2; - break; - case LWS_RXPS_04_mask_2: - wsi->ws->mask[2] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3; - break; - case LWS_RXPS_04_mask_3: - wsi->ws->mask[3] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - - /* - * start from the zero'th byte in the XOR key buffer since - * this is the start of a frame with a new key - */ - - wsi->ws->mask_idx = 0; - - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1; - break; - - /* - * 04 logical framing from the spec (all this is masked when incoming - * and has to be unmasked) - * - * We ignore the possibility of extension data because we don't - * negotiate any extensions at the moment. - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-------+-+-------------+-------------------------------+ - * |F|R|R|R| opcode|R| Payload len | Extended payload length | - * |I|S|S|S| (4) |S| (7) | (16/63) | - * |N|V|V|V| |V| | (if payload len==126/127) | - * | |1|2|3| |4| | | - * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - * | Extended payload length continued, if payload len == 127 | - * + - - - - - - - - - - - - - - - +-------------------------------+ - * | | Extension data | - * +-------------------------------+ - - - - - - - - - - - - - - - + - * : : - * +---------------------------------------------------------------+ - * : Application data : - * +---------------------------------------------------------------+ - * - * We pass payload through to userland as soon as we get it, ignoring - * FIN. It's up to userland to buffer it up if it wants to see a - * whole unfragmented block of the original size (which may be up to - * 2^63 long!) - */ - - case LWS_RXPS_04_FRAME_HDR_1: -handle_first: - - wsi->ws->opcode = c & 0xf; - wsi->ws->rsv = c & 0x70; - wsi->ws->final = !!((c >> 7) & 1); - wsi->ws->defeat_check_utf8 = 0; - - if (((wsi->ws->opcode) & 8) && !wsi->ws->final) { - lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"frag ctl", 8); - return -1; - } - - switch (wsi->ws->opcode) { - case LWSWSOPC_TEXT_FRAME: - wsi->ws->check_utf8 = lws_check_opt( - wsi->context->options, - LWS_SERVER_OPTION_VALIDATE_UTF8); - /* fallthru */ - case LWSWSOPC_BINARY_FRAME: - if (wsi->ws->opcode == LWSWSOPC_BINARY_FRAME) - wsi->ws->check_utf8 = 0; - if (wsi->ws->continuation_possible) { - lws_close_reason(wsi, - LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"bad cont", 8); - return -1; - } - 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->frame_is_binary = - wsi->ws->opcode == LWSWSOPC_BINARY_FRAME; - wsi->ws->first_fragment = 1; - wsi->ws->continuation_possible = !wsi->ws->final; - break; - case LWSWSOPC_CONTINUATION: - if (!wsi->ws->continuation_possible) { - lws_close_reason(wsi, - LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"bad cont", 8); - return -1; - } - 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: - lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"bad opc", 7); - lwsl_info("illegal opcode\n"); - return -1; - } - - if (wsi->ws->owed_a_fin && - (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME || - wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) { - lwsl_info("hey you owed us a FIN\n"); - lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"bad fin", 7); - return -1; - } - if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) { - wsi->ws->continuation_possible = 0; - wsi->ws->owed_a_fin = 0; - } - - if (!wsi->ws->final) - wsi->ws->owed_a_fin = 1; - - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN; - if (wsi->ws->rsv && - ( -#if !defined(LWS_WITHOUT_EXTENSIONS) - !wsi->ws->count_act_ext || -#endif - (wsi->ws->rsv & ~0x40))) { - lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"rsv bits", 8); - return -1; - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN: - - wsi->ws->this_frame_masked = !!(c & 0x80); - - 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 = 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 { - wsi->lws_rx_parse_state = - LWS_RXPS_WS_FRAME_PAYLOAD; - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_8: - if (c & 0x80) { - lwsl_warn("b63 of length must be zero\n"); - /* 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 - wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD; - 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; - wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD; - wsi->ws->mask_idx = 0; - if (wsi->ws->rx_packet_length == 0) { - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - break; - - - case LWS_RXPS_WS_FRAME_PAYLOAD: - assert(wsi->ws->rx_ubuf); - - if (wsi->ws->rx_ubuf_head + LWS_PRE >= wsi->ws->rx_ubuf_alloc) { - lwsl_err("Attempted overflow \n"); - return -1; - } - if (!(already_processed & ALREADY_PROCESSED_IGNORE_CHAR)) { - if (wsi->ws->all_zero_nonce) - wsi->ws->rx_ubuf[LWS_PRE + - (wsi->ws->rx_ubuf_head++)] = c; - else - wsi->ws->rx_ubuf[LWS_PRE + - (wsi->ws->rx_ubuf_head++)] = - c ^ wsi->ws->mask[(wsi->ws->mask_idx++) & 3]; - - --wsi->ws->rx_packet_length; - } - - if (!wsi->ws->rx_packet_length) { - lwsl_debug("%s: ws fragment length exhausted\n", - __func__); - /* spill because we have the whole frame */ - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->rx_draining_ext) { - lwsl_debug("%s: UNTIL_EXHAUSTED draining\n", __func__); - goto drain_extension; - } -#endif - /* - * if there's no protocol max frame size given, we are - * supposed to default to context->pt_serv_buf_size - */ - if (!wsi->protocol->rx_buffer_size && - wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size) - break; - - if (wsi->protocol->rx_buffer_size && - wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size) - break; - - /* spill because we filled our rx buffer */ -spill: - /* - * 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 - */ - - lwsl_parser("spill on %s\n", wsi->protocol->name); - - switch (wsi->ws->opcode) { - case LWSWSOPC_CLOSE: - - if (wsi->ws->peer_has_sent_close) - break; - - wsi->ws->peer_has_sent_close = 1; - - pp = &wsi->ws->rx_ubuf[LWS_PRE]; - if (lws_check_opt(wsi->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_parser("seen client close ack\n"); - return -1; - } - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE) - /* if he sends us 2 CLOSE, kill him */ - return -1; - - if (lws_partial_buffered(wsi)) { - /* - * if we're in the middle of something, - * we can't do a normal close response and - * have to just close our end. - */ - wsi->socket_is_permanently_unusable = 1; - lwsl_parser("Closing on peer close " - "due to pending tx\n"); - return -1; - } - - if (wsi->ws->rx_ubuf_head >= 2) { - close_code = (pp[0] << 8) | pp[1]; - if (close_code < 1000 || - close_code == 1004 || - close_code == 1005 || - close_code == 1006 || - close_code == 1012 || - close_code == 1013 || - close_code == 1014 || - close_code == 1015 || - (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->protocol->callback, wsi, - LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, - wsi->user_space, - &wsi->ws->rx_ubuf[LWS_PRE], - wsi->ws->rx_ubuf_head)) - return -1; - - lwsl_parser("server sees client close packet\n"); - lwsi_set_state(wsi, LRS_RETURNED_CLOSE); - /* deal with the close packet contents as a PONG */ - wsi->ws->payload_is_close = 1; - goto process_as_ping; - - case LWSWSOPC_PING: - lwsl_info("received %d byte ping, sending pong\n", - (int)wsi->ws->rx_ubuf_head); - - if (wsi->ws->ping_pending_flag) { - /* - * there is already a pending ping payload - * we should just log and drop - */ - lwsl_parser("DROP PING since one pending\n"); - goto ping_drop; - } -process_as_ping: - /* control packets can only be < 128 bytes long */ - if (wsi->ws->rx_ubuf_head > 128 - 3) { - lwsl_parser("DROP PING payload too large\n"); - goto ping_drop; - } - - /* stash the pong payload */ - memcpy(wsi->ws->ping_payload_buf + LWS_PRE, - &wsi->ws->rx_ubuf[LWS_PRE], - wsi->ws->rx_ubuf_head); - - wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head; - wsi->ws->ping_pending_flag = 1; - - /* get it sent as soon as possible */ - lws_callback_on_writable(wsi); -ping_drop: - wsi->ws->rx_ubuf_head = 0; - return 0; - - case LWSWSOPC_PONG: - lwsl_info("received pong\n"); - lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE], - wsi->ws->rx_ubuf_head); - - lws_validity_confirmed(wsi); - - /* issue it */ - callback_action = LWS_CALLBACK_RECEIVE_PONG; - break; - - case LWSWSOPC_TEXT_FRAME: - case LWSWSOPC_BINARY_FRAME: - case LWSWSOPC_CONTINUATION: - break; - - default: - lwsl_parser("unknown opc %x\n", wsi->ws->opcode); - - 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. - */ - - pmdrx.eb_in.token = &wsi->ws->rx_ubuf[LWS_PRE]; - pmdrx.eb_in.len = wsi->ws->rx_ubuf_head; - - /* for the non-pm-deflate case */ - - pmdrx.eb_out = pmdrx.eb_in; - - if (wsi->ws->opcode == LWSWSOPC_PONG && !pmdrx.eb_in.len) - goto already_done; -#if !defined(LWS_WITHOUT_EXTENSIONS) -drain_extension: -#endif - - do { - -// lwsl_notice("%s: pmdrx.eb_in.len: %d\n", __func__, -// (int)pmdrx.eb_in.len); - - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE || - lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) - goto already_done; - - n = PMDR_DID_NOTHING; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - lin = pmdrx.eb_in.len; - //if (lin) - // lwsl_hexdump_notice(ebuf.token, ebuf.len); - lwsl_ext("%s: +++ passing %d %p to ext\n", __func__, - pmdrx.eb_in.len, pmdrx.eb_in.token); - - n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &pmdrx, 0); - lwsl_debug("%s: ext says %d / ebuf.len %d\n", __func__, - n, pmdrx.eb_out.len); - if (wsi->ws->rx_draining_ext) - already_processed &= ~ALREADY_PROCESSED_NO_CB; -#endif - - /* - * ebuf may be pointing somewhere completely different - * now, it's the output - */ -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (n < 0) { - /* - * we may rely on this to get RX, just drop - * connection - */ - wsi->socket_is_permanently_unusable = 1; - return -1; - } - if (n == PMDR_DID_NOTHING) - break; -#endif - lwsl_debug("%s: post ext ret %d, ebuf in %d / out %d\n", - __func__, n, pmdrx.eb_in.len, - pmdrx.eb_out.len); - -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (rx_draining_ext && !pmdrx.eb_out.len) { - lwsl_debug(" --- ending drain on 0 read\n"); - goto already_done; - } - - if (n == PMDR_HAS_PENDING) - /* - * extension had more... - * main loop will come back - */ - lws_add_wsi_to_draining_ext_list(wsi); - else - lws_remove_wsi_from_draining_ext_list(wsi); - - rx_draining_ext = wsi->ws->rx_draining_ext; -#endif - - if (pmdrx.eb_out.len && - wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) { - if (lws_check_utf8(&wsi->ws->utf8, - pmdrx.eb_out.token, - 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_info("FINAL utf8 error\n"); - lws_close_reason(wsi, - LWS_CLOSE_STATUS_INVALID_PAYLOAD, - (uint8_t *)"partial utf8", 12); -utf8_fail: - lwsl_notice("utf8 error\n"); - lwsl_hexdump_notice(pmdrx.eb_out.token, - pmdrx.eb_out.len); - - return -1; - } - } - - /* if pmd not enabled, in == out */ - - if (n == PMDR_DID_NOTHING -#if !defined(LWS_WITHOUT_EXTENSIONS) - || - n == PMDR_UNKNOWN -#endif - ) - pmdrx.eb_in.len -= pmdrx.eb_out.len; - - if (!wsi->wsistate_pre_close && - (pmdrx.eb_out.len >= 0 || - callback_action == LWS_CALLBACK_RECEIVE_PONG || - n == PMDR_EMPTY_FINAL)) { - if (pmdrx.eb_out.len) - pmdrx.eb_out.token[pmdrx.eb_out.len] = '\0'; - - if (wsi->protocol->callback && - !(already_processed & ALREADY_PROCESSED_NO_CB)) { - if (callback_action == - LWS_CALLBACK_RECEIVE_PONG) - lwsl_info("Doing pong callback\n"); - - ret = user_callback_handle_rxflow( - wsi->protocol->callback, wsi, - (enum lws_callback_reasons) - callback_action, - wsi->user_space, - pmdrx.eb_out.token, - pmdrx.eb_out.len); - } - wsi->ws->first_fragment = 0; - } - -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (!lin) - break; -#endif - - } while (pmdrx.eb_in.len -#if !defined(LWS_WITHOUT_EXTENSIONS) - || rx_draining_ext -#endif - ); - -already_done: - wsi->ws->rx_ubuf_head = 0; - break; - } - - return ret; - -illegal_ctl_length: - - lwsl_warn("Control frame with xtended length is illegal\n"); - /* kill the connection */ - return -1; -} - - -size_t -lws_remaining_packet_payload(struct lws *wsi) -{ - return wsi->ws->rx_packet_length; -} - -int lws_frame_is_binary(struct lws *wsi) -{ - return wsi->ws->frame_is_binary; -} - -void -lws_add_wsi_to_draining_ext_list(struct lws *wsi) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - if (wsi->ws->rx_draining_ext) - return; - - lwsl_debug("%s: RX EXT DRAINING: Adding to list\n", __func__); - - wsi->ws->rx_draining_ext = 1; - wsi->ws->rx_draining_ext_list = pt->ws.rx_draining_ext_list; - pt->ws.rx_draining_ext_list = wsi; -#endif -} - -void -lws_remove_wsi_from_draining_ext_list(struct lws *wsi) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct lws **w = &pt->ws.rx_draining_ext_list; - - if (!wsi->ws->rx_draining_ext) - return; - - lwsl_debug("%s: RX EXT DRAINING: Removing from list\n", __func__); - - wsi->ws->rx_draining_ext = 0; - - /* remove us from context draining ext list */ - while (*w) { - if (*w == wsi) { - /* if us, point it instead to who we were pointing to */ - *w = wsi->ws->rx_draining_ext_list; - break; - } - w = &((*w)->ws->rx_draining_ext_list); - } - wsi->ws->rx_draining_ext_list = NULL; -#endif -} - -static int -lws_0405_frame_mask_generate(struct lws *wsi) -{ - size_t n; - /* fetch the per-frame nonce */ - - n = lws_get_random(lws_get_context(wsi), wsi->ws->mask, 4); - if (n != 4) { - lwsl_parser("Unable to read from random device %s %d\n", - SYSTEM_RANDOM_FILEPATH, (int)n); - return 1; - } - - /* start masking from first byte of masking key buffer */ - wsi->ws->mask_idx = 0; - - return 0; -} - -int -lws_server_init_wsi_for_ws(struct lws *wsi) -{ - int n; - - lwsi_set_state(wsi, LRS_ESTABLISHED); - - /* - * create the frame buffer for this connection according to the - * size mentioned in the protocol definition. If 0 there, use - * a big default for compatibility - */ - - n = (int)wsi->protocol->rx_buffer_size; - if (!n) - n = wsi->context->pt_serv_buf_size; - n += LWS_PRE; - wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "rx_ubuf"); - if (!wsi->ws->rx_ubuf) { - lwsl_err("Out of Mem allocating rx buffer %d\n", n); - return 1; - } - wsi->ws->rx_ubuf_alloc = n; - - /* notify user code that we're ready to roll */ - - if (wsi->protocol->callback) - if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED, - wsi->user_space, -#ifdef LWS_WITH_TLS - wsi->tls.ssl, -#else - NULL, -#endif - wsi->h2_stream_carries_ws)) - return 1; - - lws_validity_confirmed(wsi); - lwsl_debug("ws established\n"); - - return 0; -} - - - -int -lws_is_final_fragment(struct lws *wsi) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) - lwsl_debug("%s: final %d, rx pk length %ld, draining %ld\n", __func__, - wsi->ws->final, (long)wsi->ws->rx_packet_length, - (long)wsi->ws->rx_draining_ext); - return wsi->ws->final && !wsi->ws->rx_packet_length && - !wsi->ws->rx_draining_ext; -#else - return wsi->ws->final && !wsi->ws->rx_packet_length; -#endif -} - -int -lws_is_first_fragment(struct lws *wsi) -{ - return wsi->ws->first_fragment; -} - -unsigned char -lws_get_reserved_bits(struct lws *wsi) -{ - return wsi->ws->rsv; -} - -int -lws_get_close_length(struct lws *wsi) -{ - return wsi->ws->close_in_ping_buffer_len; -} - -unsigned char * -lws_get_close_payload(struct lws *wsi) -{ - return &wsi->ws->ping_payload_buf[LWS_PRE]; -} - -void -lws_close_reason(struct lws *wsi, enum lws_close_status status, - unsigned char *buf, size_t len) -{ - unsigned char *p, *start; - int budget = sizeof(wsi->ws->ping_payload_buf) - LWS_PRE; - - assert(lwsi_role_ws(wsi)); - - start = p = &wsi->ws->ping_payload_buf[LWS_PRE]; - - *p++ = (((int)status) >> 8) & 0xff; - *p++ = ((int)status) & 0xff; - - if (buf) - while (len-- && p < start + budget) - *p++ = *buf++; - - wsi->ws->close_in_ping_buffer_len = lws_ptr_diff(p, start); -} - -static int -lws_is_ws_with_ext(struct lws *wsi) -{ -#if defined(LWS_WITHOUT_EXTENSIONS) - return 0; -#else - return lwsi_role_ws(wsi) && !!wsi->ws->count_act_ext; -#endif -} - -static int -rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_pollfd *pollfd) -{ - unsigned int pending = 0; - struct lws_tokens ebuf; - char buffered = 0; - int n = 0, m; -#if defined(LWS_WITH_HTTP2) - struct lws *wsi1; -#endif - - if (!wsi->ws) { - lwsl_err("ws role wsi with no ws\n"); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - // lwsl_notice("%s: %s\n", __func__, wsi->protocol->name); - - //lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__, - // wsi->wsistate, pollfd->revents & LWS_POLLOUT); - - /* - * something went wrong with parsing the handshake, and - * we ended up back in the event loop without completing it - */ - if (lwsi_state(wsi) == LRS_PRE_WS_SERVING_ACCEPT) { - wsi->socket_is_permanently_unusable = 1; - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - ebuf.token = NULL; - ebuf.len = 0; - - if (lwsi_state(wsi) == LRS_WAITING_CONNECT) { -#if defined(LWS_WITH_CLIENT) - if ((pollfd->revents & LWS_POLLOUT) && - lws_handle_POLLOUT_event(wsi, pollfd)) { - lwsl_debug("POLLOUT event closed it\n"); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - n = lws_client_socket_service(wsi, pollfd); - 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; - } - - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE || - lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) { - /* - * we stopped caring about anything except control - * packets. Force flow control off, defeat tx - * draining. - */ - lws_rx_flow_control(wsi, 1); -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws) - wsi->ws->tx_draining_ext = 0; -#endif - } -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->tx_draining_ext) { - lws_handle_POLLOUT_event(wsi, pollfd); - //lwsl_notice("%s: tx drain\n", __func__); - /* - * We cannot deal with new RX until the TX ext path has - * been drained. It's because new rx will, eg, crap on - * the wsi rx buf that may be needed to retain state. - * - * TX ext drain path MUST go through event loop to avoid - * blocking. - */ - lws_callback_on_writable(wsi); - return LWS_HPI_RET_HANDLED; - } -#endif - if ((pollfd->revents & LWS_POLLIN) && lws_is_flowcontrolled(wsi)) { - /* We cannot deal with any kind of new RX because we are - * RX-flowcontrolled. - */ - lwsl_info("%s: flowcontrolled, ignoring rx\n", __func__); - - if (__lws_change_pollfd(wsi, LWS_POLLIN, 0)) - return -1; - - return LWS_HPI_RET_HANDLED; - } - - if (lws_is_flowcontrolled(wsi)) - return LWS_HPI_RET_HANDLED; - -#if defined(LWS_WITH_HTTP2) - if (wsi->mux_substream || wsi->upgraded_to_http2) { - wsi1 = lws_get_network_wsi(wsi); - if (wsi1 && lws_has_buffered_out(wsi1)) - /* We cannot deal with any kind of new RX - * because we are dealing with a partial send - * (new RX may trigger new http_action() that - * expect to be able to send) - */ - return LWS_HPI_RET_HANDLED; - } -#endif - -#if !defined(LWS_WITHOUT_EXTENSIONS) - /* 2: RX Extension needs to be drained - */ - - if (wsi->ws->rx_draining_ext) { - - lwsl_debug("%s: RX EXT DRAINING: Service\n", __func__); -#if defined(LWS_WITH_CLIENT) - if (lwsi_role_client(wsi)) { - n = lws_ws_client_rx_sm(wsi, 0); - if (n < 0) - /* we closed wsi */ - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } else -#endif - n = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0); - - return LWS_HPI_RET_HANDLED; - } - - if (wsi->ws->rx_draining_ext) - /* - * We have RX EXT content to drain, but can't do it - * right now. That means we cannot do anything lower - * priority either. - */ - return LWS_HPI_RET_HANDLED; -#endif - - /* 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) && !wsi->http.ah) - 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 && - lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK && - lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS))) { - /* - * 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; - if (lwsi_role_ws(wsi)) - ebuf.len = wsi->ws->rx_ubuf_alloc; - else - ebuf.len = wsi->context->pt_serv_buf_size; - - if ((unsigned int)ebuf.len > wsi->context->pt_serv_buf_size) - ebuf.len = wsi->context->pt_serv_buf_size; - - if ((int)pending > ebuf.len) - pending = ebuf.len; - - ebuf.len = lws_ssl_capable_read(wsi, ebuf.token, - pending ? (int)pending : - 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: - - /* - * give any active extensions a chance to munge the buffer - * before parse. We pass in a pointer to an lws_tokens struct - * prepared with the default buffer and content length that's in - * there. Rather than rewrite the default buffer, extensions - * that expect to grow the buffer can adapt .token to - * point to their own per-connection buffer in the extension - * user allocation. By default with no extensions or no - * extension callback handling, just the normal input buffer is - * used then so it is efficient. - */ - m = 0; - do { - - /* service incoming data */ - //lws_buflist_describe(&wsi->buflist, wsi, __func__); - if (ebuf.len) { -#if defined(LWS_ROLE_H2) - if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY && - lwsi_state(wsi) != LRS_DISCARD_BODY) - n = lws_read_h2(wsi, ebuf.token, - ebuf.len); - else -#endif - n = lws_read_h1(wsi, ebuf.token, - ebuf.len); - - if (n < 0) { - /* we closed wsi */ - return LWS_HPI_RET_WSI_ALREADY_DIED; - } - //lws_buflist_describe(&wsi->buflist, wsi, __func__); - //lwsl_notice("%s: consuming %d / %d\n", __func__, n, ebuf.len); - if (lws_buflist_aware_finished_consuming(wsi, &ebuf, n, - buffered, __func__)) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - ebuf.token = NULL; - ebuf.len = 0; - } while (m); - - if (wsi->http.ah -#if defined(LWS_WITH_CLIENT) - && !wsi->client_h2_alpn -#endif - ) { - lwsl_info("%s: %p: detaching ah\n", __func__, wsi); - lws_header_table_detach(wsi, 0); - } - - pending = lws_ssl_pending(wsi); - if (pending) { - if (lws_is_ws_with_ext(wsi)) - pending = pending > wsi->ws->rx_ubuf_alloc ? - wsi->ws->rx_ubuf_alloc : pending; - else - pending = pending > wsi->context->pt_serv_buf_size ? - wsi->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: %p flow buf: drained\n", __func__, 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; -} - - -int rops_handle_POLLOUT_ws(struct lws *wsi) -{ - int write_type = LWS_WRITE_PONG; -#if !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_ext_pm_deflate_rx_ebufs pmdrx; - int ret, m; -#endif - int n; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - lwsl_debug("%s: %s: wsi->ws->tx_draining_ext %d\n", __func__, - wsi->protocol->name, wsi->ws->tx_draining_ext); -#endif - - /* Priority 3: pending control packets (pong or close) - * - * 3a: close notification packet requested from close api - */ - - if (lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) { - lwsl_debug("sending close packet\n"); - lwsl_hexdump_debug(&wsi->ws->ping_payload_buf[LWS_PRE], - wsi->ws->close_in_ping_buffer_len); - wsi->waiting_to_send_close_frame = 0; - n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE], - wsi->ws->close_in_ping_buffer_len, - LWS_WRITE_CLOSE); - if (n >= 0) { - if (wsi->close_needs_ack) { - lwsi_set_state(wsi, LRS_AWAITING_CLOSE_ACK); - lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, - 5); - lwsl_debug("sent close, await ack\n"); - - return LWS_HP_RET_BAIL_OK; - } - wsi->close_needs_ack = 0; - lwsi_set_state(wsi, LRS_RETURNED_CLOSE); - } - - return LWS_HP_RET_BAIL_DIE; - } - - /* else, the send failed and we should just hang up */ - - if ((lwsi_role_ws(wsi) && wsi->ws->ping_pending_flag) || - (lwsi_state(wsi) == LRS_RETURNED_CLOSE && - wsi->ws->payload_is_close)) { - - if (wsi->ws->payload_is_close) - write_type = LWS_WRITE_CLOSE; - else { - if (wsi->wsistate_pre_close) { - /* we started close flow, forget pong */ - wsi->ws->ping_pending_flag = 0; - return LWS_HP_RET_BAIL_OK; - } - lwsl_info("issuing pong %d on wsi %p\n", - wsi->ws->ping_payload_len, wsi); - } - - n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE], - wsi->ws->ping_payload_len, write_type); - if (n < 0) - return LWS_HP_RET_BAIL_DIE; - - /* well he is sent, mark him done */ - wsi->ws->ping_pending_flag = 0; - if (wsi->ws->payload_is_close) { - // assert(0); - /* oh... a close frame was it... then we are done */ - return LWS_HP_RET_BAIL_DIE; - } - - /* otherwise for PING, leave POLLOUT active either way */ - return LWS_HP_RET_BAIL_OK; - } - - if (!wsi->socket_is_permanently_unusable && - wsi->ws->send_check_ping) { - - lwsl_info("%s: issuing ping on wsi %p: %s %s h2: %d\n", __func__, wsi, - wsi->role_ops->name, wsi->protocol->name, - wsi->mux_substream); - wsi->ws->send_check_ping = 0; - n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE], - 0, LWS_WRITE_PING); - if (n < 0) - return LWS_HP_RET_BAIL_DIE; - - return LWS_HP_RET_BAIL_OK; - } - - /* Priority 4: if we are closing, not allowed to send more data frags - * which means user callback or tx ext flush banned now - */ - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE) - return LWS_HP_RET_USER_SERVICE; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - /* Priority 5: Tx path extension with more to send - * - * These are handled as new fragments each time around - * So while we must block new writeable callback to enforce - * payload ordering, but since they are always complete - * fragments control packets can interleave OK. - */ - if (wsi->ws->tx_draining_ext) { - lwsl_ext("SERVICING TX EXT DRAINING\n"); - if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0) - return LWS_HP_RET_BAIL_DIE; - /* leave POLLOUT active */ - return LWS_HP_RET_BAIL_OK; - } - - /* Priority 6: extensions - */ - if (!wsi->ws->extension_data_pending && !wsi->ws->tx_draining_ext) { - lwsl_ext("%s: !wsi->ws->extension_data_pending\n", __func__); - return LWS_HP_RET_USER_SERVICE; - } - - /* - * Check in on the active extensions, see if they had pending stuff to - * spill... they need to get the first look-in otherwise sequence will - * be disordered. - * - * coming here with a NULL, zero-length ebuf means just spill pending - */ - - ret = 1; - if (wsi->role_ops == &role_ops_raw_skt -#if defined(LWS_ROLE_RAW_FILE) - || wsi->role_ops == &role_ops_raw_file -#endif - ) - ret = 0; - - while (ret == 1) { - - /* default to nobody has more to spill */ - - ret = 0; - pmdrx.eb_in.token = NULL; - pmdrx.eb_in.len = 0; - - /* give every extension a chance to spill */ - - m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND, - &pmdrx, 0); - if (m < 0) { - lwsl_err("ext reports fatal error\n"); - return LWS_HP_RET_BAIL_DIE; - } - if (m) - /* - * at least one extension told us he has more - * to spill, so we will go around again after - */ - ret = 1; - - /* assuming they gave us something to send, send it */ - - if (pmdrx.eb_in.len) { - n = lws_issue_raw(wsi, (unsigned char *)pmdrx.eb_in.token, - pmdrx.eb_in.len); - if (n < 0) { - lwsl_info("closing from POLLOUT spill\n"); - return LWS_HP_RET_BAIL_DIE; - } - /* - * Keep amount spilled small to minimize chance of this - */ - if (n != pmdrx.eb_in.len) { - lwsl_err("Unable to spill ext %d vs %d\n", - pmdrx.eb_in.len, n); - return LWS_HP_RET_BAIL_DIE; - } - } else - continue; - - /* no extension has more to spill */ - - if (!ret) - continue; - - /* - * There's more to spill from an extension, but we just sent - * something... did that leave the pipe choked? - */ - - if (!lws_send_pipe_choked(wsi)) - /* no we could add more */ - continue; - - lwsl_info("choked in POLLOUT service\n"); - - /* - * Yes, he's choked. Leave the POLLOUT masked on so we will - * come back here when he is unchoked. Don't call the user - * callback to enforce ordering of spilling, he'll get called - * when we come back here and there's nothing more to spill. - */ - - return LWS_HP_RET_BAIL_OK; - } - - wsi->ws->extension_data_pending = 0; -#endif - - return LWS_HP_RET_USER_SERVICE; -} - -static int -rops_service_flag_pending_ws(struct lws_context *context, int tsi) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_context_per_thread *pt = &context->pt[tsi]; - struct lws *wsi; - int forced = 0; - - /* POLLIN faking (the pt lock is taken by the parent) */ - - /* - * 1) For all guys with already-available ext data to drain, if they are - * not flowcontrolled, fake their POLLIN status - */ - wsi = pt->ws.rx_draining_ext_list; - while (wsi && wsi->position_in_fds_table != LWS_NO_FDS_POS) { - pt->fds[wsi->position_in_fds_table].revents |= - pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; - if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) - forced = 1; - - wsi = wsi->ws->rx_draining_ext_list; - } - - return forced; -#else - return 0; -#endif -} - -static int -rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason) -{ - if (!wsi->ws) - return 0; - - if (!wsi->ws->close_in_ping_buffer_len && /* already a reason */ - (reason == LWS_CLOSE_STATUS_NOSTATUS || - reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)) - return 0; - - lwsl_debug("%s: sending close indication...\n", __func__); - - /* if no prepared close reason, use 1000 and no aux data */ - - if (!wsi->ws->close_in_ping_buffer_len) { - wsi->ws->close_in_ping_buffer_len = 2; - wsi->ws->ping_payload_buf[LWS_PRE] = (reason >> 8) & 0xff; - wsi->ws->ping_payload_buf[LWS_PRE + 1] = reason & 0xff; - } - - wsi->waiting_to_send_close_frame = 1; - wsi->close_needs_ack = 1; - lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE); - __lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 5); - - lws_callback_on_writable(wsi); - - return 1; -} - -static int -rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi) -{ - if (!wsi->ws) - return 0; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - - if (wsi->ws->rx_draining_ext) { - struct lws **w = &pt->ws.rx_draining_ext_list; - - wsi->ws->rx_draining_ext = 0; - /* remove us from context draining ext list */ - while (*w) { - if (*w == wsi) { - *w = wsi->ws->rx_draining_ext_list; - break; - } - w = &((*w)->ws->rx_draining_ext_list); - } - wsi->ws->rx_draining_ext_list = NULL; - } - - if (wsi->ws->tx_draining_ext) { - struct lws **w = &pt->ws.tx_draining_ext_list; - lwsl_ext("%s: CLEARING tx_draining_ext\n", __func__); - wsi->ws->tx_draining_ext = 0; - /* remove us from context draining ext list */ - while (*w) { - if (*w == wsi) { - *w = wsi->ws->tx_draining_ext_list; - break; - } - w = &((*w)->ws->tx_draining_ext_list); - } - wsi->ws->tx_draining_ext_list = NULL; - } -#endif - lws_free_set_NULL(wsi->ws->rx_ubuf); - - wsi->ws->ping_payload_len = 0; - wsi->ws->ping_pending_flag = 0; - - /* deallocate any active extension contexts */ - - if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0) - lwsl_warn("extension destruction failed\n"); - - return 0; -} - -static int -rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len, - enum lws_write_protocol *wp) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - enum lws_write_protocol wpt; -#endif - struct lws_ext_pm_deflate_rx_ebufs pmdrx; - int masked7 = lwsi_role_client(wsi); - unsigned char is_masked_bit = 0; - unsigned char *dropmask = NULL; - size_t orig_len = len; - int pre = 0, n = 0; - - // lwsl_err("%s: wp 0x%x len %d\n", __func__, *wp, (int)len); -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->tx_draining_ext) { - /* remove us from the list */ - struct lws **w = &pt->ws.tx_draining_ext_list; - - lwsl_ext("%s: CLEARING tx_draining_ext\n", __func__); - wsi->ws->tx_draining_ext = 0; - /* remove us from context draining ext list */ - while (*w) { - if (*w == wsi) { - *w = wsi->ws->tx_draining_ext_list; - break; - } - w = &((*w)->ws->tx_draining_ext_list); - } - wsi->ws->tx_draining_ext_list = NULL; - - wpt = *wp; - *wp = (wsi->ws->tx_draining_stashed_wp & 0xc0) | - LWS_WRITE_CONTINUATION; - - /* - * When we are just flushing (len == 0), we can trust the - * stashed wp info completely. Otherwise adjust it to the - * FIN status of the incoming packet. - */ - - if (!(wpt & LWS_WRITE_NO_FIN) && len) - *wp &= ~LWS_WRITE_NO_FIN; - - lwsl_ext("FORCED draining wp to 0x%02X " - "(stashed 0x%02X, incoming 0x%02X)\n", *wp, - wsi->ws->tx_draining_stashed_wp, wpt); - // assert(0); - } -#endif - - if (((*wp) & 0x1f) == LWS_WRITE_HTTP || - ((*wp) & 0x1f) == LWS_WRITE_HTTP_FINAL || - ((*wp) & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION || - ((*wp) & 0x1f) == LWS_WRITE_HTTP_HEADERS) - goto send_raw; - - - - /* if we are continuing a frame that already had its header done */ - - if (wsi->ws->inside_frame) { - lwsl_debug("INSIDE FRAME\n"); - goto do_more_inside_frame; - } - - wsi->ws->clean_buffer = 1; - - /* - * give a chance to the extensions to modify payload - * the extension may decide to produce unlimited payload erratically - * (eg, compression extension), so we require only that if he produces - * something, it will be a complete fragment of the length known at - * the time (just the fragment length known), and if he has - * more we will come back next time he is writeable and allow him to - * produce more fragments until he's drained. - * - * This allows what is sent each time it is writeable to be limited to - * a size that can be sent without partial sends or blocking, allows - * interleaving of control frames and other connection service. - */ - - pmdrx.eb_in.token = buf; - pmdrx.eb_in.len = (int)len; - - /* for the non-pm-deflate case */ - - pmdrx.eb_out = pmdrx.eb_in; - - switch ((int)*wp) { - case LWS_WRITE_PING: - case LWS_WRITE_PONG: - case LWS_WRITE_CLOSE: - break; - default: -#if !defined(LWS_WITHOUT_EXTENSIONS) - n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &pmdrx, *wp); - if (n < 0) - return -1; - lwsl_ext("%s: defl ext ret %d, ext in remaining %d, " - "out %d compressed (wp 0x%x)\n", __func__, n, - (int)pmdrx.eb_in.len, (int)pmdrx.eb_out.len, *wp); - - if (n == PMDR_HAS_PENDING) { - lwsl_ext("%s: HAS PENDING: write drain len %d " - "(wp 0x%x) SETTING tx_draining_ext " - "(remaining in %d)\n", __func__, - (int)pmdrx.eb_out.len, *wp, - (int)pmdrx.eb_in.len); - /* extension requires further draining */ - wsi->ws->tx_draining_ext = 1; - wsi->ws->tx_draining_ext_list = - pt->ws.tx_draining_ext_list; - pt->ws.tx_draining_ext_list = wsi; - /* we must come back to do more */ - lws_callback_on_writable(wsi); - /* - * keep a copy of the write type for the overall - * action that has provoked generation of these - * fragments, so the last guy can use its FIN state. - */ - wsi->ws->tx_draining_stashed_wp = *wp; - /* - * Despite what we may have thought, this is definitely - * NOT the last fragment, because the extension asserted - * he has more coming. For example, the extension may - * be compressing, and has saved up everything until the - * end, where the output is larger than one chunk. - * - * Make sure this intermediate one doesn't actually - * go out with a FIN. - */ - *wp |= LWS_WRITE_NO_FIN; - } -#endif - if (pmdrx.eb_out.len && wsi->ws->stashed_write_pending) { - wsi->ws->stashed_write_pending = 0; - *wp = ((*wp) & 0xc0) | (int)wsi->ws->stashed_write_type; - } - } - - /* - * an extension did something we need to keep... for example, if - * compression extension, it has already updated its state according - * to this being issued - */ - if (buf != pmdrx.eb_out.token) { - /* - * ext might eat it, but not have anything to issue yet. - * In that case we have to follow his lead, but stash and - * replace the write type that was lost here the first time. - */ - if (len && !pmdrx.eb_out.len) { - if (!wsi->ws->stashed_write_pending) - wsi->ws->stashed_write_type = - (char)(*wp) & 0x3f; - wsi->ws->stashed_write_pending = 1; - return (int)len; - } - /* - * extension recreated it: - * need to buffer this if not all sent - */ - wsi->ws->clean_buffer = 0; - } - - buf = pmdrx.eb_out.token; - len = pmdrx.eb_out.len; - - if (!buf) { - lwsl_err("null buf (%d)\n", (int)len); - return -1; - } - - switch (wsi->ws->ietf_spec_revision) { - case 13: - if (masked7) { - pre += 4; - dropmask = &buf[0 - pre]; - is_masked_bit = 0x80; - } - - switch ((*wp) & 0xf) { - case LWS_WRITE_TEXT: - n = LWSWSOPC_TEXT_FRAME; - break; - case LWS_WRITE_BINARY: - n = LWSWSOPC_BINARY_FRAME; - break; - case LWS_WRITE_CONTINUATION: - n = LWSWSOPC_CONTINUATION; - break; - - case LWS_WRITE_CLOSE: - n = LWSWSOPC_CLOSE; - break; - case LWS_WRITE_PING: - n = LWSWSOPC_PING; - break; - case LWS_WRITE_PONG: - n = LWSWSOPC_PONG; - break; - default: - lwsl_warn("lws_write: unknown write opc / wp\n"); - return -1; - } - - if (!((*wp) & LWS_WRITE_NO_FIN)) - n |= 1 << 7; - - if (len < 126) { - pre += 2; - buf[-pre] = n; - buf[-pre + 1] = (unsigned char)(len | is_masked_bit); - } else { - if (len < 65536) { - pre += 4; - buf[-pre] = n; - buf[-pre + 1] = 126 | is_masked_bit; - buf[-pre + 2] = (unsigned char)(len >> 8); - buf[-pre + 3] = (unsigned char)len; - } else { - pre += 10; - buf[-pre] = n; - buf[-pre + 1] = 127 | is_masked_bit; -#if defined __LP64__ - buf[-pre + 2] = (len >> 56) & 0x7f; - buf[-pre + 3] = len >> 48; - buf[-pre + 4] = len >> 40; - buf[-pre + 5] = len >> 32; -#else - buf[-pre + 2] = 0; - buf[-pre + 3] = 0; - buf[-pre + 4] = 0; - buf[-pre + 5] = 0; -#endif - buf[-pre + 6] = (unsigned char)(len >> 24); - buf[-pre + 7] = (unsigned char)(len >> 16); - buf[-pre + 8] = (unsigned char)(len >> 8); - buf[-pre + 9] = (unsigned char)len; - } - } - break; - } - -do_more_inside_frame: - - /* - * Deal with masking if we are in client -> server direction and - * the wp demands it - */ - - if (masked7) { - if (!wsi->ws->inside_frame) - if (lws_0405_frame_mask_generate(wsi)) { - lwsl_err("frame mask generation failed\n"); - return -1; - } - - /* - * in v7, just mask the payload - */ - if (dropmask) { /* never set if already inside frame */ - for (n = 4; n < (int)len + 4; n++) - dropmask[n] = dropmask[n] ^ wsi->ws->mask[ - (wsi->ws->mask_idx++) & 3]; - - /* copy the frame nonce into place */ - memcpy(dropmask, wsi->ws->mask, 4); - } - } - - if (lwsi_role_h2_ENCAPSULATION(wsi)) { - struct lws *encap = lws_get_network_wsi(wsi); - - assert(encap != wsi); - return encap->role_ops->write_role_protocol(wsi, buf - pre, - len + pre, wp); - } - - switch ((*wp) & 0x1f) { - case LWS_WRITE_TEXT: - case LWS_WRITE_BINARY: - case LWS_WRITE_CONTINUATION: - if (!wsi->h2_stream_carries_ws) { - - /* - * give any active extensions a chance to munge the - * buffer before send. We pass in a pointer to an - * lws_tokens struct prepared with the default buffer - * and content length that's in there. Rather than - * rewrite the default buffer, extensions that expect - * to grow the buffer can adapt .token to point to their - * own per-connection buffer in the extension user - * allocation. By default with no extensions or no - * extension callback handling, just the normal input - * buffer is used then so it is efficient. - * - * callback returns 1 in case it wants to spill more - * buffers - * - * This takes care of holding the buffer if send is - * incomplete, ie, if wsi->ws->clean_buffer is 0 - * (meaning an extension meddled with the buffer). If - * wsi->ws->clean_buffer is 1, it will instead return - * to the user code how much OF THE USER BUFFER was - * consumed. - */ - - n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre); - wsi->ws->inside_frame = 1; - if (n <= 0) - return n; - - if (n == (int)len + pre) { - /* everything in the buffer was handled - * (or rebuffered...) */ - wsi->ws->inside_frame = 0; - return (int)orig_len; - } - - /* - * it is how many bytes of user buffer got sent... may - * be < orig_len in which case callback when writable - * has already been arranged and user code can call - * lws_write() again with the rest later. - */ - - return n - pre; - } - break; - default: - break; - } - -send_raw: - return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre); -} - -static int -rops_close_kill_connection_ws(struct lws *wsi, enum lws_close_status reason) -{ - /* deal with ws encapsulation in h2 */ -#if defined(LWS_WITH_HTTP2) - if (wsi->mux_substream && wsi->h2_stream_carries_ws) - return role_ops_h2.close_kill_connection(wsi, reason); - - return 0; -#else - return 0; -#endif -} - -static int -rops_callback_on_writable_ws(struct lws *wsi) -{ -#if defined(LWS_WITH_HTTP2) - if (lwsi_role_h2_ENCAPSULATION(wsi)) { - /* we know then that it has an h2 parent */ - struct lws *enc = role_ops_h2.encapsulation_parent(wsi); - - assert(enc); - if (enc->role_ops->callback_on_writable(wsi)) - return 1; - } -#endif - return 0; -} - -static int -rops_init_vhost_ws(struct lws_vhost *vh, - const struct lws_context_creation_info *info) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) -#ifdef LWS_WITH_PLUGINS - struct lws_plugin *plugin; - int m; - - if (vh->context->plugin_extension_count) { - - m = 0; - while (info->extensions && info->extensions[m].callback) - m++; - - /* - * give the vhost a unified list of extensions including the - * ones that came from plugins - */ - vh->ws.extensions = lws_zalloc(sizeof(struct lws_extension) * - (m + vh->context->plugin_extension_count + 1), - "extensions"); - if (!vh->ws.extensions) - return 1; - - memcpy((struct lws_extension *)vh->ws.extensions, info->extensions, - sizeof(struct lws_extension) * m); - plugin = vh->context->plugin_list; - while (plugin) { - memcpy((struct lws_extension *)&vh->ws.extensions[m], - plugin->caps.extensions, - sizeof(struct lws_extension) * - plugin->caps.count_extensions); - m += plugin->caps.count_extensions; - plugin = plugin->list; - } - } else -#endif - vh->ws.extensions = info->extensions; -#endif - - return 0; -} - -static int -rops_destroy_vhost_ws(struct lws_vhost *vh) -{ -#ifdef LWS_WITH_PLUGINS -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (vh->context->plugin_extension_count) - lws_free((void *)vh->ws.extensions); -#endif -#endif - - return 0; -} - -#if defined(LWS_WITH_HTTP_PROXY) -static int -ws_destroy_proxy_buf(struct lws_dll2 *d, void *user) -{ - lws_free(d); - - return 0; -} -#endif - -static int -rops_destroy_role_ws(struct lws *wsi) -{ -#if defined(LWS_WITH_HTTP_PROXY) - lws_dll2_foreach_safe(&wsi->ws->proxy_owner, NULL, ws_destroy_proxy_buf); -#endif - - lws_free_set_NULL(wsi->ws); - - return 0; -} - -static int -rops_issue_keepalive_ws(struct lws *wsi, int isvalid) -{ - uint64_t us; - -#if defined(LWS_WITH_HTTP2) - if (lwsi_role_h2_ENCAPSULATION(wsi)) { - /* we know then that it has an h2 parent */ - struct lws *enc = role_ops_h2.encapsulation_parent(wsi); - - assert(enc); - if (enc->role_ops->issue_keepalive(enc, isvalid)) - return 1; - } -#endif - - if (isvalid) - _lws_validity_confirmed_role(wsi); - else { - us = lws_now_usecs(); - memcpy(&wsi->ws->ping_payload_buf[LWS_PRE], &us, 8); - wsi->ws->send_check_ping = 1; - lws_callback_on_writable(wsi); - } - - return 0; -} - -const struct lws_role_ops role_ops_ws = { - /* role name */ "ws", - /* alpn id */ NULL, - /* check_upgrades */ NULL, - /* pt_init_destroy */ NULL, - /* init_vhost */ rops_init_vhost_ws, - /* destroy_vhost */ rops_destroy_vhost_ws, - /* service_flag_pending */ rops_service_flag_pending_ws, - /* handle_POLLIN */ rops_handle_POLLIN_ws, - /* handle_POLLOUT */ rops_handle_POLLOUT_ws, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ rops_callback_on_writable_ws, - /* tx_credit */ NULL, - /* write_role_protocol */ rops_write_role_protocol_ws, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ NULL, - /* close_via_role_protocol */ rops_close_via_role_protocol_ws, - /* close_role */ rops_close_role_ws, - /* close_kill_connection */ rops_close_kill_connection_ws, - /* destroy_role */ rops_destroy_role_ws, - /* adoption_bind */ NULL, - /* client_bind */ NULL, - /* issue_keepalive */ rops_issue_keepalive_ws, - /* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, - LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED }, - /* rx_cb clnt, srv */ { LWS_CALLBACK_CLIENT_RECEIVE, - LWS_CALLBACK_RECEIVE }, - /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_WRITEABLE, - LWS_CALLBACK_SERVER_WRITEABLE }, - /* close cb clnt, srv */ { LWS_CALLBACK_CLIENT_CLOSED, - LWS_CALLBACK_CLOSED }, - /* protocol_bind cb c, srv */ { LWS_CALLBACK_WS_CLIENT_BIND_PROTOCOL, - LWS_CALLBACK_WS_SERVER_BIND_PROTOCOL }, - /* protocol_unbind cb c, srv */ { LWS_CALLBACK_WS_CLIENT_DROP_PROTOCOL, - LWS_CALLBACK_WS_SERVER_DROP_PROTOCOL }, - /* file handles */ 0 -}; diff -Nru libwebsockets-4.0.20/lib/roles/ws/private-lib-roles-ws.h libwebsockets-2.4.2/lib/roles/ws/private-lib-roles-ws.h --- libwebsockets-4.0.20/lib/roles/ws/private-lib-roles-ws.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/ws/private-lib-roles-ws.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,195 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -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[128 - 3 + LWS_PRE]; - - unsigned int final:1; - unsigned int frame_is_binary:1; - unsigned int all_zero_nonce:1; - unsigned int this_frame_masked:1; - unsigned int inside_frame:1; /* next write will be more of frame */ - unsigned int clean_buffer:1; /* buffer not rewritten by extension */ - unsigned int payload_is_close:1; /* process as PONG, but it is close */ - unsigned int ping_pending_flag:1; - unsigned int continuation_possible:1; - unsigned int owed_a_fin:1; - unsigned int check_utf8:1; - unsigned int defeat_check_utf8:1; - unsigned int stashed_write_pending:1; - unsigned int send_check_ping:1; - unsigned int first_fragment:1; - unsigned int peer_has_sent_close:1; -#if !defined(LWS_WITHOUT_EXTENSIONS) - unsigned int extension_data_pending:1; - unsigned int rx_draining_ext:1; - unsigned int tx_draining_ext:1; - 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 ping_payload_len; - uint8_t mask_idx; - uint8_t opcode; - uint8_t rsv; - uint8_t rsv_first_msg; - /* zero if no info, or length including 2-byte close code */ - uint8_t close_in_ping_buffer_len; - uint8_t utf8; - uint8_t stashed_write_type; - uint8_t tx_draining_stashed_wp; - uint8_t ietf_spec_revision; -#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); -#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); diff -Nru libwebsockets-4.0.20/lib/roles/ws/server-ws.c libwebsockets-2.4.2/lib/roles/ws/server-ws.c --- libwebsockets-4.0.20/lib/roles/ws/server-ws.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/roles/ws/server-ws.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1023 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); } - -#if !defined(LWS_WITHOUT_EXTENSIONS) -static int -lws_extension_server_handshake(struct lws *wsi, char **p, int budget) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - char ext_name[64], *args, *end = (*p) + budget - 1; - const struct lws_ext_options *opts, *po; - const struct lws_extension *ext; - struct lws_ext_option_arg oa; - int n, m, more = 1; - int ext_count = 0; - char ignore; - char *c; - - /* - * Figure out which extensions the client has that we want to - * enable on this connection, and give him back the list - */ - if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) - return 0; - - /* - * break down the list of client extensions - * and go through them - */ - - if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size, - WSI_TOKEN_EXTENSIONS) < 0) - return 1; - - c = (char *)pt->serv_buf; - lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c); - wsi->ws->count_act_ext = 0; - ignore = 0; - n = 0; - args = NULL; - - /* - * We may get a simple request - * - * Sec-WebSocket-Extensions: permessage-deflate - * - * or an elaborated one with requested options - * - * Sec-WebSocket-Extensions: permessage-deflate; \ - * server_no_context_takeover; \ - * client_no_context_takeover - */ - - while (more) { - - if (c >= (char *)pt->serv_buf + 255) - return -1; - - if (*c && (*c != ',' && *c != '\t')) { - if (*c == ';') { - ignore = 1; - if (!args) - args = c + 1; - } - if (ignore || *c == ' ') { - c++; - continue; - } - ext_name[n] = *c++; - if (n < (int)sizeof(ext_name) - 1) - n++; - continue; - } - ext_name[n] = '\0'; - - ignore = 0; - if (!*c) - more = 0; - else { - c++; - if (!n) - continue; - } - - while (args && *args == ' ') - args++; - - /* check a client's extension against our support */ - - ext = wsi->vhost->ws.extensions; - - while (ext && ext->callback) { - - if (strcmp(ext_name, ext->name)) { - ext++; - continue; - } - - /* - * oh, we do support this one he asked for... but let's - * confirm he only gave it once - */ - for (m = 0; m < wsi->ws->count_act_ext; m++) - if (wsi->ws->active_extensions[m] == ext) { - lwsl_info("ext mentioned twice\n"); - return 1; /* shenanigans */ - } - - /* - * ask user code if it's OK to apply it on this - * particular connection + protocol - */ - m = (wsi->protocol->callback)(wsi, - LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, - wsi->user_space, ext_name, 0); - - /* - * zero return from callback means go ahead and allow - * the extension, it's what we get if the callback is - * unhandled - */ - if (m) { - ext++; - continue; - } - - /* apply it */ - - ext_count++; - - /* instantiate the extension on this conn */ - - wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext; - - /* allow him to construct his context */ - - if (ext->callback(lws_get_context(wsi), ext, wsi, - LWS_EXT_CB_CONSTRUCT, - (void *)&wsi->ws->act_ext_user[ - wsi->ws->count_act_ext], - (void *)&opts, 0)) { - lwsl_info("ext %s failed construction\n", - ext_name); - ext_count--; - ext++; - - continue; - } - - if (ext_count > 1) - *(*p)++ = ','; - else - LWS_CPYAPP(*p, - "\x0d\x0aSec-WebSocket-Extensions: "); - *p += lws_snprintf(*p, (end - *p), "%s", ext_name); - - /* - * The client may send a bunch of different option - * sets for the same extension, we are supposed to - * pick one we like the look of. The option sets are - * separated by comma. - * - * Actually we just either accept the first one or - * nothing. - * - * Go through the options trying to apply the - * recognized ones - */ - - lwsl_info("ext args %s\n", args); - - while (args && *args && *args != ',') { - while (*args == ' ') - args++; - po = opts; - while (po->name) { - /* only support arg-less options... */ - if (po->type != EXTARG_NONE || - strncmp(args, po->name, - strlen(po->name))) { - po++; - continue; - } - oa.option_name = NULL; - oa.option_index = (int)(po - opts); - oa.start = NULL; - oa.len = 0; - lwsl_info("setting '%s'\n", po->name); - if (!ext->callback(lws_get_context(wsi), - ext, wsi, - LWS_EXT_CB_OPTION_SET, - wsi->ws->act_ext_user[ - wsi->ws->count_act_ext], - &oa, (end - *p))) { - - *p += lws_snprintf(*p, - (end - *p), - "; %s", po->name); - lwsl_debug("adding option %s\n", - po->name); - } - po++; - } - while (*args && *args != ',' && *args != ';') - args++; - - if (*args == ';') - args++; - } - - wsi->ws->count_act_ext++; - lwsl_parser("cnt_act_ext <- %d\n", - wsi->ws->count_act_ext); - - if (args && *args == ',') - more = 0; - - ext++; - } - - n = 0; - args = NULL; - } - - return 0; -} -#endif - -int -lws_process_ws_upgrade2(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; -#if defined(LWS_WITH_HTTP_BASIC_AUTH) - const struct lws_protocol_vhost_options *pvos = NULL; - const char *ws_prot_basic_auth = NULL; - - - /* - * Allow basic auth a look-in now we bound the wsi to the protocol. - * - * For vhost ws basic auth, it is "basic-auth": "path" as usual but - * applied to the protocol's entry in the vhost's "ws-protocols": - * section, as a pvo. - */ - - pvos = lws_vhost_protocol_options(wsi->vhost, wsi->protocol->name); - if (pvos && pvos->options && - !lws_pvo_get_str((void *)pvos->options, "basic-auth", - &ws_prot_basic_auth)) { - lwsl_info("%s: ws upgrade requires basic auth\n", __func__); - switch (lws_check_basic_auth(wsi, ws_prot_basic_auth, LWSAUTHM_DEFAULT - /* no callback based auth here */)) { - case LCBA_CONTINUE: - break; - case LCBA_FAILED_AUTH: - return lws_unauthorised_basic_auth(wsi); - case LCBA_END_TRANSACTION: - lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); - return lws_http_transaction_completed(wsi); - } - } -#endif - - /* - * We are upgrading to ws, so http/1.1 + h2 and keepalive + pipelined - * header considerations about keeping the ah around no longer apply. - * - * However it's common for the first ws protocol data to have been - * coalesced with the browser upgrade request and to already be in the - * ah rx buffer. - */ - - lws_pt_lock(pt, __func__); - - if (!wsi->h2_stream_carries_ws) - lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, - &role_ops_ws); - - lws_pt_unlock(pt); - - /* allocate the ws struct for the wsi */ - - wsi->ws = lws_zalloc(sizeof(*wsi->ws), "ws struct"); - if (!wsi->ws) { - lwsl_notice("OOM\n"); - return 1; - } - - if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION)) - wsi->ws->ietf_spec_revision = - atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION)); - - /* allocate wsi->user storage */ - if (lws_ensure_user_space(wsi)) { - lwsl_notice("problem with user space\n"); - return 1; - } - - /* - * Give the user code a chance to study the request and - * have the opportunity to deny it - */ - if ((wsi->protocol->callback)(wsi, - LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, - wsi->user_space, - lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) { - lwsl_warn("User code denied connection\n"); - return 1; - } - - /* - * Perform the handshake according to the protocol version the - * client announced - */ - - switch (wsi->ws->ietf_spec_revision) { - default: - lwsl_notice("Unknown client spec version %d\n", - wsi->ws->ietf_spec_revision); - wsi->ws->ietf_spec_revision = 13; - //return 1; - /* fallthru */ - case 13: -#if defined(LWS_WITH_HTTP2) - if (wsi->h2_stream_carries_ws) { - if (lws_h2_ws_handshake(wsi)) { - lwsl_notice("h2 ws handshake failed\n"); - return 1; - } - lws_role_transition(wsi, - LWSIFR_SERVER | LWSIFR_P_ENCAP_H2, - LRS_ESTABLISHED, &role_ops_ws); - - /* - * There should be no validity checking since we - * are encapsulated in something else with its own - * validity checking - */ - - __lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_validity, - LWS_SET_TIMER_USEC_CANCEL); - } else -#endif - { - lwsl_parser("lws_parse calling handshake_04\n"); - if (handshake_0405(wsi->context, wsi)) { - lwsl_notice("hs0405 has failed the connection\n"); - return 1; - } - } - break; - } - - lws_server_init_wsi_for_ws(wsi); - lwsl_parser("accepted v%02d connection\n", wsi->ws->ietf_spec_revision); - -#if defined(LWS_WITH_ACCESS_LOG) - { - char *uptr = "unknown method", combo[128], dotstar[64]; - int l = 14, meth = lws_http_get_uri_and_method(wsi, &uptr, &l); - - if (wsi->h2_stream_carries_ws) - wsi->http.request_version = HTTP_VERSION_2; - - wsi->http.access_log.response = 101; - - lws_strnncpy(dotstar, uptr, l, sizeof(dotstar)); - l = lws_snprintf(combo, sizeof(combo), "%s (%s)", dotstar, - wsi->protocol->name); - - if (meth < 0) - meth = 0; - lws_prepare_access_log_info(wsi, combo, l, meth); - lws_access_log(wsi); - } -#endif - - lwsl_info("%s: %p: dropping ah on ws upgrade\n", __func__, wsi); - lws_header_table_detach(wsi, 1); - - return 0; -} - -int -lws_process_ws_upgrade(struct lws *wsi) -{ - const struct lws_protocols *pcol = NULL; - char buf[128], name[64]; - struct lws_tokenize ts; - lws_tokenize_elem e; - int n; - - if (!wsi->protocol) - lwsl_err("NULL protocol at lws_read\n"); - - /* - * It's either websocket or h2->websocket - * - * If we are on h1, confirm we got the required "connection: upgrade" - * header. h2 / ws-over-h2 does not have this. - */ - -#if defined(LWS_WITH_HTTP2) - if (!wsi->mux_substream) { -#endif - - lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST | - LWS_TOKENIZE_F_DOT_NONTERM | - LWS_TOKENIZE_F_RFC7230_DELIMS | - LWS_TOKENIZE_F_MINUS_NONTERM); - n = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_CONNECTION); - if (n <= 0) - goto bad_conn_format; - ts.len = 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 */ - bad_conn_format: - lwsl_err("%s: malformed or absent conn hdr\n", - __func__); - - return 1; - } - } while (e > 0); - -#if defined(LWS_WITH_HTTP2) - } -#endif - -#if defined(LWS_WITH_HTTP_PROXY) - { - const struct lws_http_mount *hit; - int uri_len = 0, meth; - char *uri_ptr; - - meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len); - hit = lws_find_mount(wsi, uri_ptr, uri_len); - - if (hit && (meth == LWSHUMETH_GET || - meth == LWSHUMETH_CONNECT || - meth == LWSHUMETH_COLON_PATH) && - (hit->origin_protocol == LWSMPRO_HTTPS || - hit->origin_protocol == LWSMPRO_HTTP)) - /* - * We are an h1 ws upgrade on a urlpath that corresponds - * to a proxying mount. Don't try to deal with it - * locally, eg, we won't even have the right protocol - * handler since we're not the guy handling it, just a - * conduit. - * - * Instead open the related ongoing h1 connection - * according to the mount configuration and proxy - * whatever that has to say from now on. - */ - return lws_http_proxy_start(wsi, hit, uri_ptr, 1); - } -#endif - - /* - * Select the first protocol we support from the list - * the client sent us. - */ - - lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST | - LWS_TOKENIZE_F_MINUS_NONTERM | - LWS_TOKENIZE_F_DOT_NONTERM | - LWS_TOKENIZE_F_RFC7230_DELIMS); - n = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_PROTOCOL); - if (n < 0) { - lwsl_err("%s: protocol list too long\n", __func__); - return 1; - } - ts.len = n; - if (!ts.len) { - int n = wsi->vhost->default_protocol_index; - /* - * Some clients only have one protocol and do not send the - * protocol list header... allow it and match to the vhost's - * default protocol (which itself defaults to zero). - * - * Setting the vhost default protocol index to -1 or anything - * more than the actual number of protocols on the vhost causes - * these "no protocol" ws connections to be rejected. - */ - - if (n >= wsi->vhost->count_protocols) { - lwsl_notice("%s: rejecting ws upg with no protocol\n", - __func__); - - return 1; - } - - lwsl_info("%s: defaulting to prot handler %d\n", __func__, n); - - lws_bind_protocol(wsi, &wsi->vhost->protocols[n], - "ws upgrade default pcol"); - - goto alloc_ws; - } - - /* otherwise go through the user-provided protocol list */ - - do { - e = lws_tokenize(&ts); - switch (e) { - case LWS_TOKZE_TOKEN: - - if (lws_tokenize_cstr(&ts, name, sizeof(name))) { - lwsl_err("%s: pcol name too long\n", __func__); - - return 1; - } - lwsl_debug("checking %s\n", name); - pcol = lws_vhost_name_to_protocol(wsi->vhost, name); - if (pcol) { - /* if we know it, bind to it and stop looking */ - lws_bind_protocol(wsi, pcol, "ws upg pcol"); - e = LWS_TOKZE_ENDED; - } - break; - - case LWS_TOKZE_DELIMITER: - case LWS_TOKZE_ENDED: - break; - - default: - lwsl_err("%s: malformatted protocol list", __func__); - - return 1; - } - } while (e > 0); - - /* we didn't find a protocol he wanted? */ - - if (!pcol) { - lwsl_notice("No supported protocol \"%s\"\n", buf); - - return 1; - } - -alloc_ws: - - return lws_process_ws_upgrade2(wsi); -} - -int -handshake_0405(struct lws_context *context, struct lws *wsi) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - struct lws_process_html_args args; - unsigned char hash[20]; - int n, accept_len; - char *response; - char *p; - - if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) || - !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) { - lwsl_info("handshake_04 missing pieces\n"); - /* completed header processing, but missing some bits */ - goto bail; - } - - if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= - MAX_WEBSOCKET_04_KEY_LEN) { - lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN); - goto bail; - } - - /* - * since key length is restricted above (currently 128), cannot - * overflow - */ - n = sprintf((char *)pt->serv_buf, - "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY)); - - lws_SHA1(pt->serv_buf, n, hash); - - accept_len = lws_b64_encode_string((char *)hash, 20, - (char *)pt->serv_buf, context->pt_serv_buf_size); - if (accept_len < 0) { - lwsl_warn("Base64 encoded hash too long\n"); - goto bail; - } - - /* allocate the per-connection user memory (if any) */ - if (lws_ensure_user_space(wsi)) - goto bail; - - /* create the response packet */ - - /* make a buffer big enough for everything */ - - response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + - 256 + LWS_PRE; - p = response; - LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a" - "Upgrade: WebSocket\x0d\x0a" - "Connection: Upgrade\x0d\x0a" - "Sec-WebSocket-Accept: "); - strcpy(p, (char *)pt->serv_buf); - p += accept_len; - - /* we can only return the protocol header if: - * - one came in, and ... */ - if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) && - /* - it is not an empty string */ - wsi->protocol->name && - wsi->protocol->name[0]) { - const char *prot = wsi->protocol->name; - -#if defined(LWS_WITH_HTTP_PROXY) - if (wsi->proxied_ws_parent && wsi->child_list) - prot = wsi->child_list->ws->actual_protocol; -#endif - - LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: "); - p += lws_snprintf(p, 128, "%s", prot); - } - -#if !defined(LWS_WITHOUT_EXTENSIONS) - /* - * Figure out which extensions the client has that we want to - * enable on this connection, and give him back the list. - * - * Give him a limited write bugdet - */ - if (lws_extension_server_handshake(wsi, &p, 192)) - goto bail; -#endif - LWS_CPYAPP(p, "\x0d\x0a"); - - args.p = p; - args.max_len = lws_ptr_diff((char *)pt->serv_buf + - context->pt_serv_buf_size, p); - if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_ADD_HEADERS, - wsi->user_space, &args, 0)) - goto bail; - - p = args.p; - - /* end of response packet */ - - LWS_CPYAPP(p, "\x0d\x0a"); - - /* okay send the handshake response accepting the connection */ - - lwsl_parser("issuing resp pkt %d len\n", - lws_ptr_diff(p, response)); -#if defined(DEBUG) - fwrite(response, 1, p - response, stderr); -#endif - n = lws_write(wsi, (unsigned char *)response, p - response, - LWS_WRITE_HTTP_HEADERS); - if (n != (p - response)) { - lwsl_info("%s: ERROR writing to socket %d\n", __func__, n); - goto bail; - } - - /* alright clean up and set ourselves into established state */ - - lwsi_set_state(wsi, LRS_ESTABLISHED); - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - - { - const char * uri_ptr = - lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI); - int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI); - const struct lws_http_mount *hit = - lws_find_mount(wsi, uri_ptr, uri_len); - if (hit && hit->cgienv && - wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO, - wsi->user_space, (void *)hit->cgienv, 0)) - return 1; - } - - return 0; - -bail: - /* caller will free up his parsing allocations */ - return -1; -} - - - -/* - * Once we reach LWS_RXPS_WS_FRAME_PAYLOAD, we know how much - * to expect in that state and can deal with it in bulk more efficiently. - */ - -static int -lws_ws_frame_rest_is_payload(struct lws *wsi, uint8_t **buf, size_t len) -{ - struct lws_ext_pm_deflate_rx_ebufs pmdrx; - unsigned int avail = (unsigned int)len; - uint8_t *buffer = *buf, mask[4]; -#if !defined(LWS_WITHOUT_EXTENSIONS) - unsigned int old_packet_length = (int)wsi->ws->rx_packet_length; -#endif - int n = 0; - - /* - * With zlib, we can give it as much input as we like. The pmd - * extension will draw it down in chunks (default 1024). - * - * If we try to restrict how much we give it, because we must go - * back to the event loop each time, we will drop the remainder... - */ - -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (!wsi->ws->count_act_ext) -#endif - { - if (wsi->protocol->rx_buffer_size) - avail = (int)wsi->protocol->rx_buffer_size; - else - avail = wsi->context->pt_serv_buf_size; - } - - /* do not consume more than we should */ - if (avail > wsi->ws->rx_packet_length) - avail = (unsigned int)wsi->ws->rx_packet_length; - - /* do not consume more than what is in the buffer */ - if (avail > len) - avail = (unsigned int)len; - - if (!avail) - return 0; - - pmdrx.eb_in.token = buffer; - pmdrx.eb_in.len = avail; - pmdrx.eb_out.token = buffer; - pmdrx.eb_out.len = avail; - - if (!wsi->ws->all_zero_nonce) { - - for (n = 0; n < 4; n++) - mask[n] = wsi->ws->mask[(wsi->ws->mask_idx + n) & 3]; - - /* deal with 4-byte chunks using unwrapped loop */ - n = avail >> 2; - while (n--) { - *(buffer) = *(buffer) ^ mask[0]; - buffer++; - *(buffer) = *(buffer) ^ mask[1]; - buffer++; - *(buffer) = *(buffer) ^ mask[2]; - buffer++; - *(buffer) = *(buffer) ^ mask[3]; - buffer++; - } - /* and the remaining bytes bytewise */ - for (n = 0; n < (int)(avail & 3); n++) { - *(buffer) = *(buffer) ^ mask[n]; - buffer++; - } - - wsi->ws->mask_idx = (wsi->ws->mask_idx + avail) & 3; - } - - lwsl_info("%s: using %d of raw input (total %d on offer)\n", __func__, - avail, (int)len); - - (*buf) += avail; - len -= avail; - wsi->ws->rx_packet_length -= avail; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &pmdrx, 0); - lwsl_info("%s: ext says %d / ebuf_out.len %d\n", __func__, n, - pmdrx.eb_out.len); - - /* - * ebuf may be pointing somewhere completely different now, - * it's the output - */ - - if (n < 0) { - /* - * we may rely on this to get RX, just drop connection - */ - lwsl_notice("%s: LWS_EXT_CB_PAYLOAD_RX blew out\n", __func__); - wsi->socket_is_permanently_unusable = 1; - - return -1; - } - - /* - * if we had an rx fragment right at the last compressed byte of the - * message, we can get a zero length inflated output, where no prior - * rx inflated output marked themselves with FIN, since there was - * raw ws payload still to drain at that time. - * - * Then we need to generate a zero length ws rx that can be understood - * as the message completion. - */ - - if (!pmdrx.eb_out.len && /* zero-length inflation output */ - n == PMDR_EMPTY_FINAL && /* nothing to drain from the inflator */ - old_packet_length && /* we gave the inflator new input */ - !wsi->ws->rx_packet_length && /* raw ws packet payload all gone */ - wsi->ws->final && /* the raw ws packet is a FIN guy */ - wsi->protocol->callback && - !wsi->wsistate_pre_close) { - - lwsl_ext("%s: issuing zero length FIN pkt\n", __func__); - - if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_RECEIVE, - wsi->user_space, NULL, 0)) - return -1; - - return avail; - } - - /* - * If doing permessage-deflate, above was the only way to get a zero - * length receive. Otherwise we're more willing. - */ - if (wsi->ws->count_act_ext && !pmdrx.eb_out.len) - return avail; - - if (n == PMDR_HAS_PENDING) - /* extension had more... main loop will come back */ - lws_add_wsi_to_draining_ext_list(wsi); - else - lws_remove_wsi_from_draining_ext_list(wsi); -#endif - - if (pmdrx.eb_out.len && - wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) { - if (lws_check_utf8(&wsi->ws->utf8, - pmdrx.eb_out.token, - 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 && !n) { - lwsl_info("FINAL utf8 error\n"); - lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD, - (uint8_t *)"partial utf8", 12); - -utf8_fail: - lwsl_info("utf8 error\n"); - lwsl_hexdump_info(pmdrx.eb_out.token, pmdrx.eb_out.len); - - return -1; - } - } - - if (wsi->protocol->callback && !wsi->wsistate_pre_close) - if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_RECEIVE, - wsi->user_space, - pmdrx.eb_out.token, - pmdrx.eb_out.len)) - return -1; - - wsi->ws->first_fragment = 0; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - lwsl_info("%s: input used %d, output %d, rem len %d, rx_draining_ext %d\n", - __func__, avail, pmdrx.eb_out.len, (int)len, - wsi->ws->rx_draining_ext); -#endif - - return avail; /* how much we used from the input */ -} - - -int -lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len) -{ - unsigned char *bufin = *buf; - int m, bulk = 0; - - lwsl_debug("%s: received %d byte packet\n", __func__, (int)len); - - //lwsl_hexdump_notice(*buf, len); - - /* let the rx protocol state machine have as much as it needs */ - - while (len) { - /* - * we were accepting input but now we stopped doing so - */ - if (wsi->rxflow_bitmap) { - lwsl_info("%s: doing rxflow, caching %d\n", __func__, - (int)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 - * leave it where it is, already pointed to by the head. - */ - if (lws_rxflow_cache(wsi, *buf, 0, (int)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_info("%s: trimming inside rxflow cache\n", - __func__); - *buf = bufin; - } else - *buf += len; - - return 1; - } -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->rx_draining_ext) { - lwsl_debug("%s: draining rx ext\n", __func__); - m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0); - if (m < 0) - return -1; - continue; - } -#endif - - /* consume payload bytes efficiently */ - while (wsi->lws_rx_parse_state == LWS_RXPS_WS_FRAME_PAYLOAD && - (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME || - wsi->ws->opcode == LWSWSOPC_BINARY_FRAME || - wsi->ws->opcode == LWSWSOPC_CONTINUATION) && - len) { - uint8_t *bin = *buf; - - bulk = 1; - m = lws_ws_frame_rest_is_payload(wsi, buf, len); - assert((int)lws_ptr_diff(*buf, bin) <= (int)len); - len -= lws_ptr_diff(*buf, bin); - - if (!m) { - - break; - } - if (m < 0) { - lwsl_info("%s: rest_is_payload bailed\n", - __func__); - return -1; - } - } - - if (!bulk) { - /* process the byte */ - m = lws_ws_rx_sm(wsi, 0, *(*buf)++); - len--; - } else { - /* - * We already handled this byte in bulk, just deal - * with the ramifications - */ -#if !defined(LWS_WITHOUT_EXTENSIONS) - lwsl_debug("%s: coming out of bulk with len %d, " - "wsi->ws->rx_draining_ext %d\n", - __func__, (int)len, - wsi->ws->rx_draining_ext); -#endif - m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR | - ALREADY_PROCESSED_NO_CB, 0); - } - - if (m < 0) { - lwsl_info("%s: lws_ws_rx_sm bailed %d\n", __func__, - bulk); - - return -1; - } - - bulk = 0; - } - - lwsl_debug("%s: exit with %d unused\n", __func__, (int)len); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/secure-streams/plugins/ssp-h1url/h1url.c libwebsockets-2.4.2/lib/secure-streams/plugins/ssp-h1url/h1url.c --- libwebsockets-4.0.20/lib/secure-streams/plugins/ssp-h1url/h1url.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/plugins/ssp-h1url/h1url.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* - * ssp-h1url plugin - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * CC0 so it can be used as a template for your own secure streams plugins - * licensed how you like. - */ - -#include - -static int -ssp_h1url_create(struct lws_ss_handle *ss, void *info, plugin_auth_status_cb status) -{ - return 0; -} - -static int -ssp_h1url_destroy(struct lws_ss_handle *ss) -{ - return 0; -} - -static int -ssp_h1url_munge(struct lws_ss_handle *ss, char *path, size_t path_len) -{ - return 0; -} - -/* this is the only exported symbol */ -const lws_ss_plugin_t ssp_h1url = { - .name = "h1url", - .alloc = 0, - .create = ssp_h1url_create, - .destroy = ssp_h1url_destroy, - .munge = ssp_h1url_munge -}; diff -Nru libwebsockets-4.0.20/lib/secure-streams/policy.c libwebsockets-2.4.2/lib/secure-streams/policy.c --- libwebsockets-4.0.20/lib/secure-streams/policy.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/policy.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,987 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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 - -typedef struct backoffs { - struct backoffs *next; - const char *name; - lws_retry_bo_t r; -} backoff_t; - -static const char * const lejp_tokens_policy[] = { - "release", - "product", - "schema-version", - "via-socks5", - "retry[].*.backoff", - "retry[].*.conceal", - "retry[].*.jitterpc", - "retry[].*.svalidping", - "retry[].*.svalidhup", - "retry[].*", - "certs[].*", - "trust_stores[].name", - "trust_stores[].stack", - "s[].*.endpoint", - "s[].*.via-socks5", - "s[].*.protocol", - "s[].*.port", - "s[].*.plugins", - "s[].*.tls", - "s[].*.client_cert", - "s[].*.opportunistic", - "s[].*.nailed_up", - "s[].*.urgent_tx", - "s[].*.urgent_rx", - "s[].*.long_poll", - "s[].*.retry", - "s[].*.tls_trust_store", - "s[].*.metadata", - "s[].*.metadata[].*", - - "s[].*.http_auth_header", - "s[].*.http_dsn_header", - "s[].*.http_fwv_header", - "s[].*.http_devtype_header", - - "s[].*.http_auth_preamble", - - "s[].*.http_no_content_length", - "s[].*.rideshare", /* streamtype name this rides shotgun with */ - "s[].*.payload_fmt", - "s[].*.http_method", - "s[].*.http_url", - "s[].*.nghttp2_quirk_end_stream", - "s[].*.h2q_oflow_txcr", - "s[].*.http_multipart_name", - "s[].*.http_multipart_filename", - "s[].*.http_mime_content_type", - "s[].*.http_www_form_urlencoded", - "s[].*.ws_subprotocol", - "s[].*.ws_binary", - "s[].*.local_sink", - "s[].*.mqtt_topic", - "s[].*.mqtt_subscribe", - "s[].*.mqtt_qos", - "s[].*.mqtt_keep_alive", - "s[].*.mqtt_clean_start", - "s[].*.mqtt_will_topic", - "s[].*.mqtt_will_message", - "s[].*.mqtt_will_qos", - "s[].*.mqtt_will_retain", - "s[].*", -}; - -typedef enum { - LSSPPT_RELEASE, - LSSPPT_PRODUCT, - LSSPPT_SCHEMA_VERSION, - LSSPPT_VIA_SOCKS5, - LSSPPT_BACKOFF, - LSSPPT_CONCEAL, - LSSPPT_JITTERPC, - LSSPPT_VALIDPING_S, - LSSPPT_VALIDHUP_S, - LSSPPT_RETRY, - LSSPPT_CERTS, - LSSPPT_TRUST_STORES_NAME, - LSSPPT_TRUST_STORES_STACK, - LSSPPT_ENDPOINT, - LSSPPT_VH_VIA_SOCKS5, - LSSPPT_PROTOCOL, - LSSPPT_PORT, - LSSPPT_PLUGINS, - LSSPPT_TLS, - LSSPPT_TLS_CLIENT_CERT, - LSSPPT_OPPORTUNISTIC, - LSSPPT_NAILED_UP, - LSSPPT_URGENT_TX, - LSSPPT_URGENT_RX, - LSSPPT_LONG_POLL, - LSSPPT_RETRYPTR, - LSSPPT_TRUST, - LSSPPT_METADATA, - LSSPPT_METADATA_ITEM, - - LSSPPT_HTTP_AUTH_HEADER, - LSSPPT_HTTP_DSN_HEADER, - LSSPPT_HTTP_FWV_HEADER, - LSSPPT_HTTP_TYPE_HEADER, - - LSSPPT_HTTP_AUTH_PREAMBLE, - LSSPPT_HTTP_NO_CONTENT_LENGTH, - LSSPPT_RIDESHARE, - LSSPPT_PAYLOAD_FORMAT, - LSSPPT_HTTP_METHOD, - LSSPPT_HTTP_URL, - LSSPPT_NGHTTP2_QUIRK_END_STREAM, - LSSPPT_H2_QUIRK_OVERFLOWS_TXCR, - LSSPPT_HTTP_MULTIPART_NAME, - LSSPPT_HTTP_MULTIPART_FILENAME, - LSSPPT_HTTP_MULTIPART_CONTENT_TYPE, - LSSPPT_HTTP_WWW_FORM_URLENCODED, - LSSPPT_WS_SUBPROTOCOL, - LSSPPT_WS_BINARY, - LSSPPT_LOCAL_SINK, - LSSPPT_MQTT_TOPIC, - LSSPPT_MQTT_SUBSCRIBE, - LSSPPT_MQTT_QOS, - LSSPPT_MQTT_KEEPALIVE, - LSSPPT_MQTT_CLEAN_START, - LSSPPT_MQTT_WILL_TOPIC, - LSSPPT_MQTT_WILL_MESSAGE, - LSSPPT_MQTT_WILL_QOS, - LSSPPT_MQTT_WILL_RETAIN, - LSSPPT_STREAMTYPES -} policy_token_t; - -union u { - backoff_t *b; - lws_ss_x509_t *x; - lws_ss_trust_store_t *t; - lws_ss_policy_t *p; -}; - -enum { - LTY_BACKOFF, - LTY_X509, - LTY_TRUSTSTORE, - LTY_POLICY, - - _LTY_COUNT /* always last */ -}; - -struct policy_cb_args { - struct lejp_ctx jctx; - struct lws_context *context; - struct lwsac *ac; - - const char *socks5_proxy; - - struct lws_b64state b64; - - union u heads[_LTY_COUNT]; - union u curr[_LTY_COUNT]; - - uint8_t *p; - - int count; -}; - -#define POL_AC_INITIAL 2048 -#define POL_AC_GRAIN 800 -#define MAX_CERT_TEMP 2048 /* used to discover actual cert size for realloc */ - -static uint8_t sizes[] = { - sizeof(backoff_t), - sizeof(lws_ss_x509_t), - sizeof(lws_ss_trust_store_t), - sizeof(lws_ss_policy_t), -}; - -static const char *protonames[] = { - "h1", /* LWSSSP_H1 */ - "h2", /* LWSSSP_H2 */ - "ws", /* LWSSSP_WS */ - "mqtt", /* LWSSSP_MQTT */ -}; - -lws_ss_metadata_t * -lws_ss_policy_metadata(const lws_ss_policy_t *p, const char *name) -{ - lws_ss_metadata_t *pmd = p->metadata; - - while (pmd) { - if (pmd->name && !strcmp(name, pmd->name)) - return pmd; - pmd = pmd->next; - } - - return NULL; -} - -lws_ss_metadata_t * -lws_ss_policy_metadata_index(const lws_ss_policy_t *p, size_t index) -{ - lws_ss_metadata_t *pmd = p->metadata; - - while (pmd) { - if (pmd->length == index) - return pmd; - pmd = pmd->next; - } - - return NULL; -} - -int -lws_ss_set_metadata(struct lws_ss_handle *h, const char *name, - void *value, size_t len) -{ - lws_ss_metadata_t *omd = lws_ss_policy_metadata(h->policy, name); - - if (!omd) { - lwsl_err("%s: unknown metadata %s\n", __func__, name); - return 1; - } - - h->metadata[omd->length].name = name; - h->metadata[omd->length].value = value; - h->metadata[omd->length].length = len; - - return 0; -} - -lws_ss_metadata_t * -lws_ss_get_handle_metadata(struct lws_ss_handle *h, const char *name) -{ - lws_ss_metadata_t *omd = lws_ss_policy_metadata(h->policy, name); - - if (!omd) - return NULL; - - return &h->metadata[omd->length]; -} - -static signed char -lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason) -{ - struct policy_cb_args *a = (struct policy_cb_args *)ctx->user; - const lws_ss_plugin_t **pin; - char **pp, dotstar[32], *q; - lws_ss_trust_store_t *ts; - lws_ss_metadata_t *pmd; - lws_retry_bo_t *b; - size_t inl, outl; - lws_ss_x509_t *x; - uint8_t *extant; - backoff_t *bot; - int n = -1; - - lwsl_debug("%s: %d %d %s\n", __func__, reason, ctx->path_match - 1, - ctx->path); - - switch (ctx->path_match - 1) { - case LSSPPT_RETRY: - n = LTY_BACKOFF; - break; - case LSSPPT_CERTS: - n = LTY_X509; - break; - case LSSPPT_TRUST_STORES_NAME: - case LSSPPT_TRUST_STORES_STACK: - n = LTY_TRUSTSTORE; - break; - case LSSPPT_STREAMTYPES: - n = LTY_POLICY; - break; - } - - if (reason == LEJPCB_ARRAY_START && - (ctx->path_match - 1 == LSSPPT_PLUGINS || - ctx->path_match - 1 == LSSPPT_METADATA)) - a->count = 0; - - if (reason == LEJPCB_ARRAY_END && - ctx->path_match - 1 == LSSPPT_TRUST_STORES_STACK && !a->count) { - lwsl_err("%s: at least one cert required in trust store\n", - __func__); - goto oom; - } - - if (reason == LEJPCB_OBJECT_END && a->p) { - /* - * Allocate a just-the-right-size buf for the cert DER now - * we decoded it into the a->p temp buffer and know the exact - * size - */ - a->curr[LTY_X509].x->ca_der = lws_malloc(a->count, "ssx509"); - if (!a->curr[LTY_X509].x->ca_der) - goto oom; - memcpy((uint8_t *)a->curr[LTY_X509].x->ca_der, a->p, a->count); - a->curr[LTY_X509].x->ca_der_len = a->count; - - /* - * ... and then we can free the temp buffer - */ - lws_free_set_NULL(a->p); - - return 0; - } - - if (reason == LEJPCB_PAIR_NAME && n != -1 && n != LTY_TRUSTSTORE) { - /* - * We do the pointers always as .b, all of the participating - * structs begin with .next and .name - */ - a->curr[n].b = lwsac_use_zero(&a->ac, sizes[n], POL_AC_GRAIN); - if (!a->curr[n].b) - goto oom; - - if (n == LTY_X509) { - a->p = lws_malloc(MAX_CERT_TEMP, "cert temp"); - if (!a->p) - goto oom; - memset(&a->b64, 0, sizeof(a->b64)); - } - - a->count = 0; - a->curr[n].b->next = a->heads[n].b; - a->heads[n].b = a->curr[n].b; - pp = (char **)&a->curr[n].b->name; - - goto string1; - } - - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - switch (ctx->path_match - 1) { - - /* strings */ - - case LSSPPT_RELEASE: - break; - - case LSSPPT_PRODUCT: - break; - - case LSSPPT_SCHEMA_VERSION: - break; - - case LSSPPT_VIA_SOCKS5: - /* the global / default proxy */ - pp = (char **)&a->socks5_proxy; - goto string2; - - case LSSPPT_BACKOFF: - b = &a->curr[LTY_BACKOFF].b->r; - if (b->retry_ms_table_count == 8) { - lwsl_err("%s: > 8 backoff levels\n", __func__); - return 1; - } - if (!b->retry_ms_table_count) { - b->retry_ms_table = (uint32_t *)lwsac_use_zero(&a->ac, - sizeof(uint32_t) * 8, POL_AC_GRAIN); - if (!b->retry_ms_table) - goto oom; - } - - ((uint32_t *)b->retry_ms_table) - [b->retry_ms_table_count++] = atoi(ctx->buf); - break; - - case LSSPPT_CONCEAL: - a->curr[LTY_BACKOFF].b->r.conceal_count = atoi(ctx->buf); - break; - - case LSSPPT_JITTERPC: - a->curr[LTY_BACKOFF].b->r.jitter_percent = atoi(ctx->buf); - break; - - case LSSPPT_VALIDPING_S: - a->curr[LTY_BACKOFF].b->r.secs_since_valid_ping = atoi(ctx->buf); - break; - - case LSSPPT_VALIDHUP_S: - a->curr[LTY_BACKOFF].b->r.secs_since_valid_hangup = atoi(ctx->buf); - break; - - case LSSPPT_CERTS: - if (a->count + ctx->npos >= MAX_CERT_TEMP) { - lwsl_err("%s: cert too big\n", __func__); - goto oom; - } - inl = ctx->npos; - outl = MAX_CERT_TEMP - a->count; - - lws_b64_decode_stateful(&a->b64, ctx->buf, &inl, - a->p + a->count, &outl, - reason == LEJPCB_VAL_STR_END); - a->count += outl; - if (inl != ctx->npos) { - lwsl_err("%s: b64 decode fail\n", __func__); - goto oom; - } - break; - - case LSSPPT_TRUST_STORES_NAME: - /* - * We do the pointers always as .b, all of the participating - * structs begin with .next and .name - */ - a->curr[LTY_TRUSTSTORE].b = lwsac_use_zero(&a->ac, - sizes[LTY_TRUSTSTORE], POL_AC_GRAIN); - if (!a->curr[LTY_TRUSTSTORE].b) - goto oom; - - a->count = 0; - a->curr[LTY_TRUSTSTORE].b->next = a->heads[LTY_TRUSTSTORE].b; - a->heads[LTY_TRUSTSTORE].b = a->curr[LTY_TRUSTSTORE].b; - pp = (char **)&a->curr[LTY_TRUSTSTORE].b->name; - - goto string2; - - case LSSPPT_TRUST_STORES_STACK: - if (a->count >= (int)LWS_ARRAY_SIZE( - a->curr[LTY_TRUSTSTORE].t->ssx509)) { - lwsl_err("%s: trust store too big\n", __func__); - goto oom; - } - lwsl_debug("%s: trust stores stack %.*s\n", __func__, - ctx->npos, ctx->buf); - x = a->heads[LTY_X509].x; - while (x) { - if (!strncmp(x->vhost_name, ctx->buf, ctx->npos)) { - a->curr[LTY_TRUSTSTORE].t->ssx509[a->count++] = x; - a->curr[LTY_TRUSTSTORE].t->count++; - - return 0; - } - x = x->next; - } - lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar)); - lwsl_err("%s: unknown trust store entry %s\n", __func__, - dotstar); - goto oom; - - case LSSPPT_ENDPOINT: - pp = (char **)&a->curr[LTY_POLICY].p->endpoint; - goto string2; - - case LSSPPT_VH_VIA_SOCKS5: - pp = (char **)&a->curr[LTY_POLICY].p->socks5_proxy; - goto string2; - - case LSSPPT_PORT: - a->curr[LTY_POLICY].p->port = atoi(ctx->buf); - break; - - case LSSPPT_HTTP_METHOD: - pp = (char **)&a->curr[LTY_POLICY].p->u.http.method; - goto string2; - - case LSSPPT_HTTP_URL: - pp = (char **)&a->curr[LTY_POLICY].p->u.http.url; - goto string2; - - case LSSPPT_RIDESHARE: - pp = (char **)&a->curr[LTY_POLICY].p->rideshare_streamtype; - goto string2; - - case LSSPPT_PAYLOAD_FORMAT: - pp = (char **)&a->curr[LTY_POLICY].p->payload_fmt; - goto string2; - - case LSSPPT_PLUGINS: - pin = a->context->pss_plugins; - if (a->count == - (int)LWS_ARRAY_SIZE(a->curr[LTY_POLICY].p->plugins)) { - lwsl_err("%s: too many plugins\n", __func__); - - goto oom; - } - if (!pin) - break; - while (*pin) { - if (!strncmp((*pin)->name, ctx->buf, ctx->npos)) { - a->curr[LTY_POLICY].p->plugins[a->count++] = *pin; - return 0; - } - pin++; - } - lwsl_err("%s: unknown plugin\n", __func__); - goto oom; - - case LSSPPT_TLS: - if (reason == LEJPCB_VAL_TRUE) - a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_TLS; - break; - - case LSSPPT_TLS_CLIENT_CERT: - a->curr[LTY_POLICY].p->client_cert = atoi(ctx->buf) + 1; - break; - - case LSSPPT_OPPORTUNISTIC: - if (reason == LEJPCB_VAL_TRUE) - a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_OPPORTUNISTIC; - break; - case LSSPPT_NAILED_UP: - if (reason == LEJPCB_VAL_TRUE) - a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_NAILED_UP; - break; - case LSSPPT_URGENT_TX: - if (reason == LEJPCB_VAL_TRUE) - a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_URGENT_TX; - break; - case LSSPPT_URGENT_RX: - if (reason == LEJPCB_VAL_TRUE) - a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_URGENT_RX; - break; - case LSSPPT_LONG_POLL: - if (reason == LEJPCB_VAL_TRUE) - a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_LONG_POLL; - break; - case LSSPPT_HTTP_WWW_FORM_URLENCODED: - if (reason == LEJPCB_VAL_TRUE) - a->curr[LTY_POLICY].p->flags |= - LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED; - break; - - case LSSPPT_RETRYPTR: - bot = a->heads[LTY_BACKOFF].b; - while (bot) { - if (!strncmp(ctx->buf, bot->name, ctx->npos)) { - a->curr[LTY_POLICY].p->retry_bo = &bot->r; - - return 0; - } - bot = bot->next; - } - lwsl_err("%s: unknown backoff scheme\n", __func__); - - return -1; - - case LSSPPT_TRUST: - ts = a->heads[LTY_TRUSTSTORE].t; - while (ts) { - if (!strncmp(ctx->buf, ts->name, ctx->npos)) { - a->curr[LTY_POLICY].p->trust_store = ts; - return 0; - } - ts = ts->next; - } - lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar)); - lwsl_err("%s: unknown trust store name %s\n", __func__, - dotstar); - - return -1; - - case LSSPPT_METADATA: - break; - - case LSSPPT_METADATA_ITEM: - pmd = a->curr[LTY_POLICY].p->metadata; - a->curr[LTY_POLICY].p->metadata = lwsac_use_zero(&a->ac, - sizeof(lws_ss_metadata_t) + ctx->npos + - (ctx->path_match_len - ctx->st[ctx->sp - 2].p + 1) + 2, - POL_AC_GRAIN); - a->curr[LTY_POLICY].p->metadata->next = pmd; - - q = (char *)a->curr[LTY_POLICY].p->metadata + - sizeof(lws_ss_metadata_t); - a->curr[LTY_POLICY].p->metadata->name = q; - memcpy(q, ctx->path + ctx->st[ctx->sp - 2].p + 1, - ctx->path_match_len - ctx->st[ctx->sp - 2].p); - - q += ctx->path_match_len - ctx->st[ctx->sp - 2].p; - a->curr[LTY_POLICY].p->metadata->value = q; - memcpy(q, ctx->buf, ctx->npos); - - a->curr[LTY_POLICY].p->metadata->length = /* the index in handle->metadata */ - a->curr[LTY_POLICY].p->metadata_count++; - break; - - case LSSPPT_HTTP_AUTH_HEADER: - case LSSPPT_HTTP_DSN_HEADER: - case LSSPPT_HTTP_FWV_HEADER: - case LSSPPT_HTTP_TYPE_HEADER: - pp = (char **)&a->curr[LTY_POLICY].p->u.http.blob_header[ - (ctx->path_match - 1) - LSSPPT_HTTP_AUTH_HEADER]; - goto string2; - - case LSSPPT_HTTP_AUTH_PREAMBLE: - pp = (char **)&a->curr[LTY_POLICY].p->u.http.auth_preamble; - goto string2; - - case LSSPPT_HTTP_NO_CONTENT_LENGTH: - if (reason == LEJPCB_VAL_TRUE) - a->curr[LTY_POLICY].p->flags |= - LWSSSPOLF_HTTP_NO_CONTENT_LENGTH; - break; - - case LSSPPT_NGHTTP2_QUIRK_END_STREAM: - if (reason == LEJPCB_VAL_TRUE) - a->curr[LTY_POLICY].p->flags |= - LWSSSPOLF_QUIRK_NGHTTP2_END_STREAM; - break; - case LSSPPT_H2_QUIRK_OVERFLOWS_TXCR: - if (reason == LEJPCB_VAL_TRUE) - a->curr[LTY_POLICY].p->flags |= - LWSSSPOLF_H2_QUIRK_OVERFLOWS_TXCR; - break; - case LSSPPT_HTTP_MULTIPART_NAME: - a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_HTTP_MULTIPART; - pp = (char **)&a->curr[LTY_POLICY].p->u.http.multipart_name; - goto string2; - case LSSPPT_HTTP_MULTIPART_FILENAME: - a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_HTTP_MULTIPART; - pp = (char **)&a->curr[LTY_POLICY].p->u.http.multipart_filename; - goto string2; - case LSSPPT_HTTP_MULTIPART_CONTENT_TYPE: - a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_HTTP_MULTIPART; - pp = (char **)&a->curr[LTY_POLICY].p->u.http.multipart_content_type; - goto string2; - case LSSPPT_WS_SUBPROTOCOL: - pp = (char **)&a->curr[LTY_POLICY].p->u.http.u.ws.subprotocol; - goto string2; - - case LSSPPT_WS_BINARY: - a->curr[LTY_POLICY].p->u.http.u.ws.binary = - reason == LEJPCB_VAL_TRUE; - break; - case LSSPPT_LOCAL_SINK: - if (reason == LEJPCB_VAL_TRUE) - a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_LOCAL_SINK; - break; - - case LSSPPT_MQTT_TOPIC: - pp = (char **)&a->curr[LTY_POLICY].p->u.mqtt.topic; - goto string2; - - case LSSPPT_MQTT_SUBSCRIBE: - pp = (char **)&a->curr[LTY_POLICY].p->u.mqtt.subscribe; - goto string2; - - case LSSPPT_MQTT_QOS: - a->curr[LTY_POLICY].p->u.mqtt.qos = atoi(ctx->buf); - break; - - case LSSPPT_MQTT_KEEPALIVE: - a->curr[LTY_POLICY].p->u.mqtt.keep_alive = atoi(ctx->buf); - break; - - case LSSPPT_MQTT_CLEAN_START: - a->curr[LTY_POLICY].p->u.mqtt.clean_start = - reason == LEJPCB_VAL_TRUE; - break; - case LSSPPT_MQTT_WILL_TOPIC: - pp = (char **)&a->curr[LTY_POLICY].p->u.mqtt.will_topic; - goto string2; - - case LSSPPT_MQTT_WILL_MESSAGE: - pp = (char **)&a->curr[LTY_POLICY].p->u.mqtt.will_message; - goto string2; - - case LSSPPT_MQTT_WILL_QOS: - a->curr[LTY_POLICY].p->u.mqtt.will_qos = atoi(ctx->buf); - break; - case LSSPPT_MQTT_WILL_RETAIN: - a->curr[LTY_POLICY].p->u.mqtt.will_retain = - reason == LEJPCB_VAL_TRUE; - break; - - case LSSPPT_PROTOCOL: - a->curr[LTY_POLICY].p->protocol = 0xff; - for (n = 0; n < (int)LWS_ARRAY_SIZE(protonames); n++) - if (strlen(protonames[n]) == ctx->npos && - !strncmp(ctx->buf, protonames[n], ctx->npos)) - a->curr[LTY_POLICY].p->protocol = (uint8_t)n; - - if (a->curr[LTY_POLICY].p->protocol != 0xff) - break; - lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar)); - lwsl_err("%s: unknown protocol name %s\n", __func__, dotstar); - return -1; - } - - return 0; - -string2: - /* - * If we can do const string folding, reuse the existing string rather - * than make a new entry - */ - extant = lwsac_scan_extant(a->ac, (uint8_t *)ctx->buf, ctx->npos, 1); - if (extant) { - *pp = (char *)extant; - - return 0; - } - *pp = lwsac_use_backfill(&a->ac, ctx->npos + 1, POL_AC_GRAIN); - if (!*pp) - goto oom; - memcpy(*pp, ctx->buf, ctx->npos); - (*pp)[ctx->npos] = '\0'; - - return 0; - -string1: - n = ctx->st[ctx->sp].p; - *pp = lwsac_use_backfill(&a->ac, ctx->path_match_len + 1 - n, - POL_AC_GRAIN); - if (!*pp) - goto oom; - memcpy(*pp, ctx->path + n, ctx->path_match_len - n); - (*pp)[ctx->path_match_len - n] = '\0'; - - return 0; - -oom: - lwsl_err("%s: OOM\n", __func__); - lws_free_set_NULL(a->p); - lwsac_free(&a->ac); - - return -1; -} - -int -lws_ss_policy_parse_begin(struct lws_context *context) -{ - struct policy_cb_args *args; - char *p; - - args = lws_zalloc(sizeof(struct policy_cb_args), __func__); - if (!args) { - lwsl_err("%s: OOM\n", __func__); - - return 1; - } - - context->pol_args = args; - args->context = context; - p = lwsac_use(&args->ac, 1, POL_AC_INITIAL); - if (!p) { - lwsl_err("%s: OOM\n", __func__); - lws_free_set_NULL(context->pol_args); - - return -1; - } - *p = 0; - lejp_construct(&args->jctx, lws_ss_policy_parser_cb, args, - lejp_tokens_policy, LWS_ARRAY_SIZE(lejp_tokens_policy)); - - return 0; -} - -int -lws_ss_policy_parse_abandon(struct lws_context *context) -{ - struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args; - - lejp_destruct(&args->jctx); - lws_free_set_NULL(context->pol_args); - - return 0; -} - -int -lws_ss_policy_parse(struct lws_context *context, const uint8_t *buf, size_t len) -{ - struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args; - int m; - - m = (int)(signed char)lejp_parse(&args->jctx, buf, len); - if (m == LEJP_CONTINUE || m >= 0) - return m; - - lwsl_err("%s: parse failed: %d: %s\n", __func__, m, - lejp_error_to_string(m)); - lws_ss_policy_parse_abandon(context); - - return m; -} - -int -lws_ss_policy_set(struct lws_context *context, const char *name) -{ - struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args; - struct lws_context_creation_info i; - lws_ss_trust_store_t *ts; - struct lws_vhost *v; - lws_ss_x509_t *x; - char buf[16]; - int m, ret = 0; - - /* - * Parsing seems to have succeeded, and we're going to use the new - * policy that's laid out in args->ac - */ - - lejp_destruct(&args->jctx); - - if (context->ac_policy) { - - /* - * So this is a bit fun-filled, we already had a policy in - * force, perhaps it was the default policy that's just good for - * fetching the real policy, and we're doing that now. - * - * We can destroy all the policy-related direct allocations - * easily because they're cleanly in a single lwsac... - */ - lwsac_free(&context->ac_policy); - - /* - * ...but when we did the trust stores, we created vhosts for - * each. We need to destroy those now too, and recreate new - * ones from the new policy, perhaps with different X.509s. - */ - - v = context->vhost_list; - while (v) { - if (v->from_ss_policy) { - struct lws_vhost *vh = v->vhost_next; - lwsl_debug("%s: destroying vh %p\n", __func__, v); - lws_vhost_destroy(v); - v = vh; - continue; - } - v = v->vhost_next; - } - - lws_check_deferred_free(context, 0, 1); - } - - context->pss_policies = args->heads[LTY_POLICY].p; - context->ac_policy = args->ac; - - lws_humanize(buf, sizeof(buf), lwsac_total_alloc(args->ac), - humanize_schema_si_bytes); - if (lwsac_total_alloc(args->ac)) - m = (int)((lwsac_total_overhead(args->ac) * 100) / - lwsac_total_alloc(args->ac)); - else - m = 0; - - lwsl_notice("%s: %s, pad %d%c: %s\n", __func__, buf, m, '%', name); - - /* Create vhosts for each type of trust store */ - - ts = args->heads[LTY_TRUSTSTORE].t; - while (ts) { - memset(&i, 0, sizeof(i)); - - /* - * We get called from context creation... instantiates - * vhosts with client tls contexts set up for each unique CA. - * - * Create the vhost with the first (mandatory) entry in the - * trust store... - */ - - v = lws_get_vhost_by_name(context, ts->name); - if (!v) { - int n; - - i.options = context->options; - i.vhost_name = ts->name; - lwsl_debug("%s: %s\n", __func__, i.vhost_name); - i.client_ssl_ca_mem = ts->ssx509[0]->ca_der; - i.client_ssl_ca_mem_len = ts->ssx509[0]->ca_der_len; - i.port = CONTEXT_PORT_NO_LISTEN; - lwsl_info("%s: %s trust store initial '%s'\n", __func__, - ts->name, ts->ssx509[0]->vhost_name); - - v = lws_create_vhost(context, &i); - if (!v) { - lwsl_err("%s: failed to create vhost %s\n", - __func__, ts->name); - ret = 1; - } else - v->from_ss_policy = 1; - - for (n = 1; v && n < ts->count; n++) { - lwsl_info("%s: add '%s' to trust store\n", - __func__, ts->ssx509[n]->vhost_name); - if (lws_tls_client_vhost_extra_cert_mem(v, - ts->ssx509[n]->ca_der, - ts->ssx509[n]->ca_der_len)) { - lwsl_err("%s: add extra cert failed\n", - __func__); - ret = 1; - } - } - } - - ts = ts->next; - } - if (!context->vhost_list) { - /* corner case... there's no trust store used */ - memset(&i, 0, sizeof(i)); - i.options = context->options; - i.vhost_name = "_ss_default"; - i.port = CONTEXT_PORT_NO_LISTEN; - v = lws_create_vhost(context, &i); - if (!v) - lwsl_err("%s: failed to create vhost %s\n", - __func__, i.vhost_name); - } -#if defined(LWS_WITH_SOCKS5) - - /* - * ... we need to go through every vhost updating its understanding of - * which socks5 proxy to use... - */ - - v = context->vhost_list; - while (v) { - lws_set_socks(v, args->socks5_proxy); - v = v->vhost_next; - } - if (context->vhost_system) - lws_set_socks(context->vhost_system, args->socks5_proxy); - - if (args->socks5_proxy) - lwsl_notice("%s: global socks5 proxy: %s\n", __func__, - args->socks5_proxy); -#endif - - /* now we processed the x.509 CAs, we can free all of our originals */ - - x = args->heads[LTY_X509].x; - while (x) { - /* - * Free all the DER buffers now they have been parsed into - * tls library X.509 objects - */ - lws_free((void *)x->ca_der); - x->ca_der = NULL; - x = x->next; - } - - /* and we can discard the parsing args object now, invalidating args */ - - lws_free_set_NULL(context->pol_args); - - return ret; -} - -const lws_ss_policy_t * -lws_ss_policy_lookup(const struct lws_context *context, const char *streamtype) -{ - const lws_ss_policy_t *p = context->pss_policies; - - if (!streamtype) - return NULL; - - while (p) { - if (!strcmp(p->streamtype, streamtype)) - return p; - p = p->next; - } - - return NULL; -} diff -Nru libwebsockets-4.0.20/lib/secure-streams/private-lib-secure-streams.h libwebsockets-2.4.2/lib/secure-streams/private-lib-secure-streams.h --- libwebsockets-4.0.20/lib/secure-streams/private-lib-secure-streams.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/private-lib-secure-streams.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,353 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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. - */ - -/* - * Secure Stream state - */ - -typedef enum { - SSSEQ_IDLE, - SSSEQ_TRY_CONNECT, - SSSEQ_TRY_CONNECT_NAUTH, - SSSEQ_TRY_CONNECT_SAUTH, - SSSEQ_RECONNECT_WAIT, - SSSEQ_DO_RETRY, - SSSEQ_CONNECTED, -} lws_ss_seq_state_t; - - -/** - * lws_ss_handle_t: publicly-opaque secure stream object implementation - */ - -typedef struct lws_ss_handle { - lws_ss_info_t info; /**< copy of stream creation info */ - struct lws_dll2 list; /**< pt lists active ss */ - struct lws_dll2 to_list; /**< pt lists ss with pending to-s */ - - struct lws_dll2_owner src_list; /**< sink's list of bound sources */ - - struct lws_context *context; /**< lws context we are created on */ - const lws_ss_policy_t *policy; /**< system policy for stream */ - - struct lws_sequencer *seq; /**< owning sequencer if any */ - struct lws *wsi; /**< the stream wsi if any */ - - void *nauthi; /**< the nauth plugin instance data */ - void *sauthi; /**< the sauth plugin instance data */ - - lws_ss_metadata_t *metadata; - const lws_ss_policy_t *rideshare; - - struct lws_ss_handle *h_sink; /**< sink we are bound to, or NULL */ - void *sink_obj;/**< sink's private object representing us */ - - lws_sorted_usec_list_t sul; - lws_ss_tx_ordinal_t txord; - - /* protocol-specific connection helpers */ - - union { - - /* ...for http-related protocols... */ - - struct { - - /* common to all http-related protocols */ - - /* incoming multipart parsing */ - - char boundary[24]; /* --boundary from headers */ - uint8_t boundary_len; /* length of --boundary */ - uint8_t boundary_seq; /* current match amount */ - uint8_t boundary_dashes; /* check for -- after */ - uint8_t boundary_post; /* swallow post CRLF */ - - uint8_t som:1; /* SOM has been sent */ - uint8_t any:1; /* any content has been sent */ - - - uint8_t good_respcode:1; /* 200 type response code */ - - union { - struct { /* LWSSSP_H1 */ - } h1; - struct { /* LWSSSP_H2 */ - } h2; - struct { /* LWSSSP_WS */ - } ws; - } u; - } http; - - /* details for non-http related protocols... */ -#if defined(LWS_ROLE_MQTT) - struct { - lws_mqtt_topic_elem_t topic_qos; - lws_mqtt_topic_elem_t sub_top; - lws_mqtt_subscribe_param_t sub_info; - } mqtt; -#endif - } u; - - unsigned long writeable_len; - - lws_ss_constate_t connstate;/**< public connection state */ - lws_ss_seq_state_t seqstate; /**< private connection state */ - - uint16_t retry; /**< retry / backoff tracking */ - int16_t temp16; - - uint8_t tsi; /**< service thread idx, usually 0 */ - uint8_t subseq; /**< emulate SOM tracking */ - uint8_t txn_ok; /**< 1 = transaction was OK */ - - uint8_t hanging_som:1; - uint8_t inside_msg:1; - uint8_t being_serialized:1; /* we are not the consumer */ -} lws_ss_handle_t; - -/* connection helper that doesn't need to hang around after connection starts */ - -union lws_ss_contemp { -#if defined(LWS_ROLE_MQTT) - lws_mqtt_client_connect_param_t ccp; -#endif -}; - -/* - * When allocating the opaque handle, we overallocate for: - * - * 1) policy->nauth_plugin->alloc (.nauthi) if any - * 2) policy->sauth_plugin->alloc (.sauthi) if any - * 3) copy of creation info stream type pointed to by info.streamtype... this - * may be arbitrarily long and since it may be coming from socket ipc and be - * temporary at creation time, we need a place for the copy to stay in scope - * 4) copy of info->streamtype contents - */ - - -/* the user object allocation is immediately after the ss object allocation */ -#define ss_to_userobj(ss) ((void *)&(ss)[1]) - -/* - * serialization parser state - */ - -enum { - KIND_C_TO_P, - KIND_SS_TO_P, -}; - -struct lws_ss_serialization_parser { - char streamtype[32]; - char rideshare[32]; - char metadata_name[32]; - - uint64_t ust_pwait; - - lws_ss_metadata_t *ssmd; - - int ps; - int ctr; - - uint32_t usd_phandling; - uint32_t flags; - int32_t temp32; - - int32_t txcr_out; - int32_t txcr_in; - uint16_t rem; - - uint8_t type; - uint8_t frag1; - uint8_t slen; - uint8_t rsl_pos; - uint8_t rsl_idx; -}; - -/* - * Unlike locally-fulfilled SS, SSS doesn't have to hold metadata on client side - * but pass it through to the proxy. The client side doesn't know the real - * metadata names that are available in the policy (since it's hardcoded in code - * no point passing them back to the client from the policy). Because of that, - * it doesn't know how many to allocate when we create the sspc_handle either. - * - * So we use a linked-list of changed-but-not-yet-proxied metadata allocated - * on the heap and items removed as they are proxied out. Anything on the list - * is sent to the proxy before any requested tx is handled. - * - * This is also used to queue tx credit changes - */ - -typedef struct lws_sspc_metadata { - lws_dll2_t list; - char name[32]; /* empty string, then actually TCXR */ - size_t len; - int tx_cr_adjust; - - /* the value of length .len is overallocated after this */ -} lws_sspc_metadata_t; - - -typedef struct lws_sspc_handle { - char rideshare_list[128]; - lws_ss_info_t ssi; - lws_sorted_usec_list_t sul_retry; - - struct lws_ss_serialization_parser parser; - - lws_dll2_owner_t metadata_owner; - - struct lws_dll2 client_list; - struct lws_tx_credit txc; - - struct lws *cwsi; - - struct lws_dsh *dsh; - struct lws_context *context; - - lws_usec_t us_earliest_write_req; - - lws_ss_conn_states_t state; - - int16_t temp16; - - uint32_t ord; - - uint8_t rideshare_ofs[4]; - uint8_t conn_req; - uint8_t rsidx; - - uint8_t destroying:1; -} lws_sspc_handle_t; - -int -lws_ss_deserialize_parse(struct lws_ss_serialization_parser *par, - struct lws_context *context, - struct lws_dsh *dsh, const uint8_t *cp, size_t len, - lws_ss_conn_states_t *state, void *parconn, - lws_ss_handle_t **pss, lws_ss_info_t *ssi, char client); -int -lws_ss_serialize_rx_payload(struct lws_dsh *dsh, const uint8_t *buf, - size_t len, int flags, const char *rsp); -int -lws_ss_deserialize_tx_payload(struct lws_dsh *dsh, struct lws *wsi, - lws_ss_tx_ordinal_t ord, uint8_t *buf, - size_t *len, int *flags); -int -lws_ss_serialize_state(struct lws_dsh *dsh, lws_ss_constate_t state, - lws_ss_tx_ordinal_t ack); - -void -lws_ss_serialize_state_transition(lws_ss_conn_states_t *state, int new_state); - -const lws_ss_policy_t * -lws_ss_policy_lookup(const struct lws_context *context, const char *streamtype); - -/* can be used as a cb from lws_dll2_foreach_safe() to destroy ss */ -int -lws_ss_destroy_dll(struct lws_dll2 *d, void *user); - -int -lws_sspc_destroy_dll(struct lws_dll2 *d, void *user); - - -int -lws_ss_policy_parse_begin(struct lws_context *context); - -int -lws_ss_policy_parse(struct lws_context *context, const uint8_t *buf, size_t len); - -int -lws_ss_policy_set(struct lws_context *context, const char *name); - -int -lws_ss_policy_parse_abandon(struct lws_context *context); - -int -lws_ss_sys_fetch_policy(struct lws_context *context); - -int -lws_ss_event_helper(lws_ss_handle_t *h, lws_ss_constate_t cs); - -int -lws_ss_backoff(lws_ss_handle_t *h); - -int -lws_ss_set_timeout_us(lws_ss_handle_t *h, lws_usec_t us); - -void -ss_proxy_onward_txcr(void *userobj, int bump); - -int -lws_ss_serialize_txcr(struct lws_dsh *dsh, int txcr); - -int -lws_ss_sys_auth_api_amazon_com(struct lws_context *context); - -lws_ss_metadata_t * -lws_ss_get_handle_metadata(struct lws_ss_handle *h, const char *name); -lws_ss_metadata_t * -lws_ss_policy_metadata_index(const lws_ss_policy_t *p, size_t index); - -lws_ss_metadata_t * -lws_ss_policy_metadata(const lws_ss_policy_t *p, const char *name); - -int -lws_ss_exp_cb_metadata(void *priv, const char *name, char *out, size_t *pos, - size_t olen, size_t *exp_ofs); - -int -_lws_ss_client_connect(lws_ss_handle_t *h, int is_retry); - -int -lws_ss_sys_cpd(struct lws_context *cx); - -typedef int (* const secstream_protocol_connect_munge_t)(lws_ss_handle_t *h, - char *buf, size_t len, struct lws_client_connect_info *i, - union lws_ss_contemp *ct); - -typedef int (* const secstream_protocol_add_txcr_t)(lws_ss_handle_t *h, int add); - -typedef int (* const secstream_protocol_get_txcr_t)(lws_ss_handle_t *h); - -struct ss_pcols { - const char *name; - const char *alpn; - const char *protocol_name; - const secstream_protocol_connect_munge_t munge; - const secstream_protocol_add_txcr_t tx_cr_add; - const secstream_protocol_get_txcr_t tx_cr_est; -}; - -extern const struct ss_pcols ss_pcol_h1; -extern const struct ss_pcols ss_pcol_h2; -extern const struct ss_pcols ss_pcol_ws; -extern const struct ss_pcols ss_pcol_mqtt; - -extern const struct lws_protocols protocol_secstream_h1; -extern const struct lws_protocols protocol_secstream_h2; -extern const struct lws_protocols protocol_secstream_ws; -extern const struct lws_protocols protocol_secstream_mqtt; - diff -Nru libwebsockets-4.0.20/lib/secure-streams/protocols/README.md libwebsockets-2.4.2/lib/secure-streams/protocols/README.md --- libwebsockets-4.0.20/lib/secure-streams/protocols/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/protocols/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -# Lws Protocol bindings for Secure Streams - -This directory contains the code wiring up normal lws protocols -to Secure Streams. - -## The lws_protocols callback - -This is the normal lws struct lws_protocols callback that handles events and -traffic on the lws protocol being supported. - -The various events and traffic are converted into calls using the Secure -Streams api, and Secure Streams events. - -## The connect_munge helper - -Different protocols have different semantics in the arguments to the client -connect function, this protocol-specific helper is called to munge the -connect_info struct to match the details of the protocol selected. - -The `ss->policy->aux` string is used to hold protocol-specific information -passed in the from the policy, eg, the URL path or websockets subprotocol -name. - -## The (library-private) ss_pcols export - -Each protocol binding exports two things to other parts of lws (they -are not exported to user code) - - - a struct lws_protocols, including a pointer to the callback - - - a struct ss_pcols describing how secure_streams should use, including - a pointer to the related connect_munge helper. - -In ./lib/core-net/vhost.c, enabled protocols are added to vhost protcols -lists so they may be used. And in ./lib/secure-streams/secure-streams.c, -enabled struct ss_pcols are listed and checked for matches when the user -creates a new Secure Stream. - diff -Nru libwebsockets-4.0.20/lib/secure-streams/protocols/ss-h1.c libwebsockets-2.4.2/lib/secure-streams/protocols/ss-h1.c --- libwebsockets-4.0.20/lib/secure-streams/protocols/ss-h1.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/protocols/ss-h1.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,590 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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 the glue that wires up h1 to Secure Streams. - */ - -#include - -#if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2) -#define LWS_WITH_SS_RIDESHARE -#endif - -#if defined(LWS_WITH_SS_RIDESHARE) -static int -ss_http_multipart_parser(lws_ss_handle_t *h, void *in, size_t len) -{ - uint8_t *q = (uint8_t *)in; - int pending_issue = 0, n = 0; - - /* let's stick it in the boundary state machine first */ - while (n < (int)len) { - if (h->u.http.boundary_seq != h->u.http.boundary_len) { - if (q[n] == h->u.http.boundary[h->u.http.boundary_seq]) - h->u.http.boundary_seq++; - else { - h->u.http.boundary_seq = 0; - h->u.http.boundary_dashes = 0; - h->u.http.boundary_post = 0; - } - goto around; - } - - /* - * We already matched the boundary string, now we're - * looking if there's a -- afterwards - */ - if (h->u.http.boundary_dashes < 2) { - if (q[n] == '-') { - h->u.http.boundary_dashes++; - goto around; - } - /* there was no final -- ... */ - } - - if (h->u.http.boundary_dashes == 2) { - /* - * It's an EOM boundary: issue pending + multipart EOP - */ - lwsl_debug("%s: seen EOP, n %d pi %d\n", - __func__, n, pending_issue); - /* - * It's possible we already started the decode before - * the end of the last packet. Then there is no - * remainder to send. - */ - if (n >= pending_issue + h->u.http.boundary_len + - (h->u.http.any ? 2 : 0) + 1) - h->info.rx(ss_to_userobj(h), - &q[pending_issue], - n - pending_issue - - h->u.http.boundary_len - 1 - - (h->u.http.any ? 2 : 0) /* crlf */, - (!h->u.http.som ? LWSSS_FLAG_SOM : 0) | - LWSSS_FLAG_EOM | LWSSS_FLAG_RELATED_END); - - /* - * Peer may not END_STREAM us - */ - return 0; - //return -1; - } - - /* how about --boundaryCRLF */ - - if (h->u.http.boundary_post < 2) { - if ((!h->u.http.boundary_post && q[n] == '\x0d') || - (h->u.http.boundary_post && q[n] == '\x0a')) { - h->u.http.boundary_post++; - goto around; - } - /* there was no final CRLF ... it's wrong */ - - return -1; - } - if (h->u.http.boundary_post != 2) - goto around; - - /* - * We have a starting "--boundaryCRLF" or intermediate - * "CRLF--boundaryCRLF" boundary - */ - lwsl_debug("%s: b_post = 2 (pi %d)\n", __func__, pending_issue); - h->u.http.boundary_seq = 0; - h->u.http.boundary_post = 0; - - if (n >= pending_issue && (h->u.http.any || !h->u.http.som)) { - /* Intermediate... do the EOM */ - lwsl_debug("%s: seen interm EOP n %d pi %d\n", __func__, - n, pending_issue); - /* - * It's possible we already started the decode before - * the end of the last packet. Then there is no - * remainder to send. - */ - if (n >= pending_issue + h->u.http.boundary_len + - (h->u.http.any ? 2 : 0)) - h->info.rx(ss_to_userobj(h), &q[pending_issue], - n - pending_issue - - h->u.http.boundary_len - - (h->u.http.any ? 2 /* crlf */ : 0), - (!h->u.http.som ? LWSSS_FLAG_SOM : 0) | - LWSSS_FLAG_EOM); - } - - /* Next message starts after this boundary */ - - pending_issue = n; - h->u.http.som = 0; - -around: - n++; - } - - if (pending_issue != n) { - h->info.rx(ss_to_userobj(h), &q[pending_issue], n - pending_issue, - (!h->u.http.som ? LWSSS_FLAG_SOM : 0)); - h->u.http.any = 1; - h->u.http.som = 1; - } - - return 0; -} -#endif - -static const uint8_t blob_idx[] = { - LWS_SYSBLOB_TYPE_AUTH, - LWS_SYSBLOB_TYPE_DEVICE_SERIAL, - LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION, - LWS_SYSBLOB_TYPE_DEVICE_TYPE, -}; - -int -secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi); - uint8_t buf[LWS_PRE + 1520], *p = &buf[LWS_PRE], - *end = &buf[sizeof(buf) - 1]; - int f = 0, m, status, txr; - char conceal_eom = 0; - size_t buflen; - - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - assert(h); - assert(h->policy); - lwsl_info("%s: h: %p, %s CLIENT_CONNECTION_ERROR: %s\n", __func__, - h, h->policy->streamtype, in ? (char *)in : "(null)"); - lws_ss_event_helper(h, LWSSSCS_UNREACHABLE); - h->wsi = NULL; - lws_ss_backoff(h); - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - if (!h) - break; - lwsl_info("%s: h: %p, %s LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", - __func__, h, - h->policy ? h->policy->streamtype : "no policy"); - h->wsi = NULL; - //bad = status != 200; - //lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) && - !h->txn_ok && !wsi->context->being_destroyed) - lws_ss_backoff(h); - else - h->seqstate = SSSEQ_IDLE; - - if (lws_ss_event_helper(h, LWSSSCS_DISCONNECTED)) - lws_ss_destroy(&h); - break; - - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - status = lws_http_client_http_response(wsi); - lwsl_info("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: %d\n", __func__, status); - // if (!status) - /* it's just telling use we connected / joined the nwsi */ - // break; - h->u.http.good_respcode = (status >= 200 && status < 300); - // lwsl_err("%s: good resp %d %d\n", __func__, status, h->u.http.good_respcode); - - if (h->u.http.good_respcode) - lwsl_info("%s: Connected streamtype %s, %d\n", __func__, - h->policy->streamtype, status); - else - lwsl_warn("%s: Connected streamtype %s, BAD %d\n", __func__, - h->policy->streamtype, status); - - h->hanging_som = 0; - - h->retry = 0; - h->seqstate = SSSEQ_CONNECTED; - lws_ss_set_timeout_us(h, LWS_SET_TIMER_USEC_CANCEL); - lws_ss_event_helper(h, LWSSSCS_CONNECTED); - - /* - * Since it's an http transaction we initiated... this is - * proof of connection validity - */ - lws_validity_confirmed(wsi); - -#if defined(LWS_WITH_SS_RIDESHARE) - - if (lws_hdr_copy(wsi, (char *)buf, sizeof(buf), - WSI_TOKEN_HTTP_CONTENT_TYPE) > 0 && - /* multipart/form-data; - * boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */ - - (!strncmp((char *)buf, "multipart/form-data", 19) || - !strncmp((char *)buf, "multipart/related", 17))) { - struct lws_tokenize ts; - lws_tokenize_elem e; - - // puts((const char *)buf); - - memset(&ts, 0, sizeof(ts)); - ts.start = (char *)buf; - ts.len = strlen(ts.start); - ts.flags = LWS_TOKENIZE_F_RFC7230_DELIMS | - LWS_TOKENIZE_F_SLASH_NONTERM | - LWS_TOKENIZE_F_MINUS_NONTERM; - - h->u.http.boundary[0] = '\0'; - do { - e = lws_tokenize(&ts); - if (e == LWS_TOKZE_TOKEN_NAME_EQUALS && - !strncmp(ts.token, "boundary", 8) && - ts.token_len == 8) { - e = lws_tokenize(&ts); - if (e != LWS_TOKZE_TOKEN) - goto malformed; - h->u.http.boundary[0] = '\x0d'; - h->u.http.boundary[1] = '\x0a'; - h->u.http.boundary[2] = '-'; - h->u.http.boundary[3] = '-'; - lws_strnncpy(h->u.http.boundary + 4, - ts.token, ts.token_len, - sizeof(h->u.http.boundary) - 4); - h->u.http.boundary_len = ts.token_len + 4; - h->u.http.boundary_seq = 2; - h->u.http.boundary_dashes = 0; - } - } while (e > 0); - lwsl_info("%s: multipart boundary '%s' len %d\n", __func__, - h->u.http.boundary, h->u.http.boundary_len); - - /* inform the ss that a related message group begins */ - - if (h->u.http.boundary[0]) - h->info.rx(ss_to_userobj(h), NULL, 0, - LWSSS_FLAG_RELATED_START); - - // lws_header_table_detach(wsi, 0); - } - break; -malformed: - lwsl_notice("%s: malformed multipart header\n", __func__); - return -1; -#else - break; -#endif - - case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: - if (h->writeable_len) - wsi->http.writeable_len = h->writeable_len; - - { - uint8_t **p = (uint8_t **)in, *end = (*p) + len, - *oin = *(uint8_t **)in; - - /* - * blob-based headers - */ - - for (m = 0; m < _LWSSS_HBI_COUNT; m++) { - int o = 0, n; - - if (!h->policy->u.http.blob_header[m]) - continue; - - if (m == LWSSS_HBI_AUTH && - h->policy->u.http.auth_preamble) - o = lws_snprintf((char *)buf, sizeof(buf), "%s", - h->policy->u.http.auth_preamble); - - if (o > (int)sizeof(buf) - 2) - return -1; - - buflen = sizeof(buf) - o - 2; - n = lws_system_blob_get( - lws_system_get_blob(wsi->context, blob_idx[m], 0), - buf + o, &buflen, 0); - if (n < 0) - return -1; - - buf[o + buflen] = '\0'; - lwsl_debug("%s: adding blob %d: %s\n", __func__, m, buf); - - if (lws_add_http_header_by_name(wsi, - (uint8_t *)h->policy->u.http.blob_header[m], - buf, buflen + o, p, end)) - return -1; - } - - /* - * metadata-based headers - */ - - for (m = 0; m < h->policy->metadata_count; m++) { - lws_ss_metadata_t *polmd; - - /* has to have a header string listed */ - if (!h->metadata[m].value) - continue; - - polmd = lws_ss_policy_metadata_index(h->policy, m); - - assert(polmd); - /* has to have a value */ - if (polmd->value && ((uint8_t *)polmd->value)[0]) { - if (lws_add_http_header_by_name(wsi, - polmd->value, - h->metadata[m].value, - h->metadata[m].length, p, end)) - return -1; - } - } - - /* - * Content-length on POST / PUT if we have the length information - */ - - if ((!strcmp(h->policy->u.http.method, "POST") || - !strcmp(h->policy->u.http.method, "PUT")) && - wsi->http.writeable_len) { - if (!(h->policy->flags & - LWSSSPOLF_HTTP_NO_CONTENT_LENGTH)) { - int n = lws_snprintf((char *)buf, 20, "%u", - (unsigned int)wsi->http.writeable_len); - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_LENGTH, - buf, n, p, end)) - return -1; - } - lws_client_http_body_pending(wsi, 1); - } - - (void)oin; - // if (*p != oin) - // lwsl_hexdump_notice(oin, lws_ptr_diff(*p, oin)); - - } - - break; - - /* chunks of chunked content, with header removed */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - lwsl_debug("%s: RECEIVE_CLIENT_HTTP_READ: read %d\n", - __func__, (int)len); - if (!h) - return 0; - -#if defined(LWS_WITH_SS_RIDESHARE) - if (h->u.http.boundary[0]) - return ss_http_multipart_parser(h, in, len); -#endif - - if (!h->subseq) { - f |= LWSSS_FLAG_SOM; - h->hanging_som = 1; - h->subseq = 1; - } - - // lwsl_notice("%s: HTTP_READ: client side sent len %d fl 0x%x\n", - // __func__, (int)len, (int)f); - - if (h->info.rx(ss_to_userobj(h), (const uint8_t *)in, len, f) < 0) - return -1; - - return 0; /* don't passthru */ - - /* uninterpreted http content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - { - char *px = (char *)buf + LWS_PRE; /* guarantees LWS_PRE */ - int lenx = sizeof(buf) - LWS_PRE; - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - lws_set_timeout(wsi, 99, 30); - - return 0; /* don't passthru */ - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_debug("%s: LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n", __func__); - if (h->hanging_som) - h->info.rx(ss_to_userobj(h), NULL, 0, LWSSS_FLAG_EOM); - - wsi->http.writeable_len = h->writeable_len = 0; - - if (h->u.http.good_respcode) - lws_ss_event_helper(h, LWSSSCS_QOS_ACK_REMOTE); - else - lws_ss_event_helper(h, LWSSSCS_QOS_NACK_REMOTE); - - h->wsi = NULL; - h->txn_ok = 1; - //bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: - lwsl_info("%s: LWS_CALLBACK_CLIENT_HTTP_WRITEABLE\n", __func__); - if (!h) - return 0; - - if (!h->rideshare) - h->rideshare = h->policy; - -#if defined(LWS_WITH_SS_RIDESHARE) - if (!h->inside_msg && h->rideshare->u.http.multipart_name) - lws_client_http_multipart(wsi, - h->rideshare->u.http.multipart_name, - h->rideshare->u.http.multipart_filename, - h->rideshare->u.http.multipart_content_type, - (char **)&p, (char *)end); - - buflen = lws_ptr_diff(end, p); - if (h->policy->u.http.multipart_name) - buflen -= 24; /* allow space for end of multipart */ - -#endif - - txr = h->info.tx(ss_to_userobj(h), h->txord++, p, &buflen, &f); - if (txr < 0) { - lwsl_debug("%s: tx handler asked to close\n", __func__); - return -1; - } - if (txr > 0) { - /* don't want to send anything */ - lwsl_debug("%s: dont want to write\n", __func__); - return 0; - } - - lwsl_info("%s: WRITEABLE: user tx says len %d fl 0x%x\n", - __func__, (int)buflen, (int)f); - - p += buflen; - - if (f & LWSSS_FLAG_EOM) { -#if defined(LWS_WITH_SS_RIDESHARE) - conceal_eom = 1; - /* end of rideshares */ - if (!h->rideshare->rideshare_streamtype) { - lws_client_http_body_pending(wsi, 0); - if (h->rideshare->u.http.multipart_name) - lws_client_http_multipart(wsi, NULL, NULL, NULL, - (char **)&p, (char *)end); - conceal_eom = 0; - } else { -#endif - h->rideshare = lws_ss_policy_lookup(wsi->context, - h->rideshare->rideshare_streamtype); - lws_callback_on_writable(wsi); -#if defined(LWS_WITH_SS_RIDESHARE) - } -#endif - - h->inside_msg = 0; - } else { - /* otherwise we can spin with zero length writes */ - if (!f && !lws_ptr_diff(p, buf + LWS_PRE)) - break; - h->inside_msg = 1; - lws_callback_on_writable(wsi); - } - - lwsl_info("%s: lws_write %d %d\n", __func__, - lws_ptr_diff(p, buf + LWS_PRE), f); - - if (lws_write(wsi, buf + LWS_PRE, lws_ptr_diff(p, buf + LWS_PRE), - (!conceal_eom && (f & LWSSS_FLAG_EOM)) ? - LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP) != - (int)lws_ptr_diff(p, buf + LWS_PRE)) { - lwsl_err("%s: write failed\n", __func__); - return -1; - } - - lws_set_timeout(wsi, 0, 0); - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -const struct lws_protocols protocol_secstream_h1 = { - "lws-secstream-h1", - secstream_h1, - 0, - 0, -}; - -/* - * Munge connect info according to protocol-specific considerations... this - * usually means interpreting aux in a protocol-specific way and using the - * pieces at connection setup time, eg, http url pieces. - * - * len bytes of buf can be used for things with scope until after the actual - * connect. - */ - -static int -secstream_connect_munge_h1(lws_ss_handle_t *h, char *buf, size_t len, - struct lws_client_connect_info *i, - union lws_ss_contemp *ct) -{ - size_t used_in, used_out; - lws_strexp_t exp; - - if (!h->policy->u.http.url) - return 0; - -#if defined(LWS_WITH_SS_RIDESHARE) - if (h->policy->flags & LWSSSPOLF_HTTP_MULTIPART) - i->ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME; - - if (h->policy->flags & LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED) - i->ssl_connection |= LCCSCF_HTTP_X_WWW_FORM_URLENCODED; -#endif - - /* protocol aux is the path part */ - - i->path = buf; - buf[0] = '/'; - - lws_strexp_init(&exp, (void *)h, lws_ss_exp_cb_metadata, buf + 1, len - 1); - - if (lws_strexp_expand(&exp, h->policy->u.http.url, - strlen(h->policy->u.http.url), - &used_in, &used_out) != LSTRX_DONE) - return 1; - - return 0; -} - - -const struct ss_pcols ss_pcol_h1 = { - "h1", - "http/1.1", - "lws-secstream-h1", - secstream_connect_munge_h1, - NULL -}; diff -Nru libwebsockets-4.0.20/lib/secure-streams/protocols/ss-h2.c libwebsockets-2.4.2/lib/secure-streams/protocols/ss-h2.c --- libwebsockets-4.0.20/lib/secure-streams/protocols/ss-h2.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/protocols/ss-h2.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,183 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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 - -extern int -secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len); - -static int -secstream_h2(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi); - int n; - - switch (reason) { - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - -#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) - if (h->being_serialized) { - /* - * We are the proxy-side SS for a remote client... we - * need to inform the client about the initial tx credit - * to write to it that the remote h2 server set up - */ - lwsl_info("%s: reporting initial tx cr from server %d\n", - __func__, wsi->txc.tx_cr); - ss_proxy_onward_txcr((void *)&h[1], wsi->txc.tx_cr); - } -#endif - - n = secstream_h1(wsi, reason, user, in, len); - - if (!n && (h->policy->flags & LWSSSPOLF_LONG_POLL)) { - lwsl_notice("%s: h2 client %p entering LONG_POLL\n", - __func__, wsi); - lws_h2_client_stream_long_poll_rxonly(wsi); - } - return n; - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - // lwsl_err("%s: h2 COMPLETED_CLIENT_HTTP\n", __func__); - h->info.rx(ss_to_userobj(h), NULL, 0, LWSSS_FLAG_EOM); - h->wsi = NULL; - h->txn_ok = 1; - //bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - case LWS_CALLBACK_WSI_TX_CREDIT_GET: - /* - * The peer has sent us additional tx credit... - */ - lwsl_info("%s: LWS_CALLBACK_WSI_TX_CREDIT_GET: %d\n", - __func__, (int32_t)len); - -#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) - if (h->being_serialized) - /* we are the proxy-side SS for a remote client */ - ss_proxy_onward_txcr((void *)&h[1], (int)len); -#endif - break; - - default: - break; - } - - return secstream_h1(wsi, reason, user, in, len); -} - -const struct lws_protocols protocol_secstream_h2 = { - "lws-secstream-h2", - secstream_h2, - 0, - 0, -}; - -/* - * Munge connect info according to protocol-specific considerations... this - * usually means interpreting aux in a protocol-specific way and using the - * pieces at connection setup time, eg, http url pieces. - * - * len bytes of buf can be used for things with scope until after the actual - * connect. - */ - -int -secstream_connect_munge_h2(lws_ss_handle_t *h, char *buf, size_t len, - struct lws_client_connect_info *i, - union lws_ss_contemp *ct) -{ - if (h->policy->flags & LWSSSPOLF_QUIRK_NGHTTP2_END_STREAM) - i->ssl_connection |= LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM; - - if (h->policy->flags & LWSSSPOLF_H2_QUIRK_OVERFLOWS_TXCR) - i->ssl_connection |= LCCSCF_H2_QUIRK_OVERFLOWS_TXCR; - - if (h->policy->flags & LWSSSPOLF_HTTP_MULTIPART) - i->ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME; - - if (h->policy->flags & LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED) - i->ssl_connection |= LCCSCF_HTTP_X_WWW_FORM_URLENCODED; - - i->ssl_connection |= LCCSCF_PIPELINE; - - i->alpn = "h2"; - - /* initial peer tx credit */ - - if (h->info.manual_initial_tx_credit) { - i->ssl_connection |= LCCSCF_H2_MANUAL_RXFLOW; - i->manual_initial_tx_credit = h->info.manual_initial_tx_credit; - lwsl_info("%s: initial txcr %d\n", __func__, - i->manual_initial_tx_credit); - } - - if (!h->policy->u.http.url) - return 0; - - /* protocol aux is the path part */ - - i->path = buf; - lws_snprintf(buf, len, "/%s", h->policy->u.http.url); - - return 0; -} - -static int -secstream_tx_credit_add_h2(lws_ss_handle_t *h, int add) -{ - lwsl_info("%s: h %p: add %d\n", __func__, h, add); - if (h->wsi) - return lws_h2_update_peer_txcredit(h->wsi, LWS_H2_STREAM_SID, add); - - return 0; -} - -static int -secstream_tx_credit_est_h2(lws_ss_handle_t *h) -{ - if (h->wsi) { - lwsl_info("%s: h %p: est %d\n", __func__, h, - lws_h2_get_peer_txcredit_estimate(h->wsi)); - - return lws_h2_get_peer_txcredit_estimate(h->wsi); - } - - lwsl_info("%s: h %p: Unknown (0)\n", __func__, h); - - return 0; -} - -const struct ss_pcols ss_pcol_h2 = { - "h2", - NULL, - "lws-secstream-h2", - secstream_connect_munge_h2, - secstream_tx_credit_add_h2, - secstream_tx_credit_est_h2 -}; diff -Nru libwebsockets-4.0.20/lib/secure-streams/protocols/ss-mqtt.c libwebsockets-2.4.2/lib/secure-streams/protocols/ss-mqtt.c --- libwebsockets-4.0.20/lib/secure-streams/protocols/ss-mqtt.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/protocols/ss-mqtt.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,254 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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 - -static int -secstream_mqtt(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi); - lws_mqtt_publish_param_t mqpp, *pmqpp; - uint8_t buf[LWS_PRE + 1400]; - int f = 0, txr; - size_t buflen; - - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_info("%s: CLIENT_CONNECTION_ERROR: %s\n", __func__, - in ? (char *)in : "(null)"); - if (!h) - break; - lws_ss_event_helper(h, LWSSSCS_UNREACHABLE); - h->wsi = NULL; - lws_ss_backoff(h); - break; - - case LWS_CALLBACK_MQTT_CLIENT_CLOSED: - if (!h) - break; - f = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED); - if (h->wsi) - lws_set_opaque_user_data(h->wsi, NULL); - h->wsi = NULL; - if (f) { - lws_ss_destroy(&h); - break; - } - - if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) && - !h->txn_ok && !wsi->context->being_destroyed) - lws_ss_backoff(h); - break; - - case LWS_CALLBACK_MQTT_CLIENT_ESTABLISHED: - /* - * Make sure the handle wsi points to the stream wsi not the - * original nwsi, in the case it was migrated - */ - h->wsi = wsi; - h->retry = 0; - h->seqstate = SSSEQ_CONNECTED; - lws_ss_set_timeout_us(h, LWS_SET_TIMER_USEC_CANCEL); - lws_ss_event_helper(h, LWSSSCS_CONNECTED); - if (h->policy->u.mqtt.topic) - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_MQTT_CLIENT_RX: - // lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: read %d\n", (int)len); - if (!h) - return 0; - - pmqpp = (lws_mqtt_publish_param_t *)in; - - f = 0; - if (!pmqpp->payload_pos) - f |= LWSSS_FLAG_SOM; - if (pmqpp->payload_pos + len == pmqpp->payload_len) - f |= LWSSS_FLAG_EOM; - - h->subseq = 1; - - if (h->info.rx(ss_to_userobj(h), (const uint8_t *)pmqpp->payload, - len, f) < 0) - return -1; - - return 0; /* don't passthru */ - - case LWS_CALLBACK_MQTT_SUBSCRIBED: - wsi->mqtt->done_subscribe = 1; - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_MQTT_ACK: - lws_ss_event_helper(h, LWSSSCS_QOS_ACK_REMOTE); - break; - - case LWS_CALLBACK_MQTT_CLIENT_WRITEABLE: - if (!h) - return 0; - lwsl_notice("%s: ss %p: WRITEABLE\n", __func__, h); - - if (h->seqstate != SSSEQ_CONNECTED) { - lwsl_warn("%s: seqstate %d\n", __func__, h->seqstate); - break; - } - - if (h->policy->u.mqtt.subscribe && !wsi->mqtt->done_subscribe) { - - /* - * The policy says to subscribe to something, and we - * haven't done it yet - */ - - lwsl_warn("%s: subscribing %s\n", __func__, h->policy->u.mqtt.subscribe); - - memset(&h->u.mqtt.sub_top, 0, sizeof(h->u.mqtt.sub_top)); - h->u.mqtt.sub_top.name = h->policy->u.mqtt.subscribe; - h->u.mqtt.sub_top.qos = h->policy->u.mqtt.qos; - memset(&h->u.mqtt.sub_info, 0, sizeof(h->u.mqtt.sub_info)); - h->u.mqtt.sub_info.num_topics = 1; - h->u.mqtt.sub_info.topic = &h->u.mqtt.sub_top; - - if (lws_mqtt_client_send_subcribe(wsi, &h->u.mqtt.sub_info)) { - lwsl_notice("%s: unable to subscribe", __func__); - return -1; - } - - return 0; - } - - - buflen = sizeof(buf) - LWS_PRE; - txr = h->info.tx(ss_to_userobj(h), h->txord++, buf + LWS_PRE, - &buflen, &f); - if (txr < 0) { - lwsl_debug("%s: tx handler asked to close\n", __func__); - return -1; - } - if (txr > 0) - /* don't want to send anything */ - return 0; - - memset(&mqpp, 0, sizeof(mqpp)); - mqpp.topic = (char *)h->policy->u.mqtt.topic; - mqpp.topic_len = strlen(mqpp.topic); - mqpp.packet_id = h->txord - 1; - mqpp.payload = buf + LWS_PRE; - if (h->writeable_len) - mqpp.payload_len = h->writeable_len; - else - mqpp.payload_len = buflen; - - lwsl_notice("%s: payload len %d\n", __func__, (int)mqpp.payload_len); - - mqpp.qos = h->policy->u.mqtt.qos; - - if (lws_mqtt_client_send_publish(wsi, &mqpp, - (const char *)buf + LWS_PRE, buflen, - f & LWSSS_FLAG_EOM)) { - lwsl_notice("%s: failed to publish\n", __func__); - - return -1; - } - - return 0; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -const struct lws_protocols protocol_secstream_mqtt = { - "lws-secstream-mqtt", - secstream_mqtt, - 0, - 0, -}; -/* - * Munge connect info according to protocol-specific considerations... this - * usually means interpreting aux in a protocol-specific way and using the - * pieces at connection setup time, eg, http url pieces. - * - * len bytes of buf can be used for things with scope until after the actual - * connect. - * - * For ws, protocol aux is ; - */ - -static int -secstream_connect_munge_mqtt(lws_ss_handle_t *h, char *buf, size_t len, - struct lws_client_connect_info *i, - union lws_ss_contemp *ct) -{ - memset(&ct->ccp, 0, sizeof(ct->ccp)); - - ct->ccp.client_id = "lwsMqttClient"; - ct->ccp.keep_alive = h->policy->u.mqtt.keep_alive; - ct->ccp.clean_start = h->policy->u.mqtt.clean_start; - ct->ccp.will_param.topic = h->policy->u.mqtt.will_topic; - ct->ccp.will_param.message = h->policy->u.mqtt.will_message; - ct->ccp.will_param.qos = h->policy->u.mqtt.will_qos; - ct->ccp.will_param.retain = h->policy->u.mqtt.will_retain; - - lwsl_notice("%s\n", __func__); - - h->u.mqtt.topic_qos.name = h->policy->u.mqtt.subscribe; - h->u.mqtt.topic_qos.qos = h->policy->u.mqtt.qos; - - i->method = "MQTT"; - i->mqtt_cp = &ct->ccp; - - i->alpn = "x-amzn-mqtt-ca"; - - /* share connections where possible */ - i->ssl_connection |= LCCSCF_PIPELINE; - -/* - if (!h->policy->u.http.url) - return 0; - - // protocol aux is the path part ; ws subprotocol name - - i->path = NULL; - lws_snprintf(buf, len, "/%s", h->policy->u.mqtt.topic); - -// i->protocol = h->policy->u.mqtt.u.ws.subprotocol; - - lwsl_notice("%s: url %s, ws subprotocol %s\n", __func__, buf, i->protocol); -*/ - return 0; -} - -const struct ss_pcols ss_pcol_mqtt = { - "MQTT", - "x-amzn-mqtt-ca", //"mqtt/3.1.1", - "lws-secstream-mqtt", - secstream_connect_munge_mqtt -}; diff -Nru libwebsockets-4.0.20/lib/secure-streams/protocols/ss-ws.c libwebsockets-2.4.2/lib/secure-streams/protocols/ss-ws.c --- libwebsockets-4.0.20/lib/secure-streams/protocols/ss-ws.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/protocols/ss-ws.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,171 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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 - -static int -secstream_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi); - uint8_t buf[LWS_PRE + 1400]; - int f = 0, f1, txr; - size_t buflen; - - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_info("%s: CLIENT_CONNECTION_ERROR: %s\n", __func__, - in ? (char *)in : "(null)"); - if (!h) - break; - lws_ss_event_helper(h, LWSSSCS_UNREACHABLE); - h->wsi = NULL; - lws_ss_backoff(h); - break; - - case LWS_CALLBACK_CLIENT_CLOSED: - if (!h) - break; - f = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED); - if (h->wsi) - lws_set_opaque_user_data(h->wsi, NULL); - h->wsi = NULL; - - if (f) { - lws_ss_destroy(&h); - break; - } - - if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) && - !h->txn_ok && !wsi->context->being_destroyed) - lws_ss_backoff(h); - break; - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - h->retry = 0; - h->seqstate = SSSEQ_CONNECTED; - lws_ss_set_timeout_us(h, LWS_SET_TIMER_USEC_CANCEL); - lws_ss_event_helper(h, LWSSSCS_CONNECTED); - break; - - case LWS_CALLBACK_CLIENT_RECEIVE: - // lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: read %d\n", (int)len); - if (!h) - return 0; - if (lws_is_first_fragment(wsi)) - f |= LWSSS_FLAG_SOM; - if (lws_is_final_fragment(wsi)) - f |= LWSSS_FLAG_EOM; - // lws_frame_is_binary(wsi); - - h->subseq = 1; - - if (h->info.rx(ss_to_userobj(h), (const uint8_t *)in, len, f) < 0) - return -1; - - return 0; /* don't passthru */ - - case LWS_CALLBACK_CLIENT_WRITEABLE: - if (!h) - return 0; - // lwsl_notice("%s: ss %p: WRITEABLE\n", __func__, h); - - if (h->seqstate != SSSEQ_CONNECTED) { - lwsl_warn("%s: seqstate %d\n", __func__, h->seqstate); - break; - } - - buflen = sizeof(buf) - LWS_PRE; - txr = h->info.tx(ss_to_userobj(h), h->txord++, buf + LWS_PRE, - &buflen, &f); - if (txr < 0) { - lwsl_debug("%s: tx handler asked to close\n", __func__); - return -1; - } - if (txr > 0) - /* don't want to send anything */ - return 0; - - f1 = lws_write_ws_flags(LWS_WRITE_BINARY, - !!(f & LWSSS_FLAG_SOM), - !!(f & LWSSS_FLAG_EOM)); - - if (lws_write(wsi, buf + LWS_PRE, buflen, f1) != (int)buflen) { - lwsl_err("%s: write failed\n", __func__); - return -1; - } - - return 0; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -const struct lws_protocols protocol_secstream_ws = { - "lws-secstream-ws", - secstream_ws, - 0, - 0, -}; -/* - * Munge connect info according to protocol-specific considerations... this - * usually means interpreting aux in a protocol-specific way and using the - * pieces at connection setup time, eg, http url pieces. - * - * len bytes of buf can be used for things with scope until after the actual - * connect. - * - * For ws, protocol aux is ; - */ - -static int -secstream_connect_munge_ws(lws_ss_handle_t *h, char *buf, size_t len, - struct lws_client_connect_info *i, - union lws_ss_contemp *ct) -{ - lwsl_notice("%s\n", __func__); - - if (!h->policy->u.http.url) - return 0; - - /* protocol aux is the path part ; ws subprotocol name */ - - i->path = h->policy->u.http.url; - lws_snprintf(buf, len, "/%s", h->policy->u.http.url); - - i->protocol = h->policy->u.http.u.ws.subprotocol; - - lwsl_notice("%s: url %s, ws subprotocol %s\n", __func__, buf, i->protocol); - - return 0; -} - -const struct ss_pcols ss_pcol_ws = { - "ws", "http/1.1", "lws-secstream-ws", secstream_connect_munge_ws -}; diff -Nru libwebsockets-4.0.20/lib/secure-streams/README.md libwebsockets-2.4.2/lib/secure-streams/README.md --- libwebsockets-4.0.20/lib/secure-streams/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,465 +0,0 @@ -# Secure Streams - -Secure Streams is a client api that strictly separates payload from any metadata. -That includes the endpoint address for the connection, the tls CA and even the -protocol used to connect to the endpoint. - -The user api just receives and transmits payload, and receives advisory connection -state information. - -The details about how the connections for different types of secure stream should -be made are held in JSON "policy database" initially passed in to the context -creation, but able to be updated from a remote copy. - -![overview](../doc-assets/ss-explain.png) - -## Convention for rx and tx callback return - -Function|Return|Meaning ----|---|--- -tx|0|Send the amount of `buf` stored in `*len` -tx|>0|Do not send anything -tx|<0|Finished with stream -rx|>=0|accepted -rx|<0|Finished with stream - - -# JSON Policy Database - -Example JSON policy... formatting is shown for clarity but whitespace can be -omitted in the actual policy. - -Ordering is not critical in itself, but forward references are not allowed, -things must be defined before they are allowed to be referenced later in the -JSON. - - -``` -{ - "release": "01234567", - "product": "myproduct", - "schema-version": 1, - "retry": [{ - "default": { - "backoff": [1000, 2000, 3000, 5000, 10000], - "conceal": 5, - "jitterpc": 20 - } - }], - "certs": [{ - "isrg_root_x1": "MIIFazCCA1OgAw...AnX5iItreGCc=" - }, { - "LEX3_isrg_root_x1": "MIIFjTCCA3WgAwIB...WEsikxqEt" - }], - "trust_stores": [{ - "le_via_isrg": ["isrg_root_x1", "LEX3_isrg_root_x1"] - }], - "s": [{ - "mintest": { - "endpoint": "warmcat.com", - "port": 4443, - "protocol": "h1get", - "aux": "index.html", - "plugins": [], - "tls": true, - "opportunistic": true, - "retry": "default", - "tls_trust_store": "le_via_isrg" - } - }] -} -``` - -### `Release` - -Identifies the policy version - -### `Product` - -Identifies the product the policy should apply to - -### `Schema-version` - -The minimum version of the policy parser required to parse this policy - -### `via-socks5` - -Optional redirect for Secure Streams client traffic through a socks5 -proxy given in the format `address:port`, eg, `127.0.0.1:12345`. - -### `retry` - -A list of backoff schemes referred to in the policy - -### `backoff` - -An array of ms delays for each retry in turn - -### `conceal` - -The number of retries to conceal from higher layers before giving errors. If -this is larger than the number of times in the backoff array, then the last time -is used for the extra delays - -### `jitterpc` - -Percentage of the delay times mentioned in the backoff array that may be -randomly added to the figure from the array. For example with an array entry of -1000ms, and jitterpc of 20%, actual delays will be chosen randomly from 1000ms -through 1200ms. This is to stop retry storms triggered by a single event like -an outage becoming synchronized into a DoS. - -### `certs` - -Certificates needed for validation should be listed here each with a name. The -format is base64 DER, which is the same as the part of PEM that is inside the -start and end lines. - -### `trust_stores` - -Chains of certificates given in the `certs` section may be named and described -inside the `trust_stores` section. Each entry in `trust_stores` is created as -a vhost + tls context with the given name. Stream types can later be associated -with one of these to enforce validity checking of the remote server. - -Entries should be named using "name" and the stack array defined using "stack" - -### `s` - -These are an array of policies for the supported stream type names. - -### `endpoint` - -The DNS address the secure stream should connect to. - -This may contain string symbols which will be replaced with the -corresponding streamtype metadata value at runtime. Eg, if the -streamtype lists a metadata name "region", it's then possible to -define the endpoint as, eg, `${region}.mysite.com`, and before -attempting the connection setting the stream's metadata item -"region" to the desired value, eg, "uk". - -### `port` - -The port number as an integer on the endpoint to connect to - -### `protocol` - -The wire protocol to connect to the endpoint with. Currently supported -streamtypes are - -|Wire protocol|Description| -|---|---| -|h1|http/1| -|h2|http/2| -|ws|http/1 Websockets| -|mqtt|mqtt 3.1.1| - -### `plugins` - -Array of plugin names to apply to the stream, if any - -### `tls` - -Set to `true` to enforce the stream travelling in a tls tunnel - -### `client cert` - -Set if the stream needs to authenticate itself using a tls client certificate. -Set to the certificate index counting from 0+. The certificates are managed -using lws_sytstem blobs. - -### `opportunistic` - -Set to `true` if the connection may be left dropped except when in use - -### `nailed_up` - -Set to `true` to have lws retry if the connection carrying this stream should -ever drop. - -### `retry` - -The name of the policy described in the `retry` section to apply to this -connection for retry + backoff - -### `tls_trust_store` - -The name of the trust store described in the `trust_stores` section to apply -to validate the remote server cert. - -## http transport - -### `http_method` - -HTTP method to use with http-related protocols, like GET or POST. -Not required for ws. - -### `http_url` - -Url path to use with http-related protocols - -The URL path can include metatadata like this - -"/mypath?whatever=${metadataname}" - -${metadataname} will be replaced by the current value of the -same metadata name. The metadata names must be listed in the -"metadata": [ ] section. - -### `http_auth_header` - -The name of the header that takes the auth token, with a trailing ':', eg - -``` - "http_auth_header": "authorization:" -``` - -### `http_dsn_header` - -The name of the header that takes the dsn token, with a trailing ':', eg - -``` - "http_dsn_header": "x-dsn:" -``` - -### `http_fwv_header` - -The name of the header that takes the firmware version token, with a trailing ':', eg - -``` - "http_fwv_header": "x-fw-version:" -``` - -### `http_devtype_header` - -The name of the header that takes the device type token, with a trailing ':', eg - -``` - "http_devtype_header": "x-device-type:" -``` - -### `http_auth_preamble` - -An optional string that precedes the auth token, eg - -``` - "http_auth_preamble": "bearer " -``` - -### `auth_hexify` - -Convert the auth token to hex ('A' -> "41") before transporting. Not necessary if the -auth token is already in printable string format suitable for transport. Needed if the -auth token is a chunk of 8-bit binary. - -### `nghttp2_quirk_end_stream` - -Set this to `true` if the peer server has the quirk it won't send a response until we have -sent an `END_STREAM`, even though we have sent headers with `END_HEADERS`. - -### `h2q_oflow_txcr` - -Set this to `true` if the peer server has the quirk it sends an maximum initial tx credit -of 0x7fffffff and then later increments it illegally. - -### `http_multipart_name` - -Indicates this stream goes out using multipart mime, and provides the name part of the -multipart header - -### `http_multipart_filename` - -Indicates this stream goes out using multipart mime, and provides the filename part of the -multipart header - -### `http_multipart_content_type` - -The `content-type` to mark up the multipart mime section with if present - -### `http_www_form_urlencoded` - -Indicate the data is sent in `x-www-form-urlencoded` form - -### `rideshare` - -For special cases where one logically separate stream travels with another when using this -protocol. Eg, a single multipart mime transaction carries content from two or more streams. - -## ws transport - -### `ws_subprotocol` - -Name of the ws subprotocol to use. - -### `ws_binary` - -Use if the ws messages are binary - -## MQTT transport - -### `mqtt_topic` - -Set the topic this streamtype uses for writes - -### `mqtt_subscribe` - -Set the topic this streamtype subscribes to - -### `mqtt qos` - -Set the QOS level for this streamtype - -### `mqtt_keep_alive` - -16-bit number representing MQTT keep alive for the stream. - -This is applied at connection time... where different streams may bind to the -same underlying MQTT connection, all the streams should have an identical -setting for this. - -### `mqtt_clean_start` - -Set to true if the connection should use MQTT's "clean start" feature. - -This is applied at connection time... where different streams may bind to the -same underlying MQTT connection, all the streams should have an identical -setting for this. - -### `mqtt_will_topic` - -Set the topic of the connection's will message, if any (there is none by default). - -This is applied at connection time... where different streams may bind to the -same underlying MQTT connection, all the streams should have an identical -setting for this. - -### `mqtt_will_message` - -Set the content of the connect's will message, if any (there is none by default). - -This is applied at connection time... where different streams may bind to the -same underlying MQTT connection, all the streams should have an identical -setting for this. - -### `mqtt_will_qos` - -Set the QoS of the will message, if any (there is none by default). - -This is applied at connection time... where different streams may bind to the -same underlying MQTT connection, all the streams should have an identical -setting for this. - -### `mqtt_will_retain` - -Set to true if the connection should use MQTT's "will retain" feature, if there -is a will message (there is none by default). - -This is applied at connection time... where different streams may bind to the -same underlying MQTT connection, all the streams should have an identical -setting for this. - -## Loading and using updated remote policy - -If the default, hardcoded policy includes a streamtype `fetch_policy`, -during startup when lws_system reaches the POLICY state, lws will use -a Secure Stream of type `fetch_policy` to download, parse and update -the policy to use it. - -The secure-streams-proxy minimal example shows how this is done and -fetches its real policy from warmcat.com at startup using the built-in -one. - -## Stream serialization and proxying - -By default Secure Streams expects to make the outgoing connection described in -the policy in the same process / thread, this suits the case where all the -participating clients are in the same statically-linked image. - -In this case the `lws_ss_` apis are fulfilled locally by secure-streams.c and -policy.c for policy lookups. - -However it also supports serialization, where the SS api can be streamed over -another transport such as a Unix Domain Socket connection. This suits the case -where the clients are actually in different processes in, eg, Linux or Android. - -In those cases, you run a proxy process (minimal-secure-streams-proxy) that -listens on a Unix Domain Socket and is connected to by one or more other -processes that pass their SS API activity to the proxy for fulfilment (or -onward proxying). - -In this case the proxy uses secure-streams.c and policy.c as before to fulfil -the inbound proxy streams, but uses secure-streams-serialize.c to serialize and -deserialize the proxied SS API activity. The proxy clients define -LWS_SS_USE_SSPC either very early in their sources before the includes, or on -the compiler commandline... this causes the lws_ss_ apis to be replaced at -preprocessor time with lws_sspc_ equivalents. These serialize the api action -and pass it to the proxy over a Unix Domain Socket for fulfilment, the results -and state changes etc are streamed over the Unix Domain Socket and presented to -the application exactly the same as if it was being fulfilled locally. - -To demonstrate this, some minimal examples, eg, minimal-secure-streams and -mimimal-secure-streams-avs build themselves both ways, once with direct SS API -fulfilment and once with Unix Domain Socket proxying and -client appended on the -executable name. To test the -client variants, run minimal-secure-streams-proxy -on the same machine. - -## Complicated scenarios with secure streams proxy - -As mentioned above, Secure Streams has two modes, by default the application -directly parses the policy and makes the outgoing connections itself. -However when configured at cmake with - -``` --DLWS_WITH_SOCKS5=1 -DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITH_SECURE_STREAMS_PROXY_API=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -``` - -and define `LWS_SS_USE_SSPC` when building the application, applications forward -their network requests to a local or remote SS proxy for fulfilment... and only -the SS proxy has the system policy. By default, the SS proxy is on the local -machine and is connected to via a Unix Domain Socket, but tcp links are also -possible. (Note the proxied traffic is not encrypyed by default.) - -Using the configuration above, the example SS applications are built two ways, -once for direct connection fulfilment (eg, `./bin/lws-minimal-secure-streams`), -and once with `LWS_SS_USE_SSPC` also defined so it connects via an SS proxy, -(eg, `./bin/lws-minimal-secure-streams-client`). - -## Testing an example scenario with SS Proxy and socks5 proxy - -``` - [ SS application ] --- tcp --- [ socks 5 proxy ] --- tcp --- [ SS proxy ] --- internet -``` - -In this scenario, everything is on localhost, the socks5 proxy listens on :1337 and -the SS proxy listens on :1234. The SS application connects to the socks5 -proxy to get to the SS proxy, which then goes out to the internet - -### 1 Start the SS proxy - -Tell it to listen on lo interface on port 1234 - -``` -$ ./bin/lws-minimal-secure-streams-proxy -p 1234 -i lo -``` - -### 2 Start the SOCKS5 proxy - -``` -$ ssh -D 1337 -N -v localhost -``` - -The -v makes connections to the proxy visible in the terminal for testing - -### 3 Run the SS application - -The application is told to make all connections via the socks5 proxy at -127.0.0.1:1337, and to fulfil its SS connections via an SS proxy, binding -connections to 127.0.0.1 (ipv4 lo interface, -1), to 127.0.0.1:1234 (-a/-p). - -``` -socks_proxy=127.0.0.1:1337 ./bin/lws-minimal-secure-streams-client -p 1234 -i 127.0.0.1 -a 127.0.0.1 -``` - -You can confirm this goes through the ssh socks5 proxy to get to the SS proxy -and fulfil the connection. diff -Nru libwebsockets-4.0.20/lib/secure-streams/secure-streams.c libwebsockets-2.4.2/lib/secure-streams/secure-streams.c --- libwebsockets-4.0.20/lib/secure-streams/secure-streams.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/secure-streams.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,594 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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 - -static const struct ss_pcols *ss_pcols[] = { -#if defined(LWS_ROLE_H1) - &ss_pcol_h1, /* LWSSSP_H1 */ -#else - NULL, -#endif -#if defined(LWS_ROLE_H2) - &ss_pcol_h2, /* LWSSSP_H2 */ -#else - NULL, -#endif -#if defined(LWS_ROLE_WS) - &ss_pcol_ws, /* LWSSSP_WS */ -#else - NULL, -#endif -#if defined(LWS_ROLE_MQTT) - &ss_pcol_mqtt, /* LWSSSP_MQTT */ -#else - NULL, -#endif -}; - -static const char *state_names[] = { - "LWSSSCS_CREATING", - "LWSSSCS_DISCONNECTED", - "LWSSSCS_UNREACHABLE", - "LWSSSCS_AUTH_FAILED", - "LWSSSCS_CONNECTED", - "LWSSSCS_CONNECTING", - "LWSSSCS_DESTROYING", - "LWSSSCS_POLL", - "LWSSSCS_ALL_RETRIES_FAILED", - "LWSSSCS_QOS_ACK_REMOTE", - "LWSSSCS_QOS_NACK_REMOTE", - "LWSSSCS_QOS_ACK_LOCAL", - "LWSSSCS_QOS_NACK_LOCAL", -}; - -const char * -lws_ss_state_name(int state) -{ - if (state >= (int)LWS_ARRAY_SIZE(state_names)) - return "unknown"; - - return state_names[state]; -} - -int -lws_ss_event_helper(lws_ss_handle_t *h, lws_ss_constate_t cs) -{ - if (!h) - return 0; - -#if defined(LWS_WITH_SEQUENCER) - /* - * A parent sequencer for the ss is optional, if we have one, keep it - * informed of state changes on the ss connection - */ - if (h->seq && cs != LWSSSCS_DESTROYING) - lws_seq_queue_event(h->seq, LWSSEQ_SS_STATE_BASE + cs, - (void *)h, NULL); -#endif - - if (h->h_sink &&h->h_sink->info.state(h->sink_obj, h->h_sink, cs, 0)) - return 1; - - return h->info.state(ss_to_userobj(h), NULL, cs, 0); -} - -static void -lws_ss_timeout_sul_check_cb(lws_sorted_usec_list_t *sul) -{ - lws_ss_handle_t *h = lws_container_of(sul, lws_ss_handle_t, sul); - - lwsl_err("%s: retrying ss h %p after backoff\n", __func__, h); - /* we want to retry... */ - h->seqstate = SSSEQ_DO_RETRY; - - lws_ss_request_tx(h); -} - -int -lws_ss_exp_cb_metadata(void *priv, const char *name, char *out, size_t *pos, - size_t olen, size_t *exp_ofs) -{ - lws_ss_handle_t *h = (lws_ss_handle_t *)priv; - const char *replace = NULL; - size_t total, budget; - lws_ss_metadata_t *md = lws_ss_policy_metadata(h->policy, name); - - if (!md) { - lwsl_err("%s: Unknown metadata %s\n", __func__, name); - - return LSTRX_FATAL_NAME_UNKNOWN; - } - - lwsl_info("%s %s %d\n", __func__, name, (int)md->length); - - replace = h->metadata[md->length].value; - total = h->metadata[md->length].length; - // lwsl_hexdump_err(replace, total); - - budget = olen - *pos; - total -= *exp_ofs; - if (total < budget) - budget = total; - - memcpy(out + *pos, replace + (*exp_ofs), budget); - *exp_ofs += budget; - *pos += budget; - - if (budget == total) - return LSTRX_DONE; - - return LSTRX_FILLED_OUT; -} - -int -lws_ss_set_timeout_us(lws_ss_handle_t *h, lws_usec_t us) -{ - struct lws_context_per_thread *pt = &h->context->pt[h->tsi]; - - h->sul.cb = lws_ss_timeout_sul_check_cb; - __lws_sul_insert(&pt->pt_sul_owner, &h->sul, us); - - return 0; -} - -int -lws_ss_backoff(lws_ss_handle_t *h) -{ - uint64_t ms; - char conceal; - - if (h->seqstate == SSSEQ_RECONNECT_WAIT) - return 0; - - /* figure out what we should do about another retry */ - - lwsl_info("%s: ss %p: retry backoff after failure\n", __func__, h); - ms = lws_retry_get_delay_ms(h->context, h->policy->retry_bo, - &h->retry, &conceal); - if (!conceal) { - lwsl_info("%s: ss %p: abandon conn attempt \n",__func__, h); - h->seqstate = SSSEQ_IDLE; - lws_ss_event_helper(h, LWSSSCS_ALL_RETRIES_FAILED); - return 1; - } - - h->seqstate = SSSEQ_RECONNECT_WAIT; - lws_ss_set_timeout_us(h, ms * LWS_US_PER_MS); - - lwsl_info("%s: ss %p: retry wait %"PRIu64"ms\n", __func__, h, ms); - - return 0; -} - -int -_lws_ss_client_connect(lws_ss_handle_t *h, int is_retry) -{ - struct lws_client_connect_info i; - const struct ss_pcols *ssp; - size_t used_in, used_out; - union lws_ss_contemp ct; - char path[128], ep[96]; - lws_strexp_t exp; - - if (!h->policy) { - lwsl_err("%s: ss with no policy\n", __func__); - - return -1; - } - - /* - * We are already bound to a sink? - */ - - if (h->h_sink) - return 0; - - if (!is_retry) - h->retry = 0; - - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - i.context = h->context; - - if (h->policy->flags & LWSSSPOLF_TLS) { - lwsl_info("%s: using tls\n", __func__); - i.ssl_connection = LCCSCF_USE_SSL; - - if (!h->policy->trust_store) - lwsl_info("%s: using platform trust store\n", __func__); - else { - - i.vhost = lws_get_vhost_by_name(h->context, - h->policy->trust_store->name); - if (!i.vhost) { - lwsl_err("%s: missing vh for policy ca\n", __func__); - - return -1; - } - } - } - - /* expand metadata ${symbols} that may be inside the endpoint string */ - - lws_strexp_init(&exp, (void *)h, lws_ss_exp_cb_metadata, ep, sizeof(ep)); - - if (lws_strexp_expand(&exp, h->policy->endpoint, - strlen(h->policy->endpoint), - &used_in, &used_out) != LSTRX_DONE) { - lwsl_err("%s: address strexp failed\n", __func__); - - return -1; - } - - i.address = ep; - i.port = h->policy->port; - i.host = i.address; - i.origin = i.address; - i.opaque_user_data = h; - i.seq = h->seq; - i.retry_and_idle_policy = h->policy->retry_bo; - i.sys_tls_client_cert = h->policy->client_cert; - - i.path = ""; - - ssp = ss_pcols[(int)h->policy->protocol]; - if (!ssp) { - lwsl_err("%s: unsupported protocol\n", __func__); - - return -1; - } - i.alpn = ssp->alpn; - - /* - * For http, we can get the method from the http object, override in - * the protocol-specific munge callback below if not http - */ - i.method = h->policy->u.http.method; - i.protocol = ssp->protocol_name; /* lws protocol name */ - i.local_protocol_name = i.protocol; - - ssp->munge(h, path, sizeof(path), &i, &ct); - - i.pwsi = &h->wsi; - - if (h->policy->plugins[0] && h->policy->plugins[0]->munge) - h->policy->plugins[0]->munge(h, path, sizeof(path)); - - lwsl_info("%s: connecting %s, '%s' '%s' %s\n", __func__, i.method, - i.alpn, i.address, i.path); - - h->txn_ok = 0; - if (lws_ss_event_helper(h, LWSSSCS_CONNECTING)) - return -1; - - if (!lws_client_connect_via_info(&i)) { - lws_ss_event_helper(h, LWSSSCS_UNREACHABLE); - lws_ss_backoff(h); - - return 1; - } - - return 0; -} - -int -lws_ss_client_connect(lws_ss_handle_t *h) -{ - return _lws_ss_client_connect(h, 0); -} - -/* - * Public API - */ - -/* - * Create either a stream or a sink - */ - -int -lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi, - void *opaque_user_data, lws_ss_handle_t **ppss, - struct lws_sequencer *seq_owner, const char **ppayload_fmt) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - const lws_ss_policy_t *pol; - lws_ss_metadata_t *smd; - lws_ss_handle_t *h; - size_t size; - void **v; - char *p; - int n; - - pol = lws_ss_policy_lookup(context, ssi->streamtype); - if (!pol) { - lwsl_info("%s: unknown stream type %s\n", __func__, - ssi->streamtype); - return 1; - } - - if (ssi->register_sink) { - /* - * This can register a secure streams sink as well as normal - * secure streams connections. If that's what's happening, - * confirm the policy agrees that this streamtype should be - * directed to a sink. - */ - if (!(pol->flags & LWSSSPOLF_LOCAL_SINK)) { - /* - * Caller wanted to create a sink for this streamtype, - * but the policy does not agree the streamtype should - * be routed to a local sink. - */ - lwsl_err("%s: %s policy does not allow local sink\n", - __func__, ssi->streamtype); - - return 1; - } - } else { - - if (!(pol->flags & LWSSSPOLF_LOCAL_SINK)) { - - } -// lws_dll2_foreach_safe(&pt->ss_owner, NULL, lws_ss_destroy_dll); - } - - /* - * We overallocate and point to things in the overallocation... - * - * 1) the user_alloc from the stream info - * 2) network auth plugin instantiation data - * 3) stream auth plugin instantiation data - * 4) as many metadata pointer structs as the policy tells - * 5) the streamtype name (length is not aligned) - * - * ... when we come to destroy it, just one free to do. - */ - - size = sizeof(*h) + ssi->user_alloc + strlen(ssi->streamtype) + 1; - if (pol->plugins[0]) - size += pol->plugins[0]->alloc; - if (pol->plugins[1]) - size += pol->plugins[1]->alloc; - size += pol->metadata_count * sizeof(lws_ss_metadata_t); - - h = lws_zalloc(size, __func__); - if (!h) - return 2; - - h->info = *ssi; - h->policy = pol; - h->context = context; - h->tsi = tsi; - h->seq = seq_owner; - - /* start of overallocated area */ - p = (char *)&h[1]; - - /* set the handle pointer in the user data struct */ - v = (void **)(p + ssi->handle_offset); - *v = h; - - /* set the opaque user data in the user data struct */ - v = (void **)(p + ssi->opaque_user_data_offset); - *v = opaque_user_data; - - p += ssi->user_alloc; - - if (pol->plugins[0]) { - h->nauthi = p; - p += pol->plugins[0]->alloc; - } - if (pol->plugins[1]) { - h->sauthi = p; - p += pol->plugins[1]->alloc; - } - - if (pol->metadata_count) { - h->metadata = (lws_ss_metadata_t *)p; - p += pol->metadata_count * sizeof(lws_ss_metadata_t); - - lwsl_info("%s: %s metadata count %d\n", __func__, - pol->streamtype, pol->metadata_count); - } - - smd = pol->metadata; - for (n = 0; n < pol->metadata_count; n++) { - h->metadata[n].name = smd->name; - if (n + 1 == pol->metadata_count) - h->metadata[n].next = NULL; - else - h->metadata[n].next = &h->metadata[n + 1]; - smd = smd->next; - } - - memcpy(p, ssi->streamtype, strlen(ssi->streamtype) + 1); - h->info.streamtype = p; - - lws_pt_lock(pt, __func__); - lws_dll2_add_head(&h->list, &pt->ss_owner); - lws_pt_unlock(pt); - - if (ppss) - *ppss = h; - - if (ppayload_fmt) - *ppayload_fmt = pol->payload_fmt; - - if (ssi->register_sink) { - /* - * - */ - } - - lws_ss_event_helper(h, LWSSSCS_CREATING); - - if (!ssi->register_sink && - ((h->policy->flags & LWSSSPOLF_NAILED_UP))) - if (_lws_ss_client_connect(h, 0)) - lws_ss_backoff(h); - - return 0; -} - -void -lws_ss_destroy(lws_ss_handle_t **ppss) -{ - struct lws_context_per_thread *pt; - lws_ss_handle_t *h = *ppss; - lws_ss_metadata_t *pmd; - - if (!h) - return; - - if (h->wsi) { - /* - * Don't let the wsi point to us any more, - * we (the ss object bound to the wsi) are going away now - */ -// lws_set_opaque_user_data(h->wsi, NULL); - lws_set_timeout(h->wsi, 1, LWS_TO_KILL_SYNC); - } - - pt = &h->context->pt[h->tsi]; - - lws_pt_lock(pt, __func__); - *ppss = NULL; - lws_dll2_remove(&h->list); - lws_dll2_remove(&h->to_list); - lws_ss_event_helper(h, LWSSSCS_DESTROYING); - lws_pt_unlock(pt); - - /* in proxy case, metadata value on heap may need cleaning up */ - - pmd = h->metadata; - while (pmd) { - lwsl_info("%s: pmd %p\n", __func__, pmd); - if (pmd->value_on_lws_heap) - lws_free_set_NULL(pmd->value); - pmd = pmd->next; - } - - lws_sul_schedule(h->context, 0, &h->sul, NULL, LWS_SET_TIMER_USEC_CANCEL); - - lws_free_set_NULL(h); -} - -void -lws_ss_request_tx(lws_ss_handle_t *h) -{ - lwsl_info("%s: wsi %p\n", __func__, h->wsi); - - if (h->wsi) { - lws_callback_on_writable(h->wsi); - - return; - } - - if (h->seqstate != SSSEQ_IDLE && - h->seqstate != SSSEQ_DO_RETRY) - return; - - h->seqstate = SSSEQ_TRY_CONNECT; - lws_ss_event_helper(h, LWSSSCS_POLL); - - /* - * Retries operate via lws_ss_request_tx(), explicitly ask for a - * reconnection to clear the retry limit - */ - if (_lws_ss_client_connect(h, 1)) - lws_ss_backoff(h); -} - -void -lws_ss_request_tx_len(lws_ss_handle_t *h, unsigned long len) -{ - if (h->wsi) - h->wsi->http.writeable_len = len; - else - h->writeable_len = len; - lws_ss_request_tx(h); -} - -/* - * private helpers - */ - -/* used on context destroy when iterating listed lws_ss on a pt */ - -int -lws_ss_destroy_dll(struct lws_dll2 *d, void *user) -{ - lws_ss_handle_t *h = lws_container_of(d, lws_ss_handle_t, list); - - lws_ss_destroy(&h); - - return 0; -} - -struct lws_sequencer * -lws_ss_get_sequencer(lws_ss_handle_t *h) -{ - return h->seq; -} - -struct lws_context * -lws_ss_get_context(struct lws_ss_handle *h) -{ - return h->context; -} - -const char * -lws_ss_rideshare(struct lws_ss_handle *h) -{ - if (!h->rideshare) - return h->policy->streamtype; - - return h->rideshare->streamtype; -} - -int -lws_ss_add_peer_tx_credit(struct lws_ss_handle *h, int32_t bump) -{ - const struct ss_pcols *ssp; - - ssp = ss_pcols[(int)h->policy->protocol]; - - if (h->wsi && ssp && ssp->tx_cr_add) - return ssp->tx_cr_add(h, bump); - - return 0; -} - -int -lws_ss_get_est_peer_tx_credit(struct lws_ss_handle *h) -{ - const struct ss_pcols *ssp; - - ssp = ss_pcols[(int)h->policy->protocol]; - - if (h->wsi && ssp && ssp->tx_cr_add) - return ssp->tx_cr_est(h); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/secure-streams/secure-streams-client.c libwebsockets-2.4.2/lib/secure-streams/secure-streams-client.c --- libwebsockets-4.0.20/lib/secure-streams/secure-streams-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/secure-streams-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,574 +0,0 @@ -/* - * lws-minimal-secure-streams-client - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * - * This client does not perform any INET networking... instead it opens a unix - * domain socket on a proxy that is listening for it, and that creates the - * actual secure stream connection. - * - * We are able to use the usual secure streams api in the client process, with - * payloads and connection state information proxied over the unix domain - * socket and fulfilled in the proxy process. - * - * The public client helper pieces are built as part of lws - */ -#include - -static void -lws_sspc_sul_retry_cb(lws_sorted_usec_list_t *sul) -{ - lws_sspc_handle_t *h = lws_container_of(sul, lws_sspc_handle_t, sul_retry); - static struct lws_client_connect_info i; - - /* - * We may have started up before the system proxy, so be prepared with - * a sul to retry at 1Hz - */ - - memset(&i, 0, sizeof i); - i.context = h->context; - if (h->context->ss_proxy_port) { /* tcp */ - i.address = h->context->ss_proxy_address; - i.port = h->context->ss_proxy_port; - i.iface = h->context->ss_proxy_bind; - } else { - if (h->context->ss_proxy_bind) - i.address = h->context->ss_proxy_bind; - else - i.address = "+@proxy.ss.lws"; - } - i.host = i.address; - i.origin = i.address; - i.method = "RAW"; - i.protocol = lws_sspc_protocols[0].name; - i.local_protocol_name = lws_sspc_protocols[0].name; - i.path = ""; - i.pwsi = &h->cwsi; - i.opaque_user_data = (void *)h; - - if (!lws_client_connect_via_info(&i)) { - lws_sul_schedule(h->context, 0, &h->sul_retry, - lws_sspc_sul_retry_cb, LWS_US_PER_SEC); - - return; - } -} - -static int -lws_sspc_serialize_metadata(lws_sspc_metadata_t *md, uint8_t *p) -{ - int n, txc; - - if (md->name[0] == '\0') { - - lwsl_info("%s: sending tx credit update %d\n", __func__, - md->tx_cr_adjust); - - p[0] = LWSSS_SER_TXPRE_TXCR_UPDATE; - lws_ser_wu16be(&p[1], 4); - lws_ser_wu32be(&p[3], md->tx_cr_adjust); - - n = 7; - - } else { - - lwsl_info("%s: sending metadata\n", __func__); - - p[0] = LWSSS_SER_TXPRE_METADATA; - txc = strlen(md->name); - n = txc + 1 + md->len; - lws_ser_wu16be(&p[1], n); - p[3] = txc; - memcpy(&p[4], md->name, txc); - memcpy(&p[4 + txc], &md[1], md->len); - n = 4 + txc + md->len; - } - - lws_dll2_remove(&md->list); - lws_free(md); - - return n; -} - -static int -callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - lws_sspc_handle_t *h = (lws_sspc_handle_t *)lws_get_opaque_user_data(wsi); - uint8_t s[32], pkt[LWS_PRE + 1400], *p = pkt + LWS_PRE; - void *m = (void *)((uint8_t *)&h[1]); - const uint8_t *cp; - lws_usec_t us; - int flags, n; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_warn("%s: CONNECTION_ERROR\n", __func__); - lws_set_opaque_user_data(wsi, NULL); - h->cwsi = NULL; - lws_sul_schedule(h->context, 0, &h->sul_retry, - lws_sspc_sul_retry_cb, LWS_US_PER_SEC); - break; - - case LWS_CALLBACK_RAW_CONNECTED: - if (!h) - return -1; - lwsl_info("%s: CONNECTED (%s)\n", __func__, h->ssi.streamtype); - - h->state = LPCS_SENDING_INITIAL_TX; - h->dsh = lws_dsh_create(NULL, (LWS_PRE + LWS_SS_MTU) * 160, 1); - if (!h->dsh) - return -1; - - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, 3); - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_RAW_CLOSE: - /* - * our ss proxy Unix Domain socket has closed... - */ - lwsl_notice("%s: LWS_CALLBACK_RAW_CLOSE: proxy conn down\n", __func__); - h->cwsi = NULL; - //lws_sspc_destroy(&h); - break; - - case LWS_CALLBACK_RAW_RX: - lwsl_info("%s: RAW_RX: rx %d\n", __func__, (int)len); - - if (!h || !h->cwsi) { - lwsl_err("%s: rx with bad conn state\n", __func__); - - return -1; - } - - if (lws_ss_deserialize_parse(&h->parser, lws_get_context(wsi), - h->dsh, in, len, &h->state, h, - (lws_ss_handle_t **)m, &h->ssi, 1)) - return -1; - - if (wsi && h->state == LPCS_LOCAL_CONNECTED) - lws_set_timeout(wsi, 0, 0); - - break; - - case LWS_CALLBACK_RAW_WRITEABLE: - - /* - * We can transmit something to the proxy... - */ - - if (!h) - break; - - lwsl_info("%s: WRITEABLE %p: (%s) state %d\n", __func__, wsi, - h->ssi.streamtype, h->state); - - n = 0; - cp = s; - s[1] = 0; - switch (h->state) { - case LPCS_SENDING_INITIAL_TX: - n = strlen(h->ssi.streamtype) + 4; - - s[0] = LWSSS_SER_TXPRE_STREAMTYPE; - lws_ser_wu16be(&s[1], n); - lws_ser_wu32be(&s[3], h->txc.peer_tx_cr_est); - //h->txcr_out = txc; - lws_strncpy((char *)&s[7], h->ssi.streamtype, sizeof(s) - 7); - n += 3; - h->state = LPCS_WAITING_CREATE_RESULT; - break; - - case LPCS_LOCAL_CONNECTED: - if (!h->conn_req) - break; - - /* - * Do we need to prioritize sending any metadata - * changes? - */ - - if (h->metadata_owner.count) { - lws_sspc_metadata_t *md = lws_container_of( - lws_dll2_get_tail(&h->metadata_owner), - lws_sspc_metadata_t, list); - - cp = p; - n = lws_sspc_serialize_metadata(md, p); - - /* in case anything else to write */ - lws_callback_on_writable(h->cwsi); - - break; - } - - - h->conn_req = 0; - s[0] = LWSSS_SER_TXPRE_ONWARD_CONNECT; - s[1] = 0; - s[2] = 0; - n = 3; - break; - - case LPCS_OPERATIONAL: - - /* - * Do we want to adjust the peer's ability to write - * to us? - */ - - /* - * Do we need to prioritize sending any metadata - * changes? - */ - - if (h->metadata_owner.count) { - lws_sspc_metadata_t *md = lws_container_of( - lws_dll2_get_tail(&h->metadata_owner), - lws_sspc_metadata_t, list); - - cp = p; - n = lws_sspc_serialize_metadata(md, p); - - /* in case anything else to write */ - lws_callback_on_writable(h->cwsi); - - break; - } - - - /* we can't write anything if we don't have credit */ - if (h->txc.tx_cr <= 0) { - lwsl_notice("%s: WRITEABLE / OPERATIONAL:" - " lack credit (%d)\n", __func__, - h->txc.tx_cr); - break; - } - - len = sizeof(pkt) - LWS_PRE - 19; - flags = 0; - if (h->ssi.tx(m, h->ord++, pkt + LWS_PRE + 19, &len, &flags)) - break; - - h->txc.tx_cr -= len; - - cp = p; - n = len + 19; - us = lws_now_usecs(); - p[0] = LWSSS_SER_TXPRE_TX_PAYLOAD; - lws_ser_wu16be(&p[1], len + 19 - 3); - lws_ser_wu32be(&p[3], flags); - /* time spent here waiting to send this */ - lws_ser_wu32be(&p[7], us - h->us_earliest_write_req); - /* ust that the client write happened */ - lws_ser_wu64be(&p[11], us); - h->us_earliest_write_req = 0; - - if (flags & LWSSS_FLAG_EOM) - if (h->rsidx + 1 < (int)LWS_ARRAY_SIZE(h->rideshare_ofs) && - h->rideshare_ofs[h->rsidx + 1]) - h->rsidx++; - - break; - default: - break; - } - - if (!n) - break; - - // lwsl_hexdump_notice(cp, n); - - n = lws_write(wsi, (uint8_t *)cp, n, LWS_WRITE_RAW); - if (n < 0) { - lwsl_notice("%s: WRITEABLE: %d\n", __func__, n); - - goto hangup; - } - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); - -hangup: - lwsl_warn("hangup\n"); - /* hang up on him */ - return -1; -} - -const struct lws_protocols lws_sspc_protocols[] = { - { - "ssproxy-protocol", - callback_sspc_client, - 0, - 2048, 2048, NULL, 0 - }, - { NULL, NULL, 0, 0, 0, NULL, 0 } -}; - -int -lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi, - void *opaque_user_data, lws_sspc_handle_t **ppss, - struct lws_sequencer *seq_owner, const char **ppayload_fmt) -{ - lws_sspc_handle_t *h; - uint8_t *ua; - char *p; - - lwsl_notice("%s: streamtype %s\n", __func__, ssi->streamtype); - - /* allocate the handle (including ssi), the user alloc, - * and the streamname */ - - h = malloc(sizeof(lws_sspc_handle_t) + ssi->user_alloc + - strlen(ssi->streamtype) + 1); - memset(h, 0, sizeof(*h)); - memcpy(&h->ssi, ssi, sizeof(*ssi)); - ua = (uint8_t *)&h[1]; - memset(ua, 0, ssi->user_alloc); - p = (char *)ua + ssi->user_alloc; - memcpy(p, ssi->streamtype, strlen(ssi->streamtype) + 1); - h->ssi.streamtype = (const char *)p; - h->context = context; - if (!ssi->manual_initial_tx_credit) - h->txc.peer_tx_cr_est = 500000000; - else - h->txc.peer_tx_cr_est = ssi->manual_initial_tx_credit; - - lws_dll2_add_head(&h->client_list, &context->pt[tsi].ss_client_owner); - - /* fill in the things the real api does for the caller */ - - *((void **)(ua + ssi->opaque_user_data_offset)) = opaque_user_data; - *((void **)(ua + ssi->handle_offset)) = h; - - if (ppss) - *ppss = h; - - /* try the actual connect */ - - lws_sspc_sul_retry_cb(&h->sul_retry); - - return 0; -} - -/* used on context destroy when iterating listed lws_ss on a pt */ - -int -lws_sspc_destroy_dll(struct lws_dll2 *d, void *user) -{ - lws_sspc_handle_t *h = lws_container_of(d, lws_sspc_handle_t, client_list); - - lws_sspc_destroy(&h); - - return 0; -} - - -void -lws_sspc_destroy(lws_sspc_handle_t **ph) -{ - lws_sspc_handle_t *h; - void *m; - - lwsl_debug("%s\n", __func__); - - if (!*ph) - return; - - h = *ph; - m = (void *)((uint8_t *)&h[1]); - - if (h->destroying) - return; - - h->destroying = 1; - - lws_sul_schedule(h->context, 0, &h->sul_retry, NULL, - LWS_SET_TIMER_USEC_CANCEL); - lws_dll2_remove(&h->client_list); - - if (h->dsh) - lws_dsh_destroy(&h->dsh); - if (h->cwsi) { - struct lws *wsi = h->cwsi; - h->cwsi = NULL; - lws_set_timeout(wsi, 1, LWS_TO_KILL_SYNC); - } - - /* clean out any pending metadata changes that didn't make it */ - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - lws_dll2_get_head(&(*ph)->metadata_owner)) { - lws_sspc_metadata_t *md = - lws_container_of(d, lws_sspc_metadata_t, list); - - lws_dll2_remove(&md->list); - lws_free(md); - - } lws_end_foreach_dll_safe(d, d1); - - h->ssi.state(m, NULL, LWSSSCS_DESTROYING, 0); - *ph = NULL; - free(h); -} - -void -lws_sspc_request_tx(lws_sspc_handle_t *h) -{ - if (!h || !h->cwsi) - return; - - if (!h->us_earliest_write_req) - h->us_earliest_write_req = lws_now_usecs(); - - lws_callback_on_writable(h->cwsi); -} - -int -lws_sspc_client_connect(lws_sspc_handle_t *h) -{ - if (!h || h->state == LPCS_OPERATIONAL) - return 0; - - assert(h->state == LPCS_LOCAL_CONNECTED); - h->conn_req = 1; - if (h->cwsi) - lws_callback_on_writable(h->cwsi); - - return 0; -} - -struct lws_context * -lws_sspc_get_context(struct lws_sspc_handle *h) -{ - return h->context; -} - -const char * -lws_sspc_rideshare(struct lws_sspc_handle *h) -{ - /* - * ...the serialized RX rideshare name if any... - */ - - if (h->parser.rideshare[0]) { - lwsl_info("%s: parser %s\n", __func__, h->parser.rideshare); - return h->parser.rideshare; - } - - /* - * The tx rideshare index - */ - - if (h->rideshare_list[0]) { - lwsl_info("%s: tx list %s\n", __func__, - &h->rideshare_list[h->rideshare_ofs[h->rsidx]]); - return &h->rideshare_list[h->rideshare_ofs[h->rsidx]]; - } - - /* - * ... otherwise default to our stream type name - */ - - lwsl_info("%s: def %s\n", __func__, h->ssi.streamtype); - - return h->ssi.streamtype; -} - -static int -_lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name, - void *value, size_t len, int tx_cr_adjust) -{ - lws_sspc_metadata_t *md; - - /* - * Are we replacing a pending metadata of the same name? It's not - * efficient to do this but user code can do what it likes... let's - * optimize away the old one. - * - * Tx credit adjust always has name "" - */ - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - lws_dll2_get_head(&h->metadata_owner)) { - md = lws_container_of(d, lws_sspc_metadata_t, list); - - if (!strcmp(name, md->name)) { - lws_dll2_remove(&md->list); - lws_free(md); - break; - } - - } lws_end_foreach_dll_safe(d, d1); - - /* - * We have to stash the metadata and pass it to the proxy - */ - - md = lws_malloc(sizeof(*md) + len, "set metadata"); - if (!md) { - lwsl_err("%s: OOM\n", __func__); - - return 1; - } - - memset(md, 0, sizeof(*md)); - - md->tx_cr_adjust = tx_cr_adjust; - h->txc.peer_tx_cr_est += tx_cr_adjust; - - lws_strncpy(md->name, name, sizeof(md->name)); - md->len = len; - if (len) - memcpy(&md[1], value, len); - - lws_dll2_add_tail(&md->list, &h->metadata_owner); - - if (len) { - lwsl_info("%s: set metadata %s\n", __func__, name); - lwsl_hexdump_info(value, len); - } else - lwsl_info("%s: serializing tx cr adj %d\n", __func__, - (int)tx_cr_adjust); - - if (h->cwsi) - lws_callback_on_writable(h->cwsi); - - return 0; -} - -int -lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name, - void *value, size_t len) -{ - return _lws_sspc_set_metadata(h, name, value, len, 0); -} - -int -lws_sspc_add_peer_tx_credit(struct lws_sspc_handle *h, int32_t bump) -{ - lwsl_notice("%s: %d\n", __func__, bump); - return _lws_sspc_set_metadata(h, "", NULL, 0, (int)bump); -} - -int -lws_sspc_get_est_peer_tx_credit(struct lws_sspc_handle *h) -{ - return h->txc.peer_tx_cr_est; -} diff -Nru libwebsockets-4.0.20/lib/secure-streams/secure-streams-process.c libwebsockets-2.4.2/lib/secure-streams/secure-streams-process.c --- libwebsockets-4.0.20/lib/secure-streams/secure-streams-process.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/secure-streams-process.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,533 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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. - * - * - * When the user code is in a different process, a non-tls unix domain socket - * proxy is used to asynchronusly transfer buffers in each direction via the - * network stack, without explicit IPC - * - * user_process{ [user code] | shim | socket-}------ lws_process{ lws } - * - * Lws exposes a listening unix domain socket in this case, the user processes - * connect to it and pass just info.streamtype in an initial tx packet. All - * packets are prepended by a 1-byte type field when used in this mode. See - * lws-secure-streams.h for documentation and definitions. - * - * Proxying in either direction can face the situation it cannot send the onward - * packet immediately and is subject to separating the write request from the - * write action. To make the best use of memory, a single preallocated buffer - * stashes pending packets in all four directions (c->p, p->c, p->ss, ss->p). - * This allows it to adapt to different traffic patterns without wasted areas - * dedicated to traffic that isn't coming in a particular application. - * - * A shim is provided to monitor the process' unix domain socket and regenerate - * the secure sockets api there with callbacks happening in the process thread - * context. - * - * This file implements the listening unix domain socket proxy... this code is - * only going to run on a Linux-class device with its implications about memory - * availability. - */ - -#include - -/* - * Because both sides of the connection share the conn, we allocate it - * during accepted adoption, and both sides point to it. - * - * The last one of the accepted side and the onward side to close frees it. - */ - -struct conn { - struct lws_ss_serialization_parser parser; - - lws_dsh_t *dsh; /* unified buffer for both sides */ - struct lws *wsi; /* the client side */ - lws_ss_handle_t *ss; /* the onward, ss side */ - - lws_ss_conn_states_t state; -}; - -struct raw_pss { - struct conn *conn; -}; - -/* - * Proxy - onward secure-stream handler - */ - -typedef struct ss_proxy_onward { - lws_ss_handle_t *ss; - struct conn *conn; -} ss_proxy_t; - - -/* secure streams payload interface */ - -static int -ss_proxy_onward_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ - ss_proxy_t *m = (ss_proxy_t *)userobj; - const char *rsp = NULL; - int n; - - /* - * The onward secure stream connection has received something. - */ - - if (m->ss->rideshare != m->ss->policy && m->ss->rideshare) { - rsp = m->ss->rideshare->streamtype; - flags |= LWSSS_FLAG_RIDESHARE; - } - - n = lws_ss_serialize_rx_payload(m->conn->dsh, buf, len, flags, rsp); - if (n) - return n; - - if (m->conn->wsi) /* if possible, request client conn write */ - lws_callback_on_writable(m->conn->wsi); - - return 0; -} - -/* - * we are transmitting buffered payload originally from the client on to the ss - */ - -static int -ss_proxy_onward_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, - size_t *len, int *flags) -{ - ss_proxy_t *m = (ss_proxy_t *)userobj; - void *p; - size_t si; - - if (!m->conn->ss || m->conn->state != LPCS_OPERATIONAL) { - lwsl_notice("%s: ss not ready\n", __func__); - *len = 0; - - return 1; - } - - /* - * The onward secure stream says that we could send something to it - * (by putting it in buf, and setting *len and *flags) - */ - - if (lws_ss_deserialize_tx_payload(m->conn->dsh, m->ss->wsi, - ord, buf, len, flags)) - return 1; - - if (!lws_dsh_get_head(m->conn->dsh, KIND_C_TO_P, (void **)&p, &si)) - lws_ss_request_tx(m->conn->ss); - - if (!*len && !*flags) - return 1; /* we don't actually want to send anything */ - - lwsl_info("%s: onward tx %d fl 0x%x\n", __func__, (int)*len, *flags); - -#if 0 - { - int ff = open("/tmp/z", O_RDWR | O_CREAT | O_APPEND, 0666); - if (ff == -1) - lwsl_err("%s: errno %d\n", __func__, errno); - write(ff, buf, *len); - close(ff); - } -#endif - - return 0; -} - -static int -ss_proxy_onward_state(void *userobj, void *sh, - lws_ss_constate_t state, lws_ss_tx_ordinal_t ack) -{ - ss_proxy_t *m = (ss_proxy_t *)userobj; - - switch (state) { - case LWSSSCS_CREATING: - break; - - case LWSSSCS_DESTROYING: - if (!m->conn) - break; - if (!m->conn->wsi) { - /* - * Our onward secure stream is closing and our client - * connection has already gone away... destroy the conn. - */ - lwsl_info("%s: Destroying conn\n", __func__); - lws_dsh_destroy(&m->conn->dsh); - free(m->conn); - m->conn = NULL; - return 0; - } else - lwsl_info("%s: ss DESTROYING, wsi up\n", __func__); - break; - - default: - break; - } - if (!m->conn) { - lwsl_warn("%s: dropping state due to conn not up\n", __func__); - - return 0; - } - - lws_ss_serialize_state(m->conn->dsh, state, ack); - - if (m->conn->wsi) /* if possible, request client conn write */ - lws_callback_on_writable(m->conn->wsi); - - return 0; -} - -void -ss_proxy_onward_txcr(void *userobj, int bump) -{ - ss_proxy_t *m = (ss_proxy_t *)userobj; - - if (!m->conn) - return; - - lws_ss_serialize_txcr(m->conn->dsh, bump); - - if (m->conn->wsi) /* if possible, request client conn write */ - lws_callback_on_writable(m->conn->wsi); -} - -/* - * Client - Proxy connection on unix domain socket - */ - -static int -callback_ss_proxy(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct raw_pss *pss = (struct raw_pss *)user; - const lws_ss_policy_t *rsp; - struct conn *conn = NULL; - lws_ss_info_t ssi; - const uint8_t *cp; -#if defined(LWS_WITH_DETAILED_LATENCY) - lws_usec_t us; -#endif - char s[128]; - uint8_t *p; - size_t si; - char pay; - int n; - - if (pss) - conn = pss->conn; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - break; - - /* callbacks related to raw socket descriptor "accepted side" */ - - case LWS_CALLBACK_RAW_ADOPT: - lwsl_info("LWS_CALLBACK_RAW_ADOPT\n"); - if (!pss) - return -1; - pss->conn = malloc(sizeof(struct conn)); - if (!pss->conn) - return -1; - memset(pss->conn, 0, sizeof(*pss->conn)); - - pss->conn->dsh = lws_dsh_create(&pt->ss_dsh_owner, - LWS_SS_MTU * 160, 2); - if (!pss->conn->dsh) { - free(pss->conn); - - return -1; - } - - pss->conn->wsi = wsi; - pss->conn->state = LPCS_WAIT_INITIAL_TX; - - /* - * Client is expected to follow the unix domain socket - * acceptance up rapidly with an initial tx containing the - * streamtype name. We can't create the stream until then. - */ - lws_set_timeout(wsi, - PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, 3); - break; - - case LWS_CALLBACK_RAW_CLOSE: - lwsl_info("LWS_CALLBACK_RAW_CLOSE:\n"); - - /* - * the client unix domain socket connection has closed... - * eg, client has exited or otherwise has definitively finished - * with the proxying and onward connection - */ - - if (!conn) - break; - - if (conn->ss) { - lwsl_info("%s: destroying ss\n", __func__); - /* sever relationship with ss about to be deleted */ - lws_set_opaque_user_data(wsi, NULL); - - conn->wsi = NULL; - - - lws_ss_destroy(&conn->ss); - /* conn may have gone */ - break; - } - - if (conn->state == LPCS_DESTROYED || !conn->ss) { - /* - * There's no onward secure stream and our client - * connection is closing. Destroy the conn. - */ - lws_dsh_destroy(&conn->dsh); - free(conn); - pss->conn = NULL; - } else - lwsl_debug("%s: CLOSE; ss=%p\n", __func__, conn->ss); - - break; - - case LWS_CALLBACK_RAW_RX: - lwsl_info("%s: RX: rx %d\n", __func__, (int)len); - - if (!conn || !conn->wsi) { - lwsl_err("%s: rx with bad conn state\n", __func__); - - return -1; - } - - // lwsl_hexdump_info(in, len); - - if (conn->state == LPCS_WAIT_INITIAL_TX) { - memset(&ssi, 0, sizeof(ssi)); - ssi.user_alloc = sizeof(ss_proxy_t); - ssi.handle_offset = offsetof(ss_proxy_t, ss); - ssi.opaque_user_data_offset = - offsetof(ss_proxy_t, conn); - ssi.rx = ss_proxy_onward_rx; - ssi.tx = ss_proxy_onward_tx; - ssi.state = ss_proxy_onward_state; - } - - if (lws_ss_deserialize_parse(&conn->parser, - lws_get_context(wsi), conn->dsh, in, len, - &conn->state, conn, &conn->ss, &ssi, 0)) { - lwsl_err("%s: RAW_RX: deserialize_parse fail\n", __func__); - return -1; - } - - if (conn->state == LPCS_REPORTING_FAIL || - conn->state == LPCS_REPORTING_OK) - lws_callback_on_writable(conn->wsi); - - break; - - case LWS_CALLBACK_RAW_WRITEABLE: - // lwsl_notice("LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE\n"); - - /* - * We can transmit something back to the client from the dsh - * of stuff we received on its behalf from the ss - */ - - if (!conn || !conn->wsi) - break; - - n = 0; - pay = 0; - s[3] = 0; - cp = (const uint8_t *)s; - switch (conn->state) { - case LPCS_REPORTING_FAIL: - s[3] = 1; - /* fallthru */ - case LPCS_REPORTING_OK: - s[0] = LWSSS_SER_RXPRE_CREATE_RESULT; - s[1] = 0; - s[2] = 1; - - n = 4; - - /* - * If there's rideshare sequencing, it's added after the - * first 4 bytes or the create result, comma-separated - */ - - rsp = conn->ss->policy; - - while (rsp) { - if (n != 4 && n < (int)sizeof(s) - 2) - s[n++] = ','; - n += lws_snprintf(&s[n], sizeof(s) - n, - "%s", rsp->streamtype); - rsp = lws_ss_policy_lookup(wsi->context, - rsp->rideshare_streamtype); - } - s[2] = n - 3; - conn->state = LPCS_OPERATIONAL; - lws_set_timeout(wsi, 0, 0); - break; - case LPCS_OPERATIONAL: - if (lws_dsh_get_head(conn->dsh, KIND_SS_TO_P, - (void **)&p, &si)) - break; - cp = p; - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (cp[0] == LWSSS_SER_RXPRE_RX_PAYLOAD && - wsi->context->detailed_latency_cb) { - - /* - * we're fulfilling rx that came in on ss - * by sending it back out to the client on - * the Unix Domain Socket - * - * + 7 u32 write will compute latency here... - * + 11 u32 ust we received from ss - * - * lws_write will report it and fill in - * LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE - */ - - us = lws_now_usecs(); - lws_ser_wu32be(&p[7], us - - lws_ser_ru64be(&p[11])); - lws_ser_wu64be(&p[11], us); - - wsi->detlat.acc_size = - wsi->detlat.req_size = si - 19; - /* time proxy held it */ - wsi->detlat.latencies[ - LAT_DUR_PROXY_RX_TO_ONWARD_TX] = - lws_ser_ru32be(&p[7]); - } -#endif - - pay = 1; - n = (int)si; - break; - default: - break; - } -again: - if (!n) - break; - - n = lws_write(wsi, (uint8_t *)cp, n, LWS_WRITE_RAW); - if (n < 0) { - lwsl_info("%s: WRITEABLE: %d\n", __func__, n); - - goto hangup; - } - - switch (conn->state) { - case LPCS_REPORTING_FAIL: - goto hangup; - case LPCS_OPERATIONAL: - if (pay) - lws_dsh_free((void **)&p); - if (!lws_dsh_get_head(conn->dsh, KIND_SS_TO_P, - (void **)&p, &si)) { - if (!lws_send_pipe_choked(wsi)) { - cp = p; - pay = 1; - n = (int)si; - goto again; - } - lws_callback_on_writable(wsi); - } - break; - default: - break; - } - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); - -hangup: - //lws_ss_destroy(&conn->ss); - //conn->state = LPCS_DESTROYED; - - /* hang up on him */ - return -1; -} - -static const struct lws_protocols protocols[] = { - { - "ssproxy-protocol", - callback_ss_proxy, - sizeof(struct raw_pss), - 2048, 2048, NULL, 0 - }, - { NULL, NULL, 0, 0, 0, NULL, 0 } -}; - -/* - * called from create_context() - */ - -int -lws_ss_proxy_create(struct lws_context *context, const char *bind, int port) -{ - struct lws_context_creation_info info; - - memset(&info, 0, sizeof(info)); - - info.vhost_name = "ssproxy"; - info.options = LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG; - info.port = port; - if (!port) { - if (!bind) - bind = "@proxy.ss.lws"; - info.options |= LWS_SERVER_OPTION_UNIX_SOCK; - } - info.iface = bind; - info.unix_socket_perms = "root:root"; - info.listen_accept_role = "raw-skt"; - info.listen_accept_protocol = "ssproxy-protocol"; - info.protocols = protocols; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("%s: Failed to create ss proxy vhost\n", __func__); - - return 1; - } - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/secure-streams/secure-streams-serialize.c libwebsockets-2.4.2/lib/secure-streams/secure-streams-serialize.c --- libwebsockets-4.0.20/lib/secure-streams/secure-streams-serialize.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/secure-streams-serialize.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,926 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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. - * - * - * In the case Secure Streams protocol needs to pass through a buffer, - * or a streamed connection, the protocol metadata must be serialized. This - * file provides internal apis to perform the serialization and deserialization - * in and out of an lws_dsh fifo-type buffer. - */ - -#include - -typedef enum { - RPAR_TYPE, - RPAR_LEN_MSB, - RPAR_LEN_LSB, - - RPAR_FLAG_B3, - RPAR_FLAG_B2, - RPAR_FLAG_B1, - RPAR_FLAG_B0, - - RPAR_LATA3, - RPAR_LATA2, - RPAR_LATA1, - RPAR_LATA0, - - RPAR_LATB7, - RPAR_LATB6, - RPAR_LATB5, - RPAR_LATB4, - RPAR_LATB3, - RPAR_LATB2, - RPAR_LATB1, - RPAR_LATB0, - - RPAR_RIDESHARE_LEN, - RPAR_RIDESHARE, - - RPAR_RESULT_CREATION_RIDESHARE, - - RPAR_METADATA_NAMELEN, - RPAR_METADATA_NAME, - RPAR_METADATA_VALUE, - - RPAR_PAYLOAD, - - RPAR_RX_TXCR_UPDATE, - - RPAR_STREAMTYPE, - RPAR_INITTXC0, - - RPAR_TXCR0, - - RPAR_RESULT_CREATION, - - RPAR_STATEINDEX, - RPAR_ORD3, - RPAR_ORD2, - RPAR_ORD1, - RPAR_ORD0, -} rx_parser_t; - -#if defined(_DEBUG) -static const char *sn[] = { - "unset", - - "LPCS_WAIT_INITIAL_TX", - "LPCS_REPORTING_FAIL", - "LPCS_REPORTING_OK", - "LPCS_OPERATIONAL", - "LPCS_DESTROYED", - - "LPCS_SENDING_INITIAL_TX", - "LPCS_WAITING_CREATE_RESULT", - "LPCS_LOCAL_CONNECTED", - "LPCS_ONWARD_CONNECT", -}; -#endif - -void -lws_ss_serialize_state_transition(lws_ss_conn_states_t *state, int new_state) -{ -#if defined(_DEBUG) - lwsl_info("%s: %s -> %s\n", __func__, sn[*state], sn[new_state]); -#endif - *state = new_state; -} - - -/* - * event loop received something and is queueing it for the foreign side of - * the dsh to consume later as serialized rx - */ - -int -lws_ss_serialize_rx_payload(struct lws_dsh *dsh, const uint8_t *buf, - size_t len, int flags, const char *rsp) -{ - lws_usec_t us = lws_now_usecs(); - uint8_t pre[128]; - int est = 19, l = 0; - - if (flags & LWSSS_FLAG_RIDESHARE) { - /* - * We should have the rideshare name if we have been told it's - * on a non-default rideshare - */ - assert(rsp); - l = strlen(rsp); - est += 1 + l; - } else - assert(!rsp); - - // lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags); - // lwsl_hexdump_info(buf, len); - - pre[0] = LWSSS_SER_RXPRE_RX_PAYLOAD; - lws_ser_wu16be(&pre[1], len + est - 3); - lws_ser_wu32be(&pre[3], flags); - lws_ser_wu32be(&pre[7], 0); /* write will compute latency here... */ - lws_ser_wu64be(&pre[11], us); /* ... and set this to the write time */ - - /* - * If we are on a non-default rideshare, append the non-default name to - * the headers of the payload part, 1-byte length first - */ - - if (flags & LWSSS_FLAG_RIDESHARE) { - pre[19] = (uint8_t)l; - memcpy(&pre[20], rsp, l); - } - - if (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, est, buf, len)) { - lwsl_err("%s: unable to alloc in dsh 1\n", __func__); - - return 1; - } - - return 0; -} - -/* - * event loop is consuming dsh-buffered, already-serialized tx from the - * foreign side - */ - -int -lws_ss_deserialize_tx_payload(struct lws_dsh *dsh, struct lws *wsi, - lws_ss_tx_ordinal_t ord, uint8_t *buf, - size_t *len, int *flags) -{ - uint8_t *p; - size_t si; - - if (lws_dsh_get_head(dsh, KIND_C_TO_P, (void **)&p, &si)) { - *len = 0; - return 0; - } - - /* - * The packet in the dsh has a proxying serialization header, process - * and strip it so we just forward the payload - */ - - if (*len <= si - 23 || si < 23) { - /* - * What comes out of the dsh needs to fit in the tx buffer - */ - lwsl_err("%s: *len = %d, si = %d\n", __func__, (int)*len, (int)si); - assert(0); - return 1; - } - if (p[0] != LWSSS_SER_TXPRE_TX_PAYLOAD) { - assert(0); - return 1; - } - - *len = lws_ser_ru16be(&p[1]) - (23 - 3); - assert(*len == si - 23); - - memcpy(buf, p + 23, si - 23); - - *flags = lws_ser_ru32be(&p[3]); - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (wsi && wsi->context->detailed_latency_cb) { - /* - * use the proxied latency information to compute the client - * and our delays, and apply to wsi. - * - * + 7 u32 us held at client before written - * +11 u32 us taken for transit to proxy - * +15 u64 ustime when proxy got packet from client - */ - lws_usec_t us = lws_now_usecs(); - - wsi->detlat.acc_size = wsi->detlat.req_size = si - 23; - wsi->detlat.latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE] = - lws_ser_ru32be(&p[7]); - wsi->detlat.latencies[LAT_DUR_PROXY_CLIENT_WRITE_TO_PROXY_RX] = - lws_ser_ru32be(&p[11]); - wsi->detlat.latencies[LAT_DUR_PROXY_RX_TO_ONWARD_TX] = - us - lws_ser_ru64be(&p[15]); - - wsi->detlat.latencies[LAT_DUR_USERCB] = 0; - } -#endif - - // lwsl_user("%s: len %d, flags: %d\n", __func__, (int)*len, *flags); - // lwsl_hexdump_info(buf, *len); - - lws_dsh_free((void **)&p); - - return 0; -} - -/* - * event loop side is issuing state, serialize and put it in the dbuf for - * the foreign side to consume later - */ - -int -lws_ss_serialize_state(struct lws_dsh *dsh, lws_ss_constate_t state, - lws_ss_tx_ordinal_t ack) -{ - uint8_t pre[8]; - - lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - pre[0] = LWSSS_SER_RXPRE_CONNSTATE; - pre[1] = 0; - pre[2] = 5; - pre[3] = (uint8_t)state; - lws_ser_wu32be(&pre[4], ack); - - if (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, 8, NULL, 0)) { - lwsl_err("%s: unable to alloc in dsh 2\n", __func__); - - return 1; - } - - return 0; -} - -/* - * event loop side was told about remote peer tx credit window update, serialize - * and put it in the dbuf for the foreign side to consume later - */ - -int -lws_ss_serialize_txcr(struct lws_dsh *dsh, int txcr) -{ - uint8_t pre[7]; - - lwsl_info("%s: %d\n", __func__, txcr); - - pre[0] = LWSSS_SER_RXPRE_TXCR_UPDATE; - pre[1] = 0; - pre[2] = 4; - lws_ser_wu32be(&pre[3], txcr); - - if (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, 7, NULL, 0)) { - lwsl_err("%s: unable to alloc in dsh 2\n", __func__); - - return 1; - } - - return 0; -} - -/* - * event loop side is consuming serialized data from the client via dsh, parse - * it using a bytewise parser for the serialization header(s)... - * it's possibly coalesced - */ - -int -lws_ss_deserialize_parse(struct lws_ss_serialization_parser *par, - struct lws_context *context, - struct lws_dsh *dsh, const uint8_t *cp, size_t len, - lws_ss_conn_states_t *state, void *parconn, - lws_ss_handle_t **pss, lws_ss_info_t *ssi, char client) -{ - lws_ss_metadata_t *pm; - lws_sspc_handle_t *h; - uint8_t pre[23]; - lws_usec_t us; - uint32_t flags; - uint8_t *p; - int n; - - while (len--) { - switch (par->ps) { - case RPAR_TYPE: - par->type = *cp++; - par->ps++; - break; - - case RPAR_LEN_MSB: /* this is remaining frame length */ - par->rem = (*cp++) << 8; - par->ps++; - break; - - case RPAR_LEN_LSB: - par->rem |= *cp++; - switch (par->type) { - - /* event loop side */ - - case LWSSS_SER_TXPRE_TX_PAYLOAD: - if (client) - goto hangup; - if (*state != LPCS_OPERATIONAL) - goto hangup; - par->ps = RPAR_FLAG_B3; - break; - - case LWSSS_SER_TXPRE_DESTROYING: - if (client) - goto hangup; - par->ps = RPAR_TYPE; - lwsl_notice("%s: DESTROYING\n", __func__); - goto hangup; - - case LWSSS_SER_TXPRE_ONWARD_CONNECT: - if (client) - goto hangup; - if (*state != LPCS_OPERATIONAL) - goto hangup; - par->ps = RPAR_TYPE; - if (*pss) - _lws_ss_client_connect(*pss, 0); - break; - - case LWSSS_SER_TXPRE_STREAMTYPE: - if (client) - goto hangup; - if (*state != LPCS_WAIT_INITIAL_TX) - goto hangup; - if (par->rem < 4) - goto hangup; - par->ctr = 0; - par->ps = RPAR_INITTXC0; - break; - - case LWSSS_SER_TXPRE_METADATA: - if (client) - goto hangup; - if (par->rem < 3) - goto hangup; - par->ctr = 0; - par->ps = RPAR_METADATA_NAMELEN; - break; - - case LWSSS_SER_TXPRE_TXCR_UPDATE: - par->ps = RPAR_TXCR0; - par->ctr = 0; - break; - - /* client side */ - - case LWSSS_SER_RXPRE_RX_PAYLOAD: - if (!client) - goto hangup; - if (*state != LPCS_OPERATIONAL && - *state != LPCS_LOCAL_CONNECTED) { - lwsl_err("rx in state %d\n", *state); - goto hangup; - } - par->rideshare[0] = '\0'; - par->ps = RPAR_FLAG_B3; - break; - - case LWSSS_SER_RXPRE_CREATE_RESULT: - if (!client) - goto hangup; - if (*state != LPCS_WAITING_CREATE_RESULT) { - lwsl_err("a2\n"); - goto hangup; - } - if (par->rem < 1) { - lwsl_err("a3\n"); - goto hangup; - } - par->ps = RPAR_RESULT_CREATION; - break; - - case LWSSS_SER_RXPRE_CONNSTATE: - if (!client) - goto hangup; - if (*state != LPCS_LOCAL_CONNECTED && - *state != LPCS_OPERATIONAL) { - lwsl_err("a4\n"); - goto hangup; - } - if (par->rem < 4) { - lwsl_err("a5\n"); - goto hangup; - } - par->ps = RPAR_STATEINDEX; - break; - - case LWSSS_SER_RXPRE_TXCR_UPDATE: - par->ctr = 0; - par->ps = RPAR_RX_TXCR_UPDATE; - break; - - default: - lwsl_notice("%s: bad type 0x%x\n", __func__, - par->type); - goto hangup; - } - break; - - case RPAR_FLAG_B3: - case RPAR_FLAG_B2: - case RPAR_FLAG_B1: - case RPAR_FLAG_B0: - par->flags <<= 8; - par->flags |= *cp++; - par->ps++; - if (!par->rem--) - goto hangup; - break; - - case RPAR_LATA3: - case RPAR_LATA2: - case RPAR_LATA1: - case RPAR_LATA0: - par->usd_phandling <<= 8; - par->usd_phandling |= *cp++; - par->ps++; - if (!par->rem--) - goto hangup; - break; - - case RPAR_LATB7: - case RPAR_LATB6: - case RPAR_LATB5: - case RPAR_LATB4: - case RPAR_LATB3: - case RPAR_LATB2: - case RPAR_LATB1: - case RPAR_LATB0: - par->ust_pwait <<= 8; - par->ust_pwait |= *cp++; - par->ps++; - par->frag1 = 1; - if (!par->rem--) - goto hangup; - - if (par->ps == RPAR_RIDESHARE_LEN && - !(par->flags & LWSSS_FLAG_RIDESHARE)) - par->ps = RPAR_PAYLOAD; - - if (par->rem) - break; - - /* fallthru - handle 0-length payload */ - - if (!(par->flags & LWSSS_FLAG_RIDESHARE)) - goto payload_ff; - goto hangup; - - /* - * Inbound rideshare info is provided on the RX packet - * itself - */ - - case RPAR_RIDESHARE_LEN: - par->slen = *cp++; - par->ctr = 0; - par->ps++; - if (par->rem-- < par->slen) - goto hangup; - break; - - case RPAR_RIDESHARE: - par->rideshare[par->ctr++] = *cp++; - if (!par->rem--) - goto hangup; - if (par->ctr != par->slen) - break; - par->ps = RPAR_PAYLOAD; - if (par->rem) - break; - - /* fallthru - handle 0-length payload */ - - case RPAR_PAYLOAD: -payload_ff: - n = (int)len + 1; - if (n > par->rem) - n = par->rem; - if (n > 1380) - n = 1380; - - /* deal with refragmented SOM / EOM flags */ - - flags = par->flags & LWSSS_FLAG_RELATED_START; - if (par->frag1) - flags |= par->flags & - (LWSSS_FLAG_SOM | LWSSS_FLAG_POLL); - - if (par->rem == n) - flags |= par->flags & (LWSSS_FLAG_EOM | - LWSSS_FLAG_RELATED_END); - - par->frag1 = 0; - us = lws_now_usecs(); - - if (!client) { - /* - * Proxy - we received some serialized tx from - * the client. - * - * The header for buffering private to the - * proxy is 23 bytes vs 19 to hold the - * current time when it was buffered - */ - - lwsl_info("%s: C2P RX: len %d\n", __func__, (int)n); - - p = pre; - pre[0] = LWSSS_SER_TXPRE_TX_PAYLOAD; - lws_ser_wu16be(&p[1], n + 23 - 3); - lws_ser_wu32be(&p[3], par->flags); - /* us held at client before written */ - lws_ser_wu32be(&p[7], par->usd_phandling); - /* us taken for transit to proxy */ - lws_ser_wu32be(&p[11], us - par->ust_pwait); - /* time used later to find proxy hold time */ - lws_ser_wu64be(&p[15], us); - - if (lws_dsh_alloc_tail(dsh, KIND_C_TO_P, pre, - 23, cp, n)) { - lwsl_err("%s: unable to alloc in dsh 3\n", - __func__); - - return 1; - } - - lws_ss_request_tx(*pss); - } else { - - /* - * Client receives some RX from proxy - * - * Pass whatever payload we have to ss user - */ - - lwsl_info("%s: P2C RX: len %d\n", __func__, (int)n); - - h = lws_container_of(par, lws_sspc_handle_t, parser); - h->txc.peer_tx_cr_est -= n; - - ssi->rx((void *)pss, (uint8_t *)cp, n, flags); - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (lws_det_lat_active(context)) { - lws_detlat_t d; - - d.type = LDLT_READ; - d.acc_size = d.req_size = n; - d.latencies[LAT_DUR_USERCB] = - lws_now_usecs() - us; - d.latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE] = - par->usd_phandling; - d.latencies[LAT_DUR_PROXY_CLIENT_WRITE_TO_PROXY_RX] = - us - par->ust_pwait; - - lws_det_lat_cb(context, &d); - } -#endif - } - - if (n) { - cp += n; - par->rem -= n; - len = (len + 1) - n; - } - if (!par->rem) - par->ps = RPAR_TYPE; - break; - - case RPAR_RX_TXCR_UPDATE: - if (!--par->rem && par->ctr != 3) - goto hangup; - - par->temp32 = (par->temp32 << 8) | *cp++; - if (++par->ctr < 4) - break; - - /* - * Proxy is telling us remote endpoint is allowing us - * par->temp32 more bytes tx credit to write to it - */ - - h = lws_container_of(par, lws_sspc_handle_t, parser); - h->txc.tx_cr += par->temp32; - lwsl_info("%s: RX_PEER_TXCR: %d\n", __func__, par->temp32); - lws_sspc_request_tx(h); /* in case something waiting */ - par->ctr = 0; - par->ps = RPAR_TYPE; - break; - - case RPAR_INITTXC0: - if (!--par->rem) - goto hangup; - - par->temp32 = (par->temp32 << 8) | *cp++; - if (++par->ctr < 4) - break; - - par->txcr_out = par->temp32; - par->ctr = 0; - par->ps = RPAR_STREAMTYPE; - break; - - /* - * These are the client adjusting our / the remote peer ability - * to send back to him. He's sending a signed u32 BE - */ - - case RPAR_TXCR0: - - par->temp32 = (par->temp32 << 8) | *cp++; - if (++par->ctr < 4) { - if (!--par->rem) - goto hangup; - break; - } - - if (--par->rem) - goto hangup; - - if (!client) { - /* - * We're the proxy, being told by the client - * that it wants to allow more tx from the peer - * on the onward connection towards it. - */ -#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT) - if ((*pss)->wsi) { - lws_wsi_tx_credit((*pss)->wsi, - LWSTXCR_PEER_TO_US, - par->temp32); - lwsl_notice("%s: proxy RX_PEER_TXCR: +%d (est %d)\n", - __func__, par->temp32, - (*pss)->wsi->txc.peer_tx_cr_est); - lws_ss_request_tx(*pss); - } else -#endif - lwsl_info("%s: dropping TXCR\n", __func__); - } else { - /* - * We're the client, being told by the proxy - * about tx credit being given to us from the - * remote peer, allowing the client to write to - * it. - */ - h = lws_container_of(par, lws_sspc_handle_t, parser); - h->txc.tx_cr += par->temp32; - lwsl_info("%s: client RX_PEER_TXCR: %d\n", - __func__, par->temp32); - lws_sspc_request_tx(h); /* in case something waiting */ - } - par->ps = RPAR_TYPE; - break; - - case RPAR_METADATA_NAMELEN: - if (!--par->rem) - goto hangup; - par->slen = *cp++; - if (par->slen >= sizeof(par->metadata_name) - 1) - goto hangup; - par->ctr = 0; - par->ps++; - break; - - case RPAR_METADATA_NAME: - if (!--par->rem) - goto hangup; - par->metadata_name[par->ctr++] = *cp++; - if (par->ctr != par->slen) - break; - par->ps = RPAR_METADATA_VALUE; - - /* only non-client side can receive these */ - - /* - * This is the policy's metadata list for the given - * name - */ - pm = lws_ss_policy_metadata((*pss)->policy, - par->metadata_name); - if (!pm) { - lwsl_err("%s: metadata %s not in proxy policy\n", - __func__, par->metadata_name); - - goto hangup; - } - - par->ssmd = &(*pss)->metadata[pm->length]; - - if (par->ssmd->value_on_lws_heap) - lws_free_set_NULL(par->ssmd->value); - par->ssmd->value_on_lws_heap = 0; - - par->ssmd->value = lws_malloc(par->rem + 1, "metadata"); - if (!par->ssmd->value) { - lwsl_err("%s: OOM mdv\n", __func__); - goto hangup; - } - par->ssmd->length = par->rem; - /* mark it as needing cleanup */ - par->ssmd->value_on_lws_heap = 1; - par->ctr = 0; - break; - - case RPAR_METADATA_VALUE: - ((uint8_t *)(par->ssmd->value))[par->ctr++] = *cp++; - if (--par->rem) - break; - - /* we think we got all the value */ - lwsl_info("%s: RPAR_METADATA_VALUE for %s (len %d)\n", - __func__, par->ssmd->name, - (int)par->ssmd->length); - lwsl_hexdump_info(par->ssmd->value, par->ssmd->length); - par->ps = RPAR_TYPE; - break; - - case RPAR_STREAMTYPE: - if (client) - goto hangup; - if (par->ctr == sizeof(par->streamtype) - 1) - goto hangup; - - /* - * We're the proxy, creating an SS on behalf of a - * client - */ - - par->streamtype[par->ctr++] = *cp++; - if (--par->rem) - break; - - par->ps = RPAR_TYPE; - par->streamtype[par->ctr] = '\0'; - lwsl_notice("%s: creating proxied ss '%s', txcr %d\n", - __func__, par->streamtype, par->txcr_out); - - ssi->streamtype = par->streamtype; - if (par->txcr_out) - ssi->manual_initial_tx_credit = par->txcr_out; - - if (lws_ss_create(context, 0, ssi, parconn, pss, NULL, NULL)) { - /* - * We're unable to create the onward secure - * stream he asked for... schedule a chance to - * inform him - */ - lwsl_err("%s: create '%s' fail\n", - __func__, par->streamtype); - *state = LPCS_REPORTING_FAIL; - } else { - lwsl_debug("%s: create '%s' OK\n", - __func__, par->streamtype); - *state = LPCS_REPORTING_OK; - } - - if (*pss) { - (*pss)->being_serialized = 1; - lwsl_notice("%s: Created SS initial credit %d\n", - __func__, par->txcr_out); - (*pss)->info.manual_initial_tx_credit = par->txcr_out; - } - - /* parent needs to schedule write on client conn */ - break; - - /* clientside states */ - - case RPAR_RESULT_CREATION: - if (*cp++) { - lwsl_err("%s: stream creation failed\n", - __func__); - goto hangup; - } - - lws_ss_serialize_state_transition(state, - LPCS_LOCAL_CONNECTED); - h = lws_container_of(par, lws_sspc_handle_t, parser); - if (h->cwsi) - lws_callback_on_writable(h->cwsi); - - /* - * This is telling us that the streamtype could be (and - * was) created at the proxy. It's not telling us that - * the onward peer connection could be connected. - * - * We'll get a proxied state() coming later that informs - * us about the situation with that. - */ - - par->rsl_pos = 0; - par->rsl_idx = 0; - h = lws_container_of(par, lws_sspc_handle_t, parser); - memset(&h->rideshare_ofs[0], 0, sizeof(h->rideshare_ofs[0])); - h->rideshare_list[0] = '\0'; - h->rsidx = 0; - - if (!--par->rem) - par->ps = RPAR_TYPE; - else { - par->ps = RPAR_RESULT_CREATION_RIDESHARE; - if (par->rem >= sizeof(h->rideshare_list)) - goto hangup; - } - break; - - case RPAR_RESULT_CREATION_RIDESHARE: - h = lws_container_of(par, lws_sspc_handle_t, parser); - if (*cp == ',') { - cp++; - h->rideshare_list[par->rsl_pos++] = '\0'; - if (par->rsl_idx == LWS_ARRAY_SIZE(h->rideshare_ofs)) - goto hangup; - h->rideshare_ofs[++par->rsl_idx] = par->rsl_pos; - } else - h->rideshare_list[par->rsl_pos++] = *cp++; - if (!--par->rem) - par->ps = RPAR_TYPE; - break; - - case RPAR_STATEINDEX: - par->ctr = *cp++; - par->ps = RPAR_ORD3; - break; - - case RPAR_ORD3: - par->flags = (*cp++) << 24; - par->ps++; - break; - - case RPAR_ORD2: - par->flags |= (*cp++) << 16; - par->ps++; - break; - - case RPAR_ORD1: - par->flags |= (*cp++) << 8; - par->ps++; - break; - - case RPAR_ORD0: - par->flags |= *cp++; - par->ps++; - par->ps = RPAR_TYPE; - - /* - * we received a proxied state change - */ - - switch (par->ctr) { - case LWSSSCS_DISCONNECTED: - case LWSSSCS_UNREACHABLE: - case LWSSSCS_AUTH_FAILED: - lws_ss_serialize_state_transition(state, - LPCS_LOCAL_CONNECTED); - break; - case LWSSSCS_CONNECTED: - lwsl_info("%s: CONNECTED %s\n", __func__, - ssi->streamtype); - lws_ss_serialize_state_transition(state, - LPCS_OPERATIONAL); - break; - default: - break; - } - - if (par->ctr < 0 || par->ctr > 9) - goto hangup; - -#if defined(_DEBUG) - lwsl_info("%s: forwarding proxied state %s\n", - __func__, sn[par->ctr]); -#endif - if (ssi->state((void *)pss, NULL, par->ctr, par->flags)) - goto hangup; - break; - - - default: - goto hangup; - } - } - - return 0; - -hangup: - return -1; -} diff -Nru libwebsockets-4.0.20/lib/secure-streams/system/auth-api.amazon.com/auth.c libwebsockets-2.4.2/lib/secure-streams/system/auth-api.amazon.com/auth.c --- libwebsockets-4.0.20/lib/secure-streams/system/auth-api.amazon.com/auth.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/system/auth-api.amazon.com/auth.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,276 +0,0 @@ -/* - * LWA auth support for Secure Streams - * - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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 - -typedef struct ss_api_amazon_auth { - struct lws_ss_handle *ss; - void *opaque_data; - /* ... application specific state ... */ - struct lejp_ctx jctx; - lws_sorted_usec_list_t sul; - size_t pos; - int expires_secs; -} ss_api_amazon_auth_t; - -static const char * const lejp_tokens_lwa[] = { - "access_token", - "expires_in", -}; - -typedef enum { - LSSPPT_ACCESS_TOKEN, - LSSPPT_EXPIRES_IN, -} lejp_tokens_t; - -enum { - AUTH_IDX_LWA, - AUTH_IDX_ROOT, -}; - -static void -lws_ss_sys_auth_api_amazon_com_kick(lws_sorted_usec_list_t *sul) -{ - struct lws_context *context = lws_container_of(sul, struct lws_context, - sul_api_amazon_com_kick); - - lws_state_transition_steps(&context->mgr_system, - LWS_SYSTATE_OPERATIONAL); -} - -static void -lws_ss_sys_auth_api_amazon_com_renew(lws_sorted_usec_list_t *sul) -{ - struct lws_context *context = lws_container_of(sul, struct lws_context, - sul_api_amazon_com); - - /* if nothing is there to intercept anything, go all the way */ - lws_state_transition_steps(&context->mgr_system, - LWS_SYSTATE_OPERATIONAL); -} - -static signed char -auth_api_amazon_com_parser_cb(struct lejp_ctx *ctx, char reason) -{ - ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)ctx->user; - struct lws_context *context = (struct lws_context *)m->opaque_data; - - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - switch (ctx->path_match - 1) { - case LSSPPT_ACCESS_TOKEN: - if (!ctx->npos) - break; - if (lws_system_blob_heap_append(lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, - AUTH_IDX_LWA), - (const uint8_t *)ctx->buf, - ctx->npos)) { - lwsl_err("%s: unable to store auth token\n", __func__); - return -1; - } - break; - case LSSPPT_EXPIRES_IN: - m->expires_secs = atoi(ctx->buf); - lws_sul_schedule(context, 0, &context->sul_api_amazon_com, - lws_ss_sys_auth_api_amazon_com_renew, - (uint64_t)m->expires_secs * LWS_US_PER_SEC); - break; - } - - return 0; -} - -/* secure streams payload interface */ - -static int -ss_api_amazon_auth_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ - ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj; - struct lws_context *context = (struct lws_context *)m->opaque_data; - lws_system_blob_t *ab; - size_t total; - int n; - - ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_LWA); - - if (buf) { - if (flags & LWSSS_FLAG_SOM) { - lejp_construct(&m->jctx, auth_api_amazon_com_parser_cb, - m, lejp_tokens_lwa, - LWS_ARRAY_SIZE(lejp_tokens_lwa)); - lws_system_blob_heap_empty(ab); - } - - n = (int)(signed char)lejp_parse(&m->jctx, buf, len); - if (n < 0) { - lejp_destruct(&m->jctx); - lws_system_blob_destroy( - lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, - AUTH_IDX_LWA)); - - return -1; - } - } - if (!(flags & LWSSS_FLAG_EOM)) - return 0; - - /* we should have the auth token now */ - - total = lws_system_blob_get_size(ab); - lwsl_notice("%s: acquired %u-byte api.amazon.com auth token, exp %ds\n", - __func__, (unsigned int)total, m->expires_secs); - - lejp_destruct(&m->jctx); - - /* we move the system state at auth connection close */ - - return 0; -} - -static int -ss_api_amazon_auth_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, - size_t *len, int *flags) -{ - ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj; - struct lws_context *context = (struct lws_context *)m->opaque_data; - lws_system_blob_t *ab; - size_t total; - int n; - - /* - * We send out auth slot AUTH_IDX_ROOT, it's the LWA user / device - * identity token - */ - - ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_ROOT); - total = lws_system_blob_get_size(ab); - - n = lws_system_blob_get(ab, buf, len, m->pos); - if (n < 0) - return 1; - - if (!m->pos) - *flags |= LWSSS_FLAG_SOM; - - m->pos += *len; - - if (m->pos == total) { - *flags |= LWSSS_FLAG_EOM; - m->pos = 0; /* for next time */ - } - - return 0; -} - -static int -ss_api_amazon_auth_state(void *userobj, void *sh, lws_ss_constate_t state, - lws_ss_tx_ordinal_t ack) -{ - ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj; - struct lws_context *context = (struct lws_context *)m->opaque_data; - size_t s; - - lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - case LWSSSCS_CONNECTING: - s = lws_system_blob_get_size( - lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, - AUTH_IDX_ROOT)); - lws_ss_request_tx_len(m->ss, s); - m->pos = 0; - break; - - case LWSSSCS_DISCONNECTED: - /* - * We defer moving the system state forward until we have - * closed our connection + tls for the auth action... this is - * because on small systems, we need that memory recovered - * before we can make another connection subsequently. - * - * At this point, we're ultimately being called from within - * the wsi close process, the tls tunnel is not freed yet. - * Use a sul to actually do it next time around the event loop - * when the close process for the auth wsi has completed and - * the related tls is already freed. - */ - s = lws_system_blob_get_size( - lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, - AUTH_IDX_LWA)); - - if (s) - lws_sul_schedule(context, 0, - &context->sul_api_amazon_com_kick, - lws_ss_sys_auth_api_amazon_com_kick, 1); - break; - - case LWSSSCS_DESTROYING: - lws_sul_schedule(context, 0, &context->sul_api_amazon_com, - NULL, LWS_SET_TIMER_USEC_CANCEL); - lws_system_blob_destroy( - lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, - AUTH_IDX_LWA)); - break; - - default: - break; - } - - return 0; -} - -int -lws_ss_sys_auth_api_amazon_com(struct lws_context *context) -{ - lws_ss_info_t ssi; - - if (context->hss_auth) /* already exists */ - return 0; - - /* We're making an outgoing secure stream ourselves */ - - memset(&ssi, 0, sizeof(ssi)); - ssi.handle_offset = offsetof(ss_api_amazon_auth_t, ss); - ssi.opaque_user_data_offset = offsetof(ss_api_amazon_auth_t, opaque_data); - ssi.rx = ss_api_amazon_auth_rx; - ssi.tx = ss_api_amazon_auth_tx; - ssi.state = ss_api_amazon_auth_state; - ssi.user_alloc = sizeof(ss_api_amazon_auth_t); - ssi.streamtype = "api_amazon_com_auth"; - - if (lws_ss_create(context, 0, &ssi, context, &context->hss_auth, - NULL, NULL)) { - lwsl_info("%s: Create LWA auth ss failed (policy?)\n", __func__); - return 1; - } - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/secure-streams/system/fetch-policy/fetch-policy.c libwebsockets-2.4.2/lib/secure-streams/system/fetch-policy/fetch-policy.c --- libwebsockets-4.0.20/lib/secure-streams/system/fetch-policy/fetch-policy.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/secure-streams/system/fetch-policy/fetch-policy.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -/* - * Policy fetching for Secure Streams - * - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2019 - 2020 Andy Green - * - * 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 - -typedef struct ss_fetch_policy { - struct lws_ss_handle *ss; - void *opaque_data; - /* ... application specific state ... */ - - lws_sorted_usec_list_t sul; - - uint8_t partway; -} ss_fetch_policy_t; - -/* secure streams payload interface */ - -static int -ss_fetch_policy_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ - ss_fetch_policy_t *m = (ss_fetch_policy_t *)userobj; - struct lws_context *context = (struct lws_context *)m->opaque_data; - - if (flags & LWSSS_FLAG_SOM) { - if (lws_ss_policy_parse_begin(context)) - return 1; - m->partway = 1; - } - - if (len && lws_ss_policy_parse(context, buf, len) < 0) - return 1; - - if (flags & LWSSS_FLAG_EOM) - m->partway = 2; - - return 0; -} - -static int -ss_fetch_policy_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, - size_t *len, int *flags) -{ - return 1; -} - -static void -policy_set(lws_sorted_usec_list_t *sul) -{ - ss_fetch_policy_t *m = lws_container_of(sul, ss_fetch_policy_t, sul); - struct lws_context *context = (struct lws_context *)m->opaque_data; - - /* - * We get called if the policy parse was successful, just after the - * ss connection close that was using the vhost from the old policy - */ - - if (lws_ss_policy_set(context, "updated")) - lwsl_err("%s: policy set failed\n", __func__); - else { - context->policy_updated = 1; - lws_state_transition_steps(&context->mgr_system, - LWS_SYSTATE_OPERATIONAL); - } -} - -static int -ss_fetch_policy_state(void *userobj, void *sh, lws_ss_constate_t state, - lws_ss_tx_ordinal_t ack) -{ - ss_fetch_policy_t *m = (ss_fetch_policy_t *)userobj; - struct lws_context *context = (struct lws_context *)m->opaque_data; - - lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - lws_ss_request_tx(m->ss); - break; - case LWSSSCS_CONNECTING: - break; - - case LWSSSCS_DISCONNECTED: - lwsl_info("%s: DISCONNECTED\n", __func__); - switch (m->partway) { - case 1: - lws_ss_policy_parse_abandon(context); - break; - - case 2: - lws_sul_schedule(context, 0, &m->sul, policy_set, 1); - break; - } - m->partway = 0; - break; - - default: - break; - } - - return 0; -} - -int -lws_ss_sys_fetch_policy(struct lws_context *context) -{ - lws_ss_info_t ssi; - - if (context->hss_fetch_policy) /* already exists */ - return 0; - - /* We're making an outgoing secure stream ourselves */ - - memset(&ssi, 0, sizeof(ssi)); - ssi.handle_offset = offsetof(ss_fetch_policy_t, ss); - ssi.opaque_user_data_offset = offsetof(ss_fetch_policy_t, opaque_data); - ssi.rx = ss_fetch_policy_rx; - ssi.tx = ss_fetch_policy_tx; - ssi.state = ss_fetch_policy_state; - ssi.user_alloc = sizeof(ss_fetch_policy_t); - ssi.streamtype = "fetch_policy"; - - if (lws_ss_create(context, 0, &ssi, context, &context->hss_fetch_policy, - NULL, NULL)) { - /* - * If there's no fetch_policy streamtype, it can just be we're - * running on a proxied client with no policy of its own, - * it's OK. - */ - lwsl_info("%s: Create LWA auth ss failed (policy?)\n", __func__); - - return 1; - } - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/server/access-log.c libwebsockets-2.4.2/lib/server/access-log.c --- libwebsockets-4.0.20/lib/server/access-log.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/access-log.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,172 @@ +/* + * libwebsockets - server access log handling + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +/* + * Produce Apache-compatible log string for wsi, like this: + * + * 2.31.234.19 - - [27/Mar/2016:03:22:44 +0800] + * "GET /aep-screen.png HTTP/1.1" + * 200 152987 "https://libwebsockets.org/index.html" + * "Mozilla/5.0 (Macint... Chrome/49.0.2623.87 Safari/537.36" + * + */ + +extern const char * const method_names[]; + +static const char * const hver[] = { + "HTTP/1.0", "HTTP/1.1", "HTTP/2" +}; + +void +lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth) +{ +#ifdef LWS_WITH_IPV6 + char ads[INET6_ADDRSTRLEN]; +#else + char ads[INET_ADDRSTRLEN]; +#endif + char da[64]; + const char *pa, *me; + struct tm *tmp; + time_t t = time(NULL); + int l = 256, m; + + if (wsi->access_log_pending) + lws_access_log(wsi); + + wsi->access_log.header_log = lws_malloc(l, "access log"); + if (wsi->access_log.header_log) { + + tmp = localtime(&t); + if (tmp) + strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", tmp); + else + strcpy(da, "01/Jan/1970:00:00:00 +0000"); + + pa = lws_get_peer_simple(wsi, ads, sizeof(ads)); + if (!pa) + pa = "(unknown)"; + + if (wsi->http2_substream) + me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD); + else + me = method_names[meth]; + if (!me) + me = "(null)"; + + lws_snprintf(wsi->access_log.header_log, l, + "%s - - [%s] \"%s %s %s\"", + pa, da, me, uri_ptr, + hver[wsi->u.http.request_version]); + + l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT); + if (l) { + wsi->access_log.user_agent = lws_malloc(l + 2, "access log"); + if (!wsi->access_log.user_agent) { + lwsl_err("OOM getting user agent\n"); + lws_free_set_NULL(wsi->access_log.header_log); + return; + } + + lws_hdr_copy(wsi, wsi->access_log.user_agent, + l + 1, WSI_TOKEN_HTTP_USER_AGENT); + + for (m = 0; m < l; m++) + if (wsi->access_log.user_agent[m] == '\"') + wsi->access_log.user_agent[m] = '\''; + } + l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER); + if (l) { + wsi->access_log.referrer = lws_malloc(l + 2, "referrer"); + if (!wsi->access_log.referrer) { + lwsl_err("OOM getting user agent\n"); + lws_free_set_NULL(wsi->access_log.user_agent); + lws_free_set_NULL(wsi->access_log.header_log); + return; + } + lws_hdr_copy(wsi, wsi->access_log.referrer, + l + 1, WSI_TOKEN_HTTP_REFERER); + + for (m = 0; m < l; m++) + if (wsi->access_log.referrer[m] == '\"') + wsi->access_log.referrer[m] = '\''; + } + wsi->access_log_pending = 1; + } +} + + +int +lws_access_log(struct lws *wsi) +{ + char *p = wsi->access_log.user_agent, ass[512], + *p1 = wsi->access_log.referrer; + int l; + + if (!wsi->access_log_pending) + return 0; + + if (!wsi->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->access_log.header_log, + wsi->access_log.response, wsi->access_log.sent, p1); + if (strlen(p) > sizeof(ass) - 6 - l) + p[sizeof(ass) - 6 - l] = '\0'; + l += lws_snprintf(ass + l, sizeof(ass) - 1 - l, "\" \"%s\"\n", p); + + if (wsi->vhost->log_fd != (int)LWS_INVALID_FILE) { + if (write(wsi->vhost->log_fd, ass, l) != l) + lwsl_err("Failed to write log\n"); + } else + lwsl_err("%s", ass); + + if (wsi->access_log.header_log) { + lws_free(wsi->access_log.header_log); + wsi->access_log.header_log = NULL; + } + if (wsi->access_log.user_agent) { + lws_free(wsi->access_log.user_agent); + wsi->access_log.user_agent = NULL; + } + if (wsi->access_log.referrer) { + lws_free(wsi->access_log.referrer); + wsi->access_log.referrer = NULL; + } + wsi->access_log_pending = 0; + + return 0; +} + diff -Nru libwebsockets-4.0.20/lib/server/cgi.c libwebsockets-2.4.2/lib/server/cgi.c --- libwebsockets-4.0.20/lib/server/cgi.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/cgi.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1105 @@ +/* + * libwebsockets - CGI management + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#if defined(WIN32) || defined(_WIN32) +#else +#include +#endif + +static const char *hex = "0123456789ABCDEF"; + +static int +urlencode(const char *in, int inlen, char *out, int outlen) +{ + char *start = out, *end = out + outlen; + + while (inlen-- && out < end - 4) { + if ((*in >= 'A' && *in <= 'Z') || + (*in >= 'a' && *in <= 'z') || + (*in >= '0' && *in <= '9') || + *in == '-' || + *in == '_' || + *in == '.' || + *in == '~') { + *out++ = *in++; + continue; + } + if (*in == ' ') { + *out++ = '+'; + in++; + continue; + } + *out++ = '%'; + *out++ = hex[(*in) >> 4]; + *out++ = hex[(*in++) & 15]; + } + *out = '\0'; + + if (out >= end - 4) + return -1; + + return out - start; +} + +static struct lws * +lws_create_basic_wsi(struct lws_context *context, int tsi) +{ + struct lws *new_wsi; + + if (!context->vhost_list) + return NULL; + + if ((unsigned int)context->pt[tsi].fds_count == + context->fd_limit_per_thread - 1) { + lwsl_err("no space for new conn\n"); + return NULL; + } + + new_wsi = lws_zalloc(sizeof(struct lws), "new wsi"); + if (new_wsi == NULL) { + lwsl_err("Out of memory for new connection\n"); + return NULL; + } + + new_wsi->tsi = tsi; + new_wsi->context = context; + new_wsi->pending_timeout = NO_PENDING_TIMEOUT; + new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* initialize the instance struct */ + + new_wsi->state = LWSS_CGI; + new_wsi->mode = LWSCM_CGI; + new_wsi->hdr_parsing_completed = 0; + new_wsi->position_in_fds_table = -1; + + /* + * these can only be set once the protocol is known + * we set an unestablished connection's protocol pointer + * to the start of the defauly vhost supported list, so it can look + * for matching ones during the handshake + */ + new_wsi->protocol = context->vhost_list->protocols; + new_wsi->user_space = NULL; + new_wsi->ietf_spec_revision = 0; + new_wsi->desc.sockfd = LWS_SOCK_INVALID; + context->count_wsi_allocated++; + + return new_wsi; +} + +LWS_VISIBLE LWS_EXTERN int +lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len, + int timeout_secs, const struct lws_protocol_vhost_options *mp_cgienv) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char *env_array[30], cgi_path[400], e[1024], *p = e, + *end = p + sizeof(e) - 1, tok[256], *t; + struct lws_cgi *cgi; + int n, m = 0, i, uritok = -1; + + /* + * give the master wsi a cgi struct + */ + + wsi->cgi = lws_zalloc(sizeof(*wsi->cgi), "new cgi"); + if (!wsi->cgi) { + lwsl_err("%s: OOM\n", __func__); + return -1; + } + + wsi->cgi->response_code = HTTP_STATUS_OK; + + cgi = wsi->cgi; + cgi->wsi = wsi; /* set cgi's owning wsi */ + + /* create pipes for [stdin|stdout] and [stderr] */ + + for (n = 0; n < 3; n++) + if (pipe(cgi->pipe_fds[n]) == -1) + goto bail1; + + /* create cgi wsis for each stdin/out/err fd */ + + for (n = 0; n < 3; n++) { + cgi->stdwsi[n] = lws_create_basic_wsi(wsi->context, wsi->tsi); + if (!cgi->stdwsi[n]) + goto bail2; + cgi->stdwsi[n]->cgi_channel = n; + cgi->stdwsi[n]->vhost = wsi->vhost; + + lwsl_debug("%s: cgi %p: pipe fd %d -> fd %d / %d\n", __func__, + cgi->stdwsi[n], n, cgi->pipe_fds[n][!!(n == 0)], + cgi->pipe_fds[n][!(n == 0)]); + + /* read side is 0, stdin we want the write side, others read */ + cgi->stdwsi[n]->desc.sockfd = cgi->pipe_fds[n][!!(n == 0)]; + if (fcntl(cgi->pipe_fds[n][!!(n == 0)], F_SETFL, + O_NONBLOCK) < 0) { + lwsl_err("%s: setting NONBLOCK failed\n", __func__); + goto bail2; + } + } + + for (n = 0; n < 3; n++) { + lws_libuv_accept(cgi->stdwsi[n], cgi->stdwsi[n]->desc); + if (insert_wsi_socket_into_fds(wsi->context, cgi->stdwsi[n])) + goto bail3; + cgi->stdwsi[n]->parent = wsi; + cgi->stdwsi[n]->sibling_list = wsi->child_list; + wsi->child_list = cgi->stdwsi[n]; + } + + lws_change_pollfd(cgi->stdwsi[LWS_STDIN], LWS_POLLIN, LWS_POLLOUT); + lws_change_pollfd(cgi->stdwsi[LWS_STDOUT], LWS_POLLOUT, LWS_POLLIN); + lws_change_pollfd(cgi->stdwsi[LWS_STDERR], LWS_POLLOUT, LWS_POLLIN); + + lwsl_debug("%s: fds in %d, out %d, err %d\n", __func__, + cgi->stdwsi[LWS_STDIN]->desc.sockfd, + cgi->stdwsi[LWS_STDOUT]->desc.sockfd, + cgi->stdwsi[LWS_STDERR]->desc.sockfd); + + if (timeout_secs) + lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs); + + /* the cgi stdout is always sending us http1.x header data first */ + wsi->hdr_state = LCHS_HEADER; + + /* add us to the pt list of active cgis */ + lwsl_debug("%s: adding cgi %p to list\n", __func__, wsi->cgi); + cgi->cgi_list = pt->cgi_list; + pt->cgi_list = cgi; + + /* prepare his CGI env */ + + n = 0; + + if (lws_is_ssl(wsi)) + env_array[n++] = "HTTPS=ON"; + if (wsi->u.hdr.ah) { + static const unsigned char meths[] = { + WSI_TOKEN_GET_URI, + WSI_TOKEN_POST_URI, + WSI_TOKEN_OPTIONS_URI, + WSI_TOKEN_PUT_URI, + WSI_TOKEN_PATCH_URI, + WSI_TOKEN_DELETE_URI, + WSI_TOKEN_CONNECT, + WSI_TOKEN_HEAD_URI, + #ifdef LWS_WITH_HTTP2 + WSI_TOKEN_HTTP_COLON_PATH, + #endif + }; + static const char * const meth_names[] = { + "GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", + "CONNECT", ":path" + }; + + if (script_uri_path_len >= 0) + for (m = 0; m < ARRAY_SIZE(meths); m++) + if (lws_hdr_total_length(wsi, meths[m]) >= + script_uri_path_len) { + uritok = meths[m]; + break; + } + + if (script_uri_path_len < 0 && uritok < 0) + goto bail3; +// if (script_uri_path_len < 0) +// uritok = 0; + + if (uritok >= 0) { + lws_snprintf(cgi_path, sizeof(cgi_path) - 1, + "REQUEST_URI=%s", + lws_hdr_simple_ptr(wsi, uritok)); + cgi_path[sizeof(cgi_path) - 1] = '\0'; + env_array[n++] = cgi_path; + } + + if (m >= 0) { + env_array[n++] = p; + if (m < 8) + p += lws_snprintf(p, end - p, + "REQUEST_METHOD=%s", + meth_names[m]); + else + p += lws_snprintf(p, end - p, + "REQUEST_METHOD=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD)); + p++; + } + + env_array[n++] = p; + p += lws_snprintf(p, end - p, "QUERY_STRING="); + /* dump the individual URI Arg parameters */ + m = 0; + while (script_uri_path_len >= 0) { + i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok), + WSI_TOKEN_HTTP_URI_ARGS, m); + if (i < 0) + break; + t = tok; + while (*t && *t != '=' && p < end - 4) + *p++ = *t++; + if (*t == '=') + *p++ = *t++; + i = urlencode(t, i- (t - tok), p, end - p); + if (i > 0) { + p += i; + *p++ = '&'; + } + m++; + } + if (m) + p--; + *p++ = '\0'; + + if (script_uri_path_len >= 0) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "PATH_INFO=%s", + lws_hdr_simple_ptr(wsi, uritok) + + script_uri_path_len); + p++; + } + } + if (script_uri_path_len >= 0 && + lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "HTTP_REFERER=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER)); + p++; + } + if (script_uri_path_len >= 0 && + lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "HTTP_HOST=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); + p++; + } + if (script_uri_path_len >= 0 && + lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "HTTP_COOKIE="); + m = lws_hdr_copy(wsi, p, end - p, WSI_TOKEN_HTTP_COOKIE); + if (m > 0) + p += lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE); + *p++ = '\0'; + } + if (script_uri_path_len >= 0 && + lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "USER_AGENT=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT)); + p++; + } + if (script_uri_path_len >= 0 && + uritok == WSI_TOKEN_POST_URI) { + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "CONTENT_TYPE=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "CONTENT_LENGTH=%s", + lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_CONTENT_LENGTH)); + p++; + } + } + env_array[n++] = "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin"; + + env_array[n++] = p; + p += lws_snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]) + 1; + + while (mp_cgienv) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "%s=%s", mp_cgienv->name, + mp_cgienv->value); + lwsl_debug(" Applying mount-specific cgi env '%s'\n", + env_array[n - 1]); + p++; + mp_cgienv = mp_cgienv->next; + } + + env_array[n++] = "SERVER_SOFTWARE=libwebsockets"; + env_array[n] = NULL; + +#if 0 + for (m = 0; m < n; m++) + lwsl_err(" %s\n", env_array[m]); +#endif + + /* + * Actually having made the env, as a cgi we don't need the ah + * any more + */ + if (script_uri_path_len >= 0 && + lws_header_table_is_in_detachable_state(wsi)) + lws_header_table_detach(wsi, 0); + + /* we are ready with the redirection pipes... run the thing */ +#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE) + cgi->pid = fork(); +#else + cgi->pid = vfork(); +#endif + if (cgi->pid < 0) { + lwsl_err("fork failed, errno %d", errno); + goto bail3; + } + +#if defined(__linux__) + prctl(PR_SET_PDEATHSIG, SIGTERM); +#endif + if (script_uri_path_len >= 0) + /* stops non-daemonized main processess getting SIGINT + * from TTY */ + setpgrp(); + + if (cgi->pid) { + /* we are the parent process */ + wsi->context->count_cgi_spawned++; + lwsl_debug("%s: cgi %p spawned PID %d\n", __func__, + cgi, cgi->pid); + + for (n = 0; n < 3; n++) + close(cgi->pipe_fds[n][!(n == 0)]); + + /* inform cgi owner of the child PID */ + n = user_callback_handle_rxflow(wsi->protocol->callback, wsi, + LWS_CALLBACK_CGI_PROCESS_ATTACH, + wsi->user_space, NULL, cgi->pid); + (void)n; + + return 0; + } + + /* somewhere we can at least read things and enter it */ + if (chdir("/tmp")) + lwsl_notice("%s: Failed to chdir\n", __func__); + + /* We are the forked process, redirect and kill inherited things. + * + * Because of vfork(), we cannot do anything that changes pages in + * the parent environment. Stuff that changes kernel state for the + * process is OK. Stuff that happens after the execvpe() is OK. + */ + + for (n = 0; n < 3; n++) { + if (dup2(cgi->pipe_fds[n][!(n == 0)], n) < 0) { + lwsl_err("%s: stdin dup2 failed\n", __func__); + goto bail3; + } + close(cgi->pipe_fds[n][!(n == 0)]); + } + +#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE) + for (m = 0; m < n; m++) { + p = strchr(env_array[m], '='); + *p++ = '\0'; + setenv(env_array[m], p, 1); + } + execvp(exec_array[0], (char * const *)&exec_array[0]); +#else + execvpe(exec_array[0], (char * const *)&exec_array[0], &env_array[0]); +#endif + + exit(1); + +bail3: + /* drop us from the pt cgi list */ + pt->cgi_list = cgi->cgi_list; + + while (--n >= 0) + remove_wsi_socket_from_fds(wsi->cgi->stdwsi[n]); +bail2: + for (n = 0; n < 3; n++) + if (wsi->cgi->stdwsi[n]) + lws_free_wsi(cgi->stdwsi[n]); + +bail1: + for (n = 0; n < 3; n++) { + if (cgi->pipe_fds[n][0]) + close(cgi->pipe_fds[n][0]); + if (cgi->pipe_fds[n][1]) + close(cgi->pipe_fds[n][1]); + } + + lws_free_set_NULL(wsi->cgi); + + lwsl_err("%s: failed\n", __func__); + + return -1; +} + +/* we have to parse out these headers in the CGI output */ + +static const char * const significant_hdr[SIGNIFICANT_HDR_COUNT] = { + "content-length: ", + "location: ", + "status: ", + "transfer-encoding: chunked", +}; + +enum header_recode { + HR_NAME, + HR_WHITESPACE, + HR_ARG, + HR_CRLF, +}; + +LWS_VISIBLE LWS_EXTERN int +lws_cgi_write_split_stdout_headers(struct lws *wsi) +{ + int n, m, cmd; + unsigned char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start, + *end = &buf[sizeof(buf) - 1 - LWS_PRE], *name, + *value = NULL; + char c, hrs; + + if (!wsi->cgi) + return -1; + + while (wsi->hdr_state != LHCS_PAYLOAD) { + /* + * We have to separate header / finalize and payload chunks, + * since they need to be handled separately + */ + switch (wsi->hdr_state) { + case LHCS_RESPONSE: + lwsl_debug("LHCS_RESPONSE: issuing response %d\n", + wsi->cgi->response_code); + if (lws_add_http_header_status(wsi, + wsi->cgi->response_code, + &p, end)) + return 1; + if (!wsi->cgi->explicitly_chunked && + !wsi->cgi->content_length && + lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_TRANSFER_ENCODING, + (unsigned char *)"chunked", 7, &p, end)) + return 1; + if (!(wsi->http2_substream)) + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_CONNECTION, + (unsigned char *)"close", 5, + &p, end)) + return 1; + n = lws_write(wsi, start, p - start, + LWS_WRITE_HTTP_HEADERS | LWS_WRITE_NO_FIN); + + /* + * so we have a bunch of http/1 style ascii headers + * starting from wsi->cgi->headers_buf through + * wsi->cgi->headers_pos. These are OK for http/1 + * connections, but they're no good for http/2 conns. + * + * Let's redo them at headers_pos forward using the + * correct coding for http/1 or http/2 + */ + if (!wsi->http2_substream) + goto post_hpack_recode; + + p = wsi->cgi->headers_start; + wsi->cgi->headers_start = wsi->cgi->headers_pos; + wsi->cgi->headers_dumped = wsi->cgi->headers_start; + hrs = HR_NAME; + name = buf; + + while (p < wsi->cgi->headers_start) { + switch (hrs) { + case HR_NAME: + /* + * in http/2 upper-case header names + * are illegal. So convert to lower- + * case. + */ + if (name - buf > 64) + return -1; + if (*p != ':') { + if (*p >= 'A' && *p <= 'Z') + *name++ = (*p++) + + ('a' - 'A'); + else + *name++ = *p++; + } else { + p++; + *name++ = '\0'; + value = name; + hrs = HR_WHITESPACE; + } + break; + case HR_WHITESPACE: + if (*p == ' ') { + p++; + break; + } + hrs = HR_ARG; + /* fallthru */ + case HR_ARG: + if (name > end - 64) + return -1; + + if (*p != '\x0a' && *p != '\x0d') { + *name++ = *p++; + break; + } + hrs = HR_CRLF; + /* fallthru */ + case HR_CRLF: + if ((*p != '\x0a' && *p != '\x0d') || + p + 1 == wsi->cgi->headers_start) { + *name = '\0'; + if ((strcmp((const char *)buf, + "transfer-encoding") + )) { + lwsl_debug("+ %s: %s\n", + buf, value); + if ( + lws_add_http_header_by_name(wsi, buf, + (unsigned char *)value, name - value, + (unsigned char **)&wsi->cgi->headers_pos, + (unsigned char *)wsi->cgi->headers_end)) + return 1; + hrs = HR_NAME; + name = buf; + break; + } + } + p++; + break; + } + } +post_hpack_recode: + /* finalize cached headers before dumping them */ + if (lws_finalize_http_header(wsi, + (unsigned char **)&wsi->cgi->headers_pos, + (unsigned char *)wsi->cgi->headers_end)) { + + lwsl_notice("finalize failed\n"); + return -1; + } + + wsi->hdr_state = LHCS_DUMP_HEADERS; + wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_HEADERS; + lws_callback_on_writable(wsi); + /* back to the loop for writeability again */ + return 0; + + case LHCS_DUMP_HEADERS: + + n = wsi->cgi->headers_pos - wsi->cgi->headers_dumped; + if (n > 512) + n = 512; + + lwsl_debug("LHCS_DUMP_HEADERS: %d\n", n); + + cmd = LWS_WRITE_HTTP_HEADERS_CONTINUATION; + if (wsi->cgi->headers_dumped + n != + wsi->cgi->headers_pos) { + lwsl_notice("adding no fin flag\n"); + cmd |= LWS_WRITE_NO_FIN; + } + + m = lws_write(wsi, + (unsigned char *)wsi->cgi->headers_dumped, + n, cmd); + if (m < 0) { + lwsl_debug("%s: write says %d\n", __func__, m); + return -1; + } + wsi->cgi->headers_dumped += n; + if (wsi->cgi->headers_dumped == wsi->cgi->headers_pos) { + wsi->hdr_state = LHCS_PAYLOAD; + lws_free_set_NULL(wsi->cgi->headers_buf); + lwsl_debug("freed cgi headers\n"); + } else { + wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_HEADERS; + lws_callback_on_writable(wsi); + } + + /* writeability becomes uncertain now we wrote + * something, we must return to the event loop + */ + return 0; + } + + if (!wsi->cgi->headers_buf) { + /* if we don't already have a headers buf, cook one up */ + n = 2048; + if (wsi->http2_substream) + n = 4096; + wsi->cgi->headers_buf = lws_malloc(n + LWS_PRE, + "cgi hdr buf"); + if (!wsi->cgi->headers_buf) { + lwsl_err("OOM\n"); + return -1; + } + + lwsl_debug("allocated cgi hdrs\n"); + wsi->cgi->headers_start = wsi->cgi->headers_buf + LWS_PRE; + wsi->cgi->headers_pos = wsi->cgi->headers_start; + wsi->cgi->headers_dumped = wsi->cgi->headers_pos; + wsi->cgi->headers_end = wsi->cgi->headers_buf + n - 1; + + for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) { + wsi->cgi->match[n] = 0; + wsi->cgi->lp = 0; + } + } + + n = lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]); + if (n < 0) + return -1; + n = read(n, &c, 1); + if (n < 0) { + if (errno != EAGAIN) { + lwsl_debug("%s: read says %d\n", __func__, n); + return -1; + } + else + n = 0; + + if (wsi->cgi->headers_pos >= wsi->cgi->headers_end - 4) { + lwsl_notice("CGI hdrs > buf size\n"); + + return -1; + } + } + if (!n) + goto agin; + + lwsl_debug("-- 0x%02X %c %d %d\n", (unsigned char)c, c, + wsi->cgi->match[1], wsi->hdr_state); + if (!c) + return -1; + switch (wsi->hdr_state) { + case LCHS_HEADER: + hdr: + for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) { + /* + * significant headers with + * numeric decimal payloads + */ + if (!significant_hdr[n][wsi->cgi->match[n]] && + (c >= '0' && c <= '9') && + wsi->cgi->lp < sizeof(wsi->cgi->l) - 1) { + wsi->cgi->l[wsi->cgi->lp++] = c; + wsi->cgi->l[wsi->cgi->lp] = '\0'; + switch (n) { + case SIGNIFICANT_HDR_CONTENT_LENGTH: + wsi->cgi->content_length = + atoll(wsi->cgi->l); + break; + case SIGNIFICANT_HDR_STATUS: + wsi->cgi->response_code = + atol(wsi->cgi->l); + lwsl_debug("Status set to %d\n", + wsi->cgi->response_code); + break; + default: + break; + } + } + /* hits up to the NUL are sticky until next hdr */ + if (significant_hdr[n][wsi->cgi->match[n]]) { + if (tolower(c) == + significant_hdr[n][wsi->cgi->match[n]]) + wsi->cgi->match[n]++; + else + wsi->cgi->match[n] = 0; + } + } + + /* some cgi only send us \x0a for EOL */ + if (c == '\x0a') { + wsi->hdr_state = LCHS_SINGLE_0A; + *wsi->cgi->headers_pos++ = '\x0d'; + } + *wsi->cgi->headers_pos++ = c; + if (c == '\x0d') + wsi->hdr_state = LCHS_LF1; + + if (wsi->hdr_state != LCHS_HEADER && + !significant_hdr[SIGNIFICANT_HDR_TRANSFER_ENCODING] + [wsi->cgi->match[ + SIGNIFICANT_HDR_TRANSFER_ENCODING]]) { + lwsl_debug("cgi produced chunked\n"); + wsi->cgi->explicitly_chunked = 1; + } + + /* presence of Location: mandates 302 retcode */ + if (wsi->hdr_state != LCHS_HEADER && + !significant_hdr[SIGNIFICANT_HDR_LOCATION][ + wsi->cgi->match[SIGNIFICANT_HDR_LOCATION]]) { + lwsl_debug("CGI: Location hdr seen\n"); + wsi->cgi->response_code = 302; + } + + break; + case LCHS_LF1: + *wsi->cgi->headers_pos++ = c; + if (c == '\x0a') { + wsi->hdr_state = LCHS_CR2; + break; + } + /* we got \r[^\n]... it's unreasonable */ + lwsl_debug("%s: funny CRLF 0x%02X\n", __func__, + (unsigned char)c); + return -1; + + case LCHS_CR2: + if (c == '\x0d') { + /* drop the \x0d */ + wsi->hdr_state = LCHS_LF2; + break; + } + wsi->hdr_state = LCHS_HEADER; + for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) + wsi->cgi->match[n] = 0; + wsi->cgi->lp = 0; + goto hdr; + + case LCHS_LF2: + case LCHS_SINGLE_0A: + m = wsi->hdr_state; + if (c == '\x0a') { + lwsl_debug("Content-Length: %lld\n", + (unsigned long long) + wsi->cgi->content_length); + wsi->hdr_state = LHCS_RESPONSE; + /* + * drop the \0xa ... finalize + * will add it if needed (HTTP/1) + */ + break; + } + if (m == LCHS_LF2) + /* we got \r\n\r[^\n]... unreasonable */ + return -1; + /* we got \x0anext header, it's reasonable */ + *wsi->cgi->headers_pos++ = c; + wsi->hdr_state = LCHS_HEADER; + for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) + wsi->cgi->match[n] = 0; + wsi->cgi->lp = 0; + break; + case LHCS_PAYLOAD: + break; + } + +agin: + /* ran out of input, ended the hdrs, or filled up the hdrs buf */ + if (!n || wsi->hdr_state == LHCS_PAYLOAD) + return 0; + } + + /* payload processing */ + + m = !wsi->cgi->explicitly_chunked && !wsi->cgi->content_length; + n = lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]); + if (n < 0) + return -1; + n = read(n, start, sizeof(buf) - LWS_PRE - + (m ? LWS_HTTP_CHUNK_HDR_SIZE : 0)); + + if (n < 0 && errno != EAGAIN) { + lwsl_debug("%s: stdout read says %d\n", __func__, n); + return -1; + } + if (n > 0) { + if (!wsi->http2_substream && m) { + char chdr[LWS_HTTP_CHUNK_HDR_SIZE]; + m = lws_snprintf(chdr, LWS_HTTP_CHUNK_HDR_SIZE - 3, + "%X\x0d\x0a", n); + memmove(start + m, start, n); + memcpy(start, chdr, m); + memcpy(start + m + n, "\x0d\x0a", 2); + n += m + 2; + } + cmd = LWS_WRITE_HTTP; + if (wsi->cgi->content_length_seen + n == wsi->cgi->content_length) + cmd = LWS_WRITE_HTTP_FINAL; + m = lws_write(wsi, (unsigned char *)start, n, cmd); + //lwsl_notice("write %d\n", m); + if (m < 0) { + lwsl_debug("%s: stdout write says %d\n", __func__, m); + return -1; + } + wsi->cgi->content_length_seen += n; + } else { + if (wsi->cgi_stdout_zero_length) { + lwsl_debug("%s: stdout is POLLHUP'd\n", __func__); + if (wsi->http2_substream) + m = lws_write(wsi, (unsigned char *)start, 0, + LWS_WRITE_HTTP_FINAL); + return 1; + } + wsi->cgi_stdout_zero_length = 1; + } + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_cgi_kill(struct lws *wsi) +{ + struct lws_cgi_args args; + int status, n; + + lwsl_debug("%s: %p\n", __func__, wsi); + + if (!wsi->cgi) + return 0; + + if (wsi->cgi->pid > 0) { + n = waitpid(wsi->cgi->pid, &status, WNOHANG); + if (n > 0) { + lwsl_debug("%s: PID %d reaped\n", __func__, + wsi->cgi->pid); + goto handled; + } + /* kill the process group */ + n = kill(-wsi->cgi->pid, SIGTERM); + lwsl_debug("%s: SIGTERM child PID %d says %d (errno %d)\n", + __func__, wsi->cgi->pid, n, errno); + if (n < 0) { + /* + * hum seen errno=3 when process is listed in ps, + * it seems we don't always retain process grouping + * + * Direct these fallback attempt to the exact child + */ + n = kill(wsi->cgi->pid, SIGTERM); + if (n < 0) { + n = kill(wsi->cgi->pid, SIGPIPE); + if (n < 0) { + n = kill(wsi->cgi->pid, SIGKILL); + if (n < 0) + lwsl_err("%s: SIGKILL PID %d " + "failed errno %d " + "(maybe zombie)\n", + __func__, + wsi->cgi->pid, errno); + } + } + } + /* He could be unkillable because he's a zombie */ + n = 1; + while (n > 0) { + n = waitpid(-wsi->cgi->pid, &status, WNOHANG); + if (n > 0) + lwsl_debug("%s: reaped PID %d\n", __func__, n); + if (n <= 0) { + n = waitpid(wsi->cgi->pid, &status, WNOHANG); + if (n > 0) + lwsl_debug("%s: reaped PID %d\n", + __func__, n); + } + } + } + +handled: + args.stdwsi = &wsi->cgi->stdwsi[0]; + + if (wsi->cgi->pid != -1) { + n = user_callback_handle_rxflow(wsi->protocol->callback, wsi, + LWS_CALLBACK_CGI_TERMINATED, + wsi->user_space, + (void *)&args, wsi->cgi->pid); + wsi->cgi->pid = -1; + if (n && !wsi->cgi->being_closed) + lws_close_free_wsi(wsi, 0); + } + + return 0; +} + +LWS_EXTERN int +lws_cgi_kill_terminated(struct lws_context_per_thread *pt) +{ + struct lws_cgi **pcgi, *cgi = NULL; + int status, n = 1; + + while (n > 0) { + /* find finished guys but don't reap yet */ + n = waitpid(-1, &status, WNOHANG); + if (n <= 0) + continue; + lwsl_debug("%s: observed PID %d terminated\n", __func__, n); + + pcgi = &pt->cgi_list; + + /* check all the subprocesses on the cgi list */ + while (*pcgi) { + /* get the next one first as list may change */ + cgi = *pcgi; + pcgi = &(*pcgi)->cgi_list; + + if (cgi->pid <= 0) + continue; + + /* finish sending cached headers */ + if (cgi->headers_buf) + continue; + + /* wait for stdout to be drained */ + if (cgi->content_length > cgi->content_length_seen) + continue; + + if (cgi->content_length) { + lwsl_debug("%s: wsi %p: expected content length seen: %lld\n", + __func__, cgi->wsi, + (unsigned long long)cgi->content_length_seen); + } + + /* reap it */ + waitpid(n, &status, WNOHANG); + /* + * he's already terminated so no need for kill() + * but we should do the terminated cgi callback + * and close him if he's not already closing + */ + if (n == cgi->pid) { + lwsl_debug("%s: found PID %d on cgi list\n", + __func__, n); + + if (!cgi->content_length) { + /* + * well, if he sends chunked... + * give him 2s after the + * cgi terminated to send buffered + */ + cgi->chunked_grace++; + continue; + } + + /* defeat kill() */ + cgi->pid = 0; + lws_cgi_kill(cgi->wsi); + + break; + } + cgi = NULL; + } + /* if not found on the cgi list, as he's one of ours, reap */ + if (!cgi) { + lwsl_debug("%s: reading PID %d although no cgi match\n", + __func__, n); + waitpid(n, &status, WNOHANG); + } + } + + pcgi = &pt->cgi_list; + + /* check all the subprocesses on the cgi list */ + while (*pcgi) { + /* get the next one first as list may change */ + cgi = *pcgi; + pcgi = &(*pcgi)->cgi_list; + + if (cgi->pid <= 0) + continue; + + /* we deferred killing him after reaping his PID */ + if (cgi->chunked_grace) { + cgi->chunked_grace++; + if (cgi->chunked_grace < 2) + continue; + goto finish_him; + } + + /* finish sending cached headers */ + if (cgi->headers_buf) + continue; + + /* wait for stdout to be drained */ + if (cgi->content_length > cgi->content_length_seen) + continue; + + if (cgi->content_length) + lwsl_debug("%s: wsi %p: expected content length seen: %lld\n", + __func__, cgi->wsi, + (unsigned long long)cgi->content_length_seen); + + /* reap it */ + if (waitpid(cgi->pid, &status, WNOHANG) > 0) { + + if (!cgi->content_length) { + /* + * well, if he sends chunked... + * give him 2s after the + * cgi terminated to send buffered + */ + cgi->chunked_grace++; + continue; + } +finish_him: + lwsl_debug("%s: found PID %d on cgi list\n", + __func__, cgi->pid); + + /* defeat kill() */ + cgi->pid = 0; + lws_cgi_kill(cgi->wsi); + + break; + } + } + + return 0; +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch) +{ + if (!wsi->cgi) + return NULL; + + return wsi->cgi->stdwsi[ch]; +} + +void +lws_cgi_remove_and_kill(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct lws_cgi **pcgi = &pt->cgi_list; + + /* remove us from the cgi list */ + lwsl_debug("%s: remove cgi %p from list\n", __func__, wsi->cgi); + while (*pcgi) { + if (*pcgi == wsi->cgi) { + /* drop us from the pt cgi list */ + *pcgi = (*pcgi)->cgi_list; + break; + } + pcgi = &(*pcgi)->cgi_list; + } + if (wsi->cgi->headers_buf) { + lwsl_debug("close: freed cgi headers\n"); + lws_free_set_NULL(wsi->cgi->headers_buf); + } + /* we have a cgi going, we must kill it */ + wsi->cgi->being_closed = 1; + lws_cgi_kill(wsi); +} diff -Nru libwebsockets-4.0.20/lib/server/daemonize.c libwebsockets-2.4.2/lib/server/daemonize.c --- libwebsockets-4.0.20/lib/server/daemonize.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/daemonize.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,225 @@ +/* + * This code is mainly taken from Doug Potter's page + * + * http://www-theorie.physik.unizh.ch/~dpotter/howto/daemonize + * + * I contacted him 2007-04-16 about the license for the original code, + * he replied it is Public Domain. Use the URL above to get the original + * Public Domain version if you want it. + * + * This version is LGPL2.1+SLE like the rest of libwebsockets and is + * Copyright (c)2006 - 2013 Andy Green + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "private-libwebsockets.h" + +int pid_daemon; +static char *lock_path; + +int get_daemonize_pid() +{ + return pid_daemon; +} + +static void +child_handler(int signum) +{ + int fd, len, sent; + char sz[20]; + + switch (signum) { + + case SIGALRM: /* timed out daemonizing */ + exit(0); + break; + + case SIGUSR1: /* positive confirmation we daemonized well */ + + if (lock_path) { + /* Create the lock file as the current user */ + + fd = open(lock_path, O_TRUNC | O_RDWR | O_CREAT, 0640); + if (fd < 0) { + fprintf(stderr, + "unable to create lock file %s, code=%d (%s)\n", + lock_path, errno, strerror(errno)); + exit(0); + } + len = sprintf(sz, "%u", pid_daemon); + sent = write(fd, sz, len); + if (sent != len) + fprintf(stderr, + "unable to write pid to lock file %s, code=%d (%s)\n", + lock_path, errno, strerror(errno)); + + close(fd); + } + exit(0); + //!!(sent == len)); + + case SIGCHLD: /* daemonization failed */ + exit(0); + break; + } +} + +static void lws_daemon_closing(int sigact) +{ + if (getpid() == pid_daemon) + if (lock_path) { + unlink(lock_path); + lws_free_set_NULL(lock_path); + } + + kill(getpid(), SIGKILL); +} + +/* + * You just need to call this from your main(), when it + * returns you are all set "in the background" decoupled + * from the console you were started from. + * + * The process context you called from has been terminated then. + */ + +LWS_VISIBLE int +lws_daemonize(const char *_lock_path) +{ + struct sigaction act; + pid_t sid, parent; + int n, fd, ret; + char buf[10]; + + /* already a daemon */ +// if (getppid() == 1) +// return 1; + + if (_lock_path) { + fd = open(_lock_path, O_RDONLY); + if (fd >= 0) { + n = read(fd, buf, sizeof(buf)); + close(fd); + if (n) { + n = atoi(buf); + ret = kill(n, 0); + if (ret >= 0) { + fprintf(stderr, + "Daemon already running from pid %d\n", n); + exit(1); + } + fprintf(stderr, + "Removing stale lock file %s from dead pid %d\n", + _lock_path, n); + unlink(lock_path); + } + } + + n = strlen(_lock_path) + 1; + lock_path = lws_malloc(n, "daemonize lock"); + if (!lock_path) { + fprintf(stderr, "Out of mem in lws_daemonize\n"); + return 1; + } + strcpy(lock_path, _lock_path); + } + + /* Trap signals that we expect to receive */ + signal(SIGCHLD, child_handler); /* died */ + signal(SIGUSR1, child_handler); /* was happy */ + signal(SIGALRM, child_handler); /* timeout daemonizing */ + + /* Fork off the parent process */ + pid_daemon = fork(); + if (pid_daemon < 0) { + fprintf(stderr, "unable to fork daemon, code=%d (%s)", + errno, strerror(errno)); + exit(9); + } + + /* If we got a good PID, then we can exit the parent process. */ + if (pid_daemon > 0) { + + /* + * Wait for confirmation signal from the child via + * SIGCHILD / USR1, or for two seconds to elapse + * (SIGALRM). pause() should not return. + */ + alarm(2); + + pause(); + /* should not be reachable */ + exit(1); + } + + /* At this point we are executing as the child process */ + parent = getppid(); + pid_daemon = getpid(); + + /* Cancel certain signals */ + signal(SIGCHLD, SIG_DFL); /* A child process dies */ + signal(SIGTSTP, SIG_IGN); /* Various TTY signals */ + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */ + + /* Change the file mode mask */ + umask(0); + + /* Create a new SID for the child process */ + sid = setsid(); + if (sid < 0) { + fprintf(stderr, + "unable to create a new session, code %d (%s)", + errno, strerror(errno)); + exit(2); + } + + /* + * Change the current working directory. This prevents the current + * directory from being locked; hence not being able to remove it. + */ + if (chdir("/tmp") < 0) { + fprintf(stderr, + "unable to change directory to %s, code %d (%s)", + "/", errno, strerror(errno)); + exit(3); + } + + /* Redirect standard files to /dev/null */ + if (!freopen("/dev/null", "r", stdin)) + fprintf(stderr, "unable to freopen() stdin, code %d (%s)", + errno, strerror(errno)); + + if (!freopen("/dev/null", "w", stdout)) + fprintf(stderr, "unable to freopen() stdout, code %d (%s)", + errno, strerror(errno)); + + if (!freopen("/dev/null", "w", stderr)) + fprintf(stderr, "unable to freopen() stderr, code %d (%s)", + errno, strerror(errno)); + + /* Tell the parent process that we are A-okay */ + kill(parent, SIGUSR1); + + act.sa_handler = lws_daemon_closing; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + + sigaction(SIGTERM, &act, NULL); + + /* return to continue what is now "the daemon" */ + + return 0; +} + diff -Nru libwebsockets-4.0.20/lib/server/fops-zip.c libwebsockets-2.4.2/lib/server/fops-zip.c --- libwebsockets-4.0.20/lib/server/fops-zip.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/fops-zip.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,669 @@ +/* + * 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 + * + * 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. + * + * + * lws rewrite: + * + * Copyright (C) 2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#include + +/* + * 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, +}; + +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 >= 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, len)) + return LWS_FZ_ERR_NAME_READ; + if (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, + 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, + 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, 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, 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 = vpath - vfs_path - 1; + strncpy(rp, vfs_path, m); + rp[m] = '\0'; + + /* open the zip file itself using the incoming fops, not fops_zip */ + + priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops, rp, NULL, &local_flags); + if (!priv->zip_fop_fd) { + lwsl_err("unable to open zip %s\n", rp); + goto bail1; + } + + if (*vpath == '/') + vpath++; + + m = lws_fops_zip_scan(priv, vpath, 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 += offset_from_cur_pos; + + return 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 > priv->hdr.comp_size - + (cur - priv->content_start)) + rlen = priv->hdr.comp_size - + (priv->hdr.comp_size - + 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; + /* and fall through */ + 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 > priv->hdr.uncomp_size - (cur - priv->content_start)) + len = priv->hdr.comp_size - (priv->hdr.comp_size - + priv->content_start); + + if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd, + amount, buf, len)) + return LWS_FZ_ERR_READ_CONTENT; + + 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, +}; diff -Nru libwebsockets-4.0.20/lib/server/lejp-conf.c libwebsockets-2.4.2/lib/server/lejp-conf.c --- libwebsockets-4.0.20/lib/server/lejp-conf.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/lejp-conf.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,929 @@ +/* + * libwebsockets web server application + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" +#include "../misc/lejp.h" + +#ifndef _WIN32 +/* this is needed for Travis CI */ +#include +#endif + +#define ESC_INSTALL_DATADIR "_lws_ddir_" + +static const char * const paths_global[] = { + "global.uid", + "global.gid", + "global.count-threads", + "global.init-ssl", + "global.server-string", + "global.plugin-dir", + "global.ws-pingpong-secs", + "global.timeout-secs", + "global.reject-service-keywords[].*", + "global.reject-service-keywords[]", +}; + +enum lejp_global_paths { + LEJPGP_UID, + LEJPGP_GID, + LEJPGP_COUNT_THREADS, + LWJPGP_INIT_SSL, + LEJPGP_SERVER_STRING, + LEJPGP_PLUGIN_DIR, + LWJPGP_PINGPONG_SECS, + LWJPGP_TIMEOUT_SECS, + LWJPGP_REJECT_SERVICE_KEYWORDS_NAME, + LWJPGP_REJECT_SERVICE_KEYWORDS +}; + +static const char * const paths_vhosts[] = { + "vhosts[]", + "vhosts[].mounts[]", + "vhosts[].name", + "vhosts[].port", + "vhosts[].interface", + "vhosts[].unix-socket", + "vhosts[].sts", + "vhosts[].host-ssl-key", + "vhosts[].host-ssl-cert", + "vhosts[].host-ssl-ca", + "vhosts[].access-log", + "vhosts[].mounts[].mountpoint", + "vhosts[].mounts[].origin", + "vhosts[].mounts[].protocol", + "vhosts[].mounts[].default", + "vhosts[].mounts[].auth-mask", + "vhosts[].mounts[].cgi-timeout", + "vhosts[].mounts[].cgi-env[].*", + "vhosts[].mounts[].cache-max-age", + "vhosts[].mounts[].cache-reuse", + "vhosts[].mounts[].cache-revalidate", + "vhosts[].mounts[].basic-auth", + "vhosts[].mounts[].cache-intermediaries", + "vhosts[].mounts[].extra-mimetypes.*", + "vhosts[].mounts[].interpret.*", + "vhosts[].ws-protocols[].*.*", + "vhosts[].ws-protocols[].*", + "vhosts[].ws-protocols[]", + "vhosts[].keepalive_timeout", + "vhosts[].enable-client-ssl", + "vhosts[].ciphers", + "vhosts[].ecdh-curve", + "vhosts[].noipv6", + "vhosts[].ipv6only", + "vhosts[].ssl-option-set", + "vhosts[].ssl-option-clear", + "vhosts[].mounts[].pmo[].*", + "vhosts[].headers[].*", + "vhosts[].headers[]", + "vhosts[].client-ssl-key", + "vhosts[].client-ssl-cert", + "vhosts[].client-ssl-ca", + "vhosts[].client-ssl-ciphers", + "vhosts[].onlyraw", +}; + +enum lejp_vhost_paths { + LEJPVP, + LEJPVP_MOUNTS, + LEJPVP_NAME, + LEJPVP_PORT, + LEJPVP_INTERFACE, + LEJPVP_UNIXSKT, + LEJPVP_STS, + LEJPVP_HOST_SSL_KEY, + LEJPVP_HOST_SSL_CERT, + LEJPVP_HOST_SSL_CA, + LEJPVP_ACCESS_LOG, + LEJPVP_MOUNTPOINT, + LEJPVP_ORIGIN, + LEJPVP_MOUNT_PROTOCOL, + LEJPVP_DEFAULT, + LEJPVP_DEFAULT_AUTH_MASK, + LEJPVP_CGI_TIMEOUT, + LEJPVP_CGI_ENV, + LEJPVP_MOUNT_CACHE_MAX_AGE, + LEJPVP_MOUNT_CACHE_REUSE, + LEJPVP_MOUNT_CACHE_REVALIDATE, + LEJPVP_MOUNT_BASIC_AUTH, + LEJPVP_MOUNT_CACHE_INTERMEDIARIES, + LEJPVP_MOUNT_EXTRA_MIMETYPES, + LEJPVP_MOUNT_INTERPRET, + LEJPVP_PROTOCOL_NAME_OPT, + LEJPVP_PROTOCOL_NAME, + LEJPVP_PROTOCOL, + LEJPVP_KEEPALIVE_TIMEOUT, + LEJPVP_ENABLE_CLIENT_SSL, + LEJPVP_CIPHERS, + LEJPVP_ECDH_CURVE, + LEJPVP_NOIPV6, + LEJPVP_IPV6ONLY, + LEJPVP_SSL_OPTION_SET, + LEJPVP_SSL_OPTION_CLEAR, + LEJPVP_PMO, + LEJPVP_HEADERS_NAME, + LEJPVP_HEADERS, + LEJPVP_CLIENT_SSL_KEY, + LEJPVP_CLIENT_SSL_CERT, + LEJPVP_CLIENT_SSL_CA, + LEJPVP_CLIENT_CIPHERS, + LEJPVP_FLAG_ONLYRAW, +}; + +static const char * const parser_errs[] = { + "", + "", + "No opening '{'", + "Expected closing '}'", + "Expected '\"'", + "String underrun", + "Illegal unescaped control char", + "Illegal escape format", + "Illegal hex number", + "Expected ':'", + "Illegal value start", + "Digit required after decimal point", + "Bad number format", + "Bad exponent format", + "Unknown token", + "Too many ']'", + "Mismatched ']'", + "Expected ']'", + "JSON nesting limit exceeded", + "Nesting tracking used up", + "Number too long", + "Comma or block end expected", + "Unknown", + "Parser callback errored (see earlier error)", +}; + +#define MAX_PLUGIN_DIRS 10 + +struct jpargs { + struct lws_context_creation_info *info; + struct lws_context *context; + const struct lws_protocols *protocols; + const struct lws_extension *extensions; + char *p, *end, valid; + struct lws_http_mount *head, *last; + + struct lws_protocol_vhost_options *pvo; + struct lws_protocol_vhost_options *pvo_em; + struct lws_protocol_vhost_options *pvo_int; + struct lws_http_mount m; + const char **plugin_dirs; + int count_plugin_dirs; + + unsigned int enable_client_ssl:1; + unsigned int fresh_mount:1; + unsigned int any_vhosts:1; +}; + +static void * +lwsws_align(struct jpargs *a) +{ + if ((lws_intptr_t)(a->p) & 15) + a->p += 16 - ((lws_intptr_t)(a->p) & 15); + + return a->p; +} + +static int +arg_to_bool(const char *s) +{ + static const char * const on[] = { "on", "yes", "true" }; + int n = atoi(s); + + if (n) + return 1; + + for (n = 0; n < ARRAY_SIZE(on); n++) + if (!strcasecmp(s, on[n])) + return 1; + + return 0; +} + +static signed char +lejp_globals_cb(struct lejp_ctx *ctx, char reason) +{ + struct jpargs *a = (struct jpargs *)ctx->user; + struct lws_protocol_vhost_options *rej; + int n; + + /* we only match on the prepared path strings */ + if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) + return 0; + + /* this catches, eg, vhosts[].headers[].xxx */ + if (reason == LEJPCB_VAL_STR_END && + ctx->path_match == LWJPGP_REJECT_SERVICE_KEYWORDS_NAME + 1) { + rej = lwsws_align(a); + a->p += sizeof(*rej); + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + rej->next = a->info->reject_service_keywords; + a->info->reject_service_keywords = rej; + rej->name = a->p; + lwsl_notice(" adding rej %s=%s\n", a->p, ctx->buf); + a->p += n - 1; + *(a->p++) = '\0'; + rej->value = a->p; + rej->options = NULL; + goto dostring; + } + + switch (ctx->path_match - 1) { + case LEJPGP_UID: + a->info->uid = atoi(ctx->buf); + return 0; + case LEJPGP_GID: + a->info->gid = atoi(ctx->buf); + return 0; + case LEJPGP_COUNT_THREADS: + a->info->count_threads = atoi(ctx->buf); + return 0; + case LWJPGP_INIT_SSL: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + return 0; + case LEJPGP_SERVER_STRING: + a->info->server_string = a->p; + break; + case LEJPGP_PLUGIN_DIR: + if (a->count_plugin_dirs == MAX_PLUGIN_DIRS - 1) { + lwsl_err("Too many plugin dirs\n"); + return -1; + } + a->plugin_dirs[a->count_plugin_dirs++] = a->p; + break; + + case LWJPGP_PINGPONG_SECS: + a->info->ws_ping_pong_interval = atoi(ctx->buf); + return 0; + + case LWJPGP_TIMEOUT_SECS: + a->info->timeout_secs = atoi(ctx->buf); + return 0; + + default: + return 0; + } + +dostring: + a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf); + *(a->p)++ = '\0'; + + return 0; +} + +static signed char +lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) +{ + struct jpargs *a = (struct jpargs *)ctx->user; + struct lws_protocol_vhost_options *pvo, *mp_cgienv, *headers; + struct lws_http_mount *m; + char *p, *p1; + int n; + +#if 0 + lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match); + for (n = 0; n < ctx->wildcount; n++) + lwsl_notice(" %d\n", ctx->wild[n]); +#endif + + if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) { + /* set the defaults for this vhost */ + a->valid = 1; + a->head = NULL; + a->last = NULL; + a->info->port = 0; + a->info->iface = NULL; + a->info->protocols = a->protocols; + a->info->extensions = a->extensions; + a->info->ssl_cert_filepath = NULL; + a->info->ssl_private_key_filepath = NULL; + a->info->ssl_ca_filepath = NULL; + a->info->client_ssl_cert_filepath = NULL; + a->info->client_ssl_private_key_filepath = NULL; + a->info->client_ssl_ca_filepath = NULL; + a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-GCM-SHA384:" + "DHE-RSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-SHA384:" + "HIGH:!aNULL:!eNULL:!EXPORT:" + "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:" + "!SHA1:!DHE-RSA-AES128-GCM-SHA256:" + "!DHE-RSA-AES128-SHA256:" + "!AES128-GCM-SHA256:" + "!AES128-SHA256:" + "!DHE-RSA-AES256-SHA256:" + "!AES256-GCM-SHA384:" + "!AES256-SHA256"; + a->info->timeout_secs = 5; + a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-GCM-SHA384:" + "DHE-RSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-SHA384:" + "HIGH:!aNULL:!eNULL:!EXPORT:" + "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:" + "!SHA1:!DHE-RSA-AES128-GCM-SHA256:" + "!DHE-RSA-AES128-SHA256:" + "!AES128-GCM-SHA256:" + "!AES128-SHA256:" + "!DHE-RSA-AES256-SHA256:" + "!AES256-GCM-SHA384:" + "!AES256-SHA256"; + a->info->pvo = NULL; + a->info->headers = NULL; + a->info->keepalive_timeout = 5; + a->info->log_filepath = NULL; + a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK | + LWS_SERVER_OPTION_STS | LWS_SERVER_OPTION_ONLY_RAW); + a->enable_client_ssl = 0; + } + + if (reason == LEJPCB_OBJECT_START && + ctx->path_match == LEJPVP_MOUNTS + 1) { + a->fresh_mount = 1; + memset(&a->m, 0, sizeof(a->m)); + } + + /* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */ + if (reason == LEJPCB_OBJECT_START && + ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) { + a->pvo = lwsws_align(a); + a->p += sizeof(*a->pvo); + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + /* ie, enable this protocol, no options yet */ + a->pvo->next = a->info->pvo; + a->info->pvo = a->pvo; + a->pvo->name = a->p; + lwsl_notice(" adding protocol %s\n", a->p); + a->p += n; + a->pvo->value = a->p; + a->pvo->options = NULL; + goto dostring; + } + + /* this catches, eg, vhosts[].headers[].xxx */ + if (reason == LEJPCB_VAL_STR_END && + ctx->path_match == LEJPVP_HEADERS_NAME + 1) { + headers = lwsws_align(a); + a->p += sizeof(*headers); + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + /* ie, enable this protocol, no options yet */ + headers->next = a->info->headers; + a->info->headers = headers; + headers->name = a->p; + // lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf); + a->p += n - 1; + *(a->p++) = ':'; + if (a->p < a->end) + *(a->p++) = '\0'; + else + *(a->p - 1) = '\0'; + headers->value = a->p; + headers->options = NULL; + goto dostring; + } + + if (reason == LEJPCB_OBJECT_END && + (ctx->path_match == LEJPVP + 1 || !ctx->path[0]) && + a->valid) { + + struct lws_vhost *vhost; + + //lwsl_notice("%s\n", ctx->path); + if (!a->info->port) { + lwsl_err("Port required (eg, 443)"); + return 1; + } + a->valid = 0; + a->info->mounts = a->head; + + vhost = lws_create_vhost(a->context, a->info); + if (!vhost) { + lwsl_err("Failed to create vhost %s\n", + a->info->vhost_name); + return 1; + } + a->any_vhosts = 1; + + if (a->enable_client_ssl) { + const char *cert_filepath = a->info->client_ssl_cert_filepath; + const char *private_key_filepath = a->info->client_ssl_private_key_filepath; + const char *ca_filepath = a->info->client_ssl_ca_filepath; + const char *cipher_list = a->info->client_ssl_cipher_list; + memset(a->info, 0, sizeof(*a->info)); + a->info->client_ssl_cert_filepath = cert_filepath; + a->info->client_ssl_private_key_filepath = private_key_filepath; + a->info->client_ssl_ca_filepath = ca_filepath; + a->info->client_ssl_cipher_list = cipher_list; + a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + lws_init_vhost_client_ssl(a->info, vhost); + } + + return 0; + } + + if (reason == LEJPCB_OBJECT_END && + ctx->path_match == LEJPVP_MOUNTS + 1) { + static const char * const mount_protocols[] = { + "http://", + "https://", + "file://", + "cgi://", + ">http://", + ">https://", + "callback://", + "gzip://", + }; + + if (!a->fresh_mount) + return 0; + + if (!a->m.mountpoint || !a->m.origin) { + lwsl_err("mountpoint and origin required\n"); + return 1; + } + lwsl_debug("adding mount %s\n", a->m.mountpoint); + m = lwsws_align(a); + memcpy(m, &a->m, sizeof(*m)); + if (a->last) + a->last->mount_next = m; + + for (n = 0; n < ARRAY_SIZE(mount_protocols); n++) + if (!strncmp(a->m.origin, mount_protocols[n], + strlen(mount_protocols[n]))) { + lwsl_info("----%s\n", a->m.origin); + m->origin_protocol = n; + m->origin = a->m.origin + + strlen(mount_protocols[n]); + break; + } + + if (n == ARRAY_SIZE(mount_protocols)) { + lwsl_err("unsupported protocol:// %s\n", a->m.origin); + return 1; + } + + a->p += sizeof(*m); + if (!a->head) + a->head = m; + + a->last = m; + a->fresh_mount = 0; + } + + /* we only match on the prepared path strings */ + if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) + return 0; + + switch (ctx->path_match - 1) { + case LEJPVP_NAME: + a->info->vhost_name = a->p; + break; + case LEJPVP_PORT: + a->info->port = atoi(ctx->buf); + return 0; + case LEJPVP_INTERFACE: + a->info->iface = a->p; + break; + case LEJPVP_UNIXSKT: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_UNIX_SOCK; + else + a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK); + return 0; + case LEJPVP_STS: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_STS; + else + a->info->options &= ~(LWS_SERVER_OPTION_STS); + return 0; + case LEJPVP_HOST_SSL_KEY: + a->info->ssl_private_key_filepath = a->p; + break; + case LEJPVP_HOST_SSL_CERT: + a->info->ssl_cert_filepath = a->p; + break; + case LEJPVP_HOST_SSL_CA: + a->info->ssl_ca_filepath = a->p; + break; + case LEJPVP_ACCESS_LOG: + a->info->log_filepath = a->p; + break; + case LEJPVP_MOUNTPOINT: + a->m.mountpoint = a->p; + a->m.mountpoint_len = (unsigned char)strlen(ctx->buf); + break; + case LEJPVP_ORIGIN: + if (!strncmp(ctx->buf, "callback://", 11)) + a->m.protocol = a->p + 11; + + if (!a->m.origin) + a->m.origin = a->p; + break; + case LEJPVP_DEFAULT: + a->m.def = a->p; + break; + case LEJPVP_DEFAULT_AUTH_MASK: + a->m.auth_mask = atoi(ctx->buf); + return 0; + case LEJPVP_MOUNT_CACHE_MAX_AGE: + a->m.cache_max_age = atoi(ctx->buf); + return 0; + case LEJPVP_MOUNT_CACHE_REUSE: + a->m.cache_reusable = arg_to_bool(ctx->buf); + return 0; + case LEJPVP_MOUNT_CACHE_REVALIDATE: + a->m.cache_revalidate = arg_to_bool(ctx->buf); + return 0; + case LEJPVP_MOUNT_CACHE_INTERMEDIARIES: + a->m.cache_intermediaries = arg_to_bool(ctx->buf);; + return 0; + case LEJPVP_MOUNT_BASIC_AUTH: + a->m.basic_auth_login_file = a->p; + break; + case LEJPVP_CGI_TIMEOUT: + a->m.cgi_timeout = atoi(ctx->buf); + return 0; + case LEJPVP_KEEPALIVE_TIMEOUT: + a->info->keepalive_timeout = atoi(ctx->buf); + return 0; + case LEJPVP_CLIENT_CIPHERS: + a->info->client_ssl_cipher_list = a->p; + break; + case LEJPVP_CIPHERS: + a->info->ssl_cipher_list = a->p; + break; + case LEJPVP_ECDH_CURVE: + a->info->ecdh_curve = a->p; + break; + case LEJPVP_PMO: + case LEJPVP_CGI_ENV: + mp_cgienv = lwsws_align(a); + a->p += sizeof(*a->m.cgienv); + + mp_cgienv->next = a->m.cgienv; + a->m.cgienv = mp_cgienv; + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + mp_cgienv->name = a->p; + a->p += n; + mp_cgienv->value = a->p; + mp_cgienv->options = NULL; + //lwsl_notice(" adding pmo / cgi-env '%s' = '%s'\n", mp_cgienv->name, + // mp_cgienv->value); + goto dostring; + + case LEJPVP_PROTOCOL_NAME_OPT: + /* this catches, eg, + * vhosts[].ws-protocols[].xxx-protocol.yyy-option + * ie, these are options attached to a protocol with { } + */ + pvo = lwsws_align(a); + a->p += sizeof(*a->pvo); + + n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p); + /* ie, enable this protocol, no options yet */ + pvo->next = a->pvo->options; + a->pvo->options = pvo; + pvo->name = a->p; + a->p += n; + pvo->value = a->p; + pvo->options = NULL; + break; + + case LEJPVP_MOUNT_EXTRA_MIMETYPES: + a->pvo_em = lwsws_align(a); + a->p += sizeof(*a->pvo_em); + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + /* ie, enable this protocol, no options yet */ + a->pvo_em->next = a->m.extra_mimetypes; + a->m.extra_mimetypes = a->pvo_em; + a->pvo_em->name = a->p; + lwsl_notice(" adding extra-mimetypes %s -> %s\n", a->p, ctx->buf); + a->p += n; + a->pvo_em->value = a->p; + a->pvo_em->options = NULL; + break; + + case LEJPVP_MOUNT_INTERPRET: + a->pvo_int = lwsws_align(a); + a->p += sizeof(*a->pvo_int); + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + /* ie, enable this protocol, no options yet */ + a->pvo_int->next = a->m.interpret; + a->m.interpret = a->pvo_int; + a->pvo_int->name = a->p; + lwsl_notice(" adding interpret %s -> %s\n", a->p, + ctx->buf); + a->p += n; + a->pvo_int->value = a->p; + a->pvo_int->options = NULL; + break; + + case LEJPVP_ENABLE_CLIENT_SSL: + a->enable_client_ssl = arg_to_bool(ctx->buf); + return 0; + case LEJPVP_CLIENT_SSL_KEY: + a->info->client_ssl_private_key_filepath = a->p; + break; + case LEJPVP_CLIENT_SSL_CERT: + a->info->client_ssl_cert_filepath = a->p; + break; + case LEJPVP_CLIENT_SSL_CA: + a->info->client_ssl_ca_filepath = a->p; + break; + + case LEJPVP_NOIPV6: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_DISABLE_IPV6; + else + a->info->options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6); + return 0; + + case LEJPVP_FLAG_ONLYRAW: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_ONLY_RAW; + else + a->info->options &= ~(LWS_SERVER_OPTION_ONLY_RAW); + return 0; + + case LEJPVP_IPV6ONLY: + a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY; + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE; + else + a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE); + return 0; + + case LEJPVP_SSL_OPTION_SET: + a->info->ssl_options_set |= atol(ctx->buf); + return 0; + case LEJPVP_SSL_OPTION_CLEAR: + a->info->ssl_options_clear |= atol(ctx->buf); + return 0; + + default: + return 0; + } + +dostring: + p = ctx->buf; + p1 = strstr(p, ESC_INSTALL_DATADIR); + if (p1) { + n = p1 - p; + if (n > a->end - a->p) + n = a->end - a->p; + strncpy(a->p, p, n); + a->p += n; + a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR); + p += n + strlen(ESC_INSTALL_DATADIR); + } + + a->p += lws_snprintf(a->p, a->end - a->p, "%s", p); + *(a->p)++ = '\0'; + + return 0; +} + +/* + * returns 0 = OK, 1 = can't open, 2 = parsing error + */ + +static int +lwsws_get_config(void *user, const char *f, const char * const *paths, + int count_paths, lejp_callback cb) +{ + unsigned char buf[128]; + struct lejp_ctx ctx; + int n, m, fd; + + fd = open(f, O_RDONLY); + if (fd < 0) { + lwsl_err("Cannot open %s\n", f); + return 2; + } + lwsl_info("%s: %s\n", __func__, f); + lejp_construct(&ctx, cb, user, paths, count_paths); + + do { + n = read(fd, buf, sizeof(buf)); + if (!n) + break; + + m = (int)(signed char)lejp_parse(&ctx, buf, n); + } while (m == LEJP_CONTINUE); + + close(fd); + n = ctx.line; + lejp_destruct(&ctx); + + if (m < 0) { + lwsl_err("%s(%u): parsing error %d: %s\n", f, n, m, + parser_errs[-m]); + return 2; + } + + return 0; +} + +#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0 + +static int +lwsws_get_config_d(void *user, const char *d, const char * const *paths, + int count_paths, lejp_callback cb) +{ + uv_dirent_t dent; + uv_fs_t req; + char path[256]; + int ret = 0, ir; + uv_loop_t loop; + + ir = uv_loop_init(&loop); + if (ir) { + lwsl_err("%s: loop init failed %d\n", __func__, ir); + } + + if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) { + lwsl_err("Scandir on %s failed\n", d); + return 2; + } + + while (uv_fs_scandir_next(&req, &dent) != UV_EOF) { + lws_snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name); + ret = lwsws_get_config(user, path, paths, count_paths, cb); + if (ret) + goto bail; + } + +bail: + uv_fs_req_cleanup(&req); + while (uv_loop_close(&loop)) + ; + + return ret; +} + +#else + +#ifndef _WIN32 +static int filter(const struct dirent *ent) +{ + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + return 0; + + return 1; +} +#endif + +static int +lwsws_get_config_d(void *user, const char *d, const char * const *paths, + int count_paths, lejp_callback cb) +{ +#ifndef _WIN32 + struct dirent **namelist; + char path[256]; + int n, i, ret = 0; + + n = scandir(d, &namelist, filter, alphasort); + if (n < 0) { + lwsl_err("Scandir on %s failed\n", d); + return 1; + } + + for (i = 0; i < n; i++) { + if (strchr(namelist[i]->d_name, '~')) + goto skip; + lws_snprintf(path, sizeof(path) - 1, "%s/%s", d, + namelist[i]->d_name); + ret = lwsws_get_config(user, path, paths, count_paths, cb); + if (ret) { + while (i++ < n) + free(namelist[i]); + goto bail; + } +skip: + free(namelist[i]); + } + +bail: + free(namelist); + + return ret; +#else + return 0; +#endif +} + +#endif + +int +lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d, + char **cs, int *len) +{ + struct jpargs a; + const char * const *old = info->plugin_dirs; + char dd[128]; + + memset(&a, 0, sizeof(a)); + + a.info = info; + a.p = *cs; + a.end = (a.p + *len) - 1; + a.valid = 0; + + lwsws_align(&a); + info->plugin_dirs = (void *)a.p; + a.plugin_dirs = (void *)a.p; /* writeable version */ + a.p += MAX_PLUGIN_DIRS * sizeof(void *); + + /* copy any default paths */ + + while (old && *old) { + a.plugin_dirs[a.count_plugin_dirs++] = *old; + old++; + } + + lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d); + if (lwsws_get_config(&a, dd, paths_global, + ARRAY_SIZE(paths_global), lejp_globals_cb) > 1) + return 1; + lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d); + if (lwsws_get_config_d(&a, dd, paths_global, + ARRAY_SIZE(paths_global), lejp_globals_cb) > 1) + return 1; + + a.plugin_dirs[a.count_plugin_dirs] = NULL; + + *cs = a.p; + *len = a.end - a.p; + + return 0; +} + +int +lwsws_get_config_vhosts(struct lws_context *context, + struct lws_context_creation_info *info, const char *d, + char **cs, int *len) +{ + struct jpargs a; + char dd[128]; + + memset(&a, 0, sizeof(a)); + + a.info = info; + a.p = *cs; + a.end = a.p + *len; + a.valid = 0; + a.context = context; + a.protocols = info->protocols; + a.extensions = info->extensions; + + lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d); + if (lwsws_get_config(&a, dd, paths_vhosts, + ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1) + return 1; + lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d); + if (lwsws_get_config_d(&a, dd, paths_vhosts, + ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1) + return 1; + + *cs = a.p; + *len = a.end - a.p; + + if (!a.any_vhosts) { + lwsl_err("Need at least one vhost\n"); + return 1; + } + +// lws_finalize_startup(context); + + return 0; +} diff -Nru libwebsockets-4.0.20/lib/server/lws-spa.c libwebsockets-2.4.2/lib/server/lws-spa.c --- libwebsockets-4.0.20/lib/server/lws-spa.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/lws-spa.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,588 @@ +/* + * libwebsockets - Stateful urldecode for POST + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.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, +}; + +static const char * const mp_hdr[] = { + "content-disposition: ", + "content-type: ", + "\x0d\x0a" +}; + +typedef int (*lws_urldecode_stateful_cb)(void *data, + const char *name, char **buf, int len, int final); + +struct lws_urldecode_stateful { + char *out; + void *data; + 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; + + unsigned int multipart_form_data:1; + unsigned int inside_quote:1; + unsigned int subname:1; + unsigned int boundary_real_crlf:1; + + enum urldecode_stateful state; + + lws_urldecode_stateful_cb output; +}; + +static struct lws_urldecode_stateful * +lws_urldecode_s_create(struct lws *wsi, char *out, int out_len, void *data, + lws_urldecode_stateful_cb output) +{ + struct lws_urldecode_stateful *s = lws_zalloc(sizeof(*s), + "stateful urldecode"); + char buf[200], *p; + int m = 0; + + 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 = data; + + 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)) { + 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++] = '-'; + while (m < sizeof(s->mime_boundary) - 1 && + *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, m, hit = 0; + char c, was_end = 0; + + while (len--) { + if (s->pos == s->out_len - s->mp - 1) { + if (s->output(s->data, s->name, &s->out, s->pos, 0)) + return -1; + + was_end = s->pos; + 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, 1)) + return -1; + s->pos = 0; + s->state = US_IDLE; + in++; + continue; + } + if (s->pos >= sizeof(s->name) - 1) { + 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, 1)) + 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++] = 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->pos || was_end) + if (s->output(s->data, s->name, + &s->out, s->pos, 1)) + 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; + + memcpy(s->out + s->pos, s->mime_boundary + n, + 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: + m = 0; + c =*in; + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + for (n = 0; n < ARRAY_SIZE(mp_hdr); n++) + if (c == mp_hdr[n][s->mp]) { + m++; + hit = n; + } + in++; + if (!m) { + s->mp = 0; + continue; + } + + s->mp++; + if (m != 1) + continue; + + if (mp_hdr[hit][s->mp]) + continue; + + s->mp = 0; + s->temp[0] = '\0'; + s->subname = 0; + + if (hit == 2) + s->state = MT_LOOK_BOUND_IN; + else + s->state += hit + 1; + 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 ^= 1; + goto done; + } + + if (s->subname) { + if (*in == '=') { + s->temp[s->mp] = '\0'; + s->subname = 0; + s->mp = 0; + goto done; + } + if (s->mp < sizeof(s->temp) - 1 && + (*in != ' ' || s->inside_quote)) + s->temp[s->mp++] = *in; + goto done; + } + + if (!s->temp[0]) { + if (s->mp < sizeof(s->content_disp) - 1) + s->content_disp[s->mp++] = *in; + s->content_disp[s->mp] = '\0'; + goto done; + } + + if (!strcmp(s->temp, "name")) { + if (s->mp < 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 < 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 < 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; + in++; + break; + + case MT_IGNORE2: + s->mp = 0; + if (*in == '\x0a') + s->state = MT_HNAME; + in++; + break; + } + } + + return 0; +} + +static int +lws_urldecode_s_destroy(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, 1)) + ret = -1; + + lws_free(s); + + return ret; +} + +struct lws_spa { + struct lws_urldecode_stateful *s; + lws_spa_fileupload_cb opt_cb; + const char * const *param_names; + int count_params; + char **params; + int *param_length; + void *opt_data; + + char *storage; + char *end; + int max_storage; + + char finalized; +}; + +static int +lws_urldecode_spa_lookup(struct lws_spa *spa, + const char *name) +{ + int n; + + for (n = 0; n < spa->count_params; n++) + if (!strcmp(spa->param_names[n], name)) + return n; + + return -1; +} + +static int +lws_urldecode_spa_cb(void *data, const char *name, char **buf, int len, + int final) +{ + struct lws_spa *spa = + (struct lws_spa *)data; + int n; + + if (spa->s->content_disp_filename[0]) { + if (spa->opt_cb) { + n = spa->opt_cb(spa->opt_data, name, + spa->s->content_disp_filename, + *buf, len, final); + + if (n < 0) + return -1; + } + return 0; + } + n = lws_urldecode_spa_lookup(spa, name); + + if (n == -1 || !len) /* unrecognized */ + return 0; + + if (!spa->params[n]) + spa->params[n] = *buf; + + if ((*buf) + len >= spa->end) { + lwsl_notice("%s: exceeded storage\n", __func__); + return -1; + } + + spa->param_length[n] += len; + + /* move it on inside storage */ + (*buf) += len; + *((*buf)++) = '\0'; + + spa->s->out_len -= len + 1; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN 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) +{ + struct lws_spa *spa = lws_zalloc(sizeof(*spa), "spa"); + + if (!spa) + return NULL; + + spa->param_names = param_names; + spa->count_params = count_params; + spa->max_storage = max_storage; + spa->opt_cb = opt_cb; + spa->opt_data = opt_data; + + spa->storage = lws_malloc(max_storage, "spa"); + if (!spa->storage) + goto bail2; + spa->end = spa->storage + max_storage - 1; + + spa->params = lws_zalloc(sizeof(char *) * count_params, "spa params"); + if (!spa->params) + goto bail3; + + spa->s = lws_urldecode_s_create(wsi, spa->storage, max_storage, spa, + lws_urldecode_spa_cb); + if (!spa->s) + goto bail4; + + spa->param_length = lws_zalloc(sizeof(int) * count_params, + "spa param len"); + if (!spa->param_length) + goto bail5; + + lwsl_info("%s: Created SPA %p\n", __func__, spa); + + return spa; + +bail5: + lws_urldecode_s_destroy(spa->s); +bail4: + lws_free(spa->params); +bail3: + lws_free(spa->storage); +bail2: + lws_free(spa); + + return NULL; +} + +LWS_VISIBLE LWS_EXTERN int +lws_spa_process(struct lws_spa *ludspa, const char *in, int len) +{ + if (!ludspa) { + lwsl_err("%s: NULL spa\n", __func__); + return -1; + } + /* we reject any junk after the last part arrived and we finalized */ + if (ludspa->finalized) + return 0; + + return lws_urldecode_s_process(ludspa->s, in, len); +} + +LWS_VISIBLE LWS_EXTERN int +lws_spa_get_length(struct lws_spa *ludspa, int n) +{ + if (n >= ludspa->count_params) + return 0; + + return ludspa->param_length[n]; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_spa_get_string(struct lws_spa *ludspa, int n) +{ + if (n >= ludspa->count_params) + return NULL; + + return ludspa->params[n]; +} + +LWS_VISIBLE LWS_EXTERN int +lws_spa_finalize(struct lws_spa *spa) +{ + if (spa->s) { + lws_urldecode_s_destroy(spa->s); + spa->s = NULL; + } + + spa->finalized = 1; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_spa_destroy(struct lws_spa *spa) +{ + int n = 0; + + lwsl_notice("%s: destroy spa %p\n", __func__, spa); + + if (spa->s) + lws_urldecode_s_destroy(spa->s); + + lwsl_debug("%s %p %p %p %p\n", __func__, + spa->param_length, + spa->params, + spa->storage, + spa); + + lws_free(spa->param_length); + lws_free(spa->params); + lws_free(spa->storage); + lws_free(spa); + + return n; +} diff -Nru libwebsockets-4.0.20/lib/server/parsers.c libwebsockets-2.4.2/lib/server/parsers.c --- libwebsockets-4.0.20/lib/server/parsers.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/parsers.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1783 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +const unsigned char lextable[] = { + #include "lextable.h" +}; + +#define FAIL_CHAR 0x08 + +int LWS_WARN_UNUSED_RESULT +lextable_decode(int pos, char c) +{ + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + + while (1) { + if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */ + if ((lextable[pos] & 0x7f) != c) + return -1; + /* fall thru */ + pos++; + if (lextable[pos] == FAIL_CHAR) + return -1; + return pos; + } + + if (lextable[pos] == FAIL_CHAR) + return -1; + + /* b7 = 0, end or 3-byte */ + if (lextable[pos] < FAIL_CHAR) /* terminal marker */ + return pos; + + if (lextable[pos] == c) /* goto */ + return pos + (lextable[pos + 1]) + + (lextable[pos + 2] << 8); + /* fall thru goto */ + pos += 3; + /* continue */ + } +} + +static struct allocated_headers * +_lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size) +{ + struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct"); + + if (!ah) + return NULL; + + ah->data = lws_malloc(data_size, "ah data"); + if (!ah->data) { + lws_free(ah); + + return NULL; + } + ah->next = pt->ah_list; + pt->ah_list = ah; + ah->data_length = data_size; + pt->ah_pool_length++; + + lwsl_info("%s: created ah %p (size %d): pool length %d\n", __func__, + ah, (int)data_size, pt->ah_pool_length); + + return ah; +} + +int +_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah) +{ + lws_start_foreach_llp(struct allocated_headers **, a, pt->ah_list) { + if ((*a) == ah) { + *a = ah->next; + pt->ah_pool_length--; + lwsl_info("%s: freed ah %p : pool length %d\n", + __func__, ah, pt->ah_pool_length); + if (ah->data) + lws_free(ah->data); + lws_free(ah); + + return 0; + } + } lws_end_foreach_llp(a, next); + + return 1; +} + +void +_lws_header_table_reset(struct allocated_headers *ah) +{ + /* init the ah to reflect no headers or data have appeared yet */ + memset(ah->frag_index, 0, sizeof(ah->frag_index)); + memset(ah->frags, 0, sizeof(ah->frags)); + ah->nfrag = 0; + ah->pos = 0; + ah->http_response = 0; +} + +// doesn't scrub the ah rxbuffer by default, parent must do if needed + +void +lws_header_table_reset(struct lws *wsi, int autoservice) +{ + struct allocated_headers *ah = wsi->u.hdr.ah; + struct lws_context_per_thread *pt; + struct lws_pollfd *pfd; + + /* if we have the idea we're resetting 'our' ah, must be bound to one */ + assert(ah); + /* ah also concurs with ownership */ + assert(ah->wsi == wsi); + + _lws_header_table_reset(ah); + + wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; + wsi->u.hdr.lextable_pos = 0; + + /* since we will restart the ah, our new headers are not completed */ + wsi->hdr_parsing_completed = 0; + + /* while we hold the ah, keep a timeout on the wsi */ + lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH, + wsi->vhost->timeout_secs_ah_idle); + + time(&ah->assigned); + + /* + * if we inherited pending rx (from socket adoption deferred + * processing), apply and free it. + */ + if (wsi->u.hdr.preamble_rx) { + memcpy(ah->rx, wsi->u.hdr.preamble_rx, + wsi->u.hdr.preamble_rx_len); + ah->rxlen = wsi->u.hdr.preamble_rx_len; + lws_free_set_NULL(wsi->u.hdr.preamble_rx); + + if (autoservice) { + lwsl_debug("%s: service on readbuf ah\n", __func__); + + pt = &wsi->context->pt[(int)wsi->tsi]; + /* + * Unlike a normal connect, we have the headers already + * (or the first part of them anyway) + */ + pfd = &pt->fds[wsi->position_in_fds_table]; + pfd->revents |= LWS_POLLIN; + lwsl_err("%s: calling service\n", __func__); + lws_service_fd_tsi(wsi->context, pfd, wsi->tsi); + } + } +} + +static void +_lws_header_ensure_we_are_on_waiting_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct lws_pollargs pa; + struct lws **pwsi = &pt->ah_wait_list; + + while (*pwsi) { + if (*pwsi == wsi) + return; + pwsi = &(*pwsi)->u.hdr.ah_wait_list; + } + + lwsl_info("%s: wsi: %p\n", __func__, wsi); + wsi->u.hdr.ah_wait_list = pt->ah_wait_list; + pt->ah_wait_list = wsi; + pt->ah_wait_list_length++; + + /* we cannot accept input then */ + + _lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa); +} + +static int +__lws_remove_from_ah_waiting_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct lws **pwsi =&pt->ah_wait_list; + + while (*pwsi) { + if (*pwsi == wsi) { + lwsl_info("%s: wsi %p\n", __func__, wsi); + /* point prev guy to our next */ + *pwsi = wsi->u.hdr.ah_wait_list; + /* we shouldn't point anywhere now */ + wsi->u.hdr.ah_wait_list = NULL; + pt->ah_wait_list_length--; + + return 1; + } + pwsi = &(*pwsi)->u.hdr.ah_wait_list; + } + + return 0; +} + +int LWS_WARN_UNUSED_RESULT +lws_header_table_attach(struct lws *wsi, int autoservice) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pollargs pa; + int n; + + lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__, + (void *)wsi, (void *)wsi->u.hdr.ah, wsi->tsi, + pt->ah_count_in_use); + + /* if we are already bound to one, just clear it down */ + if (wsi->u.hdr.ah) { + lwsl_info("%s: cleardown\n", __func__); + goto reset; + } + + lws_pt_lock(pt); + + n = pt->ah_count_in_use == context->max_http_header_pool; +#if defined(LWS_WITH_PEER_LIMITS) + if (!n) { + n = lws_peer_confirm_ah_attach_ok(context, wsi->peer); + if (n) + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1); + } +#endif + if (n) { + /* + * Pool is either all busy, or we don't want to give this + * particular guy an ah right now... + * + * Make sure we are on the waiting list, and return that we + * weren't able to provide the ah + */ + _lws_header_ensure_we_are_on_waiting_list(wsi); + + goto bail; + } + + __lws_remove_from_ah_waiting_list(wsi); + + wsi->u.hdr.ah = _lws_create_ah(pt, context->max_http_header_data); + if (!wsi->u.hdr.ah) { /* we could not create an ah */ + _lws_header_ensure_we_are_on_waiting_list(wsi); + + goto bail; + } + + wsi->u.hdr.ah->in_use = 1; + wsi->u.hdr.ah->wsi = wsi; /* mark our owner */ + pt->ah_count_in_use++; + +#if defined(LWS_WITH_PEER_LIMITS) + if (wsi->peer) + wsi->peer->count_ah++; +#endif + + _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa); + + lwsl_info("%s: did attach wsi %p: ah %p: count %d (on exit)\n", __func__, + (void *)wsi, (void *)wsi->u.hdr.ah, pt->ah_count_in_use); + + lws_pt_unlock(pt); + +reset: + + /* and reset the rx state */ + wsi->u.hdr.ah->rxpos = 0; + wsi->u.hdr.ah->rxlen = 0; + + lws_header_table_reset(wsi, autoservice); + +#ifndef LWS_NO_CLIENT + if (wsi->state == LWSS_CLIENT_UNCONNECTED) + if (!lws_client_connect_via_info2(wsi)) + /* our client connect has failed, the wsi + * has been closed + */ + return -1; +#endif + + return 0; + +bail: + lws_pt_unlock(pt); + + return 1; +} + +void +lws_header_table_force_to_detachable_state(struct lws *wsi) +{ + if (wsi->u.hdr.ah) { + wsi->u.hdr.ah->rxpos = -1; + wsi->u.hdr.ah->rxlen = -1; + wsi->hdr_parsing_completed = 1; + } +} + +int +lws_header_table_is_in_detachable_state(struct lws *wsi) +{ + struct allocated_headers *ah = wsi->u.hdr.ah; + + return ah && ah->rxpos == ah->rxlen && wsi->hdr_parsing_completed; +} + +int lws_header_table_detach(struct lws *wsi, int autoservice) +{ + struct lws_context *context = wsi->context; + struct allocated_headers *ah = wsi->u.hdr.ah; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pollargs pa; + struct lws **pwsi, **pwsi_eligible; + time_t now; + + lws_pt_lock(pt); + __lws_remove_from_ah_waiting_list(wsi); + lws_pt_unlock(pt); + + if (!ah) + return 0; + + lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__, + (void *)wsi, (void *)ah, wsi->tsi, + pt->ah_count_in_use); + + if (wsi->u.hdr.preamble_rx) + lws_free_set_NULL(wsi->u.hdr.preamble_rx); + + /* may not be detached while he still has unprocessed rx */ + if (!lws_header_table_is_in_detachable_state(wsi)) { + lwsl_err("%s: %p: CANNOT DETACH rxpos:%d, rxlen:%d, " + "wsi->hdr_parsing_completed = %d\n", __func__, wsi, + ah->rxpos, ah->rxlen, wsi->hdr_parsing_completed); + return 0; + } + + lws_pt_lock(pt); + + /* we did have an ah attached */ + time(&now); + if (ah->assigned && now - ah->assigned > 3) { + /* + * we're detaching the ah, but it was held an + * unreasonably long time + */ + lwsl_debug("%s: wsi %p: ah held %ds, " + "ah.rxpos %d, ah.rxlen %d, mode/state %d %d," + "wsi->more_rx_waiting %d\n", __func__, wsi, + (int)(now - ah->assigned), + ah->rxpos, ah->rxlen, wsi->mode, wsi->state, + wsi->more_rx_waiting); + } + + ah->assigned = 0; + + /* if we think we're detaching one, there should be one in use */ + assert(pt->ah_count_in_use > 0); + /* and this specific one should have been in use */ + assert(ah->in_use); + wsi->u.hdr.ah = NULL; + ah->wsi = NULL; /* no owner */ +#if defined(LWS_WITH_PEER_LIMITS) + lws_peer_track_ah_detach(context, wsi->peer); +#endif + + pwsi = &pt->ah_wait_list; + + /* oh there is nobody on the waiting list... leave the ah unattached */ + if (!*pwsi) + goto nobody_usable_waiting; + + /* + * at least one wsi on the same tsi is waiting, give it to oldest guy + * who is allowed to take it (if any) + */ + lwsl_info("pt wait list %p\n", *pwsi); + wsi = NULL; + pwsi_eligible = NULL; + + while (*pwsi) { +#if defined(LWS_WITH_PEER_LIMITS) + /* are we willing to give this guy an ah? */ + if (!lws_peer_confirm_ah_attach_ok(context, (*pwsi)->peer)) +#endif + { + wsi = *pwsi; + pwsi_eligible = pwsi; + } +#if defined(LWS_WITH_PEER_LIMITS) + else + if (!(*pwsi)->u.hdr.ah_wait_list) + lws_stats_atomic_bump(context, pt, + LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1); +#endif + pwsi = &(*pwsi)->u.hdr.ah_wait_list; + } + + if (!wsi) /* everybody waiting already has too many ah... */ + goto nobody_usable_waiting; + + lwsl_info("%s: last eligible wsi in wait list %p\n", __func__, wsi); + + wsi->u.hdr.ah = ah; + ah->wsi = wsi; /* new owner */ + + /* and reset the rx state */ + ah->rxpos = 0; + ah->rxlen = 0; + lws_header_table_reset(wsi, autoservice); +#if defined(LWS_WITH_PEER_LIMITS) + if (wsi->peer) + wsi->peer->count_ah++; +#endif + + /* clients acquire the ah and then insert themselves in fds table... */ + if (wsi->position_in_fds_table != -1) { + lwsl_info("%s: Enabling %p POLLIN\n", __func__, wsi); + + /* he has been stuck waiting for an ah, but now his wait is + * over, let him progress */ + + _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa); + } + + /* point prev guy to next guy in list instead */ + *pwsi_eligible = wsi->u.hdr.ah_wait_list; + /* the guy who got one is out of the list */ + wsi->u.hdr.ah_wait_list = NULL; + pt->ah_wait_list_length--; + +#ifndef LWS_NO_CLIENT + if (wsi->state == LWSS_CLIENT_UNCONNECTED) { + lws_pt_unlock(pt); + + if (!lws_client_connect_via_info2(wsi)) { + /* our client connect has failed, the wsi + * has been closed + */ + + return -1; + } + return 0; + } +#endif + + assert(!!pt->ah_wait_list_length == !!(lws_intptr_t)pt->ah_wait_list); +bail: + lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__, + (void *)wsi, (void *)ah, pt->tid, pt->ah_count_in_use); + + lws_pt_unlock(pt); + + return 0; + +nobody_usable_waiting: + lwsl_info("%s: nobody usable waiting\n", __func__); + _lws_destroy_ah(pt, ah); + pt->ah_count_in_use--; + + goto bail; +} + +LWS_VISIBLE int +lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx) +{ + int n; + + if (!wsi->u.hdr.ah) + return 0; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return 0; + do { + if (!frag_idx) + return wsi->u.hdr.ah->frags[n].len; + n = wsi->u.hdr.ah->frags[n].nfrag; + } while (frag_idx-- && n); + + return 0; +} + +LWS_VISIBLE int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h) +{ + int n; + int len = 0; + + if (!wsi->u.hdr.ah) + return 0; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return 0; + do { + len += wsi->u.hdr.ah->frags[n].len; + n = wsi->u.hdr.ah->frags[n].nfrag; + } while (n); + + return len; +} + +LWS_VISIBLE int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len, + enum lws_token_indexes h, int frag_idx) +{ + int n = 0; + int f; + + if (!wsi->u.hdr.ah) + return -1; + + f = wsi->u.hdr.ah->frag_index[h]; + + if (!f) + return -1; + + while (n < frag_idx) { + f = wsi->u.hdr.ah->frags[f].nfrag; + if (!f) + return -1; + n++; + } + + if (wsi->u.hdr.ah->frags[f].len >= len) + return -1; + + memcpy(dst, wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[f].offset, + wsi->u.hdr.ah->frags[f].len); + dst[wsi->u.hdr.ah->frags[f].len] = '\0'; + + return wsi->u.hdr.ah->frags[f].len; +} + +LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len, + enum lws_token_indexes h) +{ + int toklen = lws_hdr_total_length(wsi, h); + int n; + + if (toklen >= len) + return -1; + + if (!wsi->u.hdr.ah) + return -1; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return 0; + + do { + if (wsi->u.hdr.ah->frags[n].len >= len) + return -1; + strncpy(dst, &wsi->u.hdr.ah->data[wsi->u.hdr.ah->frags[n].offset], + wsi->u.hdr.ah->frags[n].len); + dst += wsi->u.hdr.ah->frags[n].len; + len -= wsi->u.hdr.ah->frags[n].len; + n = wsi->u.hdr.ah->frags[n].nfrag; + } while (n); + *dst = '\0'; + + return toklen; +} + +char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h) +{ + int n; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return NULL; + + return wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[n].offset; +} + +int LWS_WARN_UNUSED_RESULT +lws_pos_in_bounds(struct lws *wsi) +{ + if (wsi->u.hdr.ah->pos < + (unsigned int)wsi->context->max_http_header_data) + return 0; + + if (wsi->u.hdr.ah->pos == wsi->context->max_http_header_data) { + lwsl_err("Ran out of header data space\n"); + return 1; + } + + /* + * with these tests everywhere, it should never be able to exceed + * the limit, only meet it + */ + lwsl_err("%s: pos %d, limit %d\n", __func__, wsi->u.hdr.ah->pos, + wsi->context->max_http_header_data); + assert(0); + + return 1; +} + +int LWS_WARN_UNUSED_RESULT +lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s) +{ + wsi->u.hdr.ah->nfrag++; + if (wsi->u.hdr.ah->nfrag == ARRAY_SIZE(wsi->u.hdr.ah->frags)) { + lwsl_warn("More hdr frags than we can deal with, dropping\n"); + return -1; + } + + wsi->u.hdr.ah->frag_index[h] = wsi->u.hdr.ah->nfrag; + + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].offset = wsi->u.hdr.ah->pos; + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len = 0; + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].nfrag = 0; + + do { + if (lws_pos_in_bounds(wsi)) + return -1; + + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = *s; + if (*s) + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++; + } while (*s++); + + return 0; +} + +signed char char_to_hex(const char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + return -1; +} + +static int LWS_WARN_UNUSED_RESULT +issue_char(struct lws *wsi, unsigned char c) +{ + unsigned short frag_len; + + if (lws_pos_in_bounds(wsi)) + return -1; + + frag_len = wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len; + /* + * If we haven't hit the token limit, just copy the character into + * the header + */ + if (frag_len < wsi->u.hdr.current_token_limit) { + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = c; + if (c) + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++; + return 0; + } + + /* Insert a null character when we *hit* the limit: */ + if (frag_len == wsi->u.hdr.current_token_limit) { + if (lws_pos_in_bounds(wsi)) + return -1; + + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = '\0'; + lwsl_warn("header %i exceeds limit %d\n", + wsi->u.hdr.parser_state, + wsi->u.hdr.current_token_limit); + } + + return 1; +} + +int +lws_parse_urldecode(struct lws *wsi, uint8_t *_c) +{ + struct allocated_headers *ah = wsi->u.hdr.ah; + unsigned int enc = 0; + uint8_t c = *_c; + + /* + * PRIORITY 1 + * special URI processing... convert %xx + */ + switch (wsi->u.hdr.ues) { + case URIES_IDLE: + if (c == '%') { + wsi->u.hdr.ues = URIES_SEEN_PERCENT; + goto swallow; + } + break; + case URIES_SEEN_PERCENT: + if (char_to_hex(c) < 0) + /* illegal post-% char */ + goto forbid; + + wsi->u.hdr.esc_stash = c; + wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1; + goto swallow; + + case URIES_SEEN_PERCENT_H1: + if (char_to_hex(c) < 0) + /* illegal post-% char */ + goto forbid; + + *_c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) | + char_to_hex(c); + c = *_c; + enc = 1; + wsi->u.hdr.ues = URIES_IDLE; + break; + } + + /* + * PRIORITY 2 + * special URI processing... + * convert /.. or /... or /../ etc to / + * convert /./ to / + * convert // or /// etc to / + * leave /.dir or whatever alone + */ + + switch (wsi->u.hdr.ups) { + case URIPS_IDLE: + if (!c) + return -1; + /* genuine delimiter */ + if ((c == '&' || c == ';') && !enc) { + if (issue_char(wsi, c) < 0) + return -1; + /* swallow the terminator */ + ah->frags[ah->nfrag].len--; + /* link to next fragment */ + ah->frags[ah->nfrag].nfrag = ah->nfrag + 1; + ah->nfrag++; + if (ah->nfrag >= ARRAY_SIZE(ah->frags)) + goto excessive; + /* start next fragment after the & */ + wsi->u.hdr.post_literal_equal = 0; + ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].len = 0; + ah->frags[ah->nfrag].nfrag = 0; + goto swallow; + } + /* uriencoded = in the name part, disallow */ + if (c == '=' && enc && + ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] && + !wsi->u.hdr.post_literal_equal) { + c = '_'; + *_c =c; + } + + /* after the real =, we don't care how many = */ + if (c == '=' && !enc) + wsi->u.hdr.post_literal_equal = 1; + + /* + to space */ + if (c == '+' && !enc) { + c = ' '; + *_c = c; + } + /* issue the first / always */ + if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + break; + case URIPS_SEEN_SLASH: + /* swallow subsequent slashes */ + if (c == '/') + goto swallow; + /* track and swallow the first . after / */ + if (c == '.') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT; + goto swallow; + } + wsi->u.hdr.ups = URIPS_IDLE; + break; + case URIPS_SEEN_SLASH_DOT: + /* swallow second . */ + if (c == '.') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT; + goto swallow; + } + /* change /./ to / */ + if (c == '/') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + goto swallow; + } + /* it was like /.dir ... regurgitate the . */ + wsi->u.hdr.ups = URIPS_IDLE; + if (issue_char(wsi, '.') < 0) + return -1; + break; + + case URIPS_SEEN_SLASH_DOT_DOT: + + /* /../ or /..[End of URI] --> backup to last / */ + if (c == '/' || c == '?') { + /* + * back up one dir level if possible + * safe against header fragmentation because + * the method URI can only be in 1 fragment + */ + if (ah->frags[ah->nfrag].len > 2) { + ah->pos--; + ah->frags[ah->nfrag].len--; + do { + ah->pos--; + ah->frags[ah->nfrag].len--; + } while (ah->frags[ah->nfrag].len > 1 && + ah->data[ah->pos] != '/'); + } + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + if (ah->frags[ah->nfrag].len > 1) + break; + goto swallow; + } + + /* /..[^/] ... regurgitate and allow */ + + if (issue_char(wsi, '.') < 0) + return -1; + if (issue_char(wsi, '.') < 0) + return -1; + wsi->u.hdr.ups = URIPS_IDLE; + break; + } + + if (c == '?' && !enc && + !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */ + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + + /* seal off uri header */ + if (issue_char(wsi, '\0') < 0) + return -1; + + /* move to using WSI_TOKEN_HTTP_URI_ARGS */ + ah->nfrag++; + if (ah->nfrag >= ARRAY_SIZE(ah->frags)) + goto excessive; + ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].len = 0; + ah->frags[ah->nfrag].nfrag = 0; + + wsi->u.hdr.post_literal_equal = 0; + ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag; + wsi->u.hdr.ups = URIPS_IDLE; + goto swallow; + } + + return LPUR_CONTINUE; + +swallow: + return LPUR_SWALLOW; + +forbid: + return LPUR_FORBID; + +excessive: + return LPUR_EXCESSIVE; +} + +static const unsigned char methods[] = { + WSI_TOKEN_GET_URI, + WSI_TOKEN_POST_URI, + WSI_TOKEN_OPTIONS_URI, + WSI_TOKEN_PUT_URI, + WSI_TOKEN_PATCH_URI, + WSI_TOKEN_DELETE_URI, + WSI_TOKEN_CONNECT, + WSI_TOKEN_HEAD_URI, +}; + +int LWS_WARN_UNUSED_RESULT +lws_parse(struct lws *wsi, unsigned char c) +{ + struct allocated_headers *ah = wsi->u.hdr.ah; + struct lws_context *context = wsi->context; + unsigned int n, m; + int r; + + assert(wsi->u.hdr.ah); + + switch (wsi->u.hdr.parser_state) { + default: + + lwsl_parser("WSI_TOK_(%d) '%c'\n", wsi->u.hdr.parser_state, c); + + /* collect into malloc'd buffers */ + /* optional initial space swallow */ + if (!ah->frags[ah->frag_index[wsi->u.hdr.parser_state]].len && + c == ' ') + break; + + for (m = 0; m < ARRAY_SIZE(methods); m++) + if (wsi->u.hdr.parser_state == methods[m]) + break; + if (m == ARRAY_SIZE(methods)) + /* it was not any of the methods */ + goto check_eol; + + /* special URI processing... end at space */ + + if (c == ' ') { + /* enforce starting with / */ + if (!ah->frags[ah->nfrag].len) + if (issue_char(wsi, '/') < 0) + return -1; + + if (wsi->u.hdr.ups == URIPS_SEEN_SLASH_DOT_DOT) { + /* + * back up one dir level if possible + * safe against header fragmentation because + * the method URI can only be in 1 fragment + */ + if (ah->frags[ah->nfrag].len > 2) { + ah->pos--; + ah->frags[ah->nfrag].len--; + do { + ah->pos--; + ah->frags[ah->nfrag].len--; + } while (ah->frags[ah->nfrag].len > 1 && + ah->data[ah->pos] != '/'); + } + } + + /* begin parsing HTTP version: */ + if (issue_char(wsi, '\0') < 0) + return -1; + wsi->u.hdr.parser_state = WSI_TOKEN_HTTP; + goto start_fragment; + } + + r = lws_parse_urldecode(wsi, &c); + switch (r) { + case LPUR_CONTINUE: + break; + case LPUR_SWALLOW: + goto swallow; + case LPUR_FORBID: + goto forbid; + case LPUR_EXCESSIVE: + goto excessive; + default: + return -1; + } +check_eol: + /* bail at EOL */ + if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE && + c == '\x0d') { + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + + c = '\0'; + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR; + lwsl_parser("*\n"); + } + + n = issue_char(wsi, c); + if ((int)n < 0) + return -1; + if (n > 0) + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + +swallow: + /* per-protocol end of headers management */ + + if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE) + goto set_parsing_complete; + break; + + /* collecting and checking a name part */ + case WSI_TOKEN_NAME_PART: + lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X (mode=%d) wsi->u.hdr.lextable_pos=%d\n", c, c, wsi->mode, wsi->u.hdr.lextable_pos); + + wsi->u.hdr.lextable_pos = + lextable_decode(wsi->u.hdr.lextable_pos, c); + /* + * Server needs to look out for unknown methods... + */ + if (wsi->u.hdr.lextable_pos < 0 && + (wsi->mode == LWSCM_HTTP_SERVING)) { + /* this is not a header we know about */ + for (m = 0; m < ARRAY_SIZE(methods); m++) + if (ah->frag_index[methods[m]]) { + /* + * already had the method, no idea what + * this crap from the client is, ignore + */ + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + break; + } + /* + * hm it's an unknown http method from a client in fact, + * it cannot be valid http + */ + if (m == ARRAY_SIZE(methods)) { + /* + * are we set up to accept raw in these cases? + */ + if (lws_check_opt(wsi->vhost->options, + LWS_SERVER_OPTION_FALLBACK_TO_RAW)) + return 2; /* transition to raw */ + + lwsl_info("Unknown method - dropping\n"); + goto forbid; + } + break; + } + /* + * ...otherwise for a client, let him ignore unknown headers + * coming from the server + */ + if (wsi->u.hdr.lextable_pos < 0) { + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + break; + } + + if (lextable[wsi->u.hdr.lextable_pos] < FAIL_CHAR) { + /* terminal state */ + + n = ((unsigned int)lextable[wsi->u.hdr.lextable_pos] << 8) | + lextable[wsi->u.hdr.lextable_pos + 1]; + + lwsl_parser("known hdr %d\n", n); + for (m = 0; m < ARRAY_SIZE(methods); m++) + if (n == methods[m] && + ah->frag_index[methods[m]]) { + lwsl_warn("Duplicated method\n"); + return -1; + } + + /* + * WSORIGIN is protocol equiv to ORIGIN, + * JWebSocket likes to send it, map to ORIGIN + */ + if (n == WSI_TOKEN_SWORIGIN) + n = WSI_TOKEN_ORIGIN; + + wsi->u.hdr.parser_state = (enum lws_token_indexes) + (WSI_TOKEN_GET_URI + n); + + if (context->token_limits) + wsi->u.hdr.current_token_limit = + context->token_limits->token_limit[ + wsi->u.hdr.parser_state]; + else + wsi->u.hdr.current_token_limit = + wsi->context->max_http_header_data; + + if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE) + goto set_parsing_complete; + + goto start_fragment; + } + break; + +start_fragment: + ah->nfrag++; +excessive: + if (ah->nfrag == ARRAY_SIZE(ah->frags)) { + lwsl_warn("More hdr frags than we can deal with\n"); + return -1; + } + + ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].len = 0; + ah->frags[ah->nfrag].nfrag = 0; + ah->frags[ah->nfrag].flags = 2; + + n = ah->frag_index[wsi->u.hdr.parser_state]; + if (!n) { /* first fragment */ + ah->frag_index[wsi->u.hdr.parser_state] = ah->nfrag; + ah->hdr_token_idx = wsi->u.hdr.parser_state; + break; + } + /* continuation */ + while (ah->frags[n].nfrag) + n = ah->frags[n].nfrag; + ah->frags[n].nfrag = ah->nfrag; + + if (issue_char(wsi, ' ') < 0) + return -1; + break; + + /* skipping arg part of a name we didn't recognize */ + case WSI_TOKEN_SKIPPING: + lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c); + + if (c == '\x0d') + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR; + break; + + case WSI_TOKEN_SKIPPING_SAW_CR: + lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c); + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + if (c == '\x0a') { + wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; + wsi->u.hdr.lextable_pos = 0; + } else + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + break; + /* we're done, ignore anything else */ + + case WSI_PARSING_COMPLETE: + lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c); + break; + } + + return 0; + +set_parsing_complete: + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) { + if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION)) + wsi->ietf_spec_revision = + atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION)); + + lwsl_parser("v%02d hdrs completed\n", wsi->ietf_spec_revision); + } + wsi->u.hdr.parser_state = WSI_PARSING_COMPLETE; + wsi->hdr_parsing_completed = 1; + + return 0; + +forbid: + lwsl_notice(" forbidding on uri sanitation\n"); + lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); + + return -1; +} + +LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi) +{ + return wsi->u.ws.frame_is_binary; +} + +void +lws_add_wsi_to_draining_ext_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + if (wsi->u.ws.rx_draining_ext) + return; + + lwsl_ext("%s: RX EXT DRAINING: Adding to list\n", __func__); + + wsi->u.ws.rx_draining_ext = 1; + wsi->u.ws.rx_draining_ext_list = pt->rx_draining_ext_list; + pt->rx_draining_ext_list = wsi; +} + +void +lws_remove_wsi_from_draining_ext_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct lws **w = &pt->rx_draining_ext_list; + + if (!wsi->u.ws.rx_draining_ext) + return; + + lwsl_ext("%s: RX EXT DRAINING: Removing from list\n", __func__); + + wsi->u.ws.rx_draining_ext = 0; + + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + /* if us, point it instead to who we were pointing to */ + *w = wsi->u.ws.rx_draining_ext_list; + break; + } + w = &((*w)->u.ws.rx_draining_ext_list); + } + wsi->u.ws.rx_draining_ext_list = NULL; +} + +/* + * client-parser.c: lws_client_rx_sm() needs to be roughly kept in + * sync with changes here, esp related to ext draining + */ + +int +lws_rx_sm(struct lws *wsi, unsigned char c) +{ + int callback_action = LWS_CALLBACK_RECEIVE; + int ret = 0, n, rx_draining_ext = 0; + struct lws_tokens eff_buf; + + eff_buf.token = NULL; + eff_buf.token_len = 0; + if (wsi->socket_is_permanently_unusable) + return -1; + + switch (wsi->lws_rx_parse_state) { + case LWS_RXPS_NEW: + if (wsi->u.ws.rx_draining_ext) { + eff_buf.token = NULL; + eff_buf.token_len = 0; + lws_remove_wsi_from_draining_ext_list(wsi); + rx_draining_ext = 1; + lwsl_debug("%s: doing draining flow\n", __func__); + + goto drain_extension; + } + switch (wsi->ietf_spec_revision) { + case 13: + /* + * no prepended frame key any more + */ + wsi->u.ws.all_zero_nonce = 1; + goto handle_first; + + default: + lwsl_warn("lws_rx_sm: unknown spec version %d\n", + wsi->ietf_spec_revision); + break; + } + break; + case LWS_RXPS_04_mask_1: + wsi->u.ws.mask[1] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2; + break; + case LWS_RXPS_04_mask_2: + wsi->u.ws.mask[2] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3; + break; + case LWS_RXPS_04_mask_3: + wsi->u.ws.mask[3] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + + /* + * start from the zero'th byte in the XOR key buffer since + * this is the start of a frame with a new key + */ + + wsi->u.ws.mask_idx = 0; + + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1; + break; + + /* + * 04 logical framing from the spec (all this is masked when incoming + * and has to be unmasked) + * + * We ignore the possibility of extension data because we don't + * negotiate any extensions at the moment. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-------+-+-------------+-------------------------------+ + * |F|R|R|R| opcode|R| Payload len | Extended payload length | + * |I|S|S|S| (4) |S| (7) | (16/63) | + * |N|V|V|V| |V| | (if payload len==126/127) | + * | |1|2|3| |4| | | + * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + * | Extended payload length continued, if payload len == 127 | + * + - - - - - - - - - - - - - - - +-------------------------------+ + * | | Extension data | + * +-------------------------------+ - - - - - - - - - - - - - - - + + * : : + * +---------------------------------------------------------------+ + * : Application data : + * +---------------------------------------------------------------+ + * + * We pass payload through to userland as soon as we get it, ignoring + * FIN. It's up to userland to buffer it up if it wants to see a + * whole unfragmented block of the original size (which may be up to + * 2^63 long!) + */ + + case LWS_RXPS_04_FRAME_HDR_1: +handle_first: + + wsi->u.ws.opcode = c & 0xf; + wsi->u.ws.rsv = c & 0x70; + wsi->u.ws.final = !!((c >> 7) & 1); + + switch (wsi->u.ws.opcode) { + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + wsi->u.ws.rsv_first_msg = (c & 0x70); + wsi->u.ws.frame_is_binary = + wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME; + wsi->u.ws.first_fragment = 1; + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + lwsl_info("illegal opcode\n"); + return -1; + } + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN: + + wsi->u.ws.this_frame_masked = !!(c & 0x80); + + switch (c & 0x7f) { + case 126: + /* control frames are not allowed to have big lengths */ + if (wsi->u.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->u.ws.opcode & 8) + goto illegal_ctl_length; + + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8; + break; + default: + wsi->u.ws.rx_packet_length = c & 0x7f; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + break; + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_2: + wsi->u.ws.rx_packet_length = c << 8; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_1: + wsi->u.ws.rx_packet_length |= c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_8: + if (c & 0x80) { + lwsl_warn("b63 of length must be zero\n"); + /* kill the connection */ + return -1; + } +#if defined __LP64__ + wsi->u.ws.rx_packet_length = ((size_t)c) << 56; +#else + wsi->u.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->u.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->u.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->u.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->u.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->u.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->u.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->u.ws.rx_packet_length |= ((size_t)c); + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_1: + wsi->u.ws.mask[0] = c; + if (c) + wsi->u.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->u.ws.mask[1] = c; + if (c) + wsi->u.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->u.ws.mask[2] = c; + if (c) + wsi->u.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->u.ws.mask[3] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + wsi->u.ws.mask_idx = 0; + if (wsi->u.ws.rx_packet_length == 0) { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + break; + + + case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED: + assert(wsi->u.ws.rx_ubuf); + + if (wsi->u.ws.rx_draining_ext) + goto drain_extension; + + if (wsi->u.ws.rx_ubuf_head + LWS_PRE >= + wsi->u.ws.rx_ubuf_alloc) { + lwsl_err("Attempted overflow \n"); + return -1; + } + if (wsi->u.ws.all_zero_nonce) + wsi->u.ws.rx_ubuf[LWS_PRE + + (wsi->u.ws.rx_ubuf_head++)] = c; + else + wsi->u.ws.rx_ubuf[LWS_PRE + + (wsi->u.ws.rx_ubuf_head++)] = + c ^ wsi->u.ws.mask[ + (wsi->u.ws.mask_idx++) & 3]; + + if (--wsi->u.ws.rx_packet_length == 0) { + /* spill because we have the whole frame */ + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + + /* + * if there's no protocol max frame size given, we are + * supposed to default to context->pt_serv_buf_size + */ + if (!wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size) + break; + + if (wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size) + break; + + /* spill because we filled our rx buffer */ +spill: + /* + * 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 + */ + + lwsl_parser("spill on %s\n", wsi->protocol->name); + + switch (wsi->u.ws.opcode) { + case LWSWSOPC_CLOSE: + + /* is this an acknowledgement of our close? */ + if (wsi->state == LWSS_AWAITING_CLOSE_ACK) { + /* + * fine he has told us he is closing too, let's + * finish our close + */ + lwsl_parser("seen client close ack\n"); + return -1; + } + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY) + /* if he sends us 2 CLOSE, kill him */ + return -1; + + if (lws_partial_buffered(wsi)) { + /* + * if we're in the middle of something, + * we can't do a normal close response and + * have to just close our end. + */ + wsi->socket_is_permanently_unusable = 1; + lwsl_parser("Closing on peer close due to Pending tx\n"); + return -1; + } + + if (user_callback_handle_rxflow( + wsi->protocol->callback, wsi, + LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, + wsi->user_space, + &wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head)) + return -1; + + lwsl_parser("server sees client close packet\n"); + wsi->state = LWSS_RETURNED_CLOSE_ALREADY; + /* deal with the close packet contents as a PONG */ + wsi->u.ws.payload_is_close = 1; + goto process_as_ping; + + case LWSWSOPC_PING: + lwsl_info("received %d byte ping, sending pong\n", + wsi->u.ws.rx_ubuf_head); + + if (wsi->u.ws.ping_pending_flag) { + /* + * there is already a pending ping payload + * we should just log and drop + */ + lwsl_parser("DROP PING since one pending\n"); + goto ping_drop; + } +process_as_ping: + /* control packets can only be < 128 bytes long */ + if (wsi->u.ws.rx_ubuf_head > 128 - 3) { + lwsl_parser("DROP PING payload too large\n"); + goto ping_drop; + } + + /* stash the pong payload */ + memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE, + &wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head; + wsi->u.ws.ping_pending_flag = 1; + + /* get it sent as soon as possible */ + lws_callback_on_writable(wsi); +ping_drop: + wsi->u.ws.rx_ubuf_head = 0; + return 0; + + case LWSWSOPC_PONG: + lwsl_info("received pong\n"); + lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + if (wsi->pending_timeout == PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) { + lwsl_info("received expected PONG on wsi %p\n", wsi); + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + } + + /* issue it */ + callback_action = LWS_CALLBACK_RECEIVE_PONG; + break; + + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + case LWSWSOPC_CONTINUATION: + break; + + default: + lwsl_parser("passing opc %x up to exts\n", + wsi->u.ws.opcode); + /* + * It's something special we can't understand here. + * Pass the payload up to the extension's parsing + * state machine. + */ + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_EXTENDED_PAYLOAD_RX, + &eff_buf, 0) <= 0) + /* not handle or fail */ + lwsl_ext("ext opc opcode 0x%x unknown\n", + wsi->u.ws.opcode); + + wsi->u.ws.rx_ubuf_head = 0; + return 0; + } + + /* + * No it's real payload, pass it up to the user callback. + * It's nicely buffered with the pre-padding taken care of + * so it can be sent straight out again using lws_write + */ + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + + if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len) + goto already_done; + +drain_extension: + lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len); + + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_AWAITING_CLOSE_ACK) + goto already_done; + + n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0); + /* + * eff_buf may be pointing somewhere completely different now, + * it's the output + */ + wsi->u.ws.first_fragment = 0; + if (n < 0) { + /* + * we may rely on this to get RX, just drop connection + */ + wsi->socket_is_permanently_unusable = 1; + return -1; + } + + if (rx_draining_ext && eff_buf.token_len == 0) + goto already_done; + + if (n && eff_buf.token_len) + /* extension had more... main loop will come back */ + lws_add_wsi_to_draining_ext_list(wsi); + else + lws_remove_wsi_from_draining_ext_list(wsi); + + if (eff_buf.token_len > 0 || + callback_action == LWS_CALLBACK_RECEIVE_PONG) { + eff_buf.token[eff_buf.token_len] = '\0'; + + if (wsi->protocol->callback) { + + if (callback_action == LWS_CALLBACK_RECEIVE_PONG) + lwsl_info("Doing pong callback\n"); + + ret = user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, + (enum lws_callback_reasons)callback_action, + wsi->user_space, + eff_buf.token, + eff_buf.token_len); + } + else + lwsl_err("No callback on payload spill!\n"); + } + +already_done: + wsi->u.ws.rx_ubuf_head = 0; + break; + } + + return ret; + +illegal_ctl_length: + + lwsl_warn("Control frame with xtended length is illegal\n"); + /* kill the connection */ + return -1; +} + +LWS_VISIBLE size_t +lws_remaining_packet_payload(struct lws *wsi) +{ + return wsi->u.ws.rx_packet_length; +} + +/* Once we reach LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED, we know how much + * to expect in that state and can deal with it in bulk more efficiently. + */ + +int +lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf, + size_t *len) +{ + unsigned char *buffer = *buf, mask[4]; + int buffer_size, n; + unsigned int avail; + char *rx_ubuf; + + if (wsi->protocol->rx_buffer_size) + buffer_size = wsi->protocol->rx_buffer_size; + else + buffer_size = wsi->context->pt_serv_buf_size; + avail = buffer_size - wsi->u.ws.rx_ubuf_head; + + /* do not consume more than we should */ + if (avail > wsi->u.ws.rx_packet_length) + avail = wsi->u.ws.rx_packet_length; + + /* do not consume more than what is in the buffer */ + if (avail > *len) + avail = *len; + + /* we want to leave 1 byte for the parser to handle properly */ + if (avail <= 1) + return 0; + + avail--; + rx_ubuf = wsi->u.ws.rx_ubuf + LWS_PRE + wsi->u.ws.rx_ubuf_head; + if (wsi->u.ws.all_zero_nonce) + memcpy(rx_ubuf, buffer, avail); + else { + + for (n = 0; n < 4; n++) + mask[n] = wsi->u.ws.mask[(wsi->u.ws.mask_idx + n) & 3]; + + /* deal with 4-byte chunks using unwrapped loop */ + n = avail >> 2; + while (n--) { + *(rx_ubuf++) = *(buffer++) ^ mask[0]; + *(rx_ubuf++) = *(buffer++) ^ mask[1]; + *(rx_ubuf++) = *(buffer++) ^ mask[2]; + *(rx_ubuf++) = *(buffer++) ^ mask[3]; + } + /* and the remaining bytes bytewise */ + for (n = 0; n < (int)(avail & 3); n++) + *(rx_ubuf++) = *(buffer++) ^ mask[n]; + + wsi->u.ws.mask_idx = (wsi->u.ws.mask_idx + avail) & 3; + } + + (*buf) += avail; + wsi->u.ws.rx_ubuf_head += avail; + wsi->u.ws.rx_packet_length -= avail; + *len -= avail; + + return avail; +} diff -Nru libwebsockets-4.0.20/lib/server/peer-limits.c libwebsockets-2.4.2/lib/server/peer-limits.c --- libwebsockets-4.0.20/lib/server/peer-limits.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/peer-limits.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,252 @@ +/* + * libwebsockets - peer limits tracking + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +/* requires context->lock */ +static void +__lws_peer_remove_from_peer_wait_list(struct lws_context *context, + struct lws_peer *peer) +{ + struct lws_peer *df; + + lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) { + if (*p == peer) { + df = *p; + + *p = df->peer_wait_list; + df->peer_wait_list = NULL; + + return; + } + } lws_end_foreach_llp(p, peer_wait_list); +} + +/* requires context->lock */ +static void +__lws_peer_add_to_peer_wait_list(struct lws_context *context, + struct lws_peer *peer) +{ + __lws_peer_remove_from_peer_wait_list(context, peer); + + peer->peer_wait_list = context->peer_wait_list; + context->peer_wait_list = peer; +} + + +struct lws_peer * +lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd) +{ + struct lws_context *context = vhost->context; + socklen_t rlen = 0; + void *q; + uint8_t *q8; + struct lws_peer *peer; + uint32_t hash = 0; + int n, af = AF_INET; + struct sockaddr_storage addr; + +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(vhost)) { + af = AF_INET6; + } +#endif + rlen = sizeof(addr); + if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen)) + return NULL; + + if (af == AF_INET) { + struct sockaddr_in *s = (struct sockaddr_in *)&addr; + q = &s->sin_addr; + rlen = sizeof(s->sin_addr); + } else +#ifdef LWS_WITH_IPV6 + { + struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; + q = &s->sin6_addr; + rlen = sizeof(s->sin6_addr); + } +#else + return NULL; +#endif + + q8 = q; + for (n = 0; n < rlen; n++) + hash = (((hash << 4) | (hash >> 28)) * n) ^ q8[n]; + + hash = hash % context->pl_hash_elements; + + lws_context_lock(context); /* <====================================== */ + + lws_start_foreach_ll(struct lws_peer *, peerx, + context->pl_hash_table[hash]) { + if (peerx->af == af && !memcmp(q, peerx->addr, rlen)) { + lws_context_unlock(context); /* === */ + return peerx; + } + } lws_end_foreach_ll(peerx, next); + + lwsl_info("%s: creating new peer\n", __func__); + + peer = lws_zalloc(sizeof(*peer), "peer"); + if (!peer) { + lws_context_unlock(context); /* === */ + return NULL; + } + + context->count_peers++; + peer->next = context->pl_hash_table[hash]; + peer->hash = hash; + peer->af = af; + context->pl_hash_table[hash] = peer; + memcpy(peer->addr, q, rlen); + time(&peer->time_created); + /* + * On creation, the peer has no wsi attached, so is created on the + * wait list. When a wsi is added it is removed from the wait list. + */ + time(&peer->time_closed_all); + __lws_peer_add_to_peer_wait_list(context, peer); + + lws_context_unlock(context); /* ====================================> */ + + return peer; +} + +/* requires context->lock */ +static int +__lws_peer_destroy(struct lws_context *context, struct lws_peer *peer) +{ + lws_start_foreach_llp(struct lws_peer **, p, + context->pl_hash_table[peer->hash]) { + if (*p == peer) { + struct lws_peer *df = *p; + *p = df->next; + lws_free(df); + context->count_peers--; + + return 0; + } + } lws_end_foreach_llp(p, next); + + return 1; +} + +void +lws_peer_cull_peer_wait_list(struct lws_context *context) +{ + struct lws_peer *df; + time_t t; + + time(&t); + + if (context->next_cull && t < context->next_cull) + return; + + lws_context_lock(context); /* <====================================== */ + + context->next_cull = t + 5; + + lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) { + if (t - (*p)->time_closed_all > 10) { + df = *p; + + /* remove us from the peer wait list */ + *p = df->peer_wait_list; + df->peer_wait_list = NULL; + + __lws_peer_destroy(context, df); + continue; /* we already point to next, if any */ + } + } lws_end_foreach_llp(p, peer_wait_list); + + lws_context_unlock(context); /* ====================================> */ +} + +void +lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer, + struct lws *wsi) +{ + if (!peer) + return; + + lws_context_lock(context); /* <====================================== */ + + peer->count_wsi++; + wsi->peer = peer; + __lws_peer_remove_from_peer_wait_list(context, peer); + + lws_context_unlock(context); /* ====================================> */ +} + +void +lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer) +{ + if (!peer) + return; + + lws_context_lock(context); /* <====================================== */ + + assert(peer->count_wsi); + peer->count_wsi--; + + if (!peer->count_wsi && !peer->count_ah) { + /* + * in order that we can accumulate peer activity correctly + * allowing for periods when the peer has no connections, + * we don't synchronously destroy the peer when his last + * wsi closes. Instead we mark the time his last wsi + * closed and add him to a peer_wait_list to be reaped + * later if no further activity is coming. + */ + time(&peer->time_closed_all); + __lws_peer_add_to_peer_wait_list(context, peer); + } + + lws_context_unlock(context); /* ====================================> */ +} + +int +lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer) +{ + if (!peer) + return 0; + + if (context->ip_limit_ah && peer->count_ah >= context->ip_limit_ah) { + lwsl_info("peer reached ah limit %d, deferring\n", + context->ip_limit_ah); + + return 1; + } + + return 0; +} + +void +lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer) +{ + if (!peer) + return; + + assert(peer->count_ah); + peer->count_ah--; +} + diff -Nru libwebsockets-4.0.20/lib/server/ranges.c libwebsockets-2.4.2/lib/server/ranges.c --- libwebsockets-4.0.20/lib/server/ranges.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/ranges.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,214 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * RFC7233 ranges parser + * + * Copyright (C) 2016 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +/* + * RFC7233 examples + * + * o The first 500 bytes (byte offsets 0-499, inclusive): + * + * bytes=0-499 + * + * o The second 500 bytes (byte offsets 500-999, inclusive): + * + * bytes=500-999 + * + * o The final 500 bytes (byte offsets 9500-9999, inclusive): + * + * bytes=-500 + * + * Or: + * + * bytes=9500- + * + * o The first and last bytes only (bytes 0 and 9999): + * + * bytes=0-0,-1 + * + * o Other valid (but not canonical) specifications of the second 500 + * bytes (byte offsets 500-999, inclusive): + * + * bytes=500-600,601-999 + * bytes=500-700,601-999 + */ + +/* + * returns 1 if the range struct represents a usable range + * if no ranges header, you get one of these for the whole + * file. Otherwise you get one for each valid range in the + * header. + * + * returns 0 if no further valid range forthcoming; rp->state + * may be LWSRS_SYNTAX or LWSRS_COMPLETED + */ + +int +lws_ranges_next(struct lws_range_parsing *rp) +{ + static const char * const beq = "bytes="; + char c; + + while (1) { + + c = rp->buf[rp->pos]; + + switch (rp->state) { + case LWSRS_SYNTAX: + case LWSRS_COMPLETED: + return 0; + + case LWSRS_NO_ACTIVE_RANGE: + rp->state = LWSRS_COMPLETED; + return 0; + + case LWSRS_BYTES_EQ: // looking for "bytes=" + if (c != beq[rp->pos]) { + rp->state = LWSRS_SYNTAX; + return -1; + } + if (rp->pos == 5) + rp->state = LWSRS_FIRST; + break; + + case LWSRS_FIRST: + rp->start = 0; + rp->end = 0; + rp->start_valid = 0; + rp->end_valid = 0; + + rp->state = LWSRS_STARTING; + + // fallthru + + case LWSRS_STARTING: + if (c == '-') { + rp->state = LWSRS_ENDING; + break; + } + + if (!(c >= '0' && c <= '9')) { + rp->state = LWSRS_SYNTAX; + return 0; + } + rp->start = (rp->start * 10) + (c - '0'); + rp->start_valid = 1; + break; + + case LWSRS_ENDING: + if (c == ',' || c == '\0') { + rp->state = LWSRS_FIRST; + if (c == ',') + rp->pos++; + + /* + * By the end of this, start and end are + * always valid if the range still is + */ + + if (!rp->start_valid) { /* eg, -500 */ + if (rp->end > rp->extent) + rp->end = rp->extent; + + rp->start = rp->extent - rp->end; + rp->end = rp->extent - 1; + } else + if (!rp->end_valid) + rp->end = rp->extent - 1; + + rp->did_try = 1; + + /* end must be >= start or ignore it */ + if (rp->end < rp->start) { + if (c == ',') + break; + rp->state = LWSRS_COMPLETED; + return 0; + } + + return 1; /* issue range */ + } + + if (!(c >= '0' && c <= '9')) { + rp->state = LWSRS_SYNTAX; + return 0; + } + rp->end = (rp->end * 10) + (c - '0'); + rp->end_valid = 1; + break; + } + + rp->pos++; + } +} + +void +lws_ranges_reset(struct lws_range_parsing *rp) +{ + rp->pos = 0; + rp->ctr = 0; + rp->start = 0; + rp->end = 0; + rp->start_valid = 0; + rp->end_valid = 0; + rp->state = LWSRS_BYTES_EQ; +} + +/* + * returns count of valid ranges + */ +int +lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp, + unsigned long long extent) +{ + rp->agg = 0; + rp->send_ctr = 0; + rp->inside = 0; + rp->count_ranges = 0; + rp->did_try = 0; + lws_ranges_reset(rp); + rp->state = LWSRS_COMPLETED; + + rp->extent = extent; + + if (lws_hdr_copy(wsi, (char *)rp->buf, sizeof(rp->buf), + WSI_TOKEN_HTTP_RANGE) <= 0) + return 0; + + rp->state = LWSRS_BYTES_EQ; + + while (lws_ranges_next(rp)) { + rp->count_ranges++; + rp->agg += rp->end - rp->start + 1; + } + + lwsl_debug("%s: count %d\n", __func__, rp->count_ranges); + lws_ranges_reset(rp); + + if (rp->did_try && !rp->count_ranges) + return -1; /* "not satisfiable */ + + lws_ranges_next(rp); + + return rp->count_ranges; +} diff -Nru libwebsockets-4.0.20/lib/server/rewrite.c libwebsockets-2.4.2/lib/server/rewrite.c --- libwebsockets-4.0.20/lib/server/rewrite.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/rewrite.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,52 @@ +#include "private-libwebsockets.h" + + +LWS_EXTERN 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; +} + +LWS_EXTERN int +lws_rewrite_parse(struct lws_rewrite *r, + const unsigned char *in, int in_len) +{ + if (hubbub_parser_parse_chunk(r->parser, in, in_len) != HUBBUB_OK) + return -1; + + return 0; +} + +LWS_EXTERN void +lws_rewrite_destroy(struct lws_rewrite *r) +{ + hubbub_parser_destroy(r->parser); + lws_free(r); +} + diff -Nru libwebsockets-4.0.20/lib/server/server.c libwebsockets-2.4.2/lib/server/server.c --- libwebsockets-4.0.20/lib/server/server.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/server.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,3025 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +const char * const method_names[] = { + "GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT", "HEAD", +#ifdef LWS_WITH_HTTP2 + ":path", +#endif + }; + +#if defined (LWS_WITH_ESP8266) +#undef memcpy +void *memcpy(void *dest, const void *src, size_t n) +{ + return ets_memcpy(dest, src, n); +} +#endif + +int +lws_context_init_server(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ +#if LWS_POSIX + int n, opt = 1, limit = 1; +#endif + lws_sockfd_type sockfd; + struct lws_vhost *vh; + struct lws *wsi; + int m = 0; + + (void)method_names; + (void)opt; + /* set up our external listening socket we serve on */ + + if (info->port == CONTEXT_PORT_NO_LISTEN || + info->port == CONTEXT_PORT_NO_LISTEN_SERVER) + return 0; + + vh = vhost->context->vhost_list; + while (vh) { + if (vh->listen_port == info->port) { + if ((!info->iface && !vh->iface) || + (info->iface && vh->iface && + !strcmp(info->iface, vh->iface))) { + vhost->listen_port = info->port; + vhost->iface = info->iface; + lwsl_notice(" using listen skt from vhost %s\n", + vh->name); + return 0; + } + } + vh = vh->vhost_next; + } + +#if LWS_POSIX + (void)n; +#if defined(__linux__) + limit = vhost->context->count_threads; +#endif + + for (m = 0; m < limit; m++) { +#ifdef LWS_WITH_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vhost)) + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + else +#endif +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(vhost)) + sockfd = socket(AF_INET6, SOCK_STREAM, 0); + else +#endif + sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd == -1) { +#else +#if defined(LWS_WITH_ESP8266) + sockfd = esp8266_create_tcp_listen_socket(vhost); + if (!lws_sockfd_valid(sockfd)) { +#endif +#endif + lwsl_err("ERROR opening socket\n"); + return 1; + } +#if LWS_POSIX && !defined(LWS_WITH_ESP32) + +#if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE) + /* + * only accept that we are the only listener on the port + * https://msdn.microsoft.com/zh-tw/library/ + * windows/desktop/ms740621(v=vs.85).aspx + * + * for lws, to match Linux, we default to exclusive listen + */ + if (!lws_check_opt(vhost->options, + LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) { + if (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + (const void *)&opt, sizeof(opt)) < 0) { + lwsl_err("reuseaddr failed\n"); + compatible_close(sockfd); + return 1; + } + } else +#endif + + /* + * allow us to restart even if old sockets in TIME_WAIT + */ + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + (const void *)&opt, sizeof(opt)) < 0) { + lwsl_err("reuseaddr failed\n"); + compatible_close(sockfd); + return 1; + } + +#if defined(LWS_WITH_IPV6) && defined(IPV6_V6ONLY) + if (LWS_IPV6_ENABLED(vhost)) { + if (vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) { + int value = (vhost->options & + LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE) ? 1 : 0; + if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, + (const void*)&value, sizeof(value)) < 0) { + compatible_close(sockfd); + return 1; + } + } + } +#endif + +#if defined(__linux__) && defined(SO_REUSEPORT) + n = lws_check_opt(vhost->options, LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE); +#if LWS_MAX_SMP > 1 + n = 1; +#endif + + if (n) + if (vhost->context->count_threads > 1) + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, + (const void *)&opt, sizeof(opt)) < 0) { + compatible_close(sockfd); + return 1; + } +#endif +#endif + lws_plat_set_socket_options(vhost, sockfd); + +#if LWS_POSIX + n = lws_socket_bind(vhost, sockfd, info->port, info->iface); + if (n < 0) + goto bail; + info->port = n; +#endif + vhost->listen_port = info->port; + vhost->iface = info->iface; + + wsi = lws_zalloc(sizeof(struct lws), "listen wsi"); + if (wsi == NULL) { + lwsl_err("Out of mem\n"); + goto bail; + } + wsi->context = vhost->context; + wsi->desc.sockfd = sockfd; + wsi->mode = LWSCM_SERVER_LISTENER; + wsi->protocol = vhost->protocols; + wsi->tsi = m; + wsi->vhost = vhost; + wsi->listener = 1; + +#ifdef LWS_WITH_LIBUV + if (LWS_LIBUV_ENABLED(vhost->context)) + lws_uv_initvhost(vhost, wsi); +#endif + + if (insert_wsi_socket_into_fds(vhost->context, wsi)) + goto bail; + + vhost->context->count_wsi_allocated++; + vhost->lserv_wsi = wsi; + +#if LWS_POSIX + n = listen(wsi->desc.sockfd, LWS_SOMAXCONN); + if (n < 0) { + lwsl_err("listen failed with error %d\n", LWS_ERRNO); + vhost->lserv_wsi = NULL; + vhost->context->count_wsi_allocated--; + remove_wsi_socket_from_fds(wsi); + goto bail; + } + } /* for each thread able to independently listen */ +#else +#if defined(LWS_WITH_ESP8266) + esp8266_tcp_stream_bind(wsi->desc.sockfd, info->port, wsi); +#endif +#endif + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) { +#ifdef LWS_WITH_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vhost)) + lwsl_info(" Listening on \"%s\"\n", info->iface); + else +#endif + lwsl_info(" Listening on port %d\n", info->port); + } + + return 0; + +bail: + compatible_close(sockfd); + + return 1; +} + +#if defined(LWS_WITH_ESP8266) +#undef strchr +#define strchr ets_strchr +#endif + +struct lws_vhost * +lws_select_vhost(struct lws_context *context, int port, const char *servername) +{ + struct lws_vhost *vhost = context->vhost_list; + const char *p; + int n, m, colon; + + n = strlen(servername); + colon = n; + p = strchr(servername, ':'); + if (p) + colon = p - servername; + + /* Priotity 1: first try exact matches */ + + while (vhost) { + if (port == vhost->listen_port && + !strncmp(vhost->name, servername, colon)) { + lwsl_info("SNI: Found: %s\n", servername); + return vhost; + } + vhost = vhost->vhost_next; + } + + /* + * Priority 2: if no exact matches, try matching *.vhost-name + * unintentional matches are possible but resolve to x.com for *.x.com + * which is reasonable. If exact match exists we already chose it and + * never reach here. SSL will still fail it if the cert doesn't allow + * *.x.com. + */ + vhost = context->vhost_list; + while (vhost) { + m = strlen(vhost->name); + if (port == vhost->listen_port && + m <= (colon - 2) && + servername[colon - m - 1] == '.' && + !strncmp(vhost->name, servername + colon - m, m)) { + lwsl_info("SNI: Found %s on wildcard: %s\n", + servername, vhost->name); + return vhost; + } + vhost = vhost->vhost_next; + } + + /* Priority 3: match the first vhost on our port */ + + vhost = context->vhost_list; + while (vhost) { + if (port == vhost->listen_port) { + lwsl_info("vhost match to %s based on port %d\n", + vhost->name, port); + return vhost; + } + vhost = vhost->vhost_next; + } + + /* no match */ + + return NULL; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_get_mimetype(const char *file, const struct lws_http_mount *m) +{ + int n = strlen(file); + const struct lws_protocol_vhost_options *pvo = NULL; + + if (m) + pvo = m->extra_mimetypes; + + if (n < 5) + return NULL; + + if (!strcmp(&file[n - 4], ".ico")) + return "image/x-icon"; + + if (!strcmp(&file[n - 4], ".gif")) + return "image/gif"; + + if (!strcmp(&file[n - 3], ".js")) + return "text/javascript"; + + if (!strcmp(&file[n - 4], ".png")) + return "image/png"; + + if (!strcmp(&file[n - 4], ".jpg")) + return "image/jpeg"; + + if (!strcmp(&file[n - 3], ".gz")) + return "application/gzip"; + + if (!strcmp(&file[n - 4], ".JPG")) + return "image/jpeg"; + + if (!strcmp(&file[n - 5], ".html")) + return "text/html"; + + if (!strcmp(&file[n - 4], ".css")) + return "text/css"; + + if (!strcmp(&file[n - 4], ".txt")) + return "text/plain"; + + if (!strcmp(&file[n - 4], ".svg")) + return "image/svg+xml"; + + if (!strcmp(&file[n - 4], ".ttf")) + return "application/x-font-ttf"; + + if (!strcmp(&file[n - 4], ".otf")) + return "application/font-woff"; + + if (!strcmp(&file[n - 5], ".woff")) + return "application/font-woff"; + + if (!strcmp(&file[n - 4], ".xml")) + return "application/xml"; + + while (pvo) { + if (pvo->name[0] == '*') /* ie, match anything */ + return pvo->value; + + if (!strcmp(&file[n - strlen(pvo->name)], pvo->name)) + return pvo->value; + + pvo = pvo->next; + } + + return NULL; +} +static lws_fop_flags_t +lws_vfs_prepare_flags(struct lws *wsi) +{ + lws_fop_flags_t f = 0; + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING)) + return f; + + if (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING), + "gzip")) { + lwsl_info("client indicates GZIP is acceptable\n"); + f |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP; + } + + return f; +} + +static int +lws_http_serve(struct lws *wsi, char *uri, const char *origin, + const struct lws_http_mount *m) +{ + const struct lws_protocol_vhost_options *pvo = m->interpret; + struct lws_process_html_args args; + const char *mimetype; +#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) + const struct lws_plat_file_ops *fops; + const char *vpath; + lws_fop_flags_t fflags = LWS_O_RDONLY; +#if defined(WIN32) && defined(LWS_HAVE__STAT32I64) + struct _stat32i64 st; +#else + struct stat st; +#endif + int spin = 0; +#endif + char path[256], sym[512]; + unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p; + unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE; +#if !defined(WIN32) && LWS_POSIX && !defined(LWS_WITH_ESP32) + size_t len; +#endif + int n; + + lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri); + +#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) + + fflags |= lws_vfs_prepare_flags(wsi); + + do { + spin++; + fops = lws_vfs_select_fops(wsi->context->fops, path, &vpath); + + if (wsi->u.http.fop_fd) + lws_vfs_file_close(&wsi->u.http.fop_fd); + + wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops, + path, vpath, &fflags); + if (!wsi->u.http.fop_fd) { + lwsl_err("Unable to open '%s'\n", path); + + return -1; + } + + /* if it can't be statted, don't try */ + if (fflags & LWS_FOP_FLAG_VIRTUAL) + break; +#if defined(LWS_WITH_ESP32) + break; +#endif +#if !defined(WIN32) + if (fstat(wsi->u.http.fop_fd->fd, &st)) { + lwsl_info("unable to stat %s\n", path); + goto bail; + } +#else +#if defined(LWS_HAVE__STAT32I64) + if (_stat32i64(path, &st)) { + lwsl_info("unable to stat %s\n", path); + goto bail; + } +#else + if (stat(path, &st)) { + lwsl_info("unable to stat %s\n", path); + goto bail; + } +#endif +#endif + + wsi->u.http.fop_fd->mod_time = (uint32_t)st.st_mtime; + fflags |= LWS_FOP_FLAG_MOD_TIME_VALID; + +#if !defined(WIN32) && LWS_POSIX && !defined(LWS_WITH_ESP32) + if ((S_IFMT & st.st_mode) == S_IFLNK) { + len = readlink(path, sym, sizeof(sym) - 1); + if (len) { + lwsl_err("Failed to read link %s\n", path); + goto bail; + } + sym[len] = '\0'; + lwsl_debug("symlink %s -> %s\n", path, sym); + lws_snprintf(path, sizeof(path) - 1, "%s", sym); + } +#endif + if ((S_IFMT & st.st_mode) == S_IFDIR) { + lwsl_debug("default filename append to dir\n"); + lws_snprintf(path, sizeof(path) - 1, "%s/%s/index.html", + origin, uri); + } + + } while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5); + + if (spin == 5) + lwsl_err("symlink loop %s \n", path); + + n = sprintf(sym, "%08llX%08lX", + (unsigned long long)lws_vfs_get_length(wsi->u.http.fop_fd), + (unsigned long)lws_vfs_get_mod_time(wsi->u.http.fop_fd)); + + /* disable ranges if IF_RANGE token invalid */ + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_RANGE)) + if (strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_RANGE))) + /* differs - defeat Range: */ + wsi->u.http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0; + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) { + /* + * he thinks he has some version of it already, + * check if the tag matches + */ + if (!strcmp(sym, lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_IF_NONE_MATCH))) { + + lwsl_debug("%s: ETAG match %s %s\n", __func__, + uri, origin); + + /* we don't need to send the payload */ + if (lws_add_http_header_status(wsi, + HTTP_STATUS_NOT_MODIFIED, &p, end)) + return -1; + + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_ETAG, + (unsigned char *)sym, n, &p, end)) + return -1; + + if (lws_finalize_http_header(wsi, &p, end)) + return -1; + + n = lws_write(wsi, start, p - start, + LWS_WRITE_HTTP_HEADERS | + LWS_WRITE_H2_STREAM_END); + if (n != (p - start)) { + lwsl_err("_write returned %d from %ld\n", n, + (long)(p - start)); + return -1; + } + + lws_vfs_file_close(&wsi->u.http.fop_fd); + + return lws_http_transaction_completed(wsi); + } + } + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG, + (unsigned char *)sym, n, &p, end)) + return -1; +#endif + + mimetype = lws_get_mimetype(path, m); + if (!mimetype) { + lwsl_err("unknown mimetype for %s\n", path); + goto bail; + } + if (!mimetype[0]) + lwsl_debug("sending no mimetype for %s\n", path); + + wsi->sending_chunked = 0; + + /* + * check if this is in the list of file suffixes to be interpreted by + * a protocol + */ + while (pvo) { + n = strlen(path); + if (n > (int)strlen(pvo->name) && + !strcmp(&path[n - strlen(pvo->name)], pvo->name)) { + wsi->sending_chunked = 1; + wsi->protocol_interpret_idx = + (char)(lws_intptr_t)pvo->value; + lwsl_info("want %s interpreted by %s\n", path, + wsi->vhost->protocols[ + (int)(lws_intptr_t)(pvo->value)].name); + wsi->protocol = &wsi->vhost->protocols[ + (int)(lws_intptr_t)(pvo->value)]; + if (lws_ensure_user_space(wsi)) + return -1; + break; + } + pvo = pvo->next; + } + + if (m->protocol) { + const struct lws_protocols *pp = lws_vhost_name_to_protocol( + wsi->vhost, m->protocol); + + if (lws_bind_protocol(wsi, pp)) + return 1; + args.p = (char *)p; + args.max_len = end - p; + if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS, + wsi->user_space, &args, 0)) + return -1; + p = (unsigned char *)args.p; + } + + n = lws_serve_http_file(wsi, path, mimetype, (char *)start, p - start); + + if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi))) + return -1; /* error or can't reuse connection: close the socket */ + + return 0; +bail: + + return -1; +} + +const struct lws_http_mount * +lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len) +{ + const struct lws_http_mount *hm, *hit = NULL; + int best = 0; + + hm = wsi->vhost->mount_list; + while (hm) { + if (uri_len >= hm->mountpoint_len && + !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) && + (uri_ptr[hm->mountpoint_len] == '\0' || + uri_ptr[hm->mountpoint_len] == '/' || + hm->mountpoint_len == 1) + ) { + if (hm->origin_protocol == LWSMPRO_CALLBACK || + ((hm->origin_protocol == LWSMPRO_CGI || + lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) || + (wsi->http2_substream && + lws_hdr_total_length(wsi, + WSI_TOKEN_HTTP_COLON_PATH)) || + hm->protocol) && + hm->mountpoint_len > best)) { + best = hm->mountpoint_len; + hit = hm; + } + } + hm = hm->mount_next; + } + + return hit; +} + +#if LWS_POSIX + +static int +lws_find_string_in_file(const char *filename, const char *string, int stringlen) +{ + char buf[128]; + int fd, match = 0, pos = 0, n = 0, hit = 0; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + lwsl_err("can't open auth file: %s\n", filename); + return 1; + } + + while (1) { + if (pos == n) { + n = read(fd, buf, sizeof(buf)); + if (n <= 0) { + if (match == stringlen) + hit = 1; + break; + } + pos = 0; + } + + if (match == stringlen) { + if (buf[pos] == '\r' || buf[pos] == '\n') { + hit = 1; + break; + } + match = 0; + } + + if (buf[pos] == string[match]) + match++; + else + match = 0; + + pos++; + } + + close(fd); + + return hit; +} + +static int +lws_unauthorised_basic_auth(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + unsigned char *start = pt->serv_buf + LWS_PRE, + *p = start, *end = p + 512; + char buf[64]; + int n; + + /* no auth... tell him it is required */ + + if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end)) + return -1; + + n = lws_snprintf(buf, sizeof(buf), "Basic realm=\"lwsws\""); + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_WWW_AUTHENTICATE, + (unsigned char *)buf, n, &p, end)) + return -1; + + if (lws_finalize_http_header(wsi, &p, end)) + return -1; + + n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS | + LWS_WRITE_H2_STREAM_END); + if (n < 0) + return -1; + + return lws_http_transaction_completed(wsi); + +} + +#endif + +int lws_clean_url(char *p) +{ + if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') { + p += 4; + if (*p == 's') + p++; + if (*p == ':') { + p++; + if (*p == '/') + p++; + } + } + + while (*p) { + if (p[0] == '/' && p[1] == '/') { + char *p1 = p; + while (*p1) { + *p1 = p1[1]; + p1++; + } + continue; + } + p++; + } + + return 0; +} + + +static const unsigned char methods[] = { + WSI_TOKEN_GET_URI, + WSI_TOKEN_POST_URI, + WSI_TOKEN_OPTIONS_URI, + WSI_TOKEN_PUT_URI, + WSI_TOKEN_PATCH_URI, + WSI_TOKEN_DELETE_URI, + WSI_TOKEN_CONNECT, + WSI_TOKEN_HEAD_URI, +#ifdef LWS_WITH_HTTP2 + WSI_TOKEN_HTTP_COLON_PATH, +#endif +}; + +static int +lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len) +{ + int n, count = 0; + + for (n = 0; n < ARRAY_SIZE(methods); n++) + if (lws_hdr_total_length(wsi, methods[n])) + count++; + if (!count) { + lwsl_warn("Missing URI in HTTP request\n"); + return -1; + } + + if (count != 1 && + !(wsi->http2_substream && + lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH))) { + lwsl_warn("multiple methods?\n"); + return -1; + } + + for (n = 0; n < ARRAY_SIZE(methods); n++) + if (lws_hdr_total_length(wsi, methods[n])) { + *puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]); + *puri_len = lws_hdr_total_length(wsi, methods[n]); + return n; + } + + return -1; +} + +int +lws_http_action(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + enum http_connection_type connection_type; + enum http_version request_version; + char content_length_str[32]; + struct lws_process_html_args args; + const struct lws_http_mount *hit = NULL; + unsigned int n; + char http_version_str[10]; + char http_conn_str[20]; + int http_version_len; + char *uri_ptr = NULL, *s; + int uri_len = 0, meth; + static const char * const oprot[] = { + "http://", "https://" + }; + + meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len); + if (meth < 0 || meth >= ARRAY_SIZE(method_names)) + goto bail_nuke_ah; + + /* we insist on absolute paths */ + + if (!uri_ptr || uri_ptr[0] != '/') { + lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); + + goto bail_nuke_ah; + } + + lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth], + meth, uri_ptr); + + if (lws_ensure_user_space(wsi)) + goto bail_nuke_ah; + + /* HTTP header had a content length? */ + + wsi->u.http.rx_content_length = 0; + if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) || + lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) || + lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI)) + wsi->u.http.rx_content_length = 100 * 1024 * 1024; + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + lws_hdr_copy(wsi, content_length_str, + sizeof(content_length_str) - 1, + WSI_TOKEN_HTTP_CONTENT_LENGTH); + wsi->u.http.rx_content_length = atoll(content_length_str); + } + + if (wsi->http2_substream) { + wsi->u.http.request_version = HTTP_VERSION_2; + } else { + /* http_version? Default to 1.0, override with token: */ + request_version = HTTP_VERSION_1_0; + + /* Works for single digit HTTP versions. : */ + http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP); + if (http_version_len > 7) { + lws_hdr_copy(wsi, http_version_str, + sizeof(http_version_str) - 1, + WSI_TOKEN_HTTP); + if (http_version_str[5] == '1' && + http_version_str[7] == '1') + request_version = HTTP_VERSION_1_1; + } + wsi->u.http.request_version = request_version; + + /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */ + if (request_version == HTTP_VERSION_1_1) + connection_type = HTTP_CONNECTION_KEEP_ALIVE; + else + connection_type = HTTP_CONNECTION_CLOSE; + + /* Override default if http "Connection:" header: */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) { + lws_hdr_copy(wsi, http_conn_str, + sizeof(http_conn_str) - 1, + WSI_TOKEN_CONNECTION); + http_conn_str[sizeof(http_conn_str) - 1] = '\0'; + if (!strcasecmp(http_conn_str, "keep-alive")) + connection_type = HTTP_CONNECTION_KEEP_ALIVE; + else + if (!strcasecmp(http_conn_str, "close")) + connection_type = HTTP_CONNECTION_CLOSE; + } + wsi->u.http.connection_type = connection_type; + } + + n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION, + wsi->user_space, uri_ptr, uri_len); + if (n) { + lwsl_info("LWS_CALLBACK_HTTP closing\n"); + + return 1; + } + /* + * if there is content supposed to be coming, + * put a timeout on it having arrived + */ + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, + wsi->context->timeout_secs); +#ifdef LWS_OPENSSL_SUPPORT + if (wsi->redirect_to_https) { + /* + * we accepted http:// only so we could redirect to + * https://, so issue the redirect. Create the redirection + * URI from the host: header and ignore the path part + */ + unsigned char *start = pt->serv_buf + LWS_PRE, *p = start, + *end = p + 512; + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) + goto bail_nuke_ah; + + n = sprintf((char *)end, "https://%s/", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); + + n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, + end, n, &p, end); + if ((int)n < 0) + goto bail_nuke_ah; + + return lws_http_transaction_completed(wsi); + } +#endif + +#ifdef LWS_WITH_ACCESS_LOG + lws_prepare_access_log_info(wsi, uri_ptr, meth); +#endif + + /* can we serve it from the mount list? */ + + hit = lws_find_mount(wsi, uri_ptr, uri_len); + if (!hit) { + /* deferred cleanup and reset to protocols[0] */ + + lwsl_info("no hit\n"); + + if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0])) + return 1; + + n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, uri_ptr, uri_len); + + goto after; + } + + s = uri_ptr + hit->mountpoint_len; + + /* + * if we have a mountpoint like https://xxx.com/yyy + * there is an implied / at the end for our purposes since + * we can only mount on a "directory". + * + * But if we just go with that, the browser cannot understand + * that he is actually looking down one "directory level", so + * even though we give him /yyy/abc.html he acts like the + * current directory level is /. So relative urls like "x.png" + * wrongly look outside the mountpoint. + * + * Therefore if we didn't come in on a url with an explicit + * / at the end, we must redirect to add it so the browser + * understands he is one "directory level" down. + */ + if ((hit->mountpoint_len > 1 || + (hit->origin_protocol == LWSMPRO_REDIR_HTTP || + hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) && + (*s != '/' || + (hit->origin_protocol == LWSMPRO_REDIR_HTTP || + hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) && + (hit->origin_protocol != LWSMPRO_CGI && + hit->origin_protocol != LWSMPRO_CALLBACK)) { + unsigned char *start = pt->serv_buf + LWS_PRE, + *p = start, *end = p + 512; + + lwsl_debug("Doing 301 '%s' org %s\n", s, hit->origin); + + /* > at start indicates deal with by redirect */ + if (hit->origin_protocol == LWSMPRO_REDIR_HTTP || + hit->origin_protocol == LWSMPRO_REDIR_HTTPS) + n = lws_snprintf((char *)end, 256, "%s%s", + oprot[hit->origin_protocol & 1], + hit->origin); + else { + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { + if (!lws_hdr_total_length(wsi, + WSI_TOKEN_HTTP_COLON_AUTHORITY)) + goto bail_nuke_ah; + n = lws_snprintf((char *)end, 256, + "%s%s%s/", oprot[!!lws_is_ssl(wsi)], + lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_COLON_AUTHORITY), + uri_ptr); + } else + n = lws_snprintf((char *)end, 256, + "%s%s%s/", oprot[!!lws_is_ssl(wsi)], + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), + uri_ptr); + } + + lws_clean_url((char *)end); + n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, + end, n, &p, end); + if ((int)n < 0) + goto bail_nuke_ah; + + return lws_http_transaction_completed(wsi); + } + +#if LWS_POSIX + /* basic auth? */ + + if (hit->basic_auth_login_file) { + char b64[160], plain[(sizeof(b64) * 3) / 4]; + int m; + + /* Did he send auth? */ + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION)) + return lws_unauthorised_basic_auth(wsi); + + n = HTTP_STATUS_FORBIDDEN; + + m = lws_hdr_copy(wsi, b64, sizeof(b64), + WSI_TOKEN_HTTP_AUTHORIZATION); + if (m < 7) { + lwsl_err("b64 auth too long\n"); + goto transaction_result_n; + } + + b64[5] = '\0'; + if (strcasecmp(b64, "Basic")) { + lwsl_err("auth missing basic: %s\n", b64); + goto transaction_result_n; + } + + /* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */ + + m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain)); + if (m < 0) { + lwsl_err("plain auth too long\n"); + goto transaction_result_n; + } + + if (!lws_find_string_in_file(hit->basic_auth_login_file, + plain, m)) { + lwsl_err("basic auth lookup failed\n"); + return lws_unauthorised_basic_auth(wsi); + } + + lwsl_notice("basic auth accepted\n"); + + /* accept the auth */ + } +#endif + +#if defined(LWS_WITH_HTTP_PROXY) + /* + * The mount is a reverse proxy? + */ + + if (hit->origin_protocol == LWSMPRO_HTTPS || + hit->origin_protocol == LWSMPRO_HTTP) { + struct lws_client_connect_info i; + char ads[96], rpath[256], *pcolon, *pslash, *p; + int n, na; + + memset(&i, 0, sizeof(i)); + i.context = lws_get_context(wsi); + + pcolon = strchr(hit->origin, ':'); + pslash = strchr(hit->origin, '/'); + if (!pslash) { + lwsl_err("Proxy mount origin '%s' must have /\n", + hit->origin); + return -1; + } + if (pcolon > pslash) + pcolon = NULL; + + if (pcolon) + n = pcolon - hit->origin; + else + n = pslash - hit->origin; + + if (n >= sizeof(ads) - 2) + n = sizeof(ads) - 2; + + memcpy(ads, hit->origin, n); + ads[n] = '\0'; + + i.address = ads; + i.port = 80; + if (hit->origin_protocol == LWSMPRO_HTTPS) { + i.port = 443; + i.ssl_connection = 1; + } + if (pcolon) + i.port = atoi(pcolon + 1); + + lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s", pslash + 1, + uri_ptr + hit->mountpoint_len); + lws_clean_url(rpath); + na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS); + if (na) { + p = rpath + strlen(rpath); + *p++ = '?'; + lws_hdr_copy(wsi, p, &rpath[sizeof(rpath) - 1] - p, + WSI_TOKEN_HTTP_URI_ARGS); + while (--na) { + if (*p == '\0') + *p = '&'; + p++; + } + } + + + i.path = rpath; + i.host = i.address; + i.origin = NULL; + i.method = "GET"; + i.parent_wsi = wsi; + i.uri_replace_from = hit->origin; + i.uri_replace_to = hit->mountpoint; + + lwsl_notice("proxying to %s port %d url %s, ssl %d, from %s, to %s\n", + i.address, i.port, i.path, i.ssl_connection, + i.uri_replace_from, i.uri_replace_to); + + if (!lws_client_connect_via_info(&i)) { + lwsl_err("proxy connect fail\n"); + return 1; + } + + return 0; + } +#endif + + /* + * A particular protocol callback is mounted here? + * + * For the duration of this http transaction, bind us to the + * associated protocol + */ + if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) { + const struct lws_protocols *pp; + const char *name = hit->origin; + if (hit->protocol) + name = hit->protocol; + + pp = lws_vhost_name_to_protocol(wsi->vhost, name); + if (!pp) { + n = -1; + lwsl_err("Unable to find plugin '%s'\n", + hit->origin); + return 1; + } + + if (lws_bind_protocol(wsi, pp)) + return 1; + + args.p = uri_ptr; + args.len = uri_len; + args.max_len = hit->auth_mask; + args.final = 0; /* used to signal callback dealt with it */ + + n = wsi->protocol->callback(wsi, LWS_CALLBACK_CHECK_ACCESS_RIGHTS, + wsi->user_space, &args, 0); + if (n) { + lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED, + NULL); + goto bail_nuke_ah; + } + if (args.final) /* callback completely handled it well */ + return 0; + + if (hit->cgienv && wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_PMO, + wsi->user_space, (void *)hit->cgienv, 0)) + return 1; + + if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) { + n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, + uri_ptr + hit->mountpoint_len, + uri_len - hit->mountpoint_len); + goto after; + } + } + +#ifdef LWS_WITH_CGI + /* did we hit something with a cgi:// origin? */ + if (hit->origin_protocol == LWSMPRO_CGI) { + const char *cmd[] = { + NULL, /* replace with cgi path */ + NULL + }; + + lwsl_debug("%s: cgi\n", __func__); + cmd[0] = hit->origin; + + n = 5; + if (hit->cgi_timeout) + n = hit->cgi_timeout; + + n = lws_cgi(wsi, cmd, hit->mountpoint_len, n, + hit->cgienv); + if (n) { + lwsl_err("%s: cgi failed\n", __func__); + return -1; + } + + goto deal_body; + } +#endif + + n = strlen(s); + if (s[0] == '\0' || (n == 1 && s[n - 1] == '/')) + s = (char *)hit->def; + if (!s) + s = "index.html"; + + wsi->cache_secs = hit->cache_max_age; + wsi->cache_reuse = hit->cache_reusable; + wsi->cache_revalidate = hit->cache_revalidate; + wsi->cache_intermediaries = hit->cache_intermediaries; + + n = lws_http_serve(wsi, s, hit->origin, hit); + if (n) { + /* + * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL); + */ + if (hit->protocol) { + const struct lws_protocols *pp = lws_vhost_name_to_protocol( + wsi->vhost, hit->protocol); + + if (lws_bind_protocol(wsi, pp)) + return 1; + + n = pp->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, + uri_ptr + hit->mountpoint_len, + uri_len - hit->mountpoint_len); + } else + n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, uri_ptr, uri_len); + } + +after: + if (n) { + lwsl_info("LWS_CALLBACK_HTTP closing\n"); + + return 1; + } + +#ifdef LWS_WITH_CGI +deal_body: +#endif + /* + * If we're not issuing a file, check for content_length or + * HTTP keep-alive. No keep-alive header allocation for + * ISSUING_FILE, as this uses HTTP/1.0. + * + * In any case, return 0 and let lws_read decide how to + * proceed based on state + */ + if (wsi->state != LWSS_HTTP_ISSUING_FILE) { + /* Prepare to read body if we have a content length: */ + lwsl_debug("wsi->u.http.rx_content_length %lld %d %d\n", + (long long)wsi->u.http.rx_content_length, + wsi->upgraded_to_http2, wsi->http2_substream); + if (wsi->u.http.rx_content_length > 0) { + lwsl_notice("%s: %p: LWSS_HTTP_BODY state set\n", + __func__, wsi); + wsi->state = LWSS_HTTP_BODY; + wsi->u.http.rx_content_remain = + wsi->u.http.rx_content_length; + } + } + + return 0; + +bail_nuke_ah: + /* we're closing, losing some rx is OK */ + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 1); + + return 1; + +#if LWS_POSIX +transaction_result_n: + lws_return_http_status(wsi, n, NULL); + + return lws_http_transaction_completed(wsi); +#endif +} + +static int +lws_server_init_wsi_for_ws(struct lws *wsi) +{ + int n; + + wsi->state = LWSS_ESTABLISHED; + lws_restart_ws_ping_pong_timer(wsi); + + /* + * create the frame buffer for this connection according to the + * size mentioned in the protocol definition. If 0 there, use + * a big default for compatibility + */ + + n = wsi->protocol->rx_buffer_size; + if (!n) + n = wsi->context->pt_serv_buf_size; + n += LWS_PRE; + wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "rx_ubuf"); + if (!wsi->u.ws.rx_ubuf) { + lwsl_err("Out of Mem allocating rx buffer %d\n", n); + return 1; + } + wsi->u.ws.rx_ubuf_alloc = n; + lwsl_debug("Allocating RX buffer %d\n", n); + +#if LWS_POSIX && !defined(LWS_WITH_ESP32) + if (!wsi->parent_carries_io) + if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF, + (const char *)&n, sizeof n)) { + lwsl_warn("Failed to set SNDBUF to %d", n); + return 1; + } +#endif + + /* notify user code that we're ready to roll */ + + if (wsi->protocol->callback) + if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED, + wsi->user_space, +#ifdef LWS_OPENSSL_SUPPORT + wsi->ssl, +#else + NULL, +#endif + 0)) + return 1; + + return 0; +} + +int +lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len) +{ + int protocol_len, n = 0, hit, non_space_char_found = 0, m; + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct _lws_header_related hdr; + struct allocated_headers *ah; + unsigned char *obuf = *buf; + char protocol_list[128]; + char protocol_name[64]; + size_t olen = len; + char *p; + + if (len >= 10000000) { + lwsl_err("%s: assert: len %ld\n", __func__, (long)len); + assert(0); + } + + if (!wsi->u.hdr.ah) { + lwsl_err("%s: assert: NULL ah\n", __func__); + assert(0); + } + + lwsl_hexdump(*buf, len); + + while (len--) { + wsi->more_rx_waiting = !!len; + + if (wsi->mode != LWSCM_HTTP_SERVING && + wsi->mode != LWSCM_HTTP2_SERVING && + wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) { + lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode); + goto bail_nuke_ah; + } + + m = lws_parse(wsi, *(*buf)++); + if (m) { + if (m == 2) { + /* + * we are transitioning from http with + * an AH, to raw. Drop the ah and set + * the mode. + */ +raw_transition: + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + lws_bind_protocol(wsi, &wsi->vhost->protocols[ + wsi->vhost-> + raw_protocol_index]); + lwsl_info("transition to raw vh %s prot %d\n", + wsi->vhost->name, + wsi->vhost->raw_protocol_index); + if ((wsi->protocol->callback)(wsi, + LWS_CALLBACK_RAW_ADOPT, + wsi->user_space, NULL, 0)) + goto bail_nuke_ah; + + lws_header_table_force_to_detachable_state(wsi); + lws_union_transition(wsi, LWSCM_RAW); + lws_header_table_detach(wsi, 1); + + if (m == 2 && (wsi->protocol->callback)(wsi, + LWS_CALLBACK_RAW_RX, + wsi->user_space, obuf, olen)) + return 1; + + return 0; + } + lwsl_info("lws_parse failed\n"); + goto bail_nuke_ah; + } + + if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) + continue; + + lwsl_parser("%s: lws_parse sees parsing complete\n", __func__); + lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__, + wsi->more_rx_waiting); + + /* select vhost */ + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { + struct lws_vhost *vhost = lws_select_vhost( + context, wsi->vhost->listen_port, + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); + + if (vhost) + wsi->vhost = vhost; + } else + lwsl_info("no host\n"); + + if (wsi->mode != LWSCM_HTTP2_SERVING) { + wsi->vhost->conn_stats.h1_trans++; + if (!wsi->conn_stat_done) { + wsi->vhost->conn_stats.h1_conn++; + wsi->conn_stat_done = 1; + } + } + + /* check for unwelcome guests */ + + if (wsi->context->reject_service_keywords) { + const struct lws_protocol_vhost_options *rej = + wsi->context->reject_service_keywords; + char ua[384], *msg = NULL; + + if (lws_hdr_copy(wsi, ua, sizeof(ua) - 1, + WSI_TOKEN_HTTP_USER_AGENT) > 0) { + ua[sizeof(ua) - 1] = '\0'; + while (rej) { + if (strstr(ua, rej->name)) { +#ifdef LWS_WITH_ACCESS_LOG + char *uri_ptr = NULL; + int meth, uri_len; +#endif + + msg = strchr(rej->value, ' '); + if (msg) + msg++; + lws_return_http_status(wsi, + atoi(rej->value), msg); +#ifdef LWS_WITH_ACCESS_LOG + meth = lws_http_get_uri_and_method(wsi, + &uri_ptr, &uri_len); + if (meth >= 0) + lws_prepare_access_log_info(wsi, + uri_ptr, meth); + + /* wsi close will do the log */ +#endif + wsi->vhost->conn_stats.rejected++; + /* + * We don't want anything from + * this rejected guy. Follow + * the close flow, not the + * transaction complete flow. + */ + goto bail_nuke_ah; + } + rej = rej->next; + } + } + } + + + if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) { + lwsl_info("Changing to RAW mode\n"); + m = 0; + goto raw_transition; + } + + wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT; + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* is this websocket protocol or normal http 1.0? */ + + if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) { + if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE), + "websocket")) { + wsi->vhost->conn_stats.ws_upg++; + lwsl_info("Upgrade to ws\n"); + goto upgrade_ws; + } +#ifdef LWS_WITH_HTTP2 + if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE), + "h2c")) { + wsi->vhost->conn_stats.h2_upg++; + lwsl_info("Upgrade to h2c\n"); + goto upgrade_h2c; + } +#endif + lwsl_info("Unknown upgrade\n"); + /* dunno what he wanted to upgrade to */ + goto bail_nuke_ah; + } + + /* no upgrade ack... he remained as HTTP */ + + lwsl_info("No upgrade\n"); + ah = wsi->u.hdr.ah; + + lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED); + wsi->state = LWSS_HTTP; + wsi->u.http.fop_fd = NULL; + + /* expose it at the same offset as u.hdr */ + wsi->u.http.ah = ah; + lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi, + (void *)wsi->u.hdr.ah); + + n = lws_http_action(wsi); + + return n; + +#ifdef LWS_WITH_HTTP2 +upgrade_h2c: + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) { + lwsl_info("missing http2_settings\n"); + goto bail_nuke_ah; + } + + lwsl_info("h2c upgrade...\n"); + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS); + /* convert the peer's HTTP-Settings */ + n = lws_b64_decode_string(p, protocol_list, + sizeof(protocol_list)); + if (n < 0) { + lwsl_parser("HTTP2_SETTINGS too long\n"); + return 1; + } + + /* adopt the header info */ + + ah = wsi->u.hdr.ah; + + lws_union_transition(wsi, LWSCM_HTTP2_SERVING); + + /* http2 union member has http union struct at start */ + wsi->u.http.ah = ah; + + if (!wsi->u.h2.h2n) { + wsi->u.h2.h2n = lws_zalloc(sizeof(*wsi->u.h2.h2n), "h2n"); + if (!wsi->u.h2.h2n) + return 1; + } + + lws_h2_init(wsi); + + /* HTTP2 union */ + + lws_h2_settings(wsi, &wsi->u.h2.h2n->set, + (unsigned char *)protocol_list, n); + + lws_hpack_dynamic_size(wsi, wsi->u.h2.h2n->set.s[ + H2SET_HEADER_TABLE_SIZE]); + + strcpy(protocol_list, "HTTP/1.1 101 Switching Protocols\x0d\x0a" + "Connection: Upgrade\x0d\x0a" + "Upgrade: h2c\x0d\x0a\x0d\x0a"); + n = lws_issue_raw(wsi, (unsigned char *)protocol_list, + strlen(protocol_list)); + if (n != strlen(protocol_list)) { + lwsl_debug("http2 switch: ERROR writing to socket\n"); + return 1; + } + + wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE; + + return 0; +#endif + +upgrade_ws: + if (!wsi->protocol) + lwsl_err("NULL protocol at lws_read\n"); + + /* + * It's websocket + * + * Select the first protocol we support from the list + * the client sent us. + * + * Copy it to remove header fragmentation + */ + + if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1, + WSI_TOKEN_PROTOCOL) < 0) { + lwsl_err("protocol list too long"); + goto bail_nuke_ah; + } + + protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL); + protocol_list[protocol_len] = '\0'; + p = protocol_list; + hit = 0; + + while (*p && !hit) { + n = 0; + non_space_char_found = 0; + while (n < sizeof(protocol_name) - 1 && + *p && *p != ',') { + /* ignore leading spaces */ + if (!non_space_char_found && *p == ' ') { + n++; + continue; + } + non_space_char_found = 1; + protocol_name[n++] = *p++; + } + protocol_name[n] = '\0'; + if (*p) + p++; + + lwsl_info("checking %s\n", protocol_name); + + n = 0; + while (wsi->vhost->protocols[n].callback) { + lwsl_info("try %s\n", + wsi->vhost->protocols[n].name); + + if (wsi->vhost->protocols[n].name && + !strcmp(wsi->vhost->protocols[n].name, + protocol_name)) { + wsi->protocol = &wsi->vhost->protocols[n]; + hit = 1; + break; + } + + n++; + } + } + + /* we didn't find a protocol he wanted? */ + + if (!hit) { + if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) { + lwsl_info("No protocol from \"%s\" supported\n", + protocol_list); + goto bail_nuke_ah; + } + /* + * some clients only have one protocol and + * do not send the protocol list header... + * allow it and match to the vhost's default + * protocol (which itself defaults to zero) + */ + lwsl_info("defaulting to prot handler %d\n", + wsi->vhost->default_protocol_index); + n = wsi->vhost->default_protocol_index; + wsi->protocol = &wsi->vhost->protocols[ + (int)wsi->vhost->default_protocol_index]; + } + + /* allocate wsi->user storage */ + if (lws_ensure_user_space(wsi)) + goto bail_nuke_ah; + + /* + * Give the user code a chance to study the request and + * have the opportunity to deny it + */ + if ((wsi->protocol->callback)(wsi, + LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, + wsi->user_space, + lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) { + lwsl_warn("User code denied connection\n"); + goto bail_nuke_ah; + } + + /* + * Perform the handshake according to the protocol version the + * client announced + */ + + switch (wsi->ietf_spec_revision) { + case 13: + lwsl_parser("lws_parse calling handshake_04\n"); + if (handshake_0405(context, wsi)) { + lwsl_info("hs0405 has failed the connection\n"); + goto bail_nuke_ah; + } + break; + + default: + lwsl_info("Unknown client spec version %d\n", + wsi->ietf_spec_revision); + goto bail_nuke_ah; + } + + lws_same_vh_protocol_insert(wsi, n); + + /* we are upgrading to ws, so http/1.1 and keepalive + + * pipelined header considerations about keeping the ah around + * no longer apply. However it's common for the first ws + * protocol data to have been coalesced with the browser + * upgrade request and to already be in the ah rx buffer. + */ + + lwsl_info("%s: %p: inheriting ws ah (rxpos:%d, rxlen:%d)\n", + __func__, wsi, wsi->u.hdr.ah->rxpos, + wsi->u.hdr.ah->rxlen); + lws_pt_lock(pt); + hdr = wsi->u.hdr; + + lws_union_transition(wsi, LWSCM_WS_SERVING); + /* + * first service is WS mode will notice this, use the RX and + * then detach the ah (caution: we are not in u.hdr union + * mode any more then... ah_temp member is at start the same + * though) + * + * Because rxpos/rxlen shows something in the ah, we will get + * service guaranteed next time around the event loop + * + * All union members begin with hdr, so we can use it even + * though we transitioned to ws union mode (the ah detach + * code uses it anyway). + */ + wsi->u.hdr = hdr; + lws_pt_unlock(pt); + + lws_server_init_wsi_for_ws(wsi); + lwsl_parser("accepted v%02d connection\n", + wsi->ietf_spec_revision); + + /* !!! drop ah unreservedly after ESTABLISHED */ + if (!wsi->more_rx_waiting) { + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 1); + } + + return 0; + } /* while all chars are handled */ + + return 0; + +bail_nuke_ah: + /* drop the header info */ + /* we're closing, losing some rx is OK */ + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 1); + + return 1; +} + + +static int +lws_get_idlest_tsi(struct lws_context *context) +{ + unsigned int lowest = ~0; + int n = 0, hit = -1; + + for (; n < context->count_threads; n++) { + if ((unsigned int)context->pt[n].fds_count != + context->fd_limit_per_thread - 1 && + (unsigned int)context->pt[n].fds_count < lowest) { + lowest = context->pt[n].fds_count; + hit = n; + } + } + + return hit; +} + +struct lws * +lws_create_new_server_wsi(struct lws_vhost *vhost) +{ + struct lws *new_wsi; + int n = lws_get_idlest_tsi(vhost->context); + + if (n < 0) { + lwsl_err("no space for new conn\n"); + return NULL; + } + + new_wsi = lws_zalloc(sizeof(struct lws), "new server wsi"); + if (new_wsi == NULL) { + lwsl_err("Out of memory for new connection\n"); + return NULL; + } + + new_wsi->tsi = n; + lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi, + vhost->name, new_wsi->tsi); + + new_wsi->vhost = vhost; + new_wsi->context = vhost->context; + new_wsi->pending_timeout = NO_PENDING_TIMEOUT; + new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* initialize the instance struct */ + + new_wsi->state = LWSS_HTTP; + new_wsi->mode = LWSCM_HTTP_SERVING; + new_wsi->hdr_parsing_completed = 0; + +#ifdef LWS_OPENSSL_SUPPORT + new_wsi->use_ssl = LWS_SSL_ENABLED(vhost); +#endif + + /* + * these can only be set once the protocol is known + * we set an un-established connection's protocol pointer + * to the start of the supported list, so it can look + * for matching ones during the handshake + */ + new_wsi->protocol = vhost->protocols; + new_wsi->user_space = NULL; + new_wsi->ietf_spec_revision = 0; + new_wsi->desc.sockfd = LWS_SOCK_INVALID; + new_wsi->position_in_fds_table = -1; + + vhost->context->count_wsi_allocated++; + + /* + * outermost create notification for wsi + * no user_space because no protocol selection + */ + vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, + NULL, NULL, 0); + + return new_wsi; +} + +LWS_VISIBLE int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed(struct lws *wsi) +{ + int n = NO_PENDING_TIMEOUT; + + lwsl_info("%s: wsi %p\n", __func__, wsi); + + lws_access_log(wsi); + + if (!wsi->hdr_parsing_completed) { + lwsl_notice("%s: ignoring, ah parsing incomplete\n", __func__); + return 0; + } + + lwsl_debug("%s: wsi %p\n", __func__, wsi); + /* if we can't go back to accept new headers, drop the connection */ + if (wsi->http2_substream) + return 0; + + if (wsi->seen_zero_length_recv) + return 1; + + if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) { + lwsl_info("%s: %p: close connection\n", __func__, wsi); + return 1; + } + + if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0])) + return 1; + + /* otherwise set ourselves up ready to go again */ + wsi->state = LWSS_HTTP; + wsi->mode = LWSCM_HTTP_SERVING; + wsi->u.http.tx_content_length = 0; + wsi->u.http.tx_content_remain = 0; + wsi->hdr_parsing_completed = 0; +#ifdef LWS_WITH_ACCESS_LOG + wsi->access_log.sent = 0; +#endif + + if (wsi->vhost->keepalive_timeout) + n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE; + lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout); + + /* + * We already know we are on http1.1 / keepalive and the next thing + * coming will be another header set. + * + * If there is no pending rx and we still have the ah, drop it and + * reacquire a new ah when the new headers start to arrive. (Otherwise + * we needlessly hog an ah indefinitely.) + * + * However if there is pending rx and we know from the keepalive state + * that is already at least the start of another header set, simply + * reset the existing header table and keep it. + */ + if (wsi->u.hdr.ah) { + lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__, + wsi->more_rx_waiting); + + if (!wsi->more_rx_waiting) { + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 1); +#ifdef LWS_OPENSSL_SUPPORT + /* + * additionally... if we are hogging an SSL instance + * with no pending pipelined headers (or ah now), and + * SSL is scarce, drop this connection without waiting + */ + + if (wsi->vhost->use_ssl && + wsi->context->simultaneous_ssl_restriction && + wsi->context->simultaneous_ssl == + wsi->context->simultaneous_ssl_restriction) { + lwsl_info("%s: simultaneous_ssl_restriction\n", + __func__); + return 1; + } +#endif + } else { + lws_header_table_reset(wsi, 1); + /* + * If we kept the ah, we should restrict the amount + * of time we are willing to keep it. Otherwise it + * will be bound the whole time the connection remains + * open. + */ + lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH, + wsi->vhost->keepalive_timeout); + } + } + + /* If we're (re)starting on headers, need other implied init */ + wsi->u.hdr.ues = URIES_IDLE; + + lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi); + + return 0; +} + +/* if not a socket, it's a raw, non-ssl file descriptor */ + +LWS_VISIBLE struct lws * +lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, + lws_sock_file_fd_type fd, const char *vh_prot_name, + struct lws *parent) +{ + struct lws_context *context = vh->context; + struct lws *new_wsi; + struct lws_context_per_thread *pt; + int n, ssl = 0; + +#if defined(LWS_WITH_PEER_LIMITS) + struct lws_peer *peer = NULL; + + if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) { + peer = lws_get_or_create_peer(vh, fd.sockfd); + + if (!peer) { + lwsl_err("OOM creating peer\n"); + return NULL; + } + if (context->ip_limit_wsi && + peer->count_wsi >= context->ip_limit_wsi) { + lwsl_notice("Peer reached wsi limit %d\n", + context->ip_limit_wsi); + lws_stats_atomic_bump(context, &context->pt[0], + LWSSTATS_C_PEER_LIMIT_WSI_DENIED, 1); + return NULL; + } + } +#endif + + new_wsi = lws_create_new_server_wsi(vh); + if (!new_wsi) { + if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) + compatible_close(fd.sockfd); + return NULL; + } +#if defined(LWS_WITH_PEER_LIMITS) + if (peer) + lws_peer_add_wsi(context, peer, new_wsi); +#endif + pt = &context->pt[(int)new_wsi->tsi]; + lws_stats_atomic_bump(context, pt, LWSSTATS_C_CONNECTIONS, 1); + + if (parent) { + new_wsi->parent = parent; + new_wsi->sibling_list = parent->child_list; + parent->child_list = new_wsi; + + if (type & LWS_ADOPT_WS_PARENTIO) + new_wsi->parent_carries_io = 1; + } + + new_wsi->desc = fd; + + if (vh_prot_name) { + new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost, + vh_prot_name); + if (!new_wsi->protocol) { + lwsl_err("Protocol %s not enabled on vhost %s\n", + vh_prot_name, new_wsi->vhost->name); + goto bail; + } + if (lws_ensure_user_space(new_wsi)) { + lwsl_notice("OOM trying to get user_space\n"); + goto bail; + } + if (type & LWS_ADOPT_WS_PARENTIO) { + new_wsi->desc.sockfd = LWS_SOCK_INVALID; + lwsl_debug("binding to %s\n", new_wsi->protocol->name); + lws_bind_protocol(new_wsi, new_wsi->protocol); + lws_union_transition(new_wsi, LWSCM_WS_SERVING); + lws_server_init_wsi_for_ws(new_wsi); + + return new_wsi; + } + } else + if (type & LWS_ADOPT_HTTP) /* he will transition later */ + new_wsi->protocol = + &vh->protocols[vh->default_protocol_index]; + else { /* this is the only time he will transition */ + lws_bind_protocol(new_wsi, + &vh->protocols[vh->raw_protocol_index]); + lws_union_transition(new_wsi, LWSCM_RAW); + } + + if (type & LWS_ADOPT_SOCKET) { /* socket desc */ + lwsl_debug("%s: new wsi %p, sockfd %d\n", __func__, new_wsi, + (int)(lws_intptr_t)fd.sockfd); + + if (type & LWS_ADOPT_HTTP) + /* the transport is accepted... + * give him time to negotiate */ + lws_set_timeout(new_wsi, + PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, + context->timeout_secs); + +#if LWS_POSIX == 0 +#if defined(LWS_WITH_ESP8266) + esp8266_tcp_stream_accept(accept_fd, new_wsi); +#endif +#endif + } else /* file desc */ + lwsl_debug("%s: new wsi %p, filefd %d\n", __func__, new_wsi, + (int)(lws_intptr_t)fd.filefd); + + /* + * A new connection was accepted. Give the user a chance to + * set properties of the newly created wsi. There's no protocol + * selected yet so we issue this to the vhosts's default protocol, + * itself by default protocols[0] + */ + n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED; + if (!(type & LWS_ADOPT_HTTP)) { + if (!(type & LWS_ADOPT_SOCKET)) + n = LWS_CALLBACK_RAW_ADOPT_FILE; + else + n = LWS_CALLBACK_RAW_ADOPT; + } + + if (!LWS_SSL_ENABLED(new_wsi->vhost) || !(type & LWS_ADOPT_ALLOW_SSL) || + !(type & LWS_ADOPT_SOCKET)) { + /* non-SSL */ + if (!(type & LWS_ADOPT_HTTP)) { + if (!(type & LWS_ADOPT_SOCKET)) + new_wsi->mode = LWSCM_RAW_FILEDESC; + else + new_wsi->mode = LWSCM_RAW; + } + } else { + /* SSL */ + if (!(type & LWS_ADOPT_HTTP)) + new_wsi->mode = LWSCM_SSL_INIT_RAW; + else + new_wsi->mode = LWSCM_SSL_INIT; + + ssl = 1; + } + + lws_libev_accept(new_wsi, new_wsi->desc); + lws_libuv_accept(new_wsi, new_wsi->desc); + lws_libevent_accept(new_wsi, new_wsi->desc); + + if (!ssl) { + if (insert_wsi_socket_into_fds(context, new_wsi)) { + lwsl_err("%s: fail inserting socket\n", __func__); + goto fail; + } + } else + if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) { + lwsl_info("%s: fail ssl negotiation\n", __func__); + goto fail; + } + + /* + * by deferring callback to this point, after insertion to fds, + * lws_callback_on_writable() can work from the callback + */ + if ((new_wsi->protocol->callback)( + new_wsi, n, new_wsi->user_space, NULL, 0)) + goto fail; + + if (type & LWS_ADOPT_HTTP) { + if (!lws_header_table_attach(new_wsi, 0)) + lwsl_debug("Attached ah immediately\n"); + else + lwsl_info("%s: waiting for ah\n", __func__); + } + + return new_wsi; + +fail: + if (type & LWS_ADOPT_SOCKET) + lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return NULL; + +bail: + lwsl_notice("%s: exiting on bail\n", __func__); + if (parent) + parent->child_list = new_wsi->sibling_list; + if (new_wsi->user_space) + lws_free(new_wsi->user_space); + lws_free(new_wsi); + compatible_close(fd.sockfd); + + return NULL; +} + +LWS_VISIBLE struct lws * +lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd) +{ + lws_sock_file_fd_type fd; + + fd.sockfd = accept_fd; + return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET | + LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL); +} + +LWS_VISIBLE struct lws * +lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) +{ + return lws_adopt_socket_vhost(context->vhost_list, accept_fd); +} + +/* Common read-buffer adoption for lws_adopt_*_readbuf */ +static struct lws* +adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len) +{ + struct lws_context_per_thread *pt; + struct allocated_headers *ah; + struct lws_pollfd *pfd; + + if (!wsi) + return NULL; + + if (!readbuf || len == 0) + return wsi; + + if (len > sizeof(ah->rx)) { + lwsl_err("%s: rx in too big\n", __func__); + goto bail; + } + + /* + * we can't process the initial read data until we can attach an ah. + * + * if one is available, get it and place the data in his ah rxbuf... + * wsi with ah that have pending rxbuf get auto-POLLIN service. + * + * no autoservice because we didn't get a chance to attach the + * readbuf data to wsi or ah yet, and we will do it next if we get + * the ah. + */ + if (wsi->u.hdr.ah || !lws_header_table_attach(wsi, 0)) { + ah = wsi->u.hdr.ah; + memcpy(ah->rx, readbuf, len); + ah->rxpos = 0; + ah->rxlen = (int16_t)len; + + lwsl_notice("%s: calling service on readbuf ah\n", __func__); + pt = &wsi->context->pt[(int)wsi->tsi]; + + /* unlike a normal connect, we have the headers already + * (or the first part of them anyway). + * libuv won't come back and service us without a network + * event, so we need to do the header service right here. + */ + pfd = &pt->fds[wsi->position_in_fds_table]; + pfd->revents |= LWS_POLLIN; + lwsl_err("%s: calling service\n", __func__); + if (lws_service_fd_tsi(wsi->context, pfd, wsi->tsi)) + /* service closed us */ + return NULL; + + return wsi; + } + lwsl_err("%s: deferring handling ah\n", __func__); + /* + * hum if no ah came, we are on the wait list and must defer + * dealing with this until the ah arrives. + * + * later successful lws_header_table_attach() will apply the + * below to the rx buffer (via lws_header_table_reset()). + */ + wsi->u.hdr.preamble_rx = lws_malloc(len, "preamble_rx"); + if (!wsi->u.hdr.preamble_rx) { + lwsl_err("OOM\n"); + goto bail; + } + memcpy(wsi->u.hdr.preamble_rx, readbuf, len); + wsi->u.hdr.preamble_rx_len = len; + + return wsi; + +bail: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return NULL; +} + +LWS_VISIBLE struct lws * +lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, + const char *readbuf, size_t len) +{ + return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd), + readbuf, len); +} + +LWS_VISIBLE struct lws * +lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, + lws_sockfd_type accept_fd, + const char *readbuf, size_t len) +{ + return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd), + readbuf, len); +} + +LWS_VISIBLE int +lws_server_socket_service(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pollfd) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + lws_sockfd_type accept_fd = LWS_SOCK_INVALID; + struct allocated_headers *ah; + lws_sock_file_fd_type fd; + int opts = LWS_ADOPT_SOCKET | LWS_ADOPT_ALLOW_SSL; +#if LWS_POSIX + struct sockaddr_storage cli_addr; + socklen_t clilen; +#endif + int n, len; + + switch (wsi->mode) { + + case LWSCM_HTTP_SERVING: + case LWSCM_HTTP_SERVING_ACCEPTED: + case LWSCM_HTTP2_SERVING: + case LWSCM_RAW: + + /* handle http headers coming in */ + + /* pending truncated sends have uber priority */ + + if (wsi->trunc_len) { + if (!(pollfd->revents & LWS_POLLOUT)) + break; + + if (lws_issue_raw(wsi, wsi->trunc_alloc + + wsi->trunc_offset, + wsi->trunc_len) < 0) + goto fail; + /* + * we can't afford to allow input processing to send + * something new, so spin around he event loop until + * he doesn't have any partials + */ + break; + } + + /* any incoming data ready? */ + + if (!(pollfd->revents & pollfd->events & LWS_POLLIN)) + goto try_pollout; + + /* + * If we previously just did POLLIN when IN and OUT were + * signalled (because POLLIN processing may have used up + * the POLLOUT), don't let that happen twice in a row... + * next time we see the situation favour POLLOUT + */ +#if !defined(LWS_WITH_ESP8266) + if (wsi->favoured_pollin && + (pollfd->revents & pollfd->events & LWS_POLLOUT)) { + lwsl_notice("favouring pollout\n"); + wsi->favoured_pollin = 0; + goto try_pollout; + } +#endif + + /* these states imply we MUST have an ah attached */ + + if (wsi->mode != LWSCM_RAW && (wsi->state == LWSS_HTTP || + wsi->state == LWSS_HTTP_ISSUING_FILE || + wsi->state == LWSS_HTTP_HEADERS)) { + if (!wsi->u.hdr.ah) { + /* no autoservice beacuse we will do it next */ + if (lws_header_table_attach(wsi, 0)) { + lwsl_info("wsi %p: ah get fail\n", wsi); + goto try_pollout; + } + } + ah = wsi->u.hdr.ah; + + /* if nothing in ah rx buffer, get some fresh rx */ + if (ah->rxpos == ah->rxlen) { + ah->rxlen = lws_ssl_capable_read(wsi, ah->rx, + sizeof(ah->rx)); + ah->rxpos = 0; + switch (ah->rxlen) { + case 0: + lwsl_info("%s: read 0 len a\n", __func__); + wsi->seen_zero_length_recv = 1; + lws_change_pollfd(wsi, LWS_POLLIN, 0); + goto try_pollout; + /* fallthru */ + case LWS_SSL_CAPABLE_ERROR: + goto fail; + case LWS_SSL_CAPABLE_MORE_SERVICE: + ah->rxlen = ah->rxpos = 0; + goto try_pollout; + } + + /* + * make sure ah does not get detached if we + * have live data in the rx + */ + if (ah->rxlen) + wsi->more_rx_waiting = 1; + } + + if (!(ah->rxpos != ah->rxlen && ah->rxlen)) { + lwsl_err("%s: assert: rxpos %d, rxlen %d\n", + __func__, ah->rxpos, ah->rxlen); + + assert(0); + } + + /* just ignore incoming if waiting for close */ + if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE && + wsi->state != LWSS_HTTP_ISSUING_FILE) { + /* + * otherwise give it to whoever wants it + * according to the connection state + */ + + n = lws_read(wsi, ah->rx + ah->rxpos, + ah->rxlen - ah->rxpos); + if (n < 0) /* we closed wsi */ + return 1; + + if (!wsi->u.hdr.ah) + break; + if ( wsi->u.hdr.ah->rxlen) + wsi->u.hdr.ah->rxpos += n; + + lwsl_debug("%s: wsi %p: ah read rxpos %d, rxlen %d\n", + __func__, wsi, wsi->u.hdr.ah->rxpos, + wsi->u.hdr.ah->rxlen); + + if (lws_header_table_is_in_detachable_state(wsi) && + (wsi->mode != LWSCM_HTTP_SERVING && + wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED && + wsi->mode != LWSCM_HTTP2_SERVING)) + lws_header_table_detach(wsi, 1); + + break; + } + + goto try_pollout; + } + + len = lws_ssl_capable_read(wsi, pt->serv_buf, + context->pt_serv_buf_size); + lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len); + switch (len) { + case 0: + lwsl_info("%s: read 0 len b\n", __func__); + + /* fallthru */ + case LWS_SSL_CAPABLE_ERROR: + goto fail; + case LWS_SSL_CAPABLE_MORE_SERVICE: + goto try_pollout; + } + + if (len < 0) /* coverity */ + goto fail; + + if (wsi->mode == LWSCM_RAW) { + n = user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_RAW_RX, + wsi->user_space, pt->serv_buf, len); + if (n < 0) { + lwsl_info("LWS_CALLBACK_RAW_RX_fail\n"); + goto fail; + } + goto try_pollout; + } + + /* just ignore incoming if waiting for close */ + if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE && + wsi->state != LWSS_HTTP_ISSUING_FILE) { + /* + * this may want to send + * (via HTTP callback for example) + */ + n = lws_read(wsi, pt->serv_buf, len); + if (n < 0) /* we closed wsi */ + return 1; + /* + * he may have used up the + * writability above, if we will defer POLLOUT + * processing in favour of POLLIN, note it + */ + if (pollfd->revents & LWS_POLLOUT) + wsi->favoured_pollin = 1; + break; + } + /* + * he may have used up the + * writability above, if we will defer POLLOUT + * processing in favour of POLLIN, note it + */ + if (pollfd->revents & LWS_POLLOUT) + wsi->favoured_pollin = 1; + +try_pollout: + + /* this handles POLLOUT for http serving fragments */ + + if (!(pollfd->revents & LWS_POLLOUT)) + break; + + /* one shot */ + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_notice("%s a\n", __func__); + goto fail; + } + + if (wsi->mode == LWSCM_RAW) { + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_WRITEABLE_CB, 1); +#if defined(LWS_WITH_STATS) + if (wsi->active_writable_req_us) { + uint64_t ul = time_in_microseconds() - + wsi->active_writable_req_us; + + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_MS_WRITABLE_DELAY, ul); + lws_stats_atomic_max(wsi->context, pt, + LWSSTATS_MS_WORST_WRITABLE_DELAY, ul); + wsi->active_writable_req_us = 0; + } +#endif + n = user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_RAW_WRITEABLE, + wsi->user_space, NULL, 0); + if (n < 0) { + lwsl_info("writeable_fail\n"); + goto fail; + } + break; + } + + if (!wsi->hdr_parsing_completed) + break; + + if (wsi->state != LWSS_HTTP_ISSUING_FILE) { + + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_WRITEABLE_CB, 1); +#if defined(LWS_WITH_STATS) + if (wsi->active_writable_req_us) { + uint64_t ul = time_in_microseconds() - + wsi->active_writable_req_us; + + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_MS_WRITABLE_DELAY, ul); + lws_stats_atomic_max(wsi->context, pt, + LWSSTATS_MS_WORST_WRITABLE_DELAY, ul); + wsi->active_writable_req_us = 0; + } +#endif + + n = user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_HTTP_WRITEABLE, + wsi->user_space, NULL, 0); + if (n < 0) { + lwsl_info("writeable_fail\n"); + goto fail; + } + break; + } + + /* >0 == completion, <0 == error + * + * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when + * it's done. That's the case even if we just completed the + * send, so wait for that. + */ + n = lws_serve_http_file_fragment(wsi); + if (n < 0) + goto fail; + + break; + + case LWSCM_SERVER_LISTENER: + +#if LWS_POSIX + /* pollin means a client has connected to us then */ + + do { + if (!(pollfd->revents & LWS_POLLIN) || + !(pollfd->events & LWS_POLLIN)) + break; + +#ifdef LWS_OPENSSL_SUPPORT + /* + * can we really accept it, with regards to SSL limit? + * another vhost may also have had POLLIN on his listener this + * round and used it up already + */ + + if (wsi->vhost->use_ssl && + context->simultaneous_ssl_restriction && + context->simultaneous_ssl == + context->simultaneous_ssl_restriction) + /* no... ignore it, he won't come again until we are + * below the simultaneous_ssl_restriction limit and + * POLLIN is enabled on him again + */ + break; +#endif + /* listen socket got an unencrypted connection... */ + + clilen = sizeof(cli_addr); + lws_latency_pre(context, wsi); + + /* + * We cannot identify the peer who is in the listen + * socket connect queue before we accept it; even if + * we could, not accepting it due to PEER_LIMITS would + * block the connect queue for other legit peers. + */ + accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr, + &clilen); + lws_latency(context, wsi, "listener accept", accept_fd, + accept_fd >= 0); + if (accept_fd < 0) { + if (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK) { + break; + } + lwsl_err("ERROR on accept: %s\n", strerror(LWS_ERRNO)); + break; + } + + lws_plat_set_socket_options(wsi->vhost, accept_fd); + +#if defined(LWS_WITH_IPV6) + lwsl_debug("accepted new conn port %u on fd=%d\n", + ((cli_addr.ss_family == AF_INET6) ? + ntohs(((struct sockaddr_in6 *) &cli_addr)->sin6_port) : + ntohs(((struct sockaddr_in *) &cli_addr)->sin_port)), + accept_fd); +#else + lwsl_debug("accepted new conn port %u on fd=%d\n", + ntohs(((struct sockaddr_in *) &cli_addr)->sin_port), + accept_fd); +#endif + +#else + /* not very beautiful... */ + accept_fd = (lws_sockfd_type)pollfd; +#endif + /* + * look at who we connected to and give user code a chance + * to reject based on client IP. There's no protocol selected + * yet so we issue this to protocols[0] + */ + if ((wsi->vhost->protocols[0].callback)(wsi, + LWS_CALLBACK_FILTER_NETWORK_CONNECTION, + NULL, (void *)(lws_intptr_t)accept_fd, 0)) { + lwsl_debug("Callback denied network connection\n"); + compatible_close(accept_fd); + break; + } + + if (!(wsi->vhost->options & LWS_SERVER_OPTION_ONLY_RAW)) + opts |= LWS_ADOPT_HTTP; + else + opts = LWS_ADOPT_SOCKET; + + fd.sockfd = accept_fd; + if (!lws_adopt_descriptor_vhost(wsi->vhost, opts, fd, + NULL, NULL)) + /* already closed cleanly as necessary */ + return 1; + +#if LWS_POSIX + } while (pt->fds_count < context->fd_limit_per_thread - 1 && + lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0); +#endif + return 0; + + default: + break; + } + + if (!lws_server_socket_service_ssl(wsi, accept_fd)) + return 0; + +fail: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return 1; +} + +LWS_VISIBLE int +lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, + const char *other_headers, int other_headers_len) +{ + static const char * const intermediates[] = { "private", "public" }; + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; +#if defined(LWS_WITH_RANGES) + struct lws_range_parsing *rp = &wsi->u.http.range; +#endif + char cache_control[50], *cc = "no-store"; + unsigned char *response = pt->serv_buf + LWS_PRE; + unsigned char *p = response; + unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE; + lws_filepos_t computed_total_content_length; + int ret = 0, cclen = 8, n = HTTP_STATUS_OK; + lws_fop_flags_t fflags = LWS_O_RDONLY; +#if defined(LWS_WITH_RANGES) + int ranges; +#endif + const struct lws_plat_file_ops *fops; + const char *vpath; + + /* + * We either call the platform fops .open with first arg platform fops, + * or we call fops_zip .open with first arg platform fops, and fops_zip + * open will decide whether to switch to fops_zip or stay with fops_def. + * + * If wsi->u.http.fop_fd is already set, the caller already opened it + */ + if (!wsi->u.http.fop_fd) { + fops = lws_vfs_select_fops(wsi->context->fops, file, &vpath); + fflags |= lws_vfs_prepare_flags(wsi); + wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops, + file, vpath, &fflags); + if (!wsi->u.http.fop_fd) { + lwsl_err("Unable to open '%s'\n", file); + + return -1; + } + } + wsi->u.http.filelen = lws_vfs_get_length(wsi->u.http.fop_fd); + computed_total_content_length = wsi->u.http.filelen; + +#if defined(LWS_WITH_RANGES) + ranges = lws_ranges_init(wsi, rp, wsi->u.http.filelen); + + lwsl_debug("Range count %d\n", ranges); + /* + * no ranges -> 200; + * 1 range -> 206 + Content-Type: normal; Content-Range; + * more -> 206 + Content-Type: multipart/byteranges + * Repeat the true Content-Type in each multipart header + * along with Content-Range + */ + if (ranges < 0) { + /* it means he expressed a range in Range:, but it was illegal */ + lws_return_http_status(wsi, HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, + NULL); + if (lws_http_transaction_completed(wsi)) + return -1; /* <0 means just hang up */ + + lws_vfs_file_close(&wsi->u.http.fop_fd); + + return 0; /* == 0 means we dealt with the transaction complete */ + } + if (ranges) + n = HTTP_STATUS_PARTIAL_CONTENT; +#endif + + if (lws_add_http_header_status(wsi, n, &p, end)) + return -1; + + if ((wsi->u.http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | + LWS_FOP_FLAG_COMPR_IS_GZIP)) == + (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) { + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_CONTENT_ENCODING, + (unsigned char *)"gzip", 4, &p, end)) + return -1; + lwsl_info("file is being provided in gzip\n"); + } + + if ( +#if defined(LWS_WITH_RANGES) + ranges < 2 && +#endif + content_type && content_type[0]) + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)content_type, + strlen(content_type), &p, end)) + return -1; + +#if defined(LWS_WITH_RANGES) + if (ranges >= 2) { /* multipart byteranges */ + strncpy(wsi->u.http.multipart_content_type, content_type, + sizeof(wsi->u.http.multipart_content_type) - 1); + wsi->u.http.multipart_content_type[ + sizeof(wsi->u.http.multipart_content_type) - 1] = '\0'; + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)"multipart/byteranges; boundary=_lws", + 20, &p, end)) + return -1; + + /* + * our overall content length has to include + * + * - (n + 1) x "_lws\r\n" + * - n x Content-Type: xxx/xxx\r\n + * - n x Content-Range: bytes xxx-yyy/zzz\r\n + * - n x /r/n + * - the actual payloads (aggregated in rp->agg) + * + * Precompute it for the main response header + */ + + computed_total_content_length = (lws_filepos_t)rp->agg + + 6 /* final _lws\r\n */; + + lws_ranges_reset(rp); + while (lws_ranges_next(rp)) { + n = lws_snprintf(cache_control, sizeof(cache_control), + "bytes %llu-%llu/%llu", + rp->start, rp->end, rp->extent); + + computed_total_content_length += + 6 /* header _lws\r\n */ + + /* Content-Type: xxx/xxx\r\n */ + 14 + strlen(content_type) + 2 + + /* Content-Range: xxxx\r\n */ + 15 + n + 2 + + 2; /* /r/n */ + } + + lws_ranges_reset(rp); + lws_ranges_next(rp); + } + + if (ranges == 1) { + computed_total_content_length = (lws_filepos_t)rp->agg; + n = lws_snprintf(cache_control, sizeof(cache_control), + "bytes %llu-%llu/%llu", + rp->start, rp->end, rp->extent); + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_RANGE, + (unsigned char *)cache_control, + n, &p, end)) + return -1; + } + + wsi->u.http.range.inside = 0; + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES, + (unsigned char *)"bytes", 5, &p, end)) + return -1; +#endif + + if (!wsi->sending_chunked) { + if (lws_add_http_header_content_length(wsi, + computed_total_content_length, + &p, end)) + return -1; + } else { + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_TRANSFER_ENCODING, + (unsigned char *)"chunked", + 7, &p, end)) + return -1; + } + + if (wsi->cache_secs && wsi->cache_reuse) { + if (wsi->cache_revalidate) { + cc = cache_control; + cclen = sprintf(cache_control, "%s max-age: %u", + intermediates[wsi->cache_intermediaries], + wsi->cache_secs); + } else { + cc = "no-cache"; + cclen = 8; + } + } + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CACHE_CONTROL, + (unsigned char *)cc, cclen, &p, end)) + return -1; + + if (wsi->u.http.connection_type == HTTP_CONNECTION_KEEP_ALIVE) + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION, + (unsigned char *)"keep-alive", 10, &p, end)) + return -1; + + if (other_headers) { + if ((end - p) < other_headers_len) + return -1; + memcpy(p, other_headers, other_headers_len); + p += other_headers_len; + } + + if (lws_finalize_http_header(wsi, &p, end)) + return -1; + + ret = lws_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS); + if (ret != (p - response)) { + lwsl_err("_write returned %d from %ld\n", ret, + (long)(p - response)); + return -1; + } + + wsi->u.http.filepos = 0; + wsi->state = LWSS_HTTP_ISSUING_FILE; + + lws_callback_on_writable(wsi); + + return 0; +} + +int +lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len) +{ + int m; + + lwsl_parser("%s: received %d byte packet\n", __func__, (int)len); +#if 0 + lwsl_hexdump(*buf, len); +#endif + + /* let the rx protocol state machine have as much as it needs */ + + while (len) { + /* + * we were accepting input but now we stopped doing so + */ + if (wsi->rxflow_bitmap) { + lws_rxflow_cache(wsi, *buf, 0, len); + lwsl_parser("%s: cached %ld\n", __func__, (long)len); + return 1; + } + + if (wsi->u.ws.rx_draining_ext) { + m = lws_rx_sm(wsi, 0); + if (m < 0) + return -1; + continue; + } + + /* account for what we're using in rxflow buffer */ + if (wsi->rxflow_buffer) { + wsi->rxflow_pos++; + if (wsi->rxflow_pos > wsi->rxflow_len) { + lwsl_err("bumped rxflow buffer too far (%d / %d)", wsi->rxflow_pos, wsi->rxflow_len); + assert(0); + } + } + + /* consume payload bytes efficiently */ + if (wsi->lws_rx_parse_state == + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED) { + m = lws_payload_until_length_exhausted(wsi, buf, &len); + if (wsi->rxflow_buffer) + wsi->rxflow_pos += m; + } + + if (wsi->rxflow_buffer && wsi->rxflow_pos == wsi->rxflow_len) { + lwsl_debug("%s: %p flow buf: drained\n", __func__, wsi); + lws_free_set_NULL(wsi->rxflow_buffer); + /* having drained the rxflow buffer, can rearm POLLIN */ +#ifdef LWS_NO_SERVER + m = +#endif + _lws_rx_flow_control(wsi); + /* m ignored, needed for NO_SERVER case */ + } + + /* process the byte */ + m = lws_rx_sm(wsi, *(*buf)++); + if (m < 0) + return -1; + len--; + } + + lwsl_parser("%s: exit with %d unused\n", __func__, (int)len); + + return 0; +} + +LWS_VISIBLE void +lws_server_get_canonical_hostname(struct lws_context *context, + struct lws_context_creation_info *info) +{ + if (lws_check_opt(info->options, + LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)) + return; +#if LWS_POSIX && !defined(LWS_WITH_ESP32) + /* find canonical hostname */ + gethostname((char *)context->canonical_hostname, + sizeof(context->canonical_hostname) - 1); + + lwsl_info(" canonical_hostname = %s\n", context->canonical_hostname); +#else + (void)context; +#endif +} + + +LWS_VISIBLE LWS_EXTERN int +lws_chunked_html_process(struct lws_process_html_args *args, + struct lws_process_html_state *s) +{ + char *sp, buffer[32]; + const char *pc; + int old_len, n; + + /* do replacements */ + sp = args->p; + old_len = args->len; + args->len = 0; + s->start = sp; + while (sp < args->p + old_len) { + + if (args->len + 7 >= args->max_len) { + lwsl_err("Used up interpret padding\n"); + return -1; + } + + if ((!s->pos && *sp == '$') || s->pos) { + int hits = 0, hit = 0; + + if (!s->pos) + s->start = sp; + s->swallow[s->pos++] = *sp; + if (s->pos == sizeof(s->swallow) - 1) + goto skip; + for (n = 0; n < s->count_vars; n++) + if (!strncmp(s->swallow, s->vars[n], s->pos)) { + hits++; + hit = n; + } + if (!hits) { +skip: + s->swallow[s->pos] = '\0'; + memcpy(s->start, s->swallow, s->pos); + args->len++; + s->pos = 0; + sp = s->start + 1; + continue; + } + if (hits == 1 && s->pos == strlen(s->vars[hit])) { + pc = s->replace(s->data, hit); + if (!pc) + pc = "NULL"; + n = strlen(pc); + s->swallow[s->pos] = '\0'; + if (n != s->pos) { + memmove(s->start + n, + s->start + s->pos, + old_len - (sp - args->p)); + old_len += (n - s->pos) + 1; + } + memcpy(s->start, pc, n); + args->len++; + sp = s->start + 1; + + s->pos = 0; + } + sp++; + continue; + } + + args->len++; + sp++; + } + + /* no space left for final chunk trailer */ + if (args->final && args->len + 7 >= args->max_len) + return -1; + + n = sprintf(buffer, "%X\x0d\x0a", args->len); + + args->p -= n; + memcpy(args->p, buffer, n); + args->len += n; + + if (args->final) { + sp = args->p + args->len; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + *sp++ = '0'; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + args->len += 7; + } else { + sp = args->p + args->len; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + args->len += 2; + } + + return 0; +} diff -Nru libwebsockets-4.0.20/lib/server/server-handshake.c libwebsockets-2.4.2/lib/server/server-handshake.c --- libwebsockets-4.0.20/lib/server/server-handshake.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/server-handshake.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,360 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2013 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); } + +#ifndef LWS_NO_EXTENSIONS +static int +lws_extension_server_handshake(struct lws *wsi, char **p, int budget) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char ext_name[64], *args, *end = (*p) + budget - 1; + const struct lws_ext_options *opts, *po; + const struct lws_extension *ext; + struct lws_ext_option_arg oa; + int n, m, more = 1; + int ext_count = 0; + char ignore; + char *c; + + /* + * Figure out which extensions the client has that we want to + * enable on this connection, and give him back the list + */ + if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) + return 0; + + /* + * break down the list of client extensions + * and go through them + */ + + if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size, + WSI_TOKEN_EXTENSIONS) < 0) + return 1; + + c = (char *)pt->serv_buf; + lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c); + wsi->count_act_ext = 0; + ignore = 0; + n = 0; + args = NULL; + + /* + * We may get a simple request + * + * Sec-WebSocket-Extensions: permessage-deflate + * + * or an elaborated one with requested options + * + * Sec-WebSocket-Extensions: permessage-deflate; \ + * server_no_context_takeover; \ + * client_no_context_takeover + */ + + while (more) { + + if (*c && (*c != ',' && *c != '\t')) { + if (*c == ';') { + ignore = 1; + args = c + 1; + } + if (ignore || *c == ' ') { + c++; + continue; + } + ext_name[n] = *c++; + if (n < sizeof(ext_name) - 1) + n++; + continue; + } + ext_name[n] = '\0'; + + ignore = 0; + if (!*c) + more = 0; + else { + c++; + if (!n) + continue; + } + + while (args && *args && *args == ' ') + args++; + + /* check a client's extension against our support */ + + ext = wsi->vhost->extensions; + + while (ext && ext->callback) { + + if (strcmp(ext_name, ext->name)) { + ext++; + continue; + } + + /* + * oh, we do support this one he asked for... but let's + * confirm he only gave it once + */ + for (m = 0; m < wsi->count_act_ext; m++) + if (wsi->active_extensions[m] == ext) { + lwsl_info("extension mentioned twice\n"); + return 1; /* shenanigans */ + } + + /* + * ask user code if it's OK to apply it on this + * particular connection + protocol + */ + m = (wsi->protocol->callback)(wsi, + LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, + wsi->user_space, ext_name, 0); + + /* + * zero return from callback means go ahead and allow + * the extension, it's what we get if the callback is + * unhandled + */ + if (m) { + ext++; + continue; + } + + /* apply it */ + + ext_count++; + + /* instantiate the extension on this conn */ + + wsi->active_extensions[wsi->count_act_ext] = ext; + + /* allow him to construct his context */ + + if (ext->callback(lws_get_context(wsi), ext, wsi, + LWS_EXT_CB_CONSTRUCT, + (void *)&wsi->act_ext_user[ + wsi->count_act_ext], + (void *)&opts, 0)) { + lwsl_info("ext %s failed construction\n", + ext_name); + ext_count--; + ext++; + + continue; + } + + if (ext_count > 1) + *(*p)++ = ','; + else + LWS_CPYAPP(*p, + "\x0d\x0aSec-WebSocket-Extensions: "); + *p += lws_snprintf(*p, (end - *p), "%s", ext_name); + + /* + * go through the options trying to apply the + * recognized ones + */ + + lwsl_debug("ext args %s", args); + + while (args && *args && *args != ',') { + while (*args == ' ') + args++; + po = opts; + while (po->name) { + lwsl_debug("'%s' '%s'\n", po->name, args); + /* only support arg-less options... */ + if (po->type == EXTARG_NONE && + !strncmp(args, po->name, + strlen(po->name))) { + oa.option_name = NULL; + oa.option_index = po - opts; + oa.start = NULL; + lwsl_debug("setting %s\n", po->name); + if (!ext->callback( + lws_get_context(wsi), ext, wsi, + LWS_EXT_CB_OPTION_SET, + wsi->act_ext_user[ + wsi->count_act_ext], + &oa, (end - *p))) { + + *p += lws_snprintf(*p, (end - *p), "; %s", po->name); + lwsl_debug("adding option %s\n", po->name); + } + } + po++; + } + while (*args && *args != ',' && *args != ';') + args++; + } + + wsi->count_act_ext++; + lwsl_parser("count_act_ext <- %d\n", + wsi->count_act_ext); + + ext++; + } + + n = 0; + args = NULL; + } + + return 0; +} +#endif +int +handshake_0405(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_process_html_args args; + unsigned char hash[20]; + int n, accept_len; + char *response; + char *p; + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) || + !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) { + lwsl_parser("handshake_04 missing pieces\n"); + /* completed header processing, but missing some bits */ + goto bail; + } + + if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= MAX_WEBSOCKET_04_KEY_LEN) { + lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN); + goto bail; + } + + /* + * since key length is restricted above (currently 128), cannot + * overflow + */ + n = sprintf((char *)pt->serv_buf, + "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY)); + + lws_SHA1(pt->serv_buf, n, hash); + + accept_len = lws_b64_encode_string((char *)hash, 20, + (char *)pt->serv_buf, context->pt_serv_buf_size); + if (accept_len < 0) { + lwsl_warn("Base64 encoded hash too long\n"); + goto bail; + } + + /* allocate the per-connection user memory (if any) */ + if (lws_ensure_user_space(wsi)) + goto bail; + + /* create the response packet */ + + /* make a buffer big enough for everything */ + + response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE; + p = response; + LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a" + "Upgrade: WebSocket\x0d\x0a" + "Connection: Upgrade\x0d\x0a" + "Sec-WebSocket-Accept: "); + strcpy(p, (char *)pt->serv_buf); + p += accept_len; + + /* we can only return the protocol header if: + * - one came in, and ... */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) && + /* - it is not an empty string */ + wsi->protocol->name && + wsi->protocol->name[0]) { + LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: "); + p += lws_snprintf(p, 128, "%s", wsi->protocol->name); + } + +#ifndef LWS_NO_EXTENSIONS + /* + * Figure out which extensions the client has that we want to + * enable on this connection, and give him back the list. + * + * Give him a limited write bugdet + */ + if (lws_extension_server_handshake(wsi, &p, 192)) + goto bail; +#endif + LWS_CPYAPP(p, "\x0d\x0a"); + + args.p = p; + args.max_len = ((char *)pt->serv_buf + context->pt_serv_buf_size) - p; + if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, + LWS_CALLBACK_ADD_HEADERS, + wsi->user_space, &args, 0)) + goto bail; + + p = args.p; + + /* end of response packet */ + + LWS_CPYAPP(p, "\x0d\x0a"); + + if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX, + response, p - response)) { + + /* okay send the handshake response accepting the connection */ + + lwsl_parser("issuing resp pkt %d len\n", (int)(p - response)); +#if defined(DEBUG) && ! defined(LWS_WITH_ESP8266) + fwrite(response, 1, p - response, stderr); +#endif + n = lws_write(wsi, (unsigned char *)response, + p - response, LWS_WRITE_HTTP_HEADERS); + if (n != (p - response)) { + lwsl_debug("handshake_0405: ERROR writing to socket\n"); + goto bail; + } + + } + + /* alright clean up and set ourselves into established state */ + + wsi->state = LWSS_ESTABLISHED; + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + + { + const char * uri_ptr = + lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI); + int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI); + const struct lws_http_mount *hit = + lws_find_mount(wsi, uri_ptr, uri_len); + if (hit && hit->cgienv && + wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO, + wsi->user_space, (void *)hit->cgienv, 0)) + return 1; + } + + return 0; + + +bail: + /* caller will free up his parsing allocations */ + return -1; +} + diff -Nru libwebsockets-4.0.20/lib/server/ssl-server.c libwebsockets-2.4.2/lib/server/ssl-server.c --- libwebsockets-4.0.20/lib/server/ssl-server.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/server/ssl-server.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,477 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +extern int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +extern void +lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info); + +#if !defined(LWS_WITH_MBEDTLS) +static int +OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + SSL *ssl; + int n; + struct lws *wsi; + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + + /* + * !!! nasty openssl requires the index to come as a library-scope + * static + */ + wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + + n = wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, + x509_ctx, ssl, preverify_ok); + + /* convert return code from 0 = OK to 1 = OK */ + return !n; +} +#endif + +static int +lws_context_ssl_init_ecdh(struct lws_vhost *vhost) +{ +#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT + EC_KEY *EC_key = NULL; + EVP_PKEY *pkey; + int KeyType; + X509 *x; + + if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH)) + return 0; + + lwsl_notice(" Using ECDH certificate support\n"); + + /* Get X509 certificate from ssl context */ + x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0); + if (!x) { + lwsl_err("%s: x is NULL\n", __func__); + return 1; + } + /* Get the public key from certificate */ + pkey = X509_get_pubkey(x); + if (!pkey) { + lwsl_err("%s: pkey is NULL\n", __func__); + + return 1; + } + /* Get the key type */ + KeyType = EVP_PKEY_type(pkey->type); + + if (EVP_PKEY_EC != KeyType) { + lwsl_notice("Key type is not EC\n"); + return 0; + } + /* Get the key */ + EC_key = EVP_PKEY_get1_EC_KEY(pkey); + /* Set ECDH parameter */ + if (!EC_key) { + lwsl_err("%s: ECDH key is NULL \n", __func__); + return 1; + } + SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, EC_key); + EC_KEY_free(EC_key); +#endif + return 0; +} + +static int +lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ +#if defined(LWS_HAVE_OPENSSL_ECDH_H) && !defined(LWS_WITH_MBEDTLS) + EC_KEY *ecdh; + int ecdh_nid; + const char *ecdh_curve = "prime256v1"; + + if (info->ecdh_curve) + ecdh_curve = info->ecdh_curve; + + ecdh_nid = OBJ_sn2nid(ecdh_curve); + if (NID_undef == ecdh_nid) { + lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve); + return 1; + } + + ecdh = EC_KEY_new_by_curve_name(ecdh_nid); + if (NULL == ecdh) { + lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve); + return 1; + } + SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, ecdh); + EC_KEY_free(ecdh); + + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE); + + lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve); +#else +#if !defined(LWS_WITH_MBEDTLS) + lwsl_notice(" OpenSSL doesn't support ECDH\n"); +#endif +#endif + return 0; +} + +#if !defined(LWS_WITH_MBEDTLS) && defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT) +static int +lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) +{ + struct lws_context *context = (struct lws_context *)arg; + struct lws_vhost *vhost, *vh; + const char *servername; + + if (!ssl) + return SSL_TLSEXT_ERR_NOACK; + + /* + * We can only get ssl accepted connections by using a vhost's ssl_ctx + * find out which listening one took us and only match vhosts on the + * same port. + */ + vh = context->vhost_list; + while (vh) { + if (!vh->being_destroyed && ssl && vh->ssl_ctx == SSL_get_SSL_CTX(ssl)) + break; + vh = vh->vhost_next; + } + + if (!vh) { + assert(vh); /* can't match the incoming vh? */ + return SSL_TLSEXT_ERR_OK; + } + + servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (!servername) { + /* the client doesn't know what hostname it wants */ + lwsl_info("SNI: Unknown ServerName: %s\n", servername); + + return SSL_TLSEXT_ERR_OK; + } + + vhost = lws_select_vhost(context, vh->listen_port, servername); + if (!vhost) { + lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port); + + return SSL_TLSEXT_ERR_OK; + } + + lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port); + + /* select the ssl ctx from the selected vhost for this conn */ + SSL_set_SSL_CTX(ssl, vhost->ssl_ctx); + + return SSL_TLSEXT_ERR_OK; +} +#endif + +LWS_VISIBLE int +lws_context_init_server_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ + struct lws_context *context = vhost->context; + struct lws wsi; + unsigned long error; + int n; + + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { + vhost->use_ssl = 0; + return 0; + } + + /* + * If he is giving a cert filepath, take it as a sign he wants to use + * it on this vhost. User code can leave the cert filepath NULL and + * set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in + * which case he's expected to set up the cert himself at + * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which + * provides the vhost SSL_CTX * in the user parameter. + */ + if (info->ssl_cert_filepath) + info->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; + + if (info->port != CONTEXT_PORT_NO_LISTEN) { + + vhost->use_ssl = lws_check_opt(info->options, + LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX); + + if (vhost->use_ssl && info->ssl_cipher_list) + lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list); + + if (vhost->use_ssl) + lwsl_notice(" Using SSL mode\n"); + else + lwsl_notice(" Using non-SSL mode\n"); + } + + /* + * give him a fake wsi with context + vhost set, so he can use + * lws_get_context() in the callback + */ + memset(&wsi, 0, sizeof(wsi)); + wsi.vhost = vhost; + wsi.context = context; + + (void)n; + (void)error; + + /* + * Firefox insists on SSLv23 not SSLv3 + * Konq disables SSLv2 by default now, SSLv23 works + * + * SSLv23_server_method() is the openssl method for "allow all TLS + * versions", compared to e.g. TLSv1_2_server_method() which only allows + * tlsv1.2. Unwanted versions must be disabled using SSL_CTX_set_options() + */ +#if !defined(LWS_WITH_MBEDTLS) + { + SSL_METHOD *method; + + method = (SSL_METHOD *)SSLv23_server_method(); + if (!method) { + error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } + vhost->ssl_ctx = SSL_CTX_new(method); /* create context */ + if (!vhost->ssl_ctx) { + error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } + } +#else + { + const SSL_METHOD *method = TLSv1_2_server_method(); + + vhost->ssl_ctx = SSL_CTX_new(method); /* create context */ + if (!vhost->ssl_ctx) { + lwsl_err("problem creating ssl context\n"); + return 1; + } + + } +#endif +#if !defined(LWS_WITH_MBEDTLS) + + /* associate the lws context with the SSL_CTX */ + + SSL_CTX_set_ex_data(vhost->ssl_ctx, + openssl_SSL_CTX_private_data_index, (char *)vhost->context); + /* Disable SSLv2 and SSLv3 */ + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION); +#endif + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + + if (info->ssl_cipher_list) + SSL_CTX_set_cipher_list(vhost->ssl_ctx, + info->ssl_cipher_list); +#endif + + /* as a server, are we requiring clients to identify themselves? */ + + if (lws_check_opt(info->options, + LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) { + int verify_options = SSL_VERIFY_PEER; + + if (!lws_check_opt(info->options, + LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED)) + verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + +#if !defined(LWS_WITH_MBEDTLS) + SSL_CTX_set_session_id_context(vhost->ssl_ctx, + (unsigned char *)context, sizeof(void *)); + + /* absolutely require the client cert */ + + SSL_CTX_set_verify(vhost->ssl_ctx, + verify_options, OpenSSL_verify_callback); +#endif + } + +#if !defined(LWS_WITH_MBEDTLS) && !defined(OPENSSL_NO_TLSEXT) + SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx, + lws_ssl_server_name_cb); + SSL_CTX_set_tlsext_servername_arg(vhost->ssl_ctx, context); +#endif + + /* + * give user code a chance to load certs into the server + * allowing it to verify incoming client certs + */ +#if !defined(LWS_WITH_MBEDTLS) + if (info->ssl_ca_filepath && + !SSL_CTX_load_verify_locations(vhost->ssl_ctx, + info->ssl_ca_filepath, NULL)) { + lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__); + } +#endif + if (vhost->use_ssl) { + if (lws_context_ssl_init_ecdh_curve(info, vhost)) + return -1; + + vhost->protocols[0].callback(&wsi, + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, + vhost->ssl_ctx, NULL, 0); + } + + if (lws_check_opt(info->options, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT)) + /* Normally SSL listener rejects non-ssl, optionally allow */ + vhost->allow_non_ssl_on_ssl_port = 1; + + if (info->ssl_options_set) + SSL_CTX_set_options(vhost->ssl_ctx, info->ssl_options_set); + +/* SSL_clear_options introduced in 0.9.8m */ +#if !defined(LWS_WITH_MBEDTLS) +#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL) + if (info->ssl_options_clear) + SSL_CTX_clear_options(vhost->ssl_ctx, info->ssl_options_clear); +#endif +#endif + + lwsl_info(" SSL options 0x%lX\n", SSL_CTX_get_options(vhost->ssl_ctx)); + + if (vhost->use_ssl && info->ssl_cert_filepath) { + /* + * The user code can choose to either pass the cert and + * key filepaths using the info members like this, or it can + * leave them NULL; force the vhost SSL_CTX init using the info + * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and + * set up the cert himself using the user callback + * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which + * happened just above and has the vhost SSL_CTX * in the user + * parameter. + */ +#if !defined(LWS_WITH_MBEDTLS) + /* set the local certificate from CertFile */ + n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx, + info->ssl_cert_filepath); + if (n != 1) { + error = ERR_get_error(); + lwsl_err("problem getting cert '%s' %lu: %s\n", + info->ssl_cert_filepath, + error, + ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } + lws_ssl_bind_passphrase(vhost->ssl_ctx, info); +#else + uint8_t *p; + lws_filepos_t flen; + int err; + + if (alloc_pem_to_der_file(vhost->context, info->ssl_cert_filepath, &p, + &flen)) { + lwsl_err("couldn't find cert file %s\n", + info->ssl_cert_filepath); + + return 1; + } + err = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, flen, p); + if (!err) { + lwsl_err("Problem loading cert\n"); + return 1; + } +#if !defined(LWS_WITH_ESP32) + free(p); + p = NULL; +#endif + + if (info->ssl_private_key_filepath) { + if (alloc_pem_to_der_file(vhost->context, + info->ssl_private_key_filepath, &p, &flen)) { + lwsl_err("couldn't find cert file %s\n", + info->ssl_cert_filepath); + + return 1; + } + err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen); + if (!err) { + lwsl_err("Problem loading key\n"); + + return 1; + } + } + +#if !defined(LWS_WITH_ESP32) + free(p); + p = NULL; +#endif +#endif + if (info->ssl_private_key_filepath != NULL) { +#if !defined(LWS_WITH_MBEDTLS) + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx, + info->ssl_private_key_filepath, + SSL_FILETYPE_PEM) != 1) { + error = ERR_get_error(); + lwsl_err("ssl problem getting key '%s' %lu: %s\n", + info->ssl_private_key_filepath, error, + ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } +#endif + } else + if (vhost->protocols[0].callback(&wsi, + LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, + vhost->ssl_ctx, NULL, 0)) { + lwsl_err("ssl private key not set\n"); + + return 1; + } +#if !defined(LWS_WITH_MBEDTLS) + /* verify private key */ + if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + return 1; + } +#endif + } + if (vhost->use_ssl) { + if (lws_context_ssl_init_ecdh(vhost)) + return 1; + + /* + * SSL is happy and has a cert it's content with + * If we're supporting HTTP2, initialize that + */ + lws_context_init_http2_ssl(vhost); + } + + return 0; +} + diff -Nru libwebsockets-4.0.20/lib/service.c libwebsockets-2.4.2/lib/service.c --- libwebsockets-4.0.20/lib/service.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/service.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1714 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +static int +lws_calllback_as_writeable(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int n; + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB, 1); +#if defined(LWS_WITH_STATS) + if (wsi->active_writable_req_us) { + uint64_t ul = time_in_microseconds() - + wsi->active_writable_req_us; + + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_MS_WRITABLE_DELAY, ul); + lws_stats_atomic_max(wsi->context, pt, + LWSSTATS_MS_WORST_WRITABLE_DELAY, ul); + wsi->active_writable_req_us = 0; + } +#endif + + switch (wsi->mode) { + case LWSCM_RAW: + n = LWS_CALLBACK_RAW_WRITEABLE; + break; + case LWSCM_RAW_FILEDESC: + n = LWS_CALLBACK_RAW_WRITEABLE_FILE; + break; + case LWSCM_WS_CLIENT: + n = LWS_CALLBACK_CLIENT_WRITEABLE; + break; + case LWSCM_WSCL_ISSUE_HTTP_BODY: + n = LWS_CALLBACK_CLIENT_HTTP_WRITEABLE; + break; + case LWSCM_WS_SERVING: + n = LWS_CALLBACK_SERVER_WRITEABLE; + break; + default: + n = LWS_CALLBACK_HTTP_WRITEABLE; + break; + } + + return user_callback_handle_rxflow(wsi->protocol->callback, + wsi, (enum lws_callback_reasons) n, + wsi->user_space, NULL, 0); +} + +LWS_VISIBLE int +lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) +{ + int write_type = LWS_WRITE_PONG; + struct lws_tokens eff_buf; +#ifdef LWS_WITH_HTTP2 + struct lws **wsi2, *wsi2a; +#endif + int ret, m, n; + + wsi->leave_pollout_active = 0; + wsi->handling_pollout = 1; + /* + * if another thread wants POLLOUT on us, from here on while + * handling_pollout is set, he will only set leave_pollout_active. + * If we are going to disable POLLOUT, we will check that first. + */ + + /* + * user callback is lowest priority to get these notifications + * actually, since other pending things cannot be disordered + */ + + /* Priority 1: pending truncated sends are incomplete ws fragments + * If anything else sent first the protocol would be + * corrupted. + */ + if (wsi->trunc_len) { + //lwsl_notice("%s: completing partial\n", __func__); + if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset, + wsi->trunc_len) < 0) { + lwsl_info("%s signalling to close\n", __func__); + goto bail_die; + } + /* leave POLLOUT active either way */ + goto bail_ok; + } else + if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) { + wsi->socket_is_permanently_unusable = 1; + goto bail_die; /* retry closing now */ + } + + if (wsi->mode == LWSCM_WSCL_ISSUE_HTTP_BODY) + goto user_service; + +#ifdef LWS_WITH_HTTP2 + /* + * Priority 2: protocol packets + */ + if (wsi->upgraded_to_http2 && wsi->u.h2.h2n->pps) { + lwsl_info("servicing pps\n"); + if (lws_h2_do_pps_send(wsi)) { + wsi->socket_is_permanently_unusable = 1; + goto bail_die; + } + if (wsi->u.h2.h2n->pps) + goto bail_ok; + + /* we can resume whatever we were doing */ + lws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_ENABLE | + LWS_RXFLOW_REASON_H2_PPS_PENDING); + + goto bail_ok; /* leave POLLOUT active */ + } +#endif + +#ifdef LWS_WITH_CGI + if (wsi->cgi) { + /* also one shot */ + if (pollfd) + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + return 1; + } + goto user_service_go_again; + } +#endif + + /* Priority 3: pending control packets (pong or close) + * + * 3a: close notification packet requested from close api + */ + + if (wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION) { + lwsl_debug("sending close packet\n"); + wsi->waiting_to_send_close_frame = 0; + n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE], + wsi->u.ws.close_in_ping_buffer_len, + LWS_WRITE_CLOSE); + if (n >= 0) { + wsi->state = LWSS_AWAITING_CLOSE_ACK; + lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1); + lwsl_debug("sent close indication, awaiting ack\n"); + + goto bail_ok; + } + + goto bail_die; + } + + /* else, the send failed and we should just hang up */ + + if ((wsi->state == LWSS_ESTABLISHED && + wsi->u.ws.ping_pending_flag) || + (wsi->state == LWSS_RETURNED_CLOSE_ALREADY && + wsi->u.ws.payload_is_close)) { + + if (wsi->u.ws.payload_is_close) + write_type = LWS_WRITE_CLOSE; + + n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE], + wsi->u.ws.ping_payload_len, write_type); + if (n < 0) + goto bail_die; + + /* well he is sent, mark him done */ + wsi->u.ws.ping_pending_flag = 0; + if (wsi->u.ws.payload_is_close) + /* oh... a close frame was it... then we are done */ + goto bail_die; + + /* otherwise for PING, leave POLLOUT active either way */ + goto bail_ok; + } + + if (wsi->state == LWSS_ESTABLISHED && + !wsi->socket_is_permanently_unusable && + wsi->u.ws.send_check_ping) { + + lwsl_info("issuing ping on wsi %p\n", wsi); + wsi->u.ws.send_check_ping = 0; + n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE], + 0, LWS_WRITE_PING); + if (n < 0) + goto bail_die; + + /* + * we apparently were able to send the PING in a reasonable time + * now reset the clock on our peer to be able to send the + * PONG in a reasonable time. + */ + + lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG, + wsi->context->timeout_secs); + + goto bail_ok; + } + + /* Priority 4: if we are closing, not allowed to send more data frags + * which means user callback or tx ext flush banned now + */ + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY) + goto user_service; + + /* Priority 5: Tx path extension with more to send + * + * These are handled as new fragments each time around + * So while we must block new writeable callback to enforce + * payload ordering, but since they are always complete + * fragments control packets can interleave OK. + */ + if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) { + lwsl_ext("SERVICING TX EXT DRAINING\n"); + if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0) + goto bail_die; + /* leave POLLOUT active */ + goto bail_ok; + } + + /* Priority 6: user can get the callback + */ + m = lws_ext_cb_active(wsi, LWS_EXT_CB_IS_WRITEABLE, NULL, 0); + if (m) + goto bail_die; +#ifndef LWS_NO_EXTENSIONS + if (!wsi->extension_data_pending) + goto user_service; +#endif + /* + * check in on the active extensions, see if they + * had pending stuff to spill... they need to get the + * first look-in otherwise sequence will be disordered + * + * NULL, zero-length eff_buf means just spill pending + */ + + ret = 1; + if (wsi->mode == LWSCM_RAW || wsi->mode == LWSCM_RAW_FILEDESC) + ret = 0; + + while (ret == 1) { + + /* default to nobody has more to spill */ + + ret = 0; + eff_buf.token = NULL; + eff_buf.token_len = 0; + + /* give every extension a chance to spill */ + + m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND, + &eff_buf, 0); + if (m < 0) { + lwsl_err("ext reports fatal error\n"); + goto bail_die; + } + if (m) + /* + * at least one extension told us he has more + * to spill, so we will go around again after + */ + ret = 1; + + /* assuming they gave us something to send, send it */ + + if (eff_buf.token_len) { + n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) { + lwsl_info("closing from POLLOUT spill\n"); + goto bail_die; + } + /* + * Keep amount spilled small to minimize chance of this + */ + if (n != eff_buf.token_len) { + lwsl_err("Unable to spill ext %d vs %d\n", + eff_buf.token_len, n); + goto bail_die; + } + } else + continue; + + /* no extension has more to spill */ + + if (!ret) + continue; + + /* + * There's more to spill from an extension, but we just sent + * something... did that leave the pipe choked? + */ + + if (!lws_send_pipe_choked(wsi)) + /* no we could add more */ + continue; + + lwsl_info("choked in POLLOUT service\n"); + + /* + * Yes, he's choked. Leave the POLLOUT masked on so we will + * come back here when he is unchoked. Don't call the user + * callback to enforce ordering of spilling, he'll get called + * when we come back here and there's nothing more to spill. + */ + + goto bail_ok; + } +#ifndef LWS_NO_EXTENSIONS + wsi->extension_data_pending = 0; +#endif +user_service: + /* one shot */ + + if (wsi->parent_carries_io) { + wsi->handling_pollout = 0; + wsi->leave_pollout_active = 0; + + return lws_calllback_as_writeable(wsi); + } + + if (pollfd) { + int eff = wsi->leave_pollout_active; + + if (!eff) + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + goto bail_die; + } + + wsi->handling_pollout = 0; + + /* cannot get leave_pollout_active set after the above */ + if (!eff && wsi->leave_pollout_active) + /* got set inbetween sampling eff and clearing + * handling_pollout, force POLLOUT on */ + lws_calllback_as_writeable(wsi); + + wsi->leave_pollout_active = 0; + } + + if (wsi->mode != LWSCM_WSCL_ISSUE_HTTP_BODY && + !wsi->hdr_parsing_completed) + goto bail_ok; + + +#ifdef LWS_WITH_CGI +user_service_go_again: +#endif + +#ifdef LWS_WITH_HTTP2 + /* + * we are the 'network wsi' for potentially many muxed child wsi with + * no network connection of their own, who have to use us for all their + * network actions. So we use a round-robin scheme to share out the + * POLLOUT notifications to our children. + * + * But because any child could exhaust the socket's ability to take + * writes, we can only let one child get notified each time. + * + * In addition children may be closed / deleted / added between POLLOUT + * notifications, so we can't hold pointers + */ + + if (wsi->mode != LWSCM_HTTP2_SERVING) { + lwsl_info("%s: non http2\n", __func__); + goto notify; + } + + wsi->u.h2.requested_POLLOUT = 0; + if (!wsi->u.h2.initialized) { + lwsl_info("pollout on uninitialized http2 conn\n"); + goto bail_ok; + } + +// if (SSL_want_read(wsi->ssl) || SSL_want_write(wsi->ssl)) { +// lws_callback_on_writable(wsi); +// goto bail_ok; +// } + + lwsl_info("%s: %p: children waiting for POLLOUT service:\n", __func__, wsi); + wsi2a = wsi->u.h2.child_list; + while (wsi2a) { + if (wsi2a->u.h2.requested_POLLOUT) + lwsl_debug(" * %p\n", wsi2a); + else + lwsl_debug(" %p\n", wsi2a); + + wsi2a = wsi2a->u.h2.sibling_list; + } + + wsi2 = &wsi->u.h2.child_list; + if (!*wsi2) + goto bail_ok; + + do { + struct lws *w, **wa; + + wa = &(*wsi2)->u.h2.sibling_list; + if (!(*wsi2)->u.h2.requested_POLLOUT) { + lwsl_debug(" child %p doesn't want POLLOUT\n", *wsi2); + goto next_child; + } + + /* + * we're going to do writable callback for this child. + * move him to be the last child + */ + + lwsl_debug("servicing child %p\n", *wsi2); + + w = *wsi2; + while (w) { + if (!w->u.h2.sibling_list) { /* w is the current last */ + lwsl_debug("w=%p, *wsi2 = %p\n", w, *wsi2); + if (w == *wsi2) /* we are already last */ + break; + w->u.h2.sibling_list = *wsi2; /* last points to us as new last */ + *wsi2 = (*wsi2)->u.h2.sibling_list; /* guy pointing to us until now points to our old next */ + w->u.h2.sibling_list->u.h2.sibling_list = NULL; /* we point to nothing because we are last */ + w = w->u.h2.sibling_list; /* w becomes us */ + break; + } + w = w->u.h2.sibling_list; + } + + w->u.h2.requested_POLLOUT = 0; + lwsl_info("%s: child %p (state %d)\n", __func__, (*wsi2), (*wsi2)->state); + + if (w->u.h2.pending_status_body) { + w->u.h2.send_END_STREAM = 1; + n = lws_write(w, + (uint8_t *)w->u.h2.pending_status_body + LWS_PRE, + strlen(w->u.h2.pending_status_body + LWS_PRE), + LWS_WRITE_HTTP_FINAL); + lws_free_set_NULL(w->u.h2.pending_status_body); + lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS); + wa = &wsi->u.h2.child_list; + goto next_child; + } + + if (w->state == LWSS_HTTP_ISSUING_FILE) { + + w->leave_pollout_active = 0; + + /* >0 == completion, <0 == error + * + * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when + * it's done. That's the case even if we just completed the + * send, so wait for that. + */ + n = lws_serve_http_file_fragment(w); + lwsl_debug("lws_serve_http_file_fragment says %d\n", n); + + /* + * We will often hear about out having sent the final + * DATA here... if so close the actual wsi + */ + if (n < 0 || w->u.h2.send_END_STREAM) { + lwsl_debug("Closing POLLOUT child %p\n", w); + lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS); + wa = &wsi->u.h2.child_list; + goto next_child; + } + if (n > 0) + if (lws_http_transaction_completed(w)) + goto bail_die; + if (!n) { + lws_callback_on_writable(w); + (w)->u.h2.requested_POLLOUT = 1; + } + + goto next_child; + } + + if (lws_calllback_as_writeable(w) || w->u.h2.send_END_STREAM) { + lwsl_debug("Closing POLLOUT child\n"); + lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS); + wa = &wsi->u.h2.child_list; + } + +next_child: + wsi2 = wa; + } while (wsi2 && *wsi2 && !lws_send_pipe_choked(wsi)); + + lwsl_info("%s: %p: children waiting for POLLOUT service: %p\n", __func__, wsi, wsi->u.h2.child_list); + wsi2a = wsi->u.h2.child_list; + while (wsi2a) { + if (wsi2a->u.h2.requested_POLLOUT) + lwsl_debug(" * %p\n", wsi2a); + else + lwsl_debug(" %p\n", wsi2a); + + wsi2a = wsi2a->u.h2.sibling_list; + } + + + wsi2a = wsi->u.h2.child_list; + while (wsi2a) { + if (wsi2a->u.h2.requested_POLLOUT) { + lws_change_pollfd(wsi, 0, LWS_POLLOUT); + break; + } + wsi2a = wsi2a->u.h2.sibling_list; + } + + goto bail_ok; + + +notify: +#endif + wsi->leave_pollout_active = 0; + + n = lws_calllback_as_writeable(wsi); + wsi->handling_pollout = 0; + + if (wsi->leave_pollout_active) + lws_change_pollfd(wsi, 0, LWS_POLLOUT); + + return n; + + /* + * since these don't disable the POLLOUT, they are always doing the + * right thing for leave_pollout_active whether it was set or not. + */ + +bail_ok: + wsi->handling_pollout = 0; + wsi->leave_pollout_active = 0; + + return 0; + +bail_die: + wsi->handling_pollout = 0; + wsi->leave_pollout_active = 0; + + return -1; +} + +int +lws_service_timeout_check(struct lws *wsi, unsigned int sec) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int n = 0; + + (void)n; + + /* + * if extensions want in on it (eg, we are a mux parent) + * give them a chance to service child timeouts + */ + if (lws_ext_cb_active(wsi, LWS_EXT_CB_1HZ, NULL, sec) < 0) + return 0; + + if (!wsi->pending_timeout) + return 0; + + /* + * if we went beyond the allowed time, kill the + * connection + */ + if ((time_t)sec > wsi->pending_timeout_limit) { + + if (wsi->desc.sockfd != LWS_SOCK_INVALID && + wsi->position_in_fds_table >= 0) + n = pt->fds[wsi->position_in_fds_table].events; + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_TIMEOUTS, 1); + + /* no need to log normal idle keepalive timeout */ + if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE) + lwsl_info("wsi %p: TIMEDOUT WAITING on %d " + "(did hdr %d, ah %p, wl %d, pfd " + "events %d) %llu vs %llu\n", + (void *)wsi, wsi->pending_timeout, + wsi->hdr_parsing_completed, wsi->u.hdr.ah, + pt->ah_wait_list_length, n, + (unsigned long long)sec, + (unsigned long long)wsi->pending_timeout_limit); + + /* + * Since he failed a timeout, he already had a chance to do + * something and was unable to... that includes situations like + * half closed connections. So process this "failed timeout" + * close as a violent death and don't try to do protocol + * cleanup like flush partials. + */ + wsi->socket_is_permanently_unusable = 1; + if (wsi->mode == LWSCM_WSCL_WAITING_SSL) + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, + (void *)"Timed out waiting SSL", 21); + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return 1; + } + + return 0; +} + +int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len) +{ +#if defined(LWS_WITH_HTTP2) + if (wsi->upgraded_to_http2) { + struct lws_h2_netconn *h2n = wsi->u.h2.h2n; + + assert(h2n->rx_scratch); + buf += n; + len -= n; + assert ((char *)buf >= (char *)h2n->rx_scratch && + (char *)&buf[len] <= (char *)&h2n->rx_scratch[LWS_H2_RX_SCRATCH_SIZE]); + + h2n->rx_scratch_pos = ((char *)buf - (char *)h2n->rx_scratch); + h2n->rx_scratch_len = len; + + lwsl_info("%s: %p: pausing h2 rx_scratch\n", __func__, wsi); + + return 0; + } +#endif + /* his RX is flowcontrolled, don't send remaining now */ + if (wsi->rxflow_buffer) { + if (buf >= wsi->rxflow_buffer && + &buf[len - 1] < &wsi->rxflow_buffer[wsi->rxflow_len]) { + /* rxflow while we were spilling prev rxflow */ + lwsl_info("%s: staying in rxflow buf\n", __func__); + return 1; + } else { + lwsl_err("%s: conflicting rxflow buf, " + "current %p len %d, new %p len %d\n", __func__, + wsi->rxflow_buffer, wsi->rxflow_len, buf, len); + assert(0); + return 1; + } + } + + /* a new rxflow, buffer it and warn caller */ + lwsl_info("%s: new rxflow input buffer len %d\n", __func__, len - n); + wsi->rxflow_buffer = lws_malloc(len - n, "rxflow buf"); + if (!wsi->rxflow_buffer) + return -1; + + wsi->rxflow_len = len - n; + wsi->rxflow_pos = 0; + memcpy(wsi->rxflow_buffer, buf + n, len - n); + + return 0; +} + +/* this is used by the platform service code to stop us waiting for network + * activity in poll() when we have something that already needs service + */ + +LWS_VISIBLE LWS_EXTERN int +lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct allocated_headers *ah; + + /* Figure out if we really want to wait in poll() + * We only need to wait if really nothing already to do and we have + * to wait for something from network + */ + + /* 1) if we know we are draining rx ext, do not wait in poll */ + if (pt->rx_draining_ext_list) + return 0; + +#ifdef LWS_OPENSSL_SUPPORT + /* 2) if we know we have non-network pending data, do not wait in poll */ + if (lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) { + lwsl_info("ssl buffered read\n"); + return 0; + } +#endif + + /* 3) if any ah has pending rx, do not wait in poll */ + ah = pt->ah_list; + while (ah) { + if (ah->rxpos != ah->rxlen) { + if (!ah->wsi) { + assert(0); + } + return 0; + } + ah = ah->next; + } + + return timeout_ms; +} + +/* + * guys that need POLLIN service again without waiting for network action + * can force POLLIN here if not flowcontrolled, so they will get service. + * + * Return nonzero if anybody got their POLLIN faked + */ +int +lws_service_flag_pending(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct allocated_headers *ah; +#ifdef LWS_OPENSSL_SUPPORT + struct lws *wsi_next; +#endif + struct lws *wsi; + int forced = 0; + + /* POLLIN faking */ + + /* + * 1) For all guys with already-available ext data to drain, if they are + * not flowcontrolled, fake their POLLIN status + */ + wsi = pt->rx_draining_ext_list; + while (wsi) { + pt->fds[wsi->position_in_fds_table].revents |= + pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; + if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) { + forced = 1; + break; + } + wsi = wsi->u.ws.rx_draining_ext_list; + } + +#ifdef LWS_OPENSSL_SUPPORT + /* + * 2) For all guys with buffered SSL read data already saved up, if they + * are not flowcontrolled, fake their POLLIN status so they'll get + * service to use up the buffered incoming data, even though their + * network socket may have nothing + */ + wsi = pt->pending_read_list; + while (wsi) { + wsi_next = wsi->pending_read_list_next; + pt->fds[wsi->position_in_fds_table].revents |= + pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; + if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) { + forced = 1; + /* + * he's going to get serviced now, take him off the + * list of guys with buffered SSL. If he still has some + * at the end of the service, he'll get put back on the + * list then. + */ + lws_ssl_remove_wsi_from_buffered_list(wsi); + } + + wsi = wsi_next; + } +#endif + /* + * 3) For any wsi who have an ah with pending RX who did not + * complete their current headers, and are not flowcontrolled, + * fake their POLLIN status so they will be able to drain the + * rx buffered in the ah + */ + ah = pt->ah_list; + while (ah) { + if (ah->rxpos != ah->rxlen && !ah->wsi->hdr_parsing_completed) { + pt->fds[ah->wsi->position_in_fds_table].revents |= + pt->fds[ah->wsi->position_in_fds_table].events & + LWS_POLLIN; + if (pt->fds[ah->wsi->position_in_fds_table].revents & + LWS_POLLIN) { + forced = 1; + break; + } + } + ah = ah->next; + } + + return forced; +} + +#ifndef LWS_NO_CLIENT + +LWS_VISIBLE int +lws_http_client_read(struct lws *wsi, char **buf, int *len) +{ + int rlen, n; + + rlen = lws_ssl_capable_read(wsi, (unsigned char *)*buf, *len); + *len = 0; + + /* allow the source to signal he has data again next time */ + lws_change_pollfd(wsi, 0, LWS_POLLIN); + + if (rlen == LWS_SSL_CAPABLE_ERROR) { + lwsl_notice("%s: SSL capable error\n", __func__); + return -1; + } + + if (rlen == 0) + return -1; + + if (rlen < 0) + return 0; + + *len = rlen; + wsi->client_rx_avail = 0; + + /* + * server may insist on transfer-encoding: chunked, + * so http client must deal with it + */ +spin_chunks: + while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) { + switch (wsi->chunk_parser) { + case ELCP_HEX: + if ((*buf)[0] == '\x0d') { + wsi->chunk_parser = ELCP_CR; + break; + } + n = char_to_hex((*buf)[0]); + if (n < 0) { + lwsl_debug("chunking failure\n"); + return -1; + } + wsi->chunk_remaining <<= 4; + wsi->chunk_remaining |= n; + break; + case ELCP_CR: + if ((*buf)[0] != '\x0a') { + lwsl_debug("chunking failure\n"); + return -1; + } + wsi->chunk_parser = ELCP_CONTENT; + lwsl_info("chunk %d\n", wsi->chunk_remaining); + if (wsi->chunk_remaining) + break; + lwsl_info("final chunk\n"); + goto completed; + + case ELCP_CONTENT: + break; + + case ELCP_POST_CR: + if ((*buf)[0] != '\x0d') { + lwsl_debug("chunking failure\n"); + + return -1; + } + + wsi->chunk_parser = ELCP_POST_LF; + break; + + case ELCP_POST_LF: + if ((*buf)[0] != '\x0a') + return -1; + + wsi->chunk_parser = ELCP_HEX; + wsi->chunk_remaining = 0; + break; + } + (*buf)++; + (*len)--; + } + + if (wsi->chunked && !wsi->chunk_remaining) + return 0; + + if (wsi->u.http.rx_content_remain && + wsi->u.http.rx_content_remain < *len) + n = (int)wsi->u.http.rx_content_remain; + else + n = *len; + + if (wsi->chunked && wsi->chunk_remaining && + wsi->chunk_remaining < n) + n = wsi->chunk_remaining; + +#ifdef LWS_WITH_HTTP_PROXY + /* hubbub */ + if (wsi->perform_rewrite) + lws_rewrite_parse(wsi->rw, (unsigned char *)*buf, n); + else +#endif + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, + wsi->user_space, *buf, n)) { + lwsl_debug("%s: LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ returned -1\n", __func__); + + return -1; + } + + if (wsi->chunked && wsi->chunk_remaining) { + (*buf) += n; + wsi->chunk_remaining -= n; + *len -= n; + } + + if (wsi->chunked && !wsi->chunk_remaining) + wsi->chunk_parser = ELCP_POST_CR; + + if (wsi->chunked && *len) + goto spin_chunks; + + if (wsi->chunked) + return 0; + + /* if we know the content length, decrement the content remaining */ + if (wsi->u.http.rx_content_length > 0) + wsi->u.http.rx_content_remain -= n; + + if (wsi->u.http.rx_content_remain || !wsi->u.http.rx_content_length) + return 0; + +completed: + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP, + wsi->user_space, NULL, 0)) { + lwsl_debug("Completed call returned -1\n"); + return -1; + } + + if (lws_http_transaction_completed_client(wsi)) { + lwsl_notice("%s: transaction completed says -1\n", __func__); + return -1; + } + + return 0; +} +#endif + +static int +lws_is_ws_with_ext(struct lws *wsi) +{ +#if defined(LWS_NO_EXTENSIONS) + return 0; +#else + return wsi->state == LWSS_ESTABLISHED && + !!wsi->count_act_ext; +#endif +} + +LWS_VISIBLE int +lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + lws_sockfd_type our_fd = 0, tmp_fd; + struct allocated_headers *ah; + struct lws_tokens eff_buf; + unsigned int pending = 0; + struct lws *wsi, *wsi1; + char draining_flow = 0; + int timed_out = 0; + time_t now; + int n = 0, m; + int more; + + if (!context->protocol_init_done) + lws_protocol_init(context); + + time(&now); + + /* + * handle case that system time was uninitialized when lws started + * at boot, and got initialized a little later + */ + if (context->time_up < 1464083026 && now > 1464083026) + context->time_up = now; + + /* TODO: if using libev, we should probably use timeout watchers... */ + if (context->last_timeout_check_s != now) { + context->last_timeout_check_s = now; + +#if defined(LWS_WITH_STATS) + if (!tsi && now - context->last_dump > 10) { + lws_stats_log_dump(context); + context->last_dump = now; + } +#endif + + lws_plat_service_periodic(context); + + lws_check_deferred_free(context, 0); + +#if defined(LWS_WITH_PEER_LIMITS) + lws_peer_cull_peer_wait_list(context); +#endif + + /* retire unused deprecated context */ +#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_ESP32) +#if LWS_POSIX && !defined(_WIN32) + if (context->deprecated && !context->count_wsi_allocated) { + lwsl_notice("%s: ending deprecated context\n", __func__); + kill(getpid(), SIGINT); + return 0; + } +#endif +#endif + /* global timeout check once per second */ + + if (pollfd) + our_fd = pollfd->fd; + + /* + * Phase 1: check every wsi on the timeout check list + */ + + wsi = context->pt[tsi].timeout_list; + while (wsi) { + /* we have to take copies, because he may be deleted */ + wsi1 = wsi->timeout_list; + tmp_fd = wsi->desc.sockfd; + if (lws_service_timeout_check(wsi, (unsigned int)now)) { + /* he did time out... */ + if (tmp_fd == our_fd) + /* it was the guy we came to service! */ + timed_out = 1; + /* he's gone, no need to mark as handled */ + } + wsi = wsi1; + } + + /* + * Phase 2: double-check active ah timeouts independent of wsi + * timeout status + */ + + ah = pt->ah_list; + while (ah) { + int len; + char buf[256]; + const unsigned char *c; + + if (!ah->in_use || !ah->wsi || !ah->assigned || + (ah->wsi->vhost && now - ah->assigned < + ah->wsi->vhost->timeout_secs_ah_idle + 60)) { + 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'; + lws_get_peer_simple(wsi, buf, sizeof(buf)); + lwsl_notice("ah excessive hold: wsi %p\n" + " peer address: %s\n" + " ah rxpos %u, rxlen %u, pos %u\n", + wsi, buf, ah->rxpos, ah->rxlen, + ah->pos); + buf[0] = '\0'; + m = 0; + do { + c = lws_token_to_string(m); + if (!c) + break; + if (!(*c)) + break; + + len = lws_hdr_total_length(wsi, m); + if (!len || len > sizeof(buf) - 1) { + m++; + continue; + } + + if (lws_hdr_copy(wsi, buf, + sizeof buf, m) > 0) { + buf[sizeof(buf) - 1] = '\0'; + + lwsl_notice(" %s = %s\n", + (const char *)c, buf); + } + m++; + } while (1); + + /* explicitly detach the ah */ + + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 0); + + /* ... and then drop the connection */ + + if (wsi->desc.sockfd == our_fd) + /* it was the guy we came to service! */ + timed_out = 1; + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + ah = pt->ah_list; + } + +#ifdef LWS_WITH_CGI + /* + * Phase 3: handle cgi timeouts + */ + lws_cgi_kill_terminated(pt); +#endif +#if 0 + { + char s[300], *p = s; + + for (n = 0; n < context->count_threads; n++) + p += sprintf(p, " %7lu (%5d), ", + context->pt[n].count_conns, + context->pt[n].fds_count); + + lwsl_notice("load: %s\n", s); + } +#endif + } + + /* + * at intervals, check for ws connections needing ping-pong checks + */ + + if (context->ws_ping_pong_interval && + context->last_ws_ping_pong_check_s < now + 10) { + struct lws_vhost *vh = context->vhost_list; + context->last_ws_ping_pong_check_s = now; + + while (vh) { + for (n = 0; n < vh->count_protocols; n++) { + wsi = vh->same_vh_protocol_list[n]; + + while (wsi) { + if (wsi->state == LWSS_ESTABLISHED && + !wsi->socket_is_permanently_unusable && + !wsi->u.ws.send_check_ping && + wsi->u.ws.time_next_ping_check && + wsi->u.ws.time_next_ping_check < now) { + + lwsl_info("requesting ping-pong on wsi %p\n", wsi); + wsi->u.ws.send_check_ping = 1; + lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING, + context->timeout_secs); + lws_callback_on_writable(wsi); + wsi->u.ws.time_next_ping_check = now + + wsi->context->ws_ping_pong_interval; + } + wsi = wsi->same_vh_protocol_next; + } + } + vh = vh->vhost_next; + } + } + + + /* the socket we came to service timed out, nothing to do */ + if (timed_out) + return 0; + + /* just here for timeout management? */ + if (!pollfd) + return 0; + + /* no, here to service a socket descriptor */ + wsi = wsi_from_fd(context, pollfd->fd); + if (!wsi) + /* not lws connection ... leave revents alone and return */ + return 0; + + /* + * so that caller can tell we handled, past here we need to + * zero down pollfd->revents after handling + */ + +#if LWS_POSIX + /* handle session socket closed */ + + if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) && + (pollfd->revents & LWS_POLLHUP)) { + wsi->socket_is_permanently_unusable = 1; + lwsl_debug("Session Socket %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + goto close_and_handled; + } + +#ifdef _WIN32 + if (pollfd->revents & LWS_POLLOUT) + wsi->sock_send_blocking = FALSE; +#endif + +#endif + + if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) && + (pollfd->revents & LWS_POLLHUP)) { + lwsl_debug("pollhup\n"); + wsi->socket_is_permanently_unusable = 1; + goto close_and_handled; + } + +#ifdef LWS_OPENSSL_SUPPORT + if ((wsi->state == LWSS_SHUTDOWN) && lws_is_ssl(wsi) && wsi->ssl) { + n = SSL_shutdown(wsi->ssl); + lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd); + switch (n) { + case 1: + n = shutdown(wsi->desc.sockfd, SHUT_WR); + goto close_and_handled; + + case 0: + lws_change_pollfd(wsi, 0, LWS_POLLIN); + n = 0; + goto handled; + + default: + n = SSL_get_error(wsi->ssl, n); + if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) { + if (SSL_want_read(wsi->ssl)) { + lwsl_debug("(wants read)\n"); + lws_change_pollfd(wsi, 0, LWS_POLLIN); + n = 0; + goto handled; + } + if (SSL_want_write(wsi->ssl)) { + lwsl_debug("(wants write)\n"); + lws_change_pollfd(wsi, 0, LWS_POLLOUT); + n = 0; + goto handled; + } + } + + /* actual error occurred, just close the connection */ + n = shutdown(wsi->desc.sockfd, SHUT_WR); + goto close_and_handled; + } + } +#endif + + /* okay, what we came here to do... */ + + switch (wsi->mode) { + case LWSCM_HTTP_SERVING: + case LWSCM_HTTP_CLIENT: + case LWSCM_HTTP_SERVING_ACCEPTED: + case LWSCM_SERVER_LISTENER: + case LWSCM_SSL_ACK_PENDING: + case LWSCM_SSL_ACK_PENDING_RAW: + if (wsi->state == LWSS_CLIENT_HTTP_ESTABLISHED) + goto handled; + +#ifdef LWS_WITH_CGI + if (wsi->cgi && (pollfd->revents & LWS_POLLOUT)) { + n = lws_handle_POLLOUT_event(wsi, pollfd); + if (n) + goto close_and_handled; + goto handled; + } +#endif + /* fallthru */ + case LWSCM_RAW: + n = lws_server_socket_service(context, wsi, pollfd); + if (n) /* closed by above */ + return 1; + goto handled; + + case LWSCM_RAW_FILEDESC: + + if (pollfd->revents & LWS_POLLOUT) { + n = lws_calllback_as_writeable(wsi); + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + return 1; + } + if (n) + goto close_and_handled; + } + n = LWS_CALLBACK_RAW_RX; + if (wsi->mode == LWSCM_RAW_FILEDESC) + n = LWS_CALLBACK_RAW_RX_FILE; + + if (pollfd->revents & LWS_POLLIN) { + if (user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, n, + wsi->user_space, NULL, 0)) { + lwsl_debug("raw rx callback closed it\n"); + goto close_and_handled; + } + } + + if (pollfd->revents & LWS_POLLHUP) + goto close_and_handled; + n = 0; + goto handled; + + case LWSCM_WS_SERVING: + case LWSCM_WS_CLIENT: + case LWSCM_HTTP2_SERVING: + case LWSCM_HTTP_CLIENT_ACCEPTED: + + /* 1: something requested a callback when it was OK to write */ + + if ((pollfd->revents & LWS_POLLOUT) && + ((wsi->state == LWSS_ESTABLISHED || + wsi->state == LWSS_HTTP2_ESTABLISHED || + wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS || + wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION || + wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE)) && + lws_handle_POLLOUT_event(wsi, pollfd)) { + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY) + wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE; + lwsl_info("lws_service_fd: closing\n"); + goto close_and_handled; + } + + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION || + wsi->state == LWSS_AWAITING_CLOSE_ACK) { + /* + * we stopped caring about anything except control + * packets. Force flow control off, defeat tx + * draining. + */ + lws_rx_flow_control(wsi, 1); + wsi->u.ws.tx_draining_ext = 0; + } + + if (wsi->u.ws.tx_draining_ext) + /* we cannot deal with new RX until the TX ext + * path has been drained. It's because new + * rx will, eg, crap on the wsi rx buf that + * may be needed to retain state. + * + * TX ext drain path MUST go through event loop + * to avoid blocking. + */ + break; + + if (lws_is_flowcontrolled(wsi)) + /* We cannot deal with any kind of new RX + * because we are RX-flowcontrolled. + */ + break; + +#if defined(LWS_WITH_HTTP2) + if (wsi->http2_substream || wsi->upgraded_to_http2) { + wsi1 = lws_get_network_wsi(wsi); + if (wsi1 && wsi1->trunc_len) + /* We cannot deal with any kind of new RX + * because we are dealing with a partial send + * (new RX may trigger new http_action() that + * expect to be able to send) + */ + break; + } +#endif + + /* 2: RX Extension needs to be drained + */ + + if (wsi->state == LWSS_ESTABLISHED && + wsi->u.ws.rx_draining_ext) { + + lwsl_ext("%s: RX EXT DRAINING: Service\n", __func__); +#ifndef LWS_NO_CLIENT + if (wsi->mode == LWSCM_WS_CLIENT) { + n = lws_client_rx_sm(wsi, 0); + if (n < 0) + /* we closed wsi */ + n = 0; + } else +#endif + n = lws_rx_sm(wsi, 0); + + goto handled; + } + + if (wsi->u.ws.rx_draining_ext) + /* + * We have RX EXT content to drain, but can't do it + * right now. That means we cannot do anything lower + * priority either. + */ + break; + + /* 3: RX Flowcontrol buffer / h2 rx scratch needs to be drained + */ + + if (wsi->rxflow_buffer) { + lwsl_info("draining rxflow (len %d)\n", + wsi->rxflow_len - wsi->rxflow_pos); + assert(wsi->rxflow_pos < wsi->rxflow_len); + /* well, drain it */ + eff_buf.token = (char *)wsi->rxflow_buffer + + wsi->rxflow_pos; + eff_buf.token_len = wsi->rxflow_len - wsi->rxflow_pos; + draining_flow = 1; + goto drain; + } + +#if defined(LWS_WITH_HTTP2) + if (wsi->upgraded_to_http2) { + struct lws_h2_netconn *h2n = wsi->u.h2.h2n; + + if (h2n->rx_scratch_len) { + lwsl_info("%s: %p: resuming h2 rx_scratch pos = %d len = %d\n", + __func__, wsi, h2n->rx_scratch_pos, h2n->rx_scratch_len); + eff_buf.token = (char *)h2n->rx_scratch + + h2n->rx_scratch_pos; + eff_buf.token_len = h2n->rx_scratch_len; + + h2n->rx_scratch_len = 0; + goto drain; + } + } +#endif + + /* 4: any incoming (or ah-stashed incoming rx) data ready? + * notice if rx flow going off raced poll(), rx flow wins + */ + + if (!(pollfd->revents & pollfd->events & LWS_POLLIN)) + break; +read: + if (lws_is_flowcontrolled(wsi)) { + lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n", + __func__, wsi, wsi->rxflow_bitmap); + break; + } + + /* all the union members start with hdr, so even in ws mode + * we can deal with the ah via u.hdr + */ + if (wsi->u.hdr.ah) { + lwsl_info("%s: %p: inherited ah rx\n", __func__, wsi); + eff_buf.token_len = wsi->u.hdr.ah->rxlen - + wsi->u.hdr.ah->rxpos; + eff_buf.token = (char *)wsi->u.hdr.ah->rx + + wsi->u.hdr.ah->rxpos; + } else { + if (wsi->mode != LWSCM_HTTP_CLIENT_ACCEPTED) { + /* + * extension may not consume everything (eg, pmd may be constrained + * as to what it can output...) has to go in per-wsi rx buf area. + * Otherwise in large temp serv_buf area. + */ + +#if defined(LWS_WITH_HTTP2) + if (wsi->upgraded_to_http2) { + if (!wsi->u.h2.h2n->rx_scratch) { + wsi->u.h2.h2n->rx_scratch = lws_malloc(LWS_H2_RX_SCRATCH_SIZE, "h2 rx scratch"); + if (!wsi->u.h2.h2n->rx_scratch) + goto close_and_handled; + } + eff_buf.token = wsi->u.h2.h2n->rx_scratch; + eff_buf.token_len = LWS_H2_RX_SCRATCH_SIZE; + } else +#endif + { + eff_buf.token = (char *)pt->serv_buf; + if (lws_is_ws_with_ext(wsi)) { + eff_buf.token_len = wsi->u.ws.rx_ubuf_alloc; + } else { + eff_buf.token_len = context->pt_serv_buf_size; + } + + if ((unsigned int)eff_buf.token_len > context->pt_serv_buf_size) + eff_buf.token_len = context->pt_serv_buf_size; + } + + if ((int)pending > eff_buf.token_len) + pending = eff_buf.token_len; + + eff_buf.token_len = lws_ssl_capable_read(wsi, + (unsigned char *)eff_buf.token, pending ? pending : + eff_buf.token_len); + switch (eff_buf.token_len) { + case 0: + lwsl_info("%s: zero length read\n", __func__); + goto close_and_handled; + case LWS_SSL_CAPABLE_MORE_SERVICE: + lwsl_info("SSL Capable more service\n"); + n = 0; + goto handled; + case LWS_SSL_CAPABLE_ERROR: + lwsl_info("Closing when error\n"); + goto close_and_handled; + } + // lwsl_notice("Actual RX %d\n", eff_buf.token_len); + } + } + +drain: +#ifndef LWS_NO_CLIENT + if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED && + !wsi->told_user_closed) { + + /* + * In SSL mode we get POLLIN notification about + * encrypted data in. + * + * But that is not necessarily related to decrypted + * data out becoming available; in may need to perform + * other in or out before that happens. + * + * simply mark ourselves as having readable data + * and turn off our POLLIN + */ + wsi->client_rx_avail = 1; + lws_change_pollfd(wsi, LWS_POLLIN, 0); + + /* let user code know, he'll usually ask for writeable + * callback and drain / re-enable it there + */ + if (user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP, + wsi->user_space, NULL, 0)) { + lwsl_info("RECEIVE_CLIENT_HTTP closed it\n"); + goto close_and_handled; + } + + n = 0; + goto handled; + } +#endif + /* + * give any active extensions a chance to munge the buffer + * before parse. We pass in a pointer to an lws_tokens struct + * prepared with the default buffer and content length that's in + * there. Rather than rewrite the default buffer, extensions + * that expect to grow the buffer can adapt .token to + * point to their own per-connection buffer in the extension + * user allocation. By default with no extensions or no + * extension callback handling, just the normal input buffer is + * used then so it is efficient. + */ + do { + more = 0; + + m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_RX_PREPARSE, + &eff_buf, 0); + if (m < 0) + goto close_and_handled; + if (m) + more = 1; + + /* service incoming data */ + + if (eff_buf.token_len) { + /* + * if draining from rxflow buffer, not + * critical to track what was used since at the + * use it bumps wsi->rxflow_pos. If we come + * around again it will pick up from where it + * left off. + */ + n = lws_read(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) { + /* we closed wsi */ + n = 0; + goto handled; + } + } + + eff_buf.token = NULL; + eff_buf.token_len = 0; + } while (more); + + if (wsi->u.hdr.ah) { + lwsl_debug("%s: %p: detaching\n", __func__, wsi); + lws_header_table_force_to_detachable_state(wsi); + /* we can run the normal ah detach flow despite + * being in ws union mode, since all union members + * start with hdr */ + lws_header_table_detach(wsi, 0); + } + + pending = lws_ssl_pending(wsi); + if (pending) { + if (lws_is_ws_with_ext(wsi)) + pending = pending > wsi->u.ws.rx_ubuf_alloc ? + wsi->u.ws.rx_ubuf_alloc : pending; + else + pending = pending > context->pt_serv_buf_size ? + context->pt_serv_buf_size : pending; + goto read; + } + + if (draining_flow && wsi->rxflow_buffer && + wsi->rxflow_pos == wsi->rxflow_len) { + lwsl_info("%s: %p flow buf: drained\n", __func__, wsi); + lws_free_set_NULL(wsi->rxflow_buffer); + /* having drained the rxflow buffer, can rearm POLLIN */ +#ifdef LWS_NO_SERVER + n = +#endif + _lws_rx_flow_control(wsi); + /* n ignored, needed for NO_SERVER case */ + } + + break; +#ifdef LWS_WITH_CGI + case LWSCM_CGI: /* we exist to handle a cgi's stdin/out/err data... + * do the callback on our master wsi + */ + { + struct lws_cgi_args args; + + if (wsi->cgi_channel >= LWS_STDOUT && + !(pollfd->revents & pollfd->events & LWS_POLLIN)) + break; + if (wsi->cgi_channel == LWS_STDIN && + !(pollfd->revents & pollfd->events & LWS_POLLOUT)) + break; + + if (wsi->cgi_channel == LWS_STDIN) + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + return 1; + } + + args.ch = wsi->cgi_channel; + args.stdwsi = &wsi->parent->cgi->stdwsi[0]; + args.hdr_state = wsi->hdr_state; + + lwsl_debug("CGI LWS_STDOUT %p mode %d state %d\n", + wsi->parent, wsi->parent->mode, + wsi->parent->state); + + if (user_callback_handle_rxflow( + wsi->parent->protocol->callback, + wsi->parent, LWS_CALLBACK_CGI, + wsi->parent->user_space, + (void *)&args, 0)) + return 1; + + break; + } +#endif + /* + * something went wrong with parsing the handshake, and + * we ended up back in the event loop without completing it + */ + case LWSCM_PRE_WS_SERVING_ACCEPT: + wsi->socket_is_permanently_unusable = 1; + goto close_and_handled; + + default: +#ifdef LWS_NO_CLIENT + break; +#else + if ((pollfd->revents & LWS_POLLOUT) && + lws_handle_POLLOUT_event(wsi, pollfd)) { + lwsl_debug("POLLOUT event closed it\n"); + goto close_and_handled; + } + + n = lws_client_socket_service(context, wsi, pollfd); + if (n) + return 1; + goto handled; +#endif + } + + n = 0; + goto handled; + +close_and_handled: + lwsl_debug("%p: Close and handled\n", wsi); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + /* + * pollfd may point to something else after the close + * due to pollfd swapping scheme on delete on some platforms + * we can't clear revents now because it'd be the wrong guy's revents + */ + return 1; + +handled: + pollfd->revents = 0; + return n; +} + +LWS_VISIBLE int +lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd) +{ + return lws_service_fd_tsi(context, pollfd, 0); +} + +LWS_VISIBLE int +lws_service(struct lws_context *context, int timeout_ms) +{ + return lws_plat_service(context, timeout_ms); +} + +LWS_VISIBLE int +lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + return _lws_plat_service_tsi(context, timeout_ms, tsi); +} + diff -Nru libwebsockets-4.0.20/lib/ssl.c libwebsockets-2.4.2/lib/ssl.c --- libwebsockets-4.0.20/lib/ssl.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/lib/ssl.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,977 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +/* workaround for mingw */ +#if !defined(ECONNABORTED) +#define ECONNABORTED 103 +#endif + +int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount) +{ + lws_filepos_t len; + lws_fop_flags_t flags = LWS_O_RDONLY; + lws_fop_fd_t fops_fd = lws_vfs_file_open( + lws_get_fops(context), filename, &flags); + int ret = 1; + + if (!fops_fd) + return 1; + + len = lws_vfs_get_length(fops_fd); + + *buf = lws_malloc((size_t)len, "lws_alloc_vfs_file"); + if (!*buf) + goto bail; + + if (lws_vfs_file_read(fops_fd, amount, *buf, len)) + goto bail; + + ret = 0; +bail: + lws_vfs_file_close(&fops_fd); + + return ret; +} + +#if defined(LWS_WITH_MBEDTLS) +#if defined(LWS_WITH_ESP32) +int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount) +{ + nvs_handle nvh; + size_t s; + int n = 0; + + ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh)); + if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) { + n = 1; + goto bail; + } + *buf = lws_malloc(s, "alloc_file"); + if (!*buf) { + n = 2; + goto bail; + } + if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) { + lws_free(*buf); + n = 1; + goto bail; + } + + *amount = s; + +bail: + nvs_close(nvh); + + return n; +} +#else +int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount) +{ + FILE *f; + size_t s; + int n = 0; + + f = fopen(filename, "rb"); + if (f == NULL) { + n = 1; + goto bail; + } + + if (fseek(f, 0, SEEK_END) != 0) { + n = 1; + goto bail; + } + + s = ftell(f); + if (s == -1) { + n = 1; + goto bail; + } + + if (fseek(f, 0, SEEK_SET) != 0) { + n = 1; + goto bail; + } + + *buf = lws_malloc(s, "alloc_file"); + if (!*buf) { + n = 2; + goto bail; + } + + if (fread(*buf, s, 1, f) != 1) { + lws_free(*buf); + n = 1; + goto bail; + } + + *amount = s; + +bail: + if (f) + fclose(f); + + return n; + +} +#endif +int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount) +{ + uint8_t *pem, *p, *q, *end; + lws_filepos_t len; + int n; + + n = alloc_file(context, filename, &pem, &len); + if (n) + return n; + + /* trim the first line */ + + p = pem; + end = p + len; + if (strncmp((char *)p, "-----", 5)) + goto bail; + p += 5; + while (p < end && *p != '\n' && *p != '-') + p++; + + if (*p != '-') + goto bail; + + while (p < end && *p != '\n') + p++; + + if (p >= end) + goto bail; + + p++; + + /* trim the last line */ + + q = end - 2; + + while (q > pem && *q != '\n') + q--; + + if (*q != '\n') + goto bail; + + *q = '\0'; + + *amount = lws_b64_decode_string((char *)p, (char *)pem, len); + *buf = pem; + + return 0; + +bail: + lws_free(pem); + + return 4; +} +#endif + +int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +int lws_ssl_get_error(struct lws *wsi, int n) +{ + int m; + + if (!wsi->ssl) + return 99; + + m = SSL_get_error(wsi->ssl, n); + lwsl_debug("%s: %p %d -> %d\n", __func__, wsi->ssl, n, m); + + return m; +} + +/* Copies a string describing the code returned by lws_ssl_get_error(), + * which may also contain system error information in the case of SSL_ERROR_SYSCALL, + * into buf up to len. + * Returns a pointer to buf. + * + * Note: the lws_ssl_get_error() code is *not* an error code that can be passed + * to ERR_error_string(), + * + * ret is the return value originally passed to lws_ssl_get_error(), needed to disambiguate + * SYS_ERROR_SYSCALL. + * + * See man page for SSL_get_error(). + * + * Not thread safe, uses strerror() + */ +char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) { + switch (status) { + case SSL_ERROR_NONE: return strncpy(buf, "SSL_ERROR_NONE", len); + case SSL_ERROR_ZERO_RETURN: return strncpy(buf, "SSL_ERROR_ZERO_RETURN", len); + case SSL_ERROR_WANT_READ: return strncpy(buf, "SSL_ERROR_WANT_READ", len); + case SSL_ERROR_WANT_WRITE: return strncpy(buf, "SSL_ERROR_WANT_WRITE", len); + case SSL_ERROR_WANT_CONNECT: return strncpy(buf, "SSL_ERROR_WANT_CONNECT", len); + case SSL_ERROR_WANT_ACCEPT: return strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len); + case SSL_ERROR_WANT_X509_LOOKUP: return strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len); + case SSL_ERROR_SYSCALL: + switch (ret) { + case 0: + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: EOF"); + return buf; + case -1: +#ifndef LWS_PLAT_OPTEE + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %s", strerror(errno)); +#else + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %d", errno); +#endif + return buf; + default: + return strncpy(buf, "SSL_ERROR_SYSCALL", len); + } + case SSL_ERROR_SSL: return "SSL_ERROR_SSL"; + default: return "SSL_ERROR_UNKNOWN"; + } +} + +void +lws_ssl_elaborate_error(void) +{ +#if defined(LWS_WITH_MBEDTLS) +#else + char buf[256]; + u_long err; + + while ((err = ERR_get_error()) != 0) { + ERR_error_string_n(err, buf, sizeof(buf)); + lwsl_info("*** %s\n", buf); + } +#endif +} + +#if !defined(LWS_WITH_MBEDTLS) + +static int +lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, void *userdata) +{ + struct lws_context_creation_info * info = + (struct lws_context_creation_info *)userdata; + + strncpy(buf, info->ssl_private_key_password, size); + buf[size - 1] = '\0'; + + return strlen(buf); +} + +void +lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info) +{ + if (!info->ssl_private_key_password) + return; + /* + * password provided, set ssl callback and user data + * for checking password which will be trigered during + * SSL_CTX_use_PrivateKey_file function + */ + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info); + SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb); +} +#endif + +int +lws_context_init_ssl_library(struct lws_context_creation_info *info) +{ +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + lwsl_info(" Compiled with CyaSSL support\n"); +#else + lwsl_info(" Compiled with wolfSSL support\n"); +#endif +#else +#if defined(LWS_WITH_BORINGSSL) + lwsl_info(" Compiled with BoringSSL support\n"); +#else +#if defined(LWS_WITH_MBEDTLS) + lwsl_info(" Compiled with MbedTLS support\n"); +#else + lwsl_info(" Compiled with OpenSSL support\n"); +#endif +#endif +#endif + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { + lwsl_info(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); + return 0; + } + + /* basic openssl init */ + + lwsl_info("Doing SSL library init\n"); + +#if !defined(LWS_WITH_MBEDTLS) + SSL_library_init(); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + + openssl_websocket_private_data_index = + SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL); + + openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0, + NULL, NULL, NULL, NULL); +#endif + + return 0; +} + +LWS_VISIBLE void +lws_ssl_destroy(struct lws_vhost *vhost) +{ + if (!lws_check_opt(vhost->context->options, + LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) + return; + + if (vhost->ssl_ctx) + SSL_CTX_free(vhost->ssl_ctx); + if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx) + SSL_CTX_free(vhost->ssl_client_ctx); + +#if defined(LWS_WITH_MBEDTLS) + if (vhost->x509_client_CA) + X509_free(vhost->x509_client_CA); +#else +// after 1.1.0 no need +#if (OPENSSL_VERSION_NUMBER < 0x10100000) +// <= 1.0.1f = old api, 1.0.1g+ = new api +#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL) + ERR_remove_state(0); +#else +#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL) + ERR_remove_thread_state(); +#else + ERR_remove_thread_state(NULL); +#endif +#endif + // after 1.1.0 no need +#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000) + SSL_COMP_free_compression_methods(); +#endif + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#endif +#endif +} + +int +lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws *wsi, *wsi_next; + + wsi = pt->pending_read_list; + while (wsi) { + wsi_next = wsi->pending_read_list_next; + pt->fds[wsi->position_in_fds_table].revents |= + pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; + if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) + return 1; + + wsi = wsi_next; + } + + return 0; +} + +LWS_VISIBLE void +lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + if (!wsi->pending_read_list_prev && + !wsi->pending_read_list_next && + pt->pending_read_list != wsi) + /* we are not on the list */ + return; + + /* point previous guy's next to our next */ + if (!wsi->pending_read_list_prev) + pt->pending_read_list = wsi->pending_read_list_next; + else + wsi->pending_read_list_prev->pending_read_list_next = + wsi->pending_read_list_next; + + /* point next guy's previous to our previous */ + if (wsi->pending_read_list_next) + wsi->pending_read_list_next->pending_read_list_prev = + wsi->pending_read_list_prev; + + wsi->pending_read_list_prev = NULL; + wsi->pending_read_list_next = NULL; +} + +LWS_VISIBLE int +lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int n = 0, m; + + if (!wsi->ssl) + return lws_ssl_capable_read_no_ssl(wsi, buf, len); + + lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1); + + errno = 0; + n = SSL_read(wsi->ssl, buf, len); +#if defined(LWS_WITH_ESP32) + if (!n && errno == ENOTCONN) { + lwsl_debug("%p: SSL_read ENOTCONN\n", wsi); + return LWS_SSL_CAPABLE_ERROR; + } +#endif +#if defined(LWS_WITH_STATS) + if (!wsi->seen_rx) { + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_MS_SSL_RX_DELAY, + time_in_microseconds() - wsi->accept_start_us); + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNS_HAD_RX, 1); + wsi->seen_rx = 1; + } +#endif + + + lwsl_debug("%p: SSL_read says %d\n", wsi, n); + /* manpage: returning 0 means connection shut down */ + if (!n || (n == -1 && errno == ENOTCONN)) { + wsi->socket_is_permanently_unusable = 1; + + return LWS_SSL_CAPABLE_ERROR; + } + + if (n < 0) { + m = lws_ssl_get_error(wsi, n); + lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno); + if (m == SSL_ERROR_ZERO_RETURN || + m == SSL_ERROR_SYSCALL) + return LWS_SSL_CAPABLE_ERROR; + + if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) { + lwsl_debug("%s: WANT_READ\n", __func__); + lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) { + lwsl_debug("%s: WANT_WRITE\n", __func__); + lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + wsi->socket_is_permanently_unusable = 1; + + return LWS_SSL_CAPABLE_ERROR; + } + + lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n); + + if (wsi->vhost) + wsi->vhost->conn_stats.rx += n; + + lws_restart_ws_ping_pong_timer(wsi); + + /* + * if it was our buffer that limited what we read, + * check if SSL has additional data pending inside SSL buffers. + * + * Because these won't signal at the network layer with POLLIN + * and if we don't realize, this data will sit there forever + */ + if (n != len) + goto bail; + if (!wsi->ssl) + goto bail; + + if (!SSL_pending(wsi->ssl)) + goto bail; + + if (wsi->pending_read_list_next) + return n; + if (wsi->pending_read_list_prev) + return n; + if (pt->pending_read_list == wsi) + return n; + + /* add us to the linked list of guys with pending ssl */ + if (pt->pending_read_list) + pt->pending_read_list->pending_read_list_prev = wsi; + + wsi->pending_read_list_next = pt->pending_read_list; + wsi->pending_read_list_prev = NULL; + pt->pending_read_list = wsi; + + return n; +bail: + lws_ssl_remove_wsi_from_buffered_list(wsi); + + return n; +} + +LWS_VISIBLE int +lws_ssl_pending(struct lws *wsi) +{ + if (!wsi->ssl) + return 0; + + return SSL_pending(wsi->ssl); +} + +LWS_VISIBLE int +lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len) +{ + int n, m; + + if (!wsi->ssl) + return lws_ssl_capable_write_no_ssl(wsi, buf, len); + + n = SSL_write(wsi->ssl, buf, len); + if (n > 0) + return n; + + m = lws_ssl_get_error(wsi, n); + if (m != SSL_ERROR_SYSCALL) { + + if (SSL_want_read(wsi->ssl)) { + lwsl_notice("%s: want read\n", __func__); + + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + + if (SSL_want_write(wsi->ssl)) { + lws_set_blocking_send(wsi); + + lwsl_notice("%s: want write\n", __func__); + + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + } + + lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL)); + lws_ssl_elaborate_error(); + + wsi->socket_is_permanently_unusable = 1; + + return LWS_SSL_CAPABLE_ERROR; +} + +static int +lws_gate_accepts(struct lws_context *context, int on) +{ + struct lws_vhost *v = context->vhost_list; + + lwsl_info("gating accepts %d\n", on); + context->ssl_gate_accepts = !on; +#if defined(LWS_WITH_STATS) + context->updated = 1; +#endif + + while (v) { + if (v->use_ssl && v->lserv_wsi) /* gate ability to accept incoming connections */ + if (lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on, + (LWS_POLLIN) * on)) + lwsl_info("Unable to set accept POLLIN %d\n", on); + + v = v->vhost_next; + } + + return 0; +} + +void +lws_ssl_info_callback(const SSL *ssl, int where, int ret) +{ + struct lws *wsi; + struct lws_context *context; + struct lws_ssl_info si; + + context = (struct lws_context *)SSL_CTX_get_ex_data( + SSL_get_SSL_CTX(ssl), + openssl_SSL_CTX_private_data_index); + if (!context) + return; + wsi = wsi_from_fd(context, SSL_get_fd(ssl)); + if (!wsi) + return; + + if (!(where & wsi->vhost->ssl_info_event_mask)) + return; + + si.where = where; + si.ret = ret; + + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_SSL_INFO, + wsi->user_space, &si, 0)) + lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1); +} + + +LWS_VISIBLE int +lws_ssl_close(struct lws *wsi) +{ + lws_sockfd_type n; + + if (!wsi->ssl) + return 0; /* not handled */ + +#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) + /* kill ssl callbacks, becausse we will remove the fd from the + * table linking it to the wsi + */ + if (wsi->vhost->ssl_info_event_mask) + SSL_set_info_callback(wsi->ssl, NULL); +#endif + + n = SSL_get_fd(wsi->ssl); + if (!wsi->socket_is_permanently_unusable) + SSL_shutdown(wsi->ssl); + compatible_close(n); + SSL_free(wsi->ssl); + wsi->ssl = NULL; + + if (wsi->context->simultaneous_ssl_restriction && + wsi->context->simultaneous_ssl-- == + wsi->context->simultaneous_ssl_restriction) + /* we made space and can do an accept */ + lws_gate_accepts(wsi->context, 1); +#if defined(LWS_WITH_STATS) + wsi->context->updated = 1; +#endif + + return 1; /* handled */ +} + +/* leave all wsi close processing to the caller */ + +LWS_VISIBLE int +lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) +{ + struct lws_context *context = wsi->context; + struct lws_vhost *vh; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int n, m; +#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) + BIO *bio; +#endif + char buf[256]; + + (void)buf; + + if (!LWS_SSL_ENABLED(wsi->vhost)) + return 0; + + switch (wsi->mode) { + case LWSCM_SSL_INIT: + case LWSCM_SSL_INIT_RAW: + if (wsi->ssl) + lwsl_err("%s: leaking ssl\n", __func__); + if (accept_fd == LWS_SOCK_INVALID) + assert(0); + if (context->simultaneous_ssl_restriction && + context->simultaneous_ssl >= context->simultaneous_ssl_restriction) { + lwsl_notice("unable to deal with SSL connection\n"); + return 1; + } + errno = 0; + wsi->ssl = SSL_new(wsi->vhost->ssl_ctx); + if (wsi->ssl == NULL) { + lwsl_err("SSL_new failed: %d (errno %d)\n", + lws_ssl_get_error(wsi, 0), errno); + + lws_ssl_elaborate_error(); + if (accept_fd != LWS_SOCK_INVALID) + compatible_close(accept_fd); + goto fail; + } +#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) + if (wsi->vhost->ssl_info_event_mask) + SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback); +#endif + if (context->simultaneous_ssl_restriction && + ++context->simultaneous_ssl == context->simultaneous_ssl_restriction) + /* that was the last allowed SSL connection */ + lws_gate_accepts(context, 0); +#if defined(LWS_WITH_STATS) + context->updated = 1; +#endif + +#if !defined(LWS_WITH_MBEDTLS) + SSL_set_ex_data(wsi->ssl, + openssl_websocket_private_data_index, wsi); +#endif + SSL_set_fd(wsi->ssl, accept_fd); + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + CyaSSL_set_using_nonblock(wsi->ssl, 1); +#else + wolfSSL_set_using_nonblock(wsi->ssl, 1); +#endif +#else +#if defined(LWS_WITH_MBEDTLS) + lws_plat_set_socket_options(wsi->vhost, accept_fd); +#else + SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + bio = SSL_get_rbio(wsi->ssl); + if (bio) + BIO_set_nbio(bio, 1); /* nonblocking */ + else + lwsl_notice("NULL rbio\n"); + bio = SSL_get_wbio(wsi->ssl); + if (bio) + BIO_set_nbio(bio, 1); /* nonblocking */ + else + lwsl_notice("NULL rbio\n"); +#endif +#endif + + /* + * we are not accepted yet, but we need to enter ourselves + * as a live connection. That way we can retry when more + * pieces come if we're not sorted yet + */ + + if (wsi->mode == LWSCM_SSL_INIT) + wsi->mode = LWSCM_SSL_ACK_PENDING; + else + wsi->mode = LWSCM_SSL_ACK_PENDING_RAW; + + if (insert_wsi_socket_into_fds(context, wsi)) { + lwsl_err("%s: failed to insert into fds\n", __func__); + goto fail; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, + context->timeout_secs); + + lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n"); + + /* fallthru */ + + case LWSCM_SSL_ACK_PENDING: + case LWSCM_SSL_ACK_PENDING_RAW: + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_err("%s: lws_change_pollfd failed\n", __func__); + goto fail; + } + + lws_latency_pre(context, wsi); + + if (wsi->vhost->allow_non_ssl_on_ssl_port) { + + n = recv(wsi->desc.sockfd, (char *)pt->serv_buf, + context->pt_serv_buf_size, MSG_PEEK); + + /* + * optionally allow non-SSL connect on SSL listening socket + * This is disabled by default, if enabled it goes around any + * SSL-level access control (eg, client-side certs) so leave + * it disabled unless you know it's not a problem for you + */ + + if (n >= 1 && pt->serv_buf[0] >= ' ') { + /* + * TLS content-type for Handshake is 0x16, and + * for ChangeCipherSpec Record, it's 0x14 + * + * A non-ssl session will start with the HTTP + * method in ASCII. If we see it's not a legit + * SSL handshake kill the SSL for this + * connection and try to handle as a HTTP + * connection upgrade directly. + */ + wsi->use_ssl = 0; + + SSL_shutdown(wsi->ssl); + SSL_free(wsi->ssl); + wsi->ssl = NULL; + if (lws_check_opt(context->options, + LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS)) + wsi->redirect_to_https = 1; + goto accepted; + } + if (!n) /* + * connection is gone, or nothing to read + * if it's gone, we will timeout on + * PENDING_TIMEOUT_SSL_ACCEPT + */ + break; + if (n < 0 && (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK)) { + /* + * well, we get no way to know ssl or not + * so go around again waiting for something + * to come and give us a hint, or timeout the + * connection. + */ + m = SSL_ERROR_WANT_READ; + goto go_again; + } + } + + /* normal SSL connection processing path */ + +#if defined(LWS_WITH_STATS) + if (!wsi->accept_start_us) + wsi->accept_start_us = time_in_microseconds(); +#endif + errno = 0; + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1); + n = SSL_accept(wsi->ssl); + lws_latency(context, wsi, + "SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1); + lwsl_info("SSL_accept says %d\n", n); + if (n == 1) + goto accepted; + + m = lws_ssl_get_error(wsi, n); + +#if defined(LWS_WITH_MBEDTLS) + if (m == SSL_ERROR_SYSCALL && errno == 11) + m = SSL_ERROR_WANT_READ; +#endif + if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL) + goto failed; + +go_again: + if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) { + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__); + goto fail; + } + + lwsl_info("SSL_ERROR_WANT_READ\n"); + break; + } + if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) { + lwsl_debug("%s: WANT_WRITE\n", __func__); + + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { + lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__); + goto fail; + } + + break; + } +failed: + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1); + wsi->socket_is_permanently_unusable = 1; + lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd, + lws_ssl_get_error_string(m, n, buf, sizeof(buf))); + lws_ssl_elaborate_error(); + goto fail; + +accepted: + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1); +#if defined(LWS_WITH_STATS) + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY, + time_in_microseconds() - wsi->accept_start_us); + wsi->accept_start_us = time_in_microseconds(); +#endif + + /* adapt our vhost to match the SNI SSL_CTX that was chosen */ + vh = context->vhost_list; + while (vh) { + if (!vh->being_destroyed && wsi->ssl && + vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) { + lwsl_info("setting wsi to vh %s\n", vh->name); + wsi->vhost = vh; + break; + } + vh = vh->vhost_next; + } + + /* OK, we are accepted... give him some time to negotiate */ + lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, + context->timeout_secs); + + if (wsi->mode == LWSCM_SSL_ACK_PENDING_RAW) + wsi->mode = LWSCM_RAW; + else + wsi->mode = LWSCM_HTTP_SERVING; +#if defined(LWS_WITH_HTTP2) + if (lws_h2_configure_if_upgraded(wsi)) + goto fail; +#endif + lwsl_debug("accepted new SSL conn\n"); + break; + } + + return 0; + +fail: + return 1; +} + +void +lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost) +{ + if (vhost->ssl_ctx) + SSL_CTX_free(vhost->ssl_ctx); + + if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx) + SSL_CTX_free(vhost->ssl_client_ctx); +} + +void +lws_ssl_context_destroy(struct lws_context *context) +{ + +#if !defined(LWS_WITH_MBEDTLS) + +// after 1.1.0 no need +#if (OPENSSL_VERSION_NUMBER < 0x10100000) +// <= 1.0.1f = old api, 1.0.1g+ = new api +#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL) + ERR_remove_state(0); +#else +#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL) + ERR_remove_thread_state(); +#else + ERR_remove_thread_state(NULL); +#endif +#endif + // after 1.1.0 no need +#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000) + SSL_COMP_free_compression_methods(); +#endif + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#endif +#endif +} diff -Nru libwebsockets-4.0.20/lib/system/async-dns/async-dns.c libwebsockets-2.4.2/lib/system/async-dns/async-dns.c --- libwebsockets-4.0.20/lib/system/async-dns/async-dns.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/system/async-dns/async-dns.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,749 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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 "private-lib-async-dns.h" - -static const uint32_t botable[] = { 500, 1000, 1250, 5000 - /* in case everything just dog slow */ }; -static const lws_retry_bo_t retry_policy = { - botable, LWS_ARRAY_SIZE(botable), LWS_ARRAY_SIZE(botable), - /* don't conceal after the last table entry */ 0, 0, 20 }; - -void -lws_adns_q_destroy(lws_adns_q_t *q) -{ - lws_dll2_remove(&q->sul.list); - lws_dll2_remove(&q->list); - lws_free(q); -} - -lws_adns_q_t * -lws_adns_get_query(lws_async_dns_t *dns, adns_query_type_t qtype, - lws_dll2_owner_t *owner, uint16_t tid, const char *name) -{ - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - lws_dll2_get_head(owner)) { - lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t, list); - - if (!name && (tid & 0xfffe) == (q->tid & 0xfffe)) - return q; - - if (name && q->qtype == ((tid & 1) ? LWS_ADNS_RECORD_AAAA : - LWS_ADNS_RECORD_A) && - !strcasecmp(name, (const char *)&q[1])) { - if (owner == &dns->cached) { - /* Keep sorted by LRU: move to the head */ - lws_dll2_remove(&q->list); - lws_dll2_add_head(&q->list, &dns->cached); - } - - return q; - } - } lws_end_foreach_dll_safe(d, d1); - - return NULL; -} - -void -lws_async_dns_drop_server(struct lws_context *context) -{ - context->async_dns.dns_server_set = 0; - lws_set_timeout(context->async_dns.wsi, 1, LWS_TO_KILL_ASYNC); - context->async_dns.wsi = NULL; -} - -int -lws_async_dns_complete(lws_adns_q_t *q, lws_adns_cache_t *c) -{ - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - lws_dll2_get_head(&q->wsi_adns)) { - struct lws *w = lws_container_of(d, struct lws, adns); - - lws_dll2_remove(d); - if (c && c->results) { - lwsl_debug("%s: q: %p, c: %p, refcount %d -> %d\n", - __func__, q, c, c->refcount, c->refcount + 1); - c->refcount++; - } - w->adns_cb(w, (const char *)&q[1], c ? c->results : NULL, 0, - q->opaque); - } lws_end_foreach_dll_safe(d, d1); - - if (q->standalone_cb) { - if (c && c->results) { - lwsl_debug("%s: q: %p, c: %p, refcount %d -> %d\n", - __func__, q, c, c->refcount, c->refcount + 1); - c->refcount++; - } - - q->standalone_cb(NULL, (const char *)&q[1], - c ? c->results : NULL, 0, q->opaque); - } - - return 0; -} - -static void -lws_async_dns_sul_cb_retry(struct lws_sorted_usec_list *sul) -{ - lws_adns_q_t *q = lws_container_of(sul, lws_adns_q_t, sul); - - // lwsl_notice("%s\n", __func__); - - lws_callback_on_writable(q->dns->wsi); -} - -static void -lws_async_dns_writeable(struct lws *wsi, lws_adns_q_t *q) -{ - uint8_t pkt[LWS_PRE + DNS_PACKET_LEN], *e = &pkt[sizeof(pkt)], *p, *pl; - int m, n, which; - const char *name; - - // lwsl_notice("%s: %p\n", __func__, q); - - /* - * UDP is not reliable, it can be locally dropped, or dropped - * by any intermediary or the remote peer. So even though we - * will do the write in a moment, we schedule another request - * for rewrite according to the wsi retry policy. - * - * If the result came before, we'll cancel it as part of the - * wsi close. - * - * If we have already reached the end of our concealed retries - * in the policy, just close without another write. - */ - if (lws_dll2_is_detached(&q->sul.list) && - lws_retry_sul_schedule_retry_wsi(wsi, &q->sul, - lws_async_dns_sul_cb_retry, &q->retry)) { - /* we have reached the end of our concealed retries */ - lwsl_notice("%s: failing query\n", __func__); - /* - * our policy is to force reloading the dns server info - * if our connection ever timed out, in case it or the - * routing state changed - */ - - lws_async_dns_drop_server(q->context); - goto qfail; - } - - name = (const char *)&q[1]; - - p = &pkt[LWS_PRE]; - memset(p, 0, DHO_SIZEOF); - -#if defined(LWS_WITH_IPV6) - if (!q->responded) { - /* must pick between ipv6 and ipv4 */ - which = q->sent[0] >= q->sent[1]; - q->sent[which]++; - q->asked = 3; /* want results for 4 & 6 before done */ - } else - which = q->responded & 1; -#else - which = 0; - q->asked = 1; -#endif - - /* we hack b0 of the tid to be 0 = A, 1 = AAAA */ - - lws_ser_wu16be(&p[DHO_TID], -#if defined(LWS_WITH_IPV6) - which ? q->tid | 1 : -#endif - q->tid); - lws_ser_wu16be(&p[DHO_FLAGS], (1 << 8)); - lws_ser_wu16be(&p[DHO_NQUERIES], 1); - - p += DHO_SIZEOF; - - /* start of label-formatted qname */ - - pl = p++; - - do { - if (*name == '.' || !*name) { - *pl = lws_ptr_diff(p, pl + 1); - pl = p; - *p++ = 0; /* also serves as terminal length */ - if (!*name++) - break; - } else - *p++ = *name++; - } while (p + 6 < e); - - if (p + 6 >= e) { - assert(0); - lwsl_err("%s: name too big\n", __func__); - goto qfail; - } - - lws_ser_wu16be(p, which ? LWS_ADNS_RECORD_AAAA : - LWS_ADNS_RECORD_A); - p += 2; - - lws_ser_wu16be(p, 1); /* IN class */ - p += 2; - - assert(p < pkt + sizeof(pkt) - LWS_PRE); - n = lws_ptr_diff(p, pkt + LWS_PRE); - m = lws_write(wsi, pkt + LWS_PRE, n, 0); - if (m != n) { - lwsl_notice("%s: dns write failed %d %d\n", __func__, - m, n); - goto qfail; - } - -#if defined(LWS_WITH_IPV6) - if (!q->responded && q->sent[0] != q->sent[1]) - lws_callback_on_writable(wsi); -#endif - - /* if we did anything, check one more time */ - lws_callback_on_writable(wsi); - - return; - -qfail: - lwsl_warn("%s: failing query doing NULL completion\n", __func__); - /* - * in ipv6 case, we made a cache entry for the first response but - * evidently the second response didn't come in time, purge the - * incomplete cache entry - */ - if (q->firstcache) - lws_adns_cache_destroy(q->firstcache); - lws_async_dns_complete(q, NULL); - lws_adns_q_destroy(q); -} - -static int -callback_async_dns(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct lws_async_dns *dns = &(lws_get_context(wsi)->async_dns); - - switch (reason) { - - /* callbacks related to raw socket descriptor */ - - case LWS_CALLBACK_RAW_ADOPT: - // lwsl_user("LWS_CALLBACK_RAW_ADOPT\n"); - break; - - case LWS_CALLBACK_RAW_CLOSE: - // lwsl_user("LWS_CALLBACK_RAW_CLOSE\n"); - break; - - case LWS_CALLBACK_RAW_RX: - // lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len); - // lwsl_hexdump_level(LLL_NOTICE, in, len); - lws_adns_parse_udp(dns, in, len); - break; - - case LWS_CALLBACK_RAW_WRITEABLE: - // lwsl_notice("%s: WRITABLE\n", __func__); - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - dns->waiting.head) { - lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t, - list); - - if (lws_dll2_is_detached(&q->sul.list) && - (!q->asked || q->responded != q->asked)) - lws_async_dns_writeable(wsi, q); - } lws_end_foreach_dll_safe(d, d1); - break; - - default: - break; - } - - return 0; -} - -struct lws_protocols lws_async_dns_protocol = { - "lws-async-dns", callback_async_dns, 0, 0 -}; - -int -lws_async_dns_init(struct lws_context *context) -{ - lws_async_dns_t *dns = &context->async_dns; - char ads[48]; - int n; - - memset(&dns->sa46, 0, sizeof(dns->sa46)); - -#if defined(LWS_WITH_SYS_DHCP_CLIENT) - if (lws_dhcpc_status(context, &dns->sa46)) - goto ok; -#endif - - n = lws_plat_asyncdns_init(context, &dns->sa46); - if (n < 0) { - lwsl_warn("%s: no valid dns server, retry\n", __func__); - - return 1; - } - - if (n != LADNS_CONF_SERVER_CHANGED) - return 0; - -#if defined(LWS_WITH_SYS_DHCP_CLIENT) -ok: -#endif - dns->sa46.sa4.sin_port = htons(53); - lws_write_numeric_address((uint8_t *)&dns->sa46.sa4.sin_addr.s_addr, 4, - ads, sizeof(ads)); - - context->async_dns.wsi = lws_create_adopt_udp(context->vhost_list, ads, - 53, 0, lws_async_dns_protocol.name, NULL, - NULL, NULL, &retry_policy); - if (!dns->wsi) { - lwsl_err("%s: foreign socket adoption failed\n", __func__); - return 1; - } - - dns->dns_server_set = 1; - - return 0; -} - -lws_adns_cache_t * -lws_adns_get_cache(lws_async_dns_t *dns, const char *name) -{ - lws_adns_cache_t *c; - const char *cn; - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - lws_dll2_get_head(&dns->cached)) { - c = lws_container_of(d, lws_adns_cache_t, list); - cn = (const char *)&c[1]; - - if (name && !c->incomplete && !strcasecmp(name, cn)) { - /* Keep sorted by LRU: move to the head */ - lws_dll2_remove(&c->list); - lws_dll2_add_head(&c->list, &dns->cached); - - return c; - } - } lws_end_foreach_dll_safe(d, d1); - - return NULL; -} - -void -lws_adns_cache_destroy(lws_adns_cache_t *c) -{ - lws_dll2_remove(&c->sul.list); - lws_dll2_remove(&c->list); - if (c->chain) - lws_free(c->chain); - lws_free(c); -} - -static int -cache_clean(struct lws_dll2 *d, void *user) -{ - lws_adns_cache_destroy(lws_container_of(d, lws_adns_cache_t, list)); - - return 0; -} - -void -sul_cb_expire(struct lws_sorted_usec_list *sul) -{ - lws_adns_cache_t *c = lws_container_of(sul, lws_adns_cache_t, sul); - - lws_adns_cache_destroy(c); -} - -void -lws_async_dns_freeaddrinfo(const struct addrinfo **pai) -{ - lws_adns_cache_t *c; - - if (!*pai) - return; - - /* - * First query may have been empty... if second has something, we - * fixed up the first result to point to second... but it means - * looking backwards from ai, which is c->result, which is the second - * packet's results, doesn't get us to the firstcache pointer. - * - * Adjust c to the firstcache in this case. - */ - - c = &((lws_adns_cache_t *)(*pai))[-1]; - if (c->firstcache) - c = c->firstcache; - - lwsl_debug("%s: c %p, %s, refcount %d -> %d\n", __func__, c, - (c->results && c->results->ai_canonname) ? - c->results->ai_canonname : "none", - c->refcount, c->refcount - 1); - - assert(c->refcount > 0); - c->refcount--; - *pai = NULL; -} - -void -lws_async_dns_trim_cache(lws_async_dns_t *dns) -{ - lws_adns_cache_t *c1; - - if (dns->cached.count + 1< MAX_CACHE_ENTRIES) - return; - - c1 = lws_container_of(lws_dll2_get_tail(&dns->cached), - lws_adns_cache_t, list); - if (c1->refcount) - lwsl_notice("%s: wsi %p: refcount %d on purge\n", - __func__, c1, c1->refcount); - else - lws_adns_cache_destroy(c1); -} - - -static int -clean(struct lws_dll2 *d, void *user) -{ - lws_adns_q_destroy(lws_container_of(d, lws_adns_q_t, list)); - - return 0; -} - -void -lws_async_dns_deinit(lws_async_dns_t *dns) -{ - lws_dll2_foreach_safe(&dns->waiting, NULL, clean); - lws_dll2_foreach_safe(&dns->cached, NULL, cache_clean); -} - - -static int -cancel(struct lws_dll2 *d, void *user) -{ - lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t, list); - - lws_start_foreach_dll_safe(struct lws_dll2 *, d3, d4, - lws_dll2_get_head(&q->wsi_adns)) { - struct lws *w = lws_container_of(d3, struct lws, adns); - - if (user == w) { - lws_dll2_remove(d3); - if (!q->wsi_adns.count) - lws_adns_q_destroy(q); - return 1; - } - } lws_end_foreach_dll_safe(d3, d4); - - return 0; -} - -void -lws_async_dns_cancel(struct lws *wsi) -{ - lws_async_dns_t *dns = &wsi->context->async_dns; - - lws_dll2_foreach_safe(&dns->waiting, wsi, cancel); -} - - -static int -check_tid(struct lws_dll2 *d, void *user) -{ - lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t, list); - - return q->tid == (uint16_t)(intptr_t)user; -} - -int -lws_async_dns_get_new_tid(struct lws_context *context, lws_adns_q_t *q) -{ - lws_async_dns_t *dns = &context->async_dns; - int budget = 10; - - /* - * Make the TID unpredictable, but must be unique amongst ongoing ones - */ - do { - uint16_t tid; - - if (lws_get_random(context, &tid, 2) != 2) - return -1; - - if (lws_dll2_foreach_safe(&dns->waiting, - (void *)(intptr_t)tid, check_tid)) - continue; - - q->tid = tid; - - return 0; - - } while (budget--); - - lwsl_err("%s: unable to get unique tid\n", __func__); - - return -1; -} - -struct temp_q { - lws_adns_q_t tq; - char name[48]; -}; - -lws_async_dns_retcode_t -lws_async_dns_query(struct lws_context *context, int tsi, const char *name, - adns_query_type_t qtype, lws_async_dns_cb_t cb, - struct lws *wsi, void *opaque) -{ - lws_async_dns_t *dns = &context->async_dns; - size_t nlen = strlen(name); - lws_sockaddr46 *sa46; - lws_adns_cache_t *c; - struct addrinfo *ai; - struct temp_q tmq; - lws_adns_q_t *q; - uint8_t ads[16]; - char *p; - int m; - -#if !defined(LWS_WITH_IPV6) - if (qtype == LWS_ADNS_RECORD_AAAA) { - lwsl_err("%s: ipv6 not enabled\n", __func__); - goto failed; - } -#endif - - if (nlen >= DNS_MAX - 1) - goto failed; - - /* - * we magically know 'localhost' and 'localhost6' if IPv6, this is a - * sort of canned /etc/hosts - */ - - if (!strcmp(name, "localhost")) - name = "127.0.0.1"; - -#if defined(LWS_WITH_IPV6) - if (!strcmp(name, "localhost6")) - name = "::1"; -#endif - - if (wsi) { - if (!lws_dll2_is_detached(&wsi->adns)) { - lwsl_err("%s: wsi %p already bound to query %p\n", - __func__, wsi, wsi->adns.owner); - goto failed; - } - wsi->adns_cb = cb; - } - - /* there's a done, cached query we can just reuse? */ - - c = lws_adns_get_cache(dns, name); - if (c) { - lwsl_err("%s: using cached, c->results %p\n", __func__, c->results); - m = c->results ? LADNS_RET_FOUND : LADNS_RET_FAILED; - if (c->results) - c->refcount++; - cb(wsi, name, c->results, m, opaque); - - return m; - } - - /* - * It's a 1.2.3.4 type IP address already? We don't need a dns - * server set up to be able to create an addrinfo result for that. - * - * Create it as a cached object so it follows the refcount lifecycle - * of any other result - */ - - m = lws_parse_numeric_address(name, ads, sizeof(ads)); - if (m == 4 -#if defined(LWS_WITH_IPV6) - || m == 16 -#endif - ) { - lws_async_dns_trim_cache(dns); - - c = lws_zalloc(sizeof(lws_adns_cache_t) + - sizeof(struct addrinfo) + - sizeof(lws_sockaddr46) + nlen + 1, "adns-numip"); - if (!c) - goto failed; - - ai = (struct addrinfo *)&c[1]; - sa46 = (lws_sockaddr46 *)&ai[1]; - - ai->ai_socktype = SOCK_STREAM; - memcpy(&sa46[1], name, nlen + 1); - ai->ai_canonname = (char *)&sa46[1]; - - c->results = ai; - memset(&tmq.tq, 0, sizeof(tmq.tq)); - tmq.tq.opaque = opaque; - if (wsi) { - wsi->adns_cb = cb; - lws_dll2_add_head(&wsi->adns, &tmq.tq.wsi_adns); - } else - tmq.tq.standalone_cb = cb; - lws_strncpy(tmq.name, name, sizeof(tmq.name)); - - lws_dll2_add_head(&c->list, &dns->cached); - lws_sul_schedule(context, 0, &c->sul, sul_cb_expire, - lws_now_usecs() + (3600ll * LWS_US_PER_SEC)); - } - - if (m == 4) { - ai->ai_family = sa46->sa4.sin_family = AF_INET; - ai->ai_addrlen = sizeof(sa46->sa4); - ai->ai_addr = (struct sockaddr *)&sa46->sa4; - memcpy(&sa46->sa4.sin_addr, ads, m); - - lws_async_dns_complete(&tmq.tq, c); - - return LADNS_RET_FOUND; - } - -#if defined(LWS_WITH_IPV6) - if (m == 16) { - ai->ai_family = sa46->sa6.sin6_family = AF_INET6; - ai->ai_addrlen = sizeof(sa46->sa6); - ai->ai_addr = (struct sockaddr *)&sa46->sa6; - memcpy(&sa46->sa6.sin6_addr, ads, m); - - lws_async_dns_complete(&tmq.tq, c); - - return LADNS_RET_FOUND; - } -#endif - - /* - * to try anything else we need a remote server configured... - */ - - if (!context->async_dns.dns_server_set && - lws_async_dns_init(context)) { - lwsl_notice("%s: init failed\n", __func__); - goto failed; - } - - /* there's an ongoing query we can share the result of */ - - q = lws_adns_get_query(dns, qtype, &dns->waiting, 0, name); - if (q) { - lwsl_debug("%s: dns piggybacking: %d:%s\n", __func__, - qtype, name); - if (wsi) - lws_dll2_add_head(&wsi->adns, &q->wsi_adns); - - return LADNS_RET_CONTINUING; - } - - /* - * Allocate new query / queries... this is a bit complicated because - * multiple queries in one packet are not supported peoperly in DNS - * itself, and there's no reliable other way to get both ipv6 and ipv4 - * (AAAA and A) responses in one hit. - * - * If we don't support ipv6, it's simple, we just ask for A and that's - * it. But if we do support ipv6, we need to ask twice, once for A - * and in a separate query, again for AAAA. - * - * For ipv6, A / ipv4 is routable over ipv6. So we always ask for A - * first and then if ipv6, AAAA separately. - * - * Allocate for DNS_MAX, because we may recurse and alter what we're - * looking for. - * - * 0 sizeof(*q) sizeof(*q) + DNS_MAX - * [lws_adns_q_t][ name (DNS_MAX reserved) ] [ name \0 ] - */ - - q = (lws_adns_q_t *)lws_malloc(sizeof(*q) + DNS_MAX + nlen + 1, - __func__); - if (!q) - goto failed; - memset(q, 0, sizeof(*q)); - - if (wsi) - lws_dll2_add_head(&wsi->adns, &q->wsi_adns); - - q->qtype = (uint16_t)qtype; - - if (lws_async_dns_get_new_tid(context, q)) - goto failed; - - q->tid &= 0xfffe; - q->context = context; - q->tsi = tsi; - q->opaque = opaque; - q->dns = dns; - - if (!wsi) - q->standalone_cb = cb; - - /* schedule a retry according to the retry policy on the wsi */ - if (lws_retry_sul_schedule_retry_wsi(dns->wsi, &q->sul, - lws_async_dns_sul_cb_retry, &q->retry)) - goto failed; - - /* - * We may rewrite the copy at +sizeof(*q) for CNAME recursion. Keep - * a second copy at + sizeof(*q) + DNS_MAX so we can create the cache - * entry for the original name, not the last CNAME we met. - */ - - p = (char *)&q[1]; - while (nlen--) { - *p++ = tolower(*name++); - p[DNS_MAX - 1] = p[-1]; - } - *p = '\0'; - p[DNS_MAX] = '\0'; - - lws_callback_on_writable(dns->wsi); - - lws_dll2_add_head(&q->list, &dns->waiting); - - lwsl_debug("%s: created new query\n", __func__); - - return LADNS_RET_CONTINUING; - -failed: - cb(wsi, NULL, NULL, LADNS_RET_FAILED, opaque); - - return LADNS_RET_FAILED; -} diff -Nru libwebsockets-4.0.20/lib/system/async-dns/async-dns-parse.c libwebsockets-2.4.2/lib/system/async-dns/async-dns-parse.c --- libwebsockets-4.0.20/lib/system/async-dns/async-dns-parse.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/system/async-dns/async-dns-parse.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,700 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-async-dns.h" - - -/* updates *dest, returns chars used from ls directly, else -1 for fail */ - -static int -lws_adns_parse_label(const uint8_t *pkt, int len, const uint8_t *ls, int budget, - char **dest, int dl) -{ - const uint8_t *e = pkt + len, *ols = ls; - char pointer = 0, first = 1; - uint8_t ll; - int n; - - if (budget < 1) - return 0; - - /* caller must catch end of labels */ - assert(*ls); - -again1: - if (ls >= e) - return -1; - - if (((*ls) & 0xc0) == 0xc0) { - if (budget < 2) - return -1; - /* pointer into message pkt to name to actually use */ - n = lws_ser_ru16be(ls) & 0x3fff; - if (n >= len) { - lwsl_notice("%s: illegal name pointer\n", __func__); - - return -1; - } - - /* dereference the label pointer */ - ls = pkt + n; - - /* are we being fuzzed or messed with? */ - if (((*ls) & 0xc0) == 0xc0) { - /* ... pointer to pointer is unreasonable */ - lwsl_notice("%s: label ptr to ptr invalid\n", __func__); - - return -1; - } - pointer = 1; - } - - if (ls >= e) - return -1; - - ll = *ls++; - if (ls + ll + 1 > e) { - lwsl_notice("%s: label len invalid, %d vs %d\n", __func__, - lws_ptr_diff((ls + ll + 1), pkt), lws_ptr_diff(e, pkt)); - - return -1; - } - if (ll > budget) { - lwsl_notice("%s: label too long %d vs %d\n", __func__, ll, budget); - - return -1; - } - - if (ll + 2 > dl) { - lwsl_notice("%s: qname too large\n", __func__); - - return -1; - } - - /* copy the label content into place */ - - memcpy(*dest, ls, ll); - (*dest)[ll] = '.'; - (*dest)[ll + 1] = '\0'; - *dest += ll + 1; - ls += ll; - - if (pointer) { - if (*ls) - goto again1; - - /* - * special fun rule... if whole qname was a pointer label, - * it has no 00 terminator afterwards - */ - if (first) - return 2; /* we just took the 16-bit pointer */ - - return 3; - } - - first = 0; - - if (*ls) - goto again1; - - ls++; - - return lws_ptr_diff(ls, ols); -} - -typedef int (*lws_async_dns_find_t)(const char *name, void *opaque, - uint32_t ttl, adns_query_type_t type, - const uint8_t *payload); - -/* locally query the response packet */ - -struct label_stack { - char name[DNS_MAX]; - int enl; - const uint8_t *p; -}; - -/* - * Walk the response packet, calling back to the user-provided callback for each - * A (and AAAA if LWS_IPV6=1) record with a matching name found in there. - * - * Able to recurse using an explicit non-CPU stack to resolve CNAME usages - * - * Return -1: unexpectedly failed - * 0: found - * 1: didn't find anything matching - */ - -static int -lws_adns_iterate(lws_adns_q_t *q, const uint8_t *pkt, int len, - const char *expname, lws_async_dns_find_t cb, void *opaque) -{ - const uint8_t *e = pkt + len, *p, *pay; - struct label_stack stack[4]; - int n = 0, stp = 0, ansc, m; - uint16_t rrtype, rrpaylen; - char *sp, inq; - uint32_t ttl; - - lws_strncpy(stack[0].name, expname, sizeof(stack[0].name)); - stack[0].enl = (int)strlen(expname); - -start: - ansc = lws_ser_ru16be(pkt + DHO_NANSWERS); - p = pkt + DHO_SIZEOF; - inq = 1; - - /* - * The response also includes the query... and we have to parse it - * so we can understand we reached the response... there's a QNAME - * made up of labels and then 2 x 16-bit fields, for query type and - * query class - */ - - - while (p + 14 < e && (inq || ansc)) { - - if (!inq && !stp) - ansc--; - - /* - * First is the name the query applies to... two main - * formats can appear here, one is a pointer to - * elsewhere in the message, the other separately - * provides len / data for each dotted "label", so for - * "warmcat.com" warmcat and com are given each with a - * prepended length byte. Any of those may be a pointer - * to somewhere else in the packet :-/ - * - * Paranoia is appropriate since the name length must be - * parsed out before the rest of the RR can be used and - * we can be attacked with absolutely any crafted - * content easily via UDP. - * - * So parse the name and additionally confirm it matches - * what the query the TID belongs to actually asked for. - */ - - sp = stack[0].name; - - /* while we have more labels */ - - n = lws_adns_parse_label(pkt, len, p, len, &sp, - sizeof(stack[0].name) - - lws_ptr_diff(sp, stack[0].name)); - /* includes case name won't fit */ - if (n < 0) - return -1; - - p += n; - - if (p + (inq ? 5 : 14) > e) - return -1; - - /* - * p is now just after the decoded RR name, pointing at: type - * - * We sent class = 1 = IN query... response must match - */ - - if (lws_ser_ru16be(&p[2]) != 1) { - lwsl_err("%s: non-IN response 0x%x\n", __func__, - lws_ser_ru16be(&p[2])); - - return -1; - } - - if (inq) { - lwsl_debug("%s: reached end of inq\n", __func__); - inq = 0; - p += 4; - continue; - } - - /* carefully validate the claimed RR payload length */ - - rrpaylen = lws_ser_ru16be(&p[8]); - if (p + 10 + rrpaylen > e) { /* it may be == e */ - lwsl_notice("%s: invalid RR data length\n", __func__); - - return -1; - } - - ttl = lws_ser_ru32be(&p[4]); - rrtype = lws_ser_ru16be(&p[0]); - p += 10; /* point to the payload */ - pay = p; - - /* - * Compare the RR names, allowing for the decoded labelname - * to have an extra '.' at the end. - */ - - n = lws_ptr_diff(sp, stack[0].name); - if (stack[0].name[n - 1] == '.') - n--; - - m = stack[stp].enl; - if (stack[stp].name[m - 1] == '.') - m--; - - if (n < 1 || n != m || - strncmp(stack[0].name, stack[stp].name, n)) { - lwsl_notice("%s: skipping %s vs %s\n", __func__, - stack[0].name, stack[stp].name); - goto skip; - } - - /* - * It's something we could be interested in... - * - * We can skip RRs we don't understand. But we need to deal - * with at least these and their payloads: - * - * A: 4: ipv4 address - * AAAA: 16: ipv6 address (if asked for AAAA) - * CNAME: ?: labelized name - * - * If we hit a CNAME we need to try to dereference it with - * stuff that is in the same response packet and judge it - * from that, without losing our place here. CNAMEs may - * point to CNAMEs to whatever depth we're willing to handle. - */ - - switch (rrtype) { - - case LWS_ADNS_RECORD_AAAA: - if (rrpaylen != 16) { - lwsl_err("%s: unexpected rrpaylen\n", __func__); - return -1; - } -#if defined(LWS_WITH_IPV6) - goto do_cb; -#else - break; -#endif - - case LWS_ADNS_RECORD_A: - if (rrpaylen != 4) { - lwsl_err("%s: unexpected rrpaylen4\n", __func__); - - return -1; - } -#if defined(LWS_WITH_IPV6) -do_cb: -#endif - cb(stack[0].name, opaque, ttl, rrtype, p); - break; - - case LWS_ADNS_RECORD_CNAME: - /* - * The name the CNAME refers to MAY itself be - * included elsewhere in the response packet. - * - * So switch tack, stack where to resume from and - * search for the decoded CNAME label name definition - * instead. - * - * First decode the CNAME label payload into the next - * stack level buffer for it. - */ - - if (++stp == (int)LWS_ARRAY_SIZE(stack)) { - lwsl_notice("%s: CNAMEs too deep\n", __func__); - - return -1; - } - sp = stack[stp].name; - /* get the cname alias */ - n = lws_adns_parse_label(pkt, len, p, rrpaylen, &sp, - sizeof(stack[stp].name) - - lws_ptr_diff(sp, stack[stp].name)); - /* includes case name won't fit */ - if (n < 0) - return -1; - - p += n; - - if (p + 14 > e) - return -1; -#if 0 - /* it should have exactly reached rrpaylen if only one - * CNAME, else somewhere in the middle */ - - if (p != pay + rrpaylen) { - lwsl_err("%s: cname name bad len %d\n", __func__, rrpaylen); - - return -1; - } -#endif - lwsl_notice("%s: recursing looking for %s\n", __func__, stack[stp].name); - - lwsl_info("%s: recursing looking for %s\n", __func__, - stack[stp].name); - - stack[stp].enl = lws_ptr_diff(sp, stack[stp].name); - /* when we unstack, resume from here */ - stack[stp].p = pay + rrpaylen; - goto start; - - default: - break; - } - -skip: - p += rrpaylen; - } - - if (!stp) - return 1; /* we didn't find anything, but we didn't error */ - - lwsl_info("%s: '%s' -> CNAME '%s' resolution not provided, recursing\n", - __func__, ((const char *)&q[1]) + DNS_MAX, - stack[stp].name); - - /* - * This implies there wasn't any usable definition for the - * CNAME in the end, eg, only AAAA when we needed an A. - * - * It's also legit if the DNS just returns the CNAME, and that server - * did not directly know the next step in resolution of the CNAME, so - * instead of putting the resolution elsewhere in the response, has - * told us just the CNAME and left it to us to find out its resolution - * separately. - * - * Reset this request to be for the CNAME, and restart the request - * action with a new tid. - */ - - if (lws_async_dns_get_new_tid(q->context, q)) - return -1; - - q->tid &= 0xfffe; - q->asked = q->responded = 0; -#if defined(LWS_WITH_IPV6) - q->sent[1] = 0; -#endif - q->sent[0] = 0; - q->recursion++; - if (q->recursion == DNS_RECURSION_LIMIT) { - lwsl_err("%s: recursion overflow\n", __func__); - - return -1; - } - - if (q->firstcache) - lws_adns_cache_destroy(q->firstcache); - q->firstcache = NULL; - - /* overwrite the query name with the CNAME */ - - n = 0; - { - char *cp = (char *)&q[1]; - - while (stack[stp].name[n]) - *cp++ = tolower(stack[stp].name[n++]); - /* trim the following . if any */ - if (n && cp[-1] == '.') - cp--; - *cp = '\0'; - } - - lws_callback_on_writable(q->dns->wsi); - - return 2; -} - -int -lws_async_dns_estimate(const char *name, void *opaque, uint32_t ttl, - adns_query_type_t type, const uint8_t *payload) -{ - size_t *est = (size_t *)opaque, my; - - my = sizeof(struct addrinfo); - if (type == LWS_ADNS_RECORD_AAAA) - my += sizeof(struct sockaddr_in6); - else - my += sizeof(struct sockaddr_in); - - *est += my; - - return 0; -} - -struct adstore { - const char *name; - struct addrinfo *pos; - struct addrinfo *prev; - int ctr; - uint32_t smallest_ttl; - uint8_t flags; -}; - -/* - * Callback for each A or AAAA record, creating getaddrinfo-compatible results - * into the preallocated exact-sized storage. - */ -int -lws_async_dns_store(const char *name, void *opaque, uint32_t ttl, - adns_query_type_t type, const uint8_t *payload) -{ - struct adstore *adst = (struct adstore *)opaque; -#if defined(_DEBUG) - char buf[48]; -#endif - size_t i; - - if (ttl < adst->smallest_ttl || !adst->ctr) - adst->smallest_ttl = ttl; - - if (adst->prev) - adst->prev->ai_next = adst->pos; - adst->prev = adst->pos; - - adst->pos->ai_flags = 0; - adst->pos->ai_family = type == LWS_ADNS_RECORD_AAAA ? - AF_INET6 : AF_INET; - adst->pos->ai_socktype = SOCK_STREAM; - adst->pos->ai_protocol = IPPROTO_UDP; /* no meaning */ - adst->pos->ai_addrlen = type == LWS_ADNS_RECORD_AAAA ? - sizeof(struct sockaddr_in6) : - sizeof(struct sockaddr_in); - adst->pos->ai_canonname = (char *)adst->name; - adst->pos->ai_addr = (struct sockaddr *)&adst->pos[1]; - adst->pos->ai_next = NULL; - -#if defined(LWS_WITH_IPV6) - if (type == LWS_ADNS_RECORD_AAAA) { - struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&adst->pos[1]; - - i = sizeof(*in6); - memset(in6, 0, i); - in6->sin6_family = adst->pos->ai_family; - memcpy(in6->sin6_addr.s6_addr, payload, 16); - adst->flags |= 2; - } else -#endif - { - struct sockaddr_in *in = (struct sockaddr_in *)&adst->pos[1]; - - i = sizeof(*in); - memset(in, 0, i); - in->sin_family = adst->pos->ai_family; - memcpy(&in->sin_addr.s_addr, payload, 4); - adst->flags |= 1; - } - - adst->pos = (struct addrinfo *)((uint8_t *)adst->pos + - sizeof(struct addrinfo) + i); - -#if defined(_DEBUG) - if (lws_write_numeric_address(payload, - type == LWS_ADNS_RECORD_AAAA ? 16 : 4, - buf, sizeof(buf)) > 0) - lwsl_info("%s: %d: %s: %s\n", __func__, adst->ctr, - adst->name, buf); -#endif - adst->ctr++; - - return 0; -} - -/* - * We want to parse out all A or AAAA records - */ - -void -lws_adns_parse_udp(lws_async_dns_t *dns, const uint8_t *pkt, size_t len) -{ - const char *nm, *nmcname; - lws_adns_cache_t *c; - struct adstore adst; - lws_adns_q_t *q; - int n, ncname; - size_t est; - - // lwsl_hexdump_notice(pkt, len); - - /* we have to at least have the header */ - - if (len < DHO_SIZEOF) - return; - - /* we asked with one query, so anything else is bogus */ - - if (lws_ser_ru16be(pkt + DHO_NQUERIES) != 1) - return; - - /* match both A and AAAA queries if any */ - - q = lws_adns_get_query(dns, 0, &dns->waiting, - lws_ser_ru16be(pkt + DHO_TID), NULL); - if (!q) { - lwsl_notice("%s: dropping unknown query tid 0x%x\n", - __func__, lws_ser_ru16be(pkt + DHO_TID)); - - return; - } - - /* we can get dups... drop any that have already happened */ - - n = 1 << (lws_ser_ru16be(pkt + DHO_TID) & 1); - if (q->responded & n) { - lwsl_notice("%s: dup\n", __func__); - goto fail_out; - } - - q->responded |= n; - - /* we want to confirm the results against what we last requested... */ - - nmcname = ((const char *)&q[1]); - - /* - * First walk the packet figuring out the allocation needed for all - * the results. Produce the following layout at c - * - * lws_adns_cache_t: new cache object - * [struct addrinfo + struct sockaddr_in or _in6]: for each A or AAAA - * char []: copy of resolved name - */ - - ncname = (int)strlen(nmcname) + 1; - - est = sizeof(lws_adns_cache_t) + ncname; - if (lws_ser_ru16be(pkt + DHO_NANSWERS)) { - int ir = lws_adns_iterate(q, pkt, (int)len, nmcname, - lws_async_dns_estimate, &est); - if (ir < 0) - goto fail_out; - - if (ir == 2) /* CNAME recursive resolution */ - return; - } - - /* but we want to create the cache entry against the original request */ - - nm = ((const char *)&q[1]) + DNS_MAX; - n = (int)strlen(nm) + 1; - - lwsl_info("%s: create cache entry for %s, %zu\n", __func__, nm, - est - sizeof(lws_adns_cache_t)); - c = lws_malloc(est, "async-dns-entry"); - if (!c) { - lwsl_err("%s: OOM %zu\n", __func__, est); - goto fail_out; - } - memset(c, 0, sizeof(*c)); - - /* place it at end, no need to care about alignment padding */ - adst.name = ((const char *)c) + est - n; - memcpy((char *)adst.name, nm, n); - - /* - * Then walk the packet again, placing the objects we accounted for - * the first time into the result allocation after the cache object - * and copy of the name - */ - - adst.pos = (struct addrinfo *)&c[1]; - adst.prev = NULL; - adst.ctr = 0; - adst.smallest_ttl = 3600; - adst.flags = 0; - - /* - * smallest_ttl applies as it is to empty results (NXDOMAIN), or is - * set to the minimum ttl seen in all the results. - */ - - if (lws_ser_ru16be(pkt + DHO_NANSWERS) && - lws_adns_iterate(q, pkt, (int)len, nmcname, lws_async_dns_store, &adst) < 0) { - lws_free(c); - goto fail_out; - } - - if (lws_ser_ru16be(pkt + DHO_NANSWERS)) { - c->results = (struct addrinfo *)&c[1]; - if (q->last) /* chain the second one on */ - *q->last = c->results; - else /* first one had no results, set first guy's c->results */ - if (q->firstcache) - q->firstcache->results = c->results; - } - - if (adst.prev) /* so we know where to continue the addrinfo list */ - /* can be NULL if first resp empty */ - q->last = &adst.prev->ai_next; - - if (q->firstcache) { /* also need to free chain when we free this guy */ - q->firstcache->chain = c; - c->firstcache = q->firstcache; - } else { - - q->firstcache = c; - c->incomplete = q->responded != q->asked; - - /* - * Only register the first one into the cache... - * Trim the oldest cache entry if necessary - */ - - lws_async_dns_trim_cache(dns); - - /* - * cache the first results object... if a second one comes, - * we won't directly register it but will chain it on to this - * first one and continue to addinfo ai_next linked list from - * the first into the second - */ - - c->flags = adst.flags; - lws_dll2_add_head(&c->list, &dns->cached); - lws_sul_schedule(q->context, 0, &c->sul, sul_cb_expire, - lws_now_usecs() + - (adst.smallest_ttl * LWS_US_PER_SEC)); - } - - if (q->responded != q->asked) - return; - - /* - * Now we captured everything into the new object, return the - * addrinfo results, if any, to all interested wsi, if any... - */ - - c->incomplete = 0; - lws_async_dns_complete(q, q->firstcache); - - /* - * the query is completely finished with - */ - -fail_out: - lws_adns_q_destroy(q); -} - diff -Nru libwebsockets-4.0.20/lib/system/async-dns/private-lib-async-dns.h libwebsockets-2.4.2/lib/system/async-dns/private-lib-async-dns.h --- libwebsockets-4.0.20/lib/system/async-dns/private-lib-async-dns.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/system/async-dns/private-lib-async-dns.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - - -#define DNS_MAX 96 /* Maximum host name */ -#define DNS_RECURSION_LIMIT 3 -#define DNS_PACKET_LEN 1400 /* Buffer size for DNS packet */ -#define MAX_CACHE_ENTRIES 10 /* Dont cache more than that */ -#define DNS_QUERY_TIMEOUT 30 /* Query timeout, seconds */ - -/* - * ... when we completed a query then the query object is destroyed and a - * cache object below is created with the results in getaddrinfo format - * appended to the allocation - */ - -typedef struct lws_adns_cache { - lws_sorted_usec_list_t sul; /* for cache TTL management */ - lws_dll2_t list; - - struct lws_adns_cache *firstcache; - struct lws_adns_cache *chain; - struct addrinfo *results; - uint8_t flags; /* b0 = has ipv4, b1 = has ipv6 */ - char refcount; - char incomplete; - /* name, and then result struct addrinfos overallocated here */ -} lws_adns_cache_t; - -/* - * these objects are used while a query is ongoing... - */ - -typedef struct { - lws_sorted_usec_list_t sul; /* per-query write retry timer */ - lws_dll2_t list; - - lws_dll2_owner_t wsi_adns; - lws_async_dns_cb_t standalone_cb; /* if not associated to wsi */ - struct lws_context *context; - void *opaque; - struct addrinfo **last; - lws_async_dns_t *dns; - - lws_adns_cache_t *firstcache; - - lws_async_dns_retcode_t ret; - uint16_t tid; - uint16_t qtype; - uint16_t retry; - uint8_t tsi; - -#if defined(LWS_WITH_IPV6) - uint8_t sent[2]; -#else - uint8_t sent[1]; -#endif - uint8_t asked; - uint8_t responded; - - uint8_t recursion; - - /* name overallocated here */ -} lws_adns_q_t; - -enum { - DHO_TID, - DHO_FLAGS = 2, - DHO_NQUERIES = 4, - DHO_NANSWERS = 6, - DHO_NAUTH = 8, - DHO_NOTHER = 10, - - DHO_SIZEOF = 12 /* last */ -}; - -void -lws_adns_q_destroy(lws_adns_q_t *q); - -void -sul_cb_expire(struct lws_sorted_usec_list *sul); - -void -lws_adns_cache_destroy(lws_adns_cache_t *c); - -int -lws_async_dns_complete(lws_adns_q_t *q, lws_adns_cache_t *c); - -lws_adns_cache_t * -lws_adns_get_cache(lws_async_dns_t *dns, const char *name); - -void -lws_adns_parse_udp(lws_async_dns_t *dns, const uint8_t *pkt, size_t len); - -lws_adns_q_t * -lws_adns_get_query(lws_async_dns_t *dns, adns_query_type_t qtype, - lws_dll2_owner_t *owner, uint16_t tid, const char *name); - -void -lws_async_dns_trim_cache(lws_async_dns_t *dns); - -int -lws_async_dns_get_new_tid(struct lws_context *context, lws_adns_q_t *q); diff -Nru libwebsockets-4.0.20/lib/system/dhcpclient/dhcpclient.c libwebsockets-2.4.2/lib/system/dhcpclient/dhcpclient.c --- libwebsockets-4.0.20/lib/system/dhcpclient/dhcpclient.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/system/dhcpclient/dhcpclient.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,779 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-system-dhcpclient.h" - -typedef enum { - LDHC_INIT_REBOOT, - LDHC_REBOOTING, /* jitterwait */ - LDHC_INIT, /* issue DHCPDISCOVER */ - LDHC_SELECTING, - LDHC_REQUESTING, - LDHC_REBINDING, - LDHC_BOUND, - LDHC_RENEWING -} lws_dhcpc_state_t; - -enum { - LWSDHCPDISCOVER = 1, - LWSDHCPOFFER, - LWSDHCPREQUEST, - LWSDHCPDECLINE, - LWSDHCPACK, - LWSDHCPNACK, - LWSDHCPRELEASE, - - IPV4_PROPOSED = 0, - IPV4_SERVER, - IPV4_ROUTER, - IPV4_SUBNET_MASK, - IPV4_BROADCAST, - IPV4_TIME_SERVER, - IPV4_DNS_SRV_1, - IPV4_DNS_SRV_2, - IPV4_DNS_SRV_3, - IPV4_DNS_SRV_4, - IPV4_LEASE_SECS, - IPV4_REBINDING_SECS, - IPV4_RENEWAL_SECS, - - _IPV4_COUNT, - - LWSDHCPOPT_PAD = 0, - LWSDHCPOPT_SUBNET_MASK = 1, - LWSDHCPOPT_TIME_OFFSET = 2, - LWSDHCPOPT_ROUTER = 3, - LWSDHCPOPT_TIME_SERVER = 4, - LWSDHCPOPT_NAME_SERVER = 5, - LWSDHCPOPT_DNSERVER = 6, - LWSDHCPOPT_LOG_SERVER = 7, - LWSDHCPOPT_COOKIE_SERVER = 8, - LWSDHCPOPT_LPR_SERVER = 9, - LWSDHCPOPT_IMPRESS_SERVER = 10, - LWSDHCPOPT_RESLOC_SERVER = 11, - LWSDHCPOPT_HOST_NAME = 12, - LWSDHCPOPT_BOOTFILE_SIZE = 13, - LWSDHCPOPT_MERIT_DUMP_FILE = 14, - LWSDHCPOPT_DOMAIN_NAME = 15, - LWSDHCPOPT_SWAP_SERVER = 16, - LWSDHCPOPT_ROOT_PATH = 17, - LWSDHCPOPT_EXTENSIONS_PATH = 18, - LWSDHCPOPT_BROADCAST_ADS = 28, - - LWSDHCPOPT_REQUESTED_ADS = 50, - LWSDHCPOPT_LEASE_TIME = 51, - LWSDHCPOPT_OPTION_OVERLOAD = 52, - LWSDHCPOPT_MESSAGE_TYPE = 53, - LWSDHCPOPT_SERVER_ID = 54, - LWSDHCPOPT_PARAM_REQ_LIST = 55, - LWSDHCPOPT_MESSAGE = 56, - LWSDHCPOPT_MAX_DHCP_MSG_SIZE = 57, - LWSDHCPOPT_RENEWAL_TIME = 58, /* AKA T1 */ - LWSDHCPOPT_REBINDING_TIME = 59, /* AKA T2 */ - LWSDHCPOPT_VENDOR_CLASS_ID = 60, - LWSDHCPOPT_CLIENT_ID = 61, - - LWSDHCPOPT_END_OPTIONS = 255 -}; - -typedef struct lws_dhcpc_req { - lws_dll2_t list; - char domain[64]; - struct lws_context *context; - lws_sorted_usec_list_t sul_conn; - lws_sorted_usec_list_t sul_write; - dhcpc_cb_t cb; /* cb on completion / failure */ - void *opaque; /* ignored by lws, give to cb */ - - /* these are separated so we can close the bcast one asynchronously */ - struct lws *wsi_raw; /* for broadcast */ - lws_dhcpc_state_t state; - - uint32_t ipv4[_IPV4_COUNT]; - - uint16_t retry_count_conn; - uint16_t retry_count_write; - uint8_t mac[6]; - uint8_t xid[4]; - uint8_t af; /* address family */ -} lws_dhcpc_req_t; -/* interface name is overallocated here */ - -static const uint32_t botable2[] = { 1500, 1750, 5000 /* in case dog slow */ }; -static const lws_retry_bo_t bo2 = { - botable2, LWS_ARRAY_SIZE(botable2), LWS_RETRY_CONCEAL_ALWAYS, 0, 0, 20 }; - -static const uint8_t rawdisc[] = { - 0x45, 0x00, 0, 0, 0, 0, 0x40, 0, 0x2e, IPPROTO_UDP, - 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, - 0, 68, 0, 67, 0, 0, 0, 0 -}; - -#define LDHC_OP_BOOTREQUEST 1 -#define LDHC_OP_BOOTREPLY 2 - -/* - * IPv4... max total 576 - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | op (1) | htype (1) | hlen (1) | hops (1) | - * +---------------+---------------+---------------+---------------+ - * | +04 xid (4) | - * +-------------------------------+-------------------------------+ - * | +08 secs (2) | +0a flags (2) | - * +-------------------------------+-------------------------------+ - * | +0C ciaddr (4) client IP | - * +---------------------------------------------------------------+ - * | +10 yiaddr (4) your IP | - * +---------------------------------------------------------------+ - * | +14 siaddr (4) server IP | - * +---------------------------------------------------------------+ - * | +18 giaddr (4) gateway IP | - * +---------------------------------------------------------------+ - * | | - * | +1C chaddr (16) client HWADDR | - * +---------------------------------------------------------------+ - * | | - * | +2C sname (64) | - * +---------------------------------------------------------------+ - * | | - * | +6C file (128) | - * +---------------------------------------------------------------+ - * | | - * | +EC options (variable) | - * +---------------------------------------------------------------+ - */ - -#if defined(_DEBUG) -static const char *dhcp_entry_names[] = { - "proposed ip", - "dhcp server", - "router", - "subnet mask", - "broadcast", - "time server", - "dns1", - "dns2", - "dns3", - "dns4", - "lease secs", - "rebinding secs", - "renewal secs", -}; -#endif - -static void -lws_dhcpc_retry_conn(struct lws_sorted_usec_list *sul) -{ - lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_conn); - - if (r->wsi_raw || !lws_dll2_is_detached(&r->sul_conn.list)) - return; - - /* create the UDP socket aimed at the server */ - - r->retry_count_write = 0; - r->wsi_raw = lws_create_adopt_udp(r->context->vhost_system, "0.0.0.0", - 68, LWS_CAUDP_PF_PACKET | - LWS_CAUDP_BROADCAST, - "lws-dhcpclient", (const char *)&r[1], - NULL, NULL, &bo2); - lwsl_debug("%s: created wsi_raw: %p\n", __func__, r->wsi_raw); - if (!r->wsi_raw) { - lwsl_err("%s: unable to create udp skt\n", __func__); - - lws_retry_sul_schedule(r->context, 0, &r->sul_conn, &bo2, - lws_dhcpc_retry_conn, - &r->retry_count_conn); - - return; - } - - /* force the network if up */ - lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 0); - lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 1); - - r->wsi_raw->user_space = r; - r->wsi_raw->user_space_externally_allocated = 1; - - lws_get_random(r->wsi_raw->context, r->xid, 4); -} - -static void -lws_dhcpc_retry_write(struct lws_sorted_usec_list *sul) -{ - lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_write); - - lwsl_debug("%s\n", __func__); - - if (r && r->wsi_raw) - lws_callback_on_writable(r->wsi_raw); -} - -#if 0 -static int -lws_sys_dhcpc_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *l, - int current, int target) -{ - lws_dhcpc_req_t *r = lws_container_of(l, lws_dhcpc_req_t, notify_link); - - if (target != LWS_SYSTATE_TIME_VALID || v->set_time) - return 0; - - /* it's trying to do it ever since the protocol / vhost was set up */ - - return 1; -} -#endif - -static int -lws_dhcpc_prep(uint8_t *start, int bufsiz, lws_dhcpc_req_t *r, int op) -{ - uint8_t *p = start; - - memset(start, 0, bufsiz); - - *p++ = 1; - *p++ = 1; - *p++ = 6; /* sizeof ethernet MAC */ - - memcpy(p + 1, r->xid, 4); - -// p[7] = 0x80; /* broadcast flag */ - - p += 0x1c - 3; - - if (lws_plat_ifname_to_hwaddr(r->wsi_raw->desc.sockfd, - (const char *)&r[1], r->mac, 6) < 0) - return -1; - - memcpy(p, r->mac, 6); - - p += 16 + 64 + 128; - - *p++ = 0x63; /* RFC2132 Magic Cookie indicates start of options */ - *p++ = 0x82; - *p++ = 0x53; - *p++ = 0x63; - - *p++ = LWSDHCPOPT_MESSAGE_TYPE; - *p++ = 1; /* length */ - *p++ = op; - - switch (op) { - case LWSDHCPDISCOVER: - *p++ = LWSDHCPOPT_PARAM_REQ_LIST; - *p++ = 4; /* length */ - *p++ = 1; /* subnet mask */ - *p++ = 3; /* router */ - *p++ = 15; /* domain name */ - *p++ = 6; /* DNServer */ - break; - case LWSDHCPREQUEST: - *p++ = LWSDHCPOPT_REQUESTED_ADS; - *p++ = 4; /* length */ - lws_ser_wu32be(p, r->ipv4[IPV4_PROPOSED]); - p += 4; - *p++ = LWSDHCPOPT_SERVER_ID; - *p++ = 4; /* length */ - lws_ser_wu32be(p, r->ipv4[IPV4_SERVER]); - p += 4; - break; - } - - *p++ = LWSDHCPOPT_END_OPTIONS; - - return lws_ptr_diff(p, start); -} - -static int -callback_dhcpc(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - lws_dhcpc_req_t *r = (lws_dhcpc_req_t *)user; - uint8_t pkt[LWS_PRE + 576], *p = pkt + LWS_PRE, *end; - int n, m; - - switch (reason) { - - case LWS_CALLBACK_RAW_ADOPT: - lwsl_debug("%s: LWS_CALLBACK_RAW_ADOPT\n", __func__); - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("%s: udp conn failed\n", __func__); - - /* fallthru */ - case LWS_CALLBACK_RAW_CLOSE: - lwsl_debug("%s: LWS_CALLBACK_RAW_CLOSE\n", __func__); - if (!r) - break; - r->wsi_raw = NULL; - lws_sul_schedule(r->context, 0, &r->sul_write, NULL, - LWS_SET_TIMER_USEC_CANCEL); - if (r->state != LDHC_BOUND) { - r->state = LDHC_INIT; - lws_retry_sul_schedule(r->context, 0, &r->sul_conn, &bo2, - lws_dhcpc_retry_conn, - &r->retry_count_conn); - } - break; - - case LWS_CALLBACK_RAW_RX: - - switch (r->state) { - case LDHC_INIT: /* expect DHCPOFFER */ - case LDHC_REQUESTING: /* expect DHCPACK */ - /* - * We should check carefully if we like what we were - * sent... anything can spam us with crafted replies - */ - if (len < 0x100) - break; - - p = (uint8_t *)in + 28; /* skip to UDP payload */ - if (p[0] != 2 || p[1] != 1 || p[2] != 6) - break; - - if (memcmp(&p[4], r->xid, 4)) /* must be our xid */ - break; - - if (memcmp(&p[0x1c], r->mac, 6)) /* our netif mac? */ - break; - - /* the DHCP magic cookie must be in place */ - if (lws_ser_ru32be(&p[0xec]) != 0x63825363) - break; - - r->ipv4[IPV4_PROPOSED] = lws_ser_ru32be(&p[0x10]); - r->ipv4[IPV4_SERVER] = lws_ser_ru32be(&p[0x14]); - - /* it looks legit so far... look at the options */ - - end = (uint8_t *)in + len; - p += 0xec + 4; - while (p < end) { - uint8_t c = *p++; - uint8_t l = 0; - - if (c && c != 0xff) { - /* pad 0 and EOT 0xff have no length */ - l = *p++; - if (!l) { - lwsl_err("%s: zero length\n", - __func__); - goto broken; - } - if (p + l > end) { - /* ...nice try... */ - lwsl_err("%s: bad len\n", - __func__); - goto broken; - } - } - - if (c == 0xff) /* end of options */ - break; - - m = 0; - switch (c) { - case LWSDHCPOPT_SUBNET_MASK: - n = IPV4_SUBNET_MASK; - goto get_ipv4; - - case LWSDHCPOPT_ROUTER: - n = IPV4_ROUTER; - goto get_ipv4; - - case LWSDHCPOPT_TIME_SERVER: - n = IPV4_TIME_SERVER; - goto get_ipv4; - - case LWSDHCPOPT_BROADCAST_ADS: - n = IPV4_BROADCAST; - goto get_ipv4; - - case LWSDHCPOPT_LEASE_TIME: - n = IPV4_LEASE_SECS; - goto get_ipv4; - - case LWSDHCPOPT_RENEWAL_TIME: /* AKA T1 */ - n = IPV4_RENEWAL_SECS; - goto get_ipv4; - - case LWSDHCPOPT_REBINDING_TIME: /* AKA T2 */ - n = IPV4_REBINDING_SECS; - goto get_ipv4; - - case LWSDHCPOPT_DNSERVER: - if (l & 3) - break; - m = IPV4_DNS_SRV_1; - while (l && m - IPV4_DNS_SRV_1 < 4) { - r->ipv4[m++] = lws_ser_ru32be(p); - l -= 4; - p += 4; - } - break; - case LWSDHCPOPT_DOMAIN_NAME: - m = l; - if (m > (int)sizeof(r->domain) - 1) - m = sizeof(r->domain) - 1; - memcpy(r->domain, p, m); - r->domain[m] = '\0'; - break; - - case LWSDHCPOPT_MESSAGE_TYPE: - /* - * Confirm this is the right message - * for the state of the negotiation - */ - if (r->state == LDHC_INIT && - *p != LWSDHCPOFFER) - goto broken; - if (r->state == LDHC_REQUESTING && - *p != LWSDHCPACK) - goto broken; - break; - - default: - break; - } - - p += l; - continue; -get_ipv4: - if (l >= 4) - r->ipv4[n] = lws_ser_ru32be(p); - p += l; - continue; -broken: - memset(r->ipv4, 0, sizeof(r->ipv4)); - break; - } - -#if defined(_DEBUG) - /* dump what we have parsed out */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(dhcp_entry_names); n++) - if (n >= IPV4_LEASE_SECS) - lwsl_info("%s: %s: %ds\n", __func__, - dhcp_entry_names[n], - r->ipv4[n]); - else { - m = ntohl(r->ipv4[n]); - lws_write_numeric_address((uint8_t *)&m, - 4,(char *)pkt, 20); - lwsl_info("%s: %s: %s\n", __func__, - dhcp_entry_names[n], - pkt); - } -#endif - - /* - * Having seen everything in there... do we really feel - * we could use it? Everything critical is there? - */ - - if (!r->ipv4[IPV4_PROPOSED] || - !r->ipv4[IPV4_SERVER] || - !r->ipv4[IPV4_ROUTER] || - !r->ipv4[IPV4_SUBNET_MASK] || - !r->ipv4[IPV4_LEASE_SECS] || - !r->ipv4[IPV4_DNS_SRV_1]) { - memset(r->ipv4, 0, sizeof(r->ipv4)); - break; - } - - /* - * Network layout has to be internally consistent... - * DHCP server has to be reachable by broadcast and - * default route has to be on same subnet - */ - - if ((r->ipv4[IPV4_PROPOSED] & r->ipv4[IPV4_SUBNET_MASK]) != - (r->ipv4[IPV4_SERVER] & r->ipv4[IPV4_SUBNET_MASK])) - break; - - if ((r->ipv4[IPV4_PROPOSED] & r->ipv4[IPV4_SUBNET_MASK]) != - (r->ipv4[IPV4_ROUTER] & r->ipv4[IPV4_SUBNET_MASK])) - break; - - if (r->state == LDHC_INIT) { - lwsl_info("%s: moving to REQ\n", __func__); - r->state = LDHC_REQUESTING; - lws_callback_on_writable(r->wsi_raw); - break; - } - - /* - * that's it... commit to the configuration - */ - - /* set up our network interface as offered */ - - if (lws_plat_ifconfig_ip((const char *)&r[1], - r->wsi_raw->desc.sockfd, - (uint8_t *)&r->ipv4[IPV4_PROPOSED], - (uint8_t *)&r->ipv4[IPV4_SUBNET_MASK], - (uint8_t *)&r->ipv4[IPV4_ROUTER])) { - /* - * Problem setting the IP... maybe something - * transient like racing with NetworkManager? - * Since the sul retries are still around it - * will retry - */ - return -1; - } - - /* clear timeouts related to the broadcast socket */ - - lws_sul_schedule(r->context, 0, &r->sul_write, NULL, - LWS_SET_TIMER_USEC_CANCEL); - lws_sul_schedule(r->context, 0, &r->sul_conn, NULL, - LWS_SET_TIMER_USEC_CANCEL); - - lwsl_notice("%s: DHCP configured %s\n", __func__, - (const char *)&r[1]); - r->state = LDHC_BOUND; - - lws_state_transition_steps(&wsi->context->mgr_system, - LWS_SYSTATE_OPERATIONAL); - - r->cb(r->opaque, r->af, - (uint8_t *)&r->ipv4[IPV4_PROPOSED], 4); - - r->wsi_raw = NULL; - return -1; /* close the broadcast wsi */ - default: - break; - } - - break; - - case LWS_CALLBACK_RAW_WRITEABLE: - - if (!r) - break; - - /* - * UDP is not reliable, it can be locally dropped, or dropped - * by any intermediary or the remote peer. So even though we - * will do the write in a moment, we schedule another request - * for rewrite according to the wsi retry policy. - * - * If the result came before, we'll cancel it in the close flow. - * - * If we have already reached the end of our concealed retries - * in the policy, just close without another write. - */ - if (lws_dll2_is_detached(&r->sul_write.list) && - lws_retry_sul_schedule_retry_wsi(wsi, &r->sul_write, - lws_dhcpc_retry_write, - &r->retry_count_write)) { - /* we have reached the end of our concealed retries */ - lwsl_warn("%s: concealed retries done, failing\n", - __func__); - goto retry_conn; - } - - switch (r->state) { - case LDHC_INIT: - n = LWSDHCPDISCOVER; - goto bcast; - - case LDHC_REQUESTING: - n = LWSDHCPREQUEST; - - /* fallthru */ -bcast: - n = lws_dhcpc_prep(p + 28, sizeof(pkt) - LWS_PRE - 28, - r, n); - if (n < 0) { - lwsl_err("%s: failed to prep\n", __func__); - break; - } - - m = lws_plat_rawudp_broadcast(p, rawdisc, - LWS_ARRAY_SIZE(rawdisc), - n + 28, - r->wsi_raw->desc.sockfd, - (const char *)&r[1]); - if (m < 0) - lwsl_err("%s: Failed to write dhcp client req: " - "%d %d, errno %d\n", __func__, - n, m, LWS_ERRNO); - break; - default: - break; - } - - return 0; - -retry_conn: - lws_retry_sul_schedule(wsi->context, 0, &r->sul_conn, &bo2, - lws_dhcpc_retry_conn, - &r->retry_count_conn); - - return -1; - - default: - break; - } - - return 0; - -#if 0 -cancel_conn_timer: - lws_sul_schedule(r->context, 0, &r->sul_conn, NULL, - LWS_SET_TIMER_USEC_CANCEL); - - return 0; -#endif -} - -struct lws_protocols lws_system_protocol_dhcpc = - { "lws-dhcpclient", callback_dhcpc, 0, 128, }; - -static void -lws_dhcpc_destroy(lws_dhcpc_req_t **pr) -{ - lws_dhcpc_req_t *r = *pr; - - lws_sul_schedule(r->context, 0, &r->sul_conn, NULL, - LWS_SET_TIMER_USEC_CANCEL); - lws_sul_schedule(r->context, 0, &r->sul_write, NULL, - LWS_SET_TIMER_USEC_CANCEL); - if (r->wsi_raw) - lws_set_timeout(r->wsi_raw, 1, LWS_TO_KILL_ASYNC); - - lws_dll2_remove(&r->list); - - lws_free_set_NULL(r); -} - -int -lws_dhcpc_status(struct lws_context *context, lws_sockaddr46 *sa46) -{ - lws_dhcpc_req_t *r; - - lws_start_foreach_dll(struct lws_dll2 *, p, context->dhcpc_owner.head) { - r = (lws_dhcpc_req_t *)p; - - if (r->state == LDHC_BOUND) { - if (sa46) { - memset(sa46, 0, sizeof(*sa46)); - sa46->sa4.sin_family = AF_INET; - sa46->sa4.sin_addr.s_addr = r->ipv4[IPV4_DNS_SRV_1]; - } - return 1; - } - - } lws_end_foreach_dll(p); - - return 0; -} - -static lws_dhcpc_req_t * -lws_dhcpc_find(struct lws_context *context, const char *iface, int af) -{ - lws_dhcpc_req_t *r; - - /* see if we are already looking after this af / iface combination */ - - lws_start_foreach_dll(struct lws_dll2 *, p, context->dhcpc_owner.head) { - r = (lws_dhcpc_req_t *)p; - - if (!strcmp((const char *)&r[1], iface) && af == r->af) - return r; /* yes... */ - - } lws_end_foreach_dll(p); - - return NULL; -} - -/* - * Create a persistent dhcp client entry for network interface "iface" and AF - * type "af" - */ - -int -lws_dhcpc_request(struct lws_context *context, const char *iface, int af, - dhcpc_cb_t cb, void *opaque) -{ - lws_dhcpc_req_t *r = lws_dhcpc_find(context, iface, af); - int n; - - /* see if we are already looking after this af / iface combination */ - - if (r) - return 0; - - /* nope... let's create a request object as he asks */ - - n = strlen(iface); - r = lws_zalloc(sizeof(*r) + n + 1, __func__); - if (!r) - return 1; - - memcpy(&r[1], iface, n + 1); - r->af = af; - r->cb = cb; - r->opaque = opaque; - r->context = context; - r->state = LDHC_INIT; - - lws_dll2_add_head(&r->list, &context->dhcpc_owner); /* add him to list */ - - lws_dhcpc_retry_conn(&r->sul_conn); - - return 0; -} - -/* - * Destroy every DHCP client object related to interface "iface" - */ - -static int -_remove_if(struct lws_dll2 *d, void *opaque) -{ - lws_dhcpc_req_t *r = lws_container_of(d, lws_dhcpc_req_t, list); - - if (!opaque || !strcmp((const char *)&r[1], (const char *)opaque)) - lws_dhcpc_destroy(&r); - - return 0; -} - -int -lws_dhcpc_remove(struct lws_context *context, const char *iface) -{ - lws_dll2_foreach_safe(&context->dhcpc_owner, (void *)iface, _remove_if); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/system/dhcpclient/private-lib-system-dhcpclient.h libwebsockets-2.4.2/lib/system/dhcpclient/private-lib-system-dhcpclient.h --- libwebsockets-4.0.20/lib/system/dhcpclient/private-lib-system-dhcpclient.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/system/dhcpclient/private-lib-system-dhcpclient.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - - diff -Nru libwebsockets-4.0.20/lib/system/ntpclient/ntpclient.c libwebsockets-2.4.2/lib/system/ntpclient/ntpclient.c --- libwebsockets-4.0.20/lib/system/ntpclient/ntpclient.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/system/ntpclient/ntpclient.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,287 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 LWSNTPC_LI_NONE 0 -#define LWSNTPC_VN_3 3 -#define LWSNTPC_MODE_CLIENT 3 - -struct vhd_ntpc { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - lws_sorted_usec_list_t sul_conn; - lws_sorted_usec_list_t sul_write; /* track write retries */ - lws_state_notify_link_t notify_link; - const char *ntp_server_ads; - struct lws *wsi_udp; - uint16_t retry_count_conn; - uint16_t retry_count_write; - - char set_time; -}; - -/* - * Without a valid ntp we won't be able to do anything requiring client tls. - * - * We have our own outer backoff scheme that just keeps retrying dns lookup - * and the transaction forever. - */ - -static const uint32_t botable[] = { 1000, 1250, 1500, 2000, 3000 }; -static const lws_retry_bo_t bo = { - botable, LWS_ARRAY_SIZE(botable), LWS_RETRY_CONCEAL_ALWAYS, 0, 0, 20 }; - -/* - * Once we resolved the remote server (implying we should have network), - * we use a different policy on the wsi itself that gives it a few tries before - * failing the wsi and using to outer retry policy to get dns to a different - * server in the pool and try fresh - */ - -static const uint32_t botable2[] = { 1000, 1250, 5000 /* in case dog slow */ }; -static const lws_retry_bo_t bo2 = { - botable2, LWS_ARRAY_SIZE(botable2), LWS_ARRAY_SIZE(botable2), - /* don't conceal after the last table entry */ 0, 0, 20 }; - -static void -lws_ntpc_retry_conn(struct lws_sorted_usec_list *sul) -{ - struct vhd_ntpc *v = lws_container_of(sul, struct vhd_ntpc, sul_conn); - - lwsl_debug("%s: wsi_udp: %p\n", __func__, v->wsi_udp); - - if (v->wsi_udp || !lws_dll2_is_detached(&v->sul_conn.list)) - return; - - /* create the UDP socket aimed at the server */ - - lwsl_debug("%s: server %s\n", __func__, v->ntp_server_ads); - - v->retry_count_write = 0; - v->wsi_udp = lws_create_adopt_udp(v->vhost, v->ntp_server_ads, 123, 0, - v->protocol->name, NULL, NULL, NULL, - &bo2); - lwsl_debug("%s: created wsi_udp: %p\n", __func__, v->wsi_udp); - if (!v->wsi_udp) { - lwsl_err("%s: unable to create udp skt\n", __func__); - - lws_retry_sul_schedule(v->context, 0, &v->sul_conn, &bo, - lws_ntpc_retry_conn, &v->retry_count_conn); - } -} - -static void -lws_ntpc_retry_write(struct lws_sorted_usec_list *sul) -{ - struct vhd_ntpc *v = lws_container_of(sul, struct vhd_ntpc, sul_write); - - lwsl_debug("%s\n", __func__); - - if (v && v->wsi_udp) - lws_callback_on_writable(v->wsi_udp); -} - -static int -lws_sys_ntpc_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *l, - int current, int target) -{ - struct vhd_ntpc *v = lws_container_of(l, struct vhd_ntpc, notify_link); - - if (target != LWS_SYSTATE_TIME_VALID || v->set_time) - return 0; - - /* it's trying to do it ever since the protocol / vhost was set up */ - - return 1; -} - -static int -callback_ntpc(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - struct vhd_ntpc *v = (struct vhd_ntpc *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - uint8_t pkt[LWS_PRE + 48]; - uint64_t ns; - - switch (reason) { - - case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */ - if (v) - break; - - lwsl_debug("%s: LWS_CALLBACK_PROTOCOL_INIT:\n", __func__); - lws_protocol_vh_priv_zalloc(wsi->vhost, wsi->protocol, - sizeof(*v)); - v = (struct vhd_ntpc *)lws_protocol_vh_priv_get(wsi->vhost, - wsi->protocol); - v->context = lws_get_context(wsi); - v->vhost = lws_get_vhost(wsi); - v->protocol = lws_get_protocol(wsi); - - if (!lws_system_get_ops(wsi->context) || - !lws_system_get_ops(wsi->context)->set_clock) { - lwsl_err("%s: set up system ops for set_clock\n", - __func__); - - // return -1; - } - - /* register our lws_system notifier */ - - v->notify_link.notify_cb = lws_sys_ntpc_notify_cb; - v->notify_link.name = "ntpclient"; - lws_state_reg_notifier(&wsi->context->mgr_system, &v->notify_link); - - v->ntp_server_ads = "pool.ntp.org"; - lws_system_blob_get_single_ptr(lws_system_get_blob( - v->context, LWS_SYSBLOB_TYPE_NTP_SERVER, 0), - (const uint8_t **)&v->ntp_server_ads); - - lws_ntpc_retry_conn(&v->sul_conn); - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: /* per vhost */ - if (!v) - break; - if (v->wsi_udp) - lws_set_timeout(v->wsi_udp, 1, LWS_TO_KILL_ASYNC); - lws_state_reg_deregister(&v->notify_link); - v->wsi_udp = NULL; - goto cancel_conn_timer; - - /* callbacks related to raw socket descriptor */ - - case LWS_CALLBACK_RAW_ADOPT: - lwsl_debug("%s: LWS_CALLBACK_RAW_ADOPT\n", __func__); - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_info("%s: CONNECTION_ERROR\n", __func__); - goto do_close; - - case LWS_CALLBACK_RAW_CLOSE: - lwsl_debug("%s: LWS_CALLBACK_RAW_CLOSE\n", __func__); -do_close: - v->wsi_udp = NULL; - - /* cancel any pending write retry */ - lws_sul_schedule(v->context, 0, &v->sul_write, NULL, - LWS_SET_TIMER_USEC_CANCEL); - - if (v->set_time) - goto cancel_conn_timer; - - lws_retry_sul_schedule(v->context, 0, &v->sul_conn, &bo, - lws_ntpc_retry_conn, - &v->retry_count_conn); - break; - - case LWS_CALLBACK_RAW_RX: - - if (len != 48) - return 0; /* ignore it */ - - /* - * First get the seconds, corrected for the ntp epoch of 1900 - * vs the unix epoch of 1970. Then shift the seconds up by 1bn - * and add in the ns - */ - - ns = lws_ser_ru32be(((uint8_t *)in) + 40) - 2208988800; - ns = (ns * 1000000000) + lws_ser_ru32be(((uint8_t *)in) + 44); - - lwsl_notice("%s: Unix time: %llu\n", __func__, - (unsigned long long)ns / 1000000000); - - // lws_system_get_ops(wsi->context)->set_clock(ns / 1000); - - v->set_time = 1; - lws_state_transition_steps(&wsi->context->mgr_system, - LWS_SYSTATE_OPERATIONAL); - - /* close the wsi */ - return -1; - - case LWS_CALLBACK_RAW_WRITEABLE: - - /* - * UDP is not reliable, it can be locally dropped, or dropped - * by any intermediary or the remote peer. So even though we - * will do the write in a moment, we schedule another request - * for rewrite according to the wsi retry policy. - * - * If the result came before, we'll cancel it in the close flow. - * - * If we have already reached the end of our concealed retries - * in the policy, just close without another write. - */ - if (lws_dll2_is_detached(&v->sul_write.list) && - lws_retry_sul_schedule_retry_wsi(wsi, &v->sul_write, - lws_ntpc_retry_write, - &v->retry_count_write)) { - /* we have reached the end of our concealed retries */ - lwsl_warn("%s: concealed retries done, failing\n", __func__); - goto retry_conn; - } - - memset(pkt + LWS_PRE, 0, sizeof(pkt) - LWS_PRE); - pkt[LWS_PRE] = (LWSNTPC_LI_NONE << 6) | - (LWSNTPC_VN_3 << 3) | - (LWSNTPC_MODE_CLIENT << 0); - - if (lws_write(wsi, pkt + LWS_PRE, sizeof(pkt) - LWS_PRE, 0) == - sizeof(pkt) - LWS_PRE) - break; - - lwsl_err("%s: Failed to write ntp client req\n", __func__); - -retry_conn: - lws_retry_sul_schedule(wsi->context, 0, &v->sul_conn, &bo, - lws_ntpc_retry_conn, - &v->retry_count_conn); - - return -1; - - default: - break; - } - - return 0; - - -cancel_conn_timer: - lws_sul_schedule(v->context, 0, &v->sul_conn, NULL, - LWS_SET_TIMER_USEC_CANCEL); - - return 0; -} - -struct lws_protocols lws_system_protocol_ntpc = - { "lws-ntpclient", callback_ntpc, 0, 128, }; - diff -Nru libwebsockets-4.0.20/lib/system/README.md libwebsockets-2.4.2/lib/system/README.md --- libwebsockets-4.0.20/lib/system/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/system/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -# LWS System Helpers - -Lws now has a little collection of helper utilities for common network-based -functions necessary for normal device operation, eg, async DNS, ntpclient -(necessary for tls validation), and DHCP client. - -## Conventions - -If any system helper is enabled for build, lws creates an additional vhost -"system" at Context Creation time. Wsi that are created for the system -features are bound to this. In the context object, this is available as -`.vhost_system`. - -# Attaching to an existing context from other threads - -To simplify the case different pieces of code want to attach to a single -lws_context at runtime, from different thread contexts, lws_system has an api -via an lws_system operation function pointer where the other threads can use -platform-specific locking to request callbacks to their own code from the -lws event loop thread context safely. - -For convenience, the callback can be delayed until the system has entered or -passed a specified system state, eg, LWS_SYSTATE_OPERATIONAL so the code will -only get called back after the network, ntpclient and auth have been done. -Additionally an opaque pointer can be passed to the callback when it is called -from the lws event loop context. - -## Implementing the system-specific locking - -`lws_system_ops_t` struct has a member `.attach` - -``` - int (*attach)(struct lws_context *context, int tsi, lws_attach_cb_t *cb, - lws_system_states_t state, void *opaque, - struct lws_attach_item **get); -``` - -This should be defined in user code as setting locking, then passing the -arguments through to a non-threadsafe helper - -``` -int -__lws_system_attach(struct lws_context *context, int tsi, lws_attach_cb_t *cb, - lws_system_states_t state, void *opaque, - struct lws_attach_item **get); -``` - -that does the actual attach work. When it returns, the locking should be -unlocked and the return passed back. - -## Attaching the callback request - -User code should call the lws_system_ops_t `.attach` function like - -``` - lws_system_get_ops(context)->attach(...); -``` - -The callback function which will be called from the lws event loop context -should look like this - -``` -void my_callback(struct lws_context *context, int tsi, void *opaque); -``` - -with the callback function name passed into the (*attach)() call above. When -the callback happens, the opaque user pointer set at the (*attach)() call is -passed back to it as an argument. diff -Nru libwebsockets-4.0.20/lib/system/system.c libwebsockets-2.4.2/lib/system/system.c --- libwebsockets-4.0.20/lib/system/system.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/system/system.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,263 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -/* - * It's either a buflist (.is_direct = 0) or - * a direct pointer + len (.is_direct = 1) - */ - -const lws_system_ops_t * -lws_system_get_ops(struct lws_context *context) -{ - return context->system_ops; -} - - -void -lws_system_blob_direct_set(lws_system_blob_t *b, const uint8_t *ptr, size_t len) -{ - b->is_direct = 1; - b->u.direct.ptr = ptr; - b->u.direct.len = len; -} - -void -lws_system_blob_heap_empty(lws_system_blob_t *b) -{ - b->is_direct = 0; - lws_buflist_destroy_all_segments(&b->u.bl); -} - -int -lws_system_blob_heap_append(lws_system_blob_t *b, const uint8_t *buf, size_t len) -{ - assert(!b->is_direct); - - lwsl_debug("%s: blob %p\n", __func__, b); - - if (lws_buflist_append_segment(&b->u.bl, buf, len) < 0) - return -1; - - return 0; -} - -size_t -lws_system_blob_get_size(lws_system_blob_t *b) -{ - if (b->is_direct) - return b->u.direct.len; - - return lws_buflist_total_len(&b->u.bl); -} - -int -lws_system_blob_get(lws_system_blob_t *b, uint8_t *buf, size_t *len, size_t ofs) -{ - int n; - - if (b->is_direct) { - - assert(b->u.direct.ptr); - - if (ofs >= b->u.direct.len) { - *len = 0; - return 1; - } - - if (*len > b->u.direct.len - ofs) - *len = b->u.direct.len - ofs; - - memcpy(buf, b->u.direct.ptr + ofs, *len); - - return 0; - } - - n = lws_buflist_linear_copy(&b->u.bl, ofs, buf, *len); - if (n < 0) - return -2; - - *len = n; - - return 0; -} - -int -lws_system_blob_get_single_ptr(lws_system_blob_t *b, const uint8_t **ptr) -{ - if (b->is_direct) { - *ptr = b->u.direct.ptr; - return 0; - } - - if (!b->u.bl) - return -1; - - if (b->u.bl->next) - return -1; /* multipart buflist, no single pointer to it all */ - - *ptr = (const uint8_t *)&b->u.bl[1]; - - return 0; -} - -void -lws_system_blob_destroy(lws_system_blob_t *b) -{ - if (!b) - return; - lwsl_info("%s: blob %p\n", __func__, b); - if (!b->is_direct) - lws_buflist_destroy_all_segments(&b->u.bl); -} - -lws_system_blob_t * -lws_system_get_blob(struct lws_context *context, lws_system_blob_item_t type, - int idx) -{ - if (idx < 0 || - idx >= (int)LWS_ARRAY_SIZE(context->system_blobs)) - return NULL; - - return &context->system_blobs[type + idx]; -} - -#if defined(LWS_WITH_NETWORK) - -/* - * Caller must protect the whole call with system-specific locking - */ - -int -__lws_system_attach(struct lws_context *context, int tsi, lws_attach_cb_t cb, - lws_system_states_t state, void *opaque, - struct lws_attach_item **get) -{ - struct lws_context_per_thread *pt = &context->pt[tsi]; - struct lws_attach_item *item; - - if (!get) { - /* - * allocate and add to the head of the pt's attach list - */ - - item = lws_zalloc(sizeof(*item), __func__); - if (!item) - return 1; - - item->cb = cb; - item->opaque = opaque; - item->state = state; - - lws_dll2_add_head(&item->list, &pt->attach_owner); - - lws_cancel_service(context); - - return 0; - } - - *get = NULL; - if (!pt->attach_owner.count) - return 0; - - /* - * If any, return the first guy whose state requirement matches - */ - - lws_start_foreach_dll(struct lws_dll2 *, d, - lws_dll2_get_head(&pt->attach_owner)) { - item = lws_container_of(d, lws_attach_item_t, list); - - if (pt->context->mgr_system.state >= (int)item->state) { - *get = item; - lws_dll2_remove(d); - - /* - * We detached it, but the caller now has the - * responsibility to lws_free() *get. - */ - - return 0; - } - } lws_end_foreach_dll(d); - - /* nobody ready to go... leave *get as NULL and return cleanly */ - - return 0; -} - -int -lws_system_do_attach(struct lws_context_per_thread *pt) -{ - /* - * If nothing to do, we just return immediately - */ - - while (pt->attach_owner.count) { - - struct lws_attach_item *item; - - /* - * If anybody used the attach apis, there must be an - * implementation of the (*attach) lws_system op function - */ - - assert(pt->context->system_ops->attach); - if (!pt->context->system_ops->attach) { - lwsl_err("%s: define (*attach)\n", __func__); - return 1; - } - - /* - * System locking is applied only around this next call, while - * we detach and get a pointer to the tail attach item. We - * become responsible to free what we have detached. - */ - - if (pt->context->system_ops->attach(pt->context, pt->tid, NULL, - 0, NULL, &item)) { - lwsl_err("%s: attach problem\n", __func__); - return 1; - } - - if (!item) - /* there's nothing more to do at the moment */ - return 0; - - /* - * Do the callback from the lws event loop thread - */ - - item->cb(pt->context, pt->tid, item->opaque); - - /* it's done, destroy the item */ - - lws_free(item); - } - - return 0; -} - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/lws-gencrypto-common.c libwebsockets-2.4.2/lib/tls/lws-gencrypto-common.c --- libwebsockets-4.0.20/lib/tls/lws-gencrypto-common.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/lws-gencrypto-common.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,695 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -/* - * These came from RFC7518 (JSON Web Algorithms) Section 3 - * - * Cryptographic Algorithms for Digital Signatures and MACs - */ - -static const struct lws_jose_jwe_alg lws_gencrypto_jws_alg_map[] = { - - /* - * JWSs MAY also be created that do not provide integrity protection. - * Such a JWS is called an Unsecured JWS. An Unsecured JWS uses the - * "alg" value "none" and is formatted identically to other JWSs, but - * MUST use the empty octet sequence as its JWS Signature value. - * Recipients MUST verify that the JWS Signature value is the empty - * octet sequence. - * - * Implementations that support Unsecured JWSs MUST NOT accept such - * objects as valid unless the application specifies that it is - * acceptable for a specific object to not be integrity protected. - * Implementations MUST NOT accept Unsecured JWSs by default. In order - * to mitigate downgrade attacks, applications MUST NOT signal - * acceptance of Unsecured JWSs at a global level, and SHOULD signal - * acceptance on a per-object basis. See Section 8.5 for security - * considerations associated with using this algorithm. - */ - { /* optional */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_NONE, - LWS_JOSE_ENCTYPE_NONE, - "none", NULL, 0, 0, 0 - }, - - /* - * HMAC with SHA-2 Functions - * - * The HMAC SHA-256 MAC for a JWS is validated by computing an HMAC - * value per RFC 2104, using SHA-256 as the hash algorithm "H", using - * the received JWS Signing Input as the "text" value, and using the - * shared key. This computed HMAC value is then compared to the result - * of base64url decoding the received encoded JWS Signature value. The - * comparison of the computed HMAC value to the JWS Signature value MUST - * be done in a constant-time manner to thwart timing attacks. - * - * Alternatively, the computed HMAC value can be base64url encoded and - * compared to the received encoded JWS Signature value (also in a - * constant-time manner), as this comparison produces the same result as - * comparing the unencoded values. In either case, if the values match, - * the HMAC has been validated. - */ - - { /* required: HMAC using SHA-256 */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_SHA256, - LWS_JOSE_ENCTYPE_NONE, - LWS_JOSE_ENCTYPE_NONE, - "HS256", NULL, 0, 0, 0 - }, - { /* optional: HMAC using SHA-384 */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_SHA384, - LWS_JOSE_ENCTYPE_NONE, - LWS_JOSE_ENCTYPE_NONE, - "HS384", NULL, 0, 0, 0 - }, - { /* optional: HMAC using SHA-512 */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_SHA512, - LWS_JOSE_ENCTYPE_NONE, - LWS_JOSE_ENCTYPE_NONE, - "HS512", NULL, 0, 0, 0 - }, - - /* - * Digital Signature with RSASSA-PKCS1-v1_5 - * - * This section defines the use of the RSASSA-PKCS1-v1_5 digital - * signature algorithm as defined in Section 8.2 of RFC 3447 [RFC3447] - * (commonly known as PKCS #1), using SHA-2 [SHS] hash functions. - * - * A key of size 2048 bits or larger MUST be used with these algorithms. - * - * The RSASSA-PKCS1-v1_5 SHA-256 digital signature is generated as - * follows: generate a digital signature of the JWS Signing Input using - * RSASSA-PKCS1-v1_5-SIGN and the SHA-256 hash function with the desired - * private key. This is the JWS Signature value. - * - * The RSASSA-PKCS1-v1_5 SHA-256 digital signature for a JWS is - * validated as follows: submit the JWS Signing Input, the JWS - * Signature, and the public key corresponding to the private key used - * by the signer to the RSASSA-PKCS1-v1_5-VERIFY algorithm using SHA-256 - * as the hash function. - */ - - { /* recommended: RSASSA-PKCS1-v1_5 using SHA-256 */ - LWS_GENHASH_TYPE_SHA256, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5, - LWS_JOSE_ENCTYPE_NONE, - "RS256", NULL, 2048, 4096, 0 - }, - { /* optional: RSASSA-PKCS1-v1_5 using SHA-384 */ - LWS_GENHASH_TYPE_SHA384, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5, - LWS_JOSE_ENCTYPE_NONE, - "RS384", NULL, 2048, 4096, 0 - }, - { /* optional: RSASSA-PKCS1-v1_5 using SHA-512 */ - LWS_GENHASH_TYPE_SHA512, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5, - LWS_JOSE_ENCTYPE_NONE, - "RS512", NULL, 2048, 4096, 0 - }, - - /* - * Digital Signature with ECDSA - * - * The ECDSA P-256 SHA-256 digital signature is generated as follows: - * - * 1. Generate a digital signature of the JWS Signing Input using ECDSA - * P-256 SHA-256 with the desired private key. The output will be - * the pair (R, S), where R and S are 256-bit unsigned integers. - * 2. Turn R and S into octet sequences in big-endian order, with each - * array being be 32 octets long. The octet sequence - * representations MUST NOT be shortened to omit any leading zero - * octets contained in the values. - * - * 3. Concatenate the two octet sequences in the order R and then S. - * (Note that many ECDSA implementations will directly produce this - * concatenation as their output.) - * - * 4. The resulting 64-octet sequence is the JWS Signature value. - * - * The ECDSA P-256 SHA-256 digital signature for a JWS is validated as - * follows: - * - * 1. The JWS Signature value MUST be a 64-octet sequence. If it is - * not a 64-octet sequence, the validation has failed. - * - * 2. Split the 64-octet sequence into two 32-octet sequences. The - * first octet sequence represents R and the second S. The values R - * and S are represented as octet sequences using the Integer-to- - * OctetString Conversion defined in Section 2.3.7 of SEC1 [SEC1] - * (in big-endian octet order). - * 3. Submit the JWS Signing Input, R, S, and the public key (x, y) to - * the ECDSA P-256 SHA-256 validator. - */ - - { /* Recommended+: ECDSA using P-256 and SHA-256 */ - LWS_GENHASH_TYPE_SHA256, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_ECDSA, - LWS_JOSE_ENCTYPE_NONE, - "ES256", "P-256", 256, 256, 0 - }, - { /* optional: ECDSA using P-384 and SHA-384 */ - LWS_GENHASH_TYPE_SHA384, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_ECDSA, - LWS_JOSE_ENCTYPE_NONE, - "ES384", "P-384", 384, 384, 0 - }, - { /* optional: ECDSA using P-521 and SHA-512 */ - LWS_GENHASH_TYPE_SHA512, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_ECDSA, - LWS_JOSE_ENCTYPE_NONE, - "ES512", "P-521", 521, 521, 0 - }, -#if 0 - Not yet supported - - /* - * Digital Signature with RSASSA-PSS - * - * A key of size 2048 bits or larger MUST be used with this algorithm. - * - * The RSASSA-PSS SHA-256 digital signature is generated as follows: - * generate a digital signature of the JWS Signing Input using RSASSA- - * PSS-SIGN, the SHA-256 hash function, and the MGF1 mask generation - * function with SHA-256 with the desired private key. This is the JWS - * Signature value. - * - * The RSASSA-PSS SHA-256 digital signature for a JWS is validated as - * follows: submit the JWS Signing Input, the JWS Signature, and the - * public key corresponding to the private key used by the signer to the - * RSASSA-PSS-VERIFY algorithm using SHA-256 as the hash function and - * using MGF1 as the mask generation function with SHA-256. - * - */ - { /* optional: RSASSA-PSS using SHA-256 and MGF1 with SHA-256 */ - LWS_GENHASH_TYPE_SHA256, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS, - LWS_JOSE_ENCTYPE_NONE, - "PS256", NULL, 2048, 4096, 0 - }, - { /* optional: RSASSA-PSS using SHA-384 and MGF1 with SHA-384 */ - LWS_GENHASH_TYPE_SHA384, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS, - LWS_JOSE_ENCTYPE_NONE, - "PS384", NULL, 2048, 4096, 0 - }, - { /* optional: RSASSA-PSS using SHA-512 and MGF1 with SHA-512*/ - LWS_GENHASH_TYPE_SHA512, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS, - LWS_JOSE_ENCTYPE_NONE, - "PS512", NULL, 2048, 4096, 0 - }, -#endif - /* list terminator */ - { 0, 0, 0, 0, NULL, NULL, 0, 0, 0} -}; - -/* - * These came from RFC7518 (JSON Web Algorithms) Section 4 - * - * Cryptographic Algorithms for Key Management - * - * JWE uses cryptographic algorithms to encrypt or determine the Content - * Encryption Key (CEK). - */ - -static const struct lws_jose_jwe_alg lws_gencrypto_jwe_alg_map[] = { - - /* - * This section defines the specifics of encrypting a JWE CEK with - * RSAES-PKCS1-v1_5 [RFC3447]. The "alg" (algorithm) Header Parameter - * value "RSA1_5" is used for this algorithm. - * - * A key of size 2048 bits or larger MUST be used with this algorithm. - */ - - { /* recommended-: RSAES-PKCS1-v1_5 */ - LWS_GENHASH_TYPE_SHA256, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5, - LWS_JOSE_ENCTYPE_NONE, - "RSA1_5", NULL, 2048, 4096, 0 - }, - { /* recommended+: RSAES OAEP using default parameters */ - LWS_GENHASH_TYPE_SHA1, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP, - LWS_JOSE_ENCTYPE_NONE, - "RSA-OAEP", NULL, 2048, 4096, 0 - }, - { /* recommended+: RSAES OAEP using SHA-256 and MGF1 SHA-256 */ - LWS_GENHASH_TYPE_SHA256, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP, - LWS_JOSE_ENCTYPE_NONE, - "RSA-OAEP-256", NULL, 2048, 4096, 0 - }, - - /* - * Key Wrapping with AES Key Wrap - * - * This section defines the specifics of encrypting a JWE CEK with the - * Advanced Encryption Standard (AES) Key Wrap Algorithm [RFC3394] using - * the default initial value specified in Section 2.2.3.1 of that - * document. - * - * - */ - { /* recommended: AES Key Wrap with AES Key Wrap with defaults - using 128-bit key */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_AES_ECB, - LWS_JOSE_ENCTYPE_NONE, - "A128KW", NULL, 128, 128, 64 - }, - - { /* optional: AES Key Wrap with AES Key Wrap with defaults - using 192-bit key */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_AES_ECB, - LWS_JOSE_ENCTYPE_NONE, - "A192KW", NULL, 192, 192, 64 - }, - - { /* recommended: AES Key Wrap with AES Key Wrap with defaults - using 256-bit key */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_AES_ECB, - LWS_JOSE_ENCTYPE_NONE, - "A256KW", NULL, 256, 256, 64 - }, - - /* - * This section defines the specifics of directly performing symmetric - * key encryption without performing a key wrapping step. In this case, - * the shared symmetric key is used directly as the Content Encryption - * Key (CEK) value for the "enc" algorithm. An empty octet sequence is - * used as the JWE Encrypted Key value. The "alg" (algorithm) Header - * Parameter value "dir" is used in this case. - */ - { /* recommended */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_NONE, - LWS_JOSE_ENCTYPE_NONE, - "dir", NULL, 0, 0, 0 - }, - - /* - * Key Agreement with Elliptic Curve Diffie-Hellman Ephemeral Static - * (ECDH-ES) - * - * This section defines the specifics of key agreement with Elliptic - * Curve Diffie-Hellman Ephemeral Static [RFC6090], in combination with - * the Concat KDF, as defined in Section 5.8.1 of [NIST.800-56A]. The - * key agreement result can be used in one of two ways: - * - * 1. directly as the Content Encryption Key (CEK) for the "enc" - * algorithm, in the Direct Key Agreement mode, or - * - * 2. as a symmetric key used to wrap the CEK with the "A128KW", - * "A192KW", or "A256KW" algorithms, in the Key Agreement with Key - * Wrapping mode. - * - * A new ephemeral public key value MUST be generated for each key - * agreement operation. - * - * In Direct Key Agreement mode, the output of the Concat KDF MUST be a - * key of the same length as that used by the "enc" algorithm. In this - * case, the empty octet sequence is used as the JWE Encrypted Key - * value. The "alg" (algorithm) Header Parameter value "ECDH-ES" is - * used in the Direct Key Agreement mode. - * - * In Key Agreement with Key Wrapping mode, the output of the Concat KDF - * MUST be a key of the length needed for the specified key wrapping - * algorithm. In this case, the JWE Encrypted Key is the CEK wrapped - * with the agreed-upon key. - */ - - { /* recommended+: ECDH Ephemeral Static Key agreement Concat KDF */ - LWS_GENHASH_TYPE_SHA256, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_ECDHES, - LWS_JOSE_ENCTYPE_NONE, - "ECDH-ES", NULL, 128, 128, 0 - }, - { /* recommended: ECDH-ES + Concat KDF + wrapped by AES128KW */ - LWS_GENHASH_TYPE_SHA256, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_ECDHES, - LWS_JOSE_ENCTYPE_AES_ECB, - "ECDH-ES+A128KW", NULL, 128, 128, 0 - }, - { /* optional: ECDH-ES + Concat KDF + wrapped by AES192KW */ - LWS_GENHASH_TYPE_SHA256, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_ECDHES, - LWS_JOSE_ENCTYPE_AES_ECB, - "ECDH-ES+A192KW", NULL, 192, 192, 0 - }, - { /* recommended: ECDH-ES + Concat KDF + wrapped by AES256KW */ - LWS_GENHASH_TYPE_SHA256, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_ECDHES, - LWS_JOSE_ENCTYPE_AES_ECB, - "ECDH-ES+A256KW", NULL, 256, 256, 0 - }, - - /* - * Key Encryption with AES GCM - * - * This section defines the specifics of encrypting a JWE Content - * Encryption Key (CEK) with Advanced Encryption Standard (AES) in - * Galois/Counter Mode (GCM) ([AES] and [NIST.800-38D]). - * - * Use of an Initialization Vector (IV) of size 96 bits is REQUIRED with - * this algorithm. The IV is represented in base64url-encoded form as - * the "iv" (initialization vector) Header Parameter value. - * - * The Additional Authenticated Data value used is the empty octet - * string. - * - * The requested size of the Authentication Tag output MUST be 128 bits, - * regardless of the key size. - * - * The JWE Encrypted Key value is the ciphertext output. - * - * The Authentication Tag output is represented in base64url-encoded - * form as the "tag" (authentication tag) Header Parameter value. - * - * - * "iv" (Initialization Vector) Header Parameter - * - * The "iv" (initialization vector) Header Parameter value is the - * base64url-encoded representation of the 96-bit IV value used for the - * key encryption operation. This Header Parameter MUST be present and - * MUST be understood and processed by implementations when these - * algorithms are used. - * - * "tag" (Authentication Tag) Header Parameter - * - * The "tag" (authentication tag) Header Parameter value is the - * base64url-encoded representation of the 128-bit Authentication Tag - * value resulting from the key encryption operation. This Header - * Parameter MUST be present and MUST be understood and processed by - * implementations when these algorithms are used. - */ - { /* optional: Key wrapping with AES GCM using 128-bit key */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_AES_ECB, - LWS_JOSE_ENCTYPE_NONE, - "A128GCMKW", NULL, 128, 128, 96 - }, - - { /* optional: Key wrapping with AES GCM using 192-bit key */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_AES_ECB, - LWS_JOSE_ENCTYPE_NONE, - "A192GCMKW", NULL, 192, 192, 96 - }, - - { /* optional: Key wrapping with AES GCM using 256-bit key */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_AES_ECB, - LWS_JOSE_ENCTYPE_NONE, - "A256GCMKW", NULL, 256, 256, 96 - }, - - /* list terminator */ - { 0, 0, 0, 0, NULL, NULL } -}; - -/* - * The "enc" (encryption algorithm) Header Parameter identifies the - * content encryption algorithm used to perform authenticated encryption - * on the plaintext to produce the ciphertext and the Authentication - * Tag. This algorithm MUST be an AEAD algorithm with a specified key - * length. The encrypted content is not usable if the "enc" value does - * not represent a supported algorithm. "enc" values should either be - * registered in the IANA "JSON Web Signature and Encryption Algorithms" - * registry established by [JWA] or be a value that contains a - * Collision-Resistant Name. The "enc" value is a case-sensitive ASCII - * string containing a StringOrURI value. This Header Parameter MUST be - * present and MUST be understood and processed by implementations. - */ - -static const struct lws_jose_jwe_alg lws_gencrypto_jwe_enc_map[] = { - /* - * AES_128_CBC_HMAC_SHA_256 / 512 - * - * It uses the HMAC message authentication code [RFC2104] with the - * SHA-256 hash function [SHS] to provide message authentication, with - * the HMAC output truncated to 128 bits, corresponding to the - * HMAC-SHA-256-128 algorithm defined in [RFC4868]. For encryption, it - * uses AES in the CBC mode of operation as defined in Section 6.2 of - * [NIST.800-38A], with PKCS #7 padding and a 128-bit IV value. - * - * The AES_CBC_HMAC_SHA2 parameters specific to AES_128_CBC_HMAC_SHA_256 - * are: - * - * The input key K is 32 octets long. - * ENC_KEY_LEN is 16 octets. - * MAC_KEY_LEN is 16 octets. - * The SHA-256 hash algorithm is used for the HMAC. - * The HMAC-SHA-256 output is truncated to T_LEN=16 octets, by - * stripping off the final 16 octets. - */ - { /* required */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_SHA256, - LWS_JOSE_ENCTYPE_NONE, - LWS_JOSE_ENCTYPE_AES_CBC, - "A128CBC-HS256", NULL, 256, 256, 128 - }, - /* - * AES_192_CBC_HMAC_SHA_384 is based on AES_128_CBC_HMAC_SHA_256, but - * with the following differences: - * - * The input key K is 48 octets long instead of 32. - * ENC_KEY_LEN is 24 octets instead of 16. - * MAC_KEY_LEN is 24 octets instead of 16. - * SHA-384 is used for the HMAC instead of SHA-256. - * The HMAC SHA-384 value is truncated to T_LEN=24 octets instead of 16. - */ - { /* required */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_SHA384, - LWS_JOSE_ENCTYPE_NONE, - LWS_JOSE_ENCTYPE_AES_CBC, - "A192CBC-HS384", NULL, 384, 384, 192 - }, - /* - * AES_256_CBC_HMAC_SHA_512 is based on AES_128_CBC_HMAC_SHA_256, but - * with the following differences: - * - * The input key K is 64 octets long instead of 32. - * ENC_KEY_LEN is 32 octets instead of 16. - * MAC_KEY_LEN is 32 octets instead of 16. - * SHA-512 is used for the HMAC instead of SHA-256. - * The HMAC SHA-512 value is truncated to T_LEN=32 octets instead of 16. - */ - { /* required */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_SHA512, - LWS_JOSE_ENCTYPE_NONE, - LWS_JOSE_ENCTYPE_AES_CBC, - "A256CBC-HS512", NULL, 512, 512, 256 - }, - - /* - * The CEK is used as the encryption key. - * - * Use of an IV of size 96 bits is REQUIRED with this algorithm. - * - * The requested size of the Authentication Tag output MUST be 128 bits, - * regardless of the key size. - */ - { /* recommended: AES GCM using 128-bit key */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_NONE, - LWS_JOSE_ENCTYPE_AES_GCM, - "A128GCM", NULL, 128, 128, 96 - }, - { /* optional: AES GCM using 192-bit key */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_NONE, - LWS_JOSE_ENCTYPE_AES_GCM, - "A192GCM", NULL, 192, 192, 96 - }, - { /* recommended: AES GCM using 256-bit key */ - LWS_GENHASH_TYPE_UNKNOWN, - LWS_GENHMAC_TYPE_UNKNOWN, - LWS_JOSE_ENCTYPE_NONE, - LWS_JOSE_ENCTYPE_AES_GCM, - "A256GCM", NULL, 256, 256, 96 - }, - { 0, 0, 0, 0, NULL, NULL, 0, 0, 0 } /* sentinel */ -}; - -int -lws_gencrypto_jws_alg_to_definition(const char *alg, - const struct lws_jose_jwe_alg **jose) -{ - const struct lws_jose_jwe_alg *a = lws_gencrypto_jws_alg_map; - - while (a->alg) { - if (!strcmp(alg, a->alg)) { - *jose = a; - - return 0; - } - a++; - } - - return 1; -} - -int -lws_gencrypto_jwe_alg_to_definition(const char *alg, - const struct lws_jose_jwe_alg **jose) -{ - const struct lws_jose_jwe_alg *a = lws_gencrypto_jwe_alg_map; - - while (a->alg) { - if (!strcmp(alg, a->alg)) { - *jose = a; - - return 0; - } - a++; - } - - return 1; -} - -int -lws_gencrypto_jwe_enc_to_definition(const char *enc, - const struct lws_jose_jwe_alg **jose) -{ - const struct lws_jose_jwe_alg *e = lws_gencrypto_jwe_enc_map; - - while (e->alg) { - if (!strcmp(enc, e->alg)) { - *jose = e; - - return 0; - } - e++; - } - - return 1; -} - -size_t -lws_genhash_size(enum lws_genhash_types type) -{ - switch(type) { - case LWS_GENHASH_TYPE_UNKNOWN: - return 0; - case LWS_GENHASH_TYPE_MD5: - return 16; - case LWS_GENHASH_TYPE_SHA1: - return 20; - case LWS_GENHASH_TYPE_SHA256: - return 32; - case LWS_GENHASH_TYPE_SHA384: - return 48; - case LWS_GENHASH_TYPE_SHA512: - return 64; - } - - return 0; -} - -size_t -lws_genhmac_size(enum lws_genhmac_types type) -{ - switch(type) { - case LWS_GENHMAC_TYPE_UNKNOWN: - return 0; - case LWS_GENHMAC_TYPE_SHA256: - return 32; - case LWS_GENHMAC_TYPE_SHA384: - return 48; - case LWS_GENHMAC_TYPE_SHA512: - return 64; - } - - return 0; -} - -int -lws_gencrypto_bits_to_bytes(int bits) -{ - if (bits & 7) - return (bits / 8) + 1; - - return bits / 8; -} - -int -lws_base64_size(int bytes) -{ - return ((bytes * 4) / 3) + 6; -} - -void -lws_gencrypto_destroy_elements(struct lws_gencrypto_keyelem *el, int m) -{ - int n; - - for (n = 0; n < m; n++) - if (el[n].buf) - lws_free_set_NULL(el[n].buf); -} - -size_t lws_gencrypto_padded_length(size_t pad_block_size, size_t len) -{ - return (len / pad_block_size + 1) * pad_block_size; -} diff -Nru libwebsockets-4.0.20/lib/tls/lws-genec-common.c libwebsockets-2.4.2/lib/tls/lws-genec-common.c --- libwebsockets-4.0.20/lib/tls/lws-genec-common.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/lws-genec-common.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws_genec provides an EC abstraction api in lws that works the - * same whether you are using openssl or mbedtls crypto functions underneath. - */ -#include "private-lib-core.h" - -const struct lws_ec_curves * -lws_genec_curve(const struct lws_ec_curves *table, const char *name) -{ - const struct lws_ec_curves *c = lws_ec_curves; - - if (table) - c = table; - - while (c->name) { - if (!strcmp(name, c->name)) - return c; - c++; - } - - return NULL; -} - -//extern const struct lws_ec_curves *lws_ec_curves; - -int -lws_genec_confirm_curve_allowed_by_tls_id(const char *allowed, int id, - struct lws_jwk *jwk) -{ - struct lws_tokenize ts; - lws_tokenize_elem e; - int n, len; - - lws_tokenize_init(&ts, allowed, LWS_TOKENIZE_F_COMMA_SEP_LIST | - LWS_TOKENIZE_F_MINUS_NONTERM); - ts.len = strlen(allowed); - do { - e = lws_tokenize(&ts); - switch (e) { - case LWS_TOKZE_TOKEN: - n = 0; - while (lws_ec_curves[n].name) { - if (id != lws_ec_curves[n].tls_lib_nid) { - n++; - continue; - } - lwsl_info("match curve %s\n", - lws_ec_curves[n].name); - len = (int)strlen(lws_ec_curves[n].name); - jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].len = len; - jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf = - lws_malloc(len + 1, "cert crv"); - if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf) { - lwsl_err("%s: OOM\n", __func__); - return 1; - } - memcpy(jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, - lws_ec_curves[n].name, len + 1); - return 0; - } - break; - - case LWS_TOKZE_DELIMITER: - break; - - default: /* includes ENDED */ - lwsl_err("%s: malformed or curve name in list\n", - __func__); - - return -1; - } - } while (e > 0); - - lwsl_err("%s: unsupported curve group nid %d\n", __func__, n); - - return -1; -} - -void -lws_genec_destroy_elements(struct lws_gencrypto_keyelem *el) -{ - int n; - - for (n = 0; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; n++) - if (el[n].buf) - lws_free_set_NULL(el[n].buf); -} - -static const char *enames[] = { "crv", "x", "d", "y" }; - -int -lws_genec_dump(struct lws_gencrypto_keyelem *el) -{ - int n; - - (void)enames; - - lwsl_info(" genec %p: crv: '%s'\n", el, - !!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf ? - (char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf: "no curve name"); - - for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; - n++) { - lwsl_info(" e: %s\n", enames[n]); - lwsl_hexdump_info(el[n].buf, el[n].len); - } - - lwsl_info("\n"); - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/lws-genaes.c libwebsockets-2.4.2/lib/tls/mbedtls/lws-genaes.c --- libwebsockets-4.0.20/lib/tls/mbedtls/lws-genaes.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/lws-genaes.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,411 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws_genaes provides an abstraction api for AES in lws that works the - * same whether you are using openssl or mbedtls hash functions underneath. - */ -#include "private-lib-core.h" -#include "private-lib-jose.h" - -static int operation_map[] = { MBEDTLS_AES_ENCRYPT, MBEDTLS_AES_DECRYPT }; - -static unsigned int -_write_pkcs7_pad(uint8_t *p, int len) -{ - unsigned int n = 0, padlen = LWS_AES_CBC_BLOCKLEN * (len / - LWS_AES_CBC_BLOCKLEN + 1) - len; - - p += len; - - while (n++ < padlen) - *p++ = (uint8_t)padlen; - - return padlen; -} - -int -lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op, - enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el, - enum enum_aes_padding padding, void *engine) -{ - int n = 0; - - ctx->mode = mode; - ctx->k = el; - ctx->op = operation_map[op]; - ctx->underway = 0; - ctx->padding = padding == LWS_GAESP_WITH_PADDING; - - switch (ctx->mode) { - case LWS_GAESM_XTS: -#if defined(MBEDTLS_CIPHER_MODE_XTS) - mbedtls_aes_xts_init(&ctx->u.ctx_xts); - break; -#else - return -1; -#endif - case LWS_GAESM_GCM: - mbedtls_gcm_init(&ctx->u.ctx_gcm); - n = mbedtls_gcm_setkey(&ctx->u.ctx_gcm, MBEDTLS_CIPHER_ID_AES, - ctx->k->buf, ctx->k->len * 8); - if (n) { - lwsl_notice("%s: mbedtls_gcm_setkey: -0x%x\n", - __func__, -n); - return n; - } - return n; - default: - mbedtls_aes_init(&ctx->u.ctx); - break; - } - - switch (op) { - case LWS_GAESO_ENC: - if (ctx->mode == LWS_GAESM_XTS) -#if defined(MBEDTLS_CIPHER_MODE_XTS) - n = mbedtls_aes_xts_setkey_enc(&ctx->u.ctx_xts, - ctx->k->buf, - ctx->k->len * 8); -#else - return -1; -#endif - else - n = mbedtls_aes_setkey_enc(&ctx->u.ctx, ctx->k->buf, - ctx->k->len * 8); - break; - case LWS_GAESO_DEC: - switch (ctx->mode) { - case LWS_GAESM_XTS: -#if defined(MBEDTLS_CIPHER_MODE_XTS) - n = mbedtls_aes_xts_setkey_dec(&ctx->u.ctx_xts, - ctx->k->buf, - ctx->k->len * 8); - break; -#else - return -1; -#endif - - case LWS_GAESM_CFB128: - case LWS_GAESM_CFB8: - case LWS_GAESM_CTR: - case LWS_GAESM_OFB: - n = mbedtls_aes_setkey_enc(&ctx->u.ctx, ctx->k->buf, - ctx->k->len * 8); - break; - default: - n = mbedtls_aes_setkey_dec(&ctx->u.ctx, ctx->k->buf, - ctx->k->len * 8); - break; - } - break; - } - - if (n) - lwsl_notice("%s: setting key: -0x%x\n", __func__, -n); - - return n; -} - -int -lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen) -{ - int n; - - if (ctx->mode == LWS_GAESM_GCM) { - n = mbedtls_gcm_finish(&ctx->u.ctx_gcm, tag, tlen); - if (n) - lwsl_notice("%s: mbedtls_gcm_finish: -0x%x\n", - __func__, -n); - if (tag && ctx->op == MBEDTLS_AES_DECRYPT && !n) { - if (lws_timingsafe_bcmp(ctx->tag, tag, ctx->taglen)) { - lwsl_err("%s: lws_genaes_crypt tag " - "mismatch (bad first)\n", - __func__); - lwsl_hexdump_notice(tag, tlen); - lwsl_hexdump_notice(ctx->tag, ctx->taglen); - n = -1; - } - } - mbedtls_gcm_free(&ctx->u.ctx_gcm); - return n; - } - if (ctx->mode == LWS_GAESM_XTS) -#if defined(MBEDTLS_CIPHER_MODE_XTS) - mbedtls_aes_xts_free(&ctx->u.ctx_xts); -#else - return -1; -#endif - else - mbedtls_aes_free(&ctx->u.ctx); - - return 0; -} - -static int -lws_genaes_rfc3394_wrap(int wrap, int cek_bits, const uint8_t *kek, - int kek_bits, const uint8_t *in, uint8_t *out) -{ - int n, m, ret = -1, c64 = cek_bits / 64; - mbedtls_aes_context ctx; - uint8_t a[8], b[16]; - - /* - * notice the KEK key used to perform the wrapping or unwrapping is - * always the size of the AES key used, eg, A128KW == 128 bits. The - * key being wrapped or unwrapped may be larger and is set by the - * 'bits' parameter. - * - * If it's larger than the KEK key size bits, we iterate over it - */ - - mbedtls_aes_init(&ctx); - - if (wrap) { - /* - * The inputs to the key wrapping process are the KEK and the - * plaintext to be wrapped. The plaintext consists of n 64-bit - * blocks, containing the key data being wrapped. - * - * Inputs: Plaintext, n 64-bit values {P1, P2, ..., Pn}, - * and Key, K (the KEK). - * Outputs: Ciphertext, (n+1) 64-bit values - * {C0, C1, ..., Cn}. - * - * The default initial value (IV) is defined to be the - * hexadecimal constant: - * - * A[0] = IV = A6A6A6A6A6A6A6A6 - */ - memset(out, 0xa6, 8); - memcpy(out + 8, in, 8 * c64); - n = mbedtls_aes_setkey_enc(&ctx, kek, kek_bits); - } else { - /* - * 2.2.2 Key Unwrap - * - * The inputs to the unwrap process are the KEK and (n+1) - * 64-bit blocks of ciphertext consisting of previously - * wrapped key. It returns n blocks of plaintext consisting - * of the n 64-bit blocks of the decrypted key data. - * - * Inputs: Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn}, - * and Key, K (the KEK). - * - * Outputs: Plaintext, n 64-bit values {P1, P2, ..., Pn}. - */ - memcpy(a, in, 8); - memcpy(out, in + 8, 8 * c64); - n = mbedtls_aes_setkey_dec(&ctx, kek, kek_bits); - } - - if (n < 0) { - lwsl_err("%s: setkey failed\n", __func__); - goto bail; - } - - if (wrap) { - for (n = 0; n <= 5; n++) { - uint8_t *r = out + 8; - for (m = 1; m <= c64; m++) { - memcpy(b, out, 8); - memcpy(b + 8, r, 8); - if (mbedtls_internal_aes_encrypt(&ctx, b, b)) - goto bail; - - memcpy(out, b, 8); - out[7] ^= c64 * n + m; - memcpy(r, b + 8, 8); - r += 8; - } - } - ret = 0; - } else { - /* - * - */ - for (n = 5; n >= 0; n--) { - uint8_t *r = out + (c64 - 1) * 8; - for (m = c64; m >= 1; m--) { - memcpy(b, a, 8); - b[7] ^= c64 * n + m; - memcpy(b + 8, r, 8); - if (mbedtls_internal_aes_decrypt(&ctx, b, b)) - goto bail; - - memcpy(a, b, 8); - memcpy(r, b + 8, 8); - r -= 8; - } - } - - ret = 0; - for (n = 0; n < 8; n++) - if (a[n] != 0xa6) - ret = -1; - } - -bail: - if (ret) - lwsl_notice("%s: failed\n", __func__); - mbedtls_aes_free(&ctx); - - return ret; -} - -int -lws_genaes_crypt(struct lws_genaes_ctx *ctx, const uint8_t *in, size_t len, - uint8_t *out, uint8_t *iv_or_nonce_ctr_or_data_unit_16, - uint8_t *stream_block_16, size_t *nc_or_iv_off, int taglen) -{ - uint8_t iv[LWS_JWE_AES_IV_BYTES], sb[16]; - int n = 0; - - switch (ctx->mode) { - case LWS_GAESM_KW: - /* a key of length ctx->k->len is wrapped by a 128-bit KEK */ - n = lws_genaes_rfc3394_wrap(ctx->op == MBEDTLS_AES_ENCRYPT, - ctx->op == MBEDTLS_AES_ENCRYPT ? len * 8 : - (len - 8) * 8, ctx->k->buf, - ctx->k->len * 8, - in, out); - break; - case LWS_GAESM_CBC: - memcpy(iv, iv_or_nonce_ctr_or_data_unit_16, 16); - - /* - * If encrypting, we do the PKCS#7 padding. - * During decryption, the caller will need to unpad. - */ - if (ctx->padding && ctx->op == MBEDTLS_AES_ENCRYPT) { - /* - * Since we don't want to burden the caller with - * the over-allocation at the end of the input, - * we have to allocate a temp with space for it - */ - uint8_t *padin = (uint8_t *)lws_malloc( - lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN, len), - __func__); - - if (!padin) - return -1; - - memcpy(padin, in, len); - len += _write_pkcs7_pad((uint8_t *)padin, len); - n = mbedtls_aes_crypt_cbc(&ctx->u.ctx, ctx->op, len, iv, - padin, out); - lws_free(padin); - } else - n = mbedtls_aes_crypt_cbc(&ctx->u.ctx, ctx->op, len, iv, - in, out); - - break; - - case LWS_GAESM_CFB128: - memcpy(iv, iv_or_nonce_ctr_or_data_unit_16, 16); - n = mbedtls_aes_crypt_cfb128(&ctx->u.ctx, ctx->op, len, - nc_or_iv_off, iv, in, out); - break; - - case LWS_GAESM_CFB8: - memcpy(iv, iv_or_nonce_ctr_or_data_unit_16, 16); - n = mbedtls_aes_crypt_cfb8(&ctx->u.ctx, ctx->op, len, iv, - in, out); - break; - - case LWS_GAESM_CTR: - memcpy(iv, iv_or_nonce_ctr_or_data_unit_16, 16); - memcpy(sb, stream_block_16, 16); - n = mbedtls_aes_crypt_ctr(&ctx->u.ctx, len, nc_or_iv_off, - iv, sb, in, out); - memcpy(iv_or_nonce_ctr_or_data_unit_16, iv, 16); - memcpy(stream_block_16, sb, 16); - break; - - case LWS_GAESM_ECB: - n = mbedtls_aes_crypt_ecb(&ctx->u.ctx, ctx->op, in, out); - break; - - case LWS_GAESM_OFB: -#if defined(MBEDTLS_CIPHER_MODE_OFB) - memcpy(iv, iv_or_nonce_ctr_or_data_unit_16, 16); - n = mbedtls_aes_crypt_ofb(&ctx->u.ctx, len, nc_or_iv_off, iv, - in, out); - break; -#else - return -1; -#endif - - case LWS_GAESM_XTS: -#if defined(MBEDTLS_CIPHER_MODE_XTS) - memcpy(iv, iv_or_nonce_ctr_or_data_unit_16, 16); - n = mbedtls_aes_crypt_xts(&ctx->u.ctx_xts, ctx->op, len, iv, - in, out); - break; -#else - return -1; -#endif - case LWS_GAESM_GCM: - if (!ctx->underway) { - ctx->underway = 1; - - memcpy(ctx->tag, stream_block_16, taglen); - ctx->taglen = taglen; - - /* - * iv: iv_or_nonce_ctr_or_data_unit_16 - * iv_len: *nc_or_iv_off - * stream_block_16: pointer to tag - * additional data: in - * additional data len: len - */ - - n = mbedtls_gcm_starts(&ctx->u.ctx_gcm, ctx->op, - iv_or_nonce_ctr_or_data_unit_16, - *nc_or_iv_off, in, len); - if (n) { - lwsl_notice("%s: mbedtls_gcm_starts: -0x%x\n", - __func__, -n); - - return -1; - } - break; - } - - n = mbedtls_gcm_update(&ctx->u.ctx_gcm, len, in, out); - if (n) { - lwsl_notice("%s: mbedtls_gcm_update: -0x%x\n", - __func__, -n); - - return -1; - } - break; - } - - if (n) { - lwsl_notice("%s: failed: -0x%x, len %d\n", __func__, -n, (int)len); - - return -1; - } - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/lws-gencrypto.c libwebsockets-2.4.2/lib/tls/mbedtls/lws-gencrypto.c --- libwebsockets-4.0.20/lib/tls/mbedtls/lws-gencrypto.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/lws-gencrypto.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws-gencrypto openssl-specific common code - */ - -#include "private-lib-core.h" -#include "private-lib-tls-mbedtls.h" - -mbedtls_md_type_t -lws_gencrypto_mbedtls_hash_to_MD_TYPE(enum lws_genhash_types hash_type) -{ - mbedtls_md_type_t h = -1; - - switch (hash_type) { - case LWS_GENHASH_TYPE_MD5: - h = MBEDTLS_MD_MD5; - break; - case LWS_GENHASH_TYPE_SHA1: - h = MBEDTLS_MD_SHA1; - break; - case LWS_GENHASH_TYPE_SHA256: - h = MBEDTLS_MD_SHA256; - break; - case LWS_GENHASH_TYPE_SHA384: - h = MBEDTLS_MD_SHA384; - break; - case LWS_GENHASH_TYPE_SHA512: - h = MBEDTLS_MD_SHA512; - break; - default: - break; - } - - return h; -} - -int -lws_gencrypto_mbedtls_rngf(void *context, unsigned char *buf, size_t len) -{ - if ((size_t)lws_get_random(context, buf, len) == len) { - // lwsl_hexdump_err(buf, len); - return 0; - } - lwsl_err("%s: rng failed\n", __func__); - return -1; -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/lws-genec.c libwebsockets-2.4.2/lib/tls/mbedtls/lws-genec.c --- libwebsockets-4.0.20/lib/tls/mbedtls/lws-genec.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/lws-genec.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,522 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws_genec provides an EC abstraction api in lws that works the - * same whether you are using openssl or mbedtls crypto functions underneath. - */ -#include "private-lib-core.h" -#include "private-lib-tls-mbedtls.h" - -const struct lws_ec_curves lws_ec_curves[] = { - /* - * These are the curves we are willing to use by default... - * - * The 3 recommended+ (P-256) and optional curves in RFC7518 7.6 - * - * Specific keys lengths from RFC8422 p20 - */ - { "P-256", MBEDTLS_ECP_DP_SECP256R1, 32 }, - { "P-384", MBEDTLS_ECP_DP_SECP384R1, 48 }, - { "P-521", MBEDTLS_ECP_DP_SECP521R1, 66 }, - - { NULL, 0, 0 } -}; - -static int -lws_genec_keypair_import(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side, - struct lws_gencrypto_keyelem *el) -{ - const struct lws_ec_curves *curve; - mbedtls_ecp_keypair kp; - int ret = -1; - - if (el[LWS_GENCRYPTO_EC_KEYEL_CRV].len < 4) { - lwsl_notice("%s: crv '%s' (%d)\n", __func__, - el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf ? - (char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf : - "null", - el[LWS_GENCRYPTO_EC_KEYEL_CRV].len); - return -21; - } - - curve = lws_genec_curve(ctx->curve_table, - (char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf); - if (!curve) - return -22; - - /* - * d (the private part) may be missing, otherwise it and everything - * else must match the expected bignum size - */ - - if ((el[LWS_GENCRYPTO_EC_KEYEL_D].len && - el[LWS_GENCRYPTO_EC_KEYEL_D].len != curve->key_bytes) || - el[LWS_GENCRYPTO_EC_KEYEL_X].len != curve->key_bytes || - el[LWS_GENCRYPTO_EC_KEYEL_Y].len != curve->key_bytes) - return -23; - - mbedtls_ecp_keypair_init(&kp); - if (mbedtls_ecp_group_load(&kp.grp, curve->tls_lib_nid)) - goto bail1; - - ctx->has_private = !!el[LWS_GENCRYPTO_EC_KEYEL_D].len; - - /* d (the private key) is directly an mpi */ - - if (ctx->has_private && - mbedtls_mpi_read_binary(&kp.d, el[LWS_GENCRYPTO_EC_KEYEL_D].buf, - el[LWS_GENCRYPTO_EC_KEYEL_D].len)) - goto bail1; - - mbedtls_ecp_set_zero(&kp.Q); - - if (mbedtls_mpi_read_binary(&kp.Q.X, el[LWS_GENCRYPTO_EC_KEYEL_X].buf, - el[LWS_GENCRYPTO_EC_KEYEL_X].len)) - goto bail1; - - if (mbedtls_mpi_read_binary(&kp.Q.Y, el[LWS_GENCRYPTO_EC_KEYEL_Y].buf, - el[LWS_GENCRYPTO_EC_KEYEL_Y].len)) - goto bail1; - - mbedtls_mpi_lset(&kp.Q.Z, 1); - - switch (ctx->genec_alg) { - case LEGENEC_ECDH: - if (mbedtls_ecdh_get_params(ctx->u.ctx_ecdh, &kp, - (mbedtls_ecdh_side)side)) - goto bail1; - /* verify the key is consistent with the claimed curve */ - if (ctx->has_private && - mbedtls_ecp_check_privkey(&ctx->u.ctx_ecdh->grp, - &ctx->u.ctx_ecdh->d)) - goto bail1; - if (mbedtls_ecp_check_pubkey(&ctx->u.ctx_ecdh->grp, - &ctx->u.ctx_ecdh->Q)) - goto bail1; - break; - case LEGENEC_ECDSA: - if (mbedtls_ecdsa_from_keypair(ctx->u.ctx_ecdsa, &kp)) - goto bail1; - /* verify the key is consistent with the claimed curve */ - if (ctx->has_private && - mbedtls_ecp_check_privkey(&ctx->u.ctx_ecdsa->grp, - &ctx->u.ctx_ecdsa->d)) - goto bail1; - if (mbedtls_ecp_check_pubkey(&ctx->u.ctx_ecdsa->grp, - &ctx->u.ctx_ecdsa->Q)) - goto bail1; - break; - default: - goto bail1; - } - - ret = 0; - -bail1: - mbedtls_ecp_keypair_free(&kp); - - return ret; -} - -int -lws_genecdh_create(struct lws_genec_ctx *ctx, struct lws_context *context, - const struct lws_ec_curves *curve_table) -{ - memset(ctx, 0, sizeof(*ctx)); - - ctx->context = context; - ctx->curve_table = curve_table; - ctx->genec_alg = LEGENEC_ECDH; - - ctx->u.ctx_ecdh = lws_zalloc(sizeof(*ctx->u.ctx_ecdh), "genecdh"); - if (!ctx->u.ctx_ecdh) - return 1; - - mbedtls_ecdh_init(ctx->u.ctx_ecdh); - - return 0; -} - -int -lws_genecdsa_create(struct lws_genec_ctx *ctx, struct lws_context *context, - const struct lws_ec_curves *curve_table) -{ - memset(ctx, 0, sizeof(*ctx)); - - ctx->context = context; - ctx->curve_table = curve_table; - ctx->genec_alg = LEGENEC_ECDSA; - - ctx->u.ctx_ecdsa = lws_zalloc(sizeof(*ctx->u.ctx_ecdsa), "genecdsa"); - if (!ctx->u.ctx_ecdsa) - return 1; - - mbedtls_ecdsa_init(ctx->u.ctx_ecdsa); - - return 0; -} - - -int -lws_genecdh_set_key(struct lws_genec_ctx *ctx, struct lws_gencrypto_keyelem *el, - enum enum_lws_dh_side side) -{ - if (ctx->genec_alg != LEGENEC_ECDH) - return -1; - - return lws_genec_keypair_import(ctx, side, el); -} - -int -lws_genecdsa_set_key(struct lws_genec_ctx *ctx, - struct lws_gencrypto_keyelem *el) -{ - if (ctx->genec_alg != LEGENEC_ECDSA) - return -1; - - return lws_genec_keypair_import(ctx, 0, el); -} - -void -lws_genec_destroy(struct lws_genec_ctx *ctx) -{ - switch (ctx->genec_alg) { - case LEGENEC_ECDH: - if (ctx->u.ctx_ecdh) { - mbedtls_ecdh_free(ctx->u.ctx_ecdh); - lws_free(ctx->u.ctx_ecdh); - ctx->u.ctx_ecdh = NULL; - } - break; - case LEGENEC_ECDSA: - if (ctx->u.ctx_ecdsa) { - mbedtls_ecdsa_free(ctx->u.ctx_ecdsa); - lws_free(ctx->u.ctx_ecdsa); - ctx->u.ctx_ecdsa = NULL; - } - break; - default: - break; - } -} - -int -lws_genecdh_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side, - const char *curve_name, - struct lws_gencrypto_keyelem *el) -{ - const struct lws_ec_curves *curve; - mbedtls_ecdsa_context ecdsa; - mbedtls_ecp_keypair *kp; - mbedtls_mpi *mpi[3]; - int n; - - if (ctx->genec_alg != LEGENEC_ECDH) - return -1; - - curve = lws_genec_curve(ctx->curve_table, curve_name); - if (!curve) { - lwsl_err("%s: curve '%s' not supported\n", - __func__, curve_name); - - return -22; - } - - mbedtls_ecdsa_init(&ecdsa); - n = mbedtls_ecdsa_genkey(&ecdsa, curve->tls_lib_nid, - lws_gencrypto_mbedtls_rngf, - ctx->context); - if (n) { - lwsl_err("mbedtls_ecdsa_genkey failed 0x%x\n", -n); - goto bail1; - } - - kp = (mbedtls_ecp_keypair *)&ecdsa; - - n = mbedtls_ecdh_get_params(ctx->u.ctx_ecdh, kp, - (mbedtls_ecdh_side)side); - if (n) { - lwsl_err("mbedtls_ecdh_get_params failed 0x%x\n", -n); - goto bail1; - } - - /* - * we need to capture the individual element BIGNUMs into - * lws_gencrypto_keyelem, so they can be serialized, used in jwk etc - */ - - mpi[0] = &kp->Q.X; - mpi[1] = &kp->d; - mpi[2] = &kp->Q.Y; - - el[LWS_GENCRYPTO_EC_KEYEL_CRV].len = strlen(curve_name) + 1; - el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf = - lws_malloc(el[LWS_GENCRYPTO_EC_KEYEL_CRV].len, "ec"); - if (!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf) - goto bail1; - strcpy((char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, curve_name); - - for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; - n++) { - el[n].len = curve->key_bytes; - el[n].buf = lws_malloc(curve->key_bytes, "ec"); - if (!el[n].buf) - goto bail2; - - if (mbedtls_mpi_write_binary(mpi[n - 1], el[n].buf, - curve->key_bytes)) - goto bail2; - } - - mbedtls_ecdsa_free(&ecdsa); - - return 0; - -bail2: - for (n = 0; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; n++) - if (el[n].buf) - lws_free_set_NULL(el[n].buf); -bail1: - mbedtls_ecdsa_free(&ecdsa); - - lws_free_set_NULL(ctx->u.ctx_ecdh); - - return -1; -} - -int -lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name, - struct lws_gencrypto_keyelem *el) -{ - const struct lws_ec_curves *curve; - mbedtls_ecp_keypair *kp; - mbedtls_mpi *mpi[3]; - int n; - - if (ctx->genec_alg != LEGENEC_ECDSA) - return -1; - - curve = lws_genec_curve(ctx->curve_table, curve_name); - if (!curve) { - lwsl_err("%s: curve '%s' not supported\n", - __func__, curve_name); - - return -22; - } - - //mbedtls_ecdsa_init(ctx->u.ctx_ecdsa); - n = mbedtls_ecdsa_genkey(ctx->u.ctx_ecdsa, curve->tls_lib_nid, - lws_gencrypto_mbedtls_rngf, ctx->context); - if (n) { - lwsl_err("mbedtls_ecdsa_genkey failed 0x%x\n", -n); - goto bail1; - } - - /* - * we need to capture the individual element BIGNUMs into - * lws_gencrypto_keyelems, so they can be serialized, used in jwk etc - */ - - kp = (mbedtls_ecp_keypair *)ctx->u.ctx_ecdsa; - - mpi[0] = &kp->Q.X; - mpi[1] = &kp->d; - mpi[2] = &kp->Q.Y; - - el[LWS_GENCRYPTO_EC_KEYEL_CRV].len = strlen(curve_name) + 1; - el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf = - lws_malloc(el[LWS_GENCRYPTO_EC_KEYEL_CRV].len, "ec"); - if (!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf) - goto bail1; - strcpy((char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, curve_name); - - for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; - n++) { - el[n].len = curve->key_bytes; - el[n].buf = lws_malloc(curve->key_bytes, "ec"); - if (!el[n].buf) - goto bail2; - - - if (mbedtls_mpi_write_binary(mpi[n - 1], el[n].buf, el[n].len)) { - lwsl_err("%s: mbedtls_mpi_write_binary failed\n", __func__); - goto bail2; - } - } - - return 0; - -bail2: - for (n = 0; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; n++) - if (el[n].buf) - lws_free_set_NULL(el[n].buf); -bail1: - - lws_free_set_NULL(ctx->u.ctx_ecdsa); - - return -1; -} - -int -lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, int keybits, - uint8_t *sig, size_t sig_len) -{ - int n, keybytes = lws_gencrypto_bits_to_bytes(keybits); - size_t hlen = lws_genhash_size(hash_type); - mbedtls_mpi mpi_r, mpi_s; - size_t slen = sig_len; - - if (ctx->genec_alg != LEGENEC_ECDSA) - return -1; - - /* - * The ECDSA P-256 SHA-256 digital signature is generated as follows: - * - * 1. Generate a digital signature of the JWS Signing Input using ECDSA - * P-256 SHA-256 with the desired private key. The output will be - * the pair (R, S), where R and S are 256-bit unsigned integers. - * - * 2. Turn R and S into octet sequences in big-endian order, with each - * array being be 32 octets long. The octet sequence - * representations MUST NOT be shortened to omit any leading zero - * octets contained in the values. - * - * 3. Concatenate the two octet sequences in the order R and then S. - * (Note that many ECDSA implementations will directly produce this - * concatenation as their output.) - * - * 4. The resulting 64-octet sequence is the JWS Signature value. - */ - - mbedtls_mpi_init(&mpi_r); - mbedtls_mpi_init(&mpi_s); - - n = mbedtls_ecdsa_sign(&ctx->u.ctx_ecdsa->grp, &mpi_r, &mpi_s, - &ctx->u.ctx_ecdsa->d, in, hlen, - lws_gencrypto_mbedtls_rngf, ctx->context); - if (n) { - lwsl_err("%s: mbedtls_ecdsa_sign failed: -0x%x\n", - __func__, -n); - - goto bail2; - } - - if (mbedtls_mpi_write_binary(&mpi_r, sig, keybytes)) - goto bail2; - mbedtls_mpi_free(&mpi_r); - if (mbedtls_mpi_write_binary(&mpi_s, sig + keybytes, keybytes)) - goto bail1; - mbedtls_mpi_free(&mpi_s); - - return (int)slen; - -bail2: - mbedtls_mpi_free(&mpi_r); -bail1: - mbedtls_mpi_free(&mpi_s); - - return -3; -} - -int -lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, int keybits, - const uint8_t *sig, size_t sig_len) -{ - int n, keybytes = lws_gencrypto_bits_to_bytes(keybits); - size_t hlen = lws_genhash_size(hash_type); - mbedtls_mpi mpi_r, mpi_s; - - if (ctx->genec_alg != LEGENEC_ECDSA) - return -1; - - if ((int)sig_len != keybytes * 2) - return -1; - - /* - * 1. The JWS Signature value MUST be a 64-octet sequence. If it is - * not a 64-octet sequence, the validation has failed. - * - * 2. Split the 64-octet sequence into two 32-octet sequences. The - * first octet sequence represents R and the second S. The values R - * and S are represented as octet sequences using the Integer-to- - * OctetString Conversion defined in Section 2.3.7 of SEC1 [SEC1] - * (in big-endian octet order). - * - * 3. Submit the JWS Signing Input, R, S, and the public key (x, y) to - * the ECDSA P-256 SHA-256 validator. - */ - - mbedtls_mpi_init(&mpi_r); - mbedtls_mpi_init(&mpi_s); - - if (mbedtls_mpi_read_binary(&mpi_r, sig, keybytes)) - return -1; - if (mbedtls_mpi_read_binary(&mpi_s, sig + keybytes, keybytes)) - goto bail1; - - n = mbedtls_ecdsa_verify(&ctx->u.ctx_ecdsa->grp, in, hlen, - &ctx->u.ctx_ecdsa->Q, &mpi_r, &mpi_s); - - mbedtls_mpi_free(&mpi_s); - mbedtls_mpi_free(&mpi_r); - - if (n) { - lwsl_err("%s: mbedtls_ecdsa_verify failed: -0x%x\n", - __func__, -n); - - goto bail; - } - - return 0; -bail1: - mbedtls_mpi_free(&mpi_r); - -bail: - - return -3; -} - -int -lws_genecdh_compute_shared_secret(struct lws_genec_ctx *ctx, uint8_t *ss, - int *ss_len) -{ - int n; - size_t st; - if (mbedtls_ecp_check_pubkey(&ctx->u.ctx_ecdh->grp, &ctx->u.ctx_ecdh->Q) || - mbedtls_ecp_check_pubkey(&ctx->u.ctx_ecdh->grp, &ctx->u.ctx_ecdh->Qp)) { - lwsl_err("%s: both sides must be set up\n", __func__); - - return -1; - } - - n = mbedtls_ecdh_calc_secret(ctx->u.ctx_ecdh, &st, ss, *ss_len, - lws_gencrypto_mbedtls_rngf, ctx->context); - if (n) - return -1; - - *ss_len = (int)st; - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/lws-genhash.c libwebsockets-2.4.2/lib/tls/mbedtls/lws-genhash.c --- libwebsockets-4.0.20/lib/tls/mbedtls/lws-genhash.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/lws-genhash.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,190 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws_genhash provides a hash / hmac abstraction api in lws that works the - * same whether you are using openssl or mbedtls hash functions underneath. - */ -#include "libwebsockets.h" -#include - -#if (MBEDTLS_VERSION_NUMBER >= 0x02070000) -#define MBA(fn) fn##_ret -#else -#define MBA(fn) fn -#endif - -int -lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type) -{ - ctx->type = type; - - switch (ctx->type) { - case LWS_GENHASH_TYPE_MD5: - mbedtls_md5_init(&ctx->u.md5); - MBA(mbedtls_md5_starts)(&ctx->u.md5); - break; - case LWS_GENHASH_TYPE_SHA1: - mbedtls_sha1_init(&ctx->u.sha1); - MBA(mbedtls_sha1_starts)(&ctx->u.sha1); - break; - case LWS_GENHASH_TYPE_SHA256: - mbedtls_sha256_init(&ctx->u.sha256); - MBA(mbedtls_sha256_starts)(&ctx->u.sha256, 0); - break; - case LWS_GENHASH_TYPE_SHA384: - mbedtls_sha512_init(&ctx->u.sha512); - MBA(mbedtls_sha512_starts)(&ctx->u.sha512, 1 /* is384 */); - break; - case LWS_GENHASH_TYPE_SHA512: - mbedtls_sha512_init(&ctx->u.sha512); - MBA(mbedtls_sha512_starts)(&ctx->u.sha512, 0); - break; - default: - return 1; - } - - return 0; -} - -int -lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len) -{ - if (!len) - return 0; - - switch (ctx->type) { - case LWS_GENHASH_TYPE_MD5: - MBA(mbedtls_md5_update)(&ctx->u.md5, in, len); - break; - case LWS_GENHASH_TYPE_SHA1: - MBA(mbedtls_sha1_update)(&ctx->u.sha1, in, len); - break; - case LWS_GENHASH_TYPE_SHA256: - MBA(mbedtls_sha256_update)(&ctx->u.sha256, in, len); - break; - case LWS_GENHASH_TYPE_SHA384: - MBA(mbedtls_sha512_update)(&ctx->u.sha512, in, len); - break; - case LWS_GENHASH_TYPE_SHA512: - MBA(mbedtls_sha512_update)(&ctx->u.sha512, in, len); - break; - } - - return 0; -} - -int -lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result) -{ - switch (ctx->type) { - case LWS_GENHASH_TYPE_MD5: - MBA(mbedtls_md5_finish)(&ctx->u.md5, result); - mbedtls_md5_free(&ctx->u.md5); - break; - case LWS_GENHASH_TYPE_SHA1: - MBA(mbedtls_sha1_finish)(&ctx->u.sha1, result); - mbedtls_sha1_free(&ctx->u.sha1); - break; - case LWS_GENHASH_TYPE_SHA256: - MBA(mbedtls_sha256_finish)(&ctx->u.sha256, result); - mbedtls_sha256_free(&ctx->u.sha256); - break; - case LWS_GENHASH_TYPE_SHA384: - MBA(mbedtls_sha512_finish)(&ctx->u.sha512, result); - mbedtls_sha512_free(&ctx->u.sha512); - break; - case LWS_GENHASH_TYPE_SHA512: - MBA(mbedtls_sha512_finish)(&ctx->u.sha512, result); - mbedtls_sha512_free(&ctx->u.sha512); - break; - } - - return 0; -} - -int -lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type, - const uint8_t *key, size_t key_len) -{ - int t; - - ctx->type = type; - - switch (type) { - case LWS_GENHMAC_TYPE_SHA256: - t = MBEDTLS_MD_SHA256; - break; - case LWS_GENHMAC_TYPE_SHA384: - t = MBEDTLS_MD_SHA384; - break; - case LWS_GENHMAC_TYPE_SHA512: - t = MBEDTLS_MD_SHA512; - break; - default: - return -1; - } - - ctx->hmac = mbedtls_md_info_from_type(t); - if (!ctx->hmac) - return -1; - - if (mbedtls_md_init_ctx(&ctx->ctx, ctx->hmac)) - return -1; - - if (mbedtls_md_hmac_starts(&ctx->ctx, key, key_len)) { - mbedtls_md_free(&ctx->ctx); - ctx->hmac = NULL; - - return -1; - } - - return 0; -} - -int -lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len) -{ - if (!len) - return 0; - - if (mbedtls_md_hmac_update(&ctx->ctx, in, len)) - return -1; - - return 0; -} - -int -lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result) -{ - int n = 0; - - if (result) - n = mbedtls_md_hmac_finish(&ctx->ctx, result); - - mbedtls_md_free(&ctx->ctx); - ctx->hmac = NULL; - if (n) - return -1; - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/lws-genrsa.c libwebsockets-2.4.2/lib/tls/mbedtls/lws-genrsa.c --- libwebsockets-4.0.20/lib/tls/mbedtls/lws-genrsa.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/lws-genrsa.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,482 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws_genrsa provides an RSA abstraction api in lws that works the - * same whether you are using openssl or mbedtls crypto functions underneath. - */ -#include "private-lib-core.h" -#include "private-lib-tls-mbedtls.h" -#include - -void -lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el) -{ - int n; - - for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++) - if (el[n].buf) - lws_free_set_NULL(el[n].buf); -} - -static int mode_map[] = { MBEDTLS_RSA_PKCS_V15, MBEDTLS_RSA_PKCS_V21 }; - -int -lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el, - struct lws_context *context, enum enum_genrsa_mode mode, - enum lws_genhash_types oaep_hashid) -{ - memset(ctx, 0, sizeof(*ctx)); - ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa"); - if (!ctx->ctx) - return 1; - - ctx->context = context; - ctx->mode = mode; - - if (mode >= LGRSAM_COUNT) - return -1; - - mbedtls_rsa_init(ctx->ctx, mode_map[mode], 0); - - ctx->ctx->padding = mode_map[mode]; - ctx->ctx->hash_id = lws_gencrypto_mbedtls_hash_to_MD_TYPE(oaep_hashid); - - { - int n; - - mbedtls_mpi *mpi[LWS_GENCRYPTO_RSA_KEYEL_COUNT] = { - &ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P, - &ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ, - &ctx->ctx->QP, - }; - - for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++) - if (el[n].buf && - mbedtls_mpi_read_binary(mpi[n], el[n].buf, - el[n].len)) { - lwsl_notice("mpi load failed\n"); - lws_free_set_NULL(ctx->ctx); - - return -1; - } - - /* mbedtls... compute missing P & Q */ - - if ( el[LWS_GENCRYPTO_RSA_KEYEL_D].len && - !el[LWS_GENCRYPTO_RSA_KEYEL_P].len && - !el[LWS_GENCRYPTO_RSA_KEYEL_Q].len) { - if (mbedtls_rsa_complete(ctx->ctx)) { - lwsl_notice("mbedtls_rsa_complete failed\n"); - lws_free_set_NULL(ctx->ctx); - - return -1; - } - - } - } - - ctx->ctx->len = el[LWS_GENCRYPTO_RSA_KEYEL_N].len; - - return 0; -} - -static int -_rngf(void *context, unsigned char *buf, size_t len) -{ - if ((size_t)lws_get_random(context, buf, len) == len) - return 0; - - return -1; -} - -int -lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx, - enum enum_genrsa_mode mode, struct lws_gencrypto_keyelem *el, - int bits) -{ - int n; - - memset(ctx, 0, sizeof(*ctx)); - ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa"); - if (!ctx->ctx) - return -1; - - ctx->context = context; - ctx->mode = mode; - - if (mode >= LGRSAM_COUNT) - return -1; - - mbedtls_rsa_init(ctx->ctx, mode_map[mode], 0); - - n = mbedtls_rsa_gen_key(ctx->ctx, _rngf, context, bits, 65537); - if (n) { - lwsl_err("mbedtls_rsa_gen_key failed 0x%x\n", -n); - goto cleanup_1; - } - - { - mbedtls_mpi *mpi[LWS_GENCRYPTO_RSA_KEYEL_COUNT] = { - &ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P, - &ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ, - &ctx->ctx->QP, - }; - - for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++) - if (mbedtls_mpi_size(mpi[n])) { - el[n].buf = lws_malloc( - mbedtls_mpi_size(mpi[n]), "genrsakey"); - if (!el[n].buf) - goto cleanup; - el[n].len = mbedtls_mpi_size(mpi[n]); - if (mbedtls_mpi_write_binary(mpi[n], el[n].buf, - el[n].len)) - goto cleanup; - } - } - - return 0; - -cleanup: - for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++) - if (el[n].buf) - lws_free_set_NULL(el[n].buf); -cleanup_1: - lws_free(ctx->ctx); - - return -1; -} - -int -lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out, size_t out_max) -{ - size_t olen = 0; - int n; - - ctx->ctx->len = in_len; - - mbedtls_rsa_complete(ctx->ctx); - - switch(ctx->mode) { - case LGRSAM_PKCS1_1_5: - n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, _rngf, - ctx->context, - MBEDTLS_RSA_PUBLIC, - &olen, in, out, - out_max); - break; - case LGRSAM_PKCS1_OAEP_PSS: - n = mbedtls_rsa_rsaes_oaep_decrypt(ctx->ctx, _rngf, - ctx->context, - MBEDTLS_RSA_PUBLIC, - NULL, 0, - &olen, in, out, out_max); - break; - default: - return -1; - } - if (n) { - lwsl_notice("%s: -0x%x\n", __func__, -n); - - return -1; - } - - return olen; -} - -int -lws_genrsa_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out, size_t out_max) -{ - size_t olen = 0; - int n; - - ctx->ctx->len = in_len; - - mbedtls_rsa_complete(ctx->ctx); - - switch(ctx->mode) { - case LGRSAM_PKCS1_1_5: - n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, _rngf, - ctx->context, - MBEDTLS_RSA_PRIVATE, - &olen, in, out, - out_max); - break; - case LGRSAM_PKCS1_OAEP_PSS: - n = mbedtls_rsa_rsaes_oaep_decrypt(ctx->ctx, _rngf, - ctx->context, - MBEDTLS_RSA_PRIVATE, - NULL, 0, - &olen, in, out, out_max); - break; - default: - return -1; - } - if (n) { - lwsl_notice("%s: -0x%x\n", __func__, -n); - - return -1; - } - - return olen; -} - -int -lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out) -{ - int n; - - mbedtls_rsa_complete(ctx->ctx); - - switch(ctx->mode) { - case LGRSAM_PKCS1_1_5: - n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, _rngf, - ctx->context, - MBEDTLS_RSA_PUBLIC, - in_len, in, out); - break; - case LGRSAM_PKCS1_OAEP_PSS: - n = mbedtls_rsa_rsaes_oaep_encrypt(ctx->ctx, _rngf, - ctx->context, - MBEDTLS_RSA_PUBLIC, - NULL, 0, - in_len, in, out); - break; - default: - return -1; - } - if (n < 0) { - lwsl_notice("%s: -0x%x: in_len: %d\n", __func__, -n, - (int)in_len); - - return -1; - } - - return mbedtls_mpi_size(&ctx->ctx->N); -} - -int -lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out) -{ - int n; - - mbedtls_rsa_complete(ctx->ctx); - - switch(ctx->mode) { - case LGRSAM_PKCS1_1_5: - n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, _rngf, - ctx->context, - MBEDTLS_RSA_PRIVATE, - in_len, in, out); - break; - case LGRSAM_PKCS1_OAEP_PSS: - n = mbedtls_rsa_rsaes_oaep_encrypt(ctx->ctx, _rngf, - ctx->context, - MBEDTLS_RSA_PRIVATE, - NULL, 0, - in_len, in, out); - break; - default: - return -1; - } - if (n) { - lwsl_notice("%s: -0x%x: in_len: %d\n", __func__, -n, - (int)in_len); - - return -1; - } - - return mbedtls_mpi_size(&ctx->ctx->N); -} - -int -lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, const uint8_t *sig, - size_t sig_len) -{ - int n, h = lws_gencrypto_mbedtls_hash_to_MD_TYPE(hash_type); - - if (h < 0) - return -1; - - mbedtls_rsa_complete(ctx->ctx); - - switch(ctx->mode) { - case LGRSAM_PKCS1_1_5: - n = mbedtls_rsa_rsassa_pkcs1_v15_verify(ctx->ctx, NULL, NULL, - MBEDTLS_RSA_PUBLIC, - h, 0, in, sig); - break; - case LGRSAM_PKCS1_OAEP_PSS: - n = mbedtls_rsa_rsassa_pss_verify(ctx->ctx, NULL, NULL, - MBEDTLS_RSA_PUBLIC, - h, 0, in, sig); - break; - default: - return -1; - } - if (n < 0) { - lwsl_notice("%s: -0x%x\n", __func__, -n); - - return -1; - } - - return n; -} - -int -lws_genrsa_hash_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, uint8_t *sig, - size_t sig_len) -{ - int n, h = lws_gencrypto_mbedtls_hash_to_MD_TYPE(hash_type); - - if (h < 0) - return -1; - - mbedtls_rsa_complete(ctx->ctx); - - /* - * The "sig" buffer must be as large as the size of ctx->N - * (eg. 128 bytes if RSA-1024 is used). - */ - if (sig_len < ctx->ctx->len) - return -1; - - switch(ctx->mode) { - case LGRSAM_PKCS1_1_5: - n = mbedtls_rsa_rsassa_pkcs1_v15_sign(ctx->ctx, NULL, NULL, - MBEDTLS_RSA_PRIVATE, - h, 0, in, sig); - break; - case LGRSAM_PKCS1_OAEP_PSS: - n = mbedtls_rsa_rsassa_pss_sign(ctx->ctx, NULL, NULL, - MBEDTLS_RSA_PRIVATE, - h, 0, in, sig); - break; - default: - return -1; - } - - if (n < 0) { - lwsl_notice("%s: -0x%x\n", __func__, -n); - - return -1; - } - - return ctx->ctx->len; -} - -int -lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private, - uint8_t *pkey_asn1, size_t pkey_asn1_len) -{ - uint8_t *p = pkey_asn1, *totlen, *end = pkey_asn1 + pkey_asn1_len - 1; - mbedtls_mpi *mpi[LWS_GENCRYPTO_RSA_KEYEL_COUNT] = { - &ctx->ctx->N, &ctx->ctx->E, &ctx->ctx->D, &ctx->ctx->P, - &ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ, - &ctx->ctx->QP, - }; - int n; - - /* 30 82 - sequence - * 09 29 <-- length(0x0929) less 4 bytes - * 02 01 <- length (1) - * 00 - * 02 82 - * 02 01 <- length (513) N - * ... - * - * 02 03 <- length (3) E - * 01 00 01 - * - * 02 82 - * 02 00 <- length (512) D P Q EXP1 EXP2 COEFF - * - * */ - - *p++ = 0x30; - *p++ = 0x82; - totlen = p; - p += 2; - - *p++ = 0x02; - *p++ = 0x01; - *p++ = 0x00; - - for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++) { - int m = mbedtls_mpi_size(mpi[n]); - uint8_t *elen; - - *p++ = 0x02; - elen = p; - if (m < 0x7f) - *p++ = m; - else { - *p++ = 0x82; - *p++ = m >> 8; - *p++ = m & 0xff; - } - - if (p + m > end) - return -1; - - if (mbedtls_mpi_write_binary(mpi[n], p, m)) - return -1; - if (p[0] & 0x80) { - p[0] = 0x00; - if (mbedtls_mpi_write_binary(mpi[n], &p[1], m)) - return -1; - m++; - } - if (m < 0x7f) - *elen = m; - else { - *elen++ = 0x82; - *elen++ = m >> 8; - *elen = m & 0xff; - } - p += m; - } - - n = lws_ptr_diff(p, pkey_asn1); - - *totlen++ = (n - 4) >> 8; - *totlen = (n - 4) & 0xff; - - return n; -} - -void -lws_genrsa_destroy(struct lws_genrsa_ctx *ctx) -{ - if (!ctx->ctx) - return; - mbedtls_rsa_free(ctx->ctx); - lws_free(ctx->ctx); - ctx->ctx = NULL; -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/mbedtls-client.c libwebsockets-2.4.2/lib/tls/mbedtls/mbedtls-client.c --- libwebsockets-4.0.20/lib/tls/mbedtls/mbedtls-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/mbedtls-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,377 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2020 Andy Green - * - * 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 -OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) -{ - return 0; -} - -int -lws_ssl_client_bio_create(struct lws *wsi) -{ - char hostname[128], *p; - const char *alpn_comma = wsi->context->tls.alpn_default; - struct alpn_ctx protos; - - if (wsi->stash) - lws_strncpy(hostname, wsi->stash->cis[CIS_HOST], sizeof(hostname)); - else - if (lws_hdr_copy(wsi, hostname, sizeof(hostname), - _WSI_TOKEN_CLIENT_HOST) <= 0) { - lwsl_err("%s: Unable to get hostname\n", __func__); - - return -1; - } - - /* - * remove any :port part on the hostname... necessary for network - * connection but typical certificates do not contain it - */ - p = hostname; - while (*p) { - if (*p == ':') { - *p = '\0'; - break; - } - p++; - } - - wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_client_ctx); - if (!wsi->tls.ssl) { - lwsl_info("%s: SSL_new() failed\n", __func__); - return -1; - } - - if (wsi->vhost->tls.ssl_info_event_mask) - SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback); - - if (!(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { - X509_VERIFY_PARAM *param = SSL_get0_param(wsi->tls.ssl); - /* Enable automatic hostname checks */ - // X509_VERIFY_PARAM_set_hostflags(param, - // X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); - X509_VERIFY_PARAM_set1_host(param, hostname, 0); - } - - if (wsi->vhost->tls.alpn) - alpn_comma = wsi->vhost->tls.alpn; - - if (wsi->stash) { - lws_strncpy(hostname, wsi->stash->cis[CIS_HOST], sizeof(hostname)); - alpn_comma = wsi->stash->cis[CIS_ALPN]; - } else { - if (lws_hdr_copy(wsi, hostname, sizeof(hostname), - _WSI_TOKEN_CLIENT_ALPN) > 0) - alpn_comma = hostname; - } - - lwsl_info("%s: %p: client conn sending ALPN list '%s'\n", - __func__, wsi, alpn_comma); - - protos.len = lws_alpn_comma_to_openssl(alpn_comma, protos.data, - sizeof(protos.data) - 1); - - /* with mbedtls, protos is not pointed to after exit from this call */ - SSL_set_alpn_select_cb(wsi->tls.ssl, &protos); - - /* - * use server name indication (SNI), if supported, - * when establishing connection - */ - SSL_set_verify(wsi->tls.ssl, SSL_VERIFY_PEER, - OpenSSL_client_verify_callback); - - SSL_set_fd(wsi->tls.ssl, wsi->desc.sockfd); - - if (wsi->sys_tls_client_cert) { - lws_system_blob_t *b = lws_system_get_blob(wsi->context, - LWS_SYSBLOB_TYPE_CLIENT_CERT_DER, - wsi->sys_tls_client_cert - 1); - const uint8_t *data; - size_t size; - - if (!b) - goto no_client_cert; - - /* - * Set up the per-connection client cert - */ - - size = lws_system_blob_get_size(b); - if (!size) - goto no_client_cert; - - if (lws_system_blob_get_single_ptr(b, &data)) - goto no_client_cert; - - if (SSL_use_certificate_ASN1(wsi->tls.ssl, data, size) != 1) - goto no_client_cert; - - b = lws_system_get_blob(wsi->context, - LWS_SYSBLOB_TYPE_CLIENT_KEY_DER, - wsi->sys_tls_client_cert - 1); - if (!b) - goto no_client_cert; - size = lws_system_blob_get_size(b); - if (!size) - goto no_client_cert; - - if (lws_system_blob_get_single_ptr(b, &data)) - goto no_client_cert; - - if (SSL_use_PrivateKey_ASN1(0, wsi->tls.ssl, data, size) != 1) - goto no_client_cert; - - /* no wrapper api for check key */ - - lwsl_notice("%s: set system client cert %u\n", __func__, - wsi->sys_tls_client_cert - 1); - } - - return 0; - -no_client_cert: - lwsl_err("%s: unable to set up system client cert %d\n", __func__, - wsi->sys_tls_client_cert - 1); - - return 1; -} - -int ERR_get_error(void) -{ - return 0; -} - -enum lws_ssl_capable_status -lws_tls_client_connect(struct lws *wsi) -{ - int m, n = SSL_connect(wsi->tls.ssl); - const unsigned char *prot; - unsigned int len; - - if (n == 1) { - SSL_get0_alpn_selected(wsi->tls.ssl, &prot, &len); - lws_role_call_alpn_negotiated(wsi, (const char *)prot); - lwsl_info("client connect OK\n"); - return LWS_SSL_CAPABLE_DONE; - } - - m = SSL_get_error(wsi->tls.ssl, n); - - if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) - return LWS_SSL_CAPABLE_MORE_SERVICE_READ; - - if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) - return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE; - - if (!n) /* we don't know what he wants, but he says to retry */ - return LWS_SSL_CAPABLE_MORE_SERVICE; - - return LWS_SSL_CAPABLE_ERROR; -} - -int -lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len) -{ - int n; - X509 *peer = SSL_get_peer_certificate(wsi->tls.ssl); - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - char *sb = (char *)&pt->serv_buf[0]; - - if (!peer) { - lwsl_info("peer did not provide cert\n"); - lws_snprintf(ebuf, ebuf_len, "no peer cert"); - - return -1; - } - lwsl_info("peer provided cert\n"); - - n = SSL_get_verify_result(wsi->tls.ssl); - lwsl_debug("get_verify says %d\n", n); - - if (n == X509_V_OK) - return 0; - - if (n == X509_V_ERR_HOSTNAME_MISMATCH && - (wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { - lwsl_info("accepting certificate for invalid hostname\n"); - return 0; - } - - if (n == X509_V_ERR_INVALID_CA && - (wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED)) { - lwsl_info("accepting certificate from untrusted CA\n"); - return 0; - } - - if ((n == X509_V_ERR_CERT_NOT_YET_VALID || - n == X509_V_ERR_CERT_HAS_EXPIRED) && - (wsi->tls.use_ssl & LCCSCF_ALLOW_EXPIRED)) { - lwsl_info("accepting expired or not yet valid certificate\n"); - - return 0; - } - lws_snprintf(ebuf, ebuf_len, - "server's cert didn't look good, (use_ssl 0x%x) X509_V_ERR = %d: %s\n", - (unsigned int)wsi->tls.use_ssl, n, ERR_error_string(n, sb)); - lwsl_info("%s\n", ebuf); - lws_tls_err_describe_clear(); - - return -1; -} - -int -lws_tls_client_create_vhost_context(struct lws_vhost *vh, - const struct lws_context_creation_info *info, - const char *cipher_list, - const char *ca_filepath, - const void *ca_mem, - unsigned int ca_mem_len, - const char *cert_filepath, - const void *cert_mem, - unsigned int cert_mem_len, - const char *private_key_filepath) -{ - X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len); - SSL_METHOD *method = (SSL_METHOD *)TLS_client_method(); - unsigned long error; - int n; - - if (!method) { - error = ERR_get_error(); - lwsl_err("problem creating ssl method %lu: %s\n", - error, ERR_error_string(error, - (char *)vh->context->pt[0].serv_buf)); - return 1; - } - /* create context */ - vh->tls.ssl_client_ctx = SSL_CTX_new(method); - if (!vh->tls.ssl_client_ctx) { - error = ERR_get_error(); - lwsl_err("problem creating ssl context %lu: %s\n", - error, ERR_error_string(error, - (char *)vh->context->pt[0].serv_buf)); - return 1; - } - - if (!ca_filepath && (!ca_mem || !ca_mem_len)) - return 0; - - if (ca_filepath) { -#if !defined(LWS_PLAT_OPTEE) - uint8_t *buf; - lws_filepos_t len; - - if (alloc_file(vh->context, ca_filepath, &buf, &len)) { - lwsl_err("Load CA cert file %s failed\n", ca_filepath); - return 1; - } - vh->tls.x509_client_CA = d2i_X509(NULL, buf, len); - free(buf); - lwsl_notice("Loading client CA for verification %s\n", ca_filepath); -#endif - } else { - vh->tls.x509_client_CA = d2i_X509(NULL, (uint8_t*)ca_mem, ca_mem_len); - lwsl_notice("%s: using mem client CA cert %d\n", - __func__, ca_mem_len); - } - - if (!vh->tls.x509_client_CA) { - lwsl_err("client CA: x509 parse failed\n"); - return 1; - } - - if (!vh->tls.ssl_ctx) - SSL_CTX_add_client_CA(vh->tls.ssl_client_ctx, vh->tls.x509_client_CA); - else - SSL_CTX_add_client_CA(vh->tls.ssl_ctx, vh->tls.x509_client_CA); - - /* support for client-side certificate authentication */ - if (cert_filepath) { -#if !defined(LWS_PLAT_OPTEE) - uint8_t *buf; - lws_filepos_t amount; - - if (lws_tls_use_any_upgrade_check_extant(cert_filepath) != - LWS_TLS_EXTANT_YES && - (info->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) - return 0; - - lwsl_notice("%s: doing cert filepath %s\n", __func__, - cert_filepath); - - if (alloc_file(vh->context, cert_filepath, &buf, &amount)) - return 1; - - buf[amount++] = '\0'; - - SSL_CTX_use_PrivateKey_ASN1(0, vh->tls.ssl_client_ctx, - buf, amount); - - n = SSL_CTX_use_certificate_ASN1(vh->tls.ssl_client_ctx, - amount, buf); - lws_free(buf); - if (n < 1) { - lwsl_err("problem %d getting cert '%s'\n", n, - cert_filepath); - lws_tls_err_describe_clear(); - return 1; - } - - lwsl_notice("Loaded client cert %s\n", cert_filepath); -#endif - } else if (cert_mem && cert_mem_len) { - // lwsl_hexdump_notice(cert_mem, cert_mem_len - 1); - SSL_CTX_use_PrivateKey_ASN1(0, vh->tls.ssl_client_ctx, - cert_mem, cert_mem_len - 1); - n = SSL_CTX_use_certificate_ASN1(vh->tls.ssl_client_ctx, - cert_mem_len, cert_mem); - if (n < 1) { - lwsl_err("%s: problem interpreting client cert\n", - __func__); - lws_tls_err_describe_clear(); - return 1; - } - lwsl_notice("%s: using mem client cert %d\n", - __func__, cert_mem_len); - } - - return 0; -} - -int -lws_tls_client_vhost_extra_cert_mem(struct lws_vhost *vh, - const uint8_t *der, size_t der_len) -{ - if (SSL_CTX_add_client_CA_ASN1(vh->tls.ssl_client_ctx, der_len, der) != 1) { - lwsl_err("%s: failed\n", __func__); - return 1; - } - - return 0; -} - diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/mbedtls-server.c libwebsockets-2.4.2/lib/tls/mbedtls/mbedtls-server.c --- libwebsockets-4.0.20/lib/tls/mbedtls/mbedtls-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/mbedtls-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,718 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -int -lws_tls_server_client_cert_verify_config(struct lws_vhost *vh) -{ - int verify_options = SSL_VERIFY_PEER; - - /* as a server, are we requiring clients to identify themselves? */ - if (!lws_check_opt(vh->options, - LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) { - lwsl_notice("no client cert required\n"); - return 0; - } - - /* - * The wrapper has this messed-up mapping: - * - * else if (ctx->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT) - * mode = MBEDTLS_SSL_VERIFY_OPTIONAL; - * - * ie the meaning is inverted. So where we should test for ! we don't - */ - if (lws_check_opt(vh->options, LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED)) - verify_options = SSL_VERIFY_FAIL_IF_NO_PEER_CERT; - - lwsl_notice("%s: vh %s requires client cert %d\n", __func__, vh->name, - verify_options); - - SSL_CTX_set_verify(vh->tls.ssl_ctx, verify_options, NULL); - - return 0; -} - -static int -lws_mbedtls_sni_cb(void *arg, mbedtls_ssl_context *mbedtls_ctx, - const unsigned char *servername, size_t len) -{ - SSL *ssl = SSL_SSL_from_mbedtls_ssl_context(mbedtls_ctx); - struct lws_context *context = (struct lws_context *)arg; - struct lws_vhost *vhost, *vh; - - lwsl_notice("%s: %s\n", __func__, servername); - - /* - * We can only get ssl accepted connections by using a vhost's ssl_ctx - * find out which listening one took us and only match vhosts on the - * same port. - */ - vh = context->vhost_list; - while (vh) { - if (!vh->being_destroyed && - vh->tls.ssl_ctx == SSL_get_SSL_CTX(ssl)) - break; - vh = vh->vhost_next; - } - - if (!vh) { - assert(vh); /* can't match the incoming vh? */ - return 0; - } - - vhost = lws_select_vhost(context, vh->listen_port, - (const char *)servername); - if (!vhost) { - lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port); - - return 0; - } - - lwsl_info("SNI: Found: %s:%d at vhost '%s'\n", servername, - vh->listen_port, vhost->name); - - if (!vhost->tls.ssl_ctx) { - lwsl_err("%s: vhost %s matches SNI but no valid cert\n", - __func__, vh->name); - - return 1; - } - - /* select the ssl ctx from the selected vhost for this conn */ - SSL_set_SSL_CTX(ssl, vhost->tls.ssl_ctx); - - return 0; -} - -int -lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, - const char *cert, const char *private_key, - const char *mem_cert, size_t mem_cert_len, - const char *mem_privkey, size_t mem_privkey_len) -{ - lws_filepos_t flen; - uint8_t *p = NULL; - long err; - int n; - - if ((!cert || !private_key) && (!mem_cert || !mem_privkey)) { - lwsl_notice("%s: no usable input\n", __func__); - return 0; - } - - n = lws_tls_generic_cert_checks(vhost, cert, private_key); - - if (n == LWS_TLS_EXTANT_NO && (!mem_cert || !mem_privkey)) - return 0; - - /* - * we can't read the root-privs files. But if mem_cert is provided, - * we should use that. - */ - if (n == LWS_TLS_EXTANT_NO) - n = LWS_TLS_EXTANT_ALTERNATIVE; - - if (n == LWS_TLS_EXTANT_ALTERNATIVE && (!mem_cert || !mem_privkey)) - return 1; /* no alternative */ - - if (n == LWS_TLS_EXTANT_ALTERNATIVE) { - /* - * Although we have prepared update certs, we no longer have - * the rights to read our own cert + key we saved. - * - * If we were passed copies in memory buffers, use those - * instead. - * - * The passed memory-buffer cert image is in DER, and the - * memory-buffer private key image is PEM. - */ - cert = NULL; - private_key = NULL; - - if (!mem_cert) - return 1; - } - if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, - mem_cert_len, &p, &flen)) { - lwsl_err("couldn't find cert file %s\n", cert); - - return 1; - } - - err = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, flen, p); - lws_free_set_NULL(p); - if (!err) { - lwsl_err("Problem loading cert\n"); - return 1; - } - - if (lws_tls_alloc_pem_to_der_file(vhost->context, private_key, - (char *)mem_privkey, mem_privkey_len, - &p, &flen)) { - lwsl_err("couldn't find private key\n"); - - return 1; - } - - err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->tls.ssl_ctx, p, flen); - lws_free_set_NULL(p); - if (!err) { - lwsl_err("Problem loading key\n"); - - return 1; - } - - if (!private_key && !mem_privkey && vhost->protocols[0].callback(wsi, - LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, - vhost->tls.ssl_ctx, NULL, 0)) { - lwsl_err("ssl private key not set\n"); - - return 1; - } - - vhost->tls.skipped_certs = 0; - - return 0; -} - -int -lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, - struct lws_vhost *vhost, struct lws *wsi) -{ - const SSL_METHOD *method = TLS_server_method(); - uint8_t *p; - lws_filepos_t flen; - int n; - - vhost->tls.ssl_ctx = SSL_CTX_new(method); /* create context */ - if (!vhost->tls.ssl_ctx) { - lwsl_err("problem creating ssl context\n"); - return 1; - } - - if (!vhost->tls.use_ssl || - (!info->ssl_cert_filepath && !info->server_ssl_cert_mem)) - return 0; - - if (info->ssl_ca_filepath) { - lwsl_notice("%s: vh %s: loading CA filepath %s\n", __func__, - vhost->name, info->ssl_ca_filepath); - if (lws_tls_alloc_pem_to_der_file(vhost->context, - info->ssl_ca_filepath, NULL, 0, &p, &flen)) { - lwsl_err("couldn't find client CA file %s\n", - info->ssl_ca_filepath); - - return 1; - } - - if (SSL_CTX_add_client_CA_ASN1(vhost->tls.ssl_ctx, (int)flen, p) != 1) { - lwsl_err("%s: SSL_CTX_add_client_CA_ASN1 unhappy\n", - __func__); - free(p); - return 1; - } - free(p); - } else { - if (info->server_ssl_ca_mem && info->server_ssl_ca_mem_len && - SSL_CTX_add_client_CA_ASN1(vhost->tls.ssl_ctx, - (int)info->server_ssl_ca_mem_len, - info->server_ssl_ca_mem) != 1) { - lwsl_err("%s: mem SSL_CTX_add_client_CA_ASN1 unhappy\n", - __func__); - return 1; - } - lwsl_notice("%s: vh %s: mem CA OK\n", __func__, vhost->name); - } - - n = lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath, - info->ssl_private_key_filepath, - info->server_ssl_cert_mem, - info->server_ssl_cert_mem_len, - info->server_ssl_private_key_mem, - info->server_ssl_private_key_mem_len); - if (n) - return n; - - return 0; -} - -int -lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd) -{ - errno = 0; - wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_ctx); - if (wsi->tls.ssl == NULL) { - lwsl_err("SSL_new failed: errno %d\n", errno); - - lws_tls_err_describe_clear(); - return 1; - } - - SSL_set_fd(wsi->tls.ssl, accept_fd); - - if (wsi->vhost->tls.ssl_info_event_mask) - SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback); - - SSL_set_sni_callback(wsi->tls.ssl, lws_mbedtls_sni_cb, wsi->context); - - return 0; -} - -#if defined(LWS_AMAZON_RTOS) -enum lws_ssl_capable_status -#else -int -#endif -lws_tls_server_abort_connection(struct lws *wsi) -{ - __lws_tls_shutdown(wsi); - SSL_free(wsi->tls.ssl); - - return 0; -} - -enum lws_ssl_capable_status -lws_tls_server_accept(struct lws *wsi) -{ - union lws_tls_cert_info_results ir; - int m, n; - - n = SSL_accept(wsi->tls.ssl); - - wsi->skip_fallback = 1; - if (n == 1) { - - if (strstr(wsi->vhost->name, ".invalid")) { - lwsl_notice("%s: vhost has .invalid, " - "rejecting accept\n", __func__); - - return LWS_SSL_CAPABLE_ERROR; - } - - n = lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME, - &ir, sizeof(ir.ns.name)); - if (!n) - lwsl_notice("%s: client cert CN '%s'\n", - __func__, ir.ns.name); - else - lwsl_info("%s: couldn't get client cert CN\n", - __func__); - return LWS_SSL_CAPABLE_DONE; - } - - m = SSL_get_error(wsi->tls.ssl, n); - lwsl_debug("%s: %p: accept SSL_get_error %d errno %d\n", __func__, - wsi, m, errno); - - // mbedtls wrapper only - if (m == SSL_ERROR_SYSCALL && errno == 11) - return LWS_SSL_CAPABLE_MORE_SERVICE_READ; - -#if defined(WIN32) - if (m == SSL_ERROR_SYSCALL && errno == 0) - return LWS_SSL_CAPABLE_MORE_SERVICE_READ; -#endif - - if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL) - return LWS_SSL_CAPABLE_ERROR; - - if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) { - if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { - lwsl_info("%s: WANT_READ change_pollfd failed\n", - __func__); - return LWS_SSL_CAPABLE_ERROR; - } - - lwsl_info("SSL_ERROR_WANT_READ\n"); - return LWS_SSL_CAPABLE_MORE_SERVICE_READ; - } - if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) { - lwsl_debug("%s: WANT_WRITE\n", __func__); - - if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { - lwsl_info("%s: WANT_WRITE change_pollfd failed\n", - __func__); - return LWS_SSL_CAPABLE_ERROR; - } - return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE; - } - - return LWS_SSL_CAPABLE_ERROR; -} - -#if defined(LWS_WITH_ACME) -/* - * mbedtls doesn't support SAN for cert creation. So we use a known-good - * tls-sni-01 cert from OpenSSL that worked on Let's Encrypt, and just replace - * the pubkey n part and the signature part. - * - * This will need redoing for tls-sni-02... - */ - -static uint8_t ss_cert_leadin[] = { - 0x30, 0x82, - 0x05, 0x56, /* total length: LEN1 (+2 / +3) (correct for 513 + 512)*/ - - 0x30, 0x82, /* length: LEN2 (+6 / +7) (correct for 513) */ - 0x03, 0x3e, - - /* addition: v3 cert (+5 bytes)*/ - 0xa0, 0x03, - 0x02, 0x01, 0x02, - - 0x02, 0x01, 0x01, - 0x30, 0x0d, 0x06, 0x09, 0x2a, - 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x3f, - 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, - 0x42, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, - 0x73, 0x6f, 0x6d, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x31, - 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11, 0x74, 0x65, - 0x6d, 0x70, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69, 0x6e, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x30, 0x1e, 0x17, 0x0d, - - /* from 2017-10-29 ... */ - 0x31, 0x37, 0x31, 0x30, 0x32, 0x39, 0x31, 0x31, 0x34, 0x39, 0x34, 0x35, - 0x5a, 0x17, 0x0d, - - /* thru 2049-10-29 we immediately discard the private key, no worries */ - 0x34, 0x39, 0x31, 0x30, 0x32, 0x39, 0x31, 0x32, 0x34, 0x39, 0x34, 0x35, - 0x5a, - - 0x30, 0x3f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x47, 0x42, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, - 0x0c, 0x0b, 0x73, 0x6f, 0x6d, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, - 0x79, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11, - 0x74, 0x65, 0x6d, 0x70, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69, 0x6e, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x30, - - 0x82, - 0x02, 0x22, /* LEN3 (+C3 / C4) */ - 0x30, 0x0d, 0x06, - 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, - 0x03, - - 0x82, - 0x02, 0x0f, /* LEN4 (+D6 / D7) */ - - 0x00, 0x30, 0x82, - - 0x02, 0x0a, /* LEN5 (+ DB / DC) */ - - 0x02, 0x82, - - //0x02, 0x01, /* length of n in bytes (including leading 00 if any) */ - }, - - /* 1 + (keybits / 8) bytes N */ - - ss_cert_san_leadin[] = { - /* e - fixed */ - 0x02, 0x03, 0x01, 0x00, 0x01, - - 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x59, 0x06, 0x03, 0x55, 0x1d, - 0x11, 0x04, 0x52, 0x30, 0x50, /* <-- SAN length + 2 */ - - 0x82, 0x4e, /* <-- SAN length */ - }, - - /* 78 bytes of SAN (tls-sni-01) - 0x61, 0x64, 0x34, 0x31, 0x61, 0x66, 0x62, 0x65, 0x30, 0x63, 0x61, 0x34, - 0x36, 0x34, 0x32, 0x66, 0x30, 0x61, 0x34, 0x34, 0x39, 0x64, 0x39, 0x63, - 0x61, 0x37, 0x36, 0x65, 0x62, 0x61, 0x61, 0x62, 0x2e, 0x32, 0x38, 0x39, - 0x34, 0x64, 0x34, 0x31, 0x36, 0x63, 0x39, 0x38, 0x33, 0x66, 0x31, 0x32, - 0x65, 0x64, 0x37, 0x33, 0x31, 0x61, 0x33, 0x30, 0x66, 0x35, 0x63, 0x34, - 0x34, 0x37, 0x37, 0x66, 0x65, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69, - 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, */ - - /* end of LEN2 area */ - - ss_cert_sig_leadin[] = { - /* it's saying that the signature is SHA256 + RSA */ - 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, - - 0x82, - 0x02, 0x01, - 0x00, - }; - - /* (keybits / 8) bytes signature to end of LEN1 area */ - -#define SAN_A_LENGTH 78 - -int -lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, - const char *san_b) -{ - int buflen = 0x560; - uint8_t *buf = lws_malloc(buflen, "tmp cert buf"), *p = buf, *pkey_asn1; - struct lws_genrsa_ctx ctx; - struct lws_gencrypto_keyelem el[LWS_GENCRYPTO_RSA_KEYEL_COUNT]; - uint8_t digest[32]; - struct lws_genhash_ctx hash_ctx; - int pkey_asn1_len = 3 * 1024; - int n, m, keybits = lws_plat_recommended_rsa_bits(), adj; - - if (!buf) - return 1; - - n = lws_genrsa_new_keypair(vhost->context, &ctx, LGRSAM_PKCS1_1_5, - &el[0], keybits); - if (n < 0) { - lws_genrsa_destroy_elements(&el[0]); - goto bail1; - } - - n = sizeof(ss_cert_leadin); - memcpy(p, ss_cert_leadin, n); - p += n; - - adj = (0x0556 - 0x401) + (keybits / 4) + 1; - buf[2] = adj >> 8; - buf[3] = adj & 0xff; - - adj = (0x033e - 0x201) + (keybits / 8) + 1; - buf[6] = adj >> 8; - buf[7] = adj & 0xff; - - adj = (0x0222 - 0x201) + (keybits / 8) + 1; - buf[0xc3] = adj >> 8; - buf[0xc4] = adj & 0xff; - - adj = (0x020f - 0x201) + (keybits / 8) + 1; - buf[0xd6] = adj >> 8; - buf[0xd7] = adj & 0xff; - - adj = (0x020a - 0x201) + (keybits / 8) + 1; - buf[0xdb] = adj >> 8; - buf[0xdc] = adj & 0xff; - - *p++ = ((keybits / 8) + 1) >> 8; - *p++ = ((keybits / 8) + 1) & 0xff; - - /* we need to drop 1 + (keybits / 8) bytes of n in here, 00 + key */ - - *p++ = 0x00; - memcpy(p, el[LWS_GENCRYPTO_RSA_KEYEL_N].buf, el[LWS_GENCRYPTO_RSA_KEYEL_N].len); - p += el[LWS_GENCRYPTO_RSA_KEYEL_N].len; - - memcpy(p, ss_cert_san_leadin, sizeof(ss_cert_san_leadin)); - p += sizeof(ss_cert_san_leadin); - - /* drop in 78 bytes of san_a */ - - memcpy(p, san_a, SAN_A_LENGTH); - p += SAN_A_LENGTH; - memcpy(p, ss_cert_sig_leadin, sizeof(ss_cert_sig_leadin)); - - p[17] = ((keybits / 8) + 1) >> 8; - p[18] = ((keybits / 8) + 1) & 0xff; - - p += sizeof(ss_cert_sig_leadin); - - /* hash the cert plaintext */ - - if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256)) - goto bail2; - - if (lws_genhash_update(&hash_ctx, buf, lws_ptr_diff(p, buf))) { - lws_genhash_destroy(&hash_ctx, NULL); - - goto bail2; - } - if (lws_genhash_destroy(&hash_ctx, digest)) - goto bail2; - - /* sign the hash */ - - n = lws_genrsa_hash_sign(&ctx, digest, LWS_GENHASH_TYPE_SHA256, p, - buflen - lws_ptr_diff(p, buf)); - if (n < 0) - goto bail2; - p += n; - - pkey_asn1 = lws_malloc(pkey_asn1_len, "mbed crt tmp"); - if (!pkey_asn1) - goto bail2; - - m = lws_genrsa_render_pkey_asn1(&ctx, 1, pkey_asn1, pkey_asn1_len); - if (m < 0) { - lws_free(pkey_asn1); - goto bail2; - } - -// lwsl_hexdump_level(LLL_DEBUG, buf, lws_ptr_diff(p, buf)); - n = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, - lws_ptr_diff(p, buf), buf); - if (n != 1) { - lws_free(pkey_asn1); - lwsl_err("%s: generated cert failed to load 0x%x\n", - __func__, -n); - } else { - //lwsl_debug("private key\n"); - //lwsl_hexdump_level(LLL_DEBUG, pkey_asn1, n); - - /* and to use our generated private key */ - n = SSL_CTX_use_PrivateKey_ASN1(0, vhost->tls.ssl_ctx, - pkey_asn1, m); - lws_free(pkey_asn1); - if (n != 1) { - lwsl_err("%s: SSL_CTX_use_PrivateKey_ASN1 failed\n", - __func__); - } - } - - lws_genrsa_destroy(&ctx); - lws_genrsa_destroy_elements(&el[0]); - - lws_free(buf); - - return n != 1; - -bail2: - lws_genrsa_destroy(&ctx); - lws_genrsa_destroy_elements(&el[0]); -bail1: - lws_free(buf); - - return -1; -} - -void -lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost) -{ -} - -#if defined(LWS_WITH_JOSE) -static int -_rngf(void *context, unsigned char *buf, size_t len) -{ - if ((size_t)lws_get_random(context, buf, len) == len) - return 0; - - return -1; -} - -static const char *x5[] = { "C", "ST", "L", "O", "CN" }; - -/* - * CSR is output formatted as b64url(DER) - * Private key is output as a PEM in memory - */ -int -lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[], - uint8_t *dcsr, size_t csr_len, char **privkey_pem, - size_t *privkey_len) -{ - mbedtls_x509write_csr csr; - mbedtls_pk_context mpk; - int buf_size = 4096, n; - char subject[200], *p = subject, *end = p + sizeof(subject) - 1; - uint8_t *buf = malloc(buf_size); /* malloc because given to user code */ - - if (!buf) - return -1; - - mbedtls_x509write_csr_init(&csr); - - mbedtls_pk_init(&mpk); - if (mbedtls_pk_setup(&mpk, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA))) { - lwsl_notice("%s: pk_setup failed\n", __func__); - goto fail; - } - - n = mbedtls_rsa_gen_key(mbedtls_pk_rsa(mpk), _rngf, context, - lws_plat_recommended_rsa_bits(), 65537); - if (n) { - lwsl_notice("%s: failed to generate keys\n", __func__); - - goto fail1; - } - - /* subject must be formatted like "C=TW,O=warmcat,CN=myserver" */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(x5); n++) { - if (p != subject) - *p++ = ','; - if (elements[n]) - p += lws_snprintf(p, end - p, "%s=%s", x5[n], - elements[n]); - } - - if (mbedtls_x509write_csr_set_subject_name(&csr, subject)) - goto fail1; - - mbedtls_x509write_csr_set_key(&csr, &mpk); - mbedtls_x509write_csr_set_md_alg(&csr, MBEDTLS_MD_SHA256); - - /* - * data is written at the end of the buffer! Use the - * return value to determine where you should start - * using the buffer - */ - n = mbedtls_x509write_csr_der(&csr, buf, buf_size, _rngf, context); - if (n < 0) { - lwsl_notice("%s: write csr der failed\n", __func__); - goto fail1; - } - - /* we have it in DER, we need it in b64URL */ - - n = lws_jws_base64_enc((char *)(buf + buf_size) - n, n, - (char *)dcsr, csr_len); - if (n < 0) - goto fail1; - - /* - * okay, the CSR is done, last we need the private key in PEM - * re-use the DER CSR buf as the result buffer since we cn do it in - * one step - */ - - if (mbedtls_pk_write_key_pem(&mpk, buf, buf_size)) { - lwsl_notice("write key pem failed\n"); - goto fail1; - } - - *privkey_pem = (char *)buf; - *privkey_len = strlen((const char *)buf); - - mbedtls_pk_free(&mpk); - mbedtls_x509write_csr_free(&csr); - - return n; - -fail1: - mbedtls_pk_free(&mpk); -fail: - mbedtls_x509write_csr_free(&csr); - free(buf); - - return -1; -} -#endif -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/mbedtls-ssl.c libwebsockets-2.4.2/lib/tls/mbedtls/mbedtls-ssl.c --- libwebsockets-4.0.20/lib/tls/mbedtls/mbedtls-ssl.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/mbedtls-ssl.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,323 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-tls-mbedtls.h" - - -void -lws_ssl_destroy(struct lws_vhost *vhost) -{ - if (!lws_check_opt(vhost->context->options, - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) - return; - - if (vhost->tls.ssl_ctx) - SSL_CTX_free(vhost->tls.ssl_ctx); - if (!vhost->tls.user_supplied_ssl_ctx && vhost->tls.ssl_client_ctx) - SSL_CTX_free(vhost->tls.ssl_client_ctx); - - if (vhost->tls.x509_client_CA) - X509_free(vhost->tls.x509_client_CA); -} - -int -lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - int n = 0, m; - - if (!wsi->tls.ssl) - return lws_ssl_capable_read_no_ssl(wsi, buf, len); - - lws_stats_bump(pt, LWSSTATS_C_API_READ, 1); - - errno = 0; - n = SSL_read(wsi->tls.ssl, buf, len); -#if defined(LWS_PLAT_FREERTOS) - if (!n && errno == LWS_ENOTCONN) { - lwsl_debug("%p: SSL_read ENOTCONN\n", wsi); - return LWS_SSL_CAPABLE_ERROR; - } -#endif -#if defined(LWS_WITH_STATS) - if (!wsi->seen_rx && wsi->accept_start_us) { - lws_stats_bump(pt, LWSSTATS_US_SSL_RX_DELAY_AVG, - lws_now_usecs() - wsi->accept_start_us); - lws_stats_bump(pt, LWSSTATS_C_SSL_CONNS_HAD_RX, 1); - wsi->seen_rx = 1; - } -#endif - - - lwsl_debug("%p: SSL_read says %d\n", wsi, n); - /* manpage: returning 0 means connection shut down */ - if (!n) { - wsi->socket_is_permanently_unusable = 1; - - return LWS_SSL_CAPABLE_ERROR; - } - - if (n < 0) { - m = SSL_get_error(wsi->tls.ssl, n); - lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno); - if (errno == LWS_ENOTCONN) { - /* If the socket isn't connected anymore, bail out. */ - wsi->socket_is_permanently_unusable = 1; - return LWS_SSL_CAPABLE_ERROR; - } - if (m == SSL_ERROR_ZERO_RETURN || - m == SSL_ERROR_SYSCALL) - return LWS_SSL_CAPABLE_ERROR; - - if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) { - lwsl_debug("%s: WANT_READ\n", __func__); - lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); - return LWS_SSL_CAPABLE_MORE_SERVICE; - } - if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) { - lwsl_debug("%s: WANT_WRITE\n", __func__); - lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); - return LWS_SSL_CAPABLE_MORE_SERVICE; - } - wsi->socket_is_permanently_unusable = 1; - - return LWS_SSL_CAPABLE_ERROR; - } - - lws_stats_bump(pt, LWSSTATS_B_READ, n); - -#if defined(LWS_WITH_SERVER_STATUS) - if (wsi->vhost) - wsi->vhost->conn_stats.rx += n; -#endif -#if defined(LWS_WITH_DETAILED_LATENCY) - if (context->detailed_latency_cb) { - wsi->detlat.req_size = len; - wsi->detlat.acc_size = n; - wsi->detlat.type = LDLT_READ; - wsi->detlat.latencies[LAT_DUR_PROXY_RX_TO_ONWARD_TX] = - lws_now_usecs() - pt->ust_left_poll; - wsi->detlat.latencies[LAT_DUR_USERCB] = 0; - lws_det_lat_cb(wsi->context, &wsi->detlat); - } -#endif - /* - * if it was our buffer that limited what we read, - * check if SSL has additional data pending inside SSL buffers. - * - * Because these won't signal at the network layer with POLLIN - * and if we don't realize, this data will sit there forever - */ - if (n != len) - goto bail; - if (!wsi->tls.ssl) - goto bail; - - if (SSL_pending(wsi->tls.ssl) && - lws_dll2_is_detached(&wsi->tls.dll_pending_tls)) - lws_dll2_add_head(&wsi->tls.dll_pending_tls, - &pt->tls.dll_pending_tls_owner); - - return n; -bail: - lws_ssl_remove_wsi_from_buffered_list(wsi); - - return n; -} - -int -lws_ssl_pending(struct lws *wsi) -{ - if (!wsi->tls.ssl) - return 0; - - return SSL_pending(wsi->tls.ssl); -} - -int -lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len) -{ - int n, m; - - if (!wsi->tls.ssl) - return lws_ssl_capable_write_no_ssl(wsi, buf, len); - - n = SSL_write(wsi->tls.ssl, buf, len); - if (n > 0) - return n; - - m = SSL_get_error(wsi->tls.ssl, n); - if (m != SSL_ERROR_SYSCALL) { - if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) { - lwsl_notice("%s: want read\n", __func__); - - return LWS_SSL_CAPABLE_MORE_SERVICE; - } - - if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) { - lws_set_blocking_send(wsi); - lwsl_debug("%s: want write\n", __func__); - - return LWS_SSL_CAPABLE_MORE_SERVICE; - } - } - - lwsl_debug("%s failed: %d\n",__func__, m); - wsi->socket_is_permanently_unusable = 1; - - return LWS_SSL_CAPABLE_ERROR; -} - -int openssl_SSL_CTX_private_data_index; - -void -lws_ssl_info_callback(const SSL *ssl, int where, int ret) -{ - struct lws *wsi; - struct lws_context *context; - struct lws_ssl_info si; - - context = (struct lws_context *)SSL_CTX_get_ex_data( - SSL_get_SSL_CTX(ssl), - openssl_SSL_CTX_private_data_index); - if (!context) - return; - wsi = wsi_from_fd(context, SSL_get_fd(ssl)); - if (!wsi) - return; - - if (!(where & wsi->vhost->tls.ssl_info_event_mask)) - return; - - si.where = where; - si.ret = ret; - - if (user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_SSL_INFO, - wsi->user_space, &si, 0)) - lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1); -} - - -int -lws_ssl_close(struct lws *wsi) -{ - lws_sockfd_type n; - - if (!wsi->tls.ssl) - return 0; /* not handled */ - -#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) - /* kill ssl callbacks, becausse we will remove the fd from the - * table linking it to the wsi - */ - if (wsi->vhost->tls.ssl_info_event_mask) - SSL_set_info_callback(wsi->tls.ssl, NULL); -#endif - - n = SSL_get_fd(wsi->tls.ssl); - if (!wsi->socket_is_permanently_unusable) - SSL_shutdown(wsi->tls.ssl); - compatible_close(n); - SSL_free(wsi->tls.ssl); - wsi->tls.ssl = NULL; - - lws_tls_restrict_return(wsi->context); - - return 1; /* handled */ -} - -void -lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost) -{ - if (vhost->tls.ssl_ctx) - SSL_CTX_free(vhost->tls.ssl_ctx); - - if (!vhost->tls.user_supplied_ssl_ctx && vhost->tls.ssl_client_ctx) - SSL_CTX_free(vhost->tls.ssl_client_ctx); -#if defined(LWS_WITH_ACME) - lws_tls_acme_sni_cert_destroy(vhost); -#endif -} - -void -lws_ssl_context_destroy(struct lws_context *context) -{ -} - -lws_tls_ctx * -lws_tls_ctx_from_wsi(struct lws *wsi) -{ - if (!wsi->tls.ssl) - return NULL; - - return SSL_get_SSL_CTX(wsi->tls.ssl); -} - -enum lws_ssl_capable_status -__lws_tls_shutdown(struct lws *wsi) -{ - int n = SSL_shutdown(wsi->tls.ssl); - - lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd); - - switch (n) { - case 1: /* successful completion */ - n = shutdown(wsi->desc.sockfd, SHUT_WR); - return LWS_SSL_CAPABLE_DONE; - - case 0: /* needs a retry */ - __lws_change_pollfd(wsi, 0, LWS_POLLIN); - return LWS_SSL_CAPABLE_MORE_SERVICE; - - default: /* fatal error, or WANT */ - n = SSL_get_error(wsi->tls.ssl, n); - if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) { - if (SSL_want_read(wsi->tls.ssl)) { - lwsl_debug("(wants read)\n"); - __lws_change_pollfd(wsi, 0, LWS_POLLIN); - return LWS_SSL_CAPABLE_MORE_SERVICE_READ; - } - if (SSL_want_write(wsi->tls.ssl)) { - lwsl_debug("(wants write)\n"); - __lws_change_pollfd(wsi, 0, LWS_POLLOUT); - return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE; - } - } - return LWS_SSL_CAPABLE_ERROR; - } -} - - -static int -tops_fake_POLLIN_for_buffered_mbedtls(struct lws_context_per_thread *pt) -{ - return lws_tls_fake_POLLIN_for_buffered(pt); -} - -const struct lws_tls_ops tls_ops_mbedtls = { - /* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_mbedtls, -}; diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/mbedtls-tls.c libwebsockets-2.4.2/lib/tls/mbedtls/mbedtls-tls.c --- libwebsockets-4.0.20/lib/tls/mbedtls/mbedtls-tls.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/mbedtls-tls.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-tls-mbedtls.h" - -void -lws_tls_err_describe_clear(void) -{ -} - -int -lws_context_init_ssl_library(const struct lws_context_creation_info *info) -{ - lwsl_info(" Compiled with MbedTLS support\n"); - - if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) - lwsl_info(" SSL disabled: no " - "LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); - - return 0; -} - -void -lws_context_deinit_ssl_library(struct lws_context *context) -{ - -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/mbedtls-x509.c libwebsockets-2.4.2/lib/tls/mbedtls/mbedtls-x509.c --- libwebsockets-4.0.20/lib/tls/mbedtls/mbedtls-x509.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/mbedtls-x509.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,435 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-tls-mbedtls.h" -#include - -#if defined(LWS_PLAT_OPTEE) || defined(OPTEE_DEV_KIT) -struct tm { -int tm_sec; // seconds [0,61] -int tm_min; // minutes [0,59] -int tm_hour; // hour [0,23] -int tm_mday; // day of month [1,31] -int tm_mon; // month of year [0,11] -int tm_year; // years since 1900 -int tm_wday; // day of week [0,6] (Sunday = 0) -int tm_yday; // day of year [0,365] -int tm_isdst; // daylight savings flag -}; -time_t mktime(struct tm *t) -{ - return (time_t)0; -} -#endif - -static time_t -lws_tls_mbedtls_time_to_unix(mbedtls_x509_time *xtime) -{ - struct tm t; - - if (!xtime || !xtime->year || xtime->year < 0) - return (time_t)(long long)-1; - - memset(&t, 0, sizeof(t)); - - t.tm_year = xtime->year - 1900; - t.tm_mon = xtime->mon - 1; /* mbedtls months are 1+, tm are 0+ */ - t.tm_mday = xtime->day - 1; /* mbedtls days are 1+, tm are 0+ */ - t.tm_hour = xtime->hour; - t.tm_min = xtime->min; - t.tm_sec = xtime->sec; - t.tm_isdst = -1; - - return mktime(&t); -} - -static int -lws_tls_mbedtls_get_x509_name(mbedtls_x509_name *name, - union lws_tls_cert_info_results *buf, size_t len) -{ - while (name) { - if (MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid)) { - name = name->next; - continue; - } - - if (len - 1 < name->val.len) - return -1; - - memcpy(&buf->ns.name[0], name->val.p, name->val.len); - buf->ns.name[name->val.len] = '\0'; - buf->ns.len = name->val.len; - - return 0; - } - - return -1; -} - -static int -lws_tls_mbedtls_cert_info(mbedtls_x509_crt *x509, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len) -{ - if (!x509) - return -1; - - switch (type) { - case LWS_TLS_CERT_INFO_VALIDITY_FROM: - buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_from); - if (buf->time == (time_t)(long long)-1) - return -1; - break; - - case LWS_TLS_CERT_INFO_VALIDITY_TO: - buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_to); - if (buf->time == (time_t)(long long)-1) - return -1; - break; - - case LWS_TLS_CERT_INFO_COMMON_NAME: - return lws_tls_mbedtls_get_x509_name(&x509->subject, buf, len); - - case LWS_TLS_CERT_INFO_ISSUER_NAME: - return lws_tls_mbedtls_get_x509_name(&x509->issuer, buf, len); - - case LWS_TLS_CERT_INFO_USAGE: - buf->usage = x509->key_usage; - break; - - case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY: - { - char *p = buf->ns.name; - size_t r = len, u; - - switch (mbedtls_pk_get_type(&x509->pk)) { - case MBEDTLS_PK_RSA: - { - mbedtls_rsa_context *rsa = mbedtls_pk_rsa(x509->pk); - - if (mbedtls_mpi_write_string(&rsa->N, 16, p, r, &u)) - return -1; - r -= u; - p += u; - if (mbedtls_mpi_write_string(&rsa->E, 16, p, r, &u)) - return -1; - - p += u; - buf->ns.len = lws_ptr_diff(p, buf->ns.name); - break; - } - case MBEDTLS_PK_ECKEY: - { - mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(x509->pk); - - if (mbedtls_mpi_write_string(&ecp->Q.X, 16, p, r, &u)) - return -1; - r -= u; - p += u; - if (mbedtls_mpi_write_string(&ecp->Q.Y, 16, p, r, &u)) - return -1; - r -= u; - p += u; - if (mbedtls_mpi_write_string(&ecp->Q.Z, 16, p, r, &u)) - return -1; - p += u; - buf->ns.len = lws_ptr_diff(p, buf->ns.name); - break; - } - default: - lwsl_notice("%s: x509 has unsupported pubkey type %d\n", - __func__, - mbedtls_pk_get_type(&x509->pk)); - - return -1; - } - break; - } - - default: - return -1; - } - - return 0; -} - -#if defined(LWS_WITH_NETWORK) -int -lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len) -{ - mbedtls_x509_crt *x509; - - x509 = ssl_ctx_get_mbedtls_x509_crt(vhost->tls.ssl_ctx); - - return lws_tls_mbedtls_cert_info(x509, type, buf, len); -} - -int -lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len) -{ - mbedtls_x509_crt *x509; - - wsi = lws_get_network_wsi(wsi); - - x509 = ssl_get_peer_mbedtls_x509_crt(wsi->tls.ssl); - - if (!x509) - return -1; - - switch (type) { - case LWS_TLS_CERT_INFO_VERIFIED: - buf->verified = SSL_get_verify_result(wsi->tls.ssl) == X509_V_OK; - return 0; - default: - return lws_tls_mbedtls_cert_info(x509, type, buf, len); - } - - return -1; -} -#endif - -int -lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len) -{ - return lws_tls_mbedtls_cert_info(&x509->cert, type, buf, len); -} - -int -lws_x509_create(struct lws_x509_cert **x509) -{ - *x509 = lws_malloc(sizeof(**x509), __func__); - - return !(*x509); -} - -/* - * Parse one DER-encoded or one or more concatenated PEM-encoded certificates - * and add them to the chained list. - */ - -int -lws_x509_parse_from_pem(struct lws_x509_cert *x509, const void *pem, size_t len) -{ - int ret; - - mbedtls_x509_crt_init(&x509->cert); - - ret = mbedtls_x509_crt_parse(&x509->cert, pem, len); - if (ret) { - if (ret > 0) - mbedtls_x509_crt_free(&x509->cert); - lwsl_err("%s: unable to parse PEM cert: -0x%x\n", - __func__, -ret); - - return -1; - } - - return 0; -} - -int -lws_x509_verify(struct lws_x509_cert *x509, struct lws_x509_cert *trusted, - const char *common_name) -{ - uint32_t flags = 0; - int ret; - - ret = mbedtls_x509_crt_verify_with_profile(&x509->cert, &trusted->cert, - NULL, - &mbedtls_x509_crt_profile_next, - common_name, &flags, NULL, - NULL); - - if (ret) { - lwsl_err("%s: unable to parse PEM cert: -0x%x\n", - __func__, -ret); - - return -1; - } - - return 0; -} - -#if defined(LWS_WITH_JOSE) - -int -lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509, - const char *curves, int rsa_min_bits) -{ - int kt = mbedtls_pk_get_type(&x509->cert.pk), n, count = 0, ret = -1; - mbedtls_rsa_context *rsactx; - mbedtls_ecp_keypair *ecpctx; - mbedtls_mpi *mpi[LWS_GENCRYPTO_RSA_KEYEL_COUNT]; - - memset(jwk, 0, sizeof(*jwk)); - - switch (kt) { - case MBEDTLS_PK_RSA: - lwsl_notice("%s: RSA key\n", __func__); - jwk->kty = LWS_GENCRYPTO_KTY_RSA; - rsactx = mbedtls_pk_rsa(x509->cert.pk); - - mpi[LWS_GENCRYPTO_RSA_KEYEL_E] = &rsactx->E; - mpi[LWS_GENCRYPTO_RSA_KEYEL_N] = &rsactx->N; - mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = &rsactx->D; - mpi[LWS_GENCRYPTO_RSA_KEYEL_P] = &rsactx->P; - mpi[LWS_GENCRYPTO_RSA_KEYEL_Q] = &rsactx->Q; - mpi[LWS_GENCRYPTO_RSA_KEYEL_DP] = &rsactx->DP; - mpi[LWS_GENCRYPTO_RSA_KEYEL_DQ] = &rsactx->DQ; - mpi[LWS_GENCRYPTO_RSA_KEYEL_QI] = &rsactx->QP; - - count = LWS_GENCRYPTO_RSA_KEYEL_COUNT; - n = LWS_GENCRYPTO_RSA_KEYEL_E; - break; - - case MBEDTLS_PK_ECKEY: - lwsl_notice("%s: EC key\n", __func__); - jwk->kty = LWS_GENCRYPTO_KTY_EC; - ecpctx = mbedtls_pk_ec(x509->cert.pk); - mpi[LWS_GENCRYPTO_EC_KEYEL_X] = &ecpctx->Q.X; - mpi[LWS_GENCRYPTO_EC_KEYEL_D] = &ecpctx->d; - mpi[LWS_GENCRYPTO_EC_KEYEL_Y] = &ecpctx->Q.Y; - - if (lws_genec_confirm_curve_allowed_by_tls_id(curves, - ecpctx->grp.id, jwk)) - /* already logged */ - goto bail; - - count = LWS_GENCRYPTO_EC_KEYEL_COUNT; - n = LWS_GENCRYPTO_EC_KEYEL_X; - break; - default: - lwsl_err("%s: key type %d not supported\n", __func__, kt); - - return -1; - } - - for (; n < count; n++) { - if (!mbedtls_mpi_size(mpi[n])) - continue; - - jwk->e[n].buf = lws_malloc(mbedtls_mpi_size(mpi[n]), "certjwk"); - if (!jwk->e[n].buf) - goto bail; - jwk->e[n].len = mbedtls_mpi_size(mpi[n]); - mbedtls_mpi_write_binary(mpi[n], jwk->e[n].buf, jwk->e[n].len); - } - - ret = 0; - -bail: - /* jwk destroy will clean up partials */ - if (ret) - lws_jwk_destroy(jwk); - - return ret; -} - -int -lws_x509_jwk_privkey_pem(struct lws_jwk *jwk, void *pem, size_t len, - const char *passphrase) -{ - mbedtls_rsa_context *rsactx; - mbedtls_ecp_keypair *ecpctx; - mbedtls_pk_context pk; - mbedtls_mpi *mpi[LWS_GENCRYPTO_RSA_KEYEL_COUNT]; - int n, ret = -1, count = 0; - - mbedtls_pk_init(&pk); - - n = 0; - if (passphrase) - n = strlen(passphrase); - n = mbedtls_pk_parse_key(&pk, pem, len, (uint8_t *)passphrase, n); - if (n) { - lwsl_err("%s: parse PEM key failed: -0x%x\n", __func__, -n); - - return -1; - } - - /* the incoming private key type */ - switch (mbedtls_pk_get_type(&pk)) { - case MBEDTLS_PK_RSA: - if (jwk->kty != LWS_GENCRYPTO_KTY_RSA) { - lwsl_err("%s: RSA privkey, non-RSA jwk\n", __func__); - goto bail; - } - rsactx = mbedtls_pk_rsa(pk); - mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = &rsactx->D; - mpi[LWS_GENCRYPTO_RSA_KEYEL_P] = &rsactx->P; - mpi[LWS_GENCRYPTO_RSA_KEYEL_Q] = &rsactx->Q; - n = LWS_GENCRYPTO_RSA_KEYEL_D; - count = LWS_GENCRYPTO_RSA_KEYEL_Q + 1; - break; - case MBEDTLS_PK_ECKEY: - if (jwk->kty != LWS_GENCRYPTO_KTY_EC) { - lwsl_err("%s: EC privkey, non-EC jwk\n", __func__); - goto bail; - } - ecpctx = mbedtls_pk_ec(pk); - mpi[LWS_GENCRYPTO_EC_KEYEL_D] = &ecpctx->d; - n = LWS_GENCRYPTO_EC_KEYEL_D; - count = n + 1; - break; - default: - lwsl_err("%s: unusable key type %d\n", __func__, - mbedtls_pk_get_type(&pk)); - goto bail; - } - - for (; n < count; n++) { - if (!mbedtls_mpi_size(mpi[n])) { - lwsl_err("%s: empty privkey\n", __func__); - goto bail; - } - - jwk->e[n].buf = lws_malloc(mbedtls_mpi_size(mpi[n]), "certjwk"); - if (!jwk->e[n].buf) - goto bail; - jwk->e[n].len = mbedtls_mpi_size(mpi[n]); - mbedtls_mpi_write_binary(mpi[n], jwk->e[n].buf, jwk->e[n].len); - } - - ret = 0; - -bail: - mbedtls_pk_free(&pk); - - return ret; -} -#endif - -void -lws_x509_destroy(struct lws_x509_cert **x509) -{ - if (!*x509) - return; - - mbedtls_x509_crt_free(&(*x509)->cert); - - lws_free_set_NULL(*x509); -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/private-lib-tls-mbedtls.h libwebsockets-2.4.2/lib/tls/mbedtls/private-lib-tls-mbedtls.h --- libwebsockets-4.0.20/lib/tls/mbedtls/private-lib-tls-mbedtls.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/private-lib-tls-mbedtls.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * gencrypto mbedtls-specific helper declarations - */ - -#include - -struct lws_x509_cert { - mbedtls_x509_crt cert; /* has a .next for linked-list / chain */ -}; - -mbedtls_md_type_t -lws_gencrypto_mbedtls_hash_to_MD_TYPE(enum lws_genhash_types hash_type); - -int -lws_gencrypto_mbedtls_rngf(void *context, unsigned char *buf, size_t len); diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl3.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl3.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl3.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl3.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL3_H_ -#define _SSL3_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -# define SSL3_AD_CLOSE_NOTIFY 0 -# define SSL3_AD_UNEXPECTED_MESSAGE 10/* fatal */ -# define SSL3_AD_BAD_RECORD_MAC 20/* fatal */ -# define SSL3_AD_DECOMPRESSION_FAILURE 30/* fatal */ -# define SSL3_AD_HANDSHAKE_FAILURE 40/* fatal */ -# define SSL3_AD_NO_CERTIFICATE 41 -# define SSL3_AD_BAD_CERTIFICATE 42 -# define SSL3_AD_UNSUPPORTED_CERTIFICATE 43 -# define SSL3_AD_CERTIFICATE_REVOKED 44 -# define SSL3_AD_CERTIFICATE_EXPIRED 45 -# define SSL3_AD_CERTIFICATE_UNKNOWN 46 -# define SSL3_AD_ILLEGAL_PARAMETER 47/* fatal */ - -# define SSL3_AL_WARNING 1 -# define SSL3_AL_FATAL 2 - -#define SSL3_VERSION 0x0300 - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_cert.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_cert.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_cert.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_cert.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_CERT_H_ -#define _SSL_CERT_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#include "ssl_types.h" - -/** - * @brief create a certification object include private key object according to input certification - * - * @param ic - input certification point - * - * @return certification object point - */ -CERT *__ssl_cert_new(CERT *ic); - -/** - * @brief create a certification object include private key object - * - * @param none - * - * @return certification object point - */ -CERT* ssl_cert_new(void); - -/** - * @brief free a certification object - * - * @param cert - certification object point - * - * @return none - */ -void ssl_cert_free(CERT *cert); - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_code.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_code.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_code.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_code.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_CODE_H_ -#define _SSL_CODE_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#include "ssl3.h" -#include "tls1.h" -#include "x509_vfy.h" - -/* Used in SSL_set_shutdown()/SSL_get_shutdown(); */ -# define SSL_SENT_SHUTDOWN 1 -# define SSL_RECEIVED_SHUTDOWN 2 - -# define SSL_VERIFY_NONE 0x00 -# define SSL_VERIFY_PEER 0x01 -# define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02 -# define SSL_VERIFY_CLIENT_ONCE 0x04 - -/* - * The following 3 states are kept in ssl->rlayer.rstate when reads fail, you - * should not need these - */ -# define SSL_ST_READ_HEADER 0xF0 -# define SSL_ST_READ_BODY 0xF1 -# define SSL_ST_READ_DONE 0xF2 - -# define SSL_NOTHING 1 -# define SSL_WRITING 2 -# define SSL_READING 3 -# define SSL_X509_LOOKUP 4 -# define SSL_ASYNC_PAUSED 5 -# define SSL_ASYNC_NO_JOBS 6 - - -# define SSL_ERROR_NONE 0 -# define SSL_ERROR_SSL 1 -# define SSL_ERROR_WANT_READ 2 -# define SSL_ERROR_WANT_WRITE 3 -# define SSL_ERROR_WANT_X509_LOOKUP 4 -# define SSL_ERROR_SYSCALL 5/* look at error stack/return value/errno */ -# define SSL_ERROR_ZERO_RETURN 6 -# define SSL_ERROR_WANT_CONNECT 7 -# define SSL_ERROR_WANT_ACCEPT 8 -# define SSL_ERROR_WANT_ASYNC 9 -# define SSL_ERROR_WANT_ASYNC_JOB 10 - -/* Message flow states */ -typedef enum { - /* No handshake in progress */ - MSG_FLOW_UNINITED, - /* A permanent error with this connection */ - MSG_FLOW_ERROR, - /* We are about to renegotiate */ - MSG_FLOW_RENEGOTIATE, - /* We are reading messages */ - MSG_FLOW_READING, - /* We are writing messages */ - MSG_FLOW_WRITING, - /* Handshake has finished */ - MSG_FLOW_FINISHED -} MSG_FLOW_STATE; - -/* SSL subsystem states */ -typedef enum { - TLS_ST_BEFORE, - TLS_ST_OK, - DTLS_ST_CR_HELLO_VERIFY_REQUEST, - TLS_ST_CR_SRVR_HELLO, - TLS_ST_CR_CERT, - TLS_ST_CR_CERT_STATUS, - TLS_ST_CR_KEY_EXCH, - TLS_ST_CR_CERT_REQ, - TLS_ST_CR_SRVR_DONE, - TLS_ST_CR_SESSION_TICKET, - TLS_ST_CR_CHANGE, - TLS_ST_CR_FINISHED, - TLS_ST_CW_CLNT_HELLO, - TLS_ST_CW_CERT, - TLS_ST_CW_KEY_EXCH, - TLS_ST_CW_CERT_VRFY, - TLS_ST_CW_CHANGE, - TLS_ST_CW_NEXT_PROTO, - TLS_ST_CW_FINISHED, - TLS_ST_SW_HELLO_REQ, - TLS_ST_SR_CLNT_HELLO, - DTLS_ST_SW_HELLO_VERIFY_REQUEST, - TLS_ST_SW_SRVR_HELLO, - TLS_ST_SW_CERT, - TLS_ST_SW_KEY_EXCH, - TLS_ST_SW_CERT_REQ, - TLS_ST_SW_SRVR_DONE, - TLS_ST_SR_CERT, - TLS_ST_SR_KEY_EXCH, - TLS_ST_SR_CERT_VRFY, - TLS_ST_SR_NEXT_PROTO, - TLS_ST_SR_CHANGE, - TLS_ST_SR_FINISHED, - TLS_ST_SW_SESSION_TICKET, - TLS_ST_SW_CERT_STATUS, - TLS_ST_SW_CHANGE, - TLS_ST_SW_FINISHED -} OSSL_HANDSHAKE_STATE; - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_dbg.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_dbg.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_dbg.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_dbg.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,190 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_DEBUG_H_ -#define _SSL_DEBUG_H_ - -#include "ssl_port.h" - -#ifdef __cplusplus - extern "C" { -#endif - -#ifdef CONFIG_OPENSSL_DEBUG_LEVEL - #define SSL_DEBUG_LEVEL CONFIG_OPENSSL_DEBUG_LEVEL -#else - #define SSL_DEBUG_LEVEL 0 -#endif - -#define SSL_DEBUG_ON (SSL_DEBUG_LEVEL + 1) -#define SSL_DEBUG_OFF (SSL_DEBUG_LEVEL - 1) - -#ifdef CONFIG_OPENSSL_DEBUG - #ifndef SSL_DEBUG_LOG - #error "SSL_DEBUG_LOG is not defined" - #endif - - #ifndef SSL_DEBUG_FL - #define SSL_DEBUG_FL "\n" - #endif - - #define SSL_SHOW_LOCATION() \ - SSL_DEBUG_LOG("SSL assert : %s %d\n", \ - __FILE__, __LINE__) - - #define SSL_DEBUG(level, fmt, ...) \ - { \ - if (level > SSL_DEBUG_LEVEL) { \ - SSL_DEBUG_LOG(fmt SSL_DEBUG_FL, ##__VA_ARGS__); \ - } \ - } -#else /* CONFIG_OPENSSL_DEBUG */ - #define SSL_SHOW_LOCATION() - - #define SSL_DEBUG(level, fmt, ...) -#endif /* CONFIG_OPENSSL_DEBUG */ - -/** - * OpenSSL assert function - * - * if select "CONFIG_OPENSSL_ASSERT_DEBUG", SSL_ASSERT* will show error file name and line - * if select "CONFIG_OPENSSL_ASSERT_EXIT", SSL_ASSERT* will just return error code. - * if select "CONFIG_OPENSSL_ASSERT_DEBUG_EXIT" SSL_ASSERT* will show error file name and line, - * then return error code. - * if select "CONFIG_OPENSSL_ASSERT_DEBUG_BLOCK", SSL_ASSERT* will show error file name and line, - * then block here with "while (1)" - * - * SSL_ASSERT1 may will return "-1", so function's return argument is integer. - * SSL_ASSERT2 may will return "NULL", so function's return argument is a point. - * SSL_ASSERT2 may will return nothing, so function's return argument is "void". - */ -#if defined(CONFIG_OPENSSL_ASSERT_DEBUG) - #define SSL_ASSERT1(s) \ - { \ - if (!(s)) { \ - SSL_SHOW_LOCATION(); \ - } \ - } - - #define SSL_ASSERT2(s) \ - { \ - if (!(s)) { \ - SSL_SHOW_LOCATION(); \ - } \ - } - - #define SSL_ASSERT3(s) \ - { \ - if (!(s)) { \ - SSL_SHOW_LOCATION(); \ - } \ - } -#elif defined(CONFIG_OPENSSL_ASSERT_EXIT) - #define SSL_ASSERT1(s) \ - { \ - if (!(s)) { \ - return -1; \ - } \ - } - - #define SSL_ASSERT2(s) \ - { \ - if (!(s)) { \ - return NULL; \ - } \ - } - - #define SSL_ASSERT3(s) \ - { \ - if (!(s)) { \ - return ; \ - } \ - } -#elif defined(CONFIG_OPENSSL_ASSERT_DEBUG_EXIT) - #define SSL_ASSERT1(s) \ - { \ - if (!(s)) { \ - SSL_SHOW_LOCATION(); \ - return -1; \ - } \ - } - - #define SSL_ASSERT2(s) \ - { \ - if (!(s)) { \ - SSL_SHOW_LOCATION(); \ - return NULL; \ - } \ - } - - #define SSL_ASSERT3(s) \ - { \ - if (!(s)) { \ - SSL_SHOW_LOCATION(); \ - return ; \ - } \ - } -#elif defined(CONFIG_OPENSSL_ASSERT_DEBUG_BLOCK) - #define SSL_ASSERT1(s) \ - { \ - if (!(s)) { \ - SSL_SHOW_LOCATION(); \ - while (1); \ - } \ - } - - #define SSL_ASSERT2(s) \ - { \ - if (!(s)) { \ - SSL_SHOW_LOCATION(); \ - while (1); \ - } \ - } - - #define SSL_ASSERT3(s) \ - { \ - if (!(s)) { \ - SSL_SHOW_LOCATION(); \ - while (1); \ - } \ - } -#else - #define SSL_ASSERT1(s) - #define SSL_ASSERT2(s) - #define SSL_ASSERT3(s) -#endif - -#define SSL_PLATFORM_DEBUG_LEVEL SSL_DEBUG_OFF -#define SSL_PLATFORM_ERROR_LEVEL SSL_DEBUG_ON - -#define SSL_CERT_DEBUG_LEVEL SSL_DEBUG_OFF -#define SSL_CERT_ERROR_LEVEL SSL_DEBUG_ON - -#define SSL_PKEY_DEBUG_LEVEL SSL_DEBUG_OFF -#define SSL_PKEY_ERROR_LEVEL SSL_DEBUG_ON - -#define SSL_X509_DEBUG_LEVEL SSL_DEBUG_OFF -#define SSL_X509_ERROR_LEVEL SSL_DEBUG_ON - -#define SSL_LIB_DEBUG_LEVEL SSL_DEBUG_OFF -#define SSL_LIB_ERROR_LEVEL SSL_DEBUG_ON - -#define SSL_STACK_DEBUG_LEVEL SSL_DEBUG_OFF -#define SSL_STACK_ERROR_LEVEL SSL_DEBUG_ON - -#ifdef __cplusplus - } -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_lib.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_lib.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_lib.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_lib.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_LIB_H_ -#define _SSL_LIB_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#include "ssl_types.h" - - void _ssl_set_alpn_list(const SSL *ssl); - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_methods.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_methods.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_methods.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_methods.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,121 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_METHODS_H_ -#define _SSL_METHODS_H_ - -#include "ssl_types.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/** - * TLS method function implement - */ -#define IMPLEMENT_TLS_METHOD_FUNC(func_name, \ - new, free, \ - handshake, shutdown, clear, \ - read, send, pending, \ - set_fd, get_fd, \ - set_bufflen, \ - get_verify_result, \ - get_state) \ - static const SSL_METHOD_FUNC func_name LOCAL_ATRR = { \ - new, \ - free, \ - handshake, \ - shutdown, \ - clear, \ - read, \ - send, \ - pending, \ - set_fd, \ - get_fd, \ - set_bufflen, \ - get_verify_result, \ - get_state \ - }; - -#define IMPLEMENT_TLS_METHOD(ver, mode, fun, func_name) \ - const SSL_METHOD* func_name(void) { \ - static const SSL_METHOD func_name##_data LOCAL_ATRR = { \ - ver, \ - mode, \ - &(fun), \ - }; \ - return &func_name##_data; \ - } - -#define IMPLEMENT_SSL_METHOD(ver, mode, fun, func_name) \ - const SSL_METHOD* func_name(void) { \ - static const SSL_METHOD func_name##_data LOCAL_ATRR = { \ - ver, \ - mode, \ - &(fun), \ - }; \ - return &func_name##_data; \ - } - -#define IMPLEMENT_X509_METHOD(func_name, \ - new, \ - free, \ - load, \ - show_info) \ - const X509_METHOD* func_name(void) { \ - static const X509_METHOD func_name##_data LOCAL_ATRR = { \ - new, \ - free, \ - load, \ - show_info \ - }; \ - return &func_name##_data; \ - } - -#define IMPLEMENT_PKEY_METHOD(func_name, \ - new, \ - free, \ - load) \ - const PKEY_METHOD* func_name(void) { \ - static const PKEY_METHOD func_name##_data LOCAL_ATRR = { \ - new, \ - free, \ - load \ - }; \ - return &func_name##_data; \ - } - -/** - * @brief get X509 object method - * - * @param none - * - * @return X509 object method point - */ -const X509_METHOD* X509_method(void); - -/** - * @brief get private key object method - * - * @param none - * - * @return private key object method point - */ -const PKEY_METHOD* EVP_PKEY_method(void); - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_pkey.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_pkey.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_pkey.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_pkey.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_PKEY_H_ -#define _SSL_PKEY_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#include "ssl_types.h" - -/** - * @brief create a private key object according to input private key - * - * @param ipk - input private key point - * - * @return new private key object point - */ -EVP_PKEY* __EVP_PKEY_new(EVP_PKEY *ipk); - -/** - * @brief create a private key object - * - * @param none - * - * @return private key object point - */ -EVP_PKEY* EVP_PKEY_new(void); - -/** - * @brief load a character key context into system context. If '*a' is pointed to the - * private key, then load key into it. Or create a new private key object - * - * @param type - private key type - * @param a - a point pointed to a private key point - * @param pp - a point pointed to the key context memory point - * @param length - key bytes - * - * @return private key object point - */ -EVP_PKEY* d2i_PrivateKey(int type, - EVP_PKEY **a, - const unsigned char **pp, - long length); - -/** - * @brief free a private key object - * - * @param pkey - private key object point - * - * @return none - */ -void EVP_PKEY_free(EVP_PKEY *x); - -/** - * @brief load private key into the SSL - * - * @param type - private key type - * @param ssl - SSL point - * @param len - data bytes - * @param d - data point - * - * @return result - * 0 : failed - * 1 : OK - */ - int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const unsigned char *d, long len); - - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_stack.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_stack.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_stack.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_stack.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -#ifndef _SSL_STACK_H_ -#define _SSL_STACK_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#include "ssl_types.h" - -#define STACK_OF(type) struct stack_st_##type - -#define SKM_DEFINE_STACK_OF(t1, t2, t3) \ - STACK_OF(t1); \ - static ossl_inline STACK_OF(t1) *sk_##t1##_new_null(void) \ - { \ - return (STACK_OF(t1) *)OPENSSL_sk_new_null(); \ - } \ - -#define DEFINE_STACK_OF(t) SKM_DEFINE_STACK_OF(t, t, t) - -/** - * @brief create a openssl stack object - * - * @param c - stack function - * - * @return openssl stack object point - */ -OPENSSL_STACK* OPENSSL_sk_new(OPENSSL_sk_compfunc c); - -/** - * @brief create a NULL function openssl stack object - * - * @param none - * - * @return openssl stack object point - */ -OPENSSL_STACK *OPENSSL_sk_new_null(void); - -/** - * @brief free openssl stack object - * - * @param openssl stack object point - * - * @return none - */ -void OPENSSL_sk_free(OPENSSL_STACK *stack); - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_types.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_types.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_types.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_types.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,311 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_TYPES_H_ -#define _SSL_TYPES_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -//#include "private-lib-core.h" -#include -#if defined(LWS_PLAT_FREERTOS) - /* AMAZON RTOS has its own setting via MTK_MBEDTLS_CONFIG_FILE */ - #if !defined(LWS_AMAZON_RTOS) - #undef MBEDTLS_CONFIG_FILE - #define MBEDTLS_CONFIG_FILE - #endif -#endif - -#include "ssl_code.h" - -typedef void SSL_CIPHER; - -typedef void X509_STORE_CTX; -typedef void X509_STORE; - -typedef void RSA; - -typedef void STACK; -typedef void BIO; - -#if defined(WIN32) || defined(_WIN32) -#define ossl_inline __inline -#else -#define ossl_inline inline -#endif - -#define SSL_METHOD_CALL(f, s, ...) s->method->func->ssl_##f(s, ##__VA_ARGS__) -#define X509_METHOD_CALL(f, x, ...) x->method->x509_##f(x, ##__VA_ARGS__) -#define EVP_PKEY_METHOD_CALL(f, k, ...) k->method->pkey_##f(k, ##__VA_ARGS__) - -typedef int (*OPENSSL_sk_compfunc)(const void *, const void *); - -struct stack_st; -typedef struct stack_st OPENSSL_STACK; - -struct ssl_method_st; -typedef struct ssl_method_st SSL_METHOD; - -struct ssl_method_func_st; -typedef struct ssl_method_func_st SSL_METHOD_FUNC; - -struct record_layer_st; -typedef struct record_layer_st RECORD_LAYER; - -struct ossl_statem_st; -typedef struct ossl_statem_st OSSL_STATEM; - -struct ssl_session_st; -typedef struct ssl_session_st SSL_SESSION; - -struct ssl_ctx_st; -typedef struct ssl_ctx_st SSL_CTX; - -struct ssl_st; -typedef struct ssl_st SSL; - -struct cert_st; -typedef struct cert_st CERT; - -struct x509_st; -typedef struct x509_st X509; - -struct X509_VERIFY_PARAM_st; -typedef struct X509_VERIFY_PARAM_st X509_VERIFY_PARAM; - -struct evp_pkey_st; -typedef struct evp_pkey_st EVP_PKEY; - -struct x509_method_st; -typedef struct x509_method_st X509_METHOD; - -struct pkey_method_st; -typedef struct pkey_method_st PKEY_METHOD; - -struct stack_st { - - char **data; - - int num_alloc; - - OPENSSL_sk_compfunc c; -}; - -struct evp_pkey_st { - - void *pkey_pm; - - const PKEY_METHOD *method; -}; - -struct x509_st { - - /* X509 certification platform private point */ - void *x509_pm; - - const X509_METHOD *method; -}; - -struct cert_st { - - int sec_level; - - X509 *x509; - - EVP_PKEY *pkey; - -}; - -struct ossl_statem_st { - - MSG_FLOW_STATE state; - - int hand_state; -}; - -struct record_layer_st { - - int rstate; - - int read_ahead; -}; - -struct ssl_session_st { - - long timeout; - - long time; - - X509 *peer; -}; - -struct X509_VERIFY_PARAM_st { - - int depth; - -}; - -typedef int (*next_proto_cb)(SSL *ssl, const unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg); - - -struct ssl_ctx_st -{ - int version; - - int references; - - unsigned long options; - - const SSL_METHOD *method; - - CERT *cert; - - X509 *client_CA; - - const char **alpn_protos; - - next_proto_cb alpn_cb; - - int verify_mode; - - int (*default_verify_callback) (int ok, X509_STORE_CTX *ctx); - - long session_timeout; - - int read_ahead; - - int read_buffer_len; - - X509_VERIFY_PARAM param; -}; - -struct ssl_st -{ - /* protocol version(one of SSL3.0, TLS1.0, etc.) */ - int version; - - unsigned long options; - - /* shut things down(0x01 : sent, 0x02 : received) */ - int shutdown; - - CERT *cert; - - X509 *client_CA; - - SSL_CTX *ctx; - - const SSL_METHOD *method; - - const char **alpn_protos; - - RECORD_LAYER rlayer; - - /* where we are */ - OSSL_STATEM statem; - - SSL_SESSION *session; - - int verify_mode; - - int (*verify_callback) (int ok, X509_STORE_CTX *ctx); - - int rwstate; - int interrupted_remaining_write; - - long verify_result; - - X509_VERIFY_PARAM param; - - int err; - - void (*info_callback) (const SSL *ssl, int type, int val); - - /* SSL low-level system arch point */ - void *ssl_pm; -}; - -struct ssl_method_st { - /* protocol version(one of SSL3.0, TLS1.0, etc.) */ - int version; - - /* SSL mode(client(0) , server(1), not known(-1)) */ - int endpoint; - - const SSL_METHOD_FUNC *func; -}; - -struct ssl_method_func_st { - - int (*ssl_new)(SSL *ssl); - - void (*ssl_free)(SSL *ssl); - - int (*ssl_handshake)(SSL *ssl); - - int (*ssl_shutdown)(SSL *ssl); - - int (*ssl_clear)(SSL *ssl); - - int (*ssl_read)(SSL *ssl, void *buffer, int len); - - int (*ssl_send)(SSL *ssl, const void *buffer, int len); - - int (*ssl_pending)(const SSL *ssl); - - void (*ssl_set_fd)(SSL *ssl, int fd, int mode); - - int (*ssl_get_fd)(const SSL *ssl, int mode); - - void (*ssl_set_bufflen)(SSL *ssl, int len); - - long (*ssl_get_verify_result)(const SSL *ssl); - - OSSL_HANDSHAKE_STATE (*ssl_get_state)(const SSL *ssl); -}; - -struct x509_method_st { - - int (*x509_new)(X509 *x, X509 *m_x); - - void (*x509_free)(X509 *x); - - int (*x509_load)(X509 *x, const unsigned char *buf, int len); - - int (*x509_show_info)(X509 *x); -}; - -struct pkey_method_st { - - int (*pkey_new)(EVP_PKEY *pkey, EVP_PKEY *m_pkey); - - void (*pkey_free)(EVP_PKEY *pkey); - - int (*pkey_load)(EVP_PKEY *pkey, const unsigned char *buf, int len); -}; - -#define OPENSSL_NPN_NEGOTIATED 1 - -int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx); -int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx); - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_x509.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_x509.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/ssl_x509.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/ssl_x509.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_X509_H_ -#define _SSL_X509_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#include "ssl_types.h" -#include "ssl_stack.h" - -DEFINE_STACK_OF(X509_NAME) - -/** - * @brief create a X509 certification object according to input X509 certification - * - * @param ix - input X509 certification point - * - * @return new X509 certification object point - */ -X509* __X509_new(X509 *ix); - -/** - * @brief create a X509 certification object - * - * @param none - * - * @return X509 certification object point - */ -X509* X509_new(void); - -/** - * @brief load a character certification context into system context. If '*cert' is pointed to the - * certification, then load certification into it. Or create a new X509 certification object - * - * @param cert - a point pointed to X509 certification - * @param buffer - a point pointed to the certification context memory point - * @param length - certification bytes - * - * @return X509 certification object point - */ -X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); - -/** - * @brief free a X509 certification object - * - * @param x - X509 certification object point - * - * @return none - */ -void X509_free(X509 *x); - -/** - * @brief set SSL context client CA certification - * - * @param ctx - SSL context point - * @param x - X509 certification point - * - * @return result - * 0 : failed - * 1 : OK - */ -int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); - -/** - * @brief add CA client certification into the SSL - * - * @param ssl - SSL point - * @param x - X509 certification point - * - * @return result - * 0 : failed - * 1 : OK - */ -int SSL_add_client_CA(SSL *ssl, X509 *x); - -/** - * @brief load certification into the SSL - * - * @param ssl - SSL point - * @param len - data bytes - * @param d - data point - * - * @return result - * 0 : failed - * 1 : OK - * - */ - -int SSL_use_certificate_ASN1(SSL *ssl, const unsigned char *d, int len); - -const char *X509_verify_cert_error_string(long n); - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/tls1.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/tls1.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/tls1.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/tls1.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _TLS1_H_ -#define _TLS1_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -# define TLS1_AD_DECRYPTION_FAILED 21 -# define TLS1_AD_RECORD_OVERFLOW 22 -# define TLS1_AD_UNKNOWN_CA 48/* fatal */ -# define TLS1_AD_ACCESS_DENIED 49/* fatal */ -# define TLS1_AD_DECODE_ERROR 50/* fatal */ -# define TLS1_AD_DECRYPT_ERROR 51 -# define TLS1_AD_EXPORT_RESTRICTION 60/* fatal */ -# define TLS1_AD_PROTOCOL_VERSION 70/* fatal */ -# define TLS1_AD_INSUFFICIENT_SECURITY 71/* fatal */ -# define TLS1_AD_INTERNAL_ERROR 80/* fatal */ -# define TLS1_AD_INAPPROPRIATE_FALLBACK 86/* fatal */ -# define TLS1_AD_USER_CANCELLED 90 -# define TLS1_AD_NO_RENEGOTIATION 100 -/* codes 110-114 are from RFC3546 */ -# define TLS1_AD_UNSUPPORTED_EXTENSION 110 -# define TLS1_AD_CERTIFICATE_UNOBTAINABLE 111 -# define TLS1_AD_UNRECOGNIZED_NAME 112 -# define TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE 113 -# define TLS1_AD_BAD_CERTIFICATE_HASH_VALUE 114 -# define TLS1_AD_UNKNOWN_PSK_IDENTITY 115/* fatal */ -# define TLS1_AD_NO_APPLICATION_PROTOCOL 120 /* fatal */ - -/* Special value for method supporting multiple versions */ -#define TLS_ANY_VERSION 0x10000 - -#define TLS1_VERSION 0x0301 -#define TLS1_1_VERSION 0x0302 -#define TLS1_2_VERSION 0x0303 - -#define SSL_TLSEXT_ERR_OK 0 -#define SSL_TLSEXT_ERR_NOACK 3 - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/x509_vfy.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/x509_vfy.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/internal/x509_vfy.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/internal/x509_vfy.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _X509_VFY_H_ -#define _X509_VFY_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#define X509_V_OK 0 -#define X509_V_ERR_UNSPECIFIED 1 -#define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT 2 -#define X509_V_ERR_UNABLE_TO_GET_CRL 3 -#define X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE 4 -#define X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE 5 -#define X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY 6 -#define X509_V_ERR_CERT_SIGNATURE_FAILURE 7 -#define X509_V_ERR_CRL_SIGNATURE_FAILURE 8 -#define X509_V_ERR_CERT_NOT_YET_VALID 9 -#define X509_V_ERR_CERT_HAS_EXPIRED 10 -#define X509_V_ERR_CRL_NOT_YET_VALID 11 -#define X509_V_ERR_CRL_HAS_EXPIRED 12 -#define X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD 13 -#define X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD 14 -#define X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD 15 -#define X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD 16 -#define X509_V_ERR_OUT_OF_MEM 17 -#define X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT 18 -#define X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN 19 -#define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY 20 -#define X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE 21 -#define X509_V_ERR_CERT_CHAIN_TOO_LONG 22 -#define X509_V_ERR_CERT_REVOKED 23 -#define X509_V_ERR_INVALID_CA 24 -#define X509_V_ERR_PATH_LENGTH_EXCEEDED 25 -#define X509_V_ERR_INVALID_PURPOSE 26 -#define X509_V_ERR_CERT_UNTRUSTED 27 -#define X509_V_ERR_CERT_REJECTED 28 -/* These are 'informational' when looking for issuer cert */ -#define X509_V_ERR_SUBJECT_ISSUER_MISMATCH 29 -#define X509_V_ERR_AKID_SKID_MISMATCH 30 -#define X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH 31 -#define X509_V_ERR_KEYUSAGE_NO_CERTSIGN 32 -#define X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER 33 -#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34 -#define X509_V_ERR_KEYUSAGE_NO_CRL_SIGN 35 -#define X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION 36 -#define X509_V_ERR_INVALID_NON_CA 37 -#define X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED 38 -#define X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE 39 -#define X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED 40 -#define X509_V_ERR_INVALID_EXTENSION 41 -#define X509_V_ERR_INVALID_POLICY_EXTENSION 42 -#define X509_V_ERR_NO_EXPLICIT_POLICY 43 -#define X509_V_ERR_DIFFERENT_CRL_SCOPE 44 -#define X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE 45 -#define X509_V_ERR_UNNESTED_RESOURCE 46 -#define X509_V_ERR_PERMITTED_VIOLATION 47 -#define X509_V_ERR_EXCLUDED_VIOLATION 48 -#define X509_V_ERR_SUBTREE_MINMAX 49 -/* The application is not happy */ -#define X509_V_ERR_APPLICATION_VERIFICATION 50 -#define X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE 51 -#define X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX 52 -#define X509_V_ERR_UNSUPPORTED_NAME_SYNTAX 53 -#define X509_V_ERR_CRL_PATH_VALIDATION_ERROR 54 -/* Another issuer check debug option */ -#define X509_V_ERR_PATH_LOOP 55 -/* Suite B mode algorithm violation */ -#define X509_V_ERR_SUITE_B_INVALID_VERSION 56 -#define X509_V_ERR_SUITE_B_INVALID_ALGORITHM 57 -#define X509_V_ERR_SUITE_B_INVALID_CURVE 58 -#define X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM 59 -#define X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED 60 -#define X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 61 -/* Host, email and IP check errors */ -#define X509_V_ERR_HOSTNAME_MISMATCH 62 -#define X509_V_ERR_EMAIL_MISMATCH 63 -#define X509_V_ERR_IP_ADDRESS_MISMATCH 64 -/* DANE TLSA errors */ -#define X509_V_ERR_DANE_NO_MATCH 65 -/* security level errors */ -#define X509_V_ERR_EE_KEY_TOO_SMALL 66 -#define X509_V_ERR_CA_KEY_TOO_SMALL 67 -#define X509_V_ERR_CA_MD_TOO_WEAK 68 -/* Caller error */ -#define X509_V_ERR_INVALID_CALL 69 -/* Issuer lookup error */ -#define X509_V_ERR_STORE_LOOKUP 70 -/* Certificate transparency */ -#define X509_V_ERR_NO_VALID_SCTS 71 - -#define X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION 72 - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/openssl/ssl.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/openssl/ssl.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/openssl/ssl.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/openssl/ssl.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,1827 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_H_ -#define _SSL_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#include -#include "ssl_x509.h" -#include "ssl_pkey.h" - -/* -{ -*/ - -#define SSL_CB_ALERT 0x4000 - -#define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT (1 << 0) -#define X509_CHECK_FLAG_NO_WILDCARDS (1 << 1) -#define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS (1 << 2) -#define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS (1 << 3) -#define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS (1 << 4) - - mbedtls_x509_crt * - ssl_ctx_get_mbedtls_x509_crt(SSL_CTX *ssl_ctx); - - mbedtls_x509_crt * - ssl_get_peer_mbedtls_x509_crt(SSL *ssl); - - int SSL_set_sni_callback(SSL *ssl, int(*cb)(void *, mbedtls_ssl_context *, - const unsigned char *, size_t), void *param); - - void SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx); - - int SSL_CTX_add_client_CA_ASN1(SSL_CTX *ssl, int len, - const unsigned char *d); - - SSL *SSL_SSL_from_mbedtls_ssl_context(mbedtls_ssl_context *msc); - -/** - * @brief create a SSL context - * - * @param method - the SSL context method point - * - * @return the context point - */ -SSL_CTX* SSL_CTX_new(const SSL_METHOD *method); - -/** - * @brief free a SSL context - * - * @param method - the SSL context point - * - * @return none - */ -void SSL_CTX_free(SSL_CTX *ctx); - -/** - * @brief create a SSL - * - * @param ctx - the SSL context point - * - * @return the SSL point - */ -SSL* SSL_new(SSL_CTX *ctx); - -/** - * @brief free the SSL - * - * @param ssl - the SSL point - * - * @return none - */ -void SSL_free(SSL *ssl); - -/** - * @brief connect to the remote SSL server - * - * @param ssl - the SSL point - * - * @return result - * 1 : OK - * -1 : failed - */ -int SSL_connect(SSL *ssl); - -/** - * @brief accept the remote connection - * - * @param ssl - the SSL point - * - * @return result - * 1 : OK - * -1 : failed - */ -int SSL_accept(SSL *ssl); - -/** - * @brief read data from to remote - * - * @param ssl - the SSL point which has been connected - * @param buffer - the received data buffer point - * @param len - the received data length - * - * @return result - * > 0 : OK, and return received data bytes - * = 0 : connection is closed - * < 0 : an error catch - */ -int SSL_read(SSL *ssl, void *buffer, int len); - -/** - * @brief send the data to remote - * - * @param ssl - the SSL point which has been connected - * @param buffer - the send data buffer point - * @param len - the send data length - * - * @return result - * > 0 : OK, and return sent data bytes - * = 0 : connection is closed - * < 0 : an error catch - */ -int SSL_write(SSL *ssl, const void *buffer, int len); - -/** - * @brief get the verifying result of the SSL certification - * - * @param ssl - the SSL point - * - * @return the result of verifying - */ -long SSL_get_verify_result(const SSL *ssl); - -/** - * @brief shutdown the connection - * - * @param ssl - the SSL point - * - * @return result - * 1 : OK - * 0 : shutdown is not finished - * -1 : an error catch - */ -int SSL_shutdown(SSL *ssl); - -/** - * @brief bind the socket file description into the SSL - * - * @param ssl - the SSL point - * @param fd - socket handle - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_set_fd(SSL *ssl, int fd); - -/** - * @brief These functions load the private key into the SSL_CTX or SSL object - * - * @param ctx - the SSL context point - * @param pkey - private key object point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); - -/** - * @brief These functions load the certification into the SSL_CTX or SSL object - * - * @param ctx - the SSL context point - * @param pkey - certification object point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); - -/** - * @brief create the target SSL context client method - * - * @param none - * - * @return the SSLV2.3 version SSL context client method - */ -const SSL_METHOD* SSLv23_client_method(void); - -/** - * @brief create the target SSL context client method - * - * @param none - * - * @return the TLSV1.0 version SSL context client method - */ -const SSL_METHOD* TLSv1_client_method(void); - -/** - * @brief create the target SSL context client method - * - * @param none - * - * @return the SSLV1.0 version SSL context client method - */ -const SSL_METHOD* SSLv3_client_method(void); - -/** - * @brief create the target SSL context client method - * - * @param none - * - * @return the TLSV1.1 version SSL context client method - */ -const SSL_METHOD* TLSv1_1_client_method(void); - -/** - * @brief create the target SSL context client method - * - * @param none - * - * @return the TLSV1.2 version SSL context client method - */ -const SSL_METHOD* TLSv1_2_client_method(void); - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the TLS any version SSL context client method - */ -const SSL_METHOD* TLS_client_method(void); - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the SSLV2.3 version SSL context server method - */ -const SSL_METHOD* SSLv23_server_method(void); - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the TLSV1.1 version SSL context server method - */ -const SSL_METHOD* TLSv1_1_server_method(void); - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the TLSV1.2 version SSL context server method - */ -const SSL_METHOD* TLSv1_2_server_method(void); - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the TLSV1.0 version SSL context server method - */ -const SSL_METHOD* TLSv1_server_method(void); - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the SSLV3.0 version SSL context server method - */ -const SSL_METHOD* SSLv3_server_method(void); - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the TLS any version SSL context server method - */ -const SSL_METHOD* TLS_server_method(void); - - -/** - * @brief set the SSL context ALPN select callback function - * - * @param ctx - SSL context point - * @param cb - ALPN select callback function - * @param arg - ALPN select callback function entry private data point - * - * @return none - */ -void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, - void *arg); - -void SSL_set_alpn_select_cb(SSL *ssl, void *arg); - -/** - * @brief set the SSL context ALPN select protocol - * - * @param ctx - SSL context point - * @param protos - ALPN protocol name - * @param protos_len - ALPN protocol name bytes - * - * @return result - * 0 : OK - * 1 : failed - */ -int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char *protos, unsigned int protos_len); - -/** - * @brief set the SSL context next ALPN select callback function - * - * @param ctx - SSL context point - * @param cb - ALPN select callback function - * @param arg - ALPN select callback function entry private data point - * - * @return none - */ -void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, - int (*cb) (SSL *ssl, - unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen, - void *arg), - void *arg); - -void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, - unsigned int *len); - -void _ssl_set_alpn_list(const SSL *ssl); - -/** - * @brief get SSL error code - * - * @param ssl - SSL point - * @param ret_code - SSL return code - * - * @return SSL error number - */ -int SSL_get_error(const SSL *ssl, int ret_code); - -/** - * @brief clear the SSL error code - * - * @param none - * - * @return none - */ -void ERR_clear_error(void); - -/** - * @brief get the current SSL error code - * - * @param none - * - * @return current SSL error number - */ -int ERR_get_error(void); - -/** - * @brief register the SSL error strings - * - * @param none - * - * @return none - */ -void ERR_load_SSL_strings(void); - -/** - * @brief initialize the SSL library - * - * @param none - * - * @return none - */ -void SSL_library_init(void); - -/** - * @brief generates a human-readable string representing the error code e - * and store it into the "ret" point memory - * - * @param e - error code - * @param ret - memory point to store the string - * - * @return the result string point - */ -char *ERR_error_string(unsigned long e, char *ret); - -/** - * @brief add the SSL context option - * - * @param ctx - SSL context point - * @param opt - new SSL context option - * - * @return the SSL context option - */ -unsigned long SSL_CTX_set_options(SSL_CTX *ctx, unsigned long opt); - -/** - * @brief add the SSL context mode - * - * @param ctx - SSL context point - * @param mod - new SSL context mod - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_set_mode(SSL_CTX *ctx, int mod); - -/* -} -*/ - -/** - * @brief perform the SSL handshake - * - * @param ssl - SSL point - * - * @return result - * 1 : OK - * 0 : failed - * -1 : a error catch - */ -int SSL_do_handshake(SSL *ssl); - -/** - * @brief get the SSL current version - * - * @param ssl - SSL point - * - * @return the version string - */ -const char *SSL_get_version(const SSL *ssl); - -/** - * @brief set the SSL context version - * - * @param ctx - SSL context point - * @param meth - SSL method point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); - -/** - * @brief get the bytes numbers which are to be read - * - * @param ssl - SSL point - * - * @return bytes number - */ -int SSL_pending(const SSL *ssl); - -/** - * @brief check if SSL want nothing - * - * @param ssl - SSL point - * - * @return result - * 0 : false - * 1 : true - */ -int SSL_want_nothing(const SSL *ssl); - -/** - * @brief check if SSL want to read - * - * @param ssl - SSL point - * - * @return result - * 0 : false - * 1 : true - */ -int SSL_want_read(const SSL *ssl); - -/** - * @brief check if SSL want to write - * - * @param ssl - SSL point - * - * @return result - * 0 : false - * 1 : true - */ -int SSL_want_write(const SSL *ssl); - -/** - * @brief get the SSL context current method - * - * @param ctx - SSL context point - * - * @return the SSL context current method - */ -const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx); - -/** - * @brief get the SSL current method - * - * @param ssl - SSL point - * - * @return the SSL current method - */ -const SSL_METHOD *SSL_get_ssl_method(SSL *ssl); - -/** - * @brief set the SSL method - * - * @param ssl - SSL point - * @param meth - SSL method point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method); - -/** - * @brief add CA client certification into the SSL - * - * @param ssl - SSL point - * @param x - CA certification point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_add_client_CA(SSL *ssl, X509 *x); - -/** - * @brief add CA client certification into the SSL context - * - * @param ctx - SSL context point - * @param x - CA certification point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); - -/** - * @brief set the SSL CA certification list - * - * @param ssl - SSL point - * @param name_list - CA certification list - * - * @return none - */ -void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list); - -/** - * @brief set the SSL context CA certification list - * - * @param ctx - SSL context point - * @param name_list - CA certification list - * - * @return none - */ -void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list); - -/** - * @briefget the SSL CA certification list - * - * @param ssl - SSL point - * - * @return CA certification list - */ -STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl); - -/** - * @brief get the SSL context CA certification list - * - * @param ctx - SSL context point - * - * @return CA certification list - */ -STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx); - -/** - * @brief get the SSL certification point - * - * @param ssl - SSL point - * - * @return SSL certification point - */ -X509 *SSL_get_certificate(const SSL *ssl); - -/** - * @brief get the SSL private key point - * - * @param ssl - SSL point - * - * @return SSL private key point - */ -EVP_PKEY *SSL_get_privatekey(const SSL *ssl); - -/** - * @brief set the SSL information callback function - * - * @param ssl - SSL point - * @param cb - information callback function - * - * @return none - */ -void SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int val)); - -/** - * @brief get the SSL state - * - * @param ssl - SSL point - * - * @return SSL state - */ -OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); - -/** - * @brief set the SSL context read buffer length - * - * @param ctx - SSL context point - * @param len - read buffer length - * - * @return none - */ -void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); - -/** - * @brief set the SSL read buffer length - * - * @param ssl - SSL point - * @param len - read buffer length - * - * @return none - */ -void SSL_set_default_read_buffer_len(SSL *ssl, size_t len); - -/** - * @brief set the SSL security level - * - * @param ssl - SSL point - * @param level - security level - * - * @return none - */ -void SSL_set_security_level(SSL *ssl, int level); - -/** - * @brief get the SSL security level - * - * @param ssl - SSL point - * - * @return security level - */ -int SSL_get_security_level(const SSL *ssl); - -/** - * @brief get the SSL verifying mode of the SSL context - * - * @param ctx - SSL context point - * - * @return verifying mode - */ -int SSL_CTX_get_verify_mode(const SSL_CTX *ctx); - -/** - * @brief get the SSL verifying depth of the SSL context - * - * @param ctx - SSL context point - * - * @return verifying depth - */ -int SSL_CTX_get_verify_depth(const SSL_CTX *ctx); - -/** - * @brief set the SSL context verifying of the SSL context - * - * @param ctx - SSL context point - * @param mode - verifying mode - * @param verify_callback - verifying callback function - * - * @return none - */ -void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); - -/** - * @brief set the SSL verifying of the SSL context - * - * @param ctx - SSL point - * @param mode - verifying mode - * @param verify_callback - verifying callback function - * - * @return none - */ -void SSL_set_verify(SSL *s, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); - -/** - * @brief set the SSL verify depth of the SSL context - * - * @param ctx - SSL context point - * @param depth - verifying depth - * - * @return none - */ -void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth); - -/** - * @brief certification verifying callback function - * - * @param preverify_ok - verifying result - * @param x509_ctx - X509 certification point - * - * @return verifying result - */ -int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx); - -/** - * @brief set the session timeout time - * - * @param ctx - SSL context point - * @param t - new session timeout time - * - * @return old session timeout time - */ -long SSL_CTX_set_timeout(SSL_CTX *ctx, long t); - -/** - * @brief get the session timeout time - * - * @param ctx - SSL context point - * - * @return current session timeout time - */ -long SSL_CTX_get_timeout(const SSL_CTX *ctx); - -/** - * @brief set the SSL context cipher through the list string - * - * @param ctx - SSL context point - * @param str - cipher controller list string - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str); - -/** - * @brief set the SSL cipher through the list string - * - * @param ssl - SSL point - * @param str - cipher controller list string - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_set_cipher_list(SSL *ssl, const char *str); - -/** - * @brief get the SSL cipher list string - * - * @param ssl - SSL point - * - * @return cipher controller list string - */ -const char *SSL_get_cipher_list(const SSL *ssl, int n); - -/** - * @brief get the SSL cipher - * - * @param ssl - SSL point - * - * @return current cipher - */ -const SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl); - -/** - * @brief get the SSL cipher string - * - * @param ssl - SSL point - * - * @return cipher string - */ -const char *SSL_get_cipher(const SSL *ssl); - -/** - * @brief get the SSL context object X509 certification storage - * - * @param ctx - SSL context point - * - * @return x509 certification storage - */ -X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *ctx); - -/** - * @brief set the SSL context object X509 certification store - * - * @param ctx - SSL context point - * @param store - X509 certification store - * - * @return none - */ -void SSL_CTX_set_cert_store(SSL_CTX *ctx, X509_STORE *store); - -/** - * @brief get the SSL specifical statement - * - * @param ssl - SSL point - * - * @return specifical statement - */ -int SSL_want(const SSL *ssl); - -/** - * @brief check if the SSL is SSL_X509_LOOKUP state - * - * @param ssl - SSL point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_want_x509_lookup(const SSL *ssl); - -/** - * @brief reset the SSL - * - * @param ssl - SSL point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_clear(SSL *ssl); - -/** - * @brief get the socket handle of the SSL - * - * @param ssl - SSL point - * - * @return result - * >= 0 : yes, and return socket handle - * < 0 : a error catch - */ -int SSL_get_fd(const SSL *ssl); - -/** - * @brief get the read only socket handle of the SSL - * - * @param ssl - SSL point - * - * @return result - * >= 0 : yes, and return socket handle - * < 0 : a error catch - */ -int SSL_get_rfd(const SSL *ssl); - -/** - * @brief get the write only socket handle of the SSL - * - * @param ssl - SSL point - * - * @return result - * >= 0 : yes, and return socket handle - * < 0 : a error catch - */ -int SSL_get_wfd(const SSL *ssl); - -/** - * @brief set the SSL if we can read as many as data - * - * @param ssl - SSL point - * @param yes - enable the function - * - * @return none - */ -void SSL_set_read_ahead(SSL *s, int yes); - -/** - * @brief set the SSL context if we can read as many as data - * - * @param ctx - SSL context point - * @param yes - enbale the function - * - * @return none - */ -void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes); - -/** - * @brief get the SSL ahead signal if we can read as many as data - * - * @param ssl - SSL point - * - * @return SSL context ahead signal - */ -int SSL_get_read_ahead(const SSL *ssl); - -/** - * @brief get the SSL context ahead signal if we can read as many as data - * - * @param ctx - SSL context point - * - * @return SSL context ahead signal - */ -long SSL_CTX_get_read_ahead(SSL_CTX *ctx); - -/** - * @brief check if some data can be read - * - * @param ssl - SSL point - * - * @return - * 1 : there are bytes to be read - * 0 : no data - */ -int SSL_has_pending(const SSL *ssl); - -/** - * @brief load the X509 certification into SSL context - * - * @param ctx - SSL context point - * @param x - X509 certification point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x);//loads the certificate x into ctx - -/** - * @brief load the ASN1 certification into SSL context - * - * @param ctx - SSL context point - * @param len - certification length - * @param d - data point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); - -/** - * @brief load the certification file into SSL context - * - * @param ctx - SSL context point - * @param file - certification file name - * @param type - certification encoding type - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type); - -/** - * @brief load the certification chain file into SSL context - * - * @param ctx - SSL context point - * @param file - certification chain file name - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file); - - -/** - * @brief load the ASN1 private key into SSL context - * - * @param ctx - SSL context point - * @param d - data point - * @param len - private key length - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d, long len);//adds the private key of type pk stored at memory location d (length len) to ctx - -/** - * @brief load the private key file into SSL context - * - * @param ctx - SSL context point - * @param file - private key file name - * @param type - private key encoding type - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type); - -/** - * @brief load the RSA private key into SSL context - * - * @param ctx - SSL context point - * @param x - RSA private key point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa); - -/** - * @brief load the RSA ASN1 private key into SSL context - * - * @param ctx - SSL context point - * @param d - data point - * @param len - RSA private key length - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); - -/** - * @brief load the RSA private key file into SSL context - * - * @param ctx - SSL context point - * @param file - RSA private key file name - * @param type - private key encoding type - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type); - - -/** - * @brief check if the private key and certification is matched - * - * @param ctx - SSL context point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_check_private_key(const SSL_CTX *ctx); - -/** - * @brief set the SSL context server information - * - * @param ctx - SSL context point - * @param serverinfo - server information string - * @param serverinfo_length - server information length - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, size_t serverinfo_length); - -/** - * @brief load the SSL context server infomation file into SSL context - * - * @param ctx - SSL context point - * @param file - server information file - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file); - -/** - * @brief SSL select next function - * - * @param out - point of output data point - * @param outlen - output data length - * @param in - input data - * @param inlen - input data length - * @param client - client data point - * @param client_len -client data length - * - * @return NPN state - * OPENSSL_NPN_UNSUPPORTED : not support - * OPENSSL_NPN_NEGOTIATED : negotiated - * OPENSSL_NPN_NO_OVERLAP : no overlap - */ -int SSL_select_next_proto(unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, - const unsigned char *client, unsigned int client_len); - -/** - * @brief load the extra certification chain into the SSL context - * - * @param ctx - SSL context point - * @param x509 - X509 certification - * - * @return result - * 1 : OK - * 0 : failed - */ -long SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *); - -/** - * @brief control the SSL context - * - * @param ctx - SSL context point - * @param cmd - command - * @param larg - parameter length - * @param parg - parameter point - * - * @return result - * 1 : OK - * 0 : failed - */ -long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, char *parg); - -/** - * @brief get the SSL context cipher - * - * @param ctx - SSL context point - * - * @return SSL context cipher - */ -STACK *SSL_CTX_get_ciphers(const SSL_CTX *ctx); - -/** - * @brief check if the SSL context can read as many as data - * - * @param ctx - SSL context point - * - * @return result - * 1 : OK - * 0 : failed - */ -long SSL_CTX_get_default_read_ahead(SSL_CTX *ctx); - -/** - * @brief get the SSL context extra data - * - * @param ctx - SSL context point - * @param idx - index - * - * @return data point - */ -void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx); - -/** - * @brief get the SSL context quiet shutdown option - * - * @param ctx - SSL context point - * - * @return quiet shutdown option - */ -int SSL_CTX_get_quiet_shutdown(const SSL_CTX *ctx); - -/** - * @brief load the SSL context CA file - * - * @param ctx - SSL context point - * @param CAfile - CA certification file - * @param CApath - CA certification file path - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath); - -/** - * @brief add SSL context reference count by '1' - * - * @param ctx - SSL context point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_up_ref(SSL_CTX *ctx); - -/** - * @brief set SSL context application private data - * - * @param ctx - SSL context point - * @param arg - private data - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_set_app_data(SSL_CTX *ctx, void *arg); - -/** - * @brief set SSL context client certification callback function - * - * @param ctx - SSL context point - * @param cb - callback function - * - * @return none - */ -void SSL_CTX_set_client_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, X509 **x509, EVP_PKEY **pkey)); - -/** - * @brief set the SSL context if we can read as many as data - * - * @param ctx - SSL context point - * @param m - enable the fuction - * - * @return none - */ -void SSL_CTX_set_default_read_ahead(SSL_CTX *ctx, int m); - -/** - * @brief set SSL context default verifying path - * - * @param ctx - SSL context point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); - -/** - * @brief set SSL context default verifying directory - * - * @param ctx - SSL context point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx); - -/** - * @brief set SSL context default verifying file - * - * @param ctx - SSL context point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_set_default_verify_file(SSL_CTX *ctx); - -/** - * @brief set SSL context extra data - * - * @param ctx - SSL context point - * @param idx - data index - * @param arg - data point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_set_ex_data(SSL_CTX *s, int idx, char *arg); - -/** - * @brief clear the SSL context option bit of "op" - * - * @param ctx - SSL context point - * @param op - option - * - * @return SSL context option - */ -unsigned long SSL_CTX_clear_options(SSL_CTX *ctx, unsigned long op); - -/** - * @brief get the SSL context option - * - * @param ctx - SSL context point - * @param op - option - * - * @return SSL context option - */ -unsigned long SSL_CTX_get_options(SSL_CTX *ctx); - -/** - * @brief set the SSL context quiet shutdown mode - * - * @param ctx - SSL context point - * @param mode - mode - * - * @return none - */ -void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode); - -/** - * @brief get the SSL context X509 certification - * - * @param ctx - SSL context point - * - * @return X509 certification - */ -X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx); - -/** - * @brief get the SSL context private key - * - * @param ctx - SSL context point - * - * @return private key - */ -EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx); - -/** - * @brief set SSL context PSK identity hint - * - * @param ctx - SSL context point - * @param hint - PSK identity hint - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *hint); - -/** - * @brief set SSL context PSK server callback function - * - * @param ctx - SSL context point - * @param callback - callback function - * - * @return none - */ -void SSL_CTX_set_psk_server_callback(SSL_CTX *ctx, - unsigned int (*callback)(SSL *ssl, - const char *identity, - unsigned char *psk, - int max_psk_len)); -/** - * @brief get alert description string - * - * @param value - alert value - * - * @return alert description string - */ -const char *SSL_alert_desc_string(int value); - -/** - * @brief get alert description long string - * - * @param value - alert value - * - * @return alert description long string - */ -const char *SSL_alert_desc_string_long(int value); - -/** - * @brief get alert type string - * - * @param value - alert value - * - * @return alert type string - */ -const char *SSL_alert_type_string(int value); - -/** - * @brief get alert type long string - * - * @param value - alert value - * - * @return alert type long string - */ -const char *SSL_alert_type_string_long(int value); - -/** - * @brief get SSL context of the SSL - * - * @param ssl - SSL point - * - * @return SSL context - */ -SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); - -/** - * @brief get SSL application data - * - * @param ssl - SSL point - * - * @return application data - */ -char *SSL_get_app_data(SSL *ssl); - -/** - * @brief get SSL cipher bits - * - * @param ssl - SSL point - * @param alg_bits - algorithm bits - * - * @return strength bits - */ -int SSL_get_cipher_bits(const SSL *ssl, int *alg_bits); - -/** - * @brief get SSL cipher name - * - * @param ssl - SSL point - * - * @return SSL cipher name - */ -char *SSL_get_cipher_name(const SSL *ssl); - -/** - * @brief get SSL cipher version - * - * @param ssl - SSL point - * - * @return SSL cipher version - */ -char *SSL_get_cipher_version(const SSL *ssl); - -/** - * @brief get SSL extra data - * - * @param ssl - SSL point - * @param idx - data index - * - * @return extra data - */ -char *SSL_get_ex_data(const SSL *ssl, int idx); - -/** - * @brief get index of the SSL extra data X509 storage context - * - * @param none - * - * @return data index - */ -int SSL_get_ex_data_X509_STORE_CTX_idx(void); - -/** - * @brief get peer certification chain - * - * @param ssl - SSL point - * - * @return certification chain - */ -STACK *SSL_get_peer_cert_chain(const SSL *ssl); - -/** - * @brief get peer certification - * - * @param ssl - SSL point - * - * @return certification - */ -X509 *SSL_get_peer_certificate(const SSL *ssl); - -/** - * @brief get SSL quiet shutdown mode - * - * @param ssl - SSL point - * - * @return quiet shutdown mode - */ -int SSL_get_quiet_shutdown(const SSL *ssl); - -/** - * @brief get SSL read only IO handle - * - * @param ssl - SSL point - * - * @return IO handle - */ -BIO *SSL_get_rbio(const SSL *ssl); - -/** - * @brief get SSL shared ciphers - * - * @param ssl - SSL point - * @param buf - buffer to store the ciphers - * @param len - buffer len - * - * @return shared ciphers - */ -char *SSL_get_shared_ciphers(const SSL *ssl, char *buf, int len); - -/** - * @brief get SSL shutdown mode - * - * @param ssl - SSL point - * - * @return shutdown mode - */ -int SSL_get_shutdown(const SSL *ssl); - -/** - * @brief get SSL session time - * - * @param ssl - SSL point - * - * @return session time - */ -long SSL_get_time(const SSL *ssl); - -/** - * @brief get SSL session timeout time - * - * @param ssl - SSL point - * - * @return session timeout time - */ -long SSL_get_timeout(const SSL *ssl); - -/** - * @brief get SSL verifying mode - * - * @param ssl - SSL point - * - * @return verifying mode - */ -int SSL_get_verify_mode(const SSL *ssl); - -/** - * @brief get SSL verify parameters - * - * @param ssl - SSL point - * - * @return verify parameters - */ -X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl); - -/** - * @brief set expected hostname the peer cert CN should have - * - * @param param - verify parameters from SSL_get0_param() - * - * @param name - the expected hostname - * - * @param namelen - the length of the hostname, or 0 if NUL terminated - * - * @return verify parameters - */ -int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, - const char *name, size_t namelen); - -/** - * @brief set parameters for X509 host verify action - * - * @param param -verify parameters from SSL_get0_param() - * - * @param flags - bitfield of X509_CHECK_FLAG_... parameters to set - * - * @return 1 for success, 0 for failure - */ -int X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param, - unsigned long flags); - -/** - * @brief clear parameters for X509 host verify action - * - * @param param -verify parameters from SSL_get0_param() - * - * @param flags - bitfield of X509_CHECK_FLAG_... parameters to clear - * - * @return 1 for success, 0 for failure - */ -int X509_VERIFY_PARAM_clear_hostflags(X509_VERIFY_PARAM *param, - unsigned long flags); - -/** - * @brief get SSL write only IO handle - * - * @param ssl - SSL point - * - * @return IO handle - */ -BIO *SSL_get_wbio(const SSL *ssl); - -/** - * @brief load SSL client CA certification file - * - * @param file - file name - * - * @return certification loading object - */ -STACK *SSL_load_client_CA_file(const char *file); - -/** - * @brief add SSL reference by '1' - * - * @param ssl - SSL point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_up_ref(SSL *ssl); - -/** - * @brief read and put data into buf, but not clear the SSL low-level storage - * - * @param ssl - SSL point - * @param buf - storage buffer point - * @param num - data bytes - * - * @return result - * > 0 : OK, and return read bytes - * = 0 : connect is closed - * < 0 : a error catch - */ -int SSL_peek(SSL *ssl, void *buf, int num); - -/** - * @brief make SSL renegotiate - * - * @param ssl - SSL point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_renegotiate(SSL *ssl); - -/** - * @brief get the state string where SSL is reading - * - * @param ssl - SSL point - * - * @return state string - */ -const char *SSL_rstate_string(SSL *ssl); - -/** - * @brief get the statement long string where SSL is reading - * - * @param ssl - SSL point - * - * @return statement long string - */ -const char *SSL_rstate_string_long(SSL *ssl); - -/** - * @brief set SSL accept statement - * - * @param ssl - SSL point - * - * @return none - */ -void SSL_set_accept_state(SSL *ssl); - -/** - * @brief set SSL application data - * - * @param ssl - SSL point - * @param arg - SSL application data point - * - * @return none - */ -void SSL_set_app_data(SSL *ssl, char *arg); - -/** - * @brief set SSL BIO - * - * @param ssl - SSL point - * @param rbio - read only IO - * @param wbio - write only IO - * - * @return none - */ -void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio); - -/** - * @brief clear SSL option - * - * @param ssl - SSL point - * @param op - clear option - * - * @return SSL option - */ -unsigned long SSL_clear_options(SSL *ssl, unsigned long op); - -/** - * @brief get SSL option - * - * @param ssl - SSL point - * - * @return SSL option - */ -unsigned long SSL_get_options(SSL *ssl); - -/** - * @brief clear SSL option - * - * @param ssl - SSL point - * @param op - setting option - * - * @return SSL option - */ -unsigned long SSL_set_options(SSL *ssl, unsigned long op); - -/** - * @brief set SSL quiet shutdown mode - * - * @param ssl - SSL point - * @param mode - quiet shutdown mode - * - * @return none - */ -void SSL_set_quiet_shutdown(SSL *ssl, int mode); - -/** - * @brief set SSL shutdown mode - * - * @param ssl - SSL point - * @param mode - shutdown mode - * - * @return none - */ -void SSL_set_shutdown(SSL *ssl, int mode); - -/** - * @brief set SSL session time - * - * @param ssl - SSL point - * @param t - session time - * - * @return session time - */ -long SSL_set_time(SSL *ssl, long t); - -/** - * @brief set SSL session timeout time - * - * @param ssl - SSL point - * @param t - session timeout time - * - * @return session timeout time - */ -long SSL_set_timeout(SSL *ssl, long t); - -/** - * @brief get SSL statement string - * - * @param ssl - SSL point - * - * @return SSL statement string - */ -char *SSL_state_string(const SSL *ssl); - -/** - * @brief get SSL statement long string - * - * @param ssl - SSL point - * - * @return SSL statement long string - */ -char *SSL_state_string_long(const SSL *ssl); - -/** - * @brief get SSL renegotiation count - * - * @param ssl - SSL point - * - * @return renegotiation count - */ -long SSL_total_renegotiations(SSL *ssl); - -/** - * @brief get SSL version - * - * @param ssl - SSL point - * - * @return SSL version - */ -int SSL_version(const SSL *ssl); - -/** - * @brief set SSL PSK identity hint - * - * @param ssl - SSL point - * @param hint - identity hint - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_use_psk_identity_hint(SSL *ssl, const char *hint); - -/** - * @brief get SSL PSK identity hint - * - * @param ssl - SSL point - * - * @return identity hint - */ -const char *SSL_get_psk_identity_hint(SSL *ssl); - -/** - * @brief get SSL PSK identity - * - * @param ssl - SSL point - * - * @return identity - */ -const char *SSL_get_psk_identity(SSL *ssl); - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/platform/ssl_pm.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/platform/ssl_pm.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/platform/ssl_pm.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/platform/ssl_pm.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_PM_H_ -#define _SSL_PM_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#include -#include "ssl_types.h" -#include "ssl_port.h" - -#define LOCAL_ATRR - -int ssl_pm_new(SSL *ssl); -void ssl_pm_free(SSL *ssl); - -int ssl_pm_handshake(SSL *ssl); -int ssl_pm_shutdown(SSL *ssl); -int ssl_pm_clear(SSL *ssl); - -int ssl_pm_read(SSL *ssl, void *buffer, int len); -int ssl_pm_send(SSL *ssl, const void *buffer, int len); -int ssl_pm_pending(const SSL *ssl); - -void ssl_pm_set_fd(SSL *ssl, int fd, int mode); -int ssl_pm_get_fd(const SSL *ssl, int mode); - -OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl); - -void ssl_pm_set_bufflen(SSL *ssl, int len); - -int x509_pm_show_info(X509 *x); -int x509_pm_new(X509 *x, X509 *m_x); -void x509_pm_free(X509 *x); -int x509_pm_load(X509 *x, const unsigned char *buffer, int len); - -int pkey_pm_new(EVP_PKEY *pk, EVP_PKEY *m_pk); -void pkey_pm_free(EVP_PKEY *pk); -int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len); - -long ssl_pm_get_verify_result(const SSL *ssl); - -#ifdef __cplusplus - } -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/platform/ssl_port.h libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/platform/ssl_port.h --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/include/platform/ssl_port.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/include/platform/ssl_port.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_PORT_H_ -#define _SSL_PORT_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#include "string.h" -#include "stdlib.h" -#if defined(LWS_HAVE_MALLOC_H) -#include "malloc.h" -#endif - -void *ssl_mem_zalloc(size_t size); - -#define ssl_mem_malloc malloc -#define ssl_mem_free free - -#define ssl_memcpy memcpy -#define ssl_strlen strlen - -#define ssl_speed_up_enter() -#define ssl_speed_up_exit() - -#define SSL_DEBUG_FL -#define SSL_DEBUG_LOG(fmt, ...) ESP_LOGI("openssl", fmt, ##__VA_ARGS__) - -#ifdef __cplusplus - } -#endif - -#endif diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_cert.c libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_cert.c --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_cert.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_cert.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ssl_cert.h" -#include "ssl_pkey.h" -#include "ssl_x509.h" -#include "ssl_dbg.h" -#include "ssl_port.h" - -/** - * @brief create a certification object according to input certification - */ -CERT *__ssl_cert_new(CERT *ic) -{ - CERT *cert; - - X509 *ix; - EVP_PKEY *ipk; - - cert = ssl_mem_zalloc(sizeof(CERT)); - if (!cert) { - SSL_DEBUG(SSL_CERT_ERROR_LEVEL, "no enough memory > (cert)"); - goto no_mem; - } - - if (ic) { - ipk = ic->pkey; - ix = ic->x509; - } else { - ipk = NULL; - ix = NULL; - } - - cert->pkey = __EVP_PKEY_new(ipk); - if (!cert->pkey) { - SSL_DEBUG(SSL_CERT_ERROR_LEVEL, "__EVP_PKEY_new() return NULL"); - goto pkey_err; - } - - cert->x509 = __X509_new(ix); - if (!cert->x509) { - SSL_DEBUG(SSL_CERT_ERROR_LEVEL, "__X509_new() return NULL"); - goto x509_err; - } - - return cert; - -x509_err: - EVP_PKEY_free(cert->pkey); -pkey_err: - ssl_mem_free(cert); -no_mem: - return NULL; -} - -/** - * @brief create a certification object include private key object - */ -CERT *ssl_cert_new(void) -{ - return __ssl_cert_new(NULL); -} - -/** - * @brief free a certification object - */ -void ssl_cert_free(CERT *cert) -{ - SSL_ASSERT3(cert); - - X509_free(cert->x509); - - EVP_PKEY_free(cert->pkey); - - ssl_mem_free(cert); -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_lib.c libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_lib.c --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_lib.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_lib.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1221 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ssl_lib.h" -#include "ssl_pkey.h" -#include "ssl_x509.h" -#include "ssl_cert.h" -#include "ssl_dbg.h" -#include "ssl_port.h" - -#include "private-lib-core.h" - -char * -lws_strncpy(char *dest, const char *src, size_t size); - -#define SSL_SEND_DATA_MAX_LENGTH 1460 - -/** - * @brief create a new SSL session object - */ -static SSL_SESSION* SSL_SESSION_new(void) -{ - SSL_SESSION *session; - - session = ssl_mem_zalloc(sizeof(SSL_SESSION)); - if (!session) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "no enough memory > (session)"); - goto failed1; - } - - session->peer = X509_new(); - if (!session->peer) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "X509_new() return NULL"); - goto failed2; - } - - return session; - -failed2: - ssl_mem_free(session); -failed1: - return NULL; -} - -/** - * @brief free a new SSL session object - */ -static void SSL_SESSION_free(SSL_SESSION *session) -{ - X509_free(session->peer); - ssl_mem_free(session); -} - -/** - * @brief Discover whether the current connection is in the error state - */ -int ossl_statem_in_error(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - if (ssl->statem.state == MSG_FLOW_ERROR) - return 1; - - return 0; -} - -/** - * @brief get the SSL specifical statement - */ -int SSL_want(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - return ssl->rwstate; -} - -/** - * @brief check if SSL want nothing - */ -int SSL_want_nothing(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - if (ssl->err) - return 1; - - return (SSL_want(ssl) == SSL_NOTHING); -} - -/** - * @brief check if SSL want to read - */ -int SSL_want_read(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - if (ssl->err) - return 0; - - return (SSL_want(ssl) == SSL_READING); -} - -/** - * @brief check if SSL want to write - */ -int SSL_want_write(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - if (ssl->err) - return 0; - - return (SSL_want(ssl) == SSL_WRITING); -} - -/** - * @brief check if SSL want to lookup X509 certification - */ -int SSL_want_x509_lookup(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - return (SSL_want(ssl) == SSL_WRITING); -} - -/** - * @brief get SSL error code - */ -int SSL_get_error(const SSL *ssl, int ret_code) -{ - int ret = SSL_ERROR_SYSCALL; - - SSL_ASSERT1(ssl); - - if (ret_code > 0) - ret = SSL_ERROR_NONE; - else if (ret_code < 0) - { - if (ssl->err == SSL_ERROR_WANT_READ || SSL_want_read(ssl)) - ret = SSL_ERROR_WANT_READ; - else if (ssl->err == SSL_ERROR_WANT_WRITE || SSL_want_write(ssl)) - ret = SSL_ERROR_WANT_WRITE; - else - ret = SSL_ERROR_SYSCALL; //unknown - } - else // ret_code == 0 - { - if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) - ret = SSL_ERROR_ZERO_RETURN; - else - ret = SSL_ERROR_SYSCALL; - } - - return ret; -} - -/** - * @brief get the SSL state - */ -OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl) -{ - OSSL_HANDSHAKE_STATE state; - - SSL_ASSERT1(ssl); - - state = SSL_METHOD_CALL(get_state, ssl); - - return state; -} - -/** - * @brief create a SSL context - */ -SSL_CTX* SSL_CTX_new(const SSL_METHOD *method) -{ - SSL_CTX *ctx; - CERT *cert; - X509 *client_ca; - - if (!method) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "no no_method"); - return NULL; - } - - client_ca = X509_new(); - if (!client_ca) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "X509_new() return NULL"); - goto failed1; - } - - cert = ssl_cert_new(); - if (!cert) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "ssl_cert_new() return NULL"); - goto failed2; - } - - ctx = (SSL_CTX *)ssl_mem_zalloc(sizeof(SSL_CTX)); - if (!ctx) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "no enough memory > (ctx)"); - goto failed3; - } - - ctx->method = method; - ctx->client_CA = client_ca; - ctx->cert = cert; - - ctx->version = method->version; - - return ctx; - -failed3: - ssl_cert_free(cert); -failed2: - X509_free(client_ca); -failed1: - return NULL; -} - -/** - * @brief free a SSL context - */ -void SSL_CTX_free(SSL_CTX* ctx) -{ - SSL_ASSERT3(ctx); - - ssl_cert_free(ctx->cert); - - X509_free(ctx->client_CA); - - if (ctx->alpn_protos) - ssl_mem_free(ctx->alpn_protos); - - ssl_mem_free(ctx); -} - -/** - * @brief set the SSL context version - */ -int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth) -{ - SSL_ASSERT1(ctx); - SSL_ASSERT1(meth); - - ctx->method = meth; - - ctx->version = meth->version; - - return 1; -} - -/** - * @brief get the SSL context current method - */ -const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx) -{ - SSL_ASSERT2(ctx); - - return ctx->method; -} - -/** - * @brief create a SSL - */ -SSL *SSL_new(SSL_CTX *ctx) -{ - int ret = 0; - SSL *ssl; - - if (!ctx) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "no ctx"); - return NULL; - } - - ssl = (SSL *)ssl_mem_zalloc(sizeof(SSL)); - if (!ssl) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "no enough memory > (ssl)"); - goto failed1; - } - - ssl->session = SSL_SESSION_new(); - if (!ssl->session) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_SESSION_new() return NULL"); - goto failed2; - } - - ssl->cert = __ssl_cert_new(ctx->cert); - if (!ssl->cert) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "__ssl_cert_new() return NULL"); - goto failed3; - } - - ssl->client_CA = __X509_new(ctx->client_CA); - if (!ssl->client_CA) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "__X509_new() return NULL"); - goto failed4; - } - - ssl->ctx = ctx; - ssl->method = ctx->method; - - ssl->version = ctx->version; - ssl->options = ctx->options; - - ssl->verify_mode = ctx->verify_mode; - - ret = SSL_METHOD_CALL(new, ssl); - if (ret) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_METHOD_CALL(new) return %d", ret); - goto failed5; - } - - _ssl_set_alpn_list(ssl); - - ssl->rwstate = SSL_NOTHING; - - return ssl; - -failed5: - X509_free(ssl->client_CA); -failed4: - ssl_cert_free(ssl->cert); -failed3: - SSL_SESSION_free(ssl->session); -failed2: - ssl_mem_free(ssl); -failed1: - return NULL; -} - -/** - * @brief free the SSL - */ -void SSL_free(SSL *ssl) -{ - SSL_ASSERT3(ssl); - - SSL_METHOD_CALL(free, ssl); - - X509_free(ssl->client_CA); - - ssl_cert_free(ssl->cert); - - SSL_SESSION_free(ssl->session); - - if (ssl->alpn_protos) - ssl_mem_free(ssl->alpn_protos); - - ssl_mem_free(ssl); -} - -/** - * @brief perform the SSL handshake - */ -int SSL_do_handshake(SSL *ssl) -{ - int ret; - - SSL_ASSERT1(ssl); - - ret = SSL_METHOD_CALL(handshake, ssl); - - return ret; -} - -/** - * @brief connect to the remote SSL server - */ -int SSL_connect(SSL *ssl) -{ - SSL_ASSERT1(ssl); - - return SSL_do_handshake(ssl); -} - -/** - * @brief accept the remote connection - */ -int SSL_accept(SSL *ssl) -{ - SSL_ASSERT1(ssl); - - return SSL_do_handshake(ssl); -} - -/** - * @brief shutdown the connection - */ -int SSL_shutdown(SSL *ssl) -{ - int ret; - - SSL_ASSERT1(ssl); - - if (SSL_get_state(ssl) != TLS_ST_OK) return 1; - - ret = SSL_METHOD_CALL(shutdown, ssl); - - return ret; -} - -/** - * @brief reset the SSL - */ -int SSL_clear(SSL *ssl) -{ - int ret; - - SSL_ASSERT1(ssl); - - ret = SSL_shutdown(ssl); - if (1 != ret) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_shutdown return %d", ret); - goto failed1; - } - - SSL_METHOD_CALL(free, ssl); - - ret = SSL_METHOD_CALL(new, ssl); - if (!ret) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_METHOD_CALL(new) return %d", ret); - goto failed1; - } - - return 1; - -failed1: - return ret; -} - -/** - * @brief read data from to remote - */ -int SSL_read(SSL *ssl, void *buffer, int len) -{ - int ret; - - SSL_ASSERT1(ssl); - SSL_ASSERT1(buffer); - SSL_ASSERT1(len); - - ssl->rwstate = SSL_READING; - - ret = SSL_METHOD_CALL(read, ssl, buffer, len); - - if (ret == len) - ssl->rwstate = SSL_NOTHING; - - return ret; -} - -/** - * @brief send the data to remote - */ -int SSL_write(SSL *ssl, const void *buffer, int len) -{ - int ret; - int send_bytes, bytes; - const unsigned char *pbuf; - - SSL_ASSERT1(ssl); - SSL_ASSERT1(buffer); - SSL_ASSERT1(len); - - ssl->rwstate = SSL_WRITING; - - send_bytes = len; - pbuf = (const unsigned char *)buffer; - - do { - if (send_bytes > SSL_SEND_DATA_MAX_LENGTH) - bytes = SSL_SEND_DATA_MAX_LENGTH; - else - bytes = send_bytes; - - if (ssl->interrupted_remaining_write) { - bytes = ssl->interrupted_remaining_write; - ssl->interrupted_remaining_write = 0; - } - - ret = SSL_METHOD_CALL(send, ssl, pbuf, bytes); - //printf("%s: ssl_pm said %d for %d requested (cum %d)\n", __func__, ret, bytes, len -send_bytes); - /* the return is a NEGATIVE OpenSSL error code, or the length sent */ - if (ret > 0) { - pbuf += ret; - send_bytes -= ret; - } else - ssl->interrupted_remaining_write = bytes; - } while (ret > 0 && send_bytes && ret == bytes); - - if (ret >= 0) { - ret = len - send_bytes; - if (!ret) - ssl->rwstate = SSL_NOTHING; - } else { - if (send_bytes == len) - ret = -1; - else - ret = len - send_bytes; - } - - return ret; -} - -/** - * @brief get SSL context of the SSL - */ -SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) -{ - SSL_ASSERT2(ssl); - - return ssl->ctx; -} - -/** - * @brief get the SSL current method - */ -const SSL_METHOD *SSL_get_ssl_method(SSL *ssl) -{ - SSL_ASSERT2(ssl); - - return ssl->method; -} - -/** - * @brief set the SSL method - */ -int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method) -{ - int ret; - - SSL_ASSERT1(ssl); - SSL_ASSERT1(method); - - if (ssl->version != method->version) { - - ret = SSL_shutdown(ssl); - if (1 != ret) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_shutdown return %d", ret); - goto failed1; - } - - SSL_METHOD_CALL(free, ssl); - - ssl->method = method; - - ret = SSL_METHOD_CALL(new, ssl); - if (!ret) { - SSL_DEBUG(SSL_LIB_ERROR_LEVEL, "SSL_METHOD_CALL(new) return %d", ret); - goto failed1; - } - } else { - ssl->method = method; - } - - - return 1; - -failed1: - return ret; -} - -/** - * @brief get SSL shutdown mode - */ -int SSL_get_shutdown(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - return ssl->shutdown; -} - -/** - * @brief set SSL shutdown mode - */ -void SSL_set_shutdown(SSL *ssl, int mode) -{ - SSL_ASSERT3(ssl); - - ssl->shutdown = mode; -} - - -/** - * @brief get the number of the bytes to be read - */ -int SSL_pending(const SSL *ssl) -{ - int ret; - - SSL_ASSERT1(ssl); - - ret = SSL_METHOD_CALL(pending, ssl); - - return ret; -} - -/** - * @brief check if some data can be read - */ -int SSL_has_pending(const SSL *ssl) -{ - int ret; - - SSL_ASSERT1(ssl); - - if (SSL_pending(ssl)) - ret = 1; - else - ret = 0; - - return ret; -} - -/** - * @brief clear the SSL context option bit of "op" - */ -unsigned long SSL_CTX_clear_options(SSL_CTX *ctx, unsigned long op) -{ - SSL_ASSERT1(ctx); - - return ctx->options &= ~op; -} - -/** - * @brief get the SSL context option - */ -unsigned long SSL_CTX_get_options(SSL_CTX *ctx) -{ - SSL_ASSERT1(ctx); - - return ctx->options; -} - -/** - * @brief set the option of the SSL context - */ -unsigned long SSL_CTX_set_options(SSL_CTX *ctx, unsigned long opt) -{ - SSL_ASSERT1(ctx); - - return ctx->options |= opt; -} - -/** - * @brief clear SSL option - */ -unsigned long SSL_clear_options(SSL *ssl, unsigned long op) -{ - SSL_ASSERT1(ssl); - - return ssl->options & ~op; -} - -/** - * @brief get SSL option - */ -unsigned long SSL_get_options(SSL *ssl) -{ - SSL_ASSERT1(ssl); - - return ssl->options; -} - -/** - * @brief clear SSL option - */ -unsigned long SSL_set_options(SSL *ssl, unsigned long op) -{ - SSL_ASSERT1(ssl); - - return ssl->options |= op; -} - -/** - * @brief get the socket handle of the SSL - */ -int SSL_get_fd(const SSL *ssl) -{ - int ret; - - SSL_ASSERT1(ssl); - - ret = SSL_METHOD_CALL(get_fd, ssl, 0); - - return ret; -} - -/** - * @brief get the read only socket handle of the SSL - */ -int SSL_get_rfd(const SSL *ssl) -{ - int ret; - - SSL_ASSERT1(ssl); - - ret = SSL_METHOD_CALL(get_fd, ssl, 0); - - return ret; -} - -/** - * @brief get the write only socket handle of the SSL - */ -int SSL_get_wfd(const SSL *ssl) -{ - int ret; - - SSL_ASSERT1(ssl); - - ret = SSL_METHOD_CALL(get_fd, ssl, 0); - - return ret; -} - -/** - * @brief bind the socket file description into the SSL - */ -int SSL_set_fd(SSL *ssl, int fd) -{ - SSL_ASSERT1(ssl); - SSL_ASSERT1(fd >= 0); - - SSL_METHOD_CALL(set_fd, ssl, fd, 0); - - return 1; -} - -/** - * @brief bind the read only socket file description into the SSL - */ -int SSL_set_rfd(SSL *ssl, int fd) -{ - SSL_ASSERT1(ssl); - SSL_ASSERT1(fd >= 0); - - SSL_METHOD_CALL(set_fd, ssl, fd, 0); - - return 1; -} - -/** - * @brief bind the write only socket file description into the SSL - */ -int SSL_set_wfd(SSL *ssl, int fd) -{ - SSL_ASSERT1(ssl); - SSL_ASSERT1(fd >= 0); - - SSL_METHOD_CALL(set_fd, ssl, fd, 0); - - return 1; -} - -/** - * @brief get SSL version - */ -int SSL_version(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - return ssl->version; -} - -/** - * @brief get the SSL version string - */ -static const char* ssl_protocol_to_string(int version) -{ - const char *str; - - if (version == TLS1_2_VERSION) - str = "TLSv1.2"; - else if (version == TLS1_1_VERSION) - str = "TLSv1.1"; - else if (version == TLS1_VERSION) - str = "TLSv1"; - else if (version == SSL3_VERSION) - str = "SSLv3"; - else - str = "unknown"; - - return str; -} - -/** - * @brief get the SSL current version - */ -const char *SSL_get_version(const SSL *ssl) -{ - SSL_ASSERT2(ssl); - - return ssl_protocol_to_string(SSL_version(ssl)); -} - -/** - * @brief get alert type string - */ -const char *SSL_alert_type_string(int value) -{ - const char *str; - - switch (value >> 8) - { - case SSL3_AL_WARNING: - str = "W"; - break; - case SSL3_AL_FATAL: - str = "F"; - break; - default: - str = "U"; - break; - } - - return str; -} - -/** - * @brief set the SSL context read buffer length - */ -void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len) -{ - SSL_ASSERT3(ctx); - - ctx->read_buffer_len = len; -} - -/** - * @brief set the SSL read buffer length - */ -void SSL_set_default_read_buffer_len(SSL *ssl, size_t len) -{ - SSL_ASSERT3(ssl); - SSL_ASSERT3(len); - - SSL_METHOD_CALL(set_bufflen, ssl, len); -} - -/** - * @brief set the SSL information callback function - */ -void SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int val)) -{ - SSL_ASSERT3(ssl); - - ssl->info_callback = cb; -} - -/** - * @brief add SSL context reference count by '1' - */ -int SSL_CTX_up_ref(SSL_CTX *ctx) -{ - SSL_ASSERT1(ctx); - - /** - * no support multi-thread SSL here - */ - ctx->references++; - - return 1; -} - -/** - * @brief set the SSL security level - */ -void SSL_set_security_level(SSL *ssl, int level) -{ - SSL_ASSERT3(ssl); - - ssl->cert->sec_level = level; -} - -/** - * @brief get the SSL security level - */ -int SSL_get_security_level(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - return ssl->cert->sec_level; -} - -/** - * @brief get the SSL verifying mode of the SSL context - */ -int SSL_CTX_get_verify_mode(const SSL_CTX *ctx) -{ - SSL_ASSERT1(ctx); - - return ctx->verify_mode; -} - -/** - * @brief set the session timeout time - */ -long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) -{ - long l; - - SSL_ASSERT1(ctx); - - l = ctx->session_timeout; - ctx->session_timeout = t; - - return l; -} - -/** - * @brief get the session timeout time - */ -long SSL_CTX_get_timeout(const SSL_CTX *ctx) -{ - SSL_ASSERT1(ctx); - - return ctx->session_timeout; -} - -/** - * @brief set the SSL if we can read as many as data - */ -void SSL_set_read_ahead(SSL *ssl, int yes) -{ - SSL_ASSERT3(ssl); - - ssl->rlayer.read_ahead = yes; -} - -/** - * @brief set the SSL context if we can read as many as data - */ -void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) -{ - SSL_ASSERT3(ctx); - - ctx->read_ahead = yes; -} - -/** - * @brief get the SSL ahead signal if we can read as many as data - */ -int SSL_get_read_ahead(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - return ssl->rlayer.read_ahead; -} - -/** - * @brief get the SSL context ahead signal if we can read as many as data - */ -long SSL_CTX_get_read_ahead(SSL_CTX *ctx) -{ - SSL_ASSERT1(ctx); - - return ctx->read_ahead; -} - -/** - * @brief check if the SSL context can read as many as data - */ -long SSL_CTX_get_default_read_ahead(SSL_CTX *ctx) -{ - SSL_ASSERT1(ctx); - - return ctx->read_ahead; -} - -/** - * @brief set SSL session time - */ -long SSL_set_time(SSL *ssl, long t) -{ - SSL_ASSERT1(ssl); - - ssl->session->time = t; - - return t; -} - -/** - * @brief set SSL session timeout time - */ -long SSL_set_timeout(SSL *ssl, long t) -{ - SSL_ASSERT1(ssl); - - ssl->session->timeout = t; - - return t; -} - -/** - * @brief get the verifying result of the SSL certification - */ -long SSL_get_verify_result(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - return SSL_METHOD_CALL(get_verify_result, ssl); -} - -/** - * @brief get the SSL verifying depth of the SSL context - */ -int SSL_CTX_get_verify_depth(const SSL_CTX *ctx) -{ - SSL_ASSERT1(ctx); - - return ctx->param.depth; -} - -/** - * @brief set the SSL verify depth of the SSL context - */ -void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) -{ - SSL_ASSERT3(ctx); - - ctx->param.depth = depth; -} - -/** - * @brief get the SSL verifying depth of the SSL - */ -int SSL_get_verify_depth(const SSL *ssl) -{ - SSL_ASSERT1(ssl); - - return ssl->param.depth; -} - -/** - * @brief set the SSL verify depth of the SSL - */ -void SSL_set_verify_depth(SSL *ssl, int depth) -{ - SSL_ASSERT3(ssl); - - ssl->param.depth = depth; -} - -/** - * @brief set the SSL context verifying of the SSL context - */ -void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) -{ - SSL_ASSERT3(ctx); - - ctx->verify_mode = mode; - ctx->default_verify_callback = verify_callback; -} - -/** - * @brief set the SSL verifying of the SSL context - */ -void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) -{ - SSL_ASSERT3(ssl); - - ssl->verify_mode = mode; - ssl->verify_callback = verify_callback; -} - -void ERR_error_string_n(unsigned long e, char *buf, size_t len) -{ - lws_strncpy(buf, "unknown", len); -} - -void ERR_free_strings(void) -{ -} - -char *ERR_error_string(unsigned long e, char *buf) -{ - if (!buf) - return "unknown"; - - switch(e) { - case X509_V_ERR_INVALID_CA: - strcpy(buf, "CA is not trusted"); - break; - case X509_V_ERR_HOSTNAME_MISMATCH: - strcpy(buf, "Hostname mismatch"); - break; - case X509_V_ERR_CA_KEY_TOO_SMALL: - strcpy(buf, "CA key too small"); - break; - case X509_V_ERR_CA_MD_TOO_WEAK: - strcpy(buf, "MD key too weak"); - break; - case X509_V_ERR_CERT_NOT_YET_VALID: - strcpy(buf, "Cert from the future"); - break; - case X509_V_ERR_CERT_HAS_EXPIRED: - strcpy(buf, "Cert expired"); - break; - default: - strcpy(buf, "unknown"); - break; - } - - return buf; -} - -void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) -{ - return NULL; -} - -/* - * Openssl wants the valid protocol names supplied like this: - * - * (unsigned char *)"\x02h2\x08http/1.1", 6 + 9 - * - * Mbedtls wants this: - * - * Pointer to a NULL-terminated list of supported protocols, in decreasing - * preference order. The pointer to the list is recorded by the library for - * later reference as required, so the lifetime of the table must be at least - * as long as the lifetime of the SSL configuration structure. - * - * So accept the OpenSSL style and convert to mbedtls style - */ - - -static void -_openssl_alpn_to_mbedtls(struct alpn_ctx *ac, char ***palpn_protos) -{ - unsigned char *p = ac->data, *q; - unsigned char len; - char **alpn_protos; - int count = 0; - - /* find out how many entries he gave us */ - - if (ac->len) { - len = *p++; - if (len) - count++; - while (p - ac->data < ac->len) { - if (len--) { - p++; - continue; - } - len = *p++; - if (!len) - break; - count++; - } - } - - if (!count) - return; - - /* allocate space for count + 1 pointers and the data afterwards */ - - alpn_protos = ssl_mem_zalloc((count + 1) * sizeof(char *) + ac->len + 1); - if (!alpn_protos) - return; - - *palpn_protos = alpn_protos; - - /* convert to mbedtls format */ - - q = (unsigned char *)alpn_protos + (count + 1) * sizeof(char *); - p = ac->data; - count = 0; - - len = *p++; - alpn_protos[count] = (char *)q; - while (p - ac->data < ac->len) { - if (len--) { - *q++ = *p++; - continue; - } - *q++ = '\0'; - count++; - len = *p++; - alpn_protos[count] = (char *)q; - if (!len) - break; - } - if (!len) { - *q++ = '\0'; - count++; - /* len = *p++; */ - alpn_protos[count] = (char *)q; - } - alpn_protos[count] = NULL; /* last pointer ends list with NULL */ -} - -void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg) -{ - struct alpn_ctx *ac = arg; - - ctx->alpn_cb = cb; - - _openssl_alpn_to_mbedtls(ac, (char ***)&ctx->alpn_protos); -} - -void SSL_set_alpn_select_cb(SSL *ssl, void *arg) -{ - struct alpn_ctx *ac = arg; - - _openssl_alpn_to_mbedtls(ac, (char ***)&ssl->alpn_protos); - - _ssl_set_alpn_list(ssl); -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_methods.c libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_methods.c --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_methods.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_methods.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ssl_methods.h" -#include "ssl_pm.h" - -/** - * TLS method function collection - */ -IMPLEMENT_TLS_METHOD_FUNC(TLS_method_func, - ssl_pm_new, ssl_pm_free, - ssl_pm_handshake, ssl_pm_shutdown, ssl_pm_clear, - ssl_pm_read, ssl_pm_send, ssl_pm_pending, - ssl_pm_set_fd, ssl_pm_get_fd, - ssl_pm_set_bufflen, - ssl_pm_get_verify_result, - ssl_pm_get_state); - -/** - * TLS or SSL client method collection - */ -IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, 0, TLS_method_func, TLS_client_method); - -IMPLEMENT_TLS_METHOD(TLS1_2_VERSION, 0, TLS_method_func, TLSv1_2_client_method); - -#if 0 -IMPLEMENT_TLS_METHOD(TLS1_1_VERSION, 0, TLS_method_func, TLSv1_1_client_method); - -IMPLEMENT_TLS_METHOD(TLS1_VERSION, 0, TLS_method_func, TLSv1_client_method); - -IMPLEMENT_SSL_METHOD(SSL3_VERSION, 0, TLS_method_func, SSLv3_client_method); -#endif - -/** - * TLS or SSL server method collection - */ -IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, 1, TLS_method_func, TLS_server_method); - -IMPLEMENT_TLS_METHOD(TLS1_2_VERSION, 1, TLS_method_func, TLSv1_2_server_method); - -#if 0 -IMPLEMENT_TLS_METHOD(TLS1_1_VERSION, 1, TLS_method_func, TLSv1_1_server_method); - -IMPLEMENT_TLS_METHOD(TLS1_VERSION, 0, TLS_method_func, TLSv1_server_method); - -IMPLEMENT_SSL_METHOD(SSL3_VERSION, 1, TLS_method_func, SSLv3_server_method); -#endif - -/** - * TLS or SSL method collection - */ -IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, -1, TLS_method_func, TLS_method); - -IMPLEMENT_SSL_METHOD(TLS1_2_VERSION, -1, TLS_method_func, TLSv1_2_method); - -#if 0 -IMPLEMENT_SSL_METHOD(TLS1_1_VERSION, -1, TLS_method_func, TLSv1_1_method); - -IMPLEMENT_SSL_METHOD(TLS1_VERSION, -1, TLS_method_func, TLSv1_method); - -IMPLEMENT_SSL_METHOD(SSL3_VERSION, -1, TLS_method_func, SSLv3_method); -#endif - -/** - * @brief get X509 object method - */ -IMPLEMENT_X509_METHOD(X509_method, - x509_pm_new, x509_pm_free, - x509_pm_load, x509_pm_show_info); - -/** - * @brief get private key object method - */ -IMPLEMENT_PKEY_METHOD(EVP_PKEY_method, - pkey_pm_new, pkey_pm_free, - pkey_pm_load); diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_pkey.c libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_pkey.c --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_pkey.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_pkey.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,239 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ssl_pkey.h" -#include "ssl_methods.h" -#include "ssl_dbg.h" -#include "ssl_port.h" - -/** - * @brief create a private key object according to input private key - */ -EVP_PKEY* __EVP_PKEY_new(EVP_PKEY *ipk) -{ - int ret; - EVP_PKEY *pkey; - - pkey = ssl_mem_zalloc(sizeof(EVP_PKEY)); - if (!pkey) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "no enough memory > (pkey)"); - goto no_mem; - } - - if (ipk) { - pkey->method = ipk->method; - } else { - pkey->method = EVP_PKEY_method(); - } - - ret = EVP_PKEY_METHOD_CALL(new, pkey, ipk); - if (ret) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "EVP_PKEY_METHOD_CALL(new) return %d", ret); - goto failed; - } - - return pkey; - -failed: - ssl_mem_free(pkey); -no_mem: - return NULL; -} - -/** - * @brief create a private key object - */ -EVP_PKEY* EVP_PKEY_new(void) -{ - return __EVP_PKEY_new(NULL); -} - -/** - * @brief free a private key object - */ -void EVP_PKEY_free(EVP_PKEY *pkey) -{ - SSL_ASSERT3(pkey); - - EVP_PKEY_METHOD_CALL(free, pkey); - - ssl_mem_free(pkey); -} - -/** - * @brief load a character key context into system context. If '*a' is pointed to the - * private key, then load key into it. Or create a new private key object - */ -EVP_PKEY *d2i_PrivateKey(int type, - EVP_PKEY **a, - const unsigned char **pp, - long length) -{ - int m = 0; - int ret; - EVP_PKEY *pkey; - - SSL_ASSERT2(pp); - SSL_ASSERT2(*pp); - SSL_ASSERT2(length); - - if (a && *a) { - pkey = *a; - } else { - pkey = EVP_PKEY_new();; - if (!pkey) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "EVP_PKEY_new() return NULL"); - goto failed1; - } - - m = 1; - } - - ret = EVP_PKEY_METHOD_CALL(load, pkey, *pp, length); - if (ret) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "EVP_PKEY_METHOD_CALL(load) return %d", ret); - goto failed2; - } - - if (a) - *a = pkey; - - return pkey; - -failed2: - if (m) - EVP_PKEY_free(pkey); -failed1: - return NULL; -} - -/** - * @brief set the SSL context private key - */ -int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) -{ - SSL_ASSERT1(ctx); - SSL_ASSERT1(pkey); - - if (ctx->cert->pkey == pkey) - return 1; - - if (ctx->cert->pkey) - EVP_PKEY_free(ctx->cert->pkey); - - ctx->cert->pkey = pkey; - - return 1; -} - -/** - * @brief set the SSL private key - */ -int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) -{ - SSL_ASSERT1(ssl); - SSL_ASSERT1(pkey); - - if (ssl->cert->pkey == pkey) - return 1; - - if (ssl->cert->pkey) - EVP_PKEY_free(ssl->cert->pkey); - - ssl->cert->pkey = pkey; - - return 1; -} - -/** - * @brief load private key into the SSL context - */ -int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, - const unsigned char *d, long len) -{ - int ret; - EVP_PKEY *pk; - - pk = d2i_PrivateKey(0, NULL, &d, len); - if (!pk) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_PrivateKey() return NULL"); - goto failed1; - } - - ret = SSL_CTX_use_PrivateKey(ctx, pk); - if (!ret) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "SSL_CTX_use_PrivateKey() return %d", ret); - goto failed2; - } - - return 1; - -failed2: - EVP_PKEY_free(pk); -failed1: - return 0; -} - -/** - * @brief load private key into the SSL - */ -int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, - const unsigned char *d, long len) -{ - int ret; - EVP_PKEY *pk; - - pk = d2i_PrivateKey(0, NULL, &d, len); - if (!pk) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_PrivateKey() return NULL"); - goto failed1; - } - - ret = SSL_use_PrivateKey(ssl, pk); - if (!ret) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "SSL_use_PrivateKey() return %d", ret); - goto failed2; - } - - return 1; - -failed2: - EVP_PKEY_free(pk); -failed1: - return 0; -} - -/** - * @brief load the private key file into SSL context - */ -int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) -{ - return 0; -} - -/** - * @brief load the private key file into SSL - */ -int SSL_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) -{ - return 0; -} - -/** - * @brief load the RSA ASN1 private key into SSL context - */ -int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len) -{ - return SSL_CTX_use_PrivateKey_ASN1(0, ctx, d, len); -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_stack.c libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_stack.c --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_stack.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_stack.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ssl_stack.h" -#include "ssl_dbg.h" -#include "ssl_port.h" - -#ifndef CONFIG_MIN_NODES - #define MIN_NODES 4 -#else - #define MIN_NODES CONFIG_MIN_NODES -#endif - -/** - * @brief create a openssl stack object - */ -OPENSSL_STACK* OPENSSL_sk_new(OPENSSL_sk_compfunc c) -{ - OPENSSL_STACK *stack; - char **data; - - stack = ssl_mem_zalloc(sizeof(OPENSSL_STACK)); - if (!stack) { - SSL_DEBUG(SSL_STACK_ERROR_LEVEL, "no enough memory > (stack)"); - goto no_mem1; - } - - data = ssl_mem_zalloc(sizeof(*data) * MIN_NODES); - if (!data) { - SSL_DEBUG(SSL_STACK_ERROR_LEVEL, "no enough memory > (data)"); - goto no_mem2; - } - - stack->data = data; - stack->num_alloc = MIN_NODES; - stack->c = c; - - return stack; - -no_mem2: - ssl_mem_free(stack); -no_mem1: - return NULL; -} - -/** - * @brief create a NULL function openssl stack object - */ -OPENSSL_STACK *OPENSSL_sk_new_null(void) -{ - return OPENSSL_sk_new((OPENSSL_sk_compfunc)NULL); -} - -/** - * @brief free openssl stack object - */ -void OPENSSL_sk_free(OPENSSL_STACK *stack) -{ - SSL_ASSERT3(stack); - - ssl_mem_free(stack->data); - ssl_mem_free(stack); -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_x509.c libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_x509.c --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/library/ssl_x509.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/library/ssl_x509.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,353 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ssl_x509.h" -#include "ssl_methods.h" -#include "ssl_dbg.h" -#include "ssl_port.h" - -#include - -/** - * @brief show X509 certification information - */ -int __X509_show_info(X509 *x) -{ - return X509_METHOD_CALL(show_info, x); -} - -/** - * @brief create a X509 certification object according to input X509 certification - */ -X509* __X509_new(X509 *ix) -{ - int ret; - X509 *x; - - x = ssl_mem_zalloc(sizeof(X509)); - if (!x) { - SSL_DEBUG(SSL_X509_ERROR_LEVEL, "no enough memory > (x)"); - goto no_mem; - } - - if (ix) - x->method = ix->method; - else - x->method = X509_method(); - - ret = X509_METHOD_CALL(new, x, ix); - if (ret) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "X509_METHOD_CALL(new) return %d", ret); - goto failed; - } - - return x; - -failed: - ssl_mem_free(x); -no_mem: - return NULL; -} - -/** - * @brief create a X509 certification object - */ -X509* X509_new(void) -{ - return __X509_new(NULL); -} - -/** - * @brief free a X509 certification object - */ -void X509_free(X509 *x) -{ - SSL_ASSERT3(x); - - X509_METHOD_CALL(free, x); - - ssl_mem_free(x); -}; - -/** - * @brief load a character certification context into system context. If '*cert' is pointed to the - * certification, then load certification into it. Or create a new X509 certification object - */ -X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) -{ - int m = 0; - int ret; - X509 *x; - - SSL_ASSERT2(buffer); - SSL_ASSERT2(len); - - if (cert && *cert) { - x = *cert; - } else { - x = X509_new(); - if (!x) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "X509_new() return NULL"); - goto failed1; - } - m = 1; - } - - ret = X509_METHOD_CALL(load, x, buffer, len); - if (ret) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "X509_METHOD_CALL(load) return %d", ret); - goto failed2; - } - - return x; - -failed2: - if (m) - X509_free(x); -failed1: - return NULL; -} - -/** - * @brief return SSL X509 verify parameters - */ - -X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl) -{ - return &ssl->param; -} - -/** - * @brief set X509 host verification flags - */ - -int X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param, - unsigned long flags) -{ - /* flags not supported yet */ - return 0; -} - -/** - * @brief clear X509 host verification flags - */ - -int X509_VERIFY_PARAM_clear_hostflags(X509_VERIFY_PARAM *param, - unsigned long flags) -{ - /* flags not supported yet */ - return 0; -} - -/** - * @brief set SSL context client CA certification - */ -int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) -{ - SSL_ASSERT1(ctx); - SSL_ASSERT1(x); - assert(ctx); - if (ctx->client_CA == x) - return 1; - - X509_free(ctx->client_CA); - - ctx->client_CA = x; - - return 1; -} - -/** - * @brief add CA client certification into the SSL - */ -int SSL_CTX_add_client_CA_ASN1(SSL_CTX *ctx, int len, - const unsigned char *d) -{ - X509 *x; - - x = d2i_X509(NULL, d, len); - if (!x) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_X509() return NULL"); - return 0; - } - SSL_ASSERT1(ctx); - - X509_free(ctx->client_CA); - - ctx->client_CA = x; - - return 1; -} - -/** - * @brief add CA client certification into the SSL - */ -int SSL_add_client_CA(SSL *ssl, X509 *x) -{ - SSL_ASSERT1(ssl); - SSL_ASSERT1(x); - - if (ssl->client_CA == x) - return 1; - - X509_free(ssl->client_CA); - - ssl->client_CA = x; - - return 1; -} - -/** - * @brief set the SSL context certification - */ -int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) -{ - SSL_ASSERT1(ctx); - SSL_ASSERT1(x); - - if (ctx->cert->x509 == x) - return 1; - - X509_free(ctx->cert->x509); - - ctx->cert->x509 = x; - - return 1; -} - -/** - * @brief set the SSL certification - */ -int SSL_use_certificate(SSL *ssl, X509 *x) -{ - SSL_ASSERT1(ssl); - SSL_ASSERT1(x); - - if (ssl->cert->x509 == x) - return 1; - - X509_free(ssl->cert->x509); - - ssl->cert->x509 = x; - - return 1; -} - -/** - * @brief get the SSL certification point - */ -X509 *SSL_get_certificate(const SSL *ssl) -{ - SSL_ASSERT2(ssl); - - return ssl->cert->x509; -} - -/** - * @brief load certification into the SSL context - */ -int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, - const unsigned char *d) -{ - int ret; - X509 *x; - - x = d2i_X509(NULL, d, len); - if (!x) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_X509() return NULL"); - goto failed1; - } - - ret = SSL_CTX_use_certificate(ctx, x); - if (!ret) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "SSL_CTX_use_certificate() return %d", ret); - goto failed2; - } - - return 1; - -failed2: - X509_free(x); -failed1: - return 0; -} - -/** - * @brief load certification into the SSL - */ -int SSL_use_certificate_ASN1(SSL *ssl, const unsigned char *d, int len) -{ - int ret; - X509 *x; - - x = d2i_X509(NULL, d, len); - if (!x) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_X509() return NULL"); - goto failed1; - } - - ret = SSL_use_certificate(ssl, x); - if (!ret) { - SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "SSL_use_certificate() return %d", ret); - goto failed2; - } - - return 1; - -failed2: - X509_free(x); -failed1: - return 0; -} - -/** - * @brief load the certification file into SSL context - */ -int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) -{ - return 0; -} - -/** - * @brief load the certification file into SSL - */ -int SSL_use_certificate_file(SSL *ssl, const char *file, int type) -{ - return 0; -} - -/** - * @brief get peer certification - */ -X509 *SSL_get_peer_certificate(const SSL *ssl) -{ - SSL_ASSERT2(ssl); - - return ssl->session->peer; -} - -int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx) -{ - return X509_V_ERR_UNSPECIFIED; -} - -int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx) -{ - return 0; -} - -const char *X509_verify_cert_error_string(long n) -{ - return "unknown"; -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/platform/ssl_pm.c libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/platform/ssl_pm.c --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/platform/ssl_pm.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/platform/ssl_pm.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,954 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ssl_pm.h" -#include "ssl_port.h" -#include "ssl_dbg.h" - -/* mbedtls include */ -#include "mbedtls/platform.h" -#if defined(LWS_HAVE_MBEDTLS_NET_SOCKETS) -#include "mbedtls/net_sockets.h" -#else -#include "mbedtls/net.h" -#endif -#include "mbedtls/debug.h" -#include "mbedtls/entropy.h" -#include "mbedtls/ctr_drbg.h" -#include "mbedtls/error.h" -#include "mbedtls/certs.h" - -#include "private-lib-core.h" - -#define X509_INFO_STRING_LENGTH 8192 - -struct ssl_pm -{ - /* local socket file description */ - mbedtls_net_context fd; - /* remote client socket file description */ - mbedtls_net_context cl_fd; - - mbedtls_ssl_config conf; - - mbedtls_ctr_drbg_context ctr_drbg; - - mbedtls_ssl_context ssl; - - mbedtls_entropy_context entropy; - - SSL *owner; -}; - -struct x509_pm -{ - mbedtls_x509_crt *x509_crt; - - mbedtls_x509_crt *ex_crt; -}; - -struct pkey_pm -{ - mbedtls_pk_context *pkey; - - mbedtls_pk_context *ex_pkey; -}; - -unsigned int max_content_len; - -/*********************************************************************************************/ -/************************************ SSL arch interface *************************************/ - -//#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG - -/* mbedtls debug level */ -#define MBEDTLS_DEBUG_LEVEL 4 - -/** - * @brief mbedtls debug function - */ -static void ssl_platform_debug(void *ctx, int level, - const char *file, int line, - const char *str) -{ - /* Shorten 'file' from the whole file path to just the filename - - This is a bit wasteful because the macros are compiled in with - the full _FILE_ path in each case. - */ -// char *file_sep = rindex(file, '/'); - // if(file_sep) - // file = file_sep + 1; - - printf("%s:%d %s", file, line, str); -} -//#endif - -/** - * @brief create SSL low-level object - */ -int ssl_pm_new(SSL *ssl) -{ - struct ssl_pm *ssl_pm; - int ret; - - const unsigned char pers[] = "OpenSSL PM"; - size_t pers_len = sizeof(pers); - - int endpoint; - int version; - - const SSL_METHOD *method = ssl->method; - - ssl_pm = ssl_mem_zalloc(sizeof(struct ssl_pm)); - if (!ssl_pm) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (ssl_pm)"); - goto no_mem; - } - - ssl_pm->owner = ssl; - - if (!ssl->ctx->read_buffer_len) - ssl->ctx->read_buffer_len = 2048; - - max_content_len = ssl->ctx->read_buffer_len; - // printf("ssl->ctx->read_buffer_len = %d ++++++++++++++++++++\n", ssl->ctx->read_buffer_len); - - mbedtls_net_init(&ssl_pm->fd); - mbedtls_net_init(&ssl_pm->cl_fd); - - mbedtls_ssl_config_init(&ssl_pm->conf); - mbedtls_ctr_drbg_init(&ssl_pm->ctr_drbg); - mbedtls_entropy_init(&ssl_pm->entropy); - mbedtls_ssl_init(&ssl_pm->ssl); - - ret = mbedtls_ctr_drbg_seed(&ssl_pm->ctr_drbg, mbedtls_entropy_func, &ssl_pm->entropy, pers, pers_len); - if (ret) { - lwsl_notice("%s: mbedtls_ctr_drbg_seed() return -0x%x", __func__, -ret); - //goto mbedtls_err1; - } - - if (method->endpoint) { - endpoint = MBEDTLS_SSL_IS_SERVER; - } else { - endpoint = MBEDTLS_SSL_IS_CLIENT; - } - ret = mbedtls_ssl_config_defaults(&ssl_pm->conf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); - if (ret) { - lwsl_err("%s: mbedtls_ssl_config_defaults() return -0x%x", __func__, -ret); - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_config_defaults() return -0x%x", -ret); - goto mbedtls_err2; - } - - if (TLS_ANY_VERSION != ssl->version) { - if (TLS1_2_VERSION == ssl->version) - version = MBEDTLS_SSL_MINOR_VERSION_3; - else if (TLS1_1_VERSION == ssl->version) - version = MBEDTLS_SSL_MINOR_VERSION_2; - else if (TLS1_VERSION == ssl->version) - version = MBEDTLS_SSL_MINOR_VERSION_1; - else - version = MBEDTLS_SSL_MINOR_VERSION_0; - - mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); - mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); - } else { - mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); - mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); - } - - mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); - -//#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG - // mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL); -// mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL); -//#else - mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL); -//#endif - - ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf); - if (ret) { - lwsl_err("%s: mbedtls_ssl_setup() return -0x%x", __func__, -ret); - - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_setup() return -0x%x", -ret); - goto mbedtls_err2; - } - - mbedtls_ssl_set_bio(&ssl_pm->ssl, &ssl_pm->fd, mbedtls_net_send, mbedtls_net_recv, NULL); - - ssl->ssl_pm = ssl_pm; - - return 0; - -mbedtls_err2: - mbedtls_ssl_config_free(&ssl_pm->conf); - mbedtls_ctr_drbg_free(&ssl_pm->ctr_drbg); -//mbedtls_err1: - mbedtls_entropy_free(&ssl_pm->entropy); - ssl_mem_free(ssl_pm); -no_mem: - return -1; -} - -/** - * @brief free SSL low-level object - */ -void ssl_pm_free(SSL *ssl) -{ - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - - mbedtls_ctr_drbg_free(&ssl_pm->ctr_drbg); - mbedtls_entropy_free(&ssl_pm->entropy); - mbedtls_ssl_config_free(&ssl_pm->conf); - mbedtls_ssl_free(&ssl_pm->ssl); - - ssl_mem_free(ssl_pm); - ssl->ssl_pm = NULL; -} - -/** - * @brief reload SSL low-level certification object - */ -static int ssl_pm_reload_crt(SSL *ssl) -{ - int ret; - int mode; - struct ssl_pm *ssl_pm = ssl->ssl_pm; - struct x509_pm *ca_pm = (struct x509_pm *)ssl->client_CA->x509_pm; - - struct pkey_pm *pkey_pm = (struct pkey_pm *)ssl->cert->pkey->pkey_pm; - struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm; - - if (ssl->verify_mode == SSL_VERIFY_PEER) - mode = MBEDTLS_SSL_VERIFY_OPTIONAL; - else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT) - mode = MBEDTLS_SSL_VERIFY_OPTIONAL; - else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE) - mode = MBEDTLS_SSL_VERIFY_UNSET; - else - mode = MBEDTLS_SSL_VERIFY_NONE; - - mbedtls_ssl_conf_authmode(&ssl_pm->conf, mode); - - if (ca_pm->x509_crt) { - mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->x509_crt, NULL); - } else if (ca_pm->ex_crt) { - mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->ex_crt, NULL); - } - - if (crt_pm->x509_crt && pkey_pm->pkey) { - ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, crt_pm->x509_crt, pkey_pm->pkey); - } else if (crt_pm->ex_crt && pkey_pm->ex_pkey) { - ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, crt_pm->ex_crt, pkey_pm->ex_pkey); - } else { - ret = 0; - } - - if (ret) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_conf_own_cert() return -0x%x", -ret); - ret = -1; - } - - return ret; -} - -/* - * Perform the mbedtls SSL handshake instead of mbedtls_ssl_handshake. - * We can add debug here. - */ -static int mbedtls_handshake( mbedtls_ssl_context *ssl ) -{ - int ret = 0; - - while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) { - ret = mbedtls_ssl_handshake_step(ssl); - - lwsl_info("%s: ssl ret -%x state %d\n", __func__, -ret, ssl->state); - - if (ret != 0) - break; - } - - return ret; -} - -#if !defined(LWS_PLAT_OPTEE) -#include -#endif - -int ssl_pm_handshake(SSL *ssl) -{ - int ret; - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - - ssl->err = 0; - errno = 0; - - ret = ssl_pm_reload_crt(ssl); - if (ret) { - printf("%s: cert reload failed\n", __func__); - return 0; - } - - if (ssl_pm->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) { - ssl_speed_up_enter(); - - /* mbedtls return codes - * 0 = successful, or MBEDTLS_ERR_SSL_WANT_READ/WRITE - * anything else = death - */ - ret = mbedtls_handshake(&ssl_pm->ssl); - ssl_speed_up_exit(); - } else - ret = 0; - - /* - * OpenSSL return codes: - * 0 = did not complete, but may be retried - * 1 = successfully completed - * <0 = death - */ - if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { - ssl->err = ret; - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_handshake() return -0x%x", -ret); - return 0; /* OpenSSL: did not complete but may be retried */ - } - - if (ret == 0) { /* successful */ - struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm; - - x509_pm->ex_crt = (mbedtls_x509_crt *)mbedtls_ssl_get_peer_cert(&ssl_pm->ssl); - return 1; /* openssl successful */ - } - - if (errno == 11) { - ssl->err = ret == MBEDTLS_ERR_SSL_WANT_READ; - - return 0; - } - - lwsl_info("%s: mbedtls_ssl_handshake() returned -0x%x\n", __func__, -ret); - - /* it's had it */ - - ssl->err = SSL_ERROR_SYSCALL; - - return -1; /* openssl death */ -} - -mbedtls_x509_crt * -ssl_ctx_get_mbedtls_x509_crt(SSL_CTX *ssl_ctx) -{ - struct x509_pm *x509_pm = (struct x509_pm *)ssl_ctx->cert->x509->x509_pm; - - if (!x509_pm) - return NULL; - - return x509_pm->x509_crt; -} - -mbedtls_x509_crt * -ssl_get_peer_mbedtls_x509_crt(SSL *ssl) -{ - struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm; - - if (!x509_pm) - return NULL; - - return x509_pm->ex_crt; -} - -int ssl_pm_shutdown(SSL *ssl) -{ - int ret; - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - - ret = mbedtls_ssl_close_notify(&ssl_pm->ssl); - if (ret) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_close_notify() return -0x%x", -ret); - if (ret == MBEDTLS_ERR_NET_CONN_RESET) - ssl->err = SSL_ERROR_SYSCALL; - ret = -1; /* OpenSSL: "Call SSL_get_error with the return value to find the reason */ - } else { - struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm; - - x509_pm->ex_crt = NULL; - ret = 1; /* OpenSSL: "The shutdown was successfully completed" - ...0 means retry */ - } - - return ret; -} - -int ssl_pm_clear(SSL *ssl) -{ - return ssl_pm_shutdown(ssl); -} - - -int ssl_pm_read(SSL *ssl, void *buffer, int len) -{ - int ret; - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - - ret = mbedtls_ssl_read(&ssl_pm->ssl, buffer, len); - if (ret < 0) { - // lwsl_notice("%s: mbedtls_ssl_read says -0x%x\n", __func__, -ret); - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_read() return -0x%x", -ret); - if (ret == MBEDTLS_ERR_NET_CONN_RESET || - ret <= MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE) /* fatal errors */ - ssl->err = SSL_ERROR_SYSCALL; - ret = -1; - } - - return ret; -} - -/* - * This returns -1, or the length sent. - * If -1, then you need to find out if the error was - * fatal or recoverable using SSL_get_error() - */ -int ssl_pm_send(SSL *ssl, const void *buffer, int len) -{ - int ret; - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - - ret = mbedtls_ssl_write(&ssl_pm->ssl, buffer, len); - /* - * We can get a positive number, which may be less than len... that - * much was sent successfully and you can call again to send more. - * - * We can get a negative mbedtls error code... if WANT_WRITE or WANT_READ, - * it's nonfatal and means it should be retried as-is. If something else, - * it's fatal actually. - * - * If this function returns something other than a positive value or - * MBEDTLS_ERR_SSL_WANT_READ/WRITE, the ssl context becomes unusable, and - * you should either free it or call mbedtls_ssl_session_reset() on it - * before re-using it for a new connection; the current connection must - * be closed. - * - * When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ, it must be - * called later with the same arguments, until it returns a positive value. - */ - - if (ret < 0) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret); - switch (ret) { - case MBEDTLS_ERR_NET_SEND_FAILED: - case MBEDTLS_ERR_NET_CONN_RESET: - ssl->err = SSL_ERROR_SYSCALL; - break; - case MBEDTLS_ERR_SSL_WANT_WRITE: - ssl->err = SSL_ERROR_WANT_WRITE; - break; - case MBEDTLS_ERR_SSL_WANT_READ: - ssl->err = SSL_ERROR_WANT_READ; - break; - default: - break; - } - - ret = -1; - } - - return ret; -} - -int ssl_pm_pending(const SSL *ssl) -{ - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - - return mbedtls_ssl_get_bytes_avail(&ssl_pm->ssl); -} - -void ssl_pm_set_fd(SSL *ssl, int fd, int mode) -{ - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - - ssl_pm->fd.fd = fd; -} - -int ssl_pm_get_fd(const SSL *ssl, int mode) -{ - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - - return ssl_pm->fd.fd; -} - -OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl) -{ - OSSL_HANDSHAKE_STATE state; - - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - - switch (ssl_pm->ssl.state) - { - case MBEDTLS_SSL_CLIENT_HELLO: - state = TLS_ST_CW_CLNT_HELLO; - break; - case MBEDTLS_SSL_SERVER_HELLO: - state = TLS_ST_SW_SRVR_HELLO; - break; - case MBEDTLS_SSL_SERVER_CERTIFICATE: - state = TLS_ST_SW_CERT; - break; - case MBEDTLS_SSL_SERVER_HELLO_DONE: - state = TLS_ST_SW_SRVR_DONE; - break; - case MBEDTLS_SSL_CLIENT_KEY_EXCHANGE: - state = TLS_ST_CW_KEY_EXCH; - break; - case MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC: - state = TLS_ST_CW_CHANGE; - break; - case MBEDTLS_SSL_CLIENT_FINISHED: - state = TLS_ST_CW_FINISHED; - break; - case MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC: - state = TLS_ST_SW_CHANGE; - break; - case MBEDTLS_SSL_SERVER_FINISHED: - state = TLS_ST_SW_FINISHED; - break; - case MBEDTLS_SSL_CLIENT_CERTIFICATE: - state = TLS_ST_CW_CERT; - break; - case MBEDTLS_SSL_SERVER_KEY_EXCHANGE: - state = TLS_ST_SR_KEY_EXCH; - break; - case MBEDTLS_SSL_SERVER_NEW_SESSION_TICKET: - state = TLS_ST_SW_SESSION_TICKET; - break; - case MBEDTLS_SSL_SERVER_HELLO_VERIFY_REQUEST_SENT: - state = TLS_ST_SW_CERT_REQ; - break; - case MBEDTLS_SSL_HANDSHAKE_OVER: - state = TLS_ST_OK; - break; - default : - state = TLS_ST_BEFORE; - break; - } - - return state; -} - -int x509_pm_show_info(X509 *x) -{ - int ret; - char *buf; - mbedtls_x509_crt *x509_crt; - struct x509_pm *x509_pm = x->x509_pm; - - if (x509_pm->x509_crt) - x509_crt = x509_pm->x509_crt; - else if (x509_pm->ex_crt) - x509_crt = x509_pm->ex_crt; - else - x509_crt = NULL; - - if (!x509_crt) - return -1; - - buf = ssl_mem_malloc(X509_INFO_STRING_LENGTH); - if (!buf) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (buf)"); - goto no_mem; - } - - ret = mbedtls_x509_crt_info(buf, X509_INFO_STRING_LENGTH - 1, "", x509_crt); - if (ret <= 0) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_x509_crt_info() return -0x%x", -ret); - goto mbedtls_err1; - } - - buf[ret] = 0; - - ssl_mem_free(buf); - - SSL_DEBUG(SSL_DEBUG_ON, "%s", buf); - - return 0; - -mbedtls_err1: - ssl_mem_free(buf); -no_mem: - return -1; -} - -int x509_pm_new(X509 *x, X509 *m_x) -{ - struct x509_pm *x509_pm; - - x509_pm = ssl_mem_zalloc(sizeof(struct x509_pm)); - if (!x509_pm) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (x509_pm)"); - goto failed1; - } - - x->x509_pm = x509_pm; - - if (m_x) { - struct x509_pm *m_x509_pm = (struct x509_pm *)m_x->x509_pm; - - x509_pm->ex_crt = m_x509_pm->x509_crt; - } - - return 0; - -failed1: - return -1; -} - -void x509_pm_free(X509 *x) -{ - struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; - - if (x509_pm->x509_crt) { - mbedtls_x509_crt_free(x509_pm->x509_crt); - - ssl_mem_free(x509_pm->x509_crt); - x509_pm->x509_crt = NULL; - } - - ssl_mem_free(x->x509_pm); - x->x509_pm = NULL; -} - -int x509_pm_load(X509 *x, const unsigned char *buffer, int len) -{ - int ret; - unsigned char *load_buf; - struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; - - if (x509_pm->x509_crt) - mbedtls_x509_crt_free(x509_pm->x509_crt); - - if (!x509_pm->x509_crt) { - x509_pm->x509_crt = ssl_mem_malloc(sizeof(mbedtls_x509_crt)); - if (!x509_pm->x509_crt) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (x509_pm->x509_crt)"); - goto no_mem; - } - } - - mbedtls_x509_crt_init(x509_pm->x509_crt); - if (buffer[0] != 0x30) { - load_buf = ssl_mem_malloc(len + 1); - if (!load_buf) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)"); - goto failed; - } - - ssl_memcpy(load_buf, buffer, len); - load_buf[len] = '\0'; - - ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1); - ssl_mem_free(load_buf); - } else { - // printf("parsing as der\n"); - - ret = mbedtls_x509_crt_parse_der(x509_pm->x509_crt, buffer, len); - } - - if (ret) { - printf("mbedtls_x509_crt_parse return -0x%x", -ret); - goto failed; - } - - return 0; - -failed: - mbedtls_x509_crt_free(x509_pm->x509_crt); - ssl_mem_free(x509_pm->x509_crt); - x509_pm->x509_crt = NULL; -no_mem: - return -1; -} - -int pkey_pm_new(EVP_PKEY *pk, EVP_PKEY *m_pkey) -{ - struct pkey_pm *pkey_pm; - - pkey_pm = ssl_mem_zalloc(sizeof(struct pkey_pm)); - if (!pkey_pm) - return -1; - - pk->pkey_pm = pkey_pm; - - if (m_pkey) { - struct pkey_pm *m_pkey_pm = (struct pkey_pm *)m_pkey->pkey_pm; - - pkey_pm->ex_pkey = m_pkey_pm->pkey; - } - - return 0; -} - -void pkey_pm_free(EVP_PKEY *pk) -{ - struct pkey_pm *pkey_pm = (struct pkey_pm *)pk->pkey_pm; - - if (pkey_pm->pkey) { - mbedtls_pk_free(pkey_pm->pkey); - - ssl_mem_free(pkey_pm->pkey); - pkey_pm->pkey = NULL; - } - - ssl_mem_free(pk->pkey_pm); - pk->pkey_pm = NULL; -} - -int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) -{ - int ret; - unsigned char *load_buf; - struct pkey_pm *pkey_pm = (struct pkey_pm *)pk->pkey_pm; - - if (pkey_pm->pkey) - mbedtls_pk_free(pkey_pm->pkey); - - if (!pkey_pm->pkey) { - pkey_pm->pkey = ssl_mem_malloc(sizeof(mbedtls_pk_context)); - if (!pkey_pm->pkey) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (pkey_pm->pkey)"); - goto no_mem; - } - } - - load_buf = ssl_mem_malloc(len + 1); - if (!load_buf) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)"); - goto failed; - } - - ssl_memcpy(load_buf, buffer, len); - load_buf[len] = '\0'; - - mbedtls_pk_init(pkey_pm->pkey); - - ret = mbedtls_pk_parse_key(pkey_pm->pkey, load_buf, len + 1, NULL, 0); - ssl_mem_free(load_buf); - - if (ret) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_pk_parse_key return -0x%x", -ret); - goto failed; - } - - return 0; - -failed: - mbedtls_pk_free(pkey_pm->pkey); - ssl_mem_free(pkey_pm->pkey); - pkey_pm->pkey = NULL; -no_mem: - return -1; -} - - - -void ssl_pm_set_bufflen(SSL *ssl, int len) -{ - max_content_len = len; -} - -long ssl_pm_get_verify_result(const SSL *ssl) -{ - uint32_t ret; - long verify_result; - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - - ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl); - if (!ret) - return X509_V_OK; - - if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED || - (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED)) - verify_result = X509_V_ERR_INVALID_CA; - - else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH) - verify_result = X509_V_ERR_HOSTNAME_MISMATCH; - - else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) || - (ret & MBEDTLS_X509_BADCRL_BAD_KEY)) - verify_result = X509_V_ERR_CA_KEY_TOO_SMALL; - - else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) || - (ret & MBEDTLS_X509_BADCRL_BAD_MD)) - verify_result = X509_V_ERR_CA_MD_TOO_WEAK; - - else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) || - (ret & MBEDTLS_X509_BADCRL_FUTURE)) - verify_result = X509_V_ERR_CERT_NOT_YET_VALID; - - else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) || - (ret & MBEDTLS_X509_BADCRL_EXPIRED)) - verify_result = X509_V_ERR_CERT_HAS_EXPIRED; - - else - verify_result = X509_V_ERR_UNSPECIFIED; - - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, - "mbedtls_ssl_get_verify_result() return 0x%x", ret); - - return verify_result; -} - -/** - * @brief set expected hostname on peer cert CN - */ - -int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, - const char *name, size_t namelen) -{ - SSL *ssl = (SSL *)((char *)param - offsetof(SSL, param)); - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - char *name_cstr = NULL; - - if (namelen) { - name_cstr = malloc(namelen + 1); - if (!name_cstr) - return 0; - memcpy(name_cstr, name, namelen); - name_cstr[namelen] = '\0'; - name = name_cstr; - } - - mbedtls_ssl_set_hostname(&ssl_pm->ssl, name); - - if (namelen) - free(name_cstr); - - return 1; -} - -void _ssl_set_alpn_list(const SSL *ssl) -{ -#if defined(LWS_HAVE_mbedtls_ssl_conf_alpn_protocols) - if (ssl->alpn_protos) { - if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->alpn_protos)) - fprintf(stderr, "mbedtls_ssl_conf_alpn_protocols failed\n"); - - return; - } - if (!ssl->ctx->alpn_protos) - return; - if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->ctx->alpn_protos)) - fprintf(stderr, "mbedtls_ssl_conf_alpn_protocols failed\n"); -#endif -} - -void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, - unsigned int *len) -{ -#if defined(LWS_HAVE_mbedtls_ssl_get_alpn_protocol) - const char *alp = mbedtls_ssl_get_alpn_protocol(&((struct ssl_pm *)(ssl->ssl_pm))->ssl); - - *data = (const unsigned char *)alp; - if (alp) - *len = strlen(alp); - else - *len = 0; -#endif -} - -int SSL_set_sni_callback(SSL *ssl, int(*cb)(void *, mbedtls_ssl_context *, - const unsigned char *, size_t), void *param) -{ -#if defined(LWS_HAVE_mbedtls_ssl_conf_sni) - struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - - mbedtls_ssl_conf_sni(&ssl_pm->conf, cb, param); -#endif - return 0; -} - -SSL *SSL_SSL_from_mbedtls_ssl_context(mbedtls_ssl_context *msc) -{ - struct ssl_pm *ssl_pm = (struct ssl_pm *)((char *)msc - offsetof(struct ssl_pm, ssl)); - - return ssl_pm->owner; -} - -#include "ssl_cert.h" - -void SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx) -{ -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_authmode) || \ - defined(LWS_HAVE_mbedtls_ssl_set_hs_ca_chain) || \ - defined(LWS_HAVE_mbedtls_ssl_set_hs_own_cert) - struct ssl_pm *ssl_pm = ssl->ssl_pm; -#endif -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_own_cert) - struct x509_pm *x509_pm; -#endif -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_ca_chain) - struct x509_pm *x509_pm_ca; -#endif -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_own_cert) - struct pkey_pm *pkey_pm; -#endif -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_authmode) - int mode; -#endif - -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_own_cert) - if (!ctx->cert || !ctx->cert->x509) - return; - x509_pm = (struct x509_pm *)ctx->cert->x509->x509_pm; -#endif -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_ca_chain) - if (!ctx->client_CA) - return; - x509_pm_ca = (struct x509_pm *)ctx->client_CA->x509_pm; -#endif -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_own_cert) - if (!ctx->cert || !ctx->cert->pkey) - return; - pkey_pm = (struct pkey_pm *)ctx->cert->pkey->pkey_pm; -#endif - - - if (ssl->cert) - ssl_cert_free(ssl->cert); - ssl->ctx = ctx; - ssl->cert = __ssl_cert_new(ctx->cert); - -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_authmode) - if (ctx->verify_mode == SSL_VERIFY_PEER) - mode = MBEDTLS_SSL_VERIFY_OPTIONAL; - else if (ctx->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT) - mode = MBEDTLS_SSL_VERIFY_OPTIONAL; - else if (ctx->verify_mode == SSL_VERIFY_CLIENT_ONCE) - mode = MBEDTLS_SSL_VERIFY_UNSET; - else - mode = MBEDTLS_SSL_VERIFY_NONE; -#endif - - /* apply new ctx cert to ssl */ - - ssl->verify_mode = ctx->verify_mode; -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_ca_chain) - mbedtls_ssl_set_hs_ca_chain(&ssl_pm->ssl, x509_pm_ca->x509_crt, NULL); -#endif -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_own_cert) - mbedtls_ssl_set_hs_own_cert(&ssl_pm->ssl, x509_pm->x509_crt, pkey_pm->pkey); -#endif -#if defined(LWS_HAVE_mbedtls_ssl_set_hs_authmode) - mbedtls_ssl_set_hs_authmode(&ssl_pm->ssl, mode); -#endif -} diff -Nru libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/platform/ssl_port.c libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/platform/ssl_port.c --- libwebsockets-4.0.20/lib/tls/mbedtls/wrapper/platform/ssl_port.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/mbedtls/wrapper/platform/ssl_port.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ssl_port.h" - -/*********************************************************************************************/ -/********************************* SSL general interface *************************************/ - -void *ssl_mem_zalloc(size_t size) -{ - void *p = malloc(size); - - if (p) - memset(p, 0, size); - - return p; -} - diff -Nru libwebsockets-4.0.20/lib/tls/openssl/lws-genaes.c libwebsockets-2.4.2/lib/tls/openssl/lws-genaes.c --- libwebsockets-4.0.20/lib/tls/openssl/lws-genaes.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/openssl/lws-genaes.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,386 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws_genaes provides an AES abstraction api in lws that works the - * same whether you are using openssl or mbedtls hash functions underneath. - */ -#include "private-lib-core.h" -#include "private-lib-jose.h" - -/* - * Care: many openssl apis return 1 for success. These are translated to the - * lws convention of 0 for success. - */ - -int -lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op, - enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el, - enum enum_aes_padding padding, void *engine) -{ - int n = 0; - - ctx->ctx = EVP_CIPHER_CTX_new(); - if (!ctx->ctx) - return -1; - - ctx->mode = mode; - ctx->k = el; - ctx->engine = engine; - ctx->init = 0; - ctx->op = op; - ctx->padding = padding; - - switch (ctx->k->len) { - case 128 / 8: - switch (mode) { - case LWS_GAESM_KW: -#if defined(LWS_HAVE_EVP_aes_128_wrap) - EVP_CIPHER_CTX_set_flags(ctx->ctx, - EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); - ctx->cipher = EVP_aes_128_wrap(); - break; -#else - lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n", - __func__); - return -1; -#endif - case LWS_GAESM_CBC: - ctx->cipher = EVP_aes_128_cbc(); - break; -#if defined(LWS_HAVE_EVP_aes_128_cfb128) - case LWS_GAESM_CFB128: - ctx->cipher = EVP_aes_128_cfb128(); - break; -#endif -#if defined(LWS_HAVE_EVP_aes_128_cfb8) - case LWS_GAESM_CFB8: - ctx->cipher = EVP_aes_128_cfb8(); - break; -#endif - case LWS_GAESM_CTR: - ctx->cipher = EVP_aes_128_ctr(); - break; - case LWS_GAESM_ECB: - ctx->cipher = EVP_aes_128_ecb(); - break; - case LWS_GAESM_OFB: - ctx->cipher = EVP_aes_128_ofb(); - break; - case LWS_GAESM_XTS: - lwsl_err("%s: AES XTS requires double-length key\n", - __func__); - break; - case LWS_GAESM_GCM: - ctx->cipher = EVP_aes_128_gcm(); - break; - default: - goto bail; - } - break; - - case 192 / 8: - switch (mode) { - case LWS_GAESM_KW: -#if defined(LWS_HAVE_EVP_aes_128_wrap) - EVP_CIPHER_CTX_set_flags(ctx->ctx, - EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); - ctx->cipher = EVP_aes_192_wrap(); - break; -#else - lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n", - __func__); - return -1; -#endif - case LWS_GAESM_CBC: - ctx->cipher = EVP_aes_192_cbc(); - break; -#if defined(LWS_HAVE_EVP_aes_192_cfb128) - case LWS_GAESM_CFB128: - ctx->cipher = EVP_aes_192_cfb128(); - break; -#endif -#if defined(LWS_HAVE_EVP_aes_192_cfb8) - case LWS_GAESM_CFB8: - ctx->cipher = EVP_aes_192_cfb8(); - break; -#endif - case LWS_GAESM_CTR: - ctx->cipher = EVP_aes_192_ctr(); - break; - case LWS_GAESM_ECB: - ctx->cipher = EVP_aes_192_ecb(); - break; - case LWS_GAESM_OFB: - ctx->cipher = EVP_aes_192_ofb(); - break; - case LWS_GAESM_XTS: - lwsl_err("%s: AES XTS 192 invalid\n", __func__); - goto bail; - case LWS_GAESM_GCM: - ctx->cipher = EVP_aes_192_gcm(); - break; - default: - goto bail; - } - break; - - case 256 / 8: - switch (mode) { - case LWS_GAESM_KW: -#if defined(LWS_HAVE_EVP_aes_128_wrap) - EVP_CIPHER_CTX_set_flags(ctx->ctx, - EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); - ctx->cipher = EVP_aes_256_wrap(); - break; -#else - lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n", - __func__); - return -1; -#endif - case LWS_GAESM_CBC: - ctx->cipher = EVP_aes_256_cbc(); - break; -#if defined(LWS_HAVE_EVP_aes_256_cfb128) - case LWS_GAESM_CFB128: - ctx->cipher = EVP_aes_256_cfb128(); - break; -#endif -#if defined(LWS_HAVE_EVP_aes_256_cfb8) - case LWS_GAESM_CFB8: - ctx->cipher = EVP_aes_256_cfb8(); - break; -#endif - case LWS_GAESM_CTR: - ctx->cipher = EVP_aes_256_ctr(); - break; - case LWS_GAESM_ECB: - ctx->cipher = EVP_aes_256_ecb(); - break; - case LWS_GAESM_OFB: - ctx->cipher = EVP_aes_256_ofb(); - break; -#if defined(LWS_HAVE_EVP_aes_128_xts) - case LWS_GAESM_XTS: - ctx->cipher = EVP_aes_128_xts(); - break; -#endif - case LWS_GAESM_GCM: - ctx->cipher = EVP_aes_256_gcm(); - break; - default: - goto bail; - } - break; - - case 512 / 8: - switch (mode) { - case LWS_GAESM_XTS: - ctx->cipher = EVP_aes_256_xts(); - break; - default: - goto bail; - } - break; - - default: - lwsl_err("%s: unsupported AES size %d bits\n", __func__, - ctx->k->len * 8); - goto bail; - } - - switch (ctx->op) { - case LWS_GAESO_ENC: - n = EVP_EncryptInit_ex(ctx->ctx, ctx->cipher, ctx->engine, - NULL, NULL); - EVP_CIPHER_CTX_set_padding(ctx->ctx, padding); - break; - case LWS_GAESO_DEC: - n = EVP_DecryptInit_ex(ctx->ctx, ctx->cipher, ctx->engine, - NULL, NULL); - EVP_CIPHER_CTX_set_padding(ctx->ctx, padding); - break; - } - if (!n) { - lwsl_err("%s: cipher init failed (cipher %p)\n", __func__, - ctx->cipher); - lws_tls_err_describe_clear(); - goto bail; - } - - return 0; -bail: - EVP_CIPHER_CTX_free(ctx->ctx); - ctx->ctx = NULL; - return -1; -} - -int -lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen) -{ - uint8_t buf[256]; - int outl = sizeof(buf), n = 0; - - if (!ctx->ctx) - return 0; - - if (ctx->init) { - switch (ctx->op) { - case LWS_GAESO_ENC: - - if (EVP_EncryptFinal_ex(ctx->ctx, buf, &outl) != 1) { - lwsl_err("%s: enc final failed\n", __func__); - n = -1; - } - - if (ctx->mode == LWS_GAESM_GCM) { - if (EVP_CIPHER_CTX_ctrl(ctx->ctx, - EVP_CTRL_GCM_GET_TAG, - ctx->taglen, tag) != 1) { - lwsl_err("get tag ctrl failed\n"); - //lws_tls_err_describe_clear(); - n = 1; - } - } - if (ctx->mode == LWS_GAESM_CBC) - memcpy(tag, buf, outl); - - break; - - case LWS_GAESO_DEC: - if (EVP_DecryptFinal_ex(ctx->ctx, buf, &outl) != 1) { - lwsl_err("%s: dec final failed\n", __func__); - lws_tls_err_describe_clear(); - n = -1; - } - - break; - } - if (outl) - lwsl_debug("%s: final len %d\n", __func__, outl); - } - - ctx->k = NULL; - EVP_CIPHER_CTX_free(ctx->ctx); - ctx->ctx = NULL; - - return n; -} - -int -lws_genaes_crypt(struct lws_genaes_ctx *ctx, - const uint8_t *in, size_t len, uint8_t *out, - uint8_t *iv_or_nonce_ctr_or_data_unit_16, - uint8_t *stream_block_16, size_t *nc_or_iv_off, int taglen) -{ - int n = 0, outl, olen; - - if (!ctx->init) { - - EVP_CIPHER_CTX_set_key_length(ctx->ctx, ctx->k->len); - - if (ctx->mode == LWS_GAESM_GCM) { - n = EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_SET_IVLEN, - (int)*nc_or_iv_off, NULL); - if (n != 1) { - lwsl_err("%s: SET_IVLEN failed\n", __func__); - return -1; - } - memcpy(ctx->tag, stream_block_16, taglen); - ctx->taglen = taglen; - } - - switch (ctx->op) { - case LWS_GAESO_ENC: - n = EVP_EncryptInit_ex(ctx->ctx, NULL, NULL, - ctx->k->buf, - iv_or_nonce_ctr_or_data_unit_16); - break; - case LWS_GAESO_DEC: - if (ctx->mode == LWS_GAESM_GCM) - EVP_CIPHER_CTX_ctrl(ctx->ctx, - EVP_CTRL_GCM_SET_TAG, - ctx->taglen, ctx->tag); - n = EVP_DecryptInit_ex(ctx->ctx, NULL, NULL, - ctx->k->buf, - iv_or_nonce_ctr_or_data_unit_16); - break; - } - - if (!n) { - lws_tls_err_describe_clear(); - lwsl_err("%s: init failed (cipher %p)\n", - __func__, ctx->cipher); - - return -1; - } - ctx->init = 1; - } - - if (ctx->mode == LWS_GAESM_GCM && !out) { - /* AAD */ - - if (!len) - return 0; - - switch (ctx->op) { - case LWS_GAESO_ENC: - n = EVP_EncryptUpdate(ctx->ctx, NULL, &olen, in, (int)len); - break; - case LWS_GAESO_DEC: - n = EVP_DecryptUpdate(ctx->ctx, NULL, &olen, in, (int)len); - break; - default: - return -1; - } - if (n != 1) { - lwsl_err("%s: set AAD failed\n", __func__); - lws_tls_err_describe_clear(); - lwsl_hexdump_err(in, len); - return -1; - } - - return 0; - } - - switch (ctx->op) { - case LWS_GAESO_ENC: - n = EVP_EncryptUpdate(ctx->ctx, out, &outl, in, (int)len); - break; - case LWS_GAESO_DEC: - n = EVP_DecryptUpdate(ctx->ctx, out, &outl, in, (int)len); - break; - default: - return -1; - } - - // lwsl_notice("discarding outl %d\n", (int)outl); - - if (!n) { - lwsl_notice("%s: update failed\n", __func__); - lws_tls_err_describe_clear(); - - return -1; - } - - return 0; -} diff -Nru libwebsockets-4.0.20/lib/tls/openssl/lws-gencrypto.c libwebsockets-2.4.2/lib/tls/openssl/lws-gencrypto.c --- libwebsockets-4.0.20/lib/tls/openssl/lws-gencrypto.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/openssl/lws-gencrypto.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws-gencrypto openssl-specific common code - */ - -#include "private-lib-core.h" -#include "private-lib-tls-openssl.h" - -/* - * Care: many openssl apis return 1 for success. These are translated to the - * lws convention of 0 for success. - */ - -int -lws_gencrypto_openssl_hash_to_NID(enum lws_genhash_types hash_type) -{ - int h = -1; - - switch (hash_type) { - case LWS_GENHASH_TYPE_UNKNOWN: - break; - case LWS_GENHASH_TYPE_MD5: - h = NID_md5; - break; - case LWS_GENHASH_TYPE_SHA1: - h = NID_sha1; - break; - case LWS_GENHASH_TYPE_SHA256: - h = NID_sha256; - break; - case LWS_GENHASH_TYPE_SHA384: - h = NID_sha384; - break; - case LWS_GENHASH_TYPE_SHA512: - h = NID_sha512; - break; - } - - return h; -} - -const EVP_MD * -lws_gencrypto_openssl_hash_to_EVP_MD(enum lws_genhash_types hash_type) -{ - const EVP_MD *h = NULL; - - switch (hash_type) { - case LWS_GENHASH_TYPE_UNKNOWN: - break; - case LWS_GENHASH_TYPE_MD5: - h = EVP_md5(); - break; - case LWS_GENHASH_TYPE_SHA1: - h = EVP_sha1(); - break; - case LWS_GENHASH_TYPE_SHA256: - h = EVP_sha256(); - break; - case LWS_GENHASH_TYPE_SHA384: - h = EVP_sha384(); - break; - case LWS_GENHASH_TYPE_SHA512: - h = EVP_sha512(); - break; - } - - return h; -} diff -Nru libwebsockets-4.0.20/lib/tls/openssl/lws-genec.c libwebsockets-2.4.2/lib/tls/openssl/lws-genec.c --- libwebsockets-4.0.20/lib/tls/openssl/lws-genec.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/openssl/lws-genec.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,664 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws_genec provides an EC abstraction api in lws that works the - * same whether you are using openssl or mbedtls crypto functions underneath. - */ -#include "private-lib-core.h" -#include "private-lib-tls-openssl.h" - -/* - * Care: many openssl apis return 1 for success. These are translated to the - * lws convention of 0 for success. - */ - -#if !defined(LWS_HAVE_ECDSA_SIG_set0) -static void -ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) -{ - if (pr != NULL) - *pr = sig->r; - if (ps != NULL) - *ps = sig->s; -} - -static int -ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) -{ - if (r == NULL || s == NULL) - return 0; - BN_clear_free(sig->r); - BN_clear_free(sig->s); - sig->r = r; - sig->s = s; - - return 1; -} -#endif -#if !defined(LWS_HAVE_BN_bn2binpad) -int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen) -{ - int i; - BN_ULONG l; - - bn_check_top(a); - i = BN_num_bytes(a); - - /* Add leading zeroes if necessary */ - if (tolen > i) { - memset(to, 0, tolen - i); - to += tolen - i; - } - while (i--) { - l = a->d[i / BN_BYTES]; - *(to++) = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff; - } - return tolen; -} -#endif - -const struct lws_ec_curves lws_ec_curves[4] = { - /* - * These are the curves we are willing to use by default... - * - * The 3 recommended+ (P-256) and optional curves in RFC7518 7.6 - * - * Specific keys lengths from RFC8422 p20 - */ - { "P-256", NID_X9_62_prime256v1, 32 }, - { "P-384", NID_secp384r1, 48 }, - { "P-521", NID_secp521r1, 66 }, - - { NULL, 0, 0 } -}; - -static int -lws_genec_eckey_import(int nid, EVP_PKEY *pkey, struct lws_gencrypto_keyelem *el) -{ - EC_KEY *ec = EC_KEY_new_by_curve_name(nid); - BIGNUM *bn_d, *bn_x, *bn_y; - int n; - - if (!ec) - return -1; - - /* - * EC_KEY contains - * - * EC_GROUP * group - * EC_POINT * pub_key - * BIGNUM * priv_key (ie, d) - */ - - bn_x = BN_bin2bn(el[LWS_GENCRYPTO_EC_KEYEL_X].buf, - el[LWS_GENCRYPTO_EC_KEYEL_X].len, NULL); - if (!bn_x) { - lwsl_err("%s: BN_bin2bn (x) fail\n", __func__); - goto bail; - } - bn_y = BN_bin2bn(el[LWS_GENCRYPTO_EC_KEYEL_Y].buf, - el[LWS_GENCRYPTO_EC_KEYEL_Y].len, NULL); - if (!bn_y) { - lwsl_err("%s: BN_bin2bn (y) fail\n", __func__); - goto bail1; - } - - n = EC_KEY_set_public_key_affine_coordinates(ec, bn_x, bn_y); - BN_free(bn_x); - BN_free(bn_y); - if (n != 1) { - lwsl_err("%s: EC_KEY_set_public_key_affine_coordinates fail:\n", - __func__); - lws_tls_err_describe_clear(); - goto bail; - } - - if (el[LWS_GENCRYPTO_EC_KEYEL_D].len) { - bn_d = BN_bin2bn(el[LWS_GENCRYPTO_EC_KEYEL_D].buf, - el[LWS_GENCRYPTO_EC_KEYEL_D].len, NULL); - if (!bn_d) { - lwsl_err("%s: BN_bin2bn (d) fail\n", __func__); - goto bail; - } - - n = EC_KEY_set_private_key(ec, bn_d); - BN_clear_free(bn_d); - if (n != 1) { - lwsl_err("%s: EC_KEY_set_private_key fail\n", __func__); - goto bail; - } - } - - /* explicitly confirm the key pieces are consistent */ - - if (EC_KEY_check_key(ec) != 1) { - lwsl_err("%s: EC_KEY_set_private_key fail\n", __func__); - goto bail; - } - - n = EVP_PKEY_assign_EC_KEY(pkey, ec); - if (n != 1) { - lwsl_err("%s: EVP_PKEY_set1_EC_KEY failed\n", __func__); - return -1; - } - - return 0; - -bail1: - BN_free(bn_x); -bail: - EC_KEY_free(ec); - - return -1; -} - -static int -lws_genec_keypair_import(struct lws_genec_ctx *ctx, - const struct lws_ec_curves *curve_table, - EVP_PKEY_CTX **pctx, struct lws_gencrypto_keyelem *el) -{ - EVP_PKEY *pkey = NULL; - const struct lws_ec_curves *curve; - - if (el[LWS_GENCRYPTO_EC_KEYEL_CRV].len < 4) - return -2; - - curve = lws_genec_curve(curve_table, - (char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf); - if (!curve) - return -3; - - if ((el[LWS_GENCRYPTO_EC_KEYEL_D].len && - el[LWS_GENCRYPTO_EC_KEYEL_D].len != curve->key_bytes) || - el[LWS_GENCRYPTO_EC_KEYEL_X].len != curve->key_bytes || - el[LWS_GENCRYPTO_EC_KEYEL_Y].len != curve->key_bytes) - return -4; - - ctx->has_private = !!el[LWS_GENCRYPTO_EC_KEYEL_D].len; - - pkey = EVP_PKEY_new(); - if (!pkey) - return -7; - - if (lws_genec_eckey_import(curve->tls_lib_nid, pkey, el)) { - lwsl_err("%s: lws_genec_eckey_import fail\n", __func__); - goto bail; - } - - *pctx = EVP_PKEY_CTX_new(pkey, NULL); - EVP_PKEY_free(pkey); - pkey = NULL; - - if (!*pctx) - goto bail; - - return 0; - -bail: - if (pkey) - EVP_PKEY_free(pkey); - - if (*pctx) { - EVP_PKEY_CTX_free(*pctx); - *pctx = NULL; - } - - return -9; -} - -int -lws_genecdh_create(struct lws_genec_ctx *ctx, struct lws_context *context, - const struct lws_ec_curves *curve_table) -{ - ctx->context = context; - ctx->ctx[0] = NULL; - ctx->ctx[1] = NULL; - ctx->curve_table = curve_table; - ctx->genec_alg = LEGENEC_ECDH; - - return 0; -} - -int -lws_genecdsa_create(struct lws_genec_ctx *ctx, struct lws_context *context, - const struct lws_ec_curves *curve_table) -{ - ctx->context = context; - ctx->ctx[0] = NULL; - ctx->ctx[1] = NULL; - ctx->curve_table = curve_table; - ctx->genec_alg = LEGENEC_ECDSA; - - return 0; -} - -int -lws_genecdh_set_key(struct lws_genec_ctx *ctx, struct lws_gencrypto_keyelem *el, - enum enum_lws_dh_side side) -{ - if (ctx->genec_alg != LEGENEC_ECDH) - return -1; - - return lws_genec_keypair_import(ctx, ctx->curve_table, &ctx->ctx[side], el); -} - -int -lws_genecdsa_set_key(struct lws_genec_ctx *ctx, - struct lws_gencrypto_keyelem *el) -{ - if (ctx->genec_alg != LEGENEC_ECDSA) - return -1; - - return lws_genec_keypair_import(ctx, ctx->curve_table, &ctx->ctx[0], el); -} - -static void -lws_genec_keypair_destroy(EVP_PKEY_CTX **pctx) -{ - if (!*pctx) - return; - -// lwsl_err("%p\n", EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(*pctx))); - -// EC_KEY_free(EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(*pctx))); - - EVP_PKEY_CTX_free(*pctx); - *pctx = NULL; -} - -void -lws_genec_destroy(struct lws_genec_ctx *ctx) -{ - if (ctx->ctx[0]) - lws_genec_keypair_destroy(&ctx->ctx[0]); - if (ctx->ctx[1]) - lws_genec_keypair_destroy(&ctx->ctx[1]); -} - -static int -lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side, - const char *curve_name, struct lws_gencrypto_keyelem *el) -{ - const struct lws_ec_curves *curve; - const EC_POINT *pubkey; - EVP_PKEY *pkey = NULL; - int ret = -29, n, m; - BIGNUM *bn[3]; - EC_KEY *ec; - - curve = lws_genec_curve(ctx->curve_table, curve_name); - if (!curve) { - lwsl_err("%s: curve '%s' not supported\n", - __func__, curve_name); - - return -22; - } - - ec = EC_KEY_new_by_curve_name(curve->tls_lib_nid); - if (!ec) { - lwsl_err("%s: unknown nid %d\n", __func__, curve->tls_lib_nid); - return -23; - } - - if (EC_KEY_generate_key(ec) != 1) { - lwsl_err("%s: EC_KEY_generate_key failed\n", __func__); - goto bail; - } - - pkey = EVP_PKEY_new(); - if (!pkey) - goto bail; - - if (EVP_PKEY_set1_EC_KEY(pkey, ec) != 1) { - lwsl_err("%s: EVP_PKEY_assign_EC_KEY failed\n", __func__); - goto bail1; - } - - ctx->ctx[side] = EVP_PKEY_CTX_new(pkey, NULL); - if (!ctx->ctx[side]) { - lwsl_err("%s: EVP_PKEY_CTX_new failed\n", __func__); - goto bail1; - } - - /* - * we need to capture the individual element BIGNUMs into - * lws_gencrypto_keyelem, so they can be serialized, used in jwk etc - */ - - pubkey = EC_KEY_get0_public_key(ec); - if (!pubkey) { - lwsl_err("%s: EC_KEY_get0_public_key failed\n", __func__); - goto bail1; - } - - bn[0] = BN_new(); - bn[1] = (BIGNUM *)EC_KEY_get0_private_key(ec); - bn[2] = BN_new(); - -#if defined(LWS_HAVE_EC_POINT_get_affine_coordinates) - if (EC_POINT_get_affine_coordinates(EC_KEY_get0_group(ec), -#else - if (EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec), -#endif - pubkey, bn[0], bn[2], NULL) != 1) { - lwsl_err("%s: EC_POINT_get_affine_coordinates_GFp failed\n", - __func__); - goto bail2; - } - - el[LWS_GENCRYPTO_EC_KEYEL_CRV].len = (uint32_t)strlen(curve_name) + 1; - el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf = - lws_malloc(el[LWS_GENCRYPTO_EC_KEYEL_CRV].len, "ec"); - if (!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf) { - lwsl_err("%s: OOM\n", __func__); - goto bail2; - } - - strcpy((char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, curve_name); - - for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; - n++) { - el[n].len = curve->key_bytes; - el[n].buf = lws_malloc(curve->key_bytes, "ec"); - if (!el[n].buf) - goto bail2; - - m = BN_bn2binpad(bn[n - 1], el[n].buf, el[n].len); - if ((uint32_t)m != el[n].len) - goto bail2; - } - - ctx->has_private = 1; - - ret = 0; - -bail2: - BN_clear_free(bn[0]); - BN_clear_free(bn[2]); -bail1: - EVP_PKEY_free(pkey); -bail: - EC_KEY_free(ec); - - return ret; -} - -int -lws_genecdh_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side, - const char *curve_name, - struct lws_gencrypto_keyelem *el) -{ - if (ctx->genec_alg != LEGENEC_ECDH) - return -1; - - return lws_genec_new_keypair(ctx, side, curve_name, el); -} - -int -lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name, - struct lws_gencrypto_keyelem *el) -{ - if (ctx->genec_alg != LEGENEC_ECDSA) - return -1; - - return lws_genec_new_keypair(ctx, LDHS_OURS, curve_name, el); -} - -#if 0 -int -lws_genecdsa_hash_sign(struct lws_genec_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, - uint8_t *sig, size_t sig_len) -{ - const EVP_MD *md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type); - EVP_MD_CTX *mdctx = NULL; - - if (ctx->genec_alg != LEGENEC_ECDSA) - return -1; - - if (!md) - return -1; - - mdctx = EVP_MD_CTX_create(); - if (!mdctx) - goto bail; - - if (EVP_DigestSignInit(mdctx, NULL, md, NULL, - EVP_PKEY_CTX_get0_pkey(ctx->ctx))) { - lwsl_err("%s: EVP_DigestSignInit failed\n", __func__); - - goto bail; - } - if (EVP_DigestSignUpdate(mdctx, in, EVP_MD_size(md))) { - lwsl_err("%s: EVP_DigestSignUpdate failed\n", __func__); - - goto bail; - } - if (EVP_DigestSignFinal(mdctx, sig, &sig_len)) { - lwsl_err("%s: EVP_DigestSignFinal failed\n", __func__); - - goto bail; - } - - EVP_MD_CTX_free(mdctx); - - return (int)sig_len; -bail: - if (mdctx) - EVP_MD_CTX_free(mdctx); - - return -1; -} -#endif - -int -lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, int keybits, - uint8_t *sig, size_t sig_len) -{ - int ret = -1, n, keybytes = lws_gencrypto_bits_to_bytes(keybits); - const BIGNUM *r = NULL, *s = NULL; - ECDSA_SIG *ecdsasig; - EC_KEY *eckey; - - if (ctx->genec_alg != LEGENEC_ECDSA) { - lwsl_notice("%s: ctx alg %d\n", __func__, ctx->genec_alg); - return -1; - } - - if (!ctx->has_private) - return -1; - - if ((int)sig_len < keybytes * 2) { - lwsl_notice("%s: sig buff %d < %d\n", __func__, - (int)sig_len, keybytes * 2); - return -1; - } - - eckey = EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(ctx->ctx[0])); - - /* - * The ECDSA P-256 SHA-256 digital signature is generated as follows: - * - * 1. Generate a digital signature of the JWS Signing Input using ECDSA - * P-256 SHA-256 with the desired private key. The output will be - * the pair (R, S), where R and S are 256-bit unsigned integers. - * - * 2. Turn R and S into octet sequences in big-endian order, with each - * array being be 32 octets long. The octet sequence - * representations MUST NOT be shortened to omit any leading zero - * octets contained in the values. - * - * 3. Concatenate the two octet sequences in the order R and then S. - * (Note that many ECDSA implementations will directly produce this - * concatenation as their output.) - * - * 4. The resulting 64-octet sequence is the JWS Signature value. - */ - - ecdsasig = ECDSA_do_sign(in, (int)lws_genhash_size(hash_type), eckey); - EC_KEY_free(eckey); - if (!ecdsasig) { - lwsl_notice("%s: ECDSA_do_sign fail\n", __func__); - goto bail; - } - - ECDSA_SIG_get0(ecdsasig, &r, &s); - - /* - * in the 521-bit case, we have to pad the last byte as it only - * generates 65 bytes - */ - - n = BN_bn2binpad(r, sig, keybytes); - if (n != keybytes) { - lwsl_notice("%s: bignum r fail %d %d\n", __func__, n, keybytes); - goto bail; - } - - n = BN_bn2binpad(s, sig + keybytes, keybytes); - if (n != keybytes) { - lwsl_notice("%s: bignum s fail %d %d\n", __func__, n, keybytes); - goto bail; - } - - ret = 0; - -bail: - if (ecdsasig) - ECDSA_SIG_free(ecdsasig); - - return ret; -} - -/* in is the JWS Signing Input hash */ - -int -lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, int keybits, - const uint8_t *sig, size_t sig_len) -{ - int ret = -1, n, keybytes = lws_gencrypto_bits_to_bytes(keybits), - hlen = (int)lws_genhash_size(hash_type); - ECDSA_SIG *ecsig = ECDSA_SIG_new(); - BIGNUM *r = NULL, *s = NULL; - EC_KEY *eckey; - - if (!ecsig) - return -1; - - if (ctx->genec_alg != LEGENEC_ECDSA) - goto bail; - - if ((int)sig_len != keybytes * 2) { - lwsl_err("%s: sig buf too small %d vs %d\n", __func__, - (int)sig_len, keybytes * 2); - goto bail; - } - /* - * 1. The JWS Signature value MUST be a 64-octet sequence. If it is - * not a 64-octet sequence, the validation has failed. - * - * 2. Split the 64-octet sequence into two 32-octet sequences. The - * first octet sequence represents R and the second S. The values R - * and S are represented as octet sequences using the Integer-to- - * OctetString Conversion defined in Section 2.3.7 of SEC1 [SEC1] - * (in big-endian octet order). - * - * 3. Submit the JWS Signing Input, R, S, and the public key (x, y) to - * the ECDSA P-256 SHA-256 validator. - */ - - r = BN_bin2bn(sig, keybytes, NULL); - if (!r) { - lwsl_err("%s: BN_bin2bn (r) fail\n", __func__); - goto bail; - } - - s = BN_bin2bn(sig + keybytes, keybytes, NULL); - if (!s) { - lwsl_err("%s: BN_bin2bn (s) fail\n", __func__); - goto bail1; - } - - if (ECDSA_SIG_set0(ecsig, r, s) != 1) { - lwsl_err("%s: ECDSA_SIG_set0 fail\n", __func__); - goto bail1; - } - - eckey = EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(ctx->ctx[0])); - - n = ECDSA_do_verify(in, hlen, ecsig, eckey); - EC_KEY_free(eckey); - if (n != 1) { - lwsl_err("%s: ECDSA_do_verify fail\n", __func__); - lws_tls_err_describe_clear(); - goto bail; - } - - ret = 0; - goto bail; - -bail1: - if (r) - BN_free(r); - if (s) - BN_free(s); - -bail: - ECDSA_SIG_free(ecsig); - - return ret; -} - -int -lws_genecdh_compute_shared_secret(struct lws_genec_ctx *ctx, uint8_t *ss, - int *ss_len) -{ - int len, ret = -1; - EC_KEY *eckey[2]; - - if (!ctx->ctx[LDHS_OURS] || !ctx->ctx[LDHS_THEIRS]) { - lwsl_err("%s: both sides must be set up\n", __func__); - - return -1; - } - - eckey[LDHS_OURS] = EVP_PKEY_get1_EC_KEY( - EVP_PKEY_CTX_get0_pkey(ctx->ctx[LDHS_OURS])); - eckey[LDHS_THEIRS] = EVP_PKEY_get1_EC_KEY( - EVP_PKEY_CTX_get0_pkey(ctx->ctx[LDHS_THEIRS])); - - len = (EC_GROUP_get_degree(EC_KEY_get0_group(eckey[LDHS_OURS])) + 7) / 8; - if (len <= *ss_len) { - *ss_len = ECDH_compute_key(ss, len, - EC_KEY_get0_public_key(eckey[LDHS_THEIRS]), - eckey[LDHS_OURS], NULL); - ret = -(*ss_len < 0); - } - - EC_KEY_free(eckey[LDHS_OURS]); - EC_KEY_free(eckey[LDHS_THEIRS]); - - return ret; -} diff -Nru libwebsockets-4.0.20/lib/tls/openssl/lws-genhash.c libwebsockets-2.4.2/lib/tls/openssl/lws-genhash.c --- libwebsockets-4.0.20/lib/tls/openssl/lws-genhash.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/openssl/lws-genhash.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,174 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws_genhash provides a hash / hmac abstraction api in lws that works the - * same whether you are using openssl or mbedtls hash functions underneath. - */ -#include "libwebsockets.h" -#include -/* - * Care: many openssl apis return 1 for success. These are translated to the - * lws convention of 0 for success. - */ - -int -lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type) -{ - ctx->type = type; - ctx->mdctx = EVP_MD_CTX_create(); - if (!ctx->mdctx) - return 1; - - switch (ctx->type) { - case LWS_GENHASH_TYPE_MD5: - ctx->evp_type = EVP_md5(); - break; - case LWS_GENHASH_TYPE_SHA1: - ctx->evp_type = EVP_sha1(); - break; - case LWS_GENHASH_TYPE_SHA256: - ctx->evp_type = EVP_sha256(); - break; - case LWS_GENHASH_TYPE_SHA384: - ctx->evp_type = EVP_sha384(); - break; - case LWS_GENHASH_TYPE_SHA512: - ctx->evp_type = EVP_sha512(); - break; - default: - return 1; - } - - if (EVP_DigestInit_ex(ctx->mdctx, ctx->evp_type, NULL) != 1) { - EVP_MD_CTX_destroy(ctx->mdctx); - - return 1; - } - - return 0; -} - -int -lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len) -{ - if (!len) - return 0; - - return EVP_DigestUpdate(ctx->mdctx, in, len) != 1; -} - -int -lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result) -{ - unsigned int len; - int ret = 0; - - if (result) - ret = EVP_DigestFinal_ex(ctx->mdctx, result, &len) != 1; - - (void)len; - - EVP_MD_CTX_destroy(ctx->mdctx); - - return ret; -} - - -int -lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type, - const uint8_t *key, size_t key_len) -{ -#if defined(LWS_HAVE_HMAC_CTX_new) - ctx->ctx = HMAC_CTX_new(); - if (!ctx->ctx) - return -1; -#else - HMAC_CTX_init(&ctx->ctx); -#endif - - ctx->evp_type = 0; - ctx->type = type; - - switch (type) { - case LWS_GENHMAC_TYPE_SHA256: - ctx->evp_type = EVP_sha256(); - break; - case LWS_GENHMAC_TYPE_SHA384: - ctx->evp_type = EVP_sha384(); - break; - case LWS_GENHMAC_TYPE_SHA512: - ctx->evp_type = EVP_sha512(); - break; - default: - lwsl_err("%s: unknown HMAC type %d\n", __func__, type); - goto bail; - } - -#if defined(LWS_HAVE_HMAC_CTX_new) - if (HMAC_Init_ex(ctx->ctx, key, (int)key_len, ctx->evp_type, NULL) != 1) -#else - if (HMAC_Init_ex(&ctx->ctx, key, (int)key_len, ctx->evp_type, NULL) != 1) -#endif - goto bail; - - return 0; - -bail: -#if defined(LWS_HAVE_HMAC_CTX_new) - HMAC_CTX_free(ctx->ctx); -#endif - - return -1; -} - -int -lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len) -{ -#if defined(LWS_HAVE_HMAC_CTX_new) - if (HMAC_Update(ctx->ctx, in, len) != 1) -#else - if (HMAC_Update(&ctx->ctx, in, len) != 1) -#endif - return -1; - - return 0; -} - -int -lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result) -{ - unsigned int size = (unsigned int)lws_genhmac_size(ctx->type); -#if defined(LWS_HAVE_HMAC_CTX_new) - int n = HMAC_Final(ctx->ctx, result, &size); - - HMAC_CTX_free(ctx->ctx); -#else - int n = HMAC_Final(&ctx->ctx, result, &size); -#endif - - if (n != 1) - return -1; - - return 0; -} - diff -Nru libwebsockets-4.0.20/lib/tls/openssl/lws-genrsa.c libwebsockets-2.4.2/lib/tls/openssl/lws-genrsa.c --- libwebsockets-4.0.20/lib/tls/openssl/lws-genrsa.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/openssl/lws-genrsa.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,407 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * lws_genrsa provides an RSA abstraction api in lws that works the - * same whether you are using openssl or mbedtls crypto functions underneath. - */ -#include "private-lib-core.h" -#include "private-lib-tls-openssl.h" - -/* - * Care: many openssl apis return 1 for success. These are translated to the - * lws convention of 0 for success. - */ - -void -lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el) -{ - lws_gencrypto_destroy_elements(el, LWS_GENCRYPTO_RSA_KEYEL_COUNT); -} - -static int mode_map_crypt[] = { RSA_PKCS1_PADDING, RSA_PKCS1_OAEP_PADDING }, - mode_map_sig[] = { RSA_PKCS1_PADDING, RSA_PKCS1_PSS_PADDING }; - -static int -rsa_pkey_wrap(struct lws_genrsa_ctx *ctx, RSA *rsa) -{ - EVP_PKEY *pkey; - - /* we have the RSA object filled up... wrap in a PKEY */ - - pkey = EVP_PKEY_new(); - if (!pkey) - return 1; - - /* bind the PKEY to the RSA key we just prepared */ - - if (EVP_PKEY_assign_RSA(pkey, rsa) != 1) { - lwsl_err("%s: EVP_PKEY_assign_RSA_KEY failed\n", __func__); - goto bail; - } - - /* pepare our PKEY_CTX with the PKEY */ - - ctx->ctx = EVP_PKEY_CTX_new(pkey, NULL); - EVP_PKEY_free(pkey); - pkey = NULL; - if (!ctx->ctx) - goto bail; - - return 0; - -bail: - if (pkey) - EVP_PKEY_free(pkey); - - return 1; -} - -int -lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el, - struct lws_context *context, enum enum_genrsa_mode mode, - enum lws_genhash_types oaep_hashid) -{ - int n; - - memset(ctx, 0, sizeof(*ctx)); - ctx->context = context; - ctx->mode = mode; - - /* Step 1: - * - * convert the MPI for e and n to OpenSSL BIGNUMs - */ - - for (n = 0; n < 5; n++) { - ctx->bn[n] = BN_bin2bn(el[n].buf, el[n].len, NULL); - if (!ctx->bn[n]) { - lwsl_notice("mpi load failed\n"); - goto bail; - } - } - - /* Step 2: - * - * assemble the OpenSSL RSA from the BIGNUMs - */ - - ctx->rsa = RSA_new(); - if (!ctx->rsa) { - lwsl_notice("Failed to create RSA\n"); - goto bail; - } - -#if defined(LWS_HAVE_RSA_SET0_KEY) - if (RSA_set0_key(ctx->rsa, ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_N], - ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_E], - ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_D]) != 1) { - lwsl_notice("RSA_set0_key failed\n"); - goto bail; - } - RSA_set0_factors(ctx->rsa, ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_P], - ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_Q]); -#else - ctx->rsa->e = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_E]; - ctx->rsa->n = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_N]; - ctx->rsa->d = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_D]; - ctx->rsa->p = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_P]; - ctx->rsa->q = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_Q]; -#endif - - if (!rsa_pkey_wrap(ctx, ctx->rsa)) - return 0; - -bail: - for (n = 0; n < 5; n++) - if (ctx->bn[n]) { - BN_clear_free(ctx->bn[n]); - ctx->bn[n] = NULL; - } - - if (ctx->rsa) { - RSA_free(ctx->rsa); - ctx->rsa = NULL; - } - - return 1; -} - -int -lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx, - enum enum_genrsa_mode mode, struct lws_gencrypto_keyelem *el, - int bits) -{ - BIGNUM *bn; - int n; - - memset(ctx, 0, sizeof(*ctx)); - ctx->context = context; - ctx->mode = mode; - - ctx->rsa = RSA_new(); - if (!ctx->rsa) { - lwsl_notice("Failed to create RSA\n"); - return -1; - } - - bn = BN_new(); - if (!bn) - goto cleanup_1; - if (BN_set_word(bn, RSA_F4) != 1) { - BN_free(bn); - goto cleanup_1; - } - - n = RSA_generate_key_ex(ctx->rsa, bits, bn, NULL); - BN_clear_free(bn); - if (n != 1) - goto cleanup_1; - -#if defined(LWS_HAVE_RSA_SET0_KEY) - { - const BIGNUM *mpi[5]; - - RSA_get0_key(ctx->rsa, &mpi[LWS_GENCRYPTO_RSA_KEYEL_N], - &mpi[LWS_GENCRYPTO_RSA_KEYEL_E], &mpi[LWS_GENCRYPTO_RSA_KEYEL_D]); - RSA_get0_factors(ctx->rsa, &mpi[LWS_GENCRYPTO_RSA_KEYEL_P], - &mpi[LWS_GENCRYPTO_RSA_KEYEL_Q]); -#else - { - BIGNUM *mpi[5] = { ctx->rsa->e, ctx->rsa->n, ctx->rsa->d, - ctx->rsa->p, ctx->rsa->q, }; -#endif - for (n = 0; n < 5; n++) - if (BN_num_bytes(mpi[n])) { - el[n].buf = lws_malloc( - BN_num_bytes(mpi[n]), "genrsakey"); - if (!el[n].buf) - goto cleanup; - el[n].len = BN_num_bytes(mpi[n]); - BN_bn2bin(mpi[n], el[n].buf); - } - } - - if (!rsa_pkey_wrap(ctx, ctx->rsa)) - return 0; - -cleanup: - for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++) - if (el[n].buf) - lws_free_set_NULL(el[n].buf); -cleanup_1: - RSA_free(ctx->rsa); - ctx->rsa = NULL; - - return -1; -} - -/* - * in_len must be less than RSA_size(rsa) - 11 for the PKCS #1 v1.5 - * based padding modes - */ - -int -lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out) -{ - int n = RSA_public_encrypt((int)in_len, in, out, ctx->rsa, - mode_map_crypt[ctx->mode]); - if (n < 0) { - lwsl_err("%s: RSA_public_encrypt failed\n", __func__); - lws_tls_err_describe_clear(); - return -1; - } - - return n; -} - -int -lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out) -{ - int n = RSA_private_encrypt((int)in_len, in, out, ctx->rsa, - mode_map_crypt[ctx->mode]); - if (n < 0) { - lwsl_err("%s: RSA_private_encrypt failed\n", __func__); - lws_tls_err_describe_clear(); - return -1; - } - - return n; -} - -int -lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out, size_t out_max) -{ - int n = RSA_public_decrypt((int)in_len, in, out, ctx->rsa, - mode_map_crypt[ctx->mode]); - if (n < 0) { - lwsl_err("%s: RSA_public_decrypt failed\n", __func__); - return -1; - } - - return n; -} - -int -lws_genrsa_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, - size_t in_len, uint8_t *out, size_t out_max) -{ - int n = RSA_private_decrypt((int)in_len, in, out, ctx->rsa, - mode_map_crypt[ctx->mode]); - if (n < 0) { - lwsl_err("%s: RSA_private_decrypt failed\n", __func__); - lws_tls_err_describe_clear(); - return -1; - } - - return n; -} - -int -lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, const uint8_t *sig, - size_t sig_len) -{ - int n = lws_gencrypto_openssl_hash_to_NID(hash_type), - h = (int)lws_genhash_size(hash_type); - const EVP_MD *md = NULL; - - if (n < 0) - return -1; - - switch(ctx->mode) { - case LGRSAM_PKCS1_1_5: - n = RSA_verify(n, in, h, (uint8_t *)sig, (int)sig_len, ctx->rsa); - break; - case LGRSAM_PKCS1_OAEP_PSS: - md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type); - if (!md) - return -1; - -#if defined(LWS_HAVE_RSA_verify_pss_mgf1) - n = RSA_verify_pss_mgf1(ctx->rsa, in, h, md, NULL, -1, - (uint8_t *)sig, -#else - n = RSA_verify_PKCS1_PSS(ctx->rsa, in, md, (uint8_t *)sig, -#endif - (int)sig_len); - break; - default: - return -1; - } - - if (n != 1) { - lwsl_notice("%s: fail\n", __func__); - lws_tls_err_describe_clear(); - - return -1; - } - - return 0; -} - -int -lws_genrsa_hash_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in, - enum lws_genhash_types hash_type, uint8_t *sig, - size_t sig_len) -{ - int n = lws_gencrypto_openssl_hash_to_NID(hash_type), - h = (int)lws_genhash_size(hash_type); - unsigned int used = 0; - EVP_MD_CTX *mdctx = NULL; - const EVP_MD *md = NULL; - - if (n < 0) - return -1; - - switch(ctx->mode) { - case LGRSAM_PKCS1_1_5: - if (RSA_sign(n, in, h, sig, &used, ctx->rsa) != 1) { - lwsl_err("%s: RSA_sign failed\n", __func__); - - goto bail; - } - break; - - case LGRSAM_PKCS1_OAEP_PSS: - - md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type); - if (!md) - return -1; - - if (EVP_PKEY_CTX_set_rsa_padding(ctx->ctx, - mode_map_sig[ctx->mode]) != 1) { - lwsl_err("%s: set_rsa_padding failed\n", __func__); - - goto bail; - } - - mdctx = EVP_MD_CTX_create(); - if (!mdctx) - goto bail; - - if (EVP_DigestSignInit(mdctx, NULL, md, NULL, - EVP_PKEY_CTX_get0_pkey(ctx->ctx))) { - lwsl_err("%s: EVP_DigestSignInit failed\n", __func__); - - goto bail; - } - if (EVP_DigestSignUpdate(mdctx, in, EVP_MD_size(md))) { - lwsl_err("%s: EVP_DigestSignUpdate failed\n", __func__); - - goto bail; - } - if (EVP_DigestSignFinal(mdctx, sig, &sig_len)) { - lwsl_err("%s: EVP_DigestSignFinal failed\n", __func__); - - goto bail; - } - EVP_MD_CTX_free(mdctx); - used = (int)sig_len; - break; - - default: - return -1; - } - - return used; - -bail: - if (mdctx) - EVP_MD_CTX_free(mdctx); - - return -1; -} - -void -lws_genrsa_destroy(struct lws_genrsa_ctx *ctx) -{ - if (!ctx->ctx) - return; - - EVP_PKEY_CTX_free(ctx->ctx); - ctx->ctx = NULL; - ctx->rsa = NULL; -} diff -Nru libwebsockets-4.0.20/lib/tls/openssl/openssl-client.c libwebsockets-2.4.2/lib/tls/openssl/openssl-client.c --- libwebsockets-4.0.20/lib/tls/openssl/openssl-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/openssl/openssl-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,838 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 - -#include "private-lib-core.h" -#include "private-lib-tls-openssl.h" - -/* - * Care: many openssl apis return 1 for success. These are translated to the - * lws convention of 0 for success. - */ - -int lws_openssl_describe_cipher(struct lws *wsi); - -extern int openssl_websocket_private_data_index, - openssl_SSL_CTX_private_data_index; - -#if !defined(USE_WOLFSSL) - -static int -OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) -{ - SSL *ssl; - int n; - struct lws *wsi; - - /* keep old behaviour accepting self-signed server certs */ - if (!preverify_ok) { - int err = X509_STORE_CTX_get_error(x509_ctx); - - if (err != X509_V_OK) { - ssl = X509_STORE_CTX_get_ex_data(x509_ctx, - SSL_get_ex_data_X509_STORE_CTX_idx()); - wsi = SSL_get_ex_data(ssl, - openssl_websocket_private_data_index); - if (!wsi) { - lwsl_err("%s: can't get wsi from ssl privdata\n", - __func__); - - return 0; - } - - if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || - err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && - wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED) { - lwsl_notice("accepting self-signed " - "certificate (verify_callback)\n"); - X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); - return 1; // ok - } else if ((err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY || - err == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE) && - wsi->tls.use_ssl & LCCSCF_ALLOW_INSECURE) { - lwsl_notice("accepting non-trusted certificate\n"); - X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); - return 1; /* ok */ - } else if ((err == X509_V_ERR_CERT_NOT_YET_VALID || - err == X509_V_ERR_CERT_HAS_EXPIRED) && - wsi->tls.use_ssl & LCCSCF_ALLOW_EXPIRED) { - if (err == X509_V_ERR_CERT_NOT_YET_VALID) - lwsl_notice("accepting not yet valid " - "certificate (verify_" - "callback)\n"); - else if (err == X509_V_ERR_CERT_HAS_EXPIRED) - lwsl_notice("accepting expired " - "certificate (verify_" - "callback)\n"); - X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); - return 1; // ok - } - } - } - - ssl = X509_STORE_CTX_get_ex_data(x509_ctx, - SSL_get_ex_data_X509_STORE_CTX_idx()); - wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); - if (!wsi) { - lwsl_err("%s: can't get wsi from ssl privdata\n", __func__); - - return 0; - } - - n = lws_get_context_protocol(wsi->context, 0).callback(wsi, - LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION, - x509_ctx, ssl, preverify_ok); - - /* keep old behaviour if something wrong with server certs */ - /* if ssl error is overruled in callback and cert is ok, - * X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); must be set and - * return value is 0 from callback */ - if (!preverify_ok) { - int err = X509_STORE_CTX_get_error(x509_ctx); - - if (err != X509_V_OK) { - /* cert validation error was not handled in callback */ - int depth = X509_STORE_CTX_get_error_depth(x509_ctx); - const char *msg = X509_verify_cert_error_string(err); - - lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;" - "depth=%d)\n", msg, preverify_ok, err, depth); - - return preverify_ok; // not ok - } - } - /* - * convert callback return code from 0 = OK to verify callback - * return value 1 = OK - */ - return !n; -} -#endif - - -int -lws_ssl_client_bio_create(struct lws *wsi) -{ - char hostname[128], *p; -#if defined(LWS_HAVE_SSL_set_alpn_protos) && \ - defined(LWS_HAVE_SSL_get0_alpn_selected) - uint8_t openssl_alpn[40]; - const char *alpn_comma = wsi->context->tls.alpn_default; - int n; -#endif - - if (wsi->stash) { - lws_strncpy(hostname, wsi->stash->cis[CIS_HOST], sizeof(hostname)); -#if defined(LWS_HAVE_SSL_set_alpn_protos) && \ - defined(LWS_HAVE_SSL_get0_alpn_selected) - alpn_comma = wsi->stash->cis[CIS_ALPN]; -#endif - } else { -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - if (lws_hdr_copy(wsi, hostname, sizeof(hostname), - _WSI_TOKEN_CLIENT_HOST) <= 0) -#endif - { - lwsl_err("%s: Unable to get hostname\n", __func__); - - return -1; - } - } - - /* - * remove any :port part on the hostname... necessary for network - * connection but typical certificates do not contain it - */ - p = hostname; - while (*p) { - if (*p == ':') { - *p = '\0'; - break; - } - p++; - } - - wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_client_ctx); - if (!wsi->tls.ssl) { - lwsl_err("SSL_new failed: %s\n", - ERR_error_string(lws_ssl_get_error(wsi, 0), NULL)); - lws_tls_err_describe_clear(); - return -1; - } - -#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) - if (wsi->vhost->tls.ssl_info_event_mask) - SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback); -#endif - -#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host - if (!(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { - X509_VERIFY_PARAM *param = SSL_get0_param(wsi->tls.ssl); - -#if !defined(USE_WOLFSSL) - /* Enable automatic hostname checks */ - X509_VERIFY_PARAM_set_hostflags(param, - X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); -#endif - /* Handle the case where the hostname is an IP address */ - if (!X509_VERIFY_PARAM_set1_ip_asc(param, hostname)) - X509_VERIFY_PARAM_set1_host(param, hostname, - strnlen(hostname, sizeof(hostname))); - } -#else - if (!(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { - lwsl_err("%s: your tls lib is too old to have " - "X509_VERIFY_PARAM_set1_host, failing all client tls\n", - __func__); - return -1; - } -#endif - -#if !defined(USE_WOLFSSL) -#ifndef USE_OLD_CYASSL - /* OpenSSL_client_verify_callback will be called @ SSL_connect() */ - SSL_set_verify(wsi->tls.ssl, SSL_VERIFY_PEER, - OpenSSL_client_verify_callback); -#endif -#endif - -#if !defined(USE_WOLFSSL) - SSL_set_mode(wsi->tls.ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); -#endif - /* - * use server name indication (SNI), if supported, - * when establishing connection - */ -#ifdef USE_WOLFSSL -#ifdef USE_OLD_CYASSL -#ifdef CYASSL_SNI_HOST_NAME - CyaSSL_UseSNI(wsi->tls.ssl, CYASSL_SNI_HOST_NAME, hostname, - strlen(hostname)); -#endif -#else -#ifdef WOLFSSL_SNI_HOST_NAME - wolfSSL_UseSNI(wsi->tls.ssl, WOLFSSL_SNI_HOST_NAME, hostname, - strlen(hostname)); -#endif -#endif -#else -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - SSL_set_tlsext_host_name(wsi->tls.ssl, hostname); -#endif -#endif - -#ifdef USE_WOLFSSL - /* - * wolfSSL/CyaSSL does certificate verification differently - * from OpenSSL. - * If we should ignore the certificate, we need to set - * this before SSL_new and SSL_connect is called. - * Otherwise the connect will simply fail with error code -155 - */ -#ifdef USE_OLD_CYASSL - if (wsi->tls.use_ssl == 2) - CyaSSL_set_verify(wsi->tls.ssl, SSL_VERIFY_NONE, NULL); -#else - if (wsi->tls.use_ssl == 2) - wolfSSL_set_verify(wsi->tls.ssl, SSL_VERIFY_NONE, NULL); -#endif -#endif /* USE_WOLFSSL */ - - wsi->tls.client_bio = BIO_new_socket((int)(lws_intptr_t)wsi->desc.sockfd, - BIO_NOCLOSE); - SSL_set_bio(wsi->tls.ssl, wsi->tls.client_bio, wsi->tls.client_bio); - -#ifdef USE_WOLFSSL -#ifdef USE_OLD_CYASSL - CyaSSL_set_using_nonblock(wsi->tls.ssl, 1); -#else - wolfSSL_set_using_nonblock(wsi->tls.ssl, 1); -#endif -#else - BIO_set_nbio(wsi->tls.client_bio, 1); /* nonblocking */ -#endif - -#if defined(LWS_HAVE_SSL_set_alpn_protos) && \ - defined(LWS_HAVE_SSL_get0_alpn_selected) - if (wsi->vhost->tls.alpn) - alpn_comma = wsi->vhost->tls.alpn; - if (wsi->stash) - alpn_comma = wsi->stash->cis[CIS_ALPN]; -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - if (lws_hdr_copy(wsi, hostname, sizeof(hostname), - _WSI_TOKEN_CLIENT_ALPN) > 0) - alpn_comma = hostname; -#endif - - lwsl_info("%s client conn using alpn list '%s'\n", wsi->role_ops->name, alpn_comma); - - n = lws_alpn_comma_to_openssl(alpn_comma, openssl_alpn, - sizeof(openssl_alpn) - 1); - - SSL_set_alpn_protos(wsi->tls.ssl, openssl_alpn, n); -#endif - - SSL_set_ex_data(wsi->tls.ssl, openssl_websocket_private_data_index, - wsi); - - if (wsi->sys_tls_client_cert) { - lws_system_blob_t *b = lws_system_get_blob(wsi->context, - LWS_SYSBLOB_TYPE_CLIENT_CERT_DER, - wsi->sys_tls_client_cert - 1); - const uint8_t *data; - size_t size; - - if (!b) - goto no_client_cert; - - /* - * Set up the per-connection client cert - */ - - size = lws_system_blob_get_size(b); - if (!size) - goto no_client_cert; - - if (lws_system_blob_get_single_ptr(b, &data)) - goto no_client_cert; - - if (SSL_use_certificate_ASN1(wsi->tls.ssl, -#if defined(USE_WOLFSSL) - (unsigned char *) -#endif - data, (int)size) != 1) { - lwsl_err("%s: use_certificate failed\n", __func__); - lws_tls_err_describe_clear(); - goto no_client_cert; - } - - b = lws_system_get_blob(wsi->context, - LWS_SYSBLOB_TYPE_CLIENT_KEY_DER, - wsi->sys_tls_client_cert - 1); - if (!b) - goto no_client_cert; - - size = lws_system_blob_get_size(b); - if (!size) - goto no_client_cert; - - if (lws_system_blob_get_single_ptr(b, &data)) - goto no_client_cert; - - if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, wsi->tls.ssl, -#if defined(USE_WOLFSSL) - (unsigned char *) -#endif - - data, (int)size) != 1 && - SSL_use_PrivateKey_ASN1(EVP_PKEY_EC, wsi->tls.ssl, -#if defined(USE_WOLFSSL) - (unsigned char *) -#endif - data, (int)size) != 1) { - lwsl_err("%s: use_privkey failed\n", __func__); - lws_tls_err_describe_clear(); - goto no_client_cert; - } - - if (SSL_check_private_key(wsi->tls.ssl) != 1) { - lwsl_err("Private SSL key doesn't match cert\n"); - lws_tls_err_describe_clear(); - return 1; - } - - lwsl_notice("%s: set system client cert %u\n", __func__, - wsi->sys_tls_client_cert - 1); - } - - return 0; - -no_client_cert: - lwsl_err("%s: unable to set up system client cert %d\n", __func__, - wsi->sys_tls_client_cert - 1); - - return 1; -} - -enum lws_ssl_capable_status -lws_tls_client_connect(struct lws *wsi) -{ -#if defined(LWS_HAVE_SSL_set_alpn_protos) && \ - defined(LWS_HAVE_SSL_get0_alpn_selected) - const unsigned char *prot; - char a[32]; - unsigned int len; -#endif - int m, n; -#if defined(WIN32) || (_LWS_ENABLED_LOGS & LLL_INFO) - int en; -#endif - - errno = 0; - ERR_clear_error(); - n = SSL_connect(wsi->tls.ssl); -#if defined(WIN32) || (_LWS_ENABLED_LOGS & LLL_INFO) - en = errno; -#endif - m = lws_ssl_get_error(wsi, n); - - if (m == SSL_ERROR_SYSCALL -#if defined(WIN32) - && en -#endif - ) { -#if defined(WIN32) || (_LWS_ENABLED_LOGS & LLL_INFO) - lwsl_info("%s: n %d, m %d, errno %d\n", __func__, n, m, en); -#endif - return LWS_SSL_CAPABLE_ERROR; - } - - if (m == SSL_ERROR_SSL) - return LWS_SSL_CAPABLE_ERROR; - - if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) - return LWS_SSL_CAPABLE_MORE_SERVICE_READ; - - if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) - return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE; - - if (n == 1 || m == SSL_ERROR_SYSCALL) { -#if defined(LWS_HAVE_SSL_set_alpn_protos) && \ - defined(LWS_HAVE_SSL_get0_alpn_selected) - SSL_get0_alpn_selected(wsi->tls.ssl, &prot, &len); - - if (len >= sizeof(a)) - len = sizeof(a) - 1; - memcpy(a, (const char *)prot, len); - a[len] = '\0'; - - lws_role_call_alpn_negotiated(wsi, (const char *)a); -#endif - lwsl_info("client connect OK\n"); - lws_openssl_describe_cipher(wsi); - return LWS_SSL_CAPABLE_DONE; - } - - if (!n) /* we don't know what he wants, but he says to retry */ - return LWS_SSL_CAPABLE_MORE_SERVICE; - - return LWS_SSL_CAPABLE_ERROR; -} - -int -lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len) -{ -#if !defined(USE_WOLFSSL) - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - char *p = (char *)&pt->serv_buf[0]; - char *sb = p; - int n; - - errno = 0; - ERR_clear_error(); - n = SSL_get_verify_result(wsi->tls.ssl); - - lwsl_debug("get_verify says %d\n", n); - - if (n == X509_V_OK) - return 0; - - if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || - n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && - (wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED)) { - lwsl_info("accepting self-signed certificate\n"); - - return 0; - } - if ((n == X509_V_ERR_CERT_NOT_YET_VALID || - n == X509_V_ERR_CERT_HAS_EXPIRED) && - (wsi->tls.use_ssl & LCCSCF_ALLOW_EXPIRED)) { - lwsl_info("accepting expired certificate\n"); - return 0; - } - if (n == X509_V_ERR_CERT_NOT_YET_VALID) { - lwsl_info("Cert is from the future... " - "probably our clock... accepting...\n"); - return 0; - } - lws_snprintf(ebuf, ebuf_len, - "server's cert didn't look good, X509_V_ERR = %d: %s\n", - n, ERR_error_string(n, sb)); - lwsl_info("%s\n", ebuf); - lws_tls_err_describe_clear(); - - return -1; - -#else /* USE_WOLFSSL */ - return 0; -#endif -} - -int -lws_tls_client_vhost_extra_cert_mem(struct lws_vhost *vh, - const uint8_t *der, size_t der_len) -{ - X509_STORE *st; - X509 *x = d2i_X509(NULL, &der, (long)der_len); - int n; - - if (!x) { - lwsl_err("%s: Failed to load DER\n", __func__); - lws_tls_err_describe_clear(); - return 1; - } - - st = SSL_CTX_get_cert_store(vh->tls.ssl_client_ctx); - if (!st) { - lwsl_err("%s: failed to get cert store\n", __func__); - X509_free(x); - return 1; - } - - n = X509_STORE_add_cert(st, x); - if (n != 1) - lwsl_err("%s: failed to add cert\n", __func__); - - X509_free(x); - - return n != 1; -} - -int -lws_tls_client_create_vhost_context(struct lws_vhost *vh, - const struct lws_context_creation_info *info, - const char *cipher_list, - const char *ca_filepath, - const void *ca_mem, - unsigned int ca_mem_len, - const char *cert_filepath, - const void *cert_mem, - unsigned int cert_mem_len, - const char *private_key_filepath) -{ - struct lws_tls_client_reuse *tcr; - X509_STORE *x509_store; - unsigned long error; - SSL_METHOD *method; - EVP_MD_CTX *mdctx; - unsigned int len; - uint8_t hash[32]; - X509 *client_CA; - char c; - int n; - - /* basic openssl init already happened in context init */ - - /* choose the most recent spin of the api */ -#if defined(LWS_HAVE_TLS_CLIENT_METHOD) - method = (SSL_METHOD *)TLS_client_method(); -#elif defined(LWS_HAVE_TLSV1_2_CLIENT_METHOD) - method = (SSL_METHOD *)TLSv1_2_client_method(); -#else - method = (SSL_METHOD *)SSLv23_client_method(); -#endif - - if (!method) { - error = ERR_get_error(); - lwsl_err("problem creating ssl method %lu: %s\n", - error, ERR_error_string(error, - (char *)vh->context->pt[0].serv_buf)); - return 1; - } - - /* - * OpenSSL client contexts are quite expensive, because they bring in - * the system certificate bundle for each one. So if you have multiple - * vhosts, each with a client context, it can add up to several - * megabytes of heap. In the case the client contexts are configured - * identically, they could perfectly well have shared just the one. - * - * For that reason, use a hash to fingerprint the context configuration - * and prefer to reuse an existing one with the same fingerprint if - * possible. - */ - - mdctx = EVP_MD_CTX_create(); - if (!mdctx) - return 1; - - if (EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) != 1) { - EVP_MD_CTX_destroy(mdctx); - - return 1; - } - - if (info->ssl_client_options_set) - EVP_DigestUpdate(mdctx, &info->ssl_client_options_set, - sizeof(info->ssl_client_options_set)); - -#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL) - if (info->ssl_client_options_clear) - EVP_DigestUpdate(mdctx, &info->ssl_client_options_clear, - sizeof(info->ssl_client_options_clear)); -#endif - - if (cipher_list) - EVP_DigestUpdate(mdctx, cipher_list, strlen(cipher_list)); - -#if defined(LWS_HAVE_SSL_CTX_set_ciphersuites) - if (info->client_tls_1_3_plus_cipher_list) - EVP_DigestUpdate(mdctx, info->client_tls_1_3_plus_cipher_list, - strlen(info->client_tls_1_3_plus_cipher_list)); -#endif - - if (!lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) { - c = 1; - EVP_DigestUpdate(mdctx, &c, 1); - } - - if (ca_filepath) - EVP_DigestUpdate(mdctx, ca_filepath, strlen(ca_filepath)); - - if (cert_filepath) - EVP_DigestUpdate(mdctx, cert_filepath, strlen(cert_filepath)); - - if (private_key_filepath) - EVP_DigestUpdate(mdctx, private_key_filepath, - strlen(private_key_filepath)); - if (ca_mem && ca_mem_len) - EVP_DigestUpdate(mdctx, ca_mem, ca_mem_len); - - if (cert_mem && cert_mem_len) - EVP_DigestUpdate(mdctx, cert_mem, cert_mem_len); - - len = sizeof(hash); - EVP_DigestFinal_ex(mdctx, hash, &len); - EVP_MD_CTX_destroy(mdctx); - - /* look for existing client context with same config already */ - - lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, - lws_dll2_get_head(&vh->context->tls.cc_owner)) { - tcr = lws_container_of(p, struct lws_tls_client_reuse, cc_list); - - if (!memcmp(hash, tcr->hash, len)) { - - /* it's a match */ - - tcr->refcount++; - vh->tls.ssl_client_ctx = tcr->ssl_client_ctx; - - lwsl_info("%s: vh %s: reusing client ctx %d: use %d\n", - __func__, vh->name, tcr->index, - tcr->refcount); - - return 0; - } - } lws_end_foreach_dll_safe(p, tp); - - /* no existing one the same... create new client SSL_CTX */ - - errno = 0; - ERR_clear_error(); - vh->tls.ssl_client_ctx = SSL_CTX_new(method); - if (!vh->tls.ssl_client_ctx) { - error = ERR_get_error(); - lwsl_err("problem creating ssl context %lu: %s\n", - error, ERR_error_string(error, - (char *)vh->context->pt[0].serv_buf)); - return 1; - } - - tcr = lws_zalloc(sizeof(*tcr), "client ctx tcr"); - if (!tcr) { - SSL_CTX_free(vh->tls.ssl_client_ctx); - return 1; - } - - tcr->ssl_client_ctx = vh->tls.ssl_client_ctx; - tcr->refcount = 1; - memcpy(tcr->hash, hash, len); - tcr->index = vh->context->tls.count_client_contexts++; - lws_dll2_add_head(&tcr->cc_list, &vh->context->tls.cc_owner); - - lwsl_info("%s: vh %s: created new client ctx %d\n", __func__, - vh->name, tcr->index); - - /* bind the tcr to the client context */ - - SSL_CTX_set_ex_data(vh->tls.ssl_client_ctx, - openssl_SSL_CTX_private_data_index, - (char *)tcr); - -#ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(vh->tls.ssl_client_ctx, SSL_OP_NO_COMPRESSION); -#endif - - SSL_CTX_set_options(vh->tls.ssl_client_ctx, - SSL_OP_CIPHER_SERVER_PREFERENCE); - - SSL_CTX_set_mode(vh->tls.ssl_client_ctx, - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | - SSL_MODE_RELEASE_BUFFERS); - - if (info->ssl_client_options_set) - SSL_CTX_set_options(vh->tls.ssl_client_ctx, - info->ssl_client_options_set); - - /* SSL_clear_options introduced in 0.9.8m */ -#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL) - if (info->ssl_client_options_clear) - SSL_CTX_clear_options(vh->tls.ssl_client_ctx, - info->ssl_client_options_clear); -#endif - - if (cipher_list) - SSL_CTX_set_cipher_list(vh->tls.ssl_client_ctx, cipher_list); - -#if defined(LWS_HAVE_SSL_CTX_set_ciphersuites) - if (info->client_tls_1_3_plus_cipher_list) - SSL_CTX_set_ciphersuites(vh->tls.ssl_client_ctx, - info->client_tls_1_3_plus_cipher_list); -#endif - -#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS - if (!lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) - /* loads OS default CA certs */ - SSL_CTX_set_default_verify_paths(vh->tls.ssl_client_ctx); -#endif - - /* openssl init for cert verification (for client sockets) */ - if (!ca_filepath && (!ca_mem || !ca_mem_len)) { - if (!SSL_CTX_load_verify_locations( - vh->tls.ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS)) - lwsl_err("Unable to load SSL Client certs from %s " - "(set by LWS_OPENSSL_CLIENT_CERTS) -- " - "client ssl isn't going to work\n", - LWS_OPENSSL_CLIENT_CERTS); - } else if (ca_filepath) { - if (!SSL_CTX_load_verify_locations( - vh->tls.ssl_client_ctx, ca_filepath, NULL)) { - lwsl_err( - "Unable to load SSL Client certs " - "file from %s -- client ssl isn't " - "going to work\n", ca_filepath); - lws_tls_err_describe_clear(); - } - else - lwsl_info("loaded ssl_ca_filepath\n"); - } else { - - lws_filepos_t amount = 0; - uint8_t *up1; - const uint8_t *up; - - if (lws_tls_alloc_pem_to_der_file(vh->context, NULL, ca_mem, - ca_mem_len, &up1, - &amount)) { - lwsl_err("%s: Unable to decode x.509 mem\n", __func__); - lwsl_hexdump_notice(ca_mem, ca_mem_len); - return 1; - } - - up = up1; - client_CA = d2i_X509(NULL, &up, (long)amount); - if (!client_CA) { - lwsl_err("%s: d2i_X509 failed\n", __func__); - lwsl_hexdump_notice(up1, (size_t)amount); - lws_tls_err_describe_clear(); - } else { - x509_store = X509_STORE_new(); - if (!X509_STORE_add_cert(x509_store, client_CA)) { - X509_STORE_free(x509_store); - lwsl_err("Unable to load SSL Client certs from " - "ssl_ca_mem -- client ssl isn't going to " - "work\n"); - lws_tls_err_describe_clear(); - } else { - /* it doesn't increment x509_store ref counter */ - SSL_CTX_set_cert_store(vh->tls.ssl_client_ctx, - x509_store); - lwsl_info("loaded ssl_ca_mem\n"); - } - } - if (client_CA) - X509_free(client_CA); - lws_free(up1); - // lws_tls_client_vhost_extra_cert_mem(vh, ca_mem, ca_mem_len); - } - - /* - * callback allowing user code to load extra verification certs - * helping the client to verify server identity - */ - - /* support for client-side certificate authentication */ - if (cert_filepath) { - if (lws_tls_use_any_upgrade_check_extant(cert_filepath) != - LWS_TLS_EXTANT_YES && - (info->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) - return 0; - - lwsl_notice("%s: doing cert filepath %s\n", __func__, - cert_filepath); - n = SSL_CTX_use_certificate_chain_file(vh->tls.ssl_client_ctx, - cert_filepath); - if (n < 1) { - lwsl_err("problem %d getting cert '%s'\n", n, - cert_filepath); - lws_tls_err_describe_clear(); - return 1; - } - lwsl_notice("Loaded client cert %s\n", cert_filepath); - } else if (cert_mem && cert_mem_len) { - n = SSL_CTX_use_certificate_ASN1(vh->tls.ssl_client_ctx, - cert_mem_len, cert_mem); - if (n < 1) { - lwsl_err("%s: problem interpreting client cert\n", - __func__); - lws_tls_err_describe_clear(); - return 1; - } - } - if (private_key_filepath) { - lwsl_notice("%s: doing private key filepath\n", __func__); - lws_ssl_bind_passphrase(vh->tls.ssl_client_ctx, 1, info); - /* set the private key from KeyFile */ - if (SSL_CTX_use_PrivateKey_file(vh->tls.ssl_client_ctx, - private_key_filepath, SSL_FILETYPE_PEM) != 1) { - lwsl_err("use_PrivateKey_file '%s'\n", - private_key_filepath); - lws_tls_err_describe_clear(); - return 1; - } - lwsl_notice("Loaded client cert private key %s\n", - private_key_filepath); - - /* verify private key */ - if (!SSL_CTX_check_private_key(vh->tls.ssl_client_ctx)) { - lwsl_err("Private SSL key doesn't match cert\n"); - return 1; - } - } - - return 0; -} - - diff -Nru libwebsockets-4.0.20/lib/tls/openssl/openssl-server.c libwebsockets-2.4.2/lib/tls/openssl/openssl-server.c --- libwebsockets-4.0.20/lib/tls/openssl/openssl-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/openssl/openssl-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1036 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -/* - * Care: many openssl apis return 1 for success. These are translated to the - * lws convention of 0 for success. - */ - -extern int openssl_websocket_private_data_index, - openssl_SSL_CTX_private_data_index; - -int lws_openssl_describe_cipher(struct lws *wsi); - -static int -OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) -{ - SSL *ssl; - int n; - struct lws *wsi; - union lws_tls_cert_info_results ir; - X509 *topcert = X509_STORE_CTX_get_current_cert(x509_ctx); - - ssl = X509_STORE_CTX_get_ex_data(x509_ctx, - SSL_get_ex_data_X509_STORE_CTX_idx()); - - /* - * !!! nasty openssl requires the index to come as a library-scope - * static - */ - wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); - - n = lws_tls_openssl_cert_info(topcert, LWS_TLS_CERT_INFO_COMMON_NAME, - &ir, sizeof(ir.ns.name)); - if (!n) - lwsl_info("%s: client cert CN '%s'\n", __func__, ir.ns.name); - else - lwsl_info("%s: couldn't get client cert CN\n", __func__); - - n = wsi->vhost->protocols[0].callback(wsi, - LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, - x509_ctx, ssl, preverify_ok); - - /* convert return code from 0 = OK to 1 = OK */ - return !n; -} - -int -lws_tls_server_client_cert_verify_config(struct lws_vhost *vh) -{ - int verify_options = SSL_VERIFY_PEER; - - /* as a server, are we requiring clients to identify themselves? */ - - if (!lws_check_opt(vh->options, - LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) - return 0; - - if (!lws_check_opt(vh->options, - LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED)) - verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; - - SSL_CTX_set_session_id_context(vh->tls.ssl_ctx, (uint8_t *)vh->context, - sizeof(void *)); - - /* absolutely require the client cert */ - SSL_CTX_set_verify(vh->tls.ssl_ctx, verify_options, - OpenSSL_verify_callback); - - return 0; -} - -#if defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT) -static int -lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) -{ - struct lws_context *context = (struct lws_context *)arg; - struct lws_vhost *vhost, *vh; - const char *servername; - - if (!ssl) - return SSL_TLSEXT_ERR_NOACK; - - /* - * We can only get ssl accepted connections by using a vhost's ssl_ctx - * find out which listening one took us and only match vhosts on the - * same port. - */ - vh = context->vhost_list; - while (vh) { - if (!vh->being_destroyed && - vh->tls.ssl_ctx == SSL_get_SSL_CTX(ssl)) - break; - vh = vh->vhost_next; - } - - if (!vh) { - assert(vh); /* can't match the incoming vh? */ - return SSL_TLSEXT_ERR_OK; - } - - servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - if (!servername) { - /* the client doesn't know what hostname it wants */ - lwsl_info("SNI: Unknown ServerName\n"); - - return SSL_TLSEXT_ERR_OK; - } - - vhost = lws_select_vhost(context, vh->listen_port, servername); - if (!vhost) { - lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port); - - return SSL_TLSEXT_ERR_OK; - } - - lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port); - - /* select the ssl ctx from the selected vhost for this conn */ - SSL_set_SSL_CTX(ssl, vhost->tls.ssl_ctx); - - return SSL_TLSEXT_ERR_OK; -} -#endif - -/* - * this may now get called after the vhost creation, when certs become - * available. - */ -int -lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, - const char *cert, const char *private_key, - const char *mem_cert, size_t mem_cert_len, - const char *mem_privkey, size_t mem_privkey_len) -{ -#if !defined(OPENSSL_NO_EC) - const char *ecdh_curve = "prime256v1"; -#if !defined(LWS_WITH_BORINGSSL) && defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS) - STACK_OF(X509) *extra_certs = NULL; -#endif - EC_KEY *ecdh, *EC_key = NULL; - EVP_PKEY *pkey; - X509 *x = NULL; - int ecdh_nid; - int KeyType; -#endif - unsigned long error; - lws_filepos_t flen; - uint8_t *p; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - int ret; -#endif - int n = lws_tls_generic_cert_checks(vhost, cert, private_key), m; - - if (!cert && !private_key) - n = LWS_TLS_EXTANT_ALTERNATIVE; - - if (n == LWS_TLS_EXTANT_NO && (!mem_cert || !mem_privkey)) - return 0; - if (n == LWS_TLS_EXTANT_NO) - n = LWS_TLS_EXTANT_ALTERNATIVE; - - if (n == LWS_TLS_EXTANT_ALTERNATIVE && (!mem_cert || !mem_privkey)) - return 1; /* no alternative */ - - if (n == LWS_TLS_EXTANT_ALTERNATIVE) { - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - - /* - * Although we have prepared update certs, we no longer have - * the rights to read our own cert + key we saved. - * - * If we were passed copies in memory buffers, use those - * in favour of the filepaths we normally want. - */ - cert = NULL; - private_key = NULL; - } - - /* - * use the multi-cert interface for backwards compatibility in the - * both simple files case - */ - - if (n != LWS_TLS_EXTANT_ALTERNATIVE && cert) { - - /* set the local certificate from CertFile */ - m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); - if (m != 1) { - error = ERR_get_error(); - lwsl_err("problem getting cert '%s' %lu: %s\n", - cert, error, ERR_error_string(error, - (char *)vhost->context->pt[0].serv_buf)); - - return 1; - } - - if (private_key) { - /* set the private key from KeyFile */ - if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, - SSL_FILETYPE_PEM) != 1) { - error = ERR_get_error(); - lwsl_err("ssl problem getting key '%s' %lu: %s\n", - private_key, error, - ERR_error_string(error, - (char *)vhost->context->pt[0].serv_buf)); - return 1; - } - } else { - if (vhost->protocols[0].callback(wsi, - LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, - vhost->tls.ssl_ctx, NULL, 0)) { - lwsl_err("ssl private key not set\n"); - - return 1; - } - } - - return 0; - } - - /* otherwise allow for DER or PEM, file or memory image */ - - if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, - mem_cert_len, &p, &flen)) { - lwsl_err("%s: couldn't read cert file\n", __func__); - - return 1; - } - -#if !defined(USE_WOLFSSL) - ret = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, (int)flen, p); -#else - ret = wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, - (uint8_t *)p, (int)flen, - WOLFSSL_FILETYPE_ASN1); -#endif - lws_free_set_NULL(p); - if (ret != 1) { - lwsl_err("%s: Problem loading cert\n", __func__); - - return 1; - } - - if (lws_tls_alloc_pem_to_der_file(vhost->context, private_key, - mem_privkey, mem_privkey_len, - &p, &flen)) { - lwsl_notice("unable to convert memory privkey\n"); - - return 1; - } - -#if !defined(USE_WOLFSSL) - ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, vhost->tls.ssl_ctx, p, - (long)(long long)flen); - if (ret != 1) { - ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_EC, - vhost->tls.ssl_ctx, p, - (long)(long long)flen); - } -#else - ret = wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, flen, - WOLFSSL_FILETYPE_ASN1); -#endif - lws_free_set_NULL(p); - if (ret != 1) { - lwsl_notice("unable to use memory privkey\n"); - - return 1; - } - -#else - /* - * Although we have prepared update certs, we no longer have - * the rights to read our own cert + key we saved. - * - * If we were passed copies in memory buffers, use those - * instead. - * - * The passed memory-buffer cert image is in DER, and the - * memory-buffer private key image is PEM. - */ -#ifndef USE_WOLFSSL - if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, - mem_cert_len, &p, &flen)) { - lwsl_err("%s: couldn't convert pem to der\n", __func__); - return 1; - } - if (SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, - (int)flen, - (uint8_t *)p) != 1) { -#else - if (wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, - (uint8_t *)mem_cert, - (int)mem_cert_len, - WOLFSSL_FILETYPE_ASN1) != 1) { - -#endif - lwsl_err("Problem loading update cert\n"); - - return 1; - } - - if (lws_tls_alloc_pem_to_der_file(vhost->context, NULL, - mem_privkey, mem_privkey_len, - &p, &flen)) { - lwsl_notice("unable to convert memory privkey\n"); - - return 1; - } -#ifndef USE_WOLFSSL - if (SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, - vhost->tls.ssl_ctx, p, - (long)(long long)flen) != 1) { -#else - if (wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, - flen, WOLFSSL_FILETYPE_ASN1) != 1) { -#endif - lwsl_notice("unable to use memory privkey\n"); - - return 1; - } - - goto check_key; - } - - /* set the local certificate from CertFile */ - m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); - if (m != 1) { - error = ERR_get_error(); - lwsl_err("problem getting cert '%s' %lu: %s\n", - cert, error, ERR_error_string(error, - (char *)vhost->context->pt[0].serv_buf)); - - return 1; - } - - if (n != LWS_TLS_EXTANT_ALTERNATIVE && private_key) { - /* set the private key from KeyFile */ - if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, - SSL_FILETYPE_PEM) != 1) { - error = ERR_get_error(); - lwsl_err("ssl problem getting key '%s' %lu: %s\n", - private_key, error, - ERR_error_string(error, - (char *)vhost->context->pt[0].serv_buf)); - return 1; - } - } else { - if (vhost->protocols[0].callback(wsi, - LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, - vhost->tls.ssl_ctx, NULL, 0)) { - lwsl_err("ssl private key not set\n"); - - return 1; - } - } - -check_key: -#endif - - /* verify private key */ - if (!SSL_CTX_check_private_key(vhost->tls.ssl_ctx)) { - lwsl_err("Private SSL key doesn't match cert\n"); - - return 1; - } - - -#if !defined(OPENSSL_NO_EC) - if (vhost->tls.ecdh_curve[0]) - ecdh_curve = vhost->tls.ecdh_curve; - - ecdh_nid = OBJ_sn2nid(ecdh_curve); - if (NID_undef == ecdh_nid) { - lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve); - return 1; - } - - ecdh = EC_KEY_new_by_curve_name(ecdh_nid); - if (NULL == ecdh) { - lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve); - return 1; - } - SSL_CTX_set_tmp_ecdh(vhost->tls.ssl_ctx, ecdh); - EC_KEY_free(ecdh); - - SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_SINGLE_ECDH_USE); - - lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve); - - if (lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH)) - lwsl_notice(" Using ECDH certificate support\n"); - - /* Get X509 certificate from ssl context */ -#if !defined(LWS_WITH_BORINGSSL) -#if !defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS) - x = sk_X509_value(vhost->tls.ssl_ctx->extra_certs, 0); -#else - SSL_CTX_get_extra_chain_certs_only(vhost->tls.ssl_ctx, &extra_certs); - if (extra_certs) - x = sk_X509_value(extra_certs, 0); - else - lwsl_info("%s: no extra certs\n", __func__); -#endif - if (!x) { - //lwsl_err("%s: x is NULL\n", __func__); - goto post_ecdh; - } -#else - return 0; -#endif - /* Get the public key from certificate */ - pkey = X509_get_pubkey(x); - if (!pkey) { - lwsl_err("%s: pkey is NULL\n", __func__); - - return 1; - } - /* Get the key type */ - KeyType = EVP_PKEY_type(EVP_PKEY_id(pkey)); - - if (EVP_PKEY_EC != KeyType) { - lwsl_notice("Key type is not EC\n"); - return 0; - } - /* Get the key */ - EC_key = EVP_PKEY_get1_EC_KEY(pkey); - /* Set ECDH parameter */ - if (!EC_key) { - lwsl_err("%s: ECDH key is NULL \n", __func__); - return 1; - } - SSL_CTX_set_tmp_ecdh(vhost->tls.ssl_ctx, EC_key); - - EC_KEY_free(EC_key); -#else - lwsl_notice(" OpenSSL doesn't support ECDH\n"); -#endif -#if !defined(OPENSSL_NO_EC) && !defined(LWS_WITH_BORINGSSL) -post_ecdh: -#endif - vhost->tls.skipped_certs = 0; - - return 0; -} - -int -lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, - struct lws_vhost *vhost, struct lws *wsi) -{ - unsigned long error; - SSL_METHOD *method = (SSL_METHOD *)SSLv23_server_method(); - - if (!method) { - error = ERR_get_error(); - lwsl_err("problem creating ssl method %lu: %s\n", - error, ERR_error_string(error, - (char *)vhost->context->pt[0].serv_buf)); - return 1; - } - vhost->tls.ssl_ctx = SSL_CTX_new(method); /* create context */ - if (!vhost->tls.ssl_ctx) { - error = ERR_get_error(); - lwsl_err("problem creating ssl context %lu: %s\n", - error, ERR_error_string(error, - (char *)vhost->context->pt[0].serv_buf)); - return 1; - } - - SSL_CTX_set_ex_data(vhost->tls.ssl_ctx, - openssl_SSL_CTX_private_data_index, - (char *)vhost->context); - /* Disable SSLv2 and SSLv3 */ - SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_NO_SSLv2 | - SSL_OP_NO_SSLv3); -#ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_NO_COMPRESSION); -#endif - SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_SINGLE_DH_USE); - SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - - if (info->ssl_cipher_list) - SSL_CTX_set_cipher_list(vhost->tls.ssl_ctx, info->ssl_cipher_list); - -#if defined(LWS_HAVE_SSL_CTX_set_ciphersuites) - if (info->tls1_3_plus_cipher_list) - SSL_CTX_set_ciphersuites(vhost->tls.ssl_ctx, - info->tls1_3_plus_cipher_list); -#endif - -#if !defined(OPENSSL_NO_TLSEXT) - SSL_CTX_set_tlsext_servername_callback(vhost->tls.ssl_ctx, - lws_ssl_server_name_cb); - SSL_CTX_set_tlsext_servername_arg(vhost->tls.ssl_ctx, vhost->context); -#endif - - if (info->ssl_ca_filepath && - !SSL_CTX_load_verify_locations(vhost->tls.ssl_ctx, - info->ssl_ca_filepath, NULL)) { - lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", - __func__); - } - - if (info->ssl_options_set) - SSL_CTX_set_options(vhost->tls.ssl_ctx, info->ssl_options_set); - -/* SSL_clear_options introduced in 0.9.8m */ -#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL) - if (info->ssl_options_clear) - SSL_CTX_clear_options(vhost->tls.ssl_ctx, - info->ssl_options_clear); -#endif - - lwsl_info(" SSL options 0x%lX\n", - (unsigned long)SSL_CTX_get_options(vhost->tls.ssl_ctx)); - if (!vhost->tls.use_ssl || - (!info->ssl_cert_filepath && !info->server_ssl_cert_mem)) - return 0; - - lws_ssl_bind_passphrase(vhost->tls.ssl_ctx, 0, info); - - return lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath, - info->ssl_private_key_filepath, - info->server_ssl_cert_mem, - info->server_ssl_cert_mem_len, - info->server_ssl_private_key_mem, - info->server_ssl_private_key_mem_len); -} - -int -lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd) -{ -#if !defined(USE_WOLFSSL) - BIO *bio; -#endif - - errno = 0; - ERR_clear_error(); - wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_ctx); - if (wsi->tls.ssl == NULL) { - lwsl_err("SSL_new failed: %d (errno %d)\n", - lws_ssl_get_error(wsi, 0), errno); - - lws_tls_err_describe_clear(); - return 1; - } - - SSL_set_ex_data(wsi->tls.ssl, openssl_websocket_private_data_index, wsi); - SSL_set_fd(wsi->tls.ssl, (int)(lws_intptr_t)accept_fd); - -#ifdef USE_WOLFSSL -#ifdef USE_OLD_CYASSL - CyaSSL_set_using_nonblock(wsi->tls.ssl, 1); -#else - wolfSSL_set_using_nonblock(wsi->tls.ssl, 1); -#endif -#else - - SSL_set_mode(wsi->tls.ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | - SSL_MODE_RELEASE_BUFFERS); - bio = SSL_get_rbio(wsi->tls.ssl); - if (bio) - BIO_set_nbio(bio, 1); /* nonblocking */ - else - lwsl_notice("NULL rbio\n"); - bio = SSL_get_wbio(wsi->tls.ssl); - if (bio) - BIO_set_nbio(bio, 1); /* nonblocking */ - else - lwsl_notice("NULL rbio\n"); -#endif - -#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) - if (wsi->vhost->tls.ssl_info_event_mask) - SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback); -#endif - - return 0; -} - -int -lws_tls_server_abort_connection(struct lws *wsi) -{ - SSL_shutdown(wsi->tls.ssl); - SSL_free(wsi->tls.ssl); - - return 0; -} - -enum lws_ssl_capable_status -lws_tls_server_accept(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - union lws_tls_cert_info_results ir; - int m, n; - - errno = 0; - ERR_clear_error(); - n = SSL_accept(wsi->tls.ssl); - - wsi->skip_fallback = 1; - - if (n == 1) { - n = lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME, &ir, - sizeof(ir.ns.name)); - if (!n) - lwsl_notice("%s: client cert CN '%s'\n", __func__, - ir.ns.name); - else - lwsl_info("%s: no client cert CN\n", __func__); - - lws_openssl_describe_cipher(wsi); - - if (SSL_pending(wsi->tls.ssl) && - lws_dll2_is_detached(&wsi->tls.dll_pending_tls)) - lws_dll2_add_head(&wsi->tls.dll_pending_tls, - &pt->tls.dll_pending_tls_owner); - - return LWS_SSL_CAPABLE_DONE; - } - - m = lws_ssl_get_error(wsi, n); - lws_tls_err_describe_clear(); - - if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL) - return LWS_SSL_CAPABLE_ERROR; - - if (m == SSL_ERROR_WANT_READ || - (m != SSL_ERROR_ZERO_RETURN && SSL_want_read(wsi->tls.ssl))) { - if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { - lwsl_info("%s: WANT_READ change_pollfd failed\n", - __func__); - return LWS_SSL_CAPABLE_ERROR; - } - - lwsl_info("SSL_ERROR_WANT_READ: m %d\n", m); - return LWS_SSL_CAPABLE_MORE_SERVICE_READ; - } - if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) { - lwsl_debug("%s: WANT_WRITE\n", __func__); - - if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { - lwsl_info("%s: WANT_WRITE change_pollfd failed\n", - __func__); - return LWS_SSL_CAPABLE_ERROR; - } - return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE; - } - - return LWS_SSL_CAPABLE_ERROR; -} - -#if defined(LWS_WITH_ACME) -static int -lws_tls_openssl_rsa_new_key(RSA **rsa, int bits) -{ - BIGNUM *bn = BN_new(); - int n; - - if (!bn) - return 1; - - if (BN_set_word(bn, RSA_F4) != 1) { - BN_free(bn); - return 1; - } - - *rsa = RSA_new(); - if (!*rsa) { - BN_free(bn); - return 1; - } - - n = RSA_generate_key_ex(*rsa, bits, bn, NULL); - BN_free(bn); - if (n == 1) - return 0; - - RSA_free(*rsa); - *rsa = NULL; - - return 1; -} - -struct lws_tls_ss_pieces { - X509 *x509; - EVP_PKEY *pkey; - RSA *rsa; -}; - -int -lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, - const char *san_b) -{ - GENERAL_NAMES *gens = sk_GENERAL_NAME_new_null(); - GENERAL_NAME *gen = NULL; - ASN1_IA5STRING *ia5 = NULL; - X509_NAME *name; - - if (!gens) - return 1; - - vhost->tls.ss = lws_zalloc(sizeof(*vhost->tls.ss), "sni cert"); - if (!vhost->tls.ss) { - GENERAL_NAMES_free(gens); - return 1; - } - - vhost->tls.ss->x509 = X509_new(); - if (!vhost->tls.ss->x509) - goto bail; - - ASN1_INTEGER_set(X509_get_serialNumber(vhost->tls.ss->x509), 1); - X509_gmtime_adj(X509_get_notBefore(vhost->tls.ss->x509), 0); - X509_gmtime_adj(X509_get_notAfter(vhost->tls.ss->x509), 3600); - - vhost->tls.ss->pkey = EVP_PKEY_new(); - if (!vhost->tls.ss->pkey) - goto bail0; - - if (lws_tls_openssl_rsa_new_key(&vhost->tls.ss->rsa, 4096)) - goto bail1; - - if (!EVP_PKEY_assign_RSA(vhost->tls.ss->pkey, vhost->tls.ss->rsa)) - goto bail2; - - X509_set_pubkey(vhost->tls.ss->x509, vhost->tls.ss->pkey); - - name = X509_get_subject_name(vhost->tls.ss->x509); - X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, - (unsigned char *)"GB", -1, -1, 0); - X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, - (unsigned char *)"somecompany", -1, -1, 0); - if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, - (unsigned char *)"temp.acme.invalid", - -1, -1, 0) != 1) { - lwsl_notice("failed to add CN\n"); - goto bail2; - } - X509_set_issuer_name(vhost->tls.ss->x509, name); - - /* add the SAN payloads */ - - gen = GENERAL_NAME_new(); - ia5 = ASN1_IA5STRING_new(); - if (!ASN1_STRING_set(ia5, san_a, -1)) { - lwsl_notice("failed to set ia5\n"); - GENERAL_NAME_free(gen); - goto bail2; - } - GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); - sk_GENERAL_NAME_push(gens, gen); - - if (X509_add1_ext_i2d(vhost->tls.ss->x509, NID_subject_alt_name, - gens, 0, X509V3_ADD_APPEND) != 1) - goto bail2; - - GENERAL_NAMES_free(gens); - - if (san_b && san_b[0]) { - gens = sk_GENERAL_NAME_new_null(); - gen = GENERAL_NAME_new(); - ia5 = ASN1_IA5STRING_new(); - if (!ASN1_STRING_set(ia5, san_a, -1)) { - lwsl_notice("failed to set ia5\n"); - GENERAL_NAME_free(gen); - goto bail2; - } - GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); - sk_GENERAL_NAME_push(gens, gen); - - if (X509_add1_ext_i2d(vhost->tls.ss->x509, NID_subject_alt_name, - gens, 0, X509V3_ADD_APPEND) != 1) - goto bail2; - - GENERAL_NAMES_free(gens); - } - - /* sign it with our private key */ - if (!X509_sign(vhost->tls.ss->x509, vhost->tls.ss->pkey, EVP_sha256())) - goto bail2; - -#if 0 - {/* useful to take a sample of a working cert for mbedtls to crib */ - FILE *fp = fopen("/tmp/acme-temp-cert", "w+"); - - i2d_X509_fp(fp, vhost->tls.ss->x509); - fclose(fp); - } -#endif - - /* tell the vhost to use our crafted certificate */ - SSL_CTX_use_certificate(vhost->tls.ssl_ctx, vhost->tls.ss->x509); - /* and to use our generated private key */ - SSL_CTX_use_PrivateKey(vhost->tls.ssl_ctx, vhost->tls.ss->pkey); - - return 0; - -bail2: - RSA_free(vhost->tls.ss->rsa); -bail1: - EVP_PKEY_free(vhost->tls.ss->pkey); -bail0: - X509_free(vhost->tls.ss->x509); -bail: - lws_free(vhost->tls.ss); - GENERAL_NAMES_free(gens); - - return 1; -} - -void -lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost) -{ - if (!vhost->tls.ss) - return; - - EVP_PKEY_free(vhost->tls.ss->pkey); - X509_free(vhost->tls.ss->x509); - lws_free_set_NULL(vhost->tls.ss); -} - -static int -lws_tls_openssl_add_nid(X509_NAME *name, int nid, const char *value) -{ - X509_NAME_ENTRY *e; - int n; - - if (!value || value[0] == '\0') - value = "none"; - - e = X509_NAME_ENTRY_create_by_NID(NULL, nid, MBSTRING_ASC, - (unsigned char *)value, -1); - if (!e) - return 1; - n = X509_NAME_add_entry(name, e, -1, 0); - X509_NAME_ENTRY_free(e); - - return n != 1; -} - -static int nid_list[] = { - NID_countryName, /* LWS_TLS_REQ_ELEMENT_COUNTRY */ - NID_stateOrProvinceName, /* LWS_TLS_REQ_ELEMENT_STATE */ - NID_localityName, /* LWS_TLS_REQ_ELEMENT_LOCALITY */ - NID_organizationName, /* LWS_TLS_REQ_ELEMENT_ORGANIZATION */ - NID_commonName, /* LWS_TLS_REQ_ELEMENT_COMMON_NAME */ - NID_subject_alt_name, /* LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME */ - NID_pkcs9_emailAddress, /* LWS_TLS_REQ_ELEMENT_EMAIL */ -}; - -int -lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[], - uint8_t *csr, size_t csr_len, char **privkey_pem, - size_t *privkey_len) -{ - uint8_t *csr_in = csr; - RSA *rsakey; - X509_REQ *req; - X509_NAME *subj; - EVP_PKEY *pkey; - char *p, *end; - BIO *bio; - long bio_len; - int n, ret = -1; - - if (lws_tls_openssl_rsa_new_key(&rsakey, 4096)) - return -1; - - pkey = EVP_PKEY_new(); - if (!pkey) - goto bail0; - if (!EVP_PKEY_set1_RSA(pkey, rsakey)) - goto bail1; - - req = X509_REQ_new(); - if (!req) - goto bail1; - - X509_REQ_set_pubkey(req, pkey); - - subj = X509_NAME_new(); - if (!subj) - goto bail2; - - for (n = 0; n < LWS_TLS_REQ_ELEMENT_COUNT; n++) - if (elements[n] && - lws_tls_openssl_add_nid(subj, nid_list[n], - elements[n])) { - lwsl_notice("%s: failed to add element %d\n", - __func__, n); - goto bail3; - } - - if (X509_REQ_set_subject_name(req, subj) != 1) - goto bail3; - - if (elements[LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME]) { - STACK_OF(X509_EXTENSION) *exts; - X509_EXTENSION *ext; - char san[256]; - - exts = sk_X509_EXTENSION_new_null(); - if (!exts) - goto bail3; - - lws_snprintf(san, sizeof(san), "DNS:%s,DNS:%s", - elements[LWS_TLS_REQ_ELEMENT_COMMON_NAME], - elements[LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME]); - - ext = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, - san); - if (!ext) { - sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); - goto bail3; - } - sk_X509_EXTENSION_push(exts, ext); - - if (!X509_REQ_add_extensions(req, exts)) { - sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); - goto bail3; - } - sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); - } - - if (!X509_REQ_sign(req, pkey, EVP_sha256())) - goto bail3; - - /* - * issue the CSR as PEM to a BIO, and translate to b64urlenc without - * headers, trailers, or whitespace - */ - - bio = BIO_new(BIO_s_mem()); - if (!bio) - goto bail3; - - if (PEM_write_bio_X509_REQ(bio, req) != 1) { - BIO_free(bio); - goto bail3; - } - - bio_len = BIO_get_mem_data(bio, &p); - end = p + bio_len; - - /* strip the header line */ - while (p < end && *p != '\n') - p++; - - while (p < end && csr_len) { - if (*p == '\n') { - p++; - continue; - } - - if (*p == '-') - break; - - if (*p == '+') - *csr++ = '-'; - else - if (*p == '/') - *csr++ = '_'; - else - *csr++ = *p; - p++; - csr_len--; - } - BIO_free(bio); - if (!csr_len) { - lwsl_notice("%s: need %ld for CSR\n", __func__, bio_len); - goto bail3; - } - - /* - * Also return the private key as a PEM in memory - * (platform may not have a filesystem) - */ - bio = BIO_new(BIO_s_mem()); - if (!bio) - goto bail3; - - if (PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, 0, NULL) != 1) { - BIO_free(bio); - goto bail3; - } - bio_len = BIO_get_mem_data(bio, &p); - *privkey_pem = malloc(bio_len); /* malloc so user code can own / free */ - *privkey_len = (size_t)bio_len; - if (!*privkey_pem) { - lwsl_notice("%s: need %ld for private key\n", __func__, - bio_len); - BIO_free(bio); - goto bail3; - } - memcpy(*privkey_pem, p, (int)(long long)bio_len); - BIO_free(bio); - - ret = lws_ptr_diff(csr, csr_in); - -bail3: - X509_NAME_free(subj); -bail2: - X509_REQ_free(req); -bail1: - EVP_PKEY_free(pkey); -bail0: - RSA_free(rsakey); - - return ret; -} -#endif diff -Nru libwebsockets-4.0.20/lib/tls/openssl/openssl-ssl.c libwebsockets-2.4.2/lib/tls/openssl/openssl-ssl.c --- libwebsockets-4.0.20/lib/tls/openssl/openssl-ssl.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/openssl/openssl-ssl.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,524 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-tls-openssl.h" -#include - -int openssl_websocket_private_data_index, - openssl_SSL_CTX_private_data_index; - -/* - * Care: many openssl apis return 1 for success. These are translated to the - * lws convention of 0 for success. - */ - -int lws_openssl_describe_cipher(struct lws *wsi) -{ -#if !defined(LWS_WITH_NO_LOGS) && !defined(USE_WOLFSSL) - int np = -1; - SSL *s = wsi->tls.ssl; - - SSL_get_cipher_bits(s, &np); - lwsl_info("%s: wsi %p: %s, %s, %d bits, %s\n", __func__, wsi, - SSL_get_cipher_name(s), SSL_get_cipher(s), np, - SSL_get_cipher_version(s)); -#endif - - return 0; -} - -int lws_ssl_get_error(struct lws *wsi, int n) -{ - int m; - - if (!wsi->tls.ssl) - return 99; - - m = SSL_get_error(wsi->tls.ssl, n); - lwsl_debug("%s: %p %d -> %d (errno %d)\n", __func__, wsi->tls.ssl, n, m, - errno); - - assert (errno != 9); - - return m; -} - -static int -lws_context_init_ssl_pem_passwd_cb(char *buf, int size, int rwflag, - void *userdata) -{ - struct lws_context_creation_info * info = - (struct lws_context_creation_info *)userdata; - - strncpy(buf, info->ssl_private_key_password, size); - buf[size - 1] = '\0'; - - return (int)strlen(buf); -} - -static int -lws_context_init_ssl_pem_passwd_client_cb(char *buf, int size, int rwflag, - void *userdata) -{ - struct lws_context_creation_info * info = - (struct lws_context_creation_info *)userdata; - const char *p = info->ssl_private_key_password; - - if (info->client_ssl_private_key_password) - p = info->client_ssl_private_key_password; - - strncpy(buf, p, size); - buf[size - 1] = '\0'; - - return (int)strlen(buf); -} - -void -lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, int is_client, - const struct lws_context_creation_info *info) -{ - if (!info->ssl_private_key_password && - !info->client_ssl_private_key_password) - return; - /* - * password provided, set ssl callback and user data - * for checking password which will be trigered during - * SSL_CTX_use_PrivateKey_file function - */ - SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info); - SSL_CTX_set_default_passwd_cb(ssl_ctx, is_client ? - lws_context_init_ssl_pem_passwd_client_cb: - lws_context_init_ssl_pem_passwd_cb); -} - -static void -lws_ssl_destroy_client_ctx(struct lws_vhost *vhost) -{ - struct lws_tls_client_reuse *tcr; - - if (vhost->tls.user_supplied_ssl_ctx || !vhost->tls.ssl_client_ctx) - return; - - tcr = SSL_CTX_get_ex_data(vhost->tls.ssl_client_ctx, - openssl_SSL_CTX_private_data_index); - - if (!tcr || --tcr->refcount) - return; - - SSL_CTX_free(vhost->tls.ssl_client_ctx); - vhost->tls.ssl_client_ctx = NULL; - - vhost->context->tls.count_client_contexts--; - - lws_dll2_remove(&tcr->cc_list); - lws_free(tcr); -} - -void -lws_ssl_destroy(struct lws_vhost *vhost) -{ - if (!lws_check_opt(vhost->context->options, - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) - return; - - if (vhost->tls.ssl_ctx) - SSL_CTX_free(vhost->tls.ssl_ctx); - - lws_ssl_destroy_client_ctx(vhost); - -// after 1.1.0 no need -#if (OPENSSL_VERSION_NUMBER < 0x10100000) -// <= 1.0.1f = old api, 1.0.1g+ = new api -#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL) - ERR_remove_state(0); -#else -#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \ - !defined(LIBRESSL_VERSION_NUMBER) && \ - !defined(OPENSSL_IS_BORINGSSL) - ERR_remove_thread_state(); -#else - ERR_remove_thread_state(NULL); -#endif -#endif - /* not needed after 1.1.0 */ -#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && \ - (OPENSSL_VERSION_NUMBER <= 0x10100000) - SSL_COMP_free_compression_methods(); -#endif - ERR_free_strings(); - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); -#endif -} - -int -lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - int n = 0, m; - - if (!wsi->tls.ssl) - return lws_ssl_capable_read_no_ssl(wsi, buf, len); - - lws_stats_bump(pt, LWSSTATS_C_API_READ, 1); - - errno = 0; - ERR_clear_error(); - n = SSL_read(wsi->tls.ssl, buf, len); -#if defined(LWS_PLAT_FREERTOS) - if (!n && errno == LWS_ENOTCONN) { - lwsl_debug("%p: SSL_read ENOTCONN\n", wsi); - return LWS_SSL_CAPABLE_ERROR; - } -#endif -#if defined(LWS_WITH_STATS) - if (!wsi->seen_rx && wsi->accept_start_us) { - lws_stats_bump(pt, LWSSTATS_US_SSL_RX_DELAY_AVG, - lws_now_usecs() - - wsi->accept_start_us); - lws_stats_bump(pt, LWSSTATS_C_SSL_CONNS_HAD_RX, 1); - wsi->seen_rx = 1; - } -#endif - - - lwsl_debug("%p: SSL_read says %d\n", wsi, n); - /* manpage: returning 0 means connection shut down - * - * 2018-09-10: https://github.com/openssl/openssl/issues/1903 - * - * So, in summary, if you get a 0 or -1 return from SSL_read() / - * SSL_write(), you should call SSL_get_error(): - * - * - If you get back SSL_ERROR_RETURN_ZERO then you know the connection - * has been cleanly shutdown by the peer. To fully close the - * connection you may choose to call SSL_shutdown() to send a - * close_notify back. - * - * - If you get back SSL_ERROR_SSL then some kind of internal or - * protocol error has occurred. More details will be on the SSL error - * queue. You can also call SSL_get_shutdown(). If this indicates a - * state of SSL_RECEIVED_SHUTDOWN then you know a fatal alert has - * been received from the peer (if it had been a close_notify then - * SSL_get_error() would have returned SSL_ERROR_RETURN_ZERO). - * SSL_ERROR_SSL is considered fatal - you should not call - * SSL_shutdown() in this case. - * - * - If you get back SSL_ERROR_SYSCALL then some kind of fatal (i.e. - * non-retryable) error has occurred in a system call. - */ - if (n <= 0) { - m = lws_ssl_get_error(wsi, n); - lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno); - if (m == SSL_ERROR_ZERO_RETURN) /* cleanly shut down */ - return LWS_SSL_CAPABLE_ERROR; - - /* hm not retryable.. could be 0 size pkt or error */ - - if (m == SSL_ERROR_SSL || m == SSL_ERROR_SYSCALL || - errno == LWS_ENOTCONN) { - - /* unclean, eg closed conn */ - - wsi->socket_is_permanently_unusable = 1; - - return LWS_SSL_CAPABLE_ERROR; - } - - /* retryable? */ - - if (SSL_want_read(wsi->tls.ssl)) { - lwsl_debug("%s: WANT_READ\n", __func__); - lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); - return LWS_SSL_CAPABLE_MORE_SERVICE; - } - if (SSL_want_write(wsi->tls.ssl)) { - lwsl_debug("%s: WANT_WRITE\n", __func__); - lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); - return LWS_SSL_CAPABLE_MORE_SERVICE; - } - - /* keep on trucking it seems */ - } - - lws_stats_bump(pt, LWSSTATS_B_READ, n); - -#if defined(LWS_WITH_SERVER_STATUS) - if (wsi->vhost) - wsi->vhost->conn_stats.rx += n; -#endif - - // lwsl_hexdump_err(buf, n); - -#if defined(LWS_WITH_DETAILED_LATENCY) - if (context->detailed_latency_cb) { - wsi->detlat.req_size = len; - wsi->detlat.acc_size = n; - wsi->detlat.type = LDLT_READ; - wsi->detlat.latencies[LAT_DUR_PROXY_RX_TO_ONWARD_TX] = - lws_now_usecs() - pt->ust_left_poll; - wsi->detlat.latencies[LAT_DUR_USERCB] = 0; - lws_det_lat_cb(wsi->context, &wsi->detlat); - } -#endif - - /* - * if it was our buffer that limited what we read, - * check if SSL has additional data pending inside SSL buffers. - * - * Because these won't signal at the network layer with POLLIN - * and if we don't realize, this data will sit there forever - */ - if (n != len) - goto bail; - if (!wsi->tls.ssl) - goto bail; - - if (SSL_pending(wsi->tls.ssl) && - lws_dll2_is_detached(&wsi->tls.dll_pending_tls)) - lws_dll2_add_head(&wsi->tls.dll_pending_tls, - &pt->tls.dll_pending_tls_owner); - - return n; -bail: - lws_ssl_remove_wsi_from_buffered_list(wsi); - - return n; -} - -int -lws_ssl_pending(struct lws *wsi) -{ - if (!wsi->tls.ssl) - return 0; - - return SSL_pending(wsi->tls.ssl); -} - -int -lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len) -{ - int n, m; - - // lwsl_notice("%s: len %d\n", __func__, len); - // lwsl_hexdump_notice(buf, len); - - if (!wsi->tls.ssl) - return lws_ssl_capable_write_no_ssl(wsi, buf, len); - - errno = 0; - ERR_clear_error(); - n = SSL_write(wsi->tls.ssl, buf, len); - if (n > 0) - return n; - - m = lws_ssl_get_error(wsi, n); - if (m != SSL_ERROR_SYSCALL) { - if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) { - lwsl_notice("%s: want read\n", __func__); - - return LWS_SSL_CAPABLE_MORE_SERVICE; - } - - if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) { - lws_set_blocking_send(wsi); - - lwsl_debug("%s: want write\n", __func__); - - return LWS_SSL_CAPABLE_MORE_SERVICE; - } - } - - lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL)); - lws_tls_err_describe_clear(); - - wsi->socket_is_permanently_unusable = 1; - - return LWS_SSL_CAPABLE_ERROR; -} - -void -lws_ssl_info_callback(const SSL *ssl, int where, int ret) -{ - struct lws *wsi; - struct lws_context *context; - struct lws_ssl_info si; - -#ifndef USE_WOLFSSL - context = (struct lws_context *)SSL_CTX_get_ex_data( - SSL_get_SSL_CTX(ssl), - openssl_SSL_CTX_private_data_index); -#else - context = (struct lws_context *)SSL_CTX_get_ex_data( - SSL_get_SSL_CTX((SSL*) ssl), - openssl_SSL_CTX_private_data_index); -#endif - if (!context) - return; - wsi = wsi_from_fd(context, SSL_get_fd(ssl)); - if (!wsi) - return; - - if (!(where & wsi->vhost->tls.ssl_info_event_mask)) - return; - - si.where = where; - si.ret = ret; - - if (user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_SSL_INFO, - wsi->user_space, &si, 0)) - lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1); -} - - -int -lws_ssl_close(struct lws *wsi) -{ - lws_sockfd_type n; - - if (!wsi->tls.ssl) - return 0; /* not handled */ - -#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) - /* kill ssl callbacks, because we will remove the fd from the - * table linking it to the wsi - */ - if (wsi->vhost->tls.ssl_info_event_mask) - SSL_set_info_callback(wsi->tls.ssl, NULL); -#endif - - n = SSL_get_fd(wsi->tls.ssl); - if (!wsi->socket_is_permanently_unusable) - SSL_shutdown(wsi->tls.ssl); - compatible_close(n); - SSL_free(wsi->tls.ssl); - wsi->tls.ssl = NULL; - - lws_tls_restrict_return(wsi->context); - - // lwsl_notice("%s: ssl restr %d, simul %d\n", __func__, - // wsi->context->simultaneous_ssl_restriction, - // wsi->context->simultaneous_ssl); - - return 1; /* handled */ -} - -void -lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost) -{ - if (vhost->tls.ssl_ctx) - SSL_CTX_free(vhost->tls.ssl_ctx); - - lws_ssl_destroy_client_ctx(vhost); - -#if defined(LWS_WITH_ACME) - lws_tls_acme_sni_cert_destroy(vhost); -#endif -} - -void -lws_ssl_context_destroy(struct lws_context *context) -{ -// after 1.1.0 no need -#if (OPENSSL_VERSION_NUMBER < 0x10100000) -// <= 1.0.1f = old api, 1.0.1g+ = new api -#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL) - ERR_remove_state(0); -#else -#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \ - !defined(LIBRESSL_VERSION_NUMBER) && \ - !defined(OPENSSL_IS_BORINGSSL) - ERR_remove_thread_state(); -#else - ERR_remove_thread_state(NULL); -#endif -#endif - // after 1.1.0 no need -#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000) - SSL_COMP_free_compression_methods(); -#endif - ERR_free_strings(); - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); -#endif -} - -lws_tls_ctx * -lws_tls_ctx_from_wsi(struct lws *wsi) -{ - if (!wsi->tls.ssl) - return NULL; - - return SSL_get_SSL_CTX(wsi->tls.ssl); -} - -enum lws_ssl_capable_status -__lws_tls_shutdown(struct lws *wsi) -{ - int n; - - errno = 0; - ERR_clear_error(); - n = SSL_shutdown(wsi->tls.ssl); - lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd); - switch (n) { - case 1: /* successful completion */ - n = shutdown(wsi->desc.sockfd, SHUT_WR); - return LWS_SSL_CAPABLE_DONE; - - case 0: /* needs a retry */ - __lws_change_pollfd(wsi, 0, LWS_POLLIN); - return LWS_SSL_CAPABLE_MORE_SERVICE; - - default: /* fatal error, or WANT */ - n = SSL_get_error(wsi->tls.ssl, n); - if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) { - if (SSL_want_read(wsi->tls.ssl)) { - lwsl_debug("(wants read)\n"); - __lws_change_pollfd(wsi, 0, LWS_POLLIN); - return LWS_SSL_CAPABLE_MORE_SERVICE_READ; - } - if (SSL_want_write(wsi->tls.ssl)) { - lwsl_debug("(wants write)\n"); - __lws_change_pollfd(wsi, 0, LWS_POLLOUT); - return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE; - } - } - return LWS_SSL_CAPABLE_ERROR; - } -} - - -static int -tops_fake_POLLIN_for_buffered_openssl(struct lws_context_per_thread *pt) -{ - return lws_tls_fake_POLLIN_for_buffered(pt); -} - -const struct lws_tls_ops tls_ops_openssl = { - /* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_openssl, -}; diff -Nru libwebsockets-4.0.20/lib/tls/openssl/openssl-tls.c libwebsockets-2.4.2/lib/tls/openssl/openssl-tls.c --- libwebsockets-4.0.20/lib/tls/openssl/openssl-tls.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/openssl/openssl-tls.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,196 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-tls-openssl.h" - -extern int openssl_websocket_private_data_index, -openssl_SSL_CTX_private_data_index; - -char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) { - switch (status) { - case SSL_ERROR_NONE: - return lws_strncpy(buf, "SSL_ERROR_NONE", len); - case SSL_ERROR_ZERO_RETURN: - return lws_strncpy(buf, "SSL_ERROR_ZERO_RETURN", len); - case SSL_ERROR_WANT_READ: - return lws_strncpy(buf, "SSL_ERROR_WANT_READ", len); - case SSL_ERROR_WANT_WRITE: - return lws_strncpy(buf, "SSL_ERROR_WANT_WRITE", len); - case SSL_ERROR_WANT_CONNECT: - return lws_strncpy(buf, "SSL_ERROR_WANT_CONNECT", len); - case SSL_ERROR_WANT_ACCEPT: - return lws_strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len); - case SSL_ERROR_WANT_X509_LOOKUP: - return lws_strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len); - case SSL_ERROR_SYSCALL: - switch (ret) { - case 0: - lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: EOF"); - return buf; - case -1: -#ifndef LWS_PLAT_OPTEE - lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %s", - strerror(errno)); -#else - lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %d", errno); -#endif - return buf; - default: - return strncpy(buf, "SSL_ERROR_SYSCALL", len); - } - case SSL_ERROR_SSL: - return "SSL_ERROR_SSL"; - default: - return "SSL_ERROR_UNKNOWN"; - } -} - -void -lws_tls_err_describe_clear(void) -{ - char buf[160]; - unsigned long l; - - do { - l = ERR_get_error(); - if (!l) - break; - - ERR_error_string_n(l, buf, sizeof(buf)); - lwsl_info(" openssl error: %s\n", buf); - } while (l); - lwsl_info("\n"); -} - -#if LWS_MAX_SMP != 1 - -static pthread_mutex_t *openssl_mutexes; - -static void -lws_openssl_lock_callback(int mode, int type, const char *file, int line) -{ - (void)file; - (void)line; - - if (mode & CRYPTO_LOCK) - pthread_mutex_lock(&openssl_mutexes[type]); - else - pthread_mutex_unlock(&openssl_mutexes[type]); -} - -static unsigned long -lws_openssl_thread_id(void) -{ - return (unsigned long)pthread_self(); -} -#endif - - -int -lws_context_init_ssl_library(const struct lws_context_creation_info *info) -{ -#ifdef USE_WOLFSSL -#ifdef USE_OLD_CYASSL - lwsl_info(" Compiled with CyaSSL support\n"); -#else - lwsl_info(" Compiled with wolfSSL support\n"); -#endif -#else -#if defined(LWS_WITH_BORINGSSL) - lwsl_info(" Compiled with BoringSSL support\n"); -#else - lwsl_info(" Compiled with OpenSSL support\n"); -#endif -#endif - if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { - lwsl_info(" SSL disabled: no " - "LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); - return 0; - } - - /* basic openssl init */ - - lwsl_info("Doing SSL library init\n"); - -#if OPENSSL_VERSION_NUMBER < 0x10100000L - SSL_library_init(); - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); -#else - OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); -#endif -#if defined(LWS_WITH_NETWORK) - openssl_websocket_private_data_index = - SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL); - - openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0, - NULL, NULL, NULL, NULL); -#endif - -#if LWS_MAX_SMP != 1 - { - int n; - - openssl_mutexes = (pthread_mutex_t *) - OPENSSL_malloc(CRYPTO_num_locks() * - sizeof(openssl_mutexes[0])); - - for (n = 0; n < CRYPTO_num_locks(); n++) - pthread_mutex_init(&openssl_mutexes[n], NULL); - - /* - * These "functions" disappeared in later OpenSSL which is - * already threadsafe. - */ - - (void)lws_openssl_thread_id; - (void)lws_openssl_lock_callback; - - CRYPTO_set_id_callback(lws_openssl_thread_id); - CRYPTO_set_locking_callback(lws_openssl_lock_callback); - } -#endif - - return 0; -} - -void -lws_context_deinit_ssl_library(struct lws_context *context) -{ -#if LWS_MAX_SMP != 1 - int n; - - if (!lws_check_opt(context->options, - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) - return; - - CRYPTO_set_locking_callback(NULL); - - for (n = 0; n < CRYPTO_num_locks(); n++) - pthread_mutex_destroy(&openssl_mutexes[n]); - - OPENSSL_free(openssl_mutexes); -#endif -} diff -Nru libwebsockets-4.0.20/lib/tls/openssl/openssl-x509.c libwebsockets-2.4.2/lib/tls/openssl/openssl-x509.c --- libwebsockets-4.0.20/lib/tls/openssl/openssl-x509.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/openssl/openssl-x509.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,667 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-tls-openssl.h" - -#if !defined(LWS_PLAT_OPTEE) -static int -dec(char c) -{ - return c - '0'; -} -#endif - -static time_t -lws_tls_openssl_asn1time_to_unix(ASN1_TIME *as) -{ -#if !defined(LWS_PLAT_OPTEE) - - const char *p = (const char *)as->data; - struct tm t; - - /* [YY]YYMMDDHHMMSSZ */ - - memset(&t, 0, sizeof(t)); - - if (strlen(p) == 13) { - t.tm_year = (dec(p[0]) * 10) + dec(p[1]) + 100; - p += 2; - } else { - t.tm_year = (dec(p[0]) * 1000) + (dec(p[1]) * 100) + - (dec(p[2]) * 10) + dec(p[3]); - p += 4; - } - t.tm_mon = (dec(p[0]) * 10) + dec(p[1]) - 1; - p += 2; - t.tm_mday = (dec(p[0]) * 10) + dec(p[1]) - 1; - p += 2; - t.tm_hour = (dec(p[0]) * 10) + dec(p[1]); - p += 2; - t.tm_min = (dec(p[0]) * 10) + dec(p[1]); - p += 2; - t.tm_sec = (dec(p[0]) * 10) + dec(p[1]); - t.tm_isdst = 0; - - return mktime(&t); -#else - return (time_t)-1; -#endif -} - -int -lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len) -{ - X509_NAME *xn; -#if !defined(LWS_PLAT_OPTEE) - char *p; -#endif - - if (!x509) - return -1; - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(X509_get_notBefore) -#define X509_get_notBefore(x) X509_getm_notBefore(x) -#define X509_get_notAfter(x) X509_getm_notAfter(x) -#endif - - switch (type) { - case LWS_TLS_CERT_INFO_VALIDITY_FROM: - buf->time = lws_tls_openssl_asn1time_to_unix( - X509_get_notBefore(x509)); - if (buf->time == (time_t)-1) - return -1; - break; - - case LWS_TLS_CERT_INFO_VALIDITY_TO: - buf->time = lws_tls_openssl_asn1time_to_unix( - X509_get_notAfter(x509)); - if (buf->time == (time_t)-1) - return -1; - break; - - case LWS_TLS_CERT_INFO_COMMON_NAME: -#if defined(LWS_PLAT_OPTEE) - return -1; -#else - xn = X509_get_subject_name(x509); - if (!xn) - return -1; - X509_NAME_oneline(xn, buf->ns.name, (int)len - 2); - p = strstr(buf->ns.name, "/CN="); - if (p) - memmove(buf->ns.name, p + 4, strlen(p + 4) + 1); - buf->ns.len = (int)strlen(buf->ns.name); - return 0; -#endif - case LWS_TLS_CERT_INFO_ISSUER_NAME: - xn = X509_get_issuer_name(x509); - if (!xn) - return -1; - X509_NAME_oneline(xn, buf->ns.name, (int)len - 1); - buf->ns.len = (int)strlen(buf->ns.name); - return 0; - - case LWS_TLS_CERT_INFO_USAGE: -#if defined(LWS_HAVE_X509_get_key_usage) - buf->usage = X509_get_key_usage(x509); - break; -#else - return -1; -#endif - - case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY: - { -#ifndef USE_WOLFSSL - size_t klen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509), NULL); - uint8_t *tmp, *ptmp; - - if (!klen || klen > len) - return -1; - - tmp = (uint8_t *)OPENSSL_malloc(klen); - if (!tmp) - return -1; - - ptmp = tmp; - if (i2d_X509_PUBKEY( - X509_get_X509_PUBKEY(x509), &ptmp) != (int)klen || - !ptmp || lws_ptr_diff(ptmp, tmp) != (int)klen) { - lwsl_info("%s: cert public key extraction failed\n", - __func__); - if (ptmp) - OPENSSL_free(tmp); - - return -1; - } - - buf->ns.len = (int)klen; - memcpy(buf->ns.name, tmp, klen); - OPENSSL_free(tmp); -#endif - return 0; - } - default: - return -1; - } - - return 0; -} - -int -lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len) -{ - return lws_tls_openssl_cert_info(x509->cert, type, buf, len); -} - -#if defined(LWS_WITH_NETWORK) -int -lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len) -{ -#if defined(LWS_HAVE_SSL_CTX_get0_certificate) - X509 *x509 = SSL_CTX_get0_certificate(vhost->tls.ssl_ctx); - - return lws_tls_openssl_cert_info(x509, type, buf, len); -#else - lwsl_notice("openssl is too old to support %s\n", __func__); - - return -1; -#endif -} - - - -int -lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len) -{ - int rc = 0; - X509 *x509; - - wsi = lws_get_network_wsi(wsi); - - x509 = SSL_get_peer_certificate(wsi->tls.ssl); - - if (!x509) { - lwsl_debug("no peer cert\n"); - - return -1; - } - - switch (type) { - case LWS_TLS_CERT_INFO_VERIFIED: - buf->verified = SSL_get_verify_result(wsi->tls.ssl) == - X509_V_OK; - break; - default: - rc = lws_tls_openssl_cert_info(x509, type, buf, len); - } - - X509_free(x509); - - return rc; -} -#endif - -int -lws_x509_create(struct lws_x509_cert **x509) -{ - *x509 = lws_malloc(sizeof(**x509), __func__); - if (*x509) - (*x509)->cert = NULL; - - return !(*x509); -} - -int -lws_x509_parse_from_pem(struct lws_x509_cert *x509, const void *pem, size_t len) -{ - BIO* bio = BIO_new(BIO_s_mem()); - - BIO_write(bio, pem, (int)len); - x509->cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); - BIO_free(bio); - if (!x509->cert) { - lwsl_err("%s: unable to parse PEM cert\n", __func__); - lws_tls_err_describe_clear(); - - return -1; - } - - return 0; -} - -int -lws_x509_verify(struct lws_x509_cert *x509, struct lws_x509_cert *trusted, - const char *common_name) -{ - char c[32], *p; - int ret; - - if (common_name) { - X509_NAME *xn = X509_get_subject_name(x509->cert); - if (!xn) - return -1; - X509_NAME_oneline(xn, c, (int)sizeof(c) - 2); - p = strstr(c, "/CN="); - if (p) - p = p + 4; - else - p = c; - - if (strcmp(p, common_name)) { - lwsl_err("%s: common name mismatch\n", __func__); - return -1; - } - } - - ret = X509_check_issued(trusted->cert, x509->cert); - if (ret != X509_V_OK) { - lwsl_err("%s: unable to verify cert relationship\n", __func__); - lws_tls_err_describe_clear(); - - return -1; - } - - return 0; -} - -#if defined(LWS_WITH_JOSE) -int -lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509, - const char *curves, int rsa_min_bits) -{ - int id, n, ret = -1, count; - ASN1_OBJECT *obj = NULL; - const EC_POINT *ecpoint; - const EC_GROUP *ecgroup; - EC_KEY *ecpub = NULL; - X509_PUBKEY *pubkey; - RSA *rsapub = NULL; - BIGNUM *mpi[4]; - EVP_PKEY *pkey; - - memset(jwk, 0, sizeof(*jwk)); - - pubkey = X509_get_X509_PUBKEY(x509->cert); - if (!pubkey) { - lwsl_err("%s: missing pubkey alg in cert\n", __func__); - - goto bail; - } - - if (X509_PUBKEY_get0_param(&obj, NULL, NULL, NULL, pubkey) != 1) { - lwsl_err("%s: missing pubkey alg in cert\n", __func__); - - goto bail; - } - - id = OBJ_obj2nid(obj); - if (id == NID_undef) { - lwsl_err("%s: missing pubkey alg in cert\n", __func__); - - goto bail; - } - - lwsl_debug("%s: key type %d \"%s\"\n", __func__, id, OBJ_nid2ln(id)); - - pkey = X509_get_pubkey(x509->cert); - if (!pkey) { - lwsl_notice("%s: unable to extract pubkey", __func__); - - goto bail; - } - - switch (id) { - case NID_X9_62_id_ecPublicKey: - lwsl_debug("%s: EC key\n", __func__); - jwk->kty = LWS_GENCRYPTO_KTY_EC; - - if (!curves) { - lwsl_err("%s: ec curves not allowed\n", __func__); - - goto bail1; - } - - ecpub = EVP_PKEY_get1_EC_KEY(pkey); - if (!ecpub) { - lwsl_notice("%s: missing EC pubkey\n", __func__); - - goto bail1; - } - - ecpoint = EC_KEY_get0_public_key(ecpub); - if (!ecpoint) { - lwsl_err("%s: EC_KEY_get0_public_key failed\n", __func__); - goto bail2; - } - - ecgroup = EC_KEY_get0_group(ecpub); - if (!ecgroup) { - lwsl_err("%s: EC_KEY_get0_group failed\n", __func__); - goto bail2; - } - - /* validate the curve against ones we allow */ - - if (lws_genec_confirm_curve_allowed_by_tls_id(curves, - EC_GROUP_get_curve_name(ecgroup), jwk)) - /* already logged */ - goto bail2; - - mpi[LWS_GENCRYPTO_EC_KEYEL_CRV] = NULL; - mpi[LWS_GENCRYPTO_EC_KEYEL_X] = BN_new(); /* X */ - mpi[LWS_GENCRYPTO_EC_KEYEL_D] = NULL; - mpi[LWS_GENCRYPTO_EC_KEYEL_Y] = BN_new(); /* Y */ - -#if defined(LWS_HAVE_EC_POINT_get_affine_coordinates) - if (EC_POINT_get_affine_coordinates(ecgroup, ecpoint, -#else - if (EC_POINT_get_affine_coordinates_GFp(ecgroup, ecpoint, -#endif - mpi[LWS_GENCRYPTO_EC_KEYEL_X], - mpi[LWS_GENCRYPTO_EC_KEYEL_Y], - NULL) != 1) { - BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]); - BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]); - lwsl_err("%s: EC_POINT_get_aff failed\n", __func__); - goto bail2; - } - count = LWS_GENCRYPTO_EC_KEYEL_COUNT; - n = LWS_GENCRYPTO_EC_KEYEL_X; - break; - - case NID_rsaEncryption: - lwsl_debug("%s: rsa key\n", __func__); - jwk->kty = LWS_GENCRYPTO_KTY_RSA; - - rsapub = EVP_PKEY_get1_RSA(pkey); - if (!rsapub) { - lwsl_notice("%s: missing RSA pubkey\n", __func__); - - goto bail1; - } - - if ((size_t)RSA_size(rsapub) * 8 < (size_t)rsa_min_bits) { - lwsl_err("%s: key bits %d less than minimum %d\n", - __func__, RSA_size(rsapub) * 8, rsa_min_bits); - - goto bail2; - } - -#if defined(LWS_HAVE_RSA_SET0_KEY) - /* we don't need d... but the api wants to write it */ - RSA_get0_key(rsapub, - (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_N], - (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_E], - (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_D]); -#else - mpi[LWS_GENCRYPTO_RSA_KEYEL_E] = rsapub->e; - mpi[LWS_GENCRYPTO_RSA_KEYEL_N] = rsapub->n; - mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = NULL; -#endif - count = LWS_GENCRYPTO_RSA_KEYEL_D; - n = LWS_GENCRYPTO_RSA_KEYEL_E; - break; - default: - lwsl_err("%s: unknown NID\n", __func__); - goto bail2; - } - - for (; n < count; n++) { - if (!mpi[n]) - continue; - jwk->e[n].len = BN_num_bytes(mpi[n]); - jwk->e[n].buf = lws_malloc(jwk->e[n].len, "certkeyimp"); - if (!jwk->e[n].buf) { - if (id == NID_X9_62_id_ecPublicKey) { - BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]); - BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]); - } - goto bail2; - } - BN_bn2bin(mpi[n], jwk->e[n].buf); - } - - if (id == NID_X9_62_id_ecPublicKey) { - BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]); - BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]); - } - - ret = 0; - -bail2: - if (id == NID_X9_62_id_ecPublicKey) - EC_KEY_free(ecpub); - else - RSA_free(rsapub); - -bail1: - EVP_PKEY_free(pkey); -bail: - /* jwk destroy will clean any partial state */ - if (ret) - lws_jwk_destroy(jwk); - - return ret; -} - -static int -lws_x509_jwk_privkey_pem_pp_cb(char *buf, int size, int rwflag, void *u) -{ - const char *pp = (const char *)u; - int n = strlen(pp); - - if (n > size - 1) - return -1; - - memcpy(buf, pp, n + 1); - - return n; -} - -int -lws_x509_jwk_privkey_pem(struct lws_jwk *jwk, void *pem, size_t len, - const char *passphrase) -{ - BIO* bio = BIO_new(BIO_s_mem()); - BIGNUM *mpi, *dummy[6]; - EVP_PKEY *pkey = NULL; - EC_KEY *ecpriv = NULL; - RSA *rsapriv = NULL; - const BIGNUM *cmpi; - int n, m, ret = -1; - - BIO_write(bio, pem, (int)len); - PEM_read_bio_PrivateKey(bio, &pkey, lws_x509_jwk_privkey_pem_pp_cb, - (void *)passphrase); - BIO_free(bio); - lws_explicit_bzero((void *)pem, len); - if (!pkey) { - lwsl_err("%s: unable to parse PEM privkey\n", __func__); - lws_tls_err_describe_clear(); - - return -1; - } - - /* confirm the key type matches the existing jwk situation */ - - switch (jwk->kty) { - case LWS_GENCRYPTO_KTY_EC: - if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) { - lwsl_err("%s: jwk is EC but privkey isn't\n", __func__); - - goto bail; - } - ecpriv = EVP_PKEY_get1_EC_KEY(pkey); - if (!ecpriv) { - lwsl_notice("%s: missing EC key\n", __func__); - - goto bail; - } - - cmpi = EC_KEY_get0_private_key(ecpriv); - - /* quick size check first */ - - n = BN_num_bytes(cmpi); - if (jwk->e[LWS_GENCRYPTO_EC_KEYEL_Y].len != (uint32_t)n) { - lwsl_err("%s: jwk key size doesn't match\n", __func__); - - goto bail1; - } - - /* TODO.. check public curve / group + point */ - - jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len = n; - jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf = lws_malloc(n, "ec"); - if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf) - goto bail1; - - m = BN_bn2binpad(cmpi, jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf, - jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len); - if ((unsigned int)m != (unsigned int)BN_num_bytes(cmpi)) - goto bail1; - - break; - - case LWS_GENCRYPTO_KTY_RSA: - if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_RSA) { - lwsl_err("%s: RSA jwk, non-RSA privkey\n", __func__); - - goto bail; - } - rsapriv = EVP_PKEY_get1_RSA(pkey); - if (!rsapriv) { - lwsl_notice("%s: missing RSA key\n", __func__); - - goto bail; - } - -#if defined(LWS_HAVE_RSA_SET0_KEY) - RSA_get0_key(rsapriv, (const BIGNUM **)&dummy[0], /* n */ - (const BIGNUM **)&dummy[1], /* e */ - (const BIGNUM **)&mpi); /* d */ - RSA_get0_factors(rsapriv, (const BIGNUM **)&dummy[4], /* p */ - (const BIGNUM **)&dummy[5]); /* q */ -#else - dummy[0] = rsapriv->n; - dummy[1] = rsapriv->e; - dummy[4] = rsapriv->p; - dummy[5] = rsapriv->q; - mpi = rsapriv->d; -#endif - - /* quick size check first */ - - n = BN_num_bytes(mpi); - if (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len != (uint32_t)n) { - lwsl_err("%s: jwk key size doesn't match\n", __func__); - - goto bail1; - } - - /* then check that n & e match what we got from the cert */ - - dummy[2] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].buf, - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len, - NULL); - dummy[3] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf, - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].len, - NULL); - - m = BN_cmp(dummy[2], dummy[0]) | BN_cmp(dummy[3], dummy[1]); - BN_clear_free(dummy[2]); - BN_clear_free(dummy[3]); - if (m) { - lwsl_err("%s: privkey doesn't match jwk pubkey\n", - __func__); - - goto bail1; - } - - /* accept d from the PEM privkey into the JWK */ - - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].len = n; - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf = lws_malloc(n, "privjk"); - if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) - goto bail1; - - BN_bn2bin(mpi, jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf); - - /* accept p and q from the PEM privkey into the JWK */ - - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].len = BN_num_bytes(dummy[4]); - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf = lws_malloc(n, "privjk"); - if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) { - lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf); - goto bail1; - } - BN_bn2bin(dummy[4], jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf); - - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].len = BN_num_bytes(dummy[5]); - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf = lws_malloc(n, "privjk"); - if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf) { - lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf); - lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf); - goto bail1; - } - BN_bn2bin(dummy[5], jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf); - break; - default: - lwsl_err("%s: JWK has unknown kty %d\n", __func__, jwk->kty); - return -1; - } - - ret = 0; - -bail1: - if (jwk->kty == LWS_GENCRYPTO_KTY_EC) - EC_KEY_free(ecpriv); - else - RSA_free(rsapriv); - -bail: - EVP_PKEY_free(pkey); - - return ret; -} -#endif - -void -lws_x509_destroy(struct lws_x509_cert **x509) -{ - if (!*x509) - return; - - if ((*x509)->cert) { - X509_free((*x509)->cert); - (*x509)->cert = NULL; - } - - lws_free_set_NULL(*x509); -} diff -Nru libwebsockets-4.0.20/lib/tls/openssl/private-lib-tls-openssl.h libwebsockets-2.4.2/lib/tls/openssl/private-lib-tls-openssl.h --- libwebsockets-4.0.20/lib/tls/openssl/private-lib-tls-openssl.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/openssl/private-lib-tls-openssl.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * gencrypto openssl-specific helper declarations - */ - -#if !defined(__LWS_PRIVATE_LIB_TLS_OPENSSL_H__) -#define __LWS_PRIVATE_LIB_TLS_OPENSSL_H__ - -/* - * one of these per different client context - * cc_owner is in lws_context.lws_context_tls - */ - -struct lws_tls_client_reuse { - lws_tls_ctx *ssl_client_ctx; - uint8_t hash[32]; - struct lws_dll2 cc_list; - int refcount; - int index; -}; - -typedef int (*next_proto_cb)(SSL *, const unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg); - -struct lws_x509_cert { - X509 *cert; /* X509 is opaque, this has to be a pointer */ -}; - -int -lws_gencrypto_openssl_hash_to_NID(enum lws_genhash_types hash_type); - -const EVP_MD * -lws_gencrypto_openssl_hash_to_EVP_MD(enum lws_genhash_types hash_type); - -#if !defined(LWS_HAVE_BN_bn2binpad) -int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen); -#endif - -#endif - diff -Nru libwebsockets-4.0.20/lib/tls/private-lib-tls.h libwebsockets-2.4.2/lib/tls/private-lib-tls.h --- libwebsockets-4.0.20/lib/tls/private-lib-tls.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/private-lib-tls.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,201 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_TLS - */ - -#if !defined(__LWS_TLS_PRIVATE_H__) -#define __LWS_TLS_PRIVATE_H__ - - -#if defined(LWS_WITH_TLS) - -#if defined(USE_WOLFSSL) - #if defined(USE_OLD_CYASSL) - #if defined(_WIN32) - #include - #include - #else - #include - #endif - #include - #include - #else - #if defined(_WIN32) - #include - #include - #else - #include - #endif - #include - #include - #define OPENSSL_NO_TLSEXT - #endif /* not USE_OLD_CYASSL */ -#else /* WOLFSSL */ - #if defined(LWS_PLAT_FREERTOS) - #define OPENSSL_NO_TLSEXT - #if !defined(LWS_AMAZON_RTOS) - /* AMAZON RTOS has its own setting via MTK_MBEDTLS_CONFIG_FILE */ - #undef MBEDTLS_CONFIG_FILE - #define MBEDTLS_CONFIG_FILE - #endif - #include - #include - #include - #include - #include "ssl.h" /* wrapper !!!! */ - #else /* not esp32 */ - #if defined(LWS_WITH_MBEDTLS) - #include - #include - #include - #include - #include - #include - #include - #if defined(LWS_AMAZON_LINUX) - #include "ssl.h" /* wrapper !!!! */ - #else - #include "openssl/ssl.h" /* wrapper !!!! */ - #endif - #else - #include - #include - #include - #include - #include - #include - #include - #include - #ifdef LWS_HAVE_OPENSSL_ECDH_H - #include - #endif - #if !defined(LWS_HAVE_EVP_MD_CTX_free) - #define EVP_MD_CTX_free EVP_MD_CTX_destroy - #endif - #include - #endif /* not mbedtls */ - #if defined(OPENSSL_VERSION_NUMBER) - #if (OPENSSL_VERSION_NUMBER < 0x0009080afL) -/* - * later openssl defines this to negate the presence of tlsext... but it was - * only introduced at 0.9.8j. Earlier versions don't know it exists so don't - * define it... making it look like the feature exists... - */ - #define OPENSSL_NO_TLSEXT - #endif - #endif - #endif /* not ESP32 */ -#endif /* not USE_WOLFSSL */ - -#endif /* LWS_WITH_TLS */ - -enum lws_tls_extant { - LWS_TLS_EXTANT_NO, - LWS_TLS_EXTANT_YES, - LWS_TLS_EXTANT_ALTERNATIVE -}; - - -#if defined(LWS_WITH_TLS) - -int -lws_tls_restrict_borrow(struct lws_context *context); - -void -lws_tls_restrict_return(struct lws_context *context); - -typedef SSL lws_tls_conn; -typedef SSL_CTX lws_tls_ctx; -typedef BIO lws_tls_bio; -typedef X509 lws_tls_x509; - -#if defined(LWS_WITH_NETWORK) -#include "private-network.h" -#endif - -LWS_EXTERN int -lws_context_init_ssl_library(const struct lws_context_creation_info *info); -LWS_EXTERN void -lws_context_deinit_ssl_library(struct lws_context *context); -#define LWS_SSL_ENABLED(vh) (vh && vh->tls.use_ssl) - -extern const struct lws_tls_ops tls_ops_openssl, tls_ops_mbedtls; - -struct lws_ec_valid_curves { - int id; - const char *jwa_name; /* list terminates with NULL jwa_name */ -}; - -LWS_EXTERN enum lws_tls_extant -lws_tls_use_any_upgrade_check_extant(const char *name); -LWS_EXTERN int openssl_websocket_private_data_index; - - -LWS_EXTERN void -lws_tls_err_describe_clear(void); - -LWS_EXTERN int -lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len); -LWS_EXTERN int -lws_tls_check_all_cert_lifetimes(struct lws_context *context); - -LWS_EXTERN int -lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename, - const char *inbuf, lws_filepos_t inlen, - uint8_t **buf, lws_filepos_t *amount); -LWS_EXTERN char * -lws_ssl_get_error_string(int status, int ret, char *buf, size_t len); - -int -lws_gencrypto_bits_to_bytes(int bits); - -void -lws_gencrypto_destroy_elements(struct lws_gencrypto_keyelem *el, int m); - -/* genec */ - -struct lws_gencrypto_keyelem; -struct lws_ec_curves; - -LWS_EXTERN const struct lws_ec_curves lws_ec_curves[4]; -const struct lws_ec_curves * -lws_genec_curve(const struct lws_ec_curves *table, const char *name); -LWS_VISIBLE void -lws_genec_destroy_elements(struct lws_gencrypto_keyelem *el); -int -lws_gencrypto_mbedtls_rngf(void *context, unsigned char *buf, size_t len); - -int -lws_genec_confirm_curve_allowed_by_tls_id(const char *allowed, int id, - struct lws_jwk *jwk); - - -#else /* ! WITH_TLS */ - -#define lws_tls_restrict_borrow(xxx) (0) -#define lws_tls_restrict_return(xxx) - -#endif -#endif diff -Nru libwebsockets-4.0.20/lib/tls/private-network.h libwebsockets-2.4.2/lib/tls/private-network.h --- libwebsockets-4.0.20/lib/tls/private-network.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/private-network.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,188 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_TLS - */ - -struct lws_context_per_thread; -struct lws_tls_ops { - int (*fake_POLLIN_for_buffered)(struct lws_context_per_thread *pt); -}; - -struct lws_context_tls { - char alpn_discovered[32]; - const char *alpn_default; - time_t last_cert_check_s; - struct lws_dll2_owner cc_owner; - int count_client_contexts; -}; - -struct lws_pt_tls { - struct lws_dll2_owner dll_pending_tls_owner; -}; - -struct lws_tls_ss_pieces; - -struct alpn_ctx { - uint8_t data[23]; - uint8_t len; -}; - -struct lws_vhost_tls { - lws_tls_ctx *ssl_ctx; - lws_tls_ctx *ssl_client_ctx; - const char *alpn; - struct lws_tls_ss_pieces *ss; /* for acme tls certs */ - char *alloc_cert_path; - char *key_path; -#if defined(LWS_WITH_MBEDTLS) - lws_tls_x509 *x509_client_CA; -#endif - char ecdh_curve[16]; - struct alpn_ctx alpn_ctx; - - int use_ssl; - int allow_non_ssl_on_ssl_port; - int ssl_info_event_mask; - - unsigned int user_supplied_ssl_ctx:1; - unsigned int skipped_certs:1; -}; - -struct lws_lws_tls { - lws_tls_conn *ssl; - lws_tls_bio *client_bio; - struct lws_dll2 dll_pending_tls; - unsigned int use_ssl; - unsigned int redirect_to_https:1; -}; - - -LWS_EXTERN void -lws_context_init_alpn(struct lws_vhost *vhost); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ssl_pending(struct lws *wsi); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd); -LWS_EXTERN int -lws_ssl_close(struct lws *wsi); -LWS_EXTERN void -lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost); -LWS_EXTERN void -lws_ssl_context_destroy(struct lws_context *context); -void -__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi); -LWS_VISIBLE void -lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi); -LWS_EXTERN int -lws_ssl_client_bio_create(struct lws *wsi); -LWS_EXTERN int -lws_ssl_client_connect1(struct lws *wsi); -LWS_EXTERN int -lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len); -LWS_EXTERN int -lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt); -LWS_EXTERN int -lws_gate_accepts(struct lws_context *context, int on); -LWS_EXTERN void -lws_ssl_bind_passphrase(lws_tls_ctx *ssl_ctx, int is_client, - const struct lws_context_creation_info *info); -LWS_EXTERN void -lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret); -LWS_EXTERN int -lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, - const char *cert, const char *private_key, - const char *mem_cert, size_t len_mem_cert, - const char *mem_privkey, size_t mem_privkey_len); -LWS_EXTERN enum lws_tls_extant -lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert, - const char *private_key); -#if defined(LWS_WITH_SERVER) - LWS_EXTERN int - lws_context_init_server_ssl(const struct lws_context_creation_info *info, - struct lws_vhost *vhost); - void - lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost); -#else - #define lws_context_init_server_ssl(_a, _b) (0) - #define lws_tls_acme_sni_cert_destroy(_a) -#endif - -LWS_EXTERN void -lws_ssl_destroy(struct lws_vhost *vhost); - -/* -* lws_tls_ abstract backend implementations -*/ - -LWS_EXTERN int -lws_tls_server_client_cert_verify_config(struct lws_vhost *vh); -LWS_EXTERN int -lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, - struct lws_vhost *vhost, struct lws *wsi); -LWS_EXTERN int -lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd); - -LWS_EXTERN enum lws_ssl_capable_status -lws_tls_server_accept(struct lws *wsi); - -LWS_EXTERN enum lws_ssl_capable_status -lws_tls_server_abort_connection(struct lws *wsi); - -LWS_EXTERN enum lws_ssl_capable_status -__lws_tls_shutdown(struct lws *wsi); - -LWS_EXTERN enum lws_ssl_capable_status -lws_tls_client_connect(struct lws *wsi); -LWS_EXTERN int -lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len); -LWS_EXTERN int -lws_tls_client_create_vhost_context(struct lws_vhost *vh, - const struct lws_context_creation_info *info, - const char *cipher_list, - const char *ca_filepath, - const void *ca_mem, - unsigned int ca_mem_len, - const char *cert_filepath, - const void *cert_mem, - unsigned int cert_mem_len, - const char *private_key_filepath); - -LWS_EXTERN lws_tls_ctx * -lws_tls_ctx_from_wsi(struct lws *wsi); -LWS_EXTERN int -lws_ssl_get_error(struct lws *wsi, int n); - -LWS_EXTERN int -lws_context_init_client_ssl(const struct lws_context_creation_info *info, - struct lws_vhost *vhost); - -LWS_EXTERN void -lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret); - -int -lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt); diff -Nru libwebsockets-4.0.20/lib/tls/tls.c libwebsockets-2.4.2/lib/tls/tls.c --- libwebsockets-4.0.20/lib/tls/tls.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/tls.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,453 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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 "private-lib-tls.h" - -#if defined(LWS_WITH_NETWORK) -#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \ - OPENSSL_VERSION_NUMBER >= 0x10002000L) -static int -alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, void *arg) -{ -#if !defined(LWS_WITH_MBEDTLS) - struct alpn_ctx *alpn_ctx = (struct alpn_ctx *)arg; - - if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_ctx->data, - alpn_ctx->len, in, inlen) != - OPENSSL_NPN_NEGOTIATED) - return SSL_TLSEXT_ERR_NOACK; -#endif - - return SSL_TLSEXT_ERR_OK; -} -#endif - -int -lws_tls_restrict_borrow(struct lws_context *context) -{ - if (!context->simultaneous_ssl_restriction) - return 0; - - if (context->simultaneous_ssl >= context->simultaneous_ssl_restriction) { - lwsl_notice("%s: tls connection limit %d\n", __func__, - context->simultaneous_ssl); - return 1; - } - - if (++context->simultaneous_ssl == context->simultaneous_ssl_restriction) - /* that was the last allowed SSL connection */ - lws_gate_accepts(context, 0); - - lwsl_info("%s: %d -> %d\n", __func__, - context->simultaneous_ssl - 1, - context->simultaneous_ssl); - - return 0; -} - -void -lws_tls_restrict_return(struct lws_context *context) -{ - if (context->simultaneous_ssl_restriction) { - if (context->simultaneous_ssl-- == - context->simultaneous_ssl_restriction) - /* we made space and can do an accept */ - lws_gate_accepts(context, 1); - lwsl_info("%s: %d -> %d\n", __func__, - context->simultaneous_ssl + 1, - context->simultaneous_ssl); - } -} - -void -lws_context_init_alpn(struct lws_vhost *vhost) -{ -#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \ - OPENSSL_VERSION_NUMBER >= 0x10002000L) - const char *alpn_comma = vhost->context->tls.alpn_default; - - if (vhost->tls.alpn) - alpn_comma = vhost->tls.alpn; - - lwsl_info(" Server '%s' advertising ALPN: %s\n", - vhost->name, alpn_comma); - vhost->tls.alpn_ctx.len = lws_alpn_comma_to_openssl(alpn_comma, - vhost->tls.alpn_ctx.data, - sizeof(vhost->tls.alpn_ctx.data) - 1); - - SSL_CTX_set_alpn_select_cb(vhost->tls.ssl_ctx, alpn_cb, - &vhost->tls.alpn_ctx); -#else - lwsl_err( - " HTTP2 / ALPN configured but not supported by OpenSSL 0x%lx\n", - OPENSSL_VERSION_NUMBER); -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L -} - -int -lws_tls_server_conn_alpn(struct lws *wsi) -{ -#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \ - OPENSSL_VERSION_NUMBER >= 0x10002000L) - const unsigned char *name = NULL; - char cstr[10]; - unsigned len; - - if (!wsi->tls.ssl) - return 0; - - SSL_get0_alpn_selected(wsi->tls.ssl, &name, &len); - if (!len) { - lwsl_info("no ALPN upgrade\n"); - return 0; - } - - if (len > sizeof(cstr) - 1) - len = sizeof(cstr) - 1; - - memcpy(cstr, name, len); - cstr[len] = '\0'; - - lwsl_info("negotiated '%s' using ALPN\n", cstr); - wsi->tls.use_ssl |= LCCSCF_USE_SSL; - - return lws_role_call_alpn_negotiated(wsi, (const char *)cstr); -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L - - return 0; -} -#endif - -#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT) -#if defined(LWS_PLAT_FREERTOS) && !defined(LWS_AMAZON_RTOS) -int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, - lws_filepos_t *amount) -{ - nvs_handle nvh; - size_t s; - int n = 0; - - ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh)); - if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) { - n = 1; - goto bail; - } - *buf = lws_malloc(s + 1, "alloc_file"); - if (!*buf) { - n = 2; - goto bail; - } - if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) { - lws_free(*buf); - n = 1; - goto bail; - } - - *amount = s; - (*buf)[s] = '\0'; - - lwsl_notice("%s: nvs: read %s, %d bytes\n", __func__, filename, (int)s); - -bail: - nvs_close(nvh); - - return n; -} -#else -int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, - lws_filepos_t *amount) -{ - FILE *f; - size_t s; - int n = 0; - - f = fopen(filename, "rb"); - if (f == NULL) { - n = 1; - goto bail; - } - - if (fseek(f, 0, SEEK_END) != 0) { - n = 1; - goto bail; - } - - s = ftell(f); - if (s == (size_t)-1) { - n = 1; - goto bail; - } - - if (fseek(f, 0, SEEK_SET) != 0) { - n = 1; - goto bail; - } - - *buf = lws_malloc(s, "alloc_file"); - if (!*buf) { - n = 2; - goto bail; - } - - if (fread(*buf, s, 1, f) != 1) { - lws_free(*buf); - n = 1; - goto bail; - } - - *amount = s; - -bail: - if (f) - fclose(f); - - return n; - -} -#endif - -/* - * filename: NULL means use buffer inbuf length inlen directly, otherwise - * load the file "filename" into an allocated buffer. - * - * Allocates a separate DER output buffer if inbuf / inlen are the input, - * since the - * - * Contents may be PEM or DER: returns with buf pointing to DER and amount - * set to the DER length. - */ - -int -lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename, - const char *inbuf, lws_filepos_t inlen, - uint8_t **buf, lws_filepos_t *amount) -{ - uint8_t *pem = NULL, *p, *end, *opem; - lws_filepos_t len; - uint8_t *q; - int n; - - if (filename) { - n = alloc_file(context, filename, (uint8_t **)&pem, &len); - if (n) - return n; - } else { - pem = (uint8_t *)inbuf; - len = inlen; - } - - opem = p = pem; - end = p + len; - - if (strncmp((char *)p, "-----", 5)) { - - /* take it as being already DER */ - - pem = lws_malloc((size_t)inlen, "alloc_der"); - if (!pem) - return 1; - - memcpy(pem, inbuf, (size_t)inlen); - - *buf = pem; - *amount = inlen; - - return 0; - } - - /* PEM -> DER */ - - if (!filename) { - /* we don't know if it's in const memory... alloc the output */ - pem = lws_malloc(((size_t)inlen * 3) / 4, "alloc_der"); - if (!pem) { - lwsl_err("a\n"); - return 1; - } - - - } /* else overwrite the allocated, b64 input with decoded DER */ - - /* trim the first line */ - - p += 5; - while (p < end && *p != '\n' && *p != '-') - p++; - - if (*p != '-') { - lwsl_err("b\n"); - goto bail; - } - - while (p < end && *p != '\n') - p++; - - if (p >= end) { - lwsl_err("c\n"); - goto bail; - } - - p++; - - /* trim the last line */ - - q = (uint8_t *)end - 2; - - while (q > opem && *q != '\n') - q--; - - if (*q != '\n') { - lwsl_err("d\n"); - goto bail; - } - - /* we can't write into the input buffer for mem, since it may be in RO - * const segment - */ - if (filename) - *q = '\0'; - - *amount = lws_b64_decode_string_len((char *)p, lws_ptr_diff(q, p), - (char *)pem, (int)(long long)len); - *buf = (uint8_t *)pem; - - return 0; - -bail: - lws_free((uint8_t *)pem); - - return 4; -} - - -#endif - -#if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT) - - -static int -lws_tls_extant(const char *name) -{ - /* it exists if we can open it... */ - int fd = open(name, O_RDONLY), n; - char buf[1]; - - if (fd < 0) - return 1; - - /* and we can read at least one byte out of it */ - n = read(fd, buf, 1); - close(fd); - - return n != 1; -} -#endif -/* - * Returns 0 if the filepath "name" exists and can be read from. - * - * In addition, if "name".upd exists, backup "name" to "name.old.1" - * and rename "name".upd to "name" before reporting its existence. - * - * There are four situations and three results possible: - * - * 1) LWS_TLS_EXTANT_NO: There are no certs at all (we are waiting for them to - * be provisioned). We also feel like this if we need privs we don't have - * any more to look in the directory. - * - * 2) There are provisioned certs written (xxx.upd) and we still have root - * privs... in this case we rename any existing cert to have a backup name - * and move the upd cert into place with the correct name. This then becomes - * situation 4 for the caller. - * - * 3) LWS_TLS_EXTANT_ALTERNATIVE: There are provisioned certs written (xxx.upd) - * but we no longer have the privs needed to read or rename them. In this - * case, indicate that the caller should use temp copies if any we do have - * rights to access. This is normal after we have updated the cert. - * - * But if we dropped privs, we can't detect the provisioned xxx.upd cert + - * key, because we can't see in the dir. So we have to upgrade NO to - * ALTERNATIVE when we actually have the in-memory alternative. - * - * 4) LWS_TLS_EXTANT_YES: The certs are present with the correct name and we - * have the rights to read them. - */ - -enum lws_tls_extant -lws_tls_use_any_upgrade_check_extant(const char *name) -{ -#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_AMAZON_RTOS) - - int n; - -#if !defined(LWS_PLAT_FREERTOS) - char buf[256]; - - lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name); - if (!lws_tls_extant(buf)) { - /* ah there is an updated file... how about the desired file? */ - if (!lws_tls_extant(name)) { - /* rename the desired file */ - for (n = 0; n < 50; n++) { - lws_snprintf(buf, sizeof(buf) - 1, - "%s.old.%d", name, n); - if (!rename(name, buf)) - break; - } - if (n == 50) { - lwsl_notice("unable to rename %s\n", name); - - return LWS_TLS_EXTANT_ALTERNATIVE; - } - lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name); - } - /* desired file is out of the way, rename the updated file */ - if (rename(buf, name)) { - lwsl_notice("unable to rename %s to %s\n", buf, name); - - return LWS_TLS_EXTANT_ALTERNATIVE; - } - } - - if (lws_tls_extant(name)) - return LWS_TLS_EXTANT_NO; -#else - nvs_handle nvh; - size_t s = 8192; - - if (nvs_open("lws-station", NVS_READWRITE, &nvh)) { - lwsl_notice("%s: can't open nvs\n", __func__); - return LWS_TLS_EXTANT_NO; - } - - n = nvs_get_blob(nvh, name, NULL, &s); - nvs_close(nvh); - - if (n) - return LWS_TLS_EXTANT_NO; -#endif -#endif - return LWS_TLS_EXTANT_YES; -} diff -Nru libwebsockets-4.0.20/lib/tls/tls-client.c libwebsockets-2.4.2/lib/tls/tls-client.c --- libwebsockets-4.0.20/lib/tls/tls-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/tls-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,157 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -int -lws_ssl_client_connect1(struct lws *wsi) -{ - int n; - - n = lws_tls_client_connect(wsi); - switch (n) { - case LWS_SSL_CAPABLE_ERROR: - return -1; - case LWS_SSL_CAPABLE_DONE: - return 1; /* connected */ - case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE: - lws_callback_on_writable(wsi); - /* fallthru */ - case LWS_SSL_CAPABLE_MORE_SERVICE: - case LWS_SSL_CAPABLE_MORE_SERVICE_READ: - lwsi_set_state(wsi, LRS_WAITING_SSL); - break; - } - - return 0; /* retry */ -} - -int -lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len) -{ - int n; - - if (lwsi_state(wsi) == LRS_WAITING_SSL) { - n = lws_tls_client_connect(wsi); - lwsl_debug("%s: SSL_connect says %d\n", __func__, n); - - switch (n) { - case LWS_SSL_CAPABLE_ERROR: - lws_snprintf(errbuf, len, "client connect failed"); - return -1; - case LWS_SSL_CAPABLE_DONE: - break; /* connected */ - case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE: - lws_callback_on_writable(wsi); - /* fallthru */ - case LWS_SSL_CAPABLE_MORE_SERVICE_READ: - lwsi_set_state(wsi, LRS_WAITING_SSL); - /* fallthru */ - case LWS_SSL_CAPABLE_MORE_SERVICE: - return 0; - } - } - - if (lws_tls_client_confirm_peer_cert(wsi, errbuf, len)) - return -1; - - return 1; -} - - -int lws_context_init_client_ssl(const struct lws_context_creation_info *info, - struct lws_vhost *vhost) -{ - const char *private_key_filepath = info->ssl_private_key_filepath; - const char *cert_filepath = info->ssl_cert_filepath; - const char *ca_filepath = info->ssl_ca_filepath; - const char *cipher_list = info->ssl_cipher_list; - struct lws *wsi = vhost->context->pt[0].fake_wsi; - - if (vhost->options & LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG) - return 0; - - if (vhost->tls.ssl_ctx) { - cert_filepath = NULL; - private_key_filepath = NULL; - ca_filepath = NULL; - } - - /* - * for backwards-compatibility default to using ssl_... members, but - * if the newer client-specific ones are given, use those - */ - if (info->client_ssl_cipher_list) - cipher_list = info->client_ssl_cipher_list; - if (info->client_ssl_cert_filepath) - cert_filepath = info->client_ssl_cert_filepath; - if (info->client_ssl_private_key_filepath) - private_key_filepath = info->client_ssl_private_key_filepath; - - if (info->client_ssl_ca_filepath) - ca_filepath = info->client_ssl_ca_filepath; - - if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) - return 0; - - if (vhost->tls.ssl_client_ctx) - return 0; - - if (info->provided_client_ssl_ctx) { - /* use the provided OpenSSL context if given one */ - vhost->tls.ssl_client_ctx = info->provided_client_ssl_ctx; - /* nothing for lib to delete */ - vhost->tls.user_supplied_ssl_ctx = 1; - - return 0; - } - - if (lws_tls_client_create_vhost_context(vhost, info, cipher_list, - ca_filepath, - info->client_ssl_ca_mem, - info->client_ssl_ca_mem_len, - cert_filepath, - info->client_ssl_cert_mem, - info->client_ssl_cert_mem_len, - private_key_filepath)) - return 1; - - lwsl_info("created client ssl context for %s\n", vhost->name); - - /* - * give him a fake wsi with context set, so he can use - * lws_get_context() in the callback - */ - - wsi->vhost = vhost; /* not a real bound wsi */ - wsi->context = vhost->context; - wsi->protocol = NULL; - - vhost->protocols[0].callback(wsi, - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS, - vhost->tls.ssl_client_ctx, NULL, 0); - - return 0; -} - diff -Nru libwebsockets-4.0.20/lib/tls/tls-network.c libwebsockets-2.4.2/lib/tls/tls-network.c --- libwebsockets-4.0.20/lib/tls/tls-network.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/tls-network.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,261 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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" - -/* - * fakes POLLIN on all tls guys with buffered rx - * - * returns nonzero if any tls guys had POLLIN faked - */ - -int -lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt) -{ - int ret = 0; - - lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1, - lws_dll2_get_head(&pt->tls.dll_pending_tls_owner)) { - struct lws *wsi = lws_container_of(p, struct lws, - tls.dll_pending_tls); - - if (wsi->position_in_fds_table >= 0) { - - pt->fds[wsi->position_in_fds_table].revents |= - pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; - ret |= pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN; - } - - } lws_end_foreach_dll_safe(p, p1); - - return !!ret; -} - -void -__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi) -{ - lws_dll2_remove(&wsi->tls.dll_pending_tls); -} - -void -lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - lws_pt_lock(pt, __func__); - __lws_ssl_remove_wsi_from_buffered_list(wsi); - lws_pt_unlock(pt); -} - -#if defined(LWS_WITH_SERVER) -int -lws_tls_check_cert_lifetime(struct lws_vhost *v) -{ - time_t now = (time_t)lws_now_secs(), life = 0; - struct lws_acme_cert_aging_args caa; - union lws_tls_cert_info_results ir; - int n; - - if (v->tls.ssl_ctx && !v->tls.skipped_certs) { - - if (now < 1542933698) /* Nov 23 2018 00:42 UTC */ - /* our clock is wrong and we can't judge the certs */ - return -1; - - n = lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, - &ir, 0); - if (n) - return 1; - - life = (ir.time - now) / (24 * 3600); - lwsl_notice(" vhost %s: cert expiry: %dd\n", v->name, - (int)life); - } else - lwsl_info(" vhost %s: no cert\n", v->name); - - memset(&caa, 0, sizeof(caa)); - caa.vh = v; - lws_broadcast(&v->context->pt[0], LWS_CALLBACK_VHOST_CERT_AGING, (void *)&caa, - (size_t)(ssize_t)life); - - return 0; -} - -int -lws_tls_check_all_cert_lifetimes(struct lws_context *context) -{ - struct lws_vhost *v = context->vhost_list; - - while (v) { - if (lws_tls_check_cert_lifetime(v) < 0) - return -1; - v = v->vhost_next; - } - - return 0; -} - -/* - * LWS_TLS_EXTANT_NO : skip adding the cert - * LWS_TLS_EXTANT_YES : use the cert and private key paths normally - * LWS_TLS_EXTANT_ALTERNATIVE: normal paths not usable, try alternate if poss - */ -enum lws_tls_extant -lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert, - const char *private_key) -{ - int n, m; - - /* - * The user code can choose to either pass the cert and - * key filepaths using the info members like this, or it can - * leave them NULL; force the vhost SSL_CTX init using the info - * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and - * set up the cert himself using the user callback - * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which - * happened just above and has the vhost SSL_CTX * in the user - * parameter. - */ - - if (!cert || !private_key) - return LWS_TLS_EXTANT_NO; - - n = lws_tls_use_any_upgrade_check_extant(cert); - if (n == LWS_TLS_EXTANT_ALTERNATIVE) - return LWS_TLS_EXTANT_ALTERNATIVE; - m = lws_tls_use_any_upgrade_check_extant(private_key); - if (m == LWS_TLS_EXTANT_ALTERNATIVE) - return LWS_TLS_EXTANT_ALTERNATIVE; - - if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) && - (vhost->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) { - lwsl_notice("Ignoring missing %s or %s\n", cert, private_key); - vhost->tls.skipped_certs = 1; - - return LWS_TLS_EXTANT_NO; - } - - /* - * the cert + key exist - */ - - return LWS_TLS_EXTANT_YES; -} - -/* - * update the cert for every vhost using the given path - */ - -int -lws_tls_cert_updated(struct lws_context *context, const char *certpath, - const char *keypath, - const char *mem_cert, size_t len_mem_cert, - const char *mem_privkey, size_t len_mem_privkey) -{ - struct lws wsi; - - wsi.context = context; - - lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) { - wsi.vhost = v; /* not a real bound wsi */ - if (v->tls.alloc_cert_path && v->tls.key_path && - !strcmp(v->tls.alloc_cert_path, certpath) && - !strcmp(v->tls.key_path, keypath)) { - lws_tls_server_certs_load(v, &wsi, certpath, keypath, - mem_cert, len_mem_cert, - mem_privkey, len_mem_privkey); - - if (v->tls.skipped_certs) - lwsl_notice("%s: vhost %s: cert unset\n", - __func__, v->name); - } - } lws_end_foreach_ll(v, vhost_next); - - return 0; -} -#endif - -int -lws_gate_accepts(struct lws_context *context, int on) -{ - struct lws_vhost *v = context->vhost_list; - - lwsl_notice("%s: on = %d\n", __func__, on); - -#if defined(LWS_WITH_STATS) - context->updated = 1; -#endif - - while (v) { - if (v->tls.use_ssl && v->lserv_wsi && - lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on, - (LWS_POLLIN) * on)) - lwsl_notice("Unable to set accept POLLIN %d\n", on); - - v = v->vhost_next; - } - - return 0; -} - -/* comma-separated alpn list, like "h2,http/1.1" to openssl alpn format */ - -int -lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len) -{ - uint8_t *oos = os, *plen = NULL; - - if (!comma) - return 0; - - while (*comma && len > 2) { - if (!plen && *comma == ' ') { - comma++; - continue; - } - if (!plen) { - plen = os++; - len--; - } - - if (*comma == ',') { - *plen = lws_ptr_diff(os, plen + 1); - plen = NULL; - comma++; - } else { - *os++ = *comma++; - len--; - } - } - - if (plen) - *plen = lws_ptr_diff(os, plen + 1); - - *os = 0; - - return lws_ptr_diff(os, oos); -} - - - diff -Nru libwebsockets-4.0.20/lib/tls/tls-server.c libwebsockets-2.4.2/lib/tls/tls-server.c --- libwebsockets-4.0.20/lib/tls/tls-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lib/tls/tls-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,371 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_SERVER) - -static void -lws_sul_tls_cb(lws_sorted_usec_list_t *sul) -{ - struct lws_context_per_thread *pt = lws_container_of(sul, - struct lws_context_per_thread, sul_tls); - - lws_tls_check_all_cert_lifetimes(pt->context); - - __lws_sul_insert(&pt->pt_sul_owner, &pt->sul_tls, - (lws_usec_t)24 * 3600 * LWS_US_PER_SEC); -} - -int -lws_context_init_server_ssl(const struct lws_context_creation_info *info, - struct lws_vhost *vhost) -{ - struct lws_context *context = vhost->context; - struct lws *wsi = context->pt[0].fake_wsi; - - if (!lws_check_opt(info->options, - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { - vhost->tls.use_ssl = 0; - - return 0; - } - - /* - * If he is giving a server cert, take it as a sign he wants to use - * it on this vhost. User code can leave the cert filepath NULL and - * set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in - * which case he's expected to set up the cert himself at - * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which - * provides the vhost SSL_CTX * in the user parameter. - */ - if (info->ssl_cert_filepath || info->server_ssl_cert_mem) - vhost->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; - - if (info->port != CONTEXT_PORT_NO_LISTEN) { - - vhost->tls.use_ssl = lws_check_opt(vhost->options, - LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX); - - if (vhost->tls.use_ssl && info->ssl_cipher_list) - lwsl_notice(" SSL ciphers: '%s'\n", - info->ssl_cipher_list); - - lwsl_notice(" Vhost '%s' using %sTLS mode\n", - vhost->name, vhost->tls.use_ssl ? "" : "non-"); - } - - /* - * give him a fake wsi with context + vhost set, so he can use - * lws_get_context() in the callback - */ - wsi->vhost = vhost; /* not a real bound wsi */ - wsi->context = context; - wsi->protocol = NULL; - - /* - * as a server, if we are requiring clients to identify themselves - * then set the backend up for it - */ - if (lws_check_opt(info->options, - LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT)) - /* Normally SSL listener rejects non-ssl, optionally allow */ - vhost->tls.allow_non_ssl_on_ssl_port = 1; - - /* - * give user code a chance to load certs into the server - * allowing it to verify incoming client certs - */ - if (vhost->tls.use_ssl) { - if (lws_tls_server_vhost_backend_init(info, vhost, wsi)) - return -1; - - lws_tls_server_client_cert_verify_config(vhost); - - if (vhost->protocols[0].callback(wsi, - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, - vhost->tls.ssl_ctx, vhost, 0)) - return -1; - } - - if (vhost->tls.use_ssl) - lws_context_init_alpn(vhost); - - /* check certs once a day */ - - context->pt[0].sul_tls.cb = lws_sul_tls_cb; - __lws_sul_insert(&context->pt[0].pt_sul_owner, &context->pt[0].sul_tls, - (lws_usec_t)24 * 3600 * LWS_US_PER_SEC); - - return 0; -} -#endif - -int -lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - struct lws_vhost *vh; - int n; - - if (!LWS_SSL_ENABLED(wsi->vhost)) - return 0; - - switch (lwsi_state(wsi)) { - case LRS_SSL_INIT: - - if (wsi->tls.ssl) - lwsl_err("%s: leaking ssl\n", __func__); - if (accept_fd == LWS_SOCK_INVALID) - assert(0); - - if (lws_tls_restrict_borrow(context)) - return 1; - - if (lws_tls_server_new_nonblocking(wsi, accept_fd)) { - if (accept_fd != LWS_SOCK_INVALID) - compatible_close(accept_fd); - lws_tls_restrict_return(context); - goto fail; - } - -#if defined(LWS_WITH_STATS) - context->updated = 1; -#endif - /* - * we are not accepted yet, but we need to enter ourselves - * as a live connection. That way we can retry when more - * pieces come if we're not sorted yet - */ - lwsi_set_state(wsi, LRS_SSL_ACK_PENDING); - - lws_pt_lock(pt, __func__); - if (__insert_wsi_socket_into_fds(context, wsi)) { - lwsl_err("%s: failed to insert into fds\n", __func__); - goto fail; - } - lws_pt_unlock(pt); - - lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, - context->timeout_secs); - - lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n"); - - /* fallthru */ - - case LRS_SSL_ACK_PENDING: - - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { - lwsl_err("%s: lws_change_pollfd failed\n", __func__); - goto fail; - } - - if (wsi->vhost->tls.allow_non_ssl_on_ssl_port && !wsi->skip_fallback) { - - n = recv(wsi->desc.sockfd, (char *)pt->serv_buf, - context->pt_serv_buf_size, MSG_PEEK); - /* - * We have LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT.. - * this just means don't hang up on him because of no - * tls hello... what happens next is driven by - * additional option flags: - * - * none: fail the connection - * - * LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS: - * Destroy the TLS, issue a redirect using plaintext - * http (this may not be accepted by a client that - * has visited the site before and received an STS - * header). - * - * LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER: - * Destroy the TLS, continue and serve normally - * using http - * - * LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG: - * Destroy the TLS, apply whatever role and protocol - * were told in the vhost info struct - * .listen_accept_role / .listen_accept_protocol and - * continue with that - */ - - if (n >= 1 && pt->serv_buf[0] >= ' ') { - /* - * TLS content-type for Handshake is 0x16, and - * for ChangeCipherSpec Record, it's 0x14 - * - * A non-ssl session will start with the HTTP - * method in ASCII. If we see it's not a legit - * SSL handshake kill the SSL for this - * connection and try to handle as a HTTP - * connection upgrade directly. - */ - wsi->tls.use_ssl = 0; - - lws_tls_server_abort_connection(wsi); - /* - * care... this creates wsi with no ssl when ssl - * is enabled and normally mandatory - */ - wsi->tls.ssl = NULL; - - if (lws_check_opt(wsi->vhost->options, - LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS)) { - lwsl_info("%s: redirecting from http " - "to https\n", __func__); - wsi->tls.redirect_to_https = 1; - goto notls_accepted; - } - - if (lws_check_opt(wsi->vhost->options, - LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER)) { - lwsl_info("%s: allowing unencrypted " - "http service on tls port\n", - __func__); - goto notls_accepted; - } - - if (lws_check_opt(wsi->vhost->options, - LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG)) { - if (lws_http_to_fallback(wsi, NULL, 0)) - goto fail; - lwsl_info("%s: allowing non-tls " - "fallback\n", __func__); - goto notls_accepted; - } - - lwsl_notice("%s: client did not send a valid " - "tls hello (default vhost %s)\n", - __func__, wsi->vhost->name); - goto fail; - } - if (!n) { - /* - * connection is gone, fail out - */ - lwsl_debug("PEEKed 0\n"); - goto fail; - } - if (n < 0 && (LWS_ERRNO == LWS_EAGAIN || - LWS_ERRNO == LWS_EWOULDBLOCK)) { - /* - * well, we get no way to know ssl or not - * so go around again waiting for something - * to come and give us a hint, or timeout the - * connection. - */ - if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { - lwsl_info("%s: change_pollfd failed\n", - __func__); - return -1; - } - - lwsl_info("SSL_ERROR_WANT_READ\n"); - return 0; - } - } - - /* normal SSL connection processing path */ - -#if defined(LWS_WITH_STATS) - /* only set this the first time around */ - if (!wsi->accept_start_us) - wsi->accept_start_us = lws_now_usecs(); -#endif - errno = 0; - lws_stats_bump(pt, LWSSTATS_C_SSL_ACCEPT_SPIN, 1); - n = lws_tls_server_accept(wsi); - lwsl_info("SSL_accept says %d\n", n); - switch (n) { - case LWS_SSL_CAPABLE_DONE: - break; - case LWS_SSL_CAPABLE_ERROR: - lws_stats_bump(pt, LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1); - lwsl_info("SSL_accept failed socket %u: %d\n", - wsi->desc.sockfd, n); - wsi->socket_is_permanently_unusable = 1; - goto fail; - - default: /* MORE_SERVICE */ - return 0; - } - - lws_stats_bump(pt, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1); -#if defined(LWS_WITH_STATS) - if (wsi->accept_start_us) - lws_stats_bump(pt, - LWSSTATS_US_SSL_ACCEPT_LATENCY_AVG, - lws_now_usecs() - - wsi->accept_start_us); - wsi->accept_start_us = lws_now_usecs(); -#endif -#if defined(LWS_WITH_DETAILED_LATENCY) - if (context->detailed_latency_cb) { - wsi->detlat.type = LDLT_TLS_NEG_SERVER; - wsi->detlat.latencies[LAT_DUR_PROXY_RX_TO_ONWARD_TX] = - lws_now_usecs() - - wsi->detlat.earliest_write_req_pre_write; - wsi->detlat.latencies[LAT_DUR_USERCB] = 0; - lws_det_lat_cb(wsi->context, &wsi->detlat); - } -#endif - - /* adapt our vhost to match the SNI SSL_CTX that was chosen */ - vh = context->vhost_list; - while (vh) { - if (!vh->being_destroyed && wsi->tls.ssl && - vh->tls.ssl_ctx == lws_tls_ctx_from_wsi(wsi)) { - lwsl_info("setting wsi to vh %s\n", vh->name); - lws_vhost_bind_wsi(vh, wsi); - break; - } - vh = vh->vhost_next; - } - - /* OK, we are accepted... give him some time to negotiate */ - lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, - context->timeout_secs); - - lwsi_set_state(wsi, LRS_ESTABLISHED); - if (lws_tls_server_conn_alpn(wsi)) - goto fail; - lwsl_debug("accepted new SSL conn\n"); - break; - - default: - break; - } - - return 0; - -notls_accepted: - lwsi_set_state(wsi, LRS_ESTABLISHED); - - return 0; - -fail: - return 1; -} - diff -Nru libwebsockets-4.0.20/libwebsockets.dox libwebsockets-2.4.2/libwebsockets.dox --- libwebsockets-4.0.20/libwebsockets.dox 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/libwebsockets.dox 2018-03-08 10:28:37.000000000 +0000 @@ -7,7 +7,7 @@ PROJECT_NAME = "libwebsockets" PROJECT_NUMBER = PROJECT_BRIEF = "Lightweight C library for HTML5 websockets" -PROJECT_LOGO = "./test-apps/libwebsockets.org-logo.svg" +PROJECT_LOGO = "./test-apps/libwebsockets.org-logo.png" OUTPUT_DIRECTORY = "doc" CREATE_SUBDIRS = NO ALLOW_UNICODE_NAMES = NO @@ -101,89 +101,21 @@ #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- -INPUT = include/libwebsockets.h \ - include/libwebsockets/lws-adopt.h \ - include/libwebsockets/lws-async-dns.h \ - include/libwebsockets/lws-callbacks.h \ - include/libwebsockets/lws-cgi.h \ - include/libwebsockets/lws-client.h \ - include/libwebsockets/lws-context-vhost.h \ - include/libwebsockets/lws-dbus.h \ - include/libwebsockets/lws-detailed-latency.h \ - include/libwebsockets/lws-diskcache.h \ - include/libwebsockets/lws-dsh.h \ - include/libwebsockets/lws-esp32.h \ - include/libwebsockets/lws-freertos.h \ - include/libwebsockets/lws-fts.h \ - include/libwebsockets/lws-genaes.h \ - include/libwebsockets/lws-gencrypto.h \ - include/libwebsockets/lws-genec.h \ - include/libwebsockets/lws-genhash.h \ - include/libwebsockets/lws-genrsa.h \ - include/libwebsockets/lws-http.h \ - include/libwebsockets/lws-jose.h \ - include/libwebsockets/lws-jwe.h \ - include/libwebsockets/lws-jwk.h \ - include/libwebsockets/lws-jws.h \ - include/libwebsockets/lws-lejp.h \ - include/libwebsockets/lws-logs.h \ - include/libwebsockets/lws-lwsac.h \ - include/libwebsockets/lws-misc.h \ - include/libwebsockets/lws-network-helper.h \ - include/libwebsockets/lws-plugin-generic-sessions.h \ - include/libwebsockets/lws-protocols-plugins.h \ - include/libwebsockets/lws-purify.h \ - include/libwebsockets/lws-retry.h \ - include/libwebsockets/lws-ring.h \ - include/libwebsockets/lws-sequencer.h \ - include/libwebsockets/lws-service.h \ - include/libwebsockets/lws-sha1-base64.h \ - include/libwebsockets/lws-spa.h \ - include/libwebsockets/lws-stats.h \ - include/libwebsockets/lws-struct.h \ - include/libwebsockets/lws-system.h \ - include/libwebsockets/lws-test-sequencer.h \ - include/libwebsockets/lws-threadpool.h \ - include/libwebsockets/lws-timeout-timer.h \ - include/libwebsockets/lws-tokenize.h \ - include/libwebsockets/lws-vfs.h \ - include/libwebsockets/lws-write.h \ - include/libwebsockets/lws-writeable.h \ - include/libwebsockets/lws-ws-close.h \ - include/libwebsockets/lws-ws-ext.h \ - include/libwebsockets/lws-ws-state.h \ - include/libwebsockets/lws-x509.h \ +INPUT = lib/libwebsockets.h \ plugins/ssh-base/include/lws-plugin-ssh.h \ ./READMEs/mainpage.md \ - ./READMEs/README.async-dns.md \ ./READMEs/README.build.md \ - ./READMEs/README.ci.md \ + ./READMEs/README.problems.md \ + ./READMEs/README.lwsws.md \ ./READMEs/README.coding.md \ - ./READMEs/README.content-security-policy.md \ - ./READMEs/README.contributing.md \ - ./READMEs/README.crypto-apis.md \ - ./READMEs/README.detailed-latency.md \ - ./READMEs/README.esp32.md \ ./READMEs/README.generic-sessions.md \ ./READMEs/README.generic-table.md \ - ./READMEs/README.h2-long-poll.md \ - ./READMEs/README.http-fallback.md \ - ./READMEs/README.lws_dll.md \ - ./READMEs/README.lws_sequencer.md \ - ./READMEs/README.lws_struct.md \ - ./READMEs/README.lws_sul.md \ - ./READMEs/README.lwsws.md \ - ./READMEs/README-plugin-sshd-base.md \ - ./READMEs/README.plugin-acme.md \ - ./READMEs/README.porting.md \ - ./READMEs/README.problems.md \ - ./READMEs/README.release-policy.md \ ./READMEs/README.test-apps.md \ - ./READMEs/README.unix-domain-reverse-proxy.md \ - ./READMEs/README.vulnerability-reporting.md \ + ./READMEs/README.lws-meta.md \ + ./READMEs/README-plugin-sshd-base.md \ ./doc-assets INPUT_ENCODING = UTF-8 -FILE_PATTERNS = lib/*.c *.md *.png include/*.h +FILE_PATTERNS = lib/*.c *.md *.png RECURSIVE = NO EXCLUDE = EXCLUDE_SYMLINKS = NO @@ -339,11 +271,11 @@ #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -ENABLE_PREPROCESSING = YES +ENABLE_PREPROCESSING = NO MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES -INCLUDE_PATH = ./include +INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = diff -Nru libwebsockets-4.0.20/LICENSE libwebsockets-2.4.2/LICENSE --- libwebsockets-4.0.20/LICENSE 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/LICENSE 2018-03-08 10:28:37.000000000 +0000 @@ -1,56 +1,555 @@ -Libwebsockets and included programs are provided under the terms of the -MIT license shown below, with the exception that some sources are under -a similar permissive license like BSD, or are explicitly CC0 / public -domain to remove any obstacles from basing differently-licensed code on -them. - -Original liberal license retained: - - - lib/misc/sha-1.c - 3-clause BSD license retained, link to original - - win32port/zlib - ZLIB license (see zlib.h) - - lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls) - - lib/misc/base64-decode.c - already MIT - -Relicensed to MIT: - - - lib/misc/daemonize.c - relicensed from Public Domain to MIT, - link to original Public Domain version - - lib/plat/windows/windows-resolv.c - relicensed from "Beerware v42" to MIT +Libwebsockets and included programs are provided under the terms of the GNU +Library General Public License (LGPL) 2.1, with the following exceptions: -Public Domain (CC-zero) to simplify reuse: +1) Any reference, whether in these modifications or in the GNU +Library General Public License 2.1, to this License, these terms, the +GNU Lesser Public License, GNU Library General Public License, LGPL, or +any similar reference shall refer to the GNU Library General Public +License 2.1 as modified by these paragraphs 1) through 4). + +2) Static linking of programs with the libwebsockets library does not +constitute a derivative work and does not require the author to provide +source code for the program, use the shared libwebsockets libraries, or +link their program against a user-supplied version of libwebsockets. + +If you link the program to a modified version of libwebsockets, then the +changes to libwebsockets must be provided under the terms of the LGPL in +sections 1, 2, and 4. + +3) You do not have to provide a copy of the libwebsockets license with +programs that are linked to the libwebsockets library, nor do you have to +identify the libwebsockets license in your program or documentation as +required by section 6 of the LGPL. + +However, programs must still identify their use of libwebsockets. The +following example statement can be included in user documentation to +satisfy this requirement: + +"[program] is based in part on the work of the libwebsockets project +(https://libwebsockets.org)" + +4) Some sources included have their own, more liberal licenses, or options +to get original sources with the liberal terms. + +Original liberal license retained + + - lib/sha-1.c - 3-clause BSD license retained, link to original + - win32port/zlib - ZLIB license (see zlib.h) + - lib/mbedtls_wrapper - Apache 2.0 (only built if linked against mbedtls) + +Relicensed to libwebsocket license + + - lib/base64-decode.c - relicensed to LGPL2.1+SLE, link to original + - lib/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE, + link to original Public Domain version + +Public Domain (CC-zero) to simplify reuse - test-apps/*.c - test-apps/*.h - - minimal-examples/* - lwsws/* -Although libwebsockets is available under a permissive license, it does not -change the reality of dealing with large lumps of external code... if your -copy diverges it is guaranteed to contain security problems after a while -and can be very painful to pick backports (especially since historically, -we are very hot on cleaning and refactoring the codebase). The least -painful and lowest risk way remains sending your changes and fixes upstream -to us so you can easily use later releases and fixes. - -MIT License applied to libwebsockets: - -https://opensource.org/licenses/MIT - - 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. +------ end of exceptions + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice +That's all there is to it! diff -Nru libwebsockets-4.0.20/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE libwebsockets-2.4.2/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE --- libwebsockets-4.0.20/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE 2018-03-08 10:28:37.000000000 +0000 @@ -2,8 +2,7 @@ "vhosts": [ { "name": "localhost", "port": "7681", -# by default, bind to all interfaces, but you can restrict it -# "interface": "lo", + "interface": "lo", # "host-ssl-key": "/etc/pki/tls/private/libwebsockets.org.key", # "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt", # "host-ssl-ca": "/etc/pki/tls/certs/libwebsockets.org.cer", diff -Nru libwebsockets-4.0.20/lwsws/main.c libwebsockets-2.4.2/lwsws/main.c --- libwebsockets-4.0.20/lwsws/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lwsws/main.c 2018-03-08 10:28:37.000000000 +0000 @@ -1,7 +1,7 @@ /* * libwebsockets web server application * - * Written in 2010-2020 by Andy Green + * Copyright (C) 2010-2017 Andy Green * * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. @@ -21,9 +21,7 @@ #include #include -#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32) #include -#endif #include #include #include @@ -38,7 +36,6 @@ #else #include #include "gettimeofday.h" -#include int fork(void) { @@ -47,27 +44,22 @@ } #endif -#include +#include "../lib/libwebsockets.h" #include -#if defined(LWS_HAVE_MALLOC_TRIM) -#include -#endif - static struct lws_context *context; -static lws_sorted_usec_list_t sul_lwsws; static char config_dir[128]; static int opts = 0, do_reload = 1; static uv_loop_t loop; -static uv_signal_t signal_outer[2]; +static uv_signal_t signal_outer; static int pids[32]; void lwsl_emit_stderr(int level, const char *line); #define LWSWS_CONFIG_STRING_SIZE (32 * 1024) static const struct lws_extension exts[] = { -#if !defined(LWS_WITHOUT_EXTENSIONS) +#if !defined(LWS_NO_EXTENSIONS) { "permessage-deflate", lws_extension_callback_pm_deflate, @@ -82,14 +74,12 @@ NULL }; -#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32) static struct option options[] = { { "help", no_argument, NULL, 'h' }, { "debug", required_argument, NULL, 'd' }, { "configdir", required_argument, NULL, 'c' }, { NULL, 0, 0, 0 } }; -#endif void signal_cb(uv_signal_t *watcher, int signum) { @@ -111,21 +101,7 @@ break; } lwsl_err("Signal %d caught\n", watcher->signum); - uv_signal_stop(watcher); - uv_signal_stop(&signal_outer[1]); - lws_context_destroy(context); -} - -static void -lwsws_min(lws_sorted_usec_list_t *sul) -{ - lwsl_debug("%s\n", __func__); - -#if defined(LWS_HAVE_MALLOC_TRIM) - malloc_trim(4 * 1024); -#endif - - lws_sul_schedule(context, 0, &sul_lwsws, lwsws_min, 60 * LWS_US_PER_SEC); + lws_libuv_stop(context); } static int @@ -134,7 +110,6 @@ int cs_len = LWSWS_CONFIG_STRING_SIZE - 1; struct lws_context_creation_info info; char *cs, *config_strings; - void *foreign_loops[1]; cs = config_strings = malloc(LWSWS_CONFIG_STRING_SIZE); if (!config_strings) { @@ -145,7 +120,7 @@ memset(&info, 0, sizeof(info)); info.external_baggage_free_on_destroy = config_strings; - info.pt_serv_buf_size = 8192; + info.max_http_header_pool = 1024; info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_LIBUV; @@ -159,16 +134,15 @@ if (lwsws_get_config_globals(&info, config_dir, &cs, &cs_len)) goto init_failed; - foreign_loops[0] = &loop; - info.foreign_loops = foreign_loops; - info.pcontext = &context; - context = lws_create_context(&info); if (context == NULL) { lwsl_err("libwebsocket init failed\n"); goto init_failed; } + lws_uv_sigint_cfg(context, 1, signal_cb); + lws_uv_initloop(context, &loop, 0); + /* * then create the vhosts... protocols are entirely coming from * plugins, so we leave it NULL @@ -176,11 +150,10 @@ info.extensions = exts; - if (lwsws_get_config_vhosts(context, &info, config_dir, &cs, &cs_len)) + if (lwsws_get_config_vhosts(context, &info, config_dir, + &cs, &cs_len)) return 1; - lws_sul_schedule(context, 0, &sul_lwsws, lwsws_min, 60 * LWS_US_PER_SEC); - return 0; init_failed: @@ -206,7 +179,7 @@ fprintf(stderr, "root process receives reload\n"); if (!do_reload) { fprintf(stderr, "passing HUP to child processes\n"); - for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++) + for (m = 0; m < ARRAY_SIZE(pids); m++) if (pids[m]) kill(pids[m], SIGHUP); sleep(1); @@ -216,10 +189,8 @@ case SIGINT: case SIGTERM: case SIGKILL: - fprintf(stderr, "master process waiting 2s...\n"); - sleep(2); /* give children a chance to deal with the signal */ fprintf(stderr, "killing service processes\n"); - for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++) + for (m = 0; m < ARRAY_SIZE(pids); m++) if (pids[m]) kill(pids[m], SIGTERM); exit(0); @@ -231,19 +202,15 @@ int main(int argc, char **argv) { - int n = 0, budget = 100, debug_level = 1024 + 7; + int n = 0, debug_level = 7; #ifndef _WIN32 int m; - int status;//, syslog_options = LOG_PID | LOG_PERROR; + int status, syslog_options = LOG_PID | LOG_PERROR; #endif strcpy(config_dir, "/etc/lwsws"); while (n >= 0) { -#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32) n = getopt_long(argc, argv, "hd:c:", options, NULL); -#else - n = getopt(argc, argv, "hd:c:"); -#endif if (n < 0) continue; switch (n) { @@ -251,7 +218,8 @@ debug_level = atoi(optarg); break; case 'c': - lws_strncpy(config_dir, optarg, sizeof(config_dir)); + strncpy(config_dir, optarg, sizeof(config_dir) - 1); + config_dir[sizeof(config_dir) - 1] = '\0'; break; case 'h': fprintf(stderr, "Usage: lwsws [-c ] " @@ -271,7 +239,7 @@ signal(SIGHUP, reload_handler); signal(SIGINT, reload_handler); - fprintf(stderr, "Root process is %u\n", (unsigned int)getpid()); + fprintf(stderr, "Root process is %u\n", getpid()); while (1) { if (do_reload) { @@ -281,8 +249,9 @@ break; /* old */ if (n > 0) - for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++) + for (m = 0; m < ARRAY_SIZE(pids); m++) if (!pids[m]) { + // fprintf(stderr, "added child pid %d\n", n); pids[m] = n; break; } @@ -292,8 +261,9 @@ n = waitpid(-1, &status, WNOHANG); if (n > 0) - for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++) + for (m = 0; m < ARRAY_SIZE(pids); m++) if (pids[m] == n) { + // fprintf(stderr, "reaped child pid %d\n", pids[m]); pids[m] = 0; break; } @@ -304,10 +274,16 @@ #endif /* child process */ - lws_set_log_level(debug_level, lwsl_emit_stderr_notimestamp); +#ifndef _WIN32 + /* we will only try to log things according to our debug_level */ + setlogmask(LOG_UPTO (LOG_DEBUG)); + openlog("lwsws", syslog_options, LOG_DAEMON); +#endif + + lws_set_log_level(debug_level, lwsl_emit_syslog); - lwsl_notice("lwsws libwebsockets web server - license CC0 + MIT\n"); - lwsl_notice("(C) Copyright 2010-2020 Andy Green \n"); + lwsl_notice("lwsws libwebsockets web server - license CC0 + LGPL2.1\n"); + lwsl_notice("(C) Copyright 2010-2016 Andy Green \n"); #if (UV_VERSION_MAJOR > 0) // Travis... uv_loop_init(&loop); @@ -315,36 +291,30 @@ fprintf(stderr, "Your libuv is too old!\n"); return 0; #endif - uv_signal_init(&loop, &signal_outer[0]); - uv_signal_start(&signal_outer[0], signal_cb, SIGINT); - uv_signal_init(&loop, &signal_outer[1]); - uv_signal_start(&signal_outer[1], signal_cb, SIGHUP); + uv_signal_init(&loop, &signal_outer); + uv_signal_start(&signal_outer, signal_cb, SIGINT); + uv_signal_start(&signal_outer, signal_cb, SIGHUP); if (context_creation()) { lwsl_err("Context creation failed\n"); return 1; } - lws_service(context, 0); - - lwsl_err("%s: closing\n", __func__); - - for (n = 0; n < 2; n++) { - uv_signal_stop(&signal_outer[n]); - uv_close((uv_handle_t *)&signal_outer[n], NULL); - } - - /* cancel the per-minute sul */ - lws_sul_schedule(context, 0, &sul_lwsws, NULL, LWS_SET_TIMER_USEC_CANCEL); + lws_libuv_run(context, 0); + uv_signal_stop(&signal_outer); lws_context_destroy(context); - (void)budget; + #if (UV_VERSION_MAJOR > 0) // Travis... - while ((n = uv_loop_close(&loop)) && --budget) - uv_run(&loop, UV_RUN_ONCE); + lws_close_all_handles_in_loop(&loop); + n = 0; + while (n++ < 4096 && uv_loop_close(&loop)) + uv_run(&loop, UV_RUN_NOWAIT); #endif - fprintf(stderr, "lwsws exited cleanly: %d\n", n); + lws_context_destroy2(context); + + fprintf(stderr, "lwsws exited cleanly\n"); #ifndef _WIN32 closelog(); diff -Nru libwebsockets-4.0.20/lwsws/usr-lib-systemd-system-lwsws.service libwebsockets-2.4.2/lwsws/usr-lib-systemd-system-lwsws.service --- libwebsockets-4.0.20/lwsws/usr-lib-systemd-system-lwsws.service 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/lwsws/usr-lib-systemd-system-lwsws.service 2018-03-08 10:28:37.000000000 +0000 @@ -6,6 +6,7 @@ ExecStart=/usr/local/bin/lwsws ExecReload=/usr/bin/kill -HUP $MAINPID ExecStop=/usr/bin/killall lwsws +StandardError=null [Install] WantedBy=multi-user.target diff -Nru libwebsockets-4.0.20/.mailmap libwebsockets-2.4.2/.mailmap --- libwebsockets-4.0.20/.mailmap 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/.mailmap 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -Andy Green -Joakim Söderberg - diff -Nru libwebsockets-4.0.20/Makefile.projbuild libwebsockets-2.4.2/Makefile.projbuild --- libwebsockets-4.0.20/Makefile.projbuild 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/Makefile.projbuild 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -CPPFLAGS += -I$(BUILD_DIR_BASE)/libwebsockets/include diff -Nru libwebsockets-4.0.20/minimal-examples/abstract/protocols/smtp-client/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/abstract/protocols/smtp-client/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/abstract/protocols/smtp-client/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/abstract/protocols/smtp-client/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -project(lws-api-test-smtp_client) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-smtp_client) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_SMTP 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/abstract/protocols/smtp-client/main.c libwebsockets-2.4.2/minimal-examples/abstract/protocols/smtp-client/main.c --- libwebsockets-4.0.20/minimal-examples/abstract/protocols/smtp-client/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/abstract/protocols/smtp-client/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -/* - * lws-api-test-smtp_client - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include - -#include - -static int interrupted, result = 1; -static const char *recip; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -static int -done_cb(struct lws_smtp_email *email, void *buf, size_t len) -{ - /* you could examine email->data here */ - if (buf) { - char dotstar[96]; - lws_strnncpy(dotstar, (const char *)buf, len, sizeof(dotstar)); - lwsl_notice("%s: %s\n", __func__, dotstar); - } else - lwsl_notice("%s:\n", __func__); - - /* destroy any allocations in email */ - - free((char *)email->payload); - - result = 0; - interrupted = 1; - - return 0; -} - -int main(int argc, const char **argv) -{ - int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_context_creation_info info; - lws_smtp_sequencer_args_t ss_args; - struct lws_context *context; - lws_smtp_sequencer_t *sseq; - lws_smtp_email_t *email; - struct lws_vhost *vh; - char payload[2048]; - const char *p; - - /* the normal lws init */ - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - p = lws_cmdline_option(argc, argv, "-r"); - if (!p) { - lwsl_err("-r is required\n"); - return 1; - } - recip = p; - - lws_set_log_level(logs, NULL); - lwsl_user("LWS API selftest: SMTP client\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - vh = lws_create_vhost(context, &info); - if (!vh) { - lwsl_err("Failed to create first vhost\n"); - goto bail1; - } - - memset(&ss_args, 0, sizeof(ss_args)); - ss_args.helo = "lws-abs-smtp-test"; - ss_args.vhost = vh; - - sseq = lws_smtp_sequencer_create(&ss_args); - if (!sseq) { - lwsl_err("%s: smtp sequencer create failed\n", __func__); - goto bail1; - } - - /* attach an email to it */ - - n = lws_snprintf(payload, sizeof(payload), - "From: noreply@example.com\n" - "To: %s\n" - "Subject: Test email for lws smtp-client\n" - "\n" - "Hello this was an api test for lws smtp-client\n" - "\r\n.\r\n", recip); - - if (lws_smtpc_add_email(sseq, payload, n, "testserver", - "andy@warmcat.com", recip, NULL, done_cb)) { - lwsl_err("%s: failed to add email\n", __func__); - goto bail1; - } - - /* the usual lws event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail1: - lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS"); - - lws_context_destroy(context); - - return result; -} diff -Nru libwebsockets-4.0.20/minimal-examples/abstract/protocols/smtp-client/README.md libwebsockets-2.4.2/minimal-examples/abstract/protocols/smtp-client/README.md --- libwebsockets-4.0.20/minimal-examples/abstract/protocols/smtp-client/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/abstract/protocols/smtp-client/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -# lws api test smtp client - -Demonstrates how to send email through your local MTA - -## build - -Requires lws was built with `-DLWS_WITH_SMTP=1` at cmake. - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --r |Send the test email to this email address - - -``` - $ ./lws-api-test-smtp_client -r andy@warmcat.com -[2019/04/17 05:12:06:5293] USER: LWS API selftest: SMTP client -[2019/04/17 05:12:06:5635] NOTICE: LGSSMTP_IDLE: connecting to 127.0.0.1:25 -[2019/04/17 05:12:06:6238] NOTICE: email_sent_or_failed: sent OK -[2019/04/17 05:12:06:6394] USER: Completed: PASS - -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-async-dns/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-async-dns/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-async-dns/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-async-dns/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-api-test-async-dns) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-async-dns) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) -require_lws_config(LWS_WITH_SYS_ASYNC_DNS 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-async-dns/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-async-dns/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-async-dns/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-async-dns/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,315 +0,0 @@ -/* - * lws-api-test-async-dns - * - * Written in 2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This api test confirms various kinds of async dns apis - */ - -#include -#include - -static int interrupted, dtest, ok, fail, _exp = 26; -struct lws_context *context; - -/* - * These are used to test the apis to parse and print ipv4 / ipv6 literal - * address strings for various cases. - * - * Expected error cases are not used to test the ip data -> string api. - */ - -static const struct ipparser_tests { - const char *test; - int rlen; - const char *emit_test; - int emit_len; - uint8_t b[16]; -} ipt[] = { - { "2001:db8:85a3:0:0:8a2e:370:7334", 16, - "2001:db8:85a3::8a2e:370:7334", 28, - { 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, - 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 } }, - - { "2001:db8:85a3::8a2e:370:7334", 16, - "2001:db8:85a3::8a2e:370:7334", 28, - { 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, - 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 } }, - - { "::1", 16, "::1", 3, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, - - { "::", 16, "::", 2, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, - - { "::ffff:192.0.2.128", 16, "::ffff:192.0.2.128", 18, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xc0, 0x00, 0x02, 0x80 } }, - - { "cats", -1, "", 0, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, - - { "onevalid.bogus.warmcat.com", -1, "", 0, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, - - { "1.cat.dog.com", -1, "", 0, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, - - { ":::1", -8, "", 0, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, - - { "0:0::0:1", 16, "::1", 3, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, - - { "1.2.3.4", 4, "1.2.3.4", 7, { 1, 2, 3, 4 } }, -}; - -static const struct async_dns_tests { - const char *dns_name; - int recordtype; - int addrlen; - uint8_t ads[16]; -} adt[] = { - { "warmcat.com", LWS_ADNS_RECORD_A, 4, - { 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } }, - { "libwebsockets.org", LWS_ADNS_RECORD_A, 4, - { 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } }, - { "doesntexist", LWS_ADNS_RECORD_A, 0, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } }, - { "localhost", LWS_ADNS_RECORD_A, 4, - { 127, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } }, - { "ipv4only.warmcat.com", LWS_ADNS_RECORD_A, 4, - { 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } }, - { "onevalid.bogus.warmcat.com", LWS_ADNS_RECORD_A, 4, - { 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } }, -#if defined(LWS_WITH_IPV6) - { "warmcat.com", LWS_ADNS_RECORD_AAAA, 16, /* check ipv6 */ - { 0x20, 0x01, 0x41, 0xd0, 0x00, 0x02, 0xee, 0x93, - 0, 0, 0, 0, 0, 0, 0, 0, } }, - { "ipv6only.warmcat.com", LWS_ADNS_RECORD_AAAA, 16, /* check ipv6 */ - { 0x20, 0x01, 0x41, 0xd0, 0x00, 0x02, 0xee, 0x93, - 0, 0, 0, 0, 0, 0, 0, 0, } }, -#endif -}; - -static lws_sorted_usec_list_t sul; - -struct lws * -cb1(struct lws *wsi_unused, const char *ads, const struct addrinfo *a, int n, - void *opaque); - -static void -next_test_cb(lws_sorted_usec_list_t *sul) -{ - int m; - - lwsl_notice("%s: querying %s\n", __func__, adt[dtest].dns_name); - - m = lws_async_dns_query(context, 0, - adt[dtest].dns_name, - adt[dtest].recordtype, cb1, NULL, - context); - if (m != LADNS_RET_CONTINUING && m != LADNS_RET_FOUND) { - lwsl_err("%s: adns 1 failed: %d\n", __func__, m); - interrupted = 1; - } -} - - -struct lws * -cb1(struct lws *wsi_unused, const char *ads, const struct addrinfo *a, int n, - void *opaque) -{ - const struct addrinfo *ac = a; - int ctr = 0, alen; - uint8_t *addr; - char buf[64]; - - dtest++; - - if (!ac) - lwsl_warn("%s: no results\n", __func__); - - /* dump the results */ - - while (ac) { - if (ac->ai_family == AF_INET) { - addr = (uint8_t *)&(((struct sockaddr_in *) - ac->ai_addr)->sin_addr.s_addr); - alen = 4; - } else { - addr = (uint8_t *)&(((struct sockaddr_in6 *) - ac->ai_addr)->sin6_addr.s6_addr); - alen = 16; - } - strcpy(buf, "unknown"); - lws_write_numeric_address(addr, alen, buf, sizeof(buf)); - - lwsl_warn("%s: %d: %s %d %s\n", __func__, ctr++, ads, alen, buf); - - ac = ac->ai_next; - } - - ac = a; - while (ac) { - if (ac->ai_family == AF_INET) { - addr = (uint8_t *)&(((struct sockaddr_in *) - ac->ai_addr)->sin_addr.s_addr); - alen = 4; - } else { -#if defined(LWS_WITH_IPV6) - addr = (uint8_t *)&(((struct sockaddr_in6 *) - ac->ai_addr)->sin6_addr.s6_addr); - alen = 16; -#else - goto again; -#endif - } - if (alen == adt[dtest - 1].addrlen && - !memcmp(adt[dtest - 1].ads, addr, alen)) { - ok++; - goto next; - } -#if !defined(LWS_WITH_IPV6) -again: -#endif - ac = ac->ai_next; - } - - /* testing for NXDOMAIN? */ - - if (!a && !adt[dtest - 1].addrlen) { - ok++; - goto next; - } - - lwsl_err("%s: dns test %d: no match\n", __func__, dtest); - fail++; - -next: - lws_async_dns_freeaddrinfo(&a); - if (dtest == (int)LWS_ARRAY_SIZE(adt)) - interrupted = 1; - else - lws_sul_schedule(context, 0, &sul, next_test_cb, 1); - - return NULL; -} - - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int -main(int argc, const char **argv) -{ - int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_context_creation_info info; - const char *p; - - /* the normal lws init */ - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS API selftest: Async DNS\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - - /* ip address parser tests */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(ipt); n++) { - uint8_t u[16]; - int m = lws_parse_numeric_address(ipt[n].test, u, sizeof(u)); - - if (m != ipt[n].rlen) { - lwsl_err("%s: fail %s ret %d\n", - __func__, ipt[n].test, m); - fail++; - continue; - } - - if (m > 0) { - if (memcmp(ipt[n].b, u, m)) { - lwsl_err("%s: fail %s compare\n", __func__, - ipt[n].test); - lwsl_hexdump_notice(u, m); - fail++; - continue; - } - } - ok++; - } - - /* ip address formatter tests */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(ipt); n++) { - char buf[64]; - int m; - - /* don't attempt to reverse the ones that are meant to fail */ - if (ipt[n].rlen < 0) - continue; - - m = lws_write_numeric_address(ipt[n].b, ipt[n].rlen, buf, - sizeof(buf)); - if (m != ipt[n].emit_len) { - lwsl_err("%s: fail %s ret %d\n", - __func__, ipt[n].emit_test, m); - fail++; - continue; - } - - if (m > 0) { - if (strcmp(ipt[n].emit_test, buf)) { - lwsl_err("%s: fail %s compare\n", __func__, - ipt[n].test); - lwsl_hexdump_notice(buf, m); - fail++; - continue; - } - } - ok++; - } - -#if !defined(LWS_WITH_IPV6) - _exp -= 2; -#endif - - /* kick off the async dns tests */ - - lws_sul_schedule(context, 0, &sul, next_test_cb, 1); - - /* the usual lws event loop */ - - n = 1; - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - if (fail || ok != _exp) - lwsl_user("Completed: PASS: %d / %d, FAIL: %d\n", ok, _exp, - fail); - else - lwsl_user("Completed: ALL PASS: %d / %d\n", ok, _exp); - - return !(ok == _exp && !fail); -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-async-dns/selftest.sh libwebsockets-2.4.2/minimal-examples/api-tests/api-test-async-dns/selftest.sh --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-async-dns/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-async-dns/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=1 - -dotest $1 $2 apiselftest -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-dhcpc/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-dhcpc/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-dhcpc/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-dhcpc/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -project(lws-api-test-dhcpc) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-dhcpc) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_SYS_DHCP_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-dhcpc/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-dhcpc/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-dhcpc/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-dhcpc/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -/* - * lws-api-test-dhcpc - * - * Written in 2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include - -static int interrupted, ok, fail, exp = 1; -struct lws_context *context; -const char *nif; - -static int -lws_dhcpc_cb(void *opaque, int af, uint8_t *ip, int ip_len) -{ - lwsl_user("%s: dhcp set OK\n", __func__); - ok = 1; - interrupted = 1; - return 0; -} - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int -main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - const char *p; - int n = 1; - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - lws_cmdline_option_handle_builtin(argc, argv, &info); - lwsl_user("LWS API selftest: DHCP Client\n"); - - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - - if ((p = lws_cmdline_option(argc, argv, "-i"))) - nif = p; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - if (nif) { - lwsl_user("%s: requesting DHCP for %s\n", __func__, nif); - lws_dhcpc_request(context, nif, AF_INET, lws_dhcpc_cb, NULL); - } else { - lwsl_err("%s: use -i to select if\n", __func__); - interrupted = 1; - } - - /* the usual lws event loop */ - - n = 1; - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - if (fail || ok != exp) - lwsl_user("Completed: PASS: %d / %d, FAIL: %d\n", ok, exp, - fail); - else - lwsl_user("Completed: ALL PASS: %d / %d\n", ok, exp); - - return !(ok == exp && !fail); -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-dhcpc/README.md libwebsockets-2.4.2/minimal-examples/api-tests/api-test-dhcpc/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-dhcpc/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-dhcpc/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -# api test dhcpc - -The application confirms it can set DHCP on the given interface - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --i |Network interface name to set by DHCP, eg, eth0 or wlo1 - -``` - $ ./lws-api-test-dhcpc -i wlo1 -[2019/10/06 14:56:41:7683] U: LWS API selftest: Async DNS -[2019/10/06 14:56:42:4461] U: main: requesting DHCP for wlo1 -[2019/10/06 14:56:42:5207] N: callback_dhcpc: DHCP configured wlo1 -[2019/10/06 14:56:42:5246] U: lws_dhcpc_cb: dhcp set OK -[2019/10/06 14:56:42:5999] U: Completed: ALL PASS: 1 / 1 -``` - - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/canned-1.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/canned-1.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/canned-1.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/canned-1.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -API selftest: full-text search -AC be: 472 agg hits -AC but: 401 agg hits -AC by: 242 agg hits -AC been: 236 agg hits -AC basil: 158 agg hits -AC before: 75 agg hits -AC beauty: 55 agg hits -AC better: 54 agg hits -AC believe: 49 agg hits -AC began: 44 agg hits -AC because: 40 agg hits -AC boy: 36 agg hits -AC book: 31 agg hits -AC body: 28 agg hits -AC both: 26 agg hits -AC broke: 22 agg hits -AC beg: 5 agg hits -AC bore: 5 agg hits -AC b: 3 agg hits -AC bee: 3 agg hits -AC beaut: 1 agg hits -no filepath results - - - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/canned-2.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/canned-2.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/canned-2.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/canned-2.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -API selftest: full-text search -no autocomplete results -../minimal-examples/api-tests/api-test-fts/the-picture-of-dorian-gray.txt: (8904 lines) 32 hits -360 -17482 -393 -18984 -562 -28820 -837 -42903 -1640 -82057 -2037 -102214 -2091 -105019 -2145 -107351 -2725 -137188 -2808 -141127 -2977 -149971 -3429 -173810 -4417 -229186 -4431 -230058 -4656 -241181 -4708 -244372 -../minimal-examples/api-tests/api-test-fts/les-mis-utf8.txt: (14399 lines) 3 hits -14106 -14313 -14396 - - - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -project(lws-api-test-fts) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-fts) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_FTS 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/les-mis-utf8.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/les-mis-utf8.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/les-mis-utf8.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/les-mis-utf8.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,14399 +0,0 @@ -The Project Gutenberg EBook of Les misérables Tome I, by Victor Hugo - -This eBook is for the use of anyone anywhere at no cost and with -almost no restrictions whatsoever. You may copy it, give it away or -re-use it under the terms of the Project Gutenberg License included -with this eBook or online at www.gutenberg.org - - -Title: Les misérables Tome I - Fantine - -Author: Victor Hugo - -Release Date: January 10, 2006 [EBook #17489] -[Date last updated: July 28, 2010] - -Language: French - - -*** START OF THIS PROJECT GUTENBERG EBOOK LES MISÉRABLES TOME I *** - - - - -Produced by www.ebooksgratuits.com and Chuck Greif - - - - -Victor Hugo - -LES MISÉRABLES - -Tome I--FANTINE - -(1862) - - -TABLE DES MATIÈRES - -Livre premier--Un juste - -Chapitre I Monsieur Myriel -Chapitre II Monsieur Myriel devient monseigneur Bienvenu -Chapitre III À bon évêque dur évêché -Chapitre IV Les oeuvres semblables aux paroles -Chapitre V Que monseigneur Bienvenu faisait durer trop longtemps ses - soutanes -Chapitre VI Par qui il faisait garder sa maison -Chapitre VII Cravatte -Chapitre VIII Philosophie après boire -Chapitre IX Le frère raconté par la soeur -Chapitre X L'évêque en présence d'une lumière inconnue -Chapitre XI Une restriction -Chapitre XII Solitude de monseigneur Bienvenu -Chapitre XIII Ce qu'il croyait -Chapitre XIV Ce qu'il pensait - - -Livre deuxième--La chute - -Chapitre I Le soir d'un jour de marche -Chapitre II La prudence conseillée à la sagesse -Chapitre III Héroïsme de l'obéissance passive -Chapitre IV Détails sur les fromageries de Pontarlier -Chapitre V Tranquillité -Chapitre VI Jean Valjean -Chapitre VII Le dedans du désespoir -Chapitre VIII L'onde et l'ombre -Chapitre IX Nouveaux griefs -Chapitre X L'homme réveillé -Chapitre XI Ce qu'il fait -Chapitre XII L'évêque travaille -Chapitre XIII Petit-Gervais - - -Livre troisième--En l'année 1817 - -Chapitre I L'année 1817 -Chapitre II Double quatuor -Chapitre III Quatre à quatre -Chapitre IV Tholomyès est si joyeux qu'il chante une chanson espagnole -Chapitre V Chez Bombarda -Chapitre VI Chapitre où l'on s'adore -Chapitre VII Sagesse de Tholomyès -Chapitre VIII Mort d'un cheval -Chapitre IX Fin joyeuse de la joie -Livre quatrième--Confier, c'est quelquefois livrer -Chapitre I Une mère qui en rencontre une autre -Chapitre II Première esquisse de deux figures louches -Chapitre III L'Alouette - - -Livre cinquième--La descente - -Chapitre I Histoire d'un progrès dans les verroteries noires -Chapitre II M. Madeleine -Chapitre III Sommes déposées chez Laffitte -Chapitre IV M. Madeleine en deuil -Chapitre V Vagues éclairs à l'horizon -Chapitre VI Le père Fauchelevent -Chapitre VII Fauchelevent devient jardinier à Paris -Chapitre VIII Madame Victurnien dépense trente-cinq francs pour la morale -Chapitre IX Succès de Madame Victurnien -Chapitre X Suite du succès -Chapitre XI _Christus nos liberavit_ -Chapitre XII Le désoeuvrement de M. Bamatabois -Chapitre XIII Solution de quelques questions de police municipale - - -Livre sixième--Javert - -Chapitre I Commencement du repos -Chapitre II Comment Jean peut devenir Champ - - -Livre septième--L'affaire Champmathieu - -Chapitre I La soeur Simplice -Chapitre II Perspicacité de maître Scaufflaire -Chapitre III Une tempête sous un crâne -Chapitre IV Formes que prend la souffrance pendant le sommeil -Chapitre V Bâtons dans les roues -Chapitre VI La soeur Simplice mise à l'épreuve -Chapitre VII Le voyageur arrivé prend ses précautions pour repartir -Chapitre VIII Entrée de faveur -Chapitre IX Un lieu où des convictions sont en train de se former -Chapitre X Le système de dénégations -Chapitre XI Champmathieu de plus en plus étonné - - -Livre huitième--Contre-coup - -Chapitre I Dans quel miroir M. Madeleine regarde ses cheveux -Chapitre II Fantine heureuse -Chapitre III Javert content -Chapitre IV L'autorité reprend ses droits -Chapitre V Tombeau convenable - - - - -Livre premier--Un juste - - - - -Chapitre I - -Monsieur Myriel - - -En 1815, M. Charles-François-Bienvenu Myriel était évêque de Digne. -C'était un vieillard d'environ soixante-quinze ans; il occupait le siège -de Digne depuis 1806. - -Quoique ce détail ne touche en aucune manière au fond même de ce que -nous avons à raconter, il n'est peut-être pas inutile, ne fût-ce que -pour être exact en tout, d'indiquer ici les bruits et les propos qui -avaient couru sur son compte au moment où il était arrivé dans le -diocèse. Vrai ou faux, ce qu'on dit des hommes tient souvent autant de -place dans leur vie et surtout dans leur destinée que ce qu'ils font. M. -Myriel était fils d'un conseiller au parlement d'Aix; noblesse de robe. -On contait de lui que son père, le réservant pour hériter de sa charge, -l'avait marié de fort bonne heure, à dix-huit ou vingt ans, suivant un -usage assez répandu dans les familles parlementaires. Charles Myriel, -nonobstant ce mariage, avait, disait-on, beaucoup fait parler de lui. Il -était bien fait de sa personne, quoique d'assez petite taille, élégant, -gracieux, spirituel; toute la première partie de sa vie avait été donnée -au monde et aux galanteries. La révolution survint, les événements se -précipitèrent, les familles parlementaires décimées, chassées, traquées, -se dispersèrent. M. Charles Myriel, dès les premiers jours de la -révolution, émigra en Italie. Sa femme y mourut d'une maladie de -poitrine dont elle était atteinte depuis longtemps. Ils n'avaient point -d'enfants. Que se passa-t-il ensuite dans la destinée de M. Myriel? -L'écroulement de l'ancienne société française, la chute de sa propre -famille, les tragiques spectacles de 93, plus effrayants encore -peut-être pour les émigrés qui les voyaient de loin avec le -grossissement de l'épouvante, firent-ils germer en lui des idées de -renoncement et de solitude? Fut-il, au milieu d'une de ces distractions -et de ces affections qui occupaient sa vie, subitement atteint d'un de -ces coups mystérieux et terribles qui viennent quelquefois renverser, en -le frappant au coeur, l'homme que les catastrophes publiques -n'ébranleraient pas en le frappant dans son existence et dans sa -fortune? Nul n'aurait pu le dire; tout ce qu'on savait, c'est que, -lorsqu'il revint d'Italie, il était prêtre. - -En 1804, M. Myriel était curé de Brignolles. Il était déjà vieux, et -vivait dans une retraite profonde. - -Vers l'époque du couronnement, une petite affaire de sa cure, on ne sait -plus trop quoi, l'amena à Paris. Entre autres personnes puissantes, il -alla solliciter pour ses paroissiens M. le cardinal Fesch. Un jour que -l'empereur était venu faire visite à son oncle, le digne curé, qui -attendait dans l'antichambre, se trouva sur le passage de sa majesté. -Napoléon, se voyant regardé avec une certaine curiosité par ce -vieillard, se retourna, et dit brusquement: - ---Quel est ce bonhomme qui me regarde? - ---Sire, dit M. Myriel, vous regardez un bonhomme, et moi je regarde un -grand homme. Chacun de nous peut profiter. - -L'empereur, le soir même, demanda au cardinal le nom de ce curé, et -quelque temps après M. Myriel fut tout surpris d'apprendre qu'il était -nommé évêque de Digne. - -Qu'y avait-il de vrai, du reste, dans les récits qu'on faisait sur la -première partie de la vie de M. Myriel? Personne ne le savait. Peu de -familles avaient connu la famille Myriel avant la révolution. - -M. Myriel devait subir le sort de tout nouveau venu dans une petite -ville où il y a beaucoup de bouches qui parlent et fort peu de têtes qui -pensent. Il devait le subir, quoiqu'il fût évêque et parce qu'il était -évêque. Mais, après tout, les propos auxquels on mêlait son nom -n'étaient peut-être que des propos; du bruit, des mots, des paroles; -moins que des paroles, des _palabres_, comme dit l'énergique langue du -midi. - -Quoi qu'il en fût, après neuf ans d'épiscopat et de résidence à Digne, -tous ces racontages, sujets de conversation qui occupent dans le premier -moment les petites villes et les petites gens, étaient tombés dans un -oubli profond. Personne n'eût osé en parler, personne n'eût même osé -s'en souvenir. - -M. Myriel était arrivé à Digne accompagné d'une vieille fille, -mademoiselle Baptistine, qui était sa soeur et qui avait dix ans de -moins que lui. - -Ils avaient pour tout domestique une servante du même âge que -mademoiselle Baptistine, et appelée madame Magloire, laquelle, après -avoir été _la servante de M. le Curé_, prenait maintenant le double -titre de femme de chambre de mademoiselle et femme de charge de -monseigneur. - -Mademoiselle Baptistine était une personne longue, pâle, mince, douce; -elle réalisait l'idéal de ce qu'exprime le mot «respectable»; car il -semble qu'il soit nécessaire qu'une femme soit mère pour être vénérable. -Elle n'avait jamais été jolie; toute sa vie, qui n'avait été qu'une -suite de saintes oeuvres, avait fini par mettre sur elle une sorte de -blancheur et de clarté; et, en vieillissant, elle avait gagné ce qu'on -pourrait appeler la beauté de la bonté. Ce qui avait été de la maigreur -dans sa jeunesse était devenu, dans sa maturité, de la transparence; et -cette diaphanéité laissait voir l'ange. C'était une âme plus encore que -ce n'était une vierge. Sa personne semblait faite d'ombre; à peine assez -de corps pour qu'il y eût là un sexe; un peu de matière contenant une -lueur; de grands yeux toujours baissés; un prétexte pour qu'une âme -reste sur la terre. - -Madame Magloire était une petite vieille, blanche, grasse, replète, -affairée, toujours haletante, à cause de son activité d'abord, ensuite à -cause d'un asthme. - -À son arrivée, on installa M. Myriel en son palais épiscopal avec les -honneurs voulus par les décrets impériaux qui classent l'évêque -immédiatement après le maréchal de camp. Le maire et le président lui -firent la première visite, et lui de son côté fit la première visite au -général et au préfet. - -L'installation terminée, la ville attendit son évêque à l'oeuvre. - - - - -Chapitre II - -Monsieur Myriel devient monseigneur Bienvenu - - -Le palais épiscopal de Digne était attenant à l'hôpital. - -Le palais épiscopal était un vaste et bel hôtel bâti en pierre au -commencement du siècle dernier par monseigneur Henri Puget, docteur en -théologie de la faculté de Paris, abbé de Simore, lequel était évêque de -Digne en 1712. Ce palais était un vrai logis seigneurial. Tout y avait -grand air, les appartements de l'évêque, les salons, les chambres, la -cour d'honneur, fort large, avec promenoirs à arcades, selon l'ancienne -mode florentine, les jardins plantés de magnifiques arbres. Dans la -salle à manger, longue et superbe galerie qui était au rez-de-chaussée -et s'ouvrait sur les jardins, monseigneur Henri Puget avait donné à -manger en cérémonie le 29 juillet 1714 à messeigneurs Charles Brûlart de -Genlis, archevêque-prince d'Embrun, Antoine de Mesgrigny, capucin, -évêque de Grasse, Philippe de Vendôme, grand prieur de France, abbé de -Saint-Honoré de Lérins, François de Berton de Grillon, évêque-baron de -Vence, César de Sabran de Forcalquier, évêque-seigneur de Glandève, et -Jean Soanen, prêtre de l'oratoire, prédicateur ordinaire du roi, -évêque-seigneur de Senez. Les portraits de ces sept révérends -personnages décoraient cette salle, et cette date mémorable, 29 juillet -1714, y était gravée en lettres d'or sur une table de marbre blanc. - -L'hôpital était une maison étroite et basse à un seul étage avec un -petit jardin. Trois jours après son arrivée, l'évêque visita l'hôpital. -La visite terminée, il fit prier le directeur de vouloir bien venir -jusque chez lui. - ---Monsieur le directeur de l'hôpital, lui dit-il, combien en ce moment -avez-vous de malades? - ---Vingt-six, monseigneur. - ---C'est ce que j'avais compté, dit l'évêque. - ---Les lits, reprit le directeur, sont bien serrés les uns contre les -autres. - ---C'est ce que j'avais remarqué. - ---Les salles ne sont que des chambres, et l'air s'y renouvelle -difficilement. - ---C'est ce qui me semble. - ---Et puis, quand il y a un rayon de soleil, le jardin est bien petit -pour les convalescents. - ---C'est ce que je me disais. - ---Dans les épidémies, nous avons eu cette année le typhus, nous avons eu -une suette militaire il y a deux ans, cent malades quelquefois; nous ne -savons que faire. - ---C'est la pensée qui m'était venue. - ---Que voulez-vous, monseigneur? dit le directeur, il faut se résigner. - -Cette conversation avait lieu dans la salle à manger-galerie du -rez-de-chaussée. L'évêque garda un moment le silence, puis il se tourna -brusquement vers le directeur de l'hôpital: - ---Monsieur, dit-il, combien pensez-vous qu'il tiendrait de lits rien que -dans cette salle? - ---La salle à manger de monseigneur! s'écria le directeur stupéfait. - -L'évêque parcourait la salle du regard et semblait y faire avec les yeux -des mesures et des calculs. - ---Il y tiendrait bien vingt lits! dit-il, comme se parlant à lui-même. - -Puis élevant la voix: - ---Tenez, monsieur le directeur de l'hôpital, je vais vous dire. Il y a -évidemment une erreur. Vous êtes vingt-six personnes dans cinq ou six -petites chambres. Nous sommes trois ici, et nous avons place pour -soixante. Il y a erreur, je vous dis. Vous avez mon logis, et j'ai le -vôtre. Rendez-moi ma maison. C'est ici chez vous. - -Le lendemain, les vingt-six pauvres étaient installés dans le palais de -l'évêque et l'évêque était à l'hôpital. - -M. Myriel n'avait point de bien, sa famille ayant été ruinée par la -révolution. Sa soeur touchait une rente viagère de cinq cents francs -qui, au presbytère, suffisait à sa dépense personnelle. M. Myriel -recevait de l'état comme évêque un traitement de quinze mille francs. Le -jour même où il vint se loger dans la maison de l'hôpital, M. Myriel -détermina l'emploi de cette somme une fois pour toutes de la manière -suivante. Nous transcrivons ici une note écrite de sa main. - -_Note pour régler les dépenses de ma maison._ - -_Pour le petit séminaire: quinze cents livres_ -_Congrégation de la mission: cent livres_ -_Pour les lazaristes de Montdidier: cent livres_ -_Séminaire des missions étrangères à Paris: deux cents livres_ -_Congrégation du Saint-Esprit: cent cinquante livres_ -_Établissements religieux de la Terre-Sainte: cent livres_ -_Sociétés de charité maternelle: trois cents livres_ -_En sus, pour celle d'Arles: cinquante livres_ -_OEuvre pour l'amélioration des prisons: quatre cents livres_ -_OEuvre pour le soulagement et la délivrance des prisonniers: cinq cents -livres_ -_Pour libérer des pères de famille prisonniers pour dettes: mille livres_ -_Supplément au traitement des pauvres maîtres d'école du diocèse: deux -mille livres_ -_Grenier d'abondance des Hautes-Alpes: cent livres_ -_Congrégation des dames de Digne, de Manosque et de Sisteron, -pour l'enseignement gratuit des filles indigentes: quinze cents livres_ -_Pour les pauvres: six mille livres_ -_Ma dépense personnelle: mille livres_ - -Total: _quinze mille livres_ - -Pendant tout le temps qu'il occupa le siège de Digne, M. Myriel ne -changea presque rien à cet arrangement. Il appelait cela, comme on voit, -_avoir réglé les dépenses de sa maison_. - -Cet arrangement fut accepté avec une soumission absolue par mademoiselle -Baptistine. Pour cette sainte fille, M. de Digne était tout à la fois -son frère et son évêque, son ami selon la nature et son supérieur selon -l'église. Elle l'aimait et elle le vénérait tout simplement. Quand il -parlait, elle s'inclinait; quand il agissait, elle adhérait. La servante -seule, madame Magloire, murmura un peu. M. l'évêque, on l'a pu -remarquer, ne s'était réservé que mille livres, ce qui, joint à la -pension de mademoiselle Baptistine, faisait quinze cents francs par an. -Avec ces quinze cents francs, ces deux vieilles femmes et ce vieillard -vivaient. - -Et quand un curé de village venait à Digne, M. l'évêque trouvait encore -moyen de le traiter, grâce à la sévère économie de madame Magloire et à -l'intelligente administration de mademoiselle Baptistine. - -Un jour--il était à Digne depuis environ trois mois--l'évêque dit: - ---Avec tout cela je suis bien gêné! - ---Je le crois bien! s'écria madame Magloire, Monseigneur n'a seulement -pas réclamé la rente que le département lui doit pour ses frais de -carrosse en ville et de tournées dans le diocèse. Pour les évêques -d'autrefois c'était l'usage. - ---Tiens! dit l'évêque, vous avez raison, madame Magloire. - -Il fit sa réclamation. - -Quelque temps après, le conseil général, prenant cette demande en -considération, lui vota une somme annuelle de trois mille francs, sous -cette rubrique: _Allocation à M. l'évêque pour frais de carrosse, frais -de poste et frais de tournées pastorales_. - -Cela fit beaucoup crier la bourgeoisie locale, et, à cette occasion, un -sénateur de l'empire, ancien membre du conseil des cinq-cents favorable -au dix-huit brumaire et pourvu près de la ville de Digne d'une -sénatorerie magnifique, écrivit au ministre des cultes, M. Bigot de -Préameneu, un petit billet irrité et confidentiel dont nous extrayons -ces lignes authentiques: - -«--Des frais de carrosse? pourquoi faire dans une ville de moins de -quatre mille habitants? Des frais de poste et de tournées? à quoi bon -ces tournées d'abord? ensuite comment courir la poste dans un pays de -montagnes? Il n'y a pas de routes. On ne va qu'à cheval. Le pont même de -la Durance à Château-Arnoux peut à peine porter des charrettes à boeufs. -Ces prêtres sont tous ainsi. Avides et avares. Celui-ci a fait le bon -apôtre en arrivant. Maintenant il fait comme les autres. Il lui faut -carrosse et chaise de poste. Il lui faut du luxe comme aux anciens -évêques. Oh! toute cette prêtraille! Monsieur le comte, les choses -n'iront bien que lorsque l'empereur nous aura délivrés des calotins. À -bas le pape! (les affaires se brouillaient avec Rome). Quant à moi, je -suis pour César tout seul. Etc., etc.» - -La chose, en revanche, réjouit fort madame Magloire. - ---Bon, dit-elle à mademoiselle Baptistine, Monseigneur a commencé par -les autres, mais il a bien fallu qu'il finît par lui-même. Il a réglé -toutes ses charités. Voilà trois mille livres pour nous. Enfin! - -Le soir même, l'évêque écrivit et remit à sa soeur une note ainsi -conçue: - -_Frais de carrosse et de tournées._ - -_Pour donner du bouillon de viande aux malades de l'hôpital: quinze -cents livres_ -_Pour la société de charité maternelle d'Aix: deux cent cinquante livres_ -_Pour la société de charité maternelle de Draguignan: deux cent cinquante -livres_ -_Pour les enfants trouvés: cinq cents livres_ -_Pour les orphelins: cinq cents livres_ - -Total: _trois mille livres_ - -Tel était le budget de M. Myriel. - -Quant au casuel épiscopal, rachats de bans, dispenses, ondoiements, -prédications, bénédictions d'églises ou de chapelles, mariages, etc., -l'évêque le percevait sur les riches avec d'autant plus d'âpreté qu'il -le donnait aux pauvres. - -Au bout de peu de temps, les offrandes d'argent affluèrent. Ceux qui ont -et ceux qui manquent frappaient à la porte de M. Myriel, les uns venant -chercher l'aumône que les autres venaient y déposer. L'évêque, en moins -d'un an, devint le trésorier de tous les bienfaits et le caissier de -toutes les détresses. Des sommes considérables passaient par ses mains; -mais rien ne put faire qu'il changeât quelque chose à son genre de vie -et qu'il ajoutât le moindre superflu à son nécessaire. - -Loin de là. Comme il y a toujours encore plus de misère en bas que de -fraternité en haut, tout était donné, pour ainsi dire, avant d'être -reçu; c'était comme de l'eau sur une terre sèche; il avait beau recevoir -de l'argent, il n'en avait jamais. Alors il se dépouillait. - -L'usage étant que les évêques énoncent leurs noms de baptême en tête de -leurs mandements et de leurs lettres pastorales, les pauvres gens du -pays avaient choisi, avec une sorte d'instinct affectueux, dans les noms -et prénoms de l'évêque, celui qui leur présentait un sens, et ils ne -l'appelaient que monseigneur Bienvenu. Nous ferons comme eux, et nous le -nommerons ainsi dans l'occasion. Du reste, cette appellation lui -plaisait. - ---J'aime ce nom-là, disait-il. Bienvenu corrige monseigneur. - -Nous ne prétendons pas que le portrait que nous faisons ici soit -vraisemblable; nous nous bornons à dire qu'il est ressemblant. - - - - -Chapitre III - -À bon évêque dur évêché - - -M. l'évêque, pour avoir converti son carrosse en aumônes, n'en faisait -pas moins ses tournées. C'est un diocèse fatigant que celui de Digne. Il -a fort peu de plaines, beaucoup de montagnes, presque pas de routes, on -l'a vu tout à l'heure; trente-deux cures, quarante et un vicariats et -deux cent quatre-vingt-cinq succursales. Visiter tout cela, c'est une -affaire. M. l'évêque en venait à bout. Il allait à pied quand c'était -dans le voisinage, en carriole dans la plaine, en cacolet dans la -montagne. Les deux vieilles femmes l'accompagnaient. Quand le trajet -était trop pénible pour elles, il allait seul. - -Un jour, il arriva à Senez, qui est une ancienne ville épiscopale, monté -sur un âne. Sa bourse, fort à sec dans ce moment, ne lui avait pas -permis d'autre équipage. Le maire de la ville vint le recevoir à la -porte de l'évêché et le regardait descendre de son âne avec des yeux -scandalisés. Quelques bourgeois riaient autour de lui. - ---Monsieur le maire, dit l'évêque, et messieurs les bourgeois, je vois -ce qui vous scandalise; vous trouvez que c'est bien de l'orgueil à un -pauvre prêtre de monter une monture qui a été celle de Jésus-Christ. Je -l'ai fait par nécessité, je vous assure, non par vanité. - -Dans ses tournées, il était indulgent et doux, et prêchait moins qu'il -ne causait. Il ne mettait aucune vertu sur un plateau inaccessible. Il -n'allait jamais chercher bien loin ses raisonnements et ses modèles. -Aux habitants d'un pays il citait l'exemple du pays voisin. Dans les -cantons où l'on était dur pour les nécessiteux, il disait: - ---Voyez les gens de Briançon. Ils ont donné aux indigents, aux veuves et -aux orphelins le droit de faire faucher leurs prairies trois jours avant -tous les autres. Ils leur rebâtissent gratuitement leurs maisons quand -elles sont en ruines. Aussi est-ce un pays béni de Dieu. Durant tout un -siècle de cent ans, il n'y a pas eu un meurtrier. - -Dans les villages âpres au gain et à la moisson, il disait: - ---Voyez ceux d'Embrun. Si un père de famille, au temps de la récolte, a -ses fils au service à l'armée et ses filles en service à la ville, et -qu'il soit malade et empêché, le curé le recommande au prône; et le -dimanche, après la messe, tous les gens du village, hommes, femmes, -enfants, vont dans le champ du pauvre homme lui faire sa moisson, et lui -rapportent paille et grain dans son grenier. - -Aux familles divisées par des questions d'argent et d'héritage, il -disait: - ---Voyez les montagnards de Devoluy, pays si sauvage qu'on n'y entend pas -le rossignol une fois en cinquante ans. Eh bien, quand le père meurt -dans une famille, les garçons s'en vont chercher fortune, et laissent le -bien aux filles, afin qu'elles puissent trouver des maris. - -Aux cantons qui ont le goût des procès et où les fermiers se ruinent en -papier timbré, il disait: - ---Voyez ces bons paysans de la vallée de Queyras. Ils sont là trois -mille âmes. Mon Dieu! c'est comme une petite république. On n'y connaît -ni le juge, ni l'huissier. Le maire fait tout. Il répartit l'impôt, taxe -chacun en conscience, juge les querelles gratis, partage les patrimoines -sans honoraires, rend des sentences sans frais; et on lui obéit, parce -que c'est un homme juste parmi des hommes simples. - -Aux villages où il ne trouvait pas de maître d'école, il citait encore -ceux de Queyras: - ---Savez-vous comment ils font? disait-il. Comme un petit pays de douze -ou quinze feux ne peut pas toujours nourrir un magister, ils ont des -maîtres d'école payés par toute la vallée qui parcourent les villages, -passant huit jours dans celui-ci, dix dans celui-là, et enseignant. Ces -magisters vont aux foires, où je les ai vus. On les reconnaît à des -plumes à écrire qu'ils portent dans la ganse de leur chapeau. Ceux qui -n'enseignent qu'à lire ont une plume, ceux qui enseignent la lecture et -le calcul ont deux plumes; ceux qui enseignent la lecture, le calcul et -le latin ont trois plumes. Ceux-là sont de grands savants. Mais quelle -honte d'être ignorants! Faites comme les gens de Queyras. - -Il parlait ainsi, gravement et paternellement, à défaut d'exemples -inventant des paraboles, allant droit au but, avec peu de phrases et -beaucoup d'images, ce qui était l'éloquence même de Jésus-Christ, -convaincu et persuadant. - - - - -Chapitre IV - -Les oeuvres semblables aux paroles - - -Sa conversation était affable et gaie. Il se mettait à la portée des -deux vieilles femmes qui passaient leur vie près de lui; quand il riait, -c'était le rire d'un écolier. - -Madame Magloire l'appelait volontiers _Votre Grandeur_. Un jour, il se -leva de son fauteuil et alla à sa bibliothèque chercher un livre. Ce -livre était sur un des rayons d'en haut. Comme l'évêque était d'assez -petite taille, il ne put y atteindre. - ---Madame Magloire, dit-il, apportez-moi une chaise. Ma grandeur ne va -pas jusqu'à cette planche. - -Une de ses parentes éloignées, madame la comtesse de Lô, laissait -rarement échapper une occasion d'énumérer en sa présence ce qu'elle -appelait «les espérances» de ses trois fils. Elle avait plusieurs -ascendants fort vieux et proches de la mort dont ses fils étaient -naturellement les héritiers. Le plus jeune des trois avait à recueillir -d'une grand'tante cent bonnes mille livres de rentes; le deuxième était -substitué au titre de duc de son oncle; l'aîné devait succéder à la -pairie de son aïeul. L'évêque écoutait habituellement en silence ces -innocents et pardonnables étalages maternels. Une fois pourtant, il -paraissait plus rêveur que de coutume, tandis que madame de Lô -renouvelait le détail de toutes ces successions et de toutes ces -«espérances». Elle s'interrompit avec quelque impatience: - ---Mon Dieu, mon cousin! mais à quoi songez-vous donc? - ---Je songe, dit l'évêque, à quelque chose de singulier qui est, je -crois, dans saint Augustin: «Mettez votre espérance dans celui auquel on -ne succède point.» - -Une autre fois, recevant une lettre de faire-part du décès d'un -gentilhomme du pays, où s'étalaient en une longue page, outre les -dignités du défunt, toutes les qualifications féodales et nobiliaires de -tous ses parents: - ---Quel bon dos a la mort! s'écria-t-il. Quelle admirable charge de -titres on lui fait allègrement porter, et comme il faut que les hommes -aient de l'esprit pour employer ainsi la tombe à la vanité! - -Il avait dans l'occasion une raillerie douce qui contenait presque -toujours un sens sérieux. Pendant un carême, un jeune vicaire vint à -Digne et prêcha dans la cathédrale. Il fut assez éloquent. Le sujet de -son sermon était la charité. Il invita les riches à donner aux -indigents, afin d'éviter l'enfer qu'il peignit le plus effroyable qu'il -put et de gagner le paradis qu'il fit désirable et charmant. Il y avait -dans l'auditoire un riche marchand retiré, un peu usurier, nommé M. -Géborand, lequel avait gagné un demi-million à fabriquer de gros draps, -des serges, des cadis et des gasquets. De sa vie M. Géborand n'avait -fait l'aumône à un malheureux. À partir de ce sermon, on remarqua qu'il -donnait tous les dimanches un sou aux vieilles mendiantes du portail de -la cathédrale. Elles étaient six à se partager cela. Un jour, l'évêque -le vit faisant sa charité et dit à sa soeur avec un sourire: - ---Voilà monsieur Géborand qui achète pour un sou de paradis. - -Quand il s'agissait de charité, il ne se rebutait pas, même devant un -refus, et il trouvait alors des mots qui faisaient réfléchir. Une fois, -il quêtait pour les pauvres dans un salon de la ville. Il y avait là le -marquis de Champtercier, vieux, riche, avare, lequel trouvait moyen -d'être tout ensemble ultra-royaliste et ultra-voltairien. Cette variété -a existé. L'évêque, arrivé à lui, lui toucha le bras. - ---Monsieur le marquis, il faut que vous me donniez quelque chose. - -Le marquis se retourna et répondit sèchement: - ---Monseigneur, j'ai mes pauvres. - ---Donnez-les-moi, dit l'évêque. - -Un jour, dans la cathédrale, il fit ce sermon. - -«Mes très chers frères, mes bons amis, il y a en France treize cent -vingt mille maisons de paysans qui n'ont que trois ouvertures, dix-huit -cent dix-sept mille qui ont deux ouvertures, la porte et une fenêtre, et -enfin trois cent quarante-six mille cabanes qui n'ont qu'une ouverture, -la porte. Et cela, à cause d'une chose qu'on appelle l'impôt des portes -et fenêtres. Mettez-moi de pauvres familles, des vieilles femmes, des -petits enfants, dans ces logis-là, et voyez les fièvres et les maladies. -Hélas! Dieu donne l'air aux hommes, la loi le leur vend. Je n'accuse pas -la loi, mais je bénis Dieu. Dans l'Isère, dans le Var, dans les deux -Alpes, les hautes et les basses, les paysans n'ont pas même de -brouettes, ils transportent les engrais à dos d'hommes; ils n'ont pas de -chandelles, et ils brûlent des bâtons résineux et des bouts de corde -trempés dans la poix résine. C'est comme cela dans tout le pays haut du -Dauphiné. Ils font le pain pour six mois, ils le font cuire avec de la -bouse de vache séchée. L'hiver, ils cassent ce pain à coups de hache et -ils le font tremper dans l'eau vingt-quatre heures pour pouvoir le -manger.--Mes frères, ayez pitié! voyez comme on souffre autour de vous.» - -Né provençal, il s'était facilement familiarisé avec tous les patois du -midi. Il disait: «_Eh bé! moussu, sès sagé?_» comme dans le bas -Languedoc. «_Onté anaras passa?_» comme dans les basses Alpes. «_Puerte -un bouen moutou embe un bouen froumage grase_», comme dans le haut -Dauphiné. Ceci plaisait au peuple, et n'avait pas peu contribué à lui -donner accès près de tous les esprits. Il était dans la chaumière et -dans la montagne comme chez lui. Il savait dire les choses les plus -grandes dans les idiomes les plus vulgaires. Parlant toutes les langues, -il entrait dans toutes les âmes. Du reste, il était le même pour les -gens du monde et pour les gens du peuple. Il ne condamnait rien -hâtivement, et sans tenir compte des circonstances environnantes. Il -disait: - ---Voyons le chemin par où la faute a passé. - -Étant, comme il se qualifiait lui-même en souriant, un _ex-pécheur_, il -n'avait aucun des escarpements du rigorisme, et il professait assez -haut, et sans le froncement de sourcil des vertueux féroces, une -doctrine qu'on pourrait résumer à peu près ainsi: - -«L'homme a sur lui la chair qui est tout à la fois son fardeau et sa -tentation. Il la traîne et lui cède. - -«Il doit la surveiller, la contenir, la réprimer, et ne lui obéir qu'à -la dernière extrémité. Dans cette obéissance-là, il peut encore y avoir -de la faute; mais la faute, ainsi faite, est vénielle. C'est une chute, -mais une chute sur les genoux, qui peut s'achever en prière. - -«Être un saint, c'est l'exception; être un juste, c'est la règle. Errez, -défaillez, péchez, mais soyez des justes. - -«Le moins de péché possible, c'est la loi de l'homme. Pas de péché du -tout est le rêve de l'ange. Tout ce qui est terrestre est soumis au -péché. Le péché est une gravitation.» - -Quand il voyait tout le monde crier bien fort et s'indigner bien vite: - ---Oh! oh! disait-il en souriant, il y a apparence que ceci est un gros -crime que tout le monde commet. Voilà les hypocrisies effarées qui se -dépêchent de protester et de se mettre à couvert. - -Il était indulgent pour les femmes et les pauvres sur qui pèse le poids -de la société humaine. Il disait: - ---Les fautes des femmes, des enfants, des serviteurs, des faibles, des -indigents et des ignorants sont la faute des maris, des pères, des -maîtres, des forts, des riches et des savants. - -Il disait encore: - ---À ceux qui ignorent, enseignez-leur le plus de choses que vous -pourrez; la société est coupable de ne pas donner l'instruction gratis; -elle répond de la nuit qu'elle produit. Cette âme est pleine d'ombre, le -péché s'y commet. Le coupable n'est pas celui qui y fait le péché, mais -celui qui y a fait l'ombre. - -Comme on voit, il avait une manière étrange et à lui de juger les -choses. Je soupçonne qu'il avait pris cela dans l'évangile. - -Il entendit un jour conter dans un salon un procès criminel qu'on -instruisait et qu'on allait juger. Un misérable homme, par amour pour -une femme et pour l'enfant qu'il avait d'elle, à bout de ressources, -avait fait de la fausse monnaie. La fausse monnaie était encore punie de -mort à cette époque. La femme avait été arrêtée émettant la première -pièce fausse fabriquée par l'homme. On la tenait, mais on n'avait de -preuves que contre elle. Elle seule pouvait charger son amant et le -perdre en avouant. Elle nia. On insista. Elle s'obstina à nier. Sur ce, -le procureur du roi avait eu une idée. Il avait supposé une infidélité -de l'amant, et était parvenu, avec des fragments de lettres savamment -présentés, à persuader à la malheureuse qu'elle avait une rivale et que -cet homme la trompait. Alors, exaspérée de jalousie, elle avait dénoncé -son amant, tout avoué, tout prouvé. L'homme était perdu. Il allait être -prochainement jugé à Aix avec sa complice. On racontait le fait, et -chacun s'extasiait sur l'habileté du magistrat. En mettant la jalousie -en jeu, il avait fait jaillir la vérité par la colère, il avait fait -sortir la justice de la vengeance. L'évêque écoutait tout cela en -silence. Quand ce fut fini, il demanda: - ---Où jugera-t-on cet homme et cette femme? - ---À la cour d'assises. - -Il reprit: - ---Et où jugera-t-on monsieur le procureur du roi? - -Il arriva à Digne une aventure tragique. Un homme fut condamné à mort -pour meurtre. C'était un malheureux pas tout à fait lettré, pas tout à -fait ignorant, qui avait été bateleur dans les foires et écrivain -public. Le procès occupa beaucoup la ville. La veille du jour fixé pour -l'exécution du condamné, l'aumônier de la prison tomba malade. Il -fallait un prêtre pour assister le patient à ses derniers moments. On -alla chercher le curé. Il paraît qu'il refusa en disant: Cela ne me -regarde pas. Je n'ai que faire de cette corvée et de ce saltimbanque; -moi aussi, je suis malade; d'ailleurs ce n'est pas là ma place. On -rapporta cette réponse à l'évêque qui dit: - ---Monsieur le curé a raison. Ce n'est pas sa place, c'est la mienne. - -Il alla sur-le-champ à la prison, il descendit au cabanon du -«saltimbanque», il l'appela par son nom, lui prit la main et lui parla. -Il passa toute la journée et toute la nuit près de lui, oubliant la -nourriture et le sommeil, priant Dieu pour l'âme du condamné et priant -le condamné pour la sienne propre. Il lui dit les meilleures vérités qui -sont les plus simples. Il fut père, frère, ami; évêque pour bénir -seulement. Il lui enseigna tout, en le rassurant et en le consolant. Cet -homme allait mourir désespéré. La mort était pour lui comme un abîme. -Debout et frémissant sur ce seuil lugubre, il reculait avec horreur. Il -n'était pas assez ignorant pour être absolument indifférent. Sa -condamnation, secousse profonde, avait en quelque sorte rompu çà et là -autour de lui cette cloison qui nous sépare du mystère des choses et que -nous appelons la vie. Il regardait sans cesse au dehors de ce monde par -ces brèches fatales, et ne voyait que des ténèbres. L'évêque lui fit -voir une clarté. - -Le lendemain, quand on vint chercher le malheureux, l'évêque était là. -Il le suivit. Il se montra aux yeux de la foule en camail violet et avec -sa croix épiscopale au cou, côte à côte avec ce misérable lié de cordes. - -Il monta sur la charrette avec lui, il monta sur l'échafaud avec lui. Le -patient, si morne et si accablé la veille, était rayonnant. Il sentait -que son âme était réconciliée et il espérait Dieu. L'évêque l'embrassa, -et, au moment où le couteau allait tomber, il lui dit: - ---Celui que l'homme tue, Dieu le ressuscite; celui que les frères -chassent retrouve le Père. Priez, croyez, entrez dans la vie! le Père -est là. - -Quand il redescendit de l'échafaud, il avait quelque chose dans son -regard qui fit ranger le peuple. On ne savait ce qui était le plus -admirable de sa pâleur ou de sa sérénité. En rentrant à cet humble logis -qu'il appelait en souriant son palais, il dit à sa soeur: - ---Je viens d'officier pontificalement. - -Comme les choses les plus sublimes sont souvent aussi les choses les -moins comprises, il y eut dans la ville des gens qui dirent, en -commentant cette conduite de l'évêque: «C'est de l'affectation.» Ceci ne -fut du reste qu'un propos de salons. Le peuple, qui n'entend pas malice -aux actions saintes, fut attendri et admira. - -Quant à l'évêque, avoir vu la guillotine fut pour lui un choc, et il fut -longtemps à s'en remettre. - -L'échafaud, en effet, quand il est là, dressé et debout, a quelque chose -qui hallucine. On peut avoir une certaine indifférence sur la peine de -mort, ne point se prononcer, dire oui et non, tant qu'on n'a pas vu de -ses yeux une guillotine; mais si l'on en rencontre une, la secousse est -violente, il faut se décider et prendre parti pour ou contre. Les uns -admirent, comme de Maistre; les autres exècrent, comme Beccaria. La -guillotine est la concrétion de la loi; elle se nomme _vindicte;_ elle -n'est pas neutre, et ne vous permet pas de rester neutre. Qui l'aperçoit -frissonne du plus mystérieux des frissons. Toutes les questions sociales -dressent autour de ce couperet leur point d'interrogation. L'échafaud -est vision. L'échafaud n'est pas une charpente, l'échafaud n'est pas une -machine, l'échafaud n'est pas une mécanique inerte faite de bois, de fer -et de cordes. Il semble que ce soit une sorte d'être qui a je ne sais -quelle sombre initiative; on dirait que cette charpente voit, que cette -machine entend, que cette mécanique comprend, que ce bois, ce fer et ces -cordes veulent. Dans la rêverie affreuse où sa présence jette l'âme, -l'échafaud apparaît terrible et se mêlant de ce qu'il fait. L'échafaud -est le complice du bourreau; il dévore; il mange de la chair, il boit du -sang. L'échafaud est une sorte de monstre fabriqué par le juge et par le -charpentier, un spectre qui semble vivre d'une espèce de vie -épouvantable faite de toute la mort qu'il a donnée. - -Aussi l'impression fut-elle horrible et profonde; le lendemain de -l'exécution et beaucoup de jours encore après, l'évêque parut accablé. -La sérénité presque violente du moment funèbre avait disparu: le fantôme -de la justice sociale l'obsédait. Lui qui d'ordinaire revenait de toutes -ses actions avec une satisfaction si rayonnante, il semblait qu'il se -fît un reproche. Par moments, il se parlait à lui-même, et bégayait à -demi-voix des monologues lugubres. En voici un que sa soeur entendit un -soir et recueillit: - ---Je ne croyais pas que cela fût si monstrueux. C'est un tort de -s'absorber dans la loi divine au point de ne plus s'apercevoir de la loi -humaine. La mort n'appartient qu'à Dieu. De quel droit les hommes -touchent-ils à cette chose inconnue? - -Avec le temps ces impressions s'atténuèrent, et probablement -s'effacèrent. Cependant on remarqua que l'évêque évitait désormais de -passer sur la place des exécutions. On pouvait appeler M. Myriel à toute -heure au chevet des malades et des mourants. Il n'ignorait pas que là -était son plus grand devoir et son plus grand travail. Les familles -veuves ou orphelines n'avaient pas besoin de le demander, il arrivait de -lui-même. Il savait s'asseoir et se taire de longues heures auprès de -l'homme qui avait perdu la femme qu'il aimait, de la mère qui avait -perdu son enfant. Comme il savait le moment de se taire, il savait aussi -le moment de parler. Ô admirable consolateur! il ne cherchait pas à -effacer la douleur par l'oubli, mais à l'agrandir et à la dignifier par -l'espérance. Il disait: - ---Prenez garde à la façon dont vous vous tournez vers les morts. Ne -songez pas à ce qui pourrit. Regardez fixement. Vous apercevrez la lueur -vivante de votre mort bien-aimé au fond du ciel. - -Il savait que la croyance est saine. Il cherchait à conseiller et à -calmer l'homme désespéré en lui indiquant du doigt l'homme résigné, et à -transformer la douleur qui regarde une fosse en lui montrant la douleur -qui regarde une étoile. - - - - -Chapitre V - -Que monseigneur Bienvenu faisait durer trop longtemps ses soutanes - - -La vie intérieure de M. Myriel était pleine des mêmes pensées que sa vie -publique. Pour qui eût pu la voir de près, c'eût été un spectacle grave -et charmant que cette pauvreté volontaire dans laquelle vivait M. -l'évêque de Digne. - -Comme tous les vieillards et comme la plupart des penseurs, il dormait -peu. Ce court sommeil était profond. Le matin il se recueillait pendant -une heure, puis il disait sa messe, soit à la cathédrale, soit dans son -oratoire. Sa messe dite, il déjeunait d'un pain de seigle trempé dans le -lait de ses vaches. Puis il travaillait. - -Un évêque est un homme fort occupé; il faut qu'il reçoive tous les jours -le secrétaire de l'évêché, qui est d'ordinaire un chanoine, presque tous -les jours ses grands vicaires. Il a des congrégations à contrôler, des -privilèges à donner, toute une librairie ecclésiastique à examiner, -paroissiens, catéchismes diocésains, livres d'heures, etc., des -mandements à écrire, des prédications à autoriser, des curés et des -maires à mettre d'accord, une correspondance cléricale, une -correspondance administrative, d'un côté l'état, de l'autre le -Saint-Siège, mille affaires. - -Le temps que lui laissaient ces mille affaires, ses offices et son -bréviaire, il le donnait d'abord aux nécessiteux, aux malades et aux -affligés; le temps que les affligés, les malades et les nécessiteux lui -laissaient, il le donnait au travail. Tantôt il bêchait la terre dans -son jardin, tantôt il lisait et écrivait. Il n'avait qu'un mot pour ces -deux sortes de travail; il appelait cela _jardiner_. - ---L'esprit est un jardin, disait-il. - -À midi, il dînait. Le dîner ressemblait au déjeuner. - -Vers deux heures, quand le temps était beau, il sortait et se promenait -à pied dans la campagne ou dans la ville, entrant souvent dans les -masures. On le voyait cheminer seul, tout à ses pensées, l'oeil baissé, -appuyé sur sa longue canne, vêtu de sa douillette violette ouatée et -bien chaude, chaussé de bas violets dans de gros souliers, et coiffé de -son chapeau plat qui laissait passer par ses trois cornes trois glands -d'or à graine d'épinards. - -C'était une fête partout où il paraissait. On eût dit que son passage -avait quelque chose de réchauffant et de lumineux. Les enfants et les -vieillards venaient sur le seuil des portes pour l'évêque comme pour le -soleil. Il bénissait et on le bénissait. On montrait sa maison à -quiconque avait besoin de quelque chose. - -Çà et là, il s'arrêtait, parlait aux petits garçons et aux petites -filles et souriait aux mères. Il visitait les pauvres tant qu'il avait -de l'argent; quand il n'en avait plus, il visitait les riches. - -Comme il faisait durer ses soutanes beaucoup de temps, et qu'il ne -voulait pas qu'on s'en aperçût, il ne sortait jamais dans la ville -autrement qu'avec sa douillette violette. Cela le gênait un peu en été. - -Le soir à huit heures et demie il soupait avec sa soeur, madame Magloire -debout derrière eux et les servant à table. Rien de plus frugal que ce -repas. Si pourtant l'évêque avait un de ses curés à souper, madame -Magloire en profitait pour servir à Monseigneur quelque excellent -poisson des lacs ou quelque fin gibier de la montagne. Tout curé était -un prétexte à bon repas; l'évêque se laissait faire. Hors de là, son -ordinaire ne se composait guère que de légumes cuits dans l'eau et de -soupe à l'huile. Aussi disait-on dans la ville: - ---Quand l'évêque fait pas chère de curé, il fait chère de trappiste. - -Après son souper, il causait pendant une demi-heure avec mademoiselle -Baptistine et madame Magloire; puis il rentrait dans sa chambre et se -remettait à écrire, tantôt sur des feuilles volantes, tantôt sur la -marge de quelque in-folio. Il était lettré et quelque peu savant. Il a -laissé cinq ou six manuscrits assez curieux; entre autres une -dissertation sur le verset de la Genèse: _Au commencement l'esprit de -Dieu flottait sur les eaux_. Il confronte avec ce verset trois textes: -la version arabe qui dit: _Les vents de Dieu soufflaient;_ Flavius -Josèphe qui dit: _Un vent d'en haut se précipitait sur la terre_, et -enfin la paraphrase chaldaïque d'Onkelos qui porte: _Un vent venant de -Dieu soufflait sur la face des eaux_. Dans une autre dissertation, il -examine les oeuvres théologiques de Hugo, évêque de Ptolémaïs, -arrière-grand-oncle de celui qui écrit ce livre, et il établit qu'il -faut attribuer à cet évêque les divers opuscules publiés, au siècle -dernier, sous le pseudonyme de Barleycourt. - -Parfois au milieu d'une lecture, quel que fût le livre qu'il eût entre -les mains, il tombait tout à coup dans une méditation profonde, d'où il -ne sortait que pour écrire quelques lignes sur les pages mêmes du -volume. Ces lignes souvent n'ont aucun rapport avec le livre qui les -contient. Nous avons sous les yeux une note écrite par lui sur une des -marges d'un in-quarto intitulé: _Correspondance du lord Germain avec les -généraux Clinton, Cornwallis et les amiraux de la station de l'Amérique. -À Versailles, chez Poinçot, libraire, et à Paris, chez Pissot, libraire, -quai des Augustins_. - -Voici cette note: - -«Ô vous qui êtes! - -«L'Ecclésiaste vous nomme Toute-Puissance, les Macchabées vous nomment -Créateur, l'Épître aux Éphésiens vous nomme Liberté, Baruch vous nomme -Immensité, les Psaumes vous nomment Sagesse et Vérité, Jean vous nomme -Lumière, les Rois vous nomment Seigneur, l'Exode vous appelle -Providence, le Lévitique Sainteté, Esdras Justice, la création vous -nomme Dieu, l'homme vous nomme Père; mais Salomon vous nomme -Miséricorde, et c'est là le plus beau de tous vos noms.» - -Vers neuf heures du soir, les deux femmes se retiraient et montaient à -leurs chambres au premier, le laissant jusqu'au matin seul au -rez-de-chaussée. - -Ici il est nécessaire que nous donnions une idée exacte du logis de M. -l'évêque de Digne. - - - - -Chapitre VI - -Par qui il faisait garder sa maison - - -La maison qu'il habitait se composait, nous l'avons dit, d'un -rez-de-chaussée et d'un seul étage: trois pièces au rez-de-chaussée, -trois chambres au premier, au-dessus un grenier. Derrière la maison, un -jardin d'un quart d'arpent. Les deux femmes occupaient le premier. -L'évêque logeait en bas. La première pièce, qui s'ouvrait sur la rue, -lui servait de salle à manger, la deuxième de chambre à coucher, et la -troisième d'oratoire. On ne pouvait sortir de cet oratoire sans passer -par la chambre à coucher, et sortir de la chambre à coucher sans passer -par la salle à manger. Dans l'oratoire, au fond, il y avait une alcôve -fermée, avec un lit pour les cas d'hospitalité. M. l'évêque offrait ce -lit aux curés de campagne que des affaires ou les besoins de leur -paroisse amenaient à Digne. - -La pharmacie de l'hôpital, petit bâtiment ajouté à la maison et pris sur -le jardin, avait été transformée en cuisine et en cellier. - -Il y avait en outre dans le jardin une étable qui était l'ancienne -cuisine de l'hospice et où l'évêque entretenait deux vaches. Quelle que -fût la quantité de lait qu'elles lui donnassent, il en envoyait -invariablement tous les matins la moitié aux malades de l'hôpital.--Je -paye ma dîme, disait-il. - -Sa chambre était assez grande et assez difficile à chauffer dans la -mauvaise saison. Comme le bois est très cher à Digne, il avait imaginé -de faire faire dans l'étable à vaches un compartiment fermé d'une -cloison en planches. C'était là qu'il passait ses soirées dans les -grands froids. Il appelait cela son _salon d'hiver_. - -Il n'y avait dans ce salon d'hiver, comme dans la salle à manger, -d'autres meubles qu'une table de bois blanc, carrée, et quatre chaises -de paille. La salle à manger était ornée en outre d'un vieux buffet -peint en rose à la détrempe. Du buffet pareil, convenablement habillé de -napperons blancs et de fausses dentelles, l'évêque avait fait l'autel -qui décorait son oratoire. - -Ses pénitentes riches et les saintes femmes de Digne s'étaient souvent -cotisées pour faire les frais d'un bel autel neuf à l'oratoire de -monseigneur; il avait chaque fois pris l'argent et l'avait donné aux -pauvres. - ---Le plus beau des autels, disait-il, c'est l'âme d'un malheureux -consolé qui remercie Dieu. - -Il avait dans son oratoire deux chaises prie-Dieu en paille, et un -fauteuil à bras également en paille dans sa chambre à coucher. Quand par -hasard il recevait sept ou huit personnes à la fois, le préfet, ou le -général, ou l'état-major du régiment en garnison, ou quelques élèves du -petit séminaire, on était obligé d'aller chercher dans l'étable les -chaises du salon d'hiver, dans l'oratoire les prie-Dieu, et le fauteuil -dans la chambre à coucher; de cette façon, on pouvait réunir jusqu'à -onze sièges pour les visiteurs. À chaque nouvelle visite on démeublait -une pièce. - -Il arrivait parfois qu'on était douze; alors l'évêque dissimulait -l'embarras de la situation en se tenant debout devant la cheminée si -c'était l'hiver, ou en proposant un tour dans le jardin si c'était -l'été. - -Il y avait bien encore dans l'alcôve fermée une chaise, mais elle était -à demi dépaillée et ne portait que sur trois pieds, ce qui faisait -qu'elle ne pouvait servir qu'appuyée contre le mur. Mademoiselle -Baptistine avait bien aussi dans sa chambre une très grande bergère en -bois jadis doré et revêtue de pékin à fleurs, mais on avait été obligé -de monter cette bergère au premier par la fenêtre, l'escalier étant trop -étroit; elle ne pouvait donc pas compter parmi les en-cas du mobilier. - -L'ambition de mademoiselle Baptistine eût été de pouvoir acheter un -meuble de salon en velours d'Utrecht jaune à rosaces et en acajou à cou -de cygne, avec canapé. Mais cela eût coûté au moins cinq cents francs, -et, ayant vu qu'elle n'avait réussi à économiser pour cet objet que -quarante-deux francs dix sous en cinq ans, elle avait fini par y -renoncer. D'ailleurs qui est-ce qui atteint son idéal? - -Rien de plus simple à se figurer que la chambre à coucher de l'évêque. -Une porte-fenêtre donnant sur le jardin, vis-à-vis le lit; un lit -d'hôpital, en fer avec baldaquin de serge verte; dans l'ombre du lit, -derrière un rideau, les ustensiles de toilette trahissant encore les -anciennes habitudes élégantes de l'homme du monde; deux portes, l'une -près de la cheminée, donnant dans l'oratoire; l'autre, près de la -bibliothèque, donnant dans la salle à manger; la bibliothèque, grande -armoire vitrée pleine de livres; la cheminée, de bois peint en marbre, -habituellement sans feu; dans la cheminée, une paire de chenets en fer -ornés de deux vases à guirlandes et cannelures jadis argentés à l'argent -haché, ce qui était un genre de luxe épiscopal; au-dessus, à l'endroit -où d'ordinaire on met la glace, un crucifix de cuivre désargenté fixé -sur un velours noir râpé dans un cadre de bois dédoré. Près de la -porte-fenêtre, une grande table avec un encrier, chargée de papiers -confus et de gros volumes. Devant la table, le fauteuil de paille. -Devant le lit, un prie-Dieu, emprunté à l'oratoire. - -Deux portraits dans des cadres ovales étaient accrochés au mur des deux -côtés du lit. De petites inscriptions dorées sur le fond neutre de la -toile à côté des figures indiquaient que les portraits représentaient, -l'un, l'abbé de Chaliot, évêque de Saint-Claude, l'autre, l'abbé -Tourteau, vicaire général d'Agde, abbé de Grand-Champ, ordre de Cîteaux, -diocèse de Chartres. L'évêque, en succédant dans cette chambre aux -malades de l'hôpital, y avait trouvé ces portraits et les y avait -laissés. C'étaient des prêtres, probablement des donateurs: deux motifs -pour qu'il les respectât. Tout ce qu'il savait de ces deux personnages, -c'est qu'ils avaient été nommés par le roi, l'un à son évêché, l'autre à -son bénéfice, le même jour, le 27 avril 1785. Madame Magloire ayant -décroché les tableaux pour en secouer la poussière, l'évêque avait -trouvé cette particularité écrite d'une encre blanchâtre sur un petit -carré de papier jauni par le temps, collé avec quatre pains à cacheter -derrière le portrait de l'abbé de Grand-Champ. - -Il avait à sa fenêtre un antique rideau de grosse étoffe de laine qui -finit par devenir tellement vieux que, pour éviter la dépense d'un neuf, -madame Magloire fut obligée de faire une grande couture au beau milieu. -Cette couture dessinait une croix. L'évêque le faisait souvent -remarquer. - ---Comme cela fait bien! disait-il. - -Toutes les chambres de la maison, au rez-de-chaussée ainsi qu'au -premier, sans exception, étaient blanchies au lait de chaux, ce qui est -une mode de caserne et d'hôpital. - -Cependant, dans les dernières années, madame Magloire retrouva, comme on -le verra plus loin, sous le papier badigeonné, des peintures qui -ornaient l'appartement de mademoiselle Baptistine. Avant d'être -l'hôpital, cette maison avait été le parloir aux bourgeois. De là cette -décoration. Les chambres étaient pavées de briques rouges qu'on lavait -toutes les semaines, avec des nattes de paille tressée devant tous les -lits. Du reste, ce logis, tenu par deux femmes, était du haut en bas -d'une propreté exquise. C'était le seul luxe que l'évêque permit. Il -disait: - ---Cela ne prend rien aux pauvres. - -Il faut convenir cependant qu'il lui restait de ce qu'il avait possédé -jadis six couverts d'argent et une grande cuiller à soupe que madame -Magloire regardait tous les jours avec bonheur reluire splendidement sur -la grosse nappe de toile blanche. Et comme nous peignons ici l'évêque de -Digne tel qu'il était, nous devons ajouter qu'il lui était arrivé plus -d'une fois de dire: - ---Je renoncerais difficilement à manger dans de l'argenterie. - -Il faut ajouter à cette argenterie deux gros flambeaux d'argent massif -qui lui venaient de l'héritage d'une grand'tante. Ces flambeaux -portaient deux bougies de cire et figuraient habituellement sur la -cheminée de l'évêque. Quand il avait quelqu'un à dîner, madame Magloire -allumait les deux bougies et mettait les deux flambeaux sur la table. - -Il y avait dans la chambre même de l'évêque, à la tête de son lit, un -petit placard dans lequel madame Magloire serrait chaque soir les six -couverts d'argent et la grande cuiller. Il faut dire qu'on n'en ôtait -jamais la clef. - -Le jardin, un peu gâté par les constructions assez laides dont nous -avons parlé, se composait de quatre allées en croix rayonnant autour -d'un puisard; une autre allée faisait tout le tour du jardin et -cheminait le long du mur blanc dont il était enclos. Ces allées -laissaient entre elles quatre carrés bordés de buis. Dans trois, madame -Magloire cultivait des légumes; dans le quatrième, l'évêque avait mis -des fleurs. Il y avait çà et là quelques arbres fruitiers. - -Une fois madame Magloire lui avait dit avec une sorte de malice douce: - ---Monseigneur, vous qui tirez parti de tout, voilà pourtant un carré -inutile. Il vaudrait mieux avoir là des salades que des bouquets. - ---Madame Magloire, répondit l'évêque, vous vous trompez. Le beau est -aussi utile que l'utile. - -Il ajouta après un silence: - ---Plus peut-être. - -Ce carré, composé de trois ou quatre plates-bandes, occupait M. l'évêque -presque autant que ses livres. Il y passait volontiers une heure ou -deux, coupant, sarclant, et piquant çà et là des trous en terre où il -mettait des graines. Il n'était pas aussi hostile aux insectes qu'un -jardinier l'eût voulu. Du reste, aucune prétention à la botanique; il -ignorait les groupes et le solidisme; il ne cherchait pas le moins du -monde à décider entre Tournefort et la méthode naturelle; il ne prenait -parti ni pour les utricules contre les cotylédons, ni pour Jussieu -contre Linné. Il n'étudiait pas les plantes; il aimait les fleurs. Il -respectait beaucoup les savants, il respectait encore plus les -ignorants, et, sans jamais manquer à ces deux respects, il arrosait ses -plates-bandes chaque soir d'été avec un arrosoir de fer-blanc peint en -vert. - -La maison n'avait pas une porte qui fermât à clef. La porte de la salle -à manger qui, nous l'avons dit, donnait de plain-pied sur la place de la -cathédrale, était jadis armée de serrures et de verrous comme une porte -de prison. L'évêque avait fait ôter toutes ces ferrures, et cette porte, -la nuit comme le jour, n'était fermée qu'au loquet. Le premier passant -venu, à quelque heure que ce fût, n'avait qu'à la pousser. Dans les -commencements, les deux femmes avaient été fort tourmentées de cette -porte jamais close; mais M. de Digne leur avait dit: - ---Faites mettre des verrous à vos chambres, si cela vous plaît. - -Elles avaient fini par partager sa confiance ou du moins par faire comme -si elles la partageaient. Madame Magloire seule avait de temps en temps -des frayeurs. Pour ce qui est de l'évêque, on peut trouver sa pensée -expliquée ou du moins indiquée dans ces trois lignes écrites par lui sur -la marge d'une bible: «Voici la nuance: la porte du médecin ne doit -jamais être fermée; la porte du prêtre doit toujours être ouverte.» Sur -un autre livre, intitulé _Philosophie de la science médicale_, il avait -écrit cette autre note: «Est-ce que je ne suis pas médecin comme eux? -Moi aussi j'ai mes malades; d'abord j'ai les leurs, qu'ils appellent les -malades; et puis j'ai les miens, que j'appelle les malheureux.» - -Ailleurs encore il avait écrit: «Ne demandez pas son nom à qui vous -demande un gîte. C'est surtout celui-là que son nom embarrasse qui a -besoin d'asile.» - -Il advint qu'un digne curé, je ne sais plus si c'était le curé de -Couloubroux ou le curé de Pompierry, s'avisa de lui demander un jour, -probablement à l'instigation de madame Magloire, si Monseigneur était -bien sûr de ne pas commettre jusqu'à un certain point une imprudence en -laissant jour et nuit sa porte ouverte à la disposition de qui voulait -entrer, et s'il ne craignait pas enfin qu'il n'arrivât quelque malheur -dans une maison si peu gardée. L'évêque lui toucha l'épaule avec une -gravité douce et lui dit:--_Nisi Dominus custodierit domum, in vanum -vigilant qui custodiunt eam_. - -Puis il parla d'autre chose. - -Il disait assez volontiers: - ---Il y a la bravoure du prêtre comme il y a la bravoure du colonel de -dragons. Seulement, ajoutait-il, la nôtre doit être tranquille. - - - - -Chapitre VII - -Cravatte - - -Ici se place naturellement un fait que nous ne devons pas omettre, car -il est de ceux qui font le mieux voir quel homme c'était que M. l'évêque -de Digne. - -Après la destruction de la bande de Gaspard Bès qui avait infesté les -gorges d'Ollioules, un de ses lieutenants, Cravatte, se réfugia dans la -montagne. Il se cacha quelque temps avec ses bandits, reste de la troupe -de Gaspard Bès, dans le comté de Nice, puis gagna le Piémont, et tout à -coup reparut en France, du côté de Barcelonnette. On le vit à Jauziers -d'abord, puis aux Tuiles. Il se cacha dans les cavernes du -Joug-de-l'Aigle, et de là il descendait vers les hameaux et les villages -par les ravins de l'Ubaye et de l'Ubayette. Il osa même pousser jusqu'à -Embrun, pénétra une nuit dans la cathédrale et dévalisa la sacristie. -Ses brigandages désolaient le pays. On mit la gendarmerie à ses -trousses, mais en vain. Il échappait toujours; quelquefois il résistait -de vive force. C'était un hardi misérable. Au milieu de toute cette -terreur, l'évêque arriva. Il faisait sa tournée. Au Chastelar, le maire -vint le trouver et l'engagea à rebrousser chemin. Cravatte tenait la -montagne jusqu'à l'Arche, et au-delà. Il y avait danger, même avec une -escorte. C'était exposer inutilement trois ou quatre malheureux -gendarmes. - ---Aussi, dit l'évêque, je compte aller sans escorte. - ---Y pensez-vous, monseigneur? s'écria le maire. - ---J'y pense tellement, que je refuse absolument les gendarmes et que je -vais partir dans une heure. - ---Partir? - ---Partir. - ---Seul? - ---Seul. - ---Monseigneur! vous ne ferez pas cela. - ---Il y a là, dans la montagne, reprit l'évêque, une humble petite -commune grande comme ça, que je n'ai pas vue depuis trois ans. Ce sont -mes bons amis. De doux et honnêtes bergers. Ils possèdent une chèvre sur -trente qu'ils gardent. Ils font de fort jolis cordons de laine de -diverses couleurs, et ils jouent des airs de montagne sur de petites -flûtes à six trous. Ils ont besoin qu'on leur parle de temps en temps du -bon Dieu. Que diraient-ils d'un évêque qui a peur? Que diraient-ils si -je n'y allais pas? - ---Mais, monseigneur, les brigands! Si vous rencontrez les brigands! - ---Tiens, dit l'évêque, j'y songe. Vous avez raison. Je puis les -rencontrer. Eux aussi doivent avoir besoin qu'on leur parle du bon Dieu. - ---Monseigneur! mais c'est une bande! c'est un troupeau de loups! - ---Monsieur le maire, c'est peut-être précisément de ce troupeau que -Jésus me fait le pasteur. Qui sait les voies de la Providence? - ---Monseigneur, ils vous dévaliseront. - ---Je n'ai rien. - ---Ils vous tueront. - ---Un vieux bonhomme de prêtre qui passe en marmottant ses momeries? Bah! -à quoi bon? - ---Ah! mon Dieu! si vous alliez les rencontrer! - ---Je leur demanderai l'aumône pour mes pauvres. - ---Monseigneur, n'y allez pas, au nom du ciel! vous exposez votre vie. - ---Monsieur le maire, dit l'évêque, n'est-ce décidément que cela? Je ne -suis pas en ce monde pour garder ma vie, mais pour garder les âmes. - -Il fallut le laisser faire. Il partit, accompagné seulement d'un enfant -qui s'offrit à lui servir de guide. Son obstination fit bruit dans le -pays, et effraya très fort. - -Il ne voulut emmener ni sa soeur ni madame Magloire. Il traversa la -montagne à mulet, ne rencontra personne, et arriva sain et sauf chez ses -«bons amis» les bergers. Il y resta quinze jours, prêchant, -administrant, enseignant, moralisant. Lorsqu'il fut proche de son -départ, il résolut de chanter pontificalement un _Te Deum_. Il en parla -au curé. Mais comment faire? pas d'ornements épiscopaux. On ne pouvait -mettre à sa disposition qu'une chétive sacristie de village avec -quelques vieilles chasubles de damas usé ornées de galons faux. - ---Bah! dit l'évêque. Monsieur le curé, annonçons toujours au prône notre -_Te Deum_. Cela s'arrangera. - -On chercha dans les églises d'alentour. Toutes les magnificences de ces -humbles paroisses réunies n'auraient pas suffi à vêtir convenablement un -chantre de cathédrale. Comme on était dans cet embarras, une grande -caisse fut apportée et déposée au presbytère pour M. l'évêque par deux -cavaliers inconnus qui repartirent sur-le-champ. On ouvrit la caisse; -elle contenait une chape de drap d'or, une mitre ornée de diamants, une -croix archiépiscopale, une crosse magnifique, tous les vêtements -pontificaux volés un mois auparavant au trésor de Notre-Dame d'Embrun. -Dans la caisse, il y avait un papier sur lequel étaient écrits ces mots: -_Cravatte à monseigneur Bienvenu_. - ---Quand je disais que cela s'arrangerait! dit l'évêque. - -Puis il ajouta en souriant: - ---À qui se contente d'un surplis de curé, Dieu envoie une chape -d'archevêque. - ---Monseigneur, murmura le curé en hochant la tête avec un sourire, Dieu, -ou le diable. - -L'évêque regarda fixement le curé et reprit avec autorité: - ---Dieu! - -Quand il revint au Chastelar, et tout le long de la route, on venait le -regarder par curiosité. Il retrouva au presbytère du Chastelar -mademoiselle Baptistine et madame Magloire qui l'attendaient, et il dit -à sa soeur: - ---Eh bien, avais-je raison? Le pauvre prêtre est allé chez ces pauvres -montagnards les mains vides, il en revient les mains pleines. J'étais -parti n'emportant que ma confiance en Dieu; je rapporte le trésor d'une -cathédrale. - -Le soir, avant de se coucher, il dit encore: - ---Ne craignons jamais les voleurs ni les meurtriers. Ce sont là les -dangers du dehors, les petits dangers. Craignons-nous nous-mêmes. Les -préjugés, voilà les voleurs; les vices, voilà les meurtriers. Les grands -dangers sont au dedans de nous. Qu'importe ce qui menace notre tête ou -notre bourse! Ne songeons qu'à ce qui menace notre âme. - -Puis se tournant vers sa soeur: - ---Ma soeur, de la part du prêtre jamais de précaution contre le -prochain. Ce que le prochain fait, Dieu le permet. Bornons-nous à prier -Dieu quand nous croyons qu'un danger arrive sur nous. Prions-le, non -pour nous, mais pour que notre frère ne tombe pas en faute à notre -occasion. - -Du reste, les événements étaient rares dans son existence. Nous -racontons ceux que nous savons; mais d'ordinaire il passait sa vie à -faire toujours les mêmes choses aux mêmes moments. Un mois de son année -ressemblait à une heure de sa journée. - -Quant à ce que devint «le trésor» de la cathédrale d'Embrun, on nous -embarrasserait de nous interroger là-dessus. C'étaient là de bien belles -choses, et bien tentantes, et bien bonnes à voler au profit des -malheureux. Volées, elles l'étaient déjà d'ailleurs. La moitié de -l'aventure était accomplie; il ne restait plus qu'à changer la direction -du vol, et qu'à lui faire faire un petit bout de chemin du côté des -pauvres. Nous n'affirmons rien du reste à ce sujet. Seulement on a -trouvé dans les papiers de l'évêque une note assez obscure qui se -rapporte peut-être à cette affaire, et qui est ainsi conçue: _La -question est de savoir si cela doit faire retour à la cathédrale ou à -l'hôpital_. - - - - -Chapitre VIII - -Philosophie après boire - - -Le sénateur dont il a été parlé plus haut était un homme entendu qui -avait fait son chemin avec une rectitude inattentive à toutes ces -rencontres qui font obstacle et qu'on nomme conscience, foi jurée, -justice, devoir; il avait marché droit à son but et sans broncher une -seule fois dans la ligne de son avancement et de son intérêt. C'était un -ancien procureur, attendri par le succès, pas méchant homme du tout, -rendant tous les petits services qu'il pouvait à ses fils, à ses -gendres, à ses parents, même à des amis; ayant sagement pris de la vie -les bons côtés, les bonnes occasions, les bonnes aubaines. Le reste lui -semblait assez bête. Il était spirituel, et juste assez lettré pour se -croire un disciple d'Épicure en n'étant peut-être qu'un produit de -Pigault-Lebrun. Il riait volontiers, et agréablement, des choses -infinies et éternelles, et des «billevesées du bonhomme évêque». Il en -riait quelquefois, avec une aimable autorité, devant M. Myriel lui-même, -qui écoutait. - -À je ne sais plus quelle cérémonie demi-officielle, le comte*** (ce -sénateur) et M. Myriel durent dîner chez le préfet. Au dessert, le -sénateur, un peu égayé, quoique toujours digne, s'écria: - ---Parbleu, monsieur l'évêque, causons. Un sénateur et un évêque se -regardent difficilement sans cligner de l'oeil. Nous sommes deux -augures. Je vais vous faire un aveu. J'ai ma philosophie. - ---Et vous avez raison, répondit l'évêque. Comme on fait sa philosophie -on se couche. Vous êtes sur le lit de pourpre, monsieur le sénateur. - -Le sénateur, encouragé, reprit: - ---Soyons bons enfants. - ---Bons diables même, dit l'évêque. - ---Je vous déclare, reprit le sénateur, que le marquis d'Argens, Pyrrhon, -Hobbes et M. Naigeon ne sont pas des maroufles. J'ai dans ma -bibliothèque tous mes philosophes dorés sur tranche. - ---Comme vous-même, monsieur le comte, interrompit l'évêque. - -Le sénateur poursuivit: - ---Je hais Diderot; c'est un idéologue, un déclamateur et un -révolutionnaire, au fond croyant en Dieu, et plus bigot que Voltaire. -Voltaire s'est moqué de Needham, et il a eu tort; car les anguilles de -Needham prouvent que Dieu est inutile. Une goutte de vinaigre dans une -cuillerée de pâte de farine supplée le _fiat lux_. Supposez la goutte -plus grosse et la cuillerée plus grande, vous avez le monde. L'homme, -c'est l'anguille. Alors à quoi bon le Père éternel? Monsieur l'évêque, -l'hypothèse Jéhovah me fatigue. Elle n'est bonne qu'à produire des gens -maigres qui songent creux. À bas ce grand Tout qui me tracasse! Vive -Zéro qui me laisse tranquille! De vous à moi, et pour vider mon sac, et -pour me confesser à mon pasteur comme il convient, je vous avoue que -j'ai du bon sens. Je ne suis pas fou de votre Jésus qui prêche à tout -bout de champ le renoncement et le sacrifice. Conseil d'avare à des -gueux. Renoncement! pourquoi? Sacrifice! à quoi? Je ne vois pas qu'un -loup s'immole au bonheur d'un autre loup. Restons donc dans la nature. -Nous sommes au sommet; ayons la philosophie supérieure. Que sert d'être -en haut, si l'on ne voit pas plus loin que le bout du nez des autres? -Vivons gaîment. La vie, c'est tout. Que l'homme ait un autre avenir, -ailleurs, là-haut, là-bas, quelque part, je n'en crois pas un traître -mot. Ah! l'on me recommande le sacrifice et le renoncement, je dois -prendre garde à tout ce que je fais, il faut que je me casse la tête sur -le bien et le mal, sur le juste et l'injuste, sur le _fas_ et le -_nefas_. Pourquoi? parce que j'aurai à rendre compte de mes actions. -Quand? après ma mort. Quel bon rêve! Après ma mort, bien fin qui me -pincera. Faites donc saisir une poignée de cendre par une main d'ombre. -Disons le vrai, nous qui sommes des initiés et qui avons levé la jupe -d'Isis: il n'y a ni bien, ni mal; il y a de la végétation. Cherchons le -réel. Creusons tout à fait. Allons au fond, que diable! Il faut flairer -la vérité, fouiller sous terre, et la saisir. Alors elle vous donne des -joies exquises. Alors vous devenez fort, et vous riez. Je suis carré par -la base, moi. Monsieur l'évêque, l'immortalité de l'homme est un -écoute-s'il-pleut. Oh! la charmante promesse! Fiez-vous-y. Le bon billet -qu'a Adam! On est âme, on sera ange, on aura des ailes bleues aux -omoplates. Aidez-moi donc, n'est-ce pas Tertullien qui dit que les -bienheureux iront d'un astre à l'autre? Soit. On sera les sauterelles -des étoiles. Et puis, on verra Dieu. Ta ta ta. Fadaises que tous ces -paradis. Dieu est une sonnette monstre. Je ne dirais point cela dans le -_Moniteur_, parbleu! mais je le chuchote entre amis. _Inter pocula_. -Sacrifier la terre au paradis, c'est lâcher la proie pour l'ombre. Être -dupe de l'infini! pas si bête. Je suis néant. Je m'appelle monsieur le -comte Néant, sénateur. Étais-je avant ma naissance? Non. Serai-je après -ma mort? Non. Que suis-je? un peu de poussière agrégée par un organisme. -Qu'ai-je à faire sur cette terre? J'ai le choix. Souffrir ou jouir. Où -me mènera la souffrance? Au néant. Mais j'aurai souffert. Où me mènera -la jouissance? Au néant. Mais j'aurai joui. Mon choix est fait. Il faut -être mangeant ou mangé. Je mange. Mieux vaut être la dent que l'herbe. -Telle est ma sagesse. Après quoi, va comme je te pousse, le fossoyeur -est là, le Panthéon pour nous autres, tout tombe dans le grand trou. -Fin. _Finis_. Liquidation totale. Ceci est l'endroit de -l'évanouissement. La mort est morte, croyez-moi. Qu'il y ait là -quelqu'un qui ait quelque chose à me dire, je ris d'y songer. Invention -de nourrices. Croquemitaine pour les enfants, Jéhovah pour les hommes. -Non, notre lendemain est de la nuit. Derrière la tombe, il n'y a plus -que des néants égaux. Vous avez été Sardanapale, vous avez été Vincent -de Paul, cela fait le même rien. Voilà le vrai. Donc vivez, par-dessus -tout. Usez de votre moi pendant que vous le tenez. En vérité, je vous le -dis, monsieur l'évêque, j'ai ma philosophie, et j'ai mes philosophes. Je -ne me laisse pas enguirlander par des balivernes. Après ça, il faut bien -quelque chose à ceux qui sont en bas, aux va-nu-pieds, aux gagne-petit, -aux misérables. On leur donne à gober les légendes, les chimères, l'âme, -l'immortalité, le paradis, les étoiles. Ils mâchent cela. Ils le mettent -sur leur pain sec. Qui n'a rien a le bon Dieu. C'est bien le moins. Je -n'y fais point obstacle, mais je garde pour moi monsieur Naigeon. Le bon -Dieu est bon pour le peuple. - -L'évêque battit des mains. - ---Voilà parler! s'écria-t-il. L'excellente chose, et vraiment -merveilleuse, que ce matérialisme-là! Ne l'a pas qui veut. Ah! quand on -l'a, on n'est plus dupe; on ne se laisse pas bêtement exiler comme -Caton, ni lapider comme Étienne, ni brûler vif comme Jeanne d'Arc. Ceux -qui ont réussi à se procurer ce matérialisme admirable ont la joie de se -sentir irresponsables, et de penser qu'ils peuvent dévorer tout, sans -inquiétude, les places, les sinécures, les dignités, le pouvoir bien ou -mal acquis, les palinodies lucratives, les trahisons utiles, les -savoureuses capitulations de conscience, et qu'ils entreront dans la -tombe, leur digestion faite. Comme c'est agréable! Je ne dis pas cela -pour vous, monsieur le sénateur. Cependant il m'est impossible de ne -point vous féliciter. Vous autres grands seigneurs, vous avez, vous le -dites, une philosophie à vous et pour vous, exquise, raffinée, -accessible aux riches seuls, bonne à toutes les sauces, assaisonnant -admirablement les voluptés de la vie. Cette philosophie est prise dans -les profondeurs et déterrée par des chercheurs spéciaux. Mais vous êtes -bons princes, et vous ne trouvez pas mauvais que la croyance au bon Dieu -soit la philosophie du peuple, à peu près comme l'oie aux marrons est la -dinde aux truffes du pauvre. - - - - -Chapitre IX - -Le frère raconté par la soeur - - -Pour donner une idée du ménage intérieur de M. l'évêque de Digne et de -la façon dont ces deux saintes filles subordonnaient leurs actions, -leurs pensées, même leurs instincts de femmes aisément effrayées, aux -habitudes et aux intentions de l'évêque, sans qu'il eût même à prendre -la peine de parler pour les exprimer, nous ne pouvons mieux faire que de -transcrire ici une lettre de mademoiselle Baptistine à madame la -vicomtesse de Boischevron, son amie d'enfance. Cette lettre est entre -nos mains. - -«Digne, 16 décembre 18.... - -«Ma bonne madame, pas un jour ne se passe sans que nous parlions de -vous. C'est assez notre habitude, mais il y a une raison de plus. -Figurez-vous qu'en lavant et époussetant les plafonds et les murs, -madame Magloire a fait des découvertes; maintenant nos deux chambres -tapissées de vieux papier blanchi à la chaux ne dépareraient pas un -château dans le genre du vôtre. Madame Magloire a déchiré tout le -papier. Il y avait des choses dessous. Mon salon, où il n'y a pas de -meubles, et dont nous nous servons pour étendre le linge après les -lessives, a quinze pieds de haut, dix-huit de large carrés, un plafond -peint anciennement avec dorure, des solives comme chez vous. C'était -recouvert d'une toile, du temps que c'était l'hôpital. Enfin des -boiseries du temps de nos grand'mères. Mais c'est ma chambre qu'il faut -voir. Madame Magloire a découvert, sous au moins dix papiers collés -dessus, des peintures, sans être bonnes, qui peuvent se supporter. C'est -Télémaque reçu chevalier par Minerve, c'est lui encore dans les jardins. -Le nom m'échappe. Enfin où les dames romaines se rendaient une seule -nuit. Que vous dirai-je? j'ai des romains, des romaines (_ici un mot -illisible_), et toute la suite. Madame Magloire a débarbouillé tout -cela, et cet été elle va réparer quelques petites avaries, revenir le -tout, et ma chambre sera un vrai musée. Elle a trouvé aussi dans un coin -du grenier deux consoles en bois, genre ancien. On demandait deux écus -de six livres pour les redorer, mais il vaut bien mieux donner cela aux -pauvres; d'ailleurs c'est fort laid, et j'aimerais mieux une table ronde -en acajou. - -«Je suis toujours bien heureuse. Mon frère est si bon. Il donne tout ce -qu'il a aux indigents et aux malades. Nous sommes très gênés. Le pays -est dur l'hiver, et il faut bien faire quelque chose pour ceux qui -manquent. Nous sommes à peu près chauffés et éclairés. Vous voyez que ce -sont de grandes douceurs. - -«Mon frère a ses habitudes à lui. Quand il cause, il dit qu'un évêque -doit être ainsi. Figurez-vous que la porte de la maison n'est jamais -fermée. Entre qui veut, et l'on est tout de suite chez mon frère. Il ne -craint rien, même la nuit. C'est là sa bravoure à lui, comme il dit. - -«Il ne veut pas que je craigne pour lui, ni que madame Magloire craigne. -Il s'expose à tous les dangers, et il ne veut même pas que nous ayons -l'air de nous en apercevoir. Il faut savoir le comprendre. - -«Il sort par la pluie, il marche dans l'eau, il voyage en hiver. Il n'a -pas peur de la nuit, des routes suspectes ni des rencontres. - -«L'an dernier, il est allé tout seul dans un pays de voleurs. Il n'a pas -voulu nous emmener. Il est resté quinze jours absent. À son retour, il -n'avait rien eu, on le croyait mort, et il se portait bien, et il a dit: -"Voilà comme on m'a volé!" Et il a ouvert une malle pleine de tous les -bijoux de la cathédrale d'Embrun, que les voleurs lui avaient donnés. - -«Cette fois-là, en revenant, comme j'étais allée à sa rencontre à deux -lieues avec d'autres de ses amis, je n'ai pu m'empêcher de le gronder un -peu, en ayant soin de ne parler que pendant que la voiture faisait du -bruit, afin que personne autre ne pût entendre. - -«Dans les premiers temps, je me disais: il n'y a pas de dangers qui -l'arrêtent, il est terrible. À présent j'ai fini par m'y accoutumer. Je -fais signe à madame Magloire pour qu'elle ne le contrarie pas. Il se -risque comme il veut. Moi j'emmène madame Magloire, je rentre dans ma -chambre, je prie pour lui, et je m'endors. Je suis tranquille, parce que -je sais bien que s'il lui arrivait malheur, ce serait ma fin. Je m'en -irais au bon Dieu avec mon frère et mon évêque. Madame Magloire a eu -plus de peine que moi à s'habituer à ce qu'elle appelait ses -imprudences. Mais à présent le pli est pris. Nous prions toutes les -deux, nous avons peur ensemble, et nous nous endormons. Le diable -entrerait dans la maison qu'on le laisserait faire. Après tout, que -craignons-nous dans cette maison? Il y a toujours quelqu'un avec nous, -qui est le plus fort. Le diable peut y passer, mais le bon Dieu -l'habite. - -«Voilà qui me suffit. Mon frère n'a plus même besoin de me dire un mot -maintenant. Je le comprends sans qu'il parle, et nous nous abandonnons à -la Providence. - -«Voilà comme il faut être avec un homme qui a du grand dans l'esprit. - -«J'ai questionné mon frère pour le renseignement que vous me demandez -sur la famille de Faux. Vous savez comme il sait tout et comme il a des -souvenirs, car il est toujours très bon royaliste. C'est de vrai une -très ancienne famille normande de la généralité de Caen. Il y a cinq -cents ans d'un Raoul de Faux, d'un Jean de Faux et d'un Thomas de Faux, -qui étaient des gentilshommes, dont un seigneur de Rochefort. Le dernier -était Guy-Étienne-Alexandre, et était maître de camp, et quelque chose -dans les chevaux-légers de Bretagne. Sa fille Marie-Louise a épousé -Adrien-Charles de Gramont, fils du duc Louis de Gramont, pair de France, -colonel des gardes françaises et lieutenant général des armées. On écrit -Faux, Fauq et Faoucq. - -«Bonne madame, recommandez-nous aux prières de votre saint parent, M. le -cardinal. Quant à votre chère Sylvanie, elle a bien fait de ne pas -prendre les courts instants qu'elle passe près de vous pour m'écrire. -Elle se porte bien, travaille selon vos désirs, m'aime toujours. C'est -tout ce que je veux. Son souvenir par vous m'est arrivé. Je m'en trouve -heureuse. Ma santé n'est pas trop mauvaise, et cependant je maigris tous -les jours davantage. Adieu, le papier me manque et me force de vous -quitter. Mille bonnes choses. - -«Baptistine. - -«P. S. Madame votre belle-soeur est toujours ici avec sa jeune famille. -Votre petit-neveu est charmant. Savez-vous qu'il a cinq ans bientôt! -Hier il a vu passer un cheval auquel on avait mis des genouillères, et -il disait: "Qu'est-ce qu'il a donc aux genoux?" Il est si gentil, cet -enfant! Son petit frère traîne un vieux balai dans l'appartement comme -une voiture, et dit: "Hu!" - -»Comme on le voit par cette lettre, ces deux femmes savaient se plier -aux façons d'être de l'évêque avec ce génie particulier de la femme qui -comprend l'homme mieux que l'homme ne se comprend. L'évêque de Digne, -sous cet air doux et candide qui ne se démentait jamais, faisait parfois -des choses grandes, hardies et magnifiques, sans paraître même s'en -douter. Elles en tremblaient, mais elles le laissaient faire. -Quelquefois madame Magloire essayait une remontrance avant; jamais -pendant ni après. Jamais on ne le troublait, ne fût-ce que par un signe, -dans une action commencée. À de certains moments, sans qu'il eût besoin -de le dire, lorsqu'il n'en avait peut-être pas lui-même conscience, tant -sa simplicité était parfaite, elles sentaient vaguement qu'il agissait -comme évêque; alors elles n'étaient plus que deux ombres dans la maison. -Elles le servaient passivement, et, si c'était obéir que de disparaître, -elles disparaissaient. Elles savaient, avec une admirable délicatesse -d'instinct, que certaines sollicitudes peuvent gêner. Aussi, même le -croyant en péril, elles comprenaient, je ne dis pas sa pensée, mais sa -nature, jusqu'au point de ne plus veiller sur lui. Elles le confiaient à -Dieu. - -D'ailleurs Baptistine disait, comme on vient de le lire, que la fin de -son frère serait la sienne. Madame Magloire ne le disait pas, mais elle -le savait. - - - - -Chapitre X - -L'évêque en présence d'une lumière inconnue - - -À une époque un peu postérieure à la date de la lettre citée dans les -pages précédentes, il fit une chose, à en croire toute la ville, plus -risquée encore que sa promenade à travers les montagnes des bandits. Il -y avait près de Digne, dans la campagne, un homme qui vivait solitaire. -Cet homme, disons tout de suite le gros mot, était un ancien -conventionnel. Il se nommait G. - -On parlait du conventionnel G. dans le petit monde de Digne avec une -sorte d'horreur. Un conventionnel, vous figurez-vous cela? Cela existait -du temps qu'on se tutoyait et qu'on disait: citoyen. Cet homme était à -peu près un monstre. Il n'avait pas voté la mort du roi, mais presque. -C'était un quasi-régicide. Il avait été terrible. Comment, au retour des -princes légitimes, n'avait-on pas traduit cet homme-là devant une cour -prévôtale? On ne lui eût pas coupé la tête, si vous voulez, il faut de -la clémence, soit; mais un bon bannissement à vie. Un exemple enfin! -etc., etc. C'était un athée d'ailleurs, comme tous ces -gens-là.--Commérages des oies sur le vautour. - -Était-ce du reste un vautour que G.? Oui, si l'on en jugeait par ce -qu'il y avait de farouche dans sa solitude. N'ayant pas voté la mort du -roi, il n'avait pas été compris dans les décrets d'exil et avait pu -rester en France. - -Il habitait, à trois quarts d'heure de la ville, loin de tout hameau, -loin de tout chemin, on ne sait quel repli perdu d'un vallon très -sauvage. Il avait là, disait-on, une espèce de champ, un trou, un -repaire. Pas de voisins; pas même de passants. Depuis qu'il demeurait -dans ce vallon, le sentier qui y conduisait avait disparu sous l'herbe. -On parlait de cet endroit-là comme de la maison du bourreau. Pourtant -l'évêque songeait, et de temps en temps regardait l'horizon à l'endroit -où un bouquet d'arbres marquait le vallon du vieux conventionnel, et il -disait: - ---Il y a là une âme qui est seule. - -Et au fond de sa pensée il ajoutait: «Je lui dois ma visite.» - -Mais, avouons-le, cette idée, au premier abord naturelle, lui -apparaissait, après un moment de réflexion, comme étrange et impossible, -et presque repoussante. Car, au fond, il partageait l'impression -générale, et le conventionnel lui inspirait, sans qu'il s'en rendît -clairement compte, ce sentiment qui est comme la frontière de la haine -et qu'exprime si bien le mot éloignement. - -Toutefois, la gale de la brebis doit-elle faire reculer le pasteur? Non. -Mais quelle brebis! - -Le bon évêque était perplexe. Quelquefois il allait de ce côté-là, puis -il revenait. Un jour enfin le bruit se répandit dans la ville qu'une -façon de jeune pâtre qui servait le conventionnel G. dans sa bauge était -venu chercher un médecin; que le vieux scélérat se mourait, que la -paralysie le gagnait, et qu'il ne passerait pas la nuit. - ---Dieu merci! ajoutaient quelques-uns. - -L'évêque prit son bâton, mit son pardessus à cause de sa soutane un peu -trop usée, comme nous l'avons dit, et aussi à cause du vent du soir qui -ne devait pas tarder à souffler, et partit. - -Le soleil déclinait et touchait presque à l'horizon, quand l'évêque -arriva à l'endroit excommunié. Il reconnut avec un certain battement de -coeur qu'il était près de la tanière. Il enjamba un fossé, franchit une -haie, leva un échalier, entra dans un courtil délabré, fit quelques pas -assez hardiment, et tout à coup, au fond de la friche, derrière une -haute broussaille, il aperçut la caverne. - -C'était une cabane toute basse, indigente, petite et propre, avec une -treille clouée à la façade. - -Devant la porte, dans une vieille chaise à roulettes, fauteuil du -paysan, il y avait un homme en cheveux blancs qui souriait au soleil. - -Près du vieillard assis se tenait debout un jeune garçon, le petit -pâtre. Il tendait au vieillard une jatte de lait. - -Pendant que l'évêque regardait, le vieillard éleva la voix: - ---Merci, dit-il, je n'ai plus besoin de rien. - -Et son sourire quitta le soleil pour s'arrêter sur l'enfant. - -L'évêque s'avança. Au bruit qu'il fit en marchant, le vieux homme assis -tourna la tête, et son visage exprima toute la quantité de surprise -qu'on peut avoir après une longue vie. - ---Depuis que je suis ici, dit-il, voilà la première fois qu'on entre -chez moi. Qui êtes-vous, monsieur? - -L'évêque répondit: - ---Je me nomme Bienvenu Myriel. - ---Bienvenu Myriel! j'ai entendu prononcer ce nom. Est-ce que c'est vous -que le peuple appelle monseigneur Bienvenu? - ---C'est moi. - -Le vieillard reprit avec un demi-sourire: - ---En ce cas, vous êtes mon évêque? - ---Un peu. - ---Entrez, monsieur. - -Le conventionnel tendit la main à l'évêque, mais l'évêque ne la prit -pas. L'évêque se borna à dire: - ---Je suis satisfait de voir qu'on m'avait trompé. Vous ne me semblez, -certes, pas malade. - ---Monsieur, répondit le vieillard, je vais guérir. - -Il fit une pause et dit: - ---Je mourrai dans trois heures. - -Puis il reprit: - ---Je suis un peu médecin; je sais de quelle façon la dernière heure -vient. Hier, je n'avais que les pieds froids; aujourd'hui, le froid a -gagné les genoux; maintenant je le sens qui monte jusqu'à la ceinture; -quand il sera au coeur, je m'arrêterai. Le soleil est beau, n'est-ce -pas? je me suis fait rouler dehors pour jeter un dernier coup d'oeil sur -les choses, vous pouvez me parler, cela ne me fatigue point. Vous faites -bien de venir regarder un homme qui va mourir. Il est bon que ce -moment-là ait des témoins. On a des manies; j'aurais voulu aller jusqu'à -l'aube. Mais je sais que j'en ai à peine pour trois heures. Il fera -nuit. Au fait, qu'importe! Finir est une affaire simple. On n'a pas -besoin du matin pour cela. Soit. Je mourrai à la belle étoile. - -Le vieillard se tourna vers le pâtre. - ---Toi, va te coucher. Tu as veillé l'autre nuit. Tu es fatigué. - -L'enfant rentra dans la cabane. - -Le vieillard le suivit des yeux et ajouta comme se parlant à lui-même: - ---Pendant qu'il dormira, je mourrai. Les deux sommeils peuvent faire bon -voisinage. - -L'évêque n'était pas ému comme il semble qu'il aurait pu l'être. Il ne -croyait pas sentir Dieu dans cette façon de mourir. Disons tout, car les -petites contradictions des grands coeurs veulent être indiquées comme le -reste, lui qui, dans l'occasion, riait si volontiers de Sa Grandeur, il -était quelque peu choqué de ne pas être appelé monseigneur, et il était -presque tenté de répliquer: citoyen. Il lui vint une velléité de -familiarité bourrue, assez ordinaire aux médecins et aux prêtres, mais -qui ne lui était pas habituelle, à lui. Cet homme, après tout, ce -conventionnel, ce représentant du peuple, avait été un puissant de la -terre; pour la première fois de sa vie peut-être, l'évêque se sentit en -humeur de sévérité. - -Le conventionnel cependant le considérait avec une cordialité modeste, -où l'on eût pu démêler l'humilité qui sied quand on est si près de sa -mise en poussière. - -L'évêque, de son côté, quoiqu'il se gardât ordinairement de la -curiosité, laquelle, selon lui, était contiguë à l'offense, ne pouvait -s'empêcher d'examiner le conventionnel avec une attention qui, n'ayant -pas sa source dans la sympathie, lui eût été probablement reprochée par -sa conscience vis-à-vis de tout autre homme. Un conventionnel lui -faisait un peu l'effet d'être hors la loi, même hors la loi de charité. - -G., calme, le buste presque droit, la voix vibrante, était un de ces -grands octogénaires qui font l'étonnement du physiologiste. La -révolution a eu beaucoup de ces hommes proportionnés à l'époque. On -sentait dans ce vieillard l'homme à l'épreuve. Si près de sa fin, il -avait conservé tous les gestes de la santé. Il y avait dans son coup -d'oeil clair, dans son accent ferme, dans son robuste mouvement -d'épaules, de quoi déconcerter la mort. Azraël, l'ange mahométan du -sépulcre, eût rebroussé chemin et eût cru se tromper de porte. G. -semblait mourir parce qu'il le voulait bien. Il y avait de la liberté -dans son agonie. Les jambes seulement étaient immobiles. Les ténèbres le -tenaient par là. Les pieds étaient morts et froids, et la tête vivait de -toute la puissance de la vie et paraissait en pleine lumière. G., en ce -grave moment, ressemblait à ce roi du conte oriental, chair par en haut, -marbre par en bas. - -Une pierre était là. L'évêque s'y assit. L'exorde fut _ex abrupto_. - ---Je vous félicite, dit-il du ton dont on réprimande. Vous n'avez -toujours pas voté la mort du roi. - -Le conventionnel ne parut pas remarquer le sous-entendu amer caché dans -ce mot: toujours. Il répondit. Tout sourire avait disparu de sa face. - ---Ne me félicitez pas trop, monsieur; j'ai voté la fin du tyran. - -C'était l'accent austère en présence de l'accent sévère. - ---Que voulez-vous dire? reprit l'évêque. - ---Je veux dire que l'homme a un tyran, l'ignorance. J'ai voté la fin de -ce tyran-là. Ce tyran-là a engendré la royauté qui est l'autorité prise -dans le faux, tandis que la science est l'autorité prise dans le vrai. -L'homme ne doit être gouverné que par la science. - ---Et la conscience, ajouta l'évêque. - ---C'est la même chose. La conscience, c'est la quantité de science innée -que nous avons en nous. - -Monseigneur Bienvenu écoutait, un peu étonné, ce langage très nouveau -pour lui. Le conventionnel poursuivit: - ---Quant à Louis XVI, j'ai dit non. Je ne me crois pas le droit de tuer -un homme; mais je me sens le devoir d'exterminer le mal. J'ai voté la -fin du tyran. C'est-à-dire la fin de la prostitution pour la femme, la -fin de l'esclavage pour l'homme, la fin de la nuit pour l'enfant. En -votant la république, j'ai voté cela. J'ai voté la fraternité, la -concorde, l'aurore! J'ai aidé à la chute des préjugés et des erreurs. -Les écroulements des erreurs et des préjugés font de la lumière. Nous -avons fait tomber le vieux monde, nous autres, et le vieux monde, vase -des misères, en se renversant sur le genre humain, est devenu une urne -de joie. - ---Joie mêlée, dit l'évêque. - ---Vous pourriez dire joie troublée, et aujourd'hui, après ce fatal -retour du passé qu'on nomme 1814, joie disparue. Hélas, l'oeuvre a été -incomplète, j'en conviens; nous avons démoli l'ancien régime dans les -faits, nous n'avons pu entièrement le supprimer dans les idées. Détruire -les abus, cela ne suffit pas; il faut modifier les moeurs. Le moulin n'y -est plus, le vent y est encore. - ---Vous avez démoli. Démolir peut être utile; mais je me défie d'une -démolition compliquée de colère. - ---Le droit a sa colère, monsieur l'évêque, et la colère du droit est un -élément du progrès. N'importe, et quoi qu'on en dise, la révolution -française est le plus puissant pas du genre humain depuis l'avènement du -Christ. Incomplète, soit; mais sublime. Elle a dégagé toutes les -inconnues sociales. Elle a adouci les esprits; elle a calmé, apaisé, -éclairé; elle a fait couler sur la terre des flots de civilisation. Elle -a été bonne. La révolution française, c'est le sacre de l'humanité. - -L'évêque ne put s'empêcher de murmurer: - ---Oui? 93! - -Le conventionnel se dressa sur sa chaise avec une solennité presque -lugubre, et, autant qu'un mourant peut s'écrier, il s'écria: - ---Ah! vous y voilà! 93! J'attendais ce mot-là. Un nuage s'est formé -pendant quinze cents ans. Au bout de quinze siècles, il a crevé. Vous -faites le procès au coup de tonnerre. - -L'évêque sentit, sans se l'avouer peut-être, que quelque chose en lui -était atteint. Pourtant il fit bonne contenance. Il répondit: - ---Le juge parle au nom de la justice; le prêtre parle au nom de la -pitié, qui n'est autre chose qu'une justice plus élevée. Un coup de -tonnerre ne doit pas se tromper. - -Et il ajouta en regardant fixement le conventionnel. - ---Louis XVII? - -Le conventionnel étendit la main et saisit le bras de l'évêque: - ---Louis XVII! Voyons, sur qui pleurez-vous? Est-ce sur l'enfant -innocent? alors, soit. Je pleure avec vous. Est-ce sur l'enfant royal? -je demande à réfléchir. Pour moi, le frère de Cartouche, enfant -innocent, pendu sous les aisselles en place de Grève jusqu'à ce que mort -s'ensuive, pour le seul crime d'avoir été le frère de Cartouche, n'est -pas moins douloureux que le petit-fils de Louis XV, enfant innocent, -martyrisé dans la tour du Temple pour le seul crime d'avoir été le -petit-fils de Louis XV. - ---Monsieur, dit l'évêque, je n'aime pas ces rapprochements de noms. - ---Cartouche? Louis XV? pour lequel des deux réclamez-vous? - -Il y eut un moment de silence. L'évêque regrettait presque d'être venu, -et pourtant il se sentait vaguement et étrangement ébranlé. - -Le conventionnel reprit: - ---Ah! monsieur le prêtre, vous n'aimez pas les crudités du vrai. Christ -les aimait, lui. Il prenait une verge et il époussetait le temple. Son -fouet plein d'éclairs était un rude diseur de vérités. Quand il -s'écriait: _Sinite parvulos_..., il ne distinguait pas entre les petits -enfants. Il ne se fût pas gêné de rapprocher le dauphin de Barabbas du -dauphin d'Hérode. Monsieur, l'innocence est sa couronne à elle-même. -L'innocence n'a que faire d'être altesse. Elle est aussi auguste -déguenillée que fleurdelysée. - ---C'est vrai, dit l'évêque à voix basse. - ---J'insiste, continua le conventionnel G. Vous m'avez nommé Louis XVII. -Entendons-nous. Pleurons-nous sur tous les innocents, sur tous les -martyrs, sur tous les enfants, sur ceux d'en bas comme sur ceux d'en -haut? J'en suis. Mais alors, je vous l'ai dit, il faut remonter plus -haut que 93, et c'est avant Louis XVII qu'il faut commencer nos larmes. -Je pleurerai sur les enfants des rois avec vous, pourvu que vous -pleuriez avec moi sur les petits du peuple. - ---Je pleure sur tous, dit l'évêque. - ---Également! s'écria G., et si la balance doit pencher, que ce soit du -côté du peuple. Il y a plus longtemps qu'il souffre. - -Il y eut encore un silence. Ce fut le conventionnel qui le rompit. Il se -souleva sur un coude, prit entre son pouce et son index replié un peu de -sa joue, comme on fait machinalement lorsqu'on interroge et qu'on juge, -et interpella l'évêque avec un regard plein de toutes les énergies de -l'agonie. Ce fut presque une explosion. - ---Oui, monsieur, il y a longtemps que le peuple souffre. Et puis, tenez, -ce n'est pas tout cela, que venez-vous me questionner et me parler de -Louis XVII? Je ne vous connais pas, moi. Depuis que je suis dans ce -pays, j'ai vécu dans cet enclos, seul, ne mettant pas les pieds dehors, -ne vient personne que cet enfant qui m'aide. Votre nom est, il est vrai, -arrivé confusément jusqu'à moi, et, je dois le dire, pas très mal -prononcé; mais cela ne signifie rien; les gens habiles ont tant de -manières d'en faire accroire à ce brave bonhomme de peuple. À propos, je -n'ai pas entendu le bruit de votre voiture, vous l'aurez sans doute -laissée derrière le taillis, là-bas, à l'embranchement de la route. Je -ne vous connais pas, vous dis-je. Vous m'avez dit que vous étiez -l'évêque, mais cela ne me renseigne point sur votre personne morale. En -somme, je vous répète ma question. Qui êtes-vous? Vous êtes un évêque, -c'est-à-dire un prince de l'église, un de ces hommes dorés, armoriés, -rentés, qui ont de grosses prébendes--l'évêché de Digne, quinze mille -francs de fixe, dix mille francs de casuel, total, vingt-cinq mille -francs--, qui ont des cuisines, qui ont des livrées, qui font bonne -chère, qui mangent des poules d'eau le vendredi, qui se pavanent, -laquais devant, laquais derrière, en berline de gala, et qui ont des -palais, et qui roulent carrosse au nom de Jésus-Christ qui allait pieds -nus! Vous êtes un prélat; rentes, palais, chevaux, valets, bonne table, -toutes les sensualités de la vie, vous avez cela comme les autres, et -comme les autres vous en jouissez, c'est bien, mais cela en dit trop ou -pas assez; cela ne m'éclaire pas sur votre valeur intrinsèque et -essentielle, à vous qui venez avec la prétention probable de m'apporter -de la sagesse. À qui est-ce que je parle? Qui êtes-vous? - -L'évêque baissa la tête et répondit: - ---_Vermis sum_. - ---Un ver de terre en carrosse! grommela le conventionnel. - -C'était le tour du conventionnel d'être hautain, et de l'évêque d'être -humble. - -L'évêque reprit avec douceur. - ---Monsieur, soit. Mais expliquez-moi en quoi mon carrosse, qui est là à -deux pas derrière les arbres, en quoi ma bonne table et les poules d'eau -que je mange le vendredi, en quoi mes vingt-cinq mille livres de rentes, -en quoi mon palais et mes laquais prouvent que la pitié n'est pas une -vertu, que la clémence n'est pas un devoir, et que 93 n'a pas été -inexorable. - -Le conventionnel passa la main sur son front comme pour en écarter un -nuage. - ---Avant de vous répondre, dit-il, je vous prie de me pardonner. Je viens -d'avoir un tort, monsieur. Vous êtes chez moi, vous êtes mon hôte. Je -vous dois courtoisie. Vous discutez mes idées, il sied que je me borne à -combattre vos raisonnements. Vos richesses et vos jouissances sont des -avantages que j'ai contre vous dans le débat, mais il est de bon goût de -ne pas m'en servir. Je vous promets de ne plus en user. - ---Je vous remercie, dit l'évêque. - -G. reprit: - ---Revenons à l'explication que vous me demandiez. Où en étions-nous? Que -me disiez-vous? que 93 a été inexorable? - ---Inexorable, oui, dit l'évêque. Que pensez-vous de Marat battant des -mains à la guillotine? - ---Que pensez-vous de Bossuet chantant le _Te Deum_ sur les dragonnades? - -La réponse était dure, mais elle allait au but avec la rigidité d'une -pointe d'acier. L'évêque en tressaillit; il ne lui vint aucune riposte, -mais il était froissé de cette façon de nommer Bossuet. Les meilleurs -esprits ont leurs fétiches, et parfois se sentent vaguement meurtris des -manques de respect de la logique. - -Le conventionnel commençait à haleter; l'asthme de l'agonie, qui se mêle -aux derniers souffles, lui entrecoupait la voix; cependant il avait -encore une parfaite lucidité d'âme dans les yeux. Il continua: - ---Disons encore quelques mots çà et là, je veux bien. En dehors de la -révolution qui, prise dans son ensemble, est une immense affirmation -humaine, 93, hélas! est une réplique. Vous le trouvez inexorable, mais -toute la monarchie, monsieur? Carrier est un bandit; mais quel nom -donnez-vous à Montrevel? Fouquier-Tinville est un gueux, mais quel est -votre avis sur Lamoignon-Bâville? Maillard est affreux, mais -Saulx-Tavannes, s'il vous plaît? Le père Duchêne est féroce, mais quelle -épithète m'accorderez-vous pour le père Letellier? Jourdan-Coupe-Tête -est un monstre, mais moindre que M. le marquis de Louvois. Monsieur, -monsieur, je plains Marie-Antoinette, archiduchesse et reine, mais je -plains aussi cette pauvre femme huguenote qui, en 1685, sous Louis le -Grand, monsieur, allaitant son enfant, fut liée, nue jusqu'à la -ceinture, à un poteau, l'enfant tenu à distance; le sein se gonflait de -lait et le coeur d'angoisse. Le petit, affamé et pâle, voyait ce sein, -agonisait et criait, et le bourreau disait à la femme, mère et nourrice: -«Abjure!» lui donnant à choisir entre la mort de son enfant et la mort -de sa conscience. Que dites-vous de ce supplice de Tantale accommodé à -une mère? Monsieur, retenez bien ceci: la révolution française a eu ses -raisons. Sa colère sera absoute par l'avenir. Son résultat, c'est le -monde meilleur. De ses coups les plus terribles, il sort une caresse -pour le genre humain. J'abrège. Je m'arrête, j'ai trop beau jeu. -D'ailleurs je me meurs. - -Et, cessant de regarder l'évêque, le conventionnel acheva sa pensée en -ces quelques mots tranquilles: - ---Oui, les brutalités du progrès s'appellent révolutions. Quand elles -sont finies, on reconnaît ceci: que le genre humain a été rudoyé, mais -qu'il a marché. - -Le conventionnel ne se doutait pas qu'il venait d'emporter -successivement l'un après l'autre tous les retranchements intérieurs de -l'évêque. Il en restait un pourtant, et de ce retranchement, suprême -ressource de la résistance de monseigneur Bienvenu, sortit cette parole -où reparut presque toute la rudesse du commencement: - ---Le progrès doit croire en Dieu. Le bien ne peut pas avoir de serviteur -impie. C'est un mauvais conducteur du genre humain que celui qui est -athée. - -Le vieux représentant du peuple ne répondit pas. Il eut un tremblement. -Il regarda le ciel, et une larme germa lentement dans ce regard. Quand -la paupière fut pleine, la larme coula le long de sa joue livide, et il -dit presque en bégayant, bas et se parlant à lui-même, l'oeil perdu dans -les profondeurs: - ---O toi! ô idéal! toi seul existes! - -L'évêque eut une sorte d'inexprimable commotion. Après un silence, le -vieillard leva un doigt vers le ciel, et dit: - ---L'infini est. Il est là. Si l'infini n'avait pas de moi, le moi serait -sa borne; il ne serait pas infini; en d'autres termes, il ne serait pas. -Or il est. Donc il a un moi. Ce moi de l'infini, c'est Dieu. - -Le mourant avait prononcé ces dernières paroles d'une voix haute et avec -le frémissement de l'extase, comme s'il voyait quelqu'un. Quand il eut -parlé, ses yeux se fermèrent. L'effort l'avait épuisé. Il était évident -qu'il venait de vivre en une minute les quelques heures qui lui -restaient. Ce qu'il venait de dire l'avait approché de celui qui est -dans la mort. L'instant suprême arrivait. - -L'évêque le comprit, le moment pressait, c'était comme prêtre qu'il -était venu; de l'extrême froideur, il était passé par degrés à l'émotion -extrême; il regarda ces yeux fermés, il prit cette vieille main ridée et -glacée, et se pencha vers le moribond: - ---Cette heure est celle de Dieu. Ne trouvez-vous pas qu'il serait -regrettable que nous nous fussions rencontrés en vain? - -Le conventionnel rouvrit les yeux. Une gravité où il y avait de l'ombre -s'empreignit sur son visage. - ---Monsieur l'évêque, dit-il, avec une lenteur qui venait peut-être plus -encore de la dignité de l'âme que de la défaillance des forces, j'ai -passé ma vie dans la méditation, l'étude et la contemplation. J'avais -soixante ans quand mon pays m'a appelé, et m'a ordonné de me mêler de -ses affaires. J'ai obéi. Il y avait des abus, je les ai combattus; il y -avait des tyrannies, je les ai détruites; il y avait des droits et des -principes, je les ai proclamés et confessés. Le territoire était envahi, -je l'ai défendu; la France était menacée, j'ai offert ma poitrine. Je -n'étais pas riche; je suis pauvre. J'ai été l'un des maîtres de l'État, -les caves du Trésor étaient encombrées d'espèces au point qu'on était -forcé d'étançonner les murs, prêts à se fendre sous le poids de l'or et -de l'argent, je dînais rue de l'Arbre-Sec à vingt-deux sous par tête. -J'ai secouru les opprimés, j'ai soulagé les souffrants. J'ai déchiré la -nappe de l'autel, c'est vrai; mais c'était pour panser les blessures de -la patrie. J'ai toujours soutenu la marche en avant du genre humain vers -la lumière, et j'ai résisté quelquefois au progrès sans pitié. J'ai, -dans l'occasion, protégé mes propres adversaires, vous autres. Et il y a -à Peteghem en Flandre, à l'endroit même où les rois mérovingiens avaient -leur palais d'été, un couvent d'urbanistes, l'abbaye de Sainte-Claire en -Beaulieu, que j'ai sauvé en 1793. J'ai fait mon devoir selon mes forces, -et le bien que j'ai pu. Après quoi j'ai été chassé, traqué, poursuivi, -persécuté, noirci, raillé, conspué, maudit, proscrit. Depuis bien des -années déjà, avec mes cheveux blancs, je sens que beaucoup de gens se -croient sur moi le droit de mépris, j'ai pour la pauvre foule ignorante -visage de damné, et j'accepte, ne haïssant personne, l'isolement de la -haine. Maintenant, j'ai quatre-vingt-six ans; je vais mourir. Qu'est-ce -que vous venez me demander? - ---Votre bénédiction, dit l'évêque. - -Et il s'agenouilla. - -Quand l'évêque releva la tête, la face du conventionnel était devenue -auguste. Il venait d'expirer. - -L'évêque rentra chez lui profondément absorbé dans on ne sait quelles -pensées. Il passa toute la nuit en prière. Le lendemain, quelques braves -curieux essayèrent de lui parler du conventionnel G.; il se borna à -montrer le ciel. À partir de ce moment, il redoubla de tendresse et de -fraternité pour les petits et les souffrants. - -Toute allusion à ce «vieux scélérat de G.» le faisait tomber dans une -préoccupation singulière. Personne ne pourrait dire que le passage de -cet esprit devant le sien et le reflet de cette grande conscience sur la -sienne ne fût pas pour quelque chose dans son approche de la perfection. - -Cette «visite pastorale» fut naturellement une occasion de bourdonnement -pour les petites coteries locales: - ---Était-ce la place d'un évêque que le chevet d'un tel mourant? Il n'y -avait évidemment pas de conversion à attendre. Tous ces révolutionnaires -sont relaps. Alors pourquoi y aller? Qu'a-t-il été regarder là? Il -fallait donc qu'il fût bien curieux d'un emportement d'âme par le -diable. - -Un jour, une douairière, de la variété impertinente qui se croit -spirituelle, lui adressa cette saillie: - ---Monseigneur, on demande quand Votre Grandeur aura le bonnet rouge. - ---Oh! oh! voilà une grosse couleur, répondit l'évêque. Heureusement que -ceux qui la méprisent dans un bonnet la vénèrent dans un chapeau. - - - - -Chapitre XI - -Une restriction - - -On risquerait fort de se tromper si l'on concluait de là que monseigneur -Bienvenu fût «un évêque philosophe» ou «un curé patriote». Sa rencontre, -ce qu'on pourrait presque appeler sa conjonction avec le conventionnel -G., lui laissa une sorte d'étonnement qui le rendit plus doux encore. -Voilà tout. - -Quoique monseigneur Bienvenu n'ait été rien moins qu'un homme politique, -c'est peut-être ici le lieu d'indiquer, très brièvement, quelle fut son -attitude dans les événements d'alors, en supposant que monseigneur -Bienvenu ait jamais songé à avoir une attitude. Remontons donc en -arrière de quelques années. - -Quelque temps après l'élévation de M. Myriel à l'épiscopat, l'empereur -l'avait fait baron de l'empire, en même temps que plusieurs autres -évêques. L'arrestation du pape eut lieu, comme on sait, dans la nuit du -5 au 6 juillet 1809; à cette occasion, M. Myriel fut appelé par Napoléon -au synode des évêques de France et d'Italie convoqué à Paris. Ce synode -se tint à Notre-Dame et s'assembla pour la première fois le 15 juin 1811 -sous la présidence de M. le cardinal Fesch. M. Myriel fut du nombre des -quatre-vingt-quinze évêques qui s'y rendirent. Mais il n'assista qu'à -une séance et à trois ou quatre conférences particulières. Évêque d'un -diocèse montagnard, vivant si près de la nature, dans la rusticité et le -dénuement, il paraît qu'il apportait parmi ces personnages éminents des -idées qui changeaient la température de l'assemblée. Il revint bien vite -à Digne. On le questionna sur ce prompt retour, il répondit: - ---Je les gênais. L'air du dehors leur venait par moi. Je leur faisais -l'effet d'une porte ouverte. - -Une autre fois il dit: - ---Que voulez-vous? ces messeigneurs-là sont des princes. Moi, je ne suis -qu'un pauvre évêque paysan. - -Le fait est qu'il avait déplu. Entre autres choses étranges, il lui -serait échappé de dire, un soir qu'il se trouvait chez un de ses -collègues les plus qualifiés: - ---Les belles pendules! les beaux tapis! les belles livrées! Ce doit être -bien importun! Oh! que je ne voudrais pas avoir tout ce superflu-là à me -crier sans cesse aux oreilles: Il y a des gens qui ont faim! il y a des -gens qui ont froid! il y a des pauvres! il y a des pauvres! - -Disons-le en passant, ce ne serait pas une haine intelligente que la -haine du luxe. Cette haine impliquerait la haine des arts. Cependant, -chez les gens d'église, en dehors de la représentation et des -cérémonies, le luxe est un tort. Il semble révéler des habitudes peu -réellement charitables. Un prêtre opulent est un contre-sens. Le prêtre -doit se tenir près des pauvres. Or peut-on toucher sans cesse, et nuit -et jour, à toutes les détresses, à toutes les infortunes, à toutes les -indigences, sans avoir soi-même sur soi un peu de cette sainte misère, -comme la poussière du travail? Se figure-t-on un homme qui est près d'un -brasier, et qui n'a pas chaud? Se figure-t-on un ouvrier qui travaille -sans cesse à une fournaise, et qui n'a ni un cheveu brûlé, ni un ongle -noirci, ni une goutte de sueur, ni un grain de cendre au visage? La -première preuve de la charité chez le prêtre, chez l'évêque surtout, -c'est la pauvreté. C'était là sans doute ce que pensait M. l'évêque de -Digne. - -Il ne faudrait pas croire d'ailleurs qu'il partageait sur certains -points délicats ce que nous appellerions «les idées du siècle». Il se -mêlait peu aux querelles théologiques du moment et se taisait sur les -questions où sont compromis l'Église et l'État; mais si on l'eût -beaucoup pressé, il paraît qu'on l'eût trouvé plutôt ultramontain que -gallican. Comme nous faisons un portrait et que nous ne voulons rien -cacher, nous sommes forcé d'ajouter qu'il fut glacial pour Napoléon -déclinant. À partir de 1813, il adhéra ou il applaudit à toutes les -manifestations hostiles. Il refusa de le voir à son passage au retour de -l'île d'Elbe, et s'abstint d'ordonner dans son diocèse les prières -publiques pour l'empereur pendant les Cent-Jours. - -Outre sa soeur, mademoiselle Baptistine, il avait deux frères: l'un -général, l'autre préfet. Il écrivait assez souvent à tous les deux. Il -tint quelque temps rigueur au premier, parce qu'ayant un commandement en -Provence, à l'époque du débarquement de Cannes, le général s'était mis à -la tête de douze cents hommes et avait poursuivi l'empereur comme -quelqu'un qui veut le laisser échapper. Sa correspondance resta plus -affectueuse pour l'autre frère, l'ancien préfet, brave et digne homme -qui vivait retiré à Paris, rue Cassette. - -Monseigneur Bienvenu eut donc, aussi lui, son heure d'esprit de parti, -son heure d'amertume, son nuage. L'ombre des passions du moment traversa -ce doux et grand esprit occupé des choses éternelles. Certes, un pareil -homme eût mérité de n'avoir pas d'opinions politiques. Qu'on ne se -méprenne pas sur notre pensée, nous ne confondons point ce qu'on appelle -«opinions politiques» avec la grande aspiration au progrès, avec la -sublime foi patriotique, démocratique et humaine, qui, de nos jours, -doit être le fond même de toute intelligence généreuse. Sans approfondir -des questions qui ne touchent qu'indirectement au sujet de ce livre, -nous disons simplement ceci: Il eût été beau que monseigneur Bienvenu -n'eût pas été royaliste et que son regard ne se fût pas détourné un seul -instant de cette contemplation sereine où l'on voit rayonner -distinctement, au-dessus du va-et-vient orageux des choses humaines, ces -trois pures lumières, la Vérité, la Justice, la Charité. - -Tout en convenant que ce n'était point pour une fonction politique que -Dieu avait créé monseigneur Bienvenu, nous eussions compris et admiré la -protestation au nom du droit et de la liberté, l'opposition fière, la -résistance périlleuse et juste à Napoléon tout-puissant. Mais ce qui -nous plaît vis-à-vis de ceux qui montent nous plaît moins vis-à-vis de -ceux qui tombent. Nous n'aimons le combat que tant qu'il y a danger; et, -dans tous les cas, les combattants de la première heure ont seuls le -droit d'être les exterminateurs de la dernière. Qui n'a pas été -accusateur opiniâtre pendant la prospérité doit se taire devant -l'écroulement. Le dénonciateur du succès est le seul légitime justicier -de la chute. Quant à nous, lorsque la Providence s'en mêle et frappe, -nous la laissons faire. 1812 commence à nous désarmer. En 1813, la lâche -rupture de silence de ce corps législatif taciturne enhardi par les -catastrophes n'avait que de quoi indigner, et c'était un tort -d'applaudir; en 1814, devant ces maréchaux trahissant, devant ce sénat -passant d'une fange à l'autre, insultant après avoir divinisé, devant -cette idolâtrie lâchant pied et crachant sur l'idole, c'était un devoir -de détourner la tête; en 1815, comme les suprêmes désastres étaient dans -l'air, comme la France avait le frisson de leur approche sinistre, comme -on pouvait vaguement distinguer Waterloo ouvert devant Napoléon, la -douloureuse acclamation de l'armée et du peuple au condamné du destin -n'avait rien de risible, et, toute réserve faite sur le despote, un -coeur comme l'évêque de Digne n'eût peut-être pas dû méconnaître ce -qu'avait d'auguste et de touchant, au bord de l'abîme, l'étroit -embrassement d'une grande nation et d'un grand homme. - -À cela près, il était et il fut, en toute chose, juste, vrai, équitable, -intelligent, humble et digne; bienfaisant, et bienveillant, ce qui est -une autre bienfaisance. C'était un prêtre, un sage, et un homme. Même, -il faut le dire, dans cette opinion politique que nous venons de lui -reprocher et que nous sommes disposé à juger presque sévèrement, il -était tolérant et facile, peut-être plus que nous qui parlons ici.--Le -portier de la maison de ville avait été placé là par l'empereur. C'était -un vieux sous-officier de la vieille garde, légionnaire d'Austerlitz, -bonapartiste comme l'aigle. Il échappait dans l'occasion à ce pauvre -diable de ces paroles peu réfléchies que la loi d'alors qualifiait -_propos séditieux_. Depuis que le profil impérial avait disparu de la -légion d'honneur, il ne s'habillait jamais _dans l'ordonnance_, comme il -disait, afin de ne pas être forcé de porter sa croix. Il avait ôté -lui-même dévotement l'effigie impériale de la croix que Napoléon lui -avait donnée, cela faisait un trou, et il n'avait rien voulu mettre à la -place. «Plutôt mourir, disait-il, que de porter sur mon coeur les trois -crapauds!» Il raillait volontiers tout haut Louis XVIII. «Vieux goutteux -à guêtres d'anglais!» disait-il, «qu'il s'en aille en Prusse avec son -salsifis!» Heureux de réunir dans la même imprécation les deux choses -qu'il détestait le plus, la Prusse et l'Angleterre. Il en fit tant qu'il -perdit sa place. Le voilà sans pain sur le pavé avec femme et enfants. -L'évêque le fit venir, le gronda doucement, et le nomma suisse de la -cathédrale. - -M. Myriel était dans le diocèse le vrai pasteur, l'ami de tous. En neuf -ans, à force de saintes actions et de douces manières, monseigneur -Bienvenu avait rempli la ville de Digne d'une sorte de vénération tendre -et filiale. Sa conduite même envers Napoléon avait été acceptée et comme -tacitement pardonnée par le peuple, bon troupeau faible, qui adorait son -empereur, mais qui aimait son évêque. - - - - -Chapitre XII - -Solitude de monseigneur Bienvenu - - -Il y a presque toujours autour d'un évêque une escouade de petits abbés -comme autour d'un général une volée de jeunes officiers. C'est là ce que -ce charmant saint François de Sales appelle quelque part «les prêtres -blancs-becs». Toute carrière a ses aspirants qui font cortège aux -arrivés. Pas une puissance qui n'ait son entourage; pas une fortune qui -n'ait sa cour. Les chercheurs d'avenir tourbillonnent autour du présent -splendide. Toute métropole a son état-major. Tout évêque un peu influent -a près de lui sa patrouille de chérubins séminaristes, qui fait la ronde -et maintient le bon ordre dans le palais épiscopal, et qui monte la -garde autour du sourire de monseigneur. Agréer à un évêque, c'est le -pied à l'étrier pour un sous-diacre. Il faut bien faire son chemin; -l'apostolat ne dédaigne pas le canonicat. - -De même qu'il y a ailleurs les gros bonnets, il y a dans l'église les -grosses mitres. Ce sont les évêques bien en cour, riches, rentés, -habiles, acceptés du monde, sachant prier, sans doute, mais sachant -aussi solliciter, peu scrupuleux de faire faire antichambre en leur -personne à tout un diocèse, traits d'union entre la sacristie et la -diplomatie, plutôt abbés que prêtres, plutôt prélats qu'évêques. Heureux -qui les approche! Gens en crédit qu'ils sont, ils font pleuvoir autour -d'eux, sur les empressés et les favorisés, et sur toute cette jeunesse -qui sait plaire, les grasses paroisses, les prébendes, les -archidiaconats, les aumôneries et les fonctions cathédrales, en -attendant les dignités épiscopales. En avançant eux-mêmes, ils font -progresser leurs satellites; c'est tout un système solaire en marche. -Leur rayonnement empourpre leur suite. Leur prospérité s'émiette sur la -cantonade en bonnes petites promotions. Plus grand diocèse au patron, -plus grosse cure au favori. Et puis Rome est là. Un évêque qui sait -devenir archevêque, un archevêque qui sait devenir cardinal, vous emmène -comme conclaviste, vous entrez dans la rote, vous avez le pallium, vous -voilà auditeur, vous voilà camérier, vous voilà monsignor, et de la -Grandeur à Imminence il n'y a qu'un pas, et entre Imminence et la -Sainteté il n'y a que la fumée d'un scrutin. Toute calotte peut rêver la -tiare. Le prêtre est de nos jours le seul homme qui puisse régulièrement -devenir roi; et quel roi! le roi suprême. Aussi quelle pépinière -d'aspirations qu'un séminaire! Que d'enfants de choeur rougissants, que -de jeunes abbés ont sur la tête le pot au lait de Perrette! Comme -l'ambition s'intitule aisément vocation, qui sait? de bonne foi -peut-être et se trompant elle-même, béate qu'elle est! - -Monseigneur Bienvenu, humble, pauvre, particulier, n'était pas compté -parmi les grosses mitres. Cela était visible à l'absence complète de -jeunes prêtres autour de lui. On a vu qu'à Paris «il n'avait pas pris». -Pas un avenir ne songeait à se greffer sur ce vieillard solitaire. Pas -une ambition en herbe ne faisait la folie de verdir à son ombre. Ses -chanoines et ses grands vicaires étaient de bons vieux hommes, un peu -peuple comme lui, murés comme lui dans ce diocèse sans issue sur le -cardinafat, et qui ressemblaient à leur évêque, avec cette différence -qu'eux étaient finis, et que lui était achevé. - -On sentait si bien l'impossibilité de croître près de monseigneur -Bienvenu qu'à peine sortis du séminaire, les jeunes gens ordonnés par -lui se faisaient recommander aux archevêques d'Aix ou d'Auch, et s'en -allaient bien vite. Car enfin, nous le répétons, on veut être poussé. Un -saint qui vit dans un excès d'abnégation est un voisinage dangereux; il -pourrait bien vous communiquer par contagion une pauvreté incurable, -l'ankylose des articulations utiles à l'avancement, et, en somme, plus -de renoncement que vous n'en voulez; et l'on fuit cette vertu galeuse. -De là l'isolement de monseigneur Bienvenu. Nous vivons dans une société -sombre. Réussir, voilà l'enseignement qui tombe goutte à goutte de la -corruption en surplomb. - -Soit dit en passant, c'est une chose assez hideuse que le succès. Sa -fausse ressemblance avec le mérite trompe les hommes. Pour la foule, la -réussite a presque le même profil que la suprématie. Le succès, ce -ménechme du talent, a une dupe: l'histoire. Juvénal et Tacite seuls en -bougonnent. De nos jours, une philosophie à peu près officielle est -entrée en domesticité chez lui, porte la livrée du succès, et fait le -service de son antichambre. Réussissez: théorie. Prospérité suppose -Capacité. Gagnez à la loterie, vous voilà un habile homme. Qui triomphe -est vénéré. Naissez coiffé, tout est là. Ayez de la chance, vous aurez -le reste; soyez heureux, on vous croira grand. En dehors des cinq ou six -exceptions immenses qui font l'éclat d'un siècle, l'admiration -contemporaine n'est guère que myopie. Dorure est or. Être le premier -venu, cela ne gâte rien, pourvu qu'on soit le parvenu. Le vulgaire est -un vieux Narcisse qui s'adore lui-même et qui applaudit le vulgaire. -Cette faculté énorme par laquelle on est Moïse, Eschyle, Dante, -Michel-Ange ou Napoléon, la multitude la décerne d'emblée et par -acclamation à quiconque atteint son but dans quoi que ce soit. Qu'un -notaire se transfigure en député, qu'un faux Corneille fasse _Tiridate_, -qu'un eunuque parvienne à posséder un harem, qu'un Prud'homme militaire -gagne par accident la bataille décisive d'une époque, qu'un apothicaire -invente les semelles de carton pour l'armée de Sambre-et-Meuse et se -construise, avec ce carton vendu pour du cuir, quatre cent mille livres -de rente, qu'un porte-balle épouse l'usure et la fasse accoucher de sept -ou huit millions dont il est le père et dont elle est la mère, qu'un -prédicateur devienne évêque par le nasillement, qu'un intendant de bonne -maison soit si riche en sortant de service qu'on le fasse ministre des -finances, les hommes appellent cela Génie, de même qu'ils appellent -Beauté la figure de Mousqueton et Majesté l'encolure de Claude. Ils -confondent avec les constellations de l'abîme les étoiles que font dans -la vase molle du bourbier les pattes des canards. - - - - -Chapitre XIII - -Ce qu'il croyait - - -Au point de vue de l'orthodoxie, nous n'avons point à sonder M. l'évêque -de Digne. Devant une telle âme, nous ne nous sentons en humeur que de -respect. La conscience du juste doit être crue sur parole. D'ailleurs, -de certaines natures étant données, nous admettons le développement -possible de toutes les beautés de la vertu humaine dans une croyance -différente de la nôtre. - -Que pensait-il de ce dogme-ci ou de ce mystère-là? Ces secrets du for -intérieur ne sont connus que de la tombe où les âmes entrent nues. Ce -dont nous sommes certain, c'est que jamais les difficultés de foi ne se -résolvaient pour lui en hypocrisie. Aucune pourriture n'est possible au -diamant. Il croyait le plus qu'il pouvait. _Credo in Patrem_, -s'écriait-il souvent. Puisant d'ailleurs dans les bonnes oeuvres cette -quantité de satisfaction qui suffit à la conscience, et qui vous dit -tout bas: «Tu es avec Dieu.» - -Ce que nous croyons devoir noter, c'est que, en dehors, pour ainsi dire, -et au-delà de sa foi, l'évêque avait un excès d'amour. C'est par là, -_quia multum amavit_, qu'il était jugé vulnérable par les «hommes -sérieux», les «personnes graves» et les «gens raisonnables»; locutions -favorites de notre triste monde où l'égoïsme reçoit le mot d'ordre du -pédantisme. Qu'était-ce que cet excès d'amour? C'était une bienveillance -sereine, débordant les hommes, comme nous l'avons indiqué déjà, et, dans -l'occasion, s'étendant jusqu'aux choses. Il vivait sans dédain. Il était -indulgent pour la création de Dieu. Tout homme, même le meilleur, a en -lui une dureté irréfléchie qu'il tient en réserve pour l'animal. -L'évêque de Digne n'avait point cette dureté-là, particulière à beaucoup -de prêtres pourtant. Il n'allait pas jusqu'au bramine, mais il semblait -avoir médité cette parole de l'Ecclésiaste: «Sait-on où va l'âme des -animaux?» Les laideurs de l'aspect, les difformités de l'instinct, ne le -troublaient pas et ne l'indignaient pas. Il en était ému, presque -attendri. Il semblait que, pensif, il en allât chercher, au-delà de la -vie apparente, la cause, l'explication ou l'excuse. Il semblait par -moments demander à Dieu des commutations. Il examinait sans colère, et -avec l'oeil du linguiste qui déchiffre un palimpseste, la quantité de -chaos qui est encore dans la nature. Cette rêverie faisait parfois -sortir de lui des mots étranges. Un matin, il était dans son jardin; il -se croyait seul, mais sa soeur marchait derrière lui sans qu'il la vît; -tout à coup, il s'arrêta, et il regarda quelque chose à terre; c'était -une grosse araignée, noire, velue, horrible. Sa soeur l'entendit qui -disait: - ---Pauvre bête! ce n'est pas sa faute. - -Pourquoi ne pas dire ces enfantillages presque divins de la bonté? -Puérilités, soit; mais ces puérilités sublimes ont été celles de saint -François d'Assise et de Marc-Aurèle. Un jour il se donna une entorse -pour n'avoir pas voulu écraser une fourmi. - -Ainsi vivait cet homme juste. Quelquefois, il s'endormait dans son -jardin, et alors il n'était rien de plus vénérable. - -Monseigneur Bienvenu avait été jadis, à en croire les récits sur sa -jeunesse et même sur sa virilité, un homme passionné, peut-être violent. -Sa mansuétude universelle était moins un instinct de nature que le -résultat d'une grande conviction filtrée dans son coeur à travers la vie -et lentement tombée en lui, pensée à pensée; car, dans un caractère -comme dans un rocher, il peut y avoir des trous de gouttes d'eau. Ces -creusements-là sont ineffaçables; ces formations-là sont -indestructibles. - -En 1815, nous croyons l'avoir dit, il atteignit soixante-quinze ans, -mais il n'en paraissait pas avoir plus de soixante. Il n'était pas -grand; il avait quelque embonpoint, et, pour le combattre, il faisait -volontiers de longues marches à pied, il avait le pas ferme et n'était -que fort peu courbé, détail d'où nous ne prétendons rien conclure; -Grégoire XVI, à quatre-vingts ans, se tenait droit et souriant, ce qui -ne l'empêchait pas d'être un mauvais évêque. Monseigneur Bienvenu avait -ce que le peuple appelle «une belle tête», mais si aimable qu'on -oubliait qu'elle était belle. - -Quand il causait avec cette santé enfantine qui était une de ses grâces, -et dont nous avons déjà parlé, on se sentait à l'aise près de lui, il -semblait que de toute sa personne il sortît de la joie. Son teint coloré -et frais, toutes ses dents bien blanches qu'il avait conservées et que -son rire faisait voir, lui donnaient cet air ouvert et facile qui fait -dire d'un homme: «C'est un bon enfant», et d'un vieillard: «C'est un -bonhomme». C'était, on s'en souvient, l'effet qu'il avait fait à -Napoléon. Au premier abord, et pour qui le voyait pour la première fois, -ce n'était guère qu'un bonhomme en effet. Mais si l'on restait quelques -heures près de lui, et pour peu qu'on le vît pensif, le bonhomme se -transfigurait peu à peu et prenait je ne sais quoi d'imposant; son front -large et sérieux, auguste par les cheveux blancs, devenait auguste aussi -par la méditation; la majesté se dégageait de cette bonté, sans que la -bonté cessât de rayonner; on éprouvait quelque chose de l'émotion qu'on -aurait si l'on voyait un ange souriant ouvrir lentement ses ailes sans -cesser de sourire. Le respect, un respect inexprimable, vous pénétrait -par degrés et vous montait au coeur, et l'on sentait qu'on avait devant -soi une de ces âmes fortes, éprouvées et indulgentes, où la pensée est -si grande qu'elle ne peut plus être que douce. - -Comme on l'a vu, la prière, la célébration des offices religieux, -l'aumône, la consolation aux affligés, la culture d'un coin de terre, la -fraternité, la frugalité, l'hospitalité, le renoncement, la confiance, -l'étude, le travail remplissaient chacune des journées de sa vie. -_Remplissaient_ est bien le mot, et certes cette journée de l'évêque -était bien pleine jusqu'aux bords de bonnes pensées, de bonnes paroles -et de bonnes actions. Cependant elle n'était pas complète si le temps -froid ou pluvieux l'empêchait d'aller passer, le soir, quand les deux -femmes s'étaient retirées, une heure ou deux dans son jardin avant de -s'endormir. Il semblait que ce fût une sorte de rite pour lui de se -préparer au sommeil par la méditation en présence des grands spectacles -du ciel nocturne. Quelquefois, à une heure même assez avancée de la -nuit, si les deux vieilles filles ne dormaient pas, elles l'entendaient -marcher lentement dans les allées. Il était là, seul avec lui-même, -recueilli, paisible, adorant, comparant la sérénité de son coeur à la -sérénité de l'éther, ému dans les ténèbres par les splendeurs visibles -des constellations et les splendeurs invisibles de Dieu, ouvrant son âme -aux pensées qui tombent de l'inconnu. Dans ces moments-là, offrant son -coeur à l'heure où les fleurs nocturnes offrent leur parfum, allumé -comme une lampe au centre de la nuit étoilée, se répandant en extase au -milieu du rayonnement universel de la création, il n'eût pu peut-être -dire lui-même ce qui se passait dans son esprit, il sentait quelque -chose s'envoler hors de lui et quelque chose descendre en lui. -Mystérieux échanges des gouffres de l'âme avec les gouffres de -l'univers! - -Il songeait à la grandeur et à la présence de Dieu; à l'éternité future, -étrange mystère; à l'éternité passée, mystère plus étrange encore; à -tous les infinis qui s'enfonçaient sous ses yeux dans tous les sens; et, -sans chercher à comprendre l'incompréhensible, il le regardait. Il -n'étudiait pas Dieu, il s'en éblouissait. Il considérait ces magnifiques -rencontres des atomes qui donnent des aspects à la matière, révèlent les -forces en les constatant, créent les individualités dans l'unité, les -proportions dans l'étendue, l'innombrable dans l'infini, et par la -lumière produisent la beauté. Ces rencontres se nouent et se dénouent -sans cesse; de là la vie et la mort. Il s'asseyait sur un banc de bois -adossé à une treille décrépite, et il regardait les astres à travers les -silhouettes chétives et rachitiques de ses arbres fruitiers. Ce quart -d'arpent, si pauvrement planté, si encombré de masures et de hangars, -lui était cher et lui suffisait. - -Que fallait-il de plus à ce vieillard, qui partageait le loisir de sa -vie, où il y avait si peu de loisir, entre le jardinage le jour et la -contemplation la nuit? Cet étroit enclos, ayant les cieux pour plafond, -n'était-ce pas assez pour pouvoir adorer Dieu tour à tour dans ses -oeuvres les plus charmantes et dans ses oeuvres les plus sublimes? -N'est-ce pas là tout, en effet, et que désirer au-delà? Un petit jardin -pour se promener, et l'immensité pour rêver. À ses pieds ce qu'on peut -cultiver et cueillir; sur sa tête ce qu'on peut étudier et méditer; -quelques fleurs sur la terre et toutes les étoiles dans le ciel. - - - - -Chapitre XIV - -Ce qu'il pensait - - -Un dernier mot. - -Comme cette nature de détails pourrait, particulièrement au moment où -nous sommes, et pour nous servir d'une expression actuellement à la -mode, donner à l'évêque de Digne une certaine physionomie «panthéiste», -et faire croire, soit à son blâme, soit à sa louange, qu'il y avait en -lui une de ces philosophies personnelles, propres à notre siècle, qui -germent quelquefois dans les esprits solitaires et s'y construisent et y -grandissent jusqu'à y remplacer les religions, nous insistons sur ceci -que pas un de ceux qui ont connu monseigneur Bienvenu ne se fût cru -autorisé à penser rien de pareil. Ce qui éclairait cet homme, c'était le -coeur. Sa sagesse était faite de la lumière qui vient de là. - -Point de systèmes, beaucoup d'oeuvres. Les spéculations abstruses -contiennent du vertige; rien n'indique qu'il hasardât son esprit dans -les apocalypses. L'apôtre peut être hardi, mais l'évêque doit être -timide. Il se fût probablement fait scrupule de sonder trop avant de -certains problèmes réservés en quelque sorte aux grands esprits -terribles. Il y a de l'horreur sacrée sous les porches de l'énigme; ces -ouvertures sombres sont là béantes, mais quelque chose vous dit, à vous -passant de la vie, qu'on n'entre pas. Malheur à qui y pénètre! Les -génies, dans les profondeurs inouïes de l'abstraction et de la -spéculation pure, situés pour ainsi dire au-dessus des dogmes, proposent -leurs idées à Dieu. Leur prière offre audacieusement la discussion. Leur -adoration interroge. Ceci est la religion directe, pleine d'anxiété et -de responsabilité pour qui en tente les escarpements. - -La méditation humaine n'a point de limite. À ses risques et périls, elle -analyse et creuse son propre éblouissement. On pourrait presque dire -que, par une sorte de réaction splendide, elle en éblouit la nature; le -mystérieux monde qui nous entoure rend ce qu'il reçoit, il est probable -que les contemplateurs sont contemplés. Quoi qu'il en soit, il y a sur -la terre des hommes--sont-ce des hommes?--qui aperçoivent distinctement -au fond des horizons du rêve les hauteurs de l'absolu, et qui ont la -vision terrible de la montagne infinie. Monseigneur Bienvenu n'était -point de ces hommes-là, monseigneur Bienvenu n'était pas un génie. Il -eût redouté ces sublimités d'où quelques-uns, très grands même, comme -Swedenborg et Pascal, ont glissé dans la démence. Certes, ces puissantes -rêveries ont leur utilité morale, et par ces routes ardues on s'approche -de la perfection idéale. Lui, il prenait le sentier qui abrège: -l'évangile. Il n'essayait point de faire faire à sa chasuble les plis du -manteau d'Élie, il ne projetait aucun rayon d'avenir sur le roulis -ténébreux des événements, il ne cherchait pas à condenser en flamme la -lueur des choses, il n'avait rien du prophète et rien du mage. Cette âme -simple aimait, voilà tout. - -Qu'il dilatât la prière jusqu'à une aspiration surhumaine, cela est -probable; mais on ne peut pas plus prier trop qu'aimer trop; et, si -c'était une hérésie de prier au-delà des textes, sainte Thérèse et saint -Jérôme seraient des hérétiques. - -Il se penchait sur ce qui gémit et sur ce qui expie. L'univers lui -apparaissait comme une immense maladie; il sentait partout de la fièvre, -il auscultait partout de la souffrance, et, sans chercher à deviner -l'énigme, il tâchait de panser la plaie. Le redoutable spectacle des -choses créées développait en lui l'attendrissement; il n'était occupé -qu'à trouver pour lui-même et à inspirer aux autres la meilleure manière -de plaindre et de soulager. Ce qui existe était pour ce bon et rare -prêtre un sujet permanent de tristesse cherchant à consoler. - -Il y a des hommes qui travaillent à l'extraction de l'or; lui, il -travaillait à l'extraction de la pitié. L'universelle misère était sa -mine. La douleur partout n'était qu'une occasion de bonté toujours. -_Aimez-vous les uns les autres;_ il déclarait cela complet, ne -souhaitait rien de plus, et c'était là toute sa doctrine. Un jour, cet -homme qui se croyait «philosophe», ce sénateur, déjà nommé, dit à -l'évêque: - ---Mais voyez donc le spectacle du monde; guerre de tous contre tous; le -plus fort a le plus d'esprit. Votre _aimez-vous les uns les autres_ est -une bêtise. - ---Eh bien, répondit monseigneur Bienvenu sans disputer, si c'est une -bêtise, l'âme doit s'y enfermer comme la perle dans l'huître. - -Il s'y enfermait donc, il y vivait, il s'en satisfaisait absolument, -laissant de côté les questions prodigieuses qui attirent et qui -épouvantent, les perspectives insondables de l'abstraction, les -précipices de la métaphysique, toutes ces profondeurs convergentes, pour -l'apôtre à Dieu, pour l'athée au néant: la destinée, le bien et le mal, -la guerre de l'être contre l'être, la conscience de l'homme, le -somnambulisme pensif de l'animal, la transformation par la mort, la -récapitulation d'existences que contient le tombeau, la greffe -incompréhensible des amours successifs sur le moi persistant, l'essence, -la substance, le Nil et l'Ens, l'âme, la nature, la liberté, la -nécessité; problèmes à pic, épaisseurs sinistres, où se penchent les -gigantesques archanges de l'esprit humain; formidables abîmes que -Lucrèce, Manou, saint Paul et Dante contemplent avec cet oeil fulgurant -qui semble, en regardant fixement l'infini, y faire éclore des étoiles. - -Monseigneur Bienvenu était simplement un homme qui constatait du dehors -les questions mystérieuses sans les scruter, sans les agiter, et sans en -troubler son propre esprit, et qui avait dans l'âme le grave respect de -l'ombre. - - - - -Livre deuxième--La chute - - - - -Chapitre I - -Le soir d'un jour de marche - - -Dans les premiers jours du mois d'octobre 1815, une heure environ avant -le coucher du soleil, un homme qui voyageait à pied entrait dans la -petite ville de Digne. Les rares habitants qui se trouvaient en ce moment -à leurs fenêtres ou sur le seuil de leurs maisons regardaient ce -voyageur avec une sorte d'inquiétude. Il était difficile de rencontrer -un passant d'un aspect plus misérable. C'était un homme de moyenne -taille, trapu et robuste, dans la force de l'âge. Il pouvait avoir -quarante-six ou quarante-huit ans. Une casquette à visière de cuir -rabattue cachait en partie son visage, brûlé par le soleil et le hâle, -et ruisselant de sueur. Sa chemise de grosse toile jaune, rattachée au -col par une petite ancre d'argent, laissait voir sa poitrine velue; il -avait une cravate tordue en corde, un pantalon de coutil bleu, usé et -râpé, blanc à un genou, troué à l'autre, une vieille blouse grise en -haillons, rapiécée à l'un des coudes d'un morceau de drap vert cousu -avec de la ficelle, sur le dos un sac de soldat fort plein, bien bouclé -et tout neuf, à la main un énorme bâton noueux, les pieds sans bas dans -des souliers ferrés, la tête tondue et la barbe longue. - -La sueur, la chaleur, le voyage à pied, la poussière, ajoutaient je ne -sais quoi de sordide à cet ensemble délabré. - -Les cheveux étaient ras, et pourtant hérissés; car ils commençaient à -pousser un peu, et semblaient n'avoir pas été coupés depuis quelque -temps. - -Personne ne le connaissait. Ce n'était évidemment qu'un passant. D'où -venait-il? Du midi. Des bords de la mer peut-être. Car il faisait son -entrée dans Digne par la même rue qui, sept mois auparavant, avait vu -passer l'empereur Napoléon allant de Cannes à Paris. Cet homme avait dû -marcher tout le jour. Il paraissait très fatigué. Des femmes de l'ancien -bourg qui est au bas de la ville l'avaient vu s'arrêter sous les arbres -du boulevard Gassendi et boire à la fontaine qui est à l'extrémité de la -promenade. Il fallait qu'il eût bien soif, car des enfants qui le -suivaient le virent encore s'arrêter, et boire, deux cents pas plus -loin, à la fontaine de la place du marché. - -Arrivé au coin de la rue Poichevert, il tourna à gauche et se dirigea -vers la mairie. Il y entra, puis sortit un quart d'heure après. Un -gendarme était assis près de la porte sur le banc de pierre où le -général Drouot monta le 4 mars pour lire à la foule effarée des -habitants de Digne la proclamation du golfe Juan. L'homme ôta sa -casquette et salua humblement le gendarme. - -Le gendarme, sans répondre à son salut, le regarda avec attention, le -suivit quelque temps des yeux, puis entra dans la maison de ville. - -Il y avait alors à Digne une belle auberge à l'enseigne de _la -Croix-de-Colbas_. Cette auberge avait pour hôtelier un nommé Jacquin -Labarre, homme considéré dans la ville pour sa parenté avec un autre -Labarre, qui tenait à Grenoble l'auberge des _Trois-Dauphins_ et qui -avait servi dans les guides. Lors du débarquement de l'empereur, -beaucoup de bruits avaient couru dans le pays sur cette auberge des -_Trois-Dauphins_. On contait que le général Bertrand, déguisé en -charretier, y avait fait de fréquents voyages au mois de janvier, et -qu'il y avait distribué des croix d'honneur à des soldats et des -poignées de napoléons à des bourgeois. La réalité est que l'empereur, -entré dans Grenoble, avait refusé de s'installer à l'hôtel de la -préfecture; il avait remercié le maire en disant: _Je vais chez un brave -homme que je connais_, et il était allé aux _Trois-Dauphins_. Cette -gloire du Labarre des _Trois-Dauphins_ se reflétait à vingt-cinq lieues -de distance jusque sur le Labarre de la _Croix-de-Colbas_. On disait de -lui dans la ville: _C'est le cousin de celui de Grenoble_. - -L'homme se dirigea vers cette auberge, qui était la meilleure du pays. -Il entra dans la cuisine, laquelle s'ouvrait de plain-pied sur la rue. -Tous les fourneaux étaient allumés; un grand feu flambait gaîment dans -la cheminée. L'hôte, qui était en même temps le chef, allait de l'âtre -aux casseroles, fort occupé et surveillant un excellent dîner destiné à -des rouliers qu'on entendait rire et parler à grand bruit dans une salle -voisine. Quiconque a voyagé sait que personne ne fait meilleure chère -que les rouliers. Une marmotte grasse, flanquée de perdrix blanches et -de coqs de bruyère, tournait sur une longue broche devant le feu; sur -les fourneaux cuisaient deux grosses carpes du lac de Lauzet et une -truite du lac d'Alloz. - -L'hôte, entendant la porte s'ouvrir et entrer un nouveau venu, dit sans -lever les yeux de ses fourneaux: - ---Que veut monsieur? - ---Manger et coucher, dit l'homme. - ---Rien de plus facile, reprit l'hôte. - -En ce moment il tourna la tête, embrassa d'un coup d'oeil tout -l'ensemble du voyageur, et ajouta: - ---... en payant. - -L'homme tira une grosse bourse de cuir de la poche de sa blouse et -répondit: - ---J'ai de l'argent. - ---En ce cas on est à vous, dit l'hôte. - -L'homme remit sa bourse en poche, se déchargea de son sac, le posa à -terre près de la porte, garda son bâton à la main, et alla s'asseoir sur -une escabelle basse près du feu. Digne est dans la montagne. Les soirées -d'octobre y sont froides. - -Cependant, tout en allant et venant, l'homme considérait le voyageur. - ---Dîne-t-on bientôt? dit l'homme. - ---Tout à l'heure, dit l'hôte. - -Pendant que le nouveau venu se chauffait, le dos tourné, le digne -aubergiste Jacquin Labarre tira un crayon de sa poche, puis il déchira -le coin d'un vieux journal qui traînait sur une petite table près de la -fenêtre. Sur la marge blanche il écrivit une ligne ou deux, plia sans -cacheter et remit ce chiffon de papier à un enfant qui paraissait lui -servir tout à la fois de marmiton et de laquais. L'aubergiste dit un mot -à l'oreille du marmiton, et l'enfant partit en courant dans la direction -de la mairie. - -Le voyageur n'avait rien vu de tout cela. - -Il demanda encore une fois: - ---Dîne-t-on bientôt? - ---Tout à l'heure, dit l'hôte. - -L'enfant revint. Il rapportait le papier. L'hôte le déplia avec -empressement, comme quelqu'un qui attend une réponse. Il parut lire -attentivement, puis hocha la tête, et resta un moment pensif. Enfin il -fit un pas vers le voyageur qui semblait plongé dans des réflexions peu -sereines. - ---Monsieur, dit-il, je ne puis vous recevoir. - -L'homme se dressa à demi sur son séant. - ---Comment! Avez-vous peur que je ne paye pas? Voulez-vous que je paye -d'avance? J'ai de l'argent, vous dis-je. - ---Ce n'est pas cela. - ---Quoi donc? - ---Vous avez de l'argent.... - ---Oui, dit l'homme. - ---Et moi, dit l'hôte, je n'ai pas de chambre. - -L'homme reprit tranquillement: - ---Mettez-moi à l'écurie. - ---Je ne puis. - ---Pourquoi? - ---Les chevaux prennent toute la place. - ---Eh bien, repartit l'homme, un coin dans le grenier. Une botte de -paille. Nous verrons cela après dîner. - ---Je ne puis vous donner à dîner. - -Cette déclaration, faite d'un ton mesuré, mais ferme, parut grave à -l'étranger. Il se leva. - ---Ah bah! mais je meurs de faim, moi. J'ai marché dès le soleil levé. -J'ai fait douze lieues. Je paye. Je veux manger. - ---Je n'ai rien, dit l'hôte. - -L'homme éclata de rire et se tourna vers la cheminée et les fourneaux. - ---Rien! et tout cela? - ---Tout cela m'est retenu. - ---Par qui? - ---Par ces messieurs les rouliers. - ---Combien sont-ils? - ---Douze. - ---Il y a là à manger pour vingt. - ---Ils ont tout retenu et tout payé d'avance. - -L'homme se rassit et dit sans hausser la voix: - ---Je suis à l'auberge, j'ai faim, et je reste. - -L'hôte alors se pencha à son oreille, et lui dit d'un accent qui le fit -tressaillir: - ---Allez-vous en. - -Le voyageur était courbé en cet instant et poussait quelques braises -dans le feu avec le bout ferré de son bâton, il se retourna vivement, -et, comme il ouvrait la bouche pour répliquer, l'hôte le regarda -fixement et ajouta toujours à voix basse: - ---Tenez, assez de paroles comme cela. Voulez-vous que je vous dise votre -nom? Vous vous appelez Jean Valjean. Maintenant voulez-vous que je vous -dise qui vous êtes? En vous voyant entrer, je me suis douté de quelque -chose, j'ai envoyé à la mairie, et voici ce qu'on m'a répondu. -Savez-vous lire? - -En parlant ainsi il tendait à l'étranger, tout déplié, le papier qui -venait de voyager de l'auberge à la mairie, et de la mairie à l'auberge. -L'homme y jeta un regard. L'aubergiste reprit après un silence: - ---J'ai l'habitude d'être poli avec tout le monde. Allez-vous-en. - -L'homme baissa la tête, ramassa le sac qu'il avait déposé à terre, et -s'en alla. Il prit la grande rue. Il marchait devant lui au hasard, -rasant de près les maisons, comme un homme humilié et triste. Il ne se -retourna pas une seule fois. S'il s'était retourné, il aurait vu -l'aubergiste de la _Croix-de-Colbas_ sur le seuil de sa porte, entouré -de tous les voyageurs de son auberge et de tous les passants de la rue, -parlant vivement et le désignant du doigt, et, aux regards de défiance -et d'effroi du groupe, il aurait deviné qu'avant peu son arrivée serait -l'événement de toute la ville. - -Il ne vit rien de tout cela. Les gens accablés ne regardent pas derrière -eux. Ils ne savent que trop que le mauvais sort les suit. - -Il chemina ainsi quelque temps, marchant toujours, allant à l'aventure -par des rues qu'il ne connaissait pas, oubliant la fatigue, comme cela -arrive dans la tristesse. Tout à coup il sentit vivement la faim. La -nuit approchait. Il regarda autour de lui pour voir s'il ne découvrirait -pas quelque gîte. - -La belle hôtellerie s'était fermée pour lui; il cherchait quelque -cabaret bien humble, quelque bouge bien pauvre. - -Précisément une lumière s'allumait au bout de la rue; une branche de -pin, pendue à une potence en fer, se dessinait sur le ciel blanc du -crépuscule. Il y alla. - -C'était en effet un cabaret. Le cabaret qui est dans la rue de Chaffaut. - -Le voyageur s'arrêta un moment, et regarda par la vitre l'intérieur de -la salle basse du cabaret, éclairée par une petite lampe sur une table -et par un grand feu dans la cheminée. Quelques hommes y buvaient. L'hôte -se chauffait. La flamme faisait bruire une marmite de fer accrochée à la -crémaillère. - -On entre dans ce cabaret, qui est aussi une espèce d'auberge, par deux -portes. L'une donne sur la rue, l'autre s'ouvre sur une petite cour -pleine de fumier. - -Le voyageur n'osa pas entrer par la porte de la rue. Il se glissa dans -la cour, s'arrêta encore, puis leva timidement le loquet et poussa la -porte. - ---Qui va là? dit le maître. - ---Quelqu'un qui voudrait souper et coucher. - ---C'est bon. Ici on soupe et on couche. - -Il entra. Tous les gens qui buvaient se retournèrent. La lampe -l'éclairait d'un côté, le feu de l'autre. On l'examina quelque temps -pendant qu'il défaisait son sac. - -L'hôte lui dit: - ---Voilà du feu. Le souper cuit dans la marmite. Venez vous chauffer, -camarade. - -Il alla s'asseoir près de l'âtre. Il allongea devant le feu ses pieds -meurtris par la fatigue; une bonne odeur sortait de la marmite. Tout ce -qu'on pouvait distinguer de son visage sous sa casquette baissée prit -une vague apparence de bien-être mêlée à cet autre aspect si poignant -que donne l'habitude de la souffrance. - -C'était d'ailleurs un profil ferme, énergique et triste. Cette -physionomie était étrangement composée; elle commençait par paraître -humble et finissait par sembler sévère. L'oeil luisait sous les sourcils -comme un feu sous une broussaille. - -Cependant un des hommes attablés était un poissonnier qui, avant -d'entrer au cabaret de la rue de Chaffaut, était allé mettre son cheval -à l'écurie chez Labarre. Le hasard faisait que le matin même il avait -rencontré cet étranger de mauvaise mine, cheminant entre Bras dasse -et... j'ai oublié le nom. (Je crois que c'est Escoublon). Or, en le -rencontrant, l'homme, qui paraissait déjà très fatigué, lui avait -demandé de le prendre en croupe; à quoi le poissonnier n'avait répondu -qu'en doublant le pas. Ce poissonnier faisait partie, une demi-heure -auparavant, du groupe qui entourait Jacquin Labarre, et lui-même avait -raconté sa désagréable rencontre du matin aux gens de _la -Croix-de-Colbas_. Il fit de sa place au cabaretier un signe -imperceptible. Le cabaretier vint à lui. Ils échangèrent quelques -paroles à voix basse. L'homme était retombé dans ses réflexions. - -Le cabaretier revint à la cheminée, posa brusquement sa main sur -l'épaule de l'homme, et lui dit: - ---Tu vas t'en aller d'ici. - -L'étranger se retourna et répondit avec douceur. - ---Ah! vous savez? - ---Oui. - ---On m'a renvoyé de l'autre auberge. - ---Et l'on te chasse de celle-ci. - ---Où voulez-vous que j'aille? - ---Ailleurs. - -L'homme prit son bâton et son sac, et s'en alla. - -Comme il sortait, quelques enfants, qui l'avaient suivi depuis _la -Croix-de-Colbas_ et qui semblaient l'attendre, lui jetèrent des pierres. -Il revint sur ses pas avec colère et les menaça de son bâton; les -enfants se dispersèrent comme une volée d'oiseaux. - -Il passa devant la prison. À la porte pendait une chaîne de fer attachée -à une cloche. Il sonna. - -Un guichet s'ouvrit. - ---Monsieur le guichetier, dit-il en ôtant respectueusement sa casquette, -voudriez-vous bien m'ouvrir et me loger pour cette nuit? - -Une voix répondit: - ---Une prison n'est pas une auberge. Faites-vous arrêter. On vous -ouvrira. - -Le guichet se referma. - -Il entra dans une petite rue où il y a beaucoup de jardins. Quelques-uns -ne sont enclos que de haies, ce qui égaye la rue. Parmi ces jardins et -ces haies, il vit une petite maison d'un seul étage dont la fenêtre -était éclairée. Il regarda par cette vitre comme il avait fait pour le -cabaret. C'était une grande chambre blanchie à la chaux, avec un lit -drapé d'indienne imprimée, et un berceau dans un coin, quelques chaises -de bois et un fusil à deux coups accroché au mur. Une table était servie -au milieu de la chambre. Une lampe de cuivre éclairait la nappe de -grosse toile blanche, le broc d'étain luisant comme l'argent et plein de -vin et la soupière brune qui fumait. À cette table était assis un homme -d'une quarantaine d'années, à la figure joyeuse et ouverte, qui faisait -sauter un petit enfant sur ses genoux. Près de lui, une femme toute -jeune allaitait un autre enfant. Le père riait, l'enfant riait, la mère -souriait. - -L'étranger resta un moment rêveur devant ce spectacle doux et calmant. -Que se passait-il en lui? Lui seul eût pu le dire. Il est probable qu'il -pensa que cette maison joyeuse serait hospitalière, et que là où il -voyait tant de bonheur il trouverait peut-être un peu de pitié. - -Il frappa au carreau un petit coup très faible. - -On n'entendit pas. - -Il frappa un second coup. - -Il entendit la femme qui disait: - ---Mon homme, il me semble qu'on frappe. - ---Non, répondit le mari. - -Il frappa un troisième coup. - -Le mari se leva, prit la lampe, et alla à la porte qu'il ouvrit. - -C'était un homme de haute taille, demi-paysan, demi-artisan. Il portait -un vaste tablier de cuir qui montait jusqu'à son épaule gauche, et dans -lequel faisaient ventre un marteau, un mouchoir rouge, une poire à -poudre, toutes sortes d'objets que la ceinture retenait comme dans une -poche. Il renversait la tête en arrière; sa chemise largement ouverte et -rabattue montrait son cou de taureau, blanc et nu. Il avait d'épais -sourcils, d'énormes favoris noirs, les yeux à fleur de tête, le bas du -visage en museau, et sur tout cela cet air d'être chez soi qui est une -chose inexprimable. - ---Monsieur, dit le voyageur, pardon. En payant, pourriez-vous me donner -une assiettée de soupe et un coin pour dormir dans ce hangar qui est là -dans ce jardin? Dites, pourriez-vous? En payant? - ---Qui êtes-vous? demanda le maître du logis. - -L'homme répondit: - ---J'arrive de Puy-Moisson. J'ai marché toute la journée. J'ai fait douze -lieues. Pourriez-vous? En payant? - ---Je ne refuserais pas, dit le paysan, de loger quelqu'un de bien qui -payerait. Mais pourquoi n'allez-vous pas à l'auberge. - ---Il n'y a pas de place. - ---Bah! pas possible. Ce n'est pas jour de foire ni de marché. Êtes-vous -allé chez Labarre? - ---Oui. - ---Eh bien? - -Le voyageur répondit avec embarras: - ---Je ne sais pas, il ne m'a pas reçu. - ---Êtes-vous allé chez chose, de la rue de Chaffaut? - -L'embarras de l'étranger croissait. Il balbutia: - ---Il ne m'a pas reçu non plus. - -Le visage du paysan prit une expression de défiance, il regarda le -nouveau venu de la tête aux pieds, et tout à coup il s'écria avec une -sorte de frémissement: - ---Est-ce que vous seriez l'homme?... - -Il jeta un nouveau coup d'oeil sur l'étranger, fit trois pas en arrière, -posa la lampe sur la table et décrocha son fusil du mur. - -Cependant aux paroles du paysan: _Est-ce que vous seriez l'homme?..._ la -femme s'était levée, avait pris ses deux enfants dans ses bras et -s'était réfugiée précipitamment derrière son mari, regardant l'étranger -avec épouvante, la gorge nue, les yeux effarés, en murmurant tout bas:_ -Tso-maraude_. - -Tout cela se fit en moins de temps qu'il ne faut pour se le figurer. -Après avoir examiné quelques instants l'homme comme on examine une -vipère, le maître du logis revint à la porte et dit: - ---Va-t'en. - ---Par grâce, reprit l'homme, un verre d'eau. - ---Un coup de fusil! dit le paysan. - -Puis il referma la porte violemment, et l'homme l'entendit tirer deux -gros verrous. Un moment après, la fenêtre se ferma au volet, et un bruit -de barre de fer qu'on posait parvint au dehors. - -La nuit continuait de tomber. Le vent froid des Alpes soufflait. À la -lueur du jour expirant, l'étranger aperçut dans un des jardins qui -bordent la rue une sorte de hutte qui lui parut maçonnée en mottes de -gazon. Il franchit résolument une barrière de bois et se trouva dans le -jardin. Il s'approcha de la hutte; elle avait pour porte une étroite -ouverture très basse et elle ressemblait à ces constructions que les -cantonniers se bâtissent au bord des routes. Il pensa sans doute que -c'était en effet le logis d'un cantonnier; il souffrait du froid et de -la faim; il s'était résigné à la faim, mais c'était du moins là un abri -contre le froid. Ces sortes de logis ne sont habituellement pas occupés -la nuit. Il se coucha à plat ventre et se glissa dans la hutte. Il y -faisait chaud, et il y trouva un assez bon lit de paille. Il resta un -moment étendu sur ce lit, sans pouvoir faire un mouvement tant il était -fatigué. Puis, comme son sac sur son dos le gênait et que c'était -d'ailleurs un oreiller tout trouvé, il se mit à déboucler une des -courroies. En ce moment un grondement farouche se fit entendre. Il leva -les yeux. La tête d'un dogue énorme se dessinait dans l'ombre à -l'ouverture de la hutte. - -C'était la niche d'un chien. - -Il était lui-même vigoureux et redoutable; il s'arma de son bâton, il se -fit de son sac un bouclier, et sortit de la niche comme il put, non sans -élargir les déchirures de ses haillons. - -Il sortit également du jardin, mais à reculons, obligé, pour tenir le -dogue en respect, d'avoir recours à cette manoeuvre du bâton que les -maîtres en ce genre d'escrime appellent _la rose couverte_. - -Quand il eut, non sans peine, repassé la barrière et qu'il se retrouva -dans la rue, seul, sans gîte, sans toit, sans abri, chassé même de ce -lit de paille et de cette niche misérable, il se laissa tomber plutôt -qu'il ne s'assit sur une pierre, et il paraît qu'un passant qui -traversait l'entendit s'écrier: - ---Je ne suis pas même un chien! - -Bientôt il se releva et se remit à marcher. Il sortit de la ville, -espérant trouver quelque arbre ou quelque meule dans les champs, et s'y -abriter. - -Il chemina ainsi quelque temps, la tête toujours baissée. Quand il se -sentit loin de toute habitation humaine, il leva les yeux et chercha -autour de lui. Il était dans un champ; il avait devant lui une de ces -collines basses couvertes de chaume coupé ras, qui après la moisson -ressemblent à des têtes tondues. - -L'horizon était tout noir; ce n'était pas seulement le sombre de la -nuit; c'étaient des nuages très bas qui semblaient s'appuyer sur la -colline même et qui montaient, emplissant tout le ciel. Cependant, comme -la lune allait se lever et qu'il flottait encore au zénith un reste de -clarté crépusculaire, ces nuages formaient au haut du ciel une sorte de -voûte blanchâtre d'où tombait sur la terre une lueur. - -La terre était donc plus éclairée que le ciel, ce qui est un effet -particulièrement sinistre, et la colline, d'un pauvre et chétif contour, -se dessinait vague et blafarde sur l'horizon ténébreux. Tout cet -ensemble était hideux, petit, lugubre et borné. Rien dans le champ ni -sur la colline qu'un arbre difforme qui se tordait en frissonnant à -quelques pas du voyageur. - -Cet homme était évidemment très loin d'avoir de ces délicates habitudes -d'intelligence et d'esprit qui font qu'on est sensible aux aspects -mystérieux des choses; cependant il y avait dans ce ciel, dans cette -colline, dans cette plaine et dans cet arbre, quelque chose de si -profondément désolé qu'après un moment d'immobilité et de rêverie, il -rebroussa chemin brusquement. Il y a des instants où la nature semble -hostile. - -Il revint sur ses pas. Les portes de Digne étaient fermées. Digne, qui a -soutenu des sièges dans les guerres de religion, était encore entourée -en 1815 de vieilles murailles flanquées de tours carrées qu'on a -démolies depuis. Il passa par une brèche et rentra dans la ville. - -Il pouvait être huit heures du soir. Comme il ne connaissait pas les -rues, il recommença sa promenade à l'aventure. - -Il parvint ainsi à la préfecture, puis au séminaire. En passant sur la -place de la cathédrale, il montra le poing à l'église. - -Il y a au coin de cette place une imprimerie. C'est là que furent -imprimées pour la première fois les proclamations de l'empereur et de la -garde impériale à l'armée, apportées de l'île d'Elbe et dictées par -Napoléon lui-même. - -Épuisé de fatigue et n'espérant plus rien, il se coucha sur le banc de -pierre qui est à la porte de cette imprimerie. - -Une vieille femme sortait de l'église en ce moment. Elle vit cet homme -étendu dans l'ombre. - ---Que faites-vous là, mon ami? dit-elle. - -Il répondit durement et avec colère: - ---Vous le voyez, bonne femme, je me couche. - -La bonne femme, bien digne de ce nom en effet, était madame la marquise -de R. - ---Sur ce banc? reprit-elle. - ---J'ai eu pendant dix-neuf ans un matelas de bois, dit l'homme, j'ai -aujourd'hui un matelas de pierre. - ---Vous avez été soldat? - ---Oui, bonne femme. Soldat. - ---Pourquoi n'allez-vous pas à l'auberge? - ---Parce que je n'ai pas d'argent. - ---Hélas, dit madame de R., je n'ai dans ma bourse que quatre sous. - ---Donnez toujours. - -L'homme prit les quatre sous. Madame de R. continua: - ---Vous ne pouvez vous loger avec si peu dans une auberge. Avez-vous -essayé pourtant? Il est impossible que vous passiez ainsi la nuit. Vous -avez sans doute froid et faim. On aurait pu vous loger par charité. - ---J'ai frappé à toutes les portes. - ---Eh bien? - ---Partout on m'a chassé. - -La «bonne femme» toucha le bras de l'homme et lui montra de l'autre côté -de la place une petite maison basse à côté de l'évêché. - ---Vous avez, reprit-elle, frappé à toutes les portes? - ---Oui. - ---Avez-vous frappé à celle-là? - ---Non. - ---Frappez-y. - - - - -Chapitre II - -La prudence conseillée à la sagesse - - -Ce soir-là, M. l'évêque de Digne, après sa promenade en ville, était -resté assez tard enfermé dans sa chambre. Il s'occupait d'un grand -travail sur les _Devoirs_, lequel est malheureusement demeuré inachevé. -Il dépouillait soigneusement tout ce que les Pères et les Docteurs ont -dit sur cette grave matière. Son livre était divisé en deux parties; -premièrement les devoirs de tous, deuxièmement les devoirs de chacun, -selon la classe à laquelle il appartient. Les devoirs de tous sont les -grands devoirs. Il y en a quatre. Saint Matthieu les indique: devoirs -envers Dieu (Matth., VI), devoirs envers soi-même (Matth., V, 29, 30), -devoirs envers le prochain (Matth., VII, 12), devoirs envers les -créatures (Matth., VI, 20, 25). Pour les autres devoirs, l'évêque les -avait trouvés indiqués et prescrits ailleurs; aux souverains et aux -sujets, dans l'Épître aux Romains; aux magistrats, aux épouses, aux -mères et aux jeunes hommes, par saint Pierre; aux maris, aux pères, aux -enfants et aux serviteurs, dans l'Épître aux Éphésiens; aux fidèles, -dans l'Épître aux Hébreux; aux vierges, dans l'Épître aux Corinthiens. -Il faisait laborieusement de toutes ces prescriptions un ensemble -harmonieux qu'il voulait présenter aux âmes. - -Il travaillait encore à huit heures, écrivant assez incommodément sur de -petits carrés de papier avec un gros livre ouvert sur ses genoux, quand -madame Magloire entra, selon son habitude, pour prendre l'argenterie -dans le placard près du lit. Un moment après, l'évêque, sentant que le -couvert était mis et que sa soeur l'attendait peut-être, ferma son -livre, se leva de sa table et entra dans la salle à manger. - -La salle à manger était une pièce oblongue à cheminée, avec porte sur la -rue (nous l'avons dit), et fenêtre sur le jardin. - -Madame Magloire achevait en effet de mettre le couvert. - -Tout en vaquant au service, elle causait avec mademoiselle Baptistine. - -Une lampe était sur la table; la table était près de la cheminée. Un -assez bon feu était allumé. - -On peut se figurer facilement ces deux femmes qui avaient toutes deux -passé soixante ans: madame Magloire petite, grasse, vive; mademoiselle -Baptistine, douce, mince, frêle, un peu plus grande que son frère, vêtue -d'une robe de soie puce, couleur à la mode en 1806, qu'elle avait -achetée alors à Paris et qui lui durait encore. Pour emprunter des -locutions vulgaires qui ont le mérite de dire avec un seul mot une idée -qu'une page suffirait à peine à exprimer, madame Magloire avait l'air -d'une _paysanne_ et mademoiselle Baptistine d'une _dame_. Madame -Magloire avait un bonnet blanc à tuyaux, au cou une jeannette d'or, le -seul bijou de femme qu'il y eût dans la maison, un fichu très blanc -sortant de la robe de bure noire à manches larges et courtes, un tablier -de toile de coton à carreaux rouges et verts, noué à la ceinture d'un -ruban vert, avec pièce d'estomac pareille rattachée par deux épingles -aux deux coins d'en haut, aux pieds de gros souliers et des bas jaunes -comme les femmes de Marseille. La robe de mademoiselle Baptistine était -coupée sur les patrons de 1806, taille courte, fourreau étroit, manches -à épaulettes, avec pattes et boutons. Elle cachait ses cheveux gris sous -une perruque frisée dite à _l'enfant_. Madame Magloire avait l'air -intelligent, vif et bon; les deux angles de sa bouche inégalement -relevés et la lèvre supérieure plus grosse que la lèvre inférieure lui -donnaient quelque chose de bourru et d'impérieux. Tant que monseigneur -se taisait, elle lui parlait résolument avec un mélange de respect et de -liberté; mais dès que monseigneur parlait, on a vu cela, elle obéissait -passivement comme mademoiselle. Mademoiselle Baptistine ne parlait même -pas. Elle se bornait à obéir et à complaire. Même quand elle était -jeune, elle n'était pas jolie, elle avait de gros yeux bleus à fleur de -tête et le nez long et busqué; mais tout son visage, toute sa personne, -nous l'avons dit en commençant, respiraient une ineffable bonté. Elle -avait toujours été prédestinée à la mansuétude; mais la foi, la charité, -l'espérance, ces trois vertus qui chauffent doucement l'âme, avaient -élevé peu à peu cette mansuétude jusqu'à la sainteté. La nature n'en -avait fait qu'une brebis, la religion en avait fait un ange. Pauvre -sainte fille! doux souvenir disparu! Mademoiselle Baptistine a depuis -raconté tant de fois ce qui s'était passé à l'évêché cette soirée-là, -que plusieurs personnes qui vivent encore s'en rappellent les moindres -détails. - -Au moment où M. l'évêque entra, madame Magloire parlait avec quelque -vivacité. Elle entretenait _mademoiselle_ d'un sujet qui lui était -familier et auquel l'évêque était accoutumé. Il s'agissait du loquet de -la porte d'entrée. - -Il paraît que, tout en allant faire quelques provisions pour le souper, -madame Magloire avait entendu dire des choses en divers lieux. On -parlait d'un rôdeur de mauvaise mine; qu'un vagabond suspect serait -arrivé, qu'il devait être quelque part dans la ville, et qu'il se -pourrait qu'il y eût de méchantes rencontres pour ceux qui s'aviseraient -de rentrer tard chez eux cette nuit-là. Que la police était bien mal -faite du reste, attendu que M. le préfet et M. le maire ne s'aimaient -pas, et cherchaient à se nuire en faisant arriver des événements. Que -c'était donc aux gens sages à faire la police eux-mêmes et à se bien -garder, et qu'il faudrait avoir soin de dûment clore, verrouiller et -barricader sa maison, _et de bien fermer ses portes_. - -Madame Magloire appuya sur ce dernier mot; mais l'évêque venait de sa -chambre où il avait eu assez froid, il s'était assis devant la cheminée -et se chauffait, et puis il pensait à autre chose. Il ne releva pas le -mot à effet que madame Magloire venait de laisser tomber. Elle le -répéta. Alors, mademoiselle Baptistine, voulant satisfaire madame -Magloire sans déplaire à son frère, se hasarda à dire timidement: - ---Mon frère, entendez-vous ce que dit madame Magloire? - ---J'en ai entendu vaguement quelque chose, répondit l'évêque. - -Puis tournant à demi sa chaise, mettant ses deux mains sur ses genoux, -et levant vers la vieille servante son visage cordial et facilement -joyeux, que le feu éclairait d'en bas: - ---Voyons. Qu'y a-t-il? qu'y a-t-il? Nous sommes donc dans quelque gros -danger? - -Alors madame Magloire recommença toute l'histoire, en l'exagérant -quelque peu, sans s'en douter. Il paraîtrait qu'un bohémien, un -va-nu-pieds, une espèce de mendiant dangereux serait en ce moment dans -la ville. Il s'était présenté pour loger chez Jacquin Labarre qui -n'avait pas voulu le recevoir. On l'avait vu arriver par le boulevard -Gassendi et rôder dans les rues à la brume. Un homme de sac et de corde -avec une figure terrible. - ---Vraiment? dit l'évêque. - -Ce consentement à l'interroger encouragea madame Magloire; cela lui -semblait indiquer que l'évêque n'était pas loin de s'alarmer; elle -poursuivit triomphante: - ---Oui, monseigneur. C'est comme cela. Il y aura quelque malheur cette -nuit dans la ville. Tout le monde le dit. Avec cela que la police est si -mal faite (répétition inutile). Vivre dans un pays de montagnes, et -n'avoir pas même de lanternes la nuit dans les rues! On sort. Des fours, -quoi! Et je dis, monseigneur, et mademoiselle que voilà dit comme moi.... - ---Moi, interrompit la soeur, je ne dis rien. Ce que mon frère fait est -bien fait. - -Madame Magloire continua comme s'il n'y avait pas eu de protestation: - ---Nous disons que cette maison-ci n'est pas sûre du tout; que, si -monseigneur le permet, je vais aller dire à Paulin Musebois, le -serrurier, qu'il vienne remettre les anciens verrous de la porte; on les -a là, c'est une minute; et je dis qu'il faut des verrous, monseigneur, -ne serait-ce que pour cette nuit; car je dis qu'une porte qui s'ouvre du -dehors avec un loquet, par le premier passant venu, rien n'est plus -terrible; avec cela que monseigneur a l'habitude de toujours dire -d'entrer, et que d'ailleurs, même au milieu de la nuit, ô mon Dieu! on -n'a pas besoin d'en demander la permission.... - -En ce moment, on frappa à la porte un coup assez violent. - ---Entrez, dit l'évêque. - - - - -Chapitre III - -Héroïsme de l'obéissance passive - - -La porte s'ouvrit. - -Elle s'ouvrit vivement, toute grande, comme si quelqu'un la poussait -avec énergie et résolution. - -Un homme entra. - -Cet homme, nous le connaissons déjà. C'est le voyageur que nous avons vu -tout à l'heure errer cherchant un gîte. - -Il entra, fit un pas, et s'arrêta, laissant la porte ouverte derrière -lui. Il avait son sac sur l'épaule, son bâton à la main, une expression -rude, hardie, fatiguée et violente dans les yeux. Le feu de la cheminée -l'éclairait. Il était hideux. C'était une sinistre apparition. - -Madame Magloire n'eut pas même la force de jeter un cri. Elle -tressaillit, et resta béante. - -Mademoiselle Baptistine se retourna, aperçut l'homme qui entrait et se -dressa à demi d'effarement, puis, ramenant peu à peu sa tête vers la -cheminée, elle se mit à regarder son frère et son visage redevint -profondément calme et serein. - -L'évêque fixait sur l'homme un oeil tranquille. - -Comme il ouvrait la bouche, sans doute pour demander au nouveau venu ce -qu'il désirait, l'homme appuya ses deux mains à la fois sur son bâton, -promena ses yeux tour à tour sur le vieillard et les femmes, et, sans -attendre que l'évêque parlât, dit d'une voix haute: - ---Voici. Je m'appelle Jean Valjean. Je suis un galérien. J'ai passé -dix-neuf ans au bagne. Je suis libéré depuis quatre jours et en route -pour Pontarlier qui est ma destination. Quatre jours et que je marche -depuis Toulon. Aujourd'hui, j'ai fait douze lieues à pied. Ce soir, en -arrivant dans ce pays, j'ai été dans une auberge, on m'a renvoyé à cause -de mon passeport jaune que j'avais montré à la mairie. Il avait fallu. -J'ai été à une autre auberge. On m'a dit: Va-t-en! Chez l'un, chez -l'autre. Personne n'a voulu de moi. J'ai été à la prison, le guichetier -n'a pas ouvert. J'ai été dans la niche d'un chien. Ce chien m'a mordu et -m'a chassé, comme s'il avait été un homme. On aurait dit qu'il savait -qui j'étais. Je m'en suis allé dans les champs pour coucher à la belle -étoile. Il n'y avait pas d'étoile. J'ai pensé qu'il pleuvrait, et qu'il -n'y avait pas de bon Dieu pour empêcher de pleuvoir, et je suis rentré -dans la ville pour y trouver le renfoncement d'une porte. Là, dans la -place, j'allais me coucher sur une pierre. Une bonne femme m'a montré -votre maison et m'a dit: «Frappe là». J'ai frappé. Qu'est-ce que c'est -ici? Êtes-vous une auberge? J'ai de l'argent. Ma masse. Cent neuf francs -quinze sous que j'ai gagnés au bagne par mon travail en dix-neuf ans. Je -payerai. Qu'est-ce que cela me fait? J'ai de l'argent. Je suis très -fatigué, douze lieues à pied, j'ai bien faim. Voulez-vous que je reste? - ---Madame Magloire, dit l'évêque, vous mettrez un couvert de plus. - -L'homme fit trois pas et s'approcha de la lampe qui était sur la table. - ---Tenez, reprit-il, comme s'il n'avait pas bien compris, ce n'est pas -ça. Avez-vous entendu? Je suis un galérien. Un forçat. Je viens des -galères. - -Il tira de sa poche une grande feuille de papier jaune qu'il déplia. - ---Voilà mon passeport. Jaune, comme vous voyez. Cela sert à me faire -chasser de partout où je suis. Voulez-vous lire? Je sais lire, moi. J'ai -appris au bagne. Il y a une école pour ceux qui veulent. Tenez, voilà ce -qu'on a mis sur le passeport: «Jean Valjean, forçat libéré, natif -de...--cela vous est égal...--Est resté dix-neuf ans au bagne. Cinq ans -pour vol avec effraction. Quatorze ans pour avoir tenté de s'évader -quatre fois. Cet homme est très dangereux.»--Voilà! Tout le monde m'a -jeté dehors. Voulez-vous me recevoir, vous? Est-ce une auberge? -Voulez-vous me donner à manger et à coucher? Avez-vous une écurie? - ---Madame Magloire, dit l'évêque, vous mettrez des draps blancs au lit de -l'alcôve. - -Nous avons déjà expliqué de quelle nature était l'obéissance des deux -femmes. - -Madame Magloire sortit pour exécuter ces ordres. L'évêque se tourna vers -l'homme. - ---Monsieur, asseyez-vous et chauffez-vous. Nous allons souper dans un -instant, et l'on fera votre lit pendant que vous souperez. - -Ici l'homme comprit tout à fait. L'expression de son visage, jusqu'alors -sombre et dure, s'empreignit de stupéfaction, de doute, de joie, et -devint extraordinaire. Il se mit à balbutier comme un homme fou: - ---Vrai? quoi? vous me gardez? vous ne me chassez pas! un forçat! Vous -m'appelez monsieur! vous ne me tutoyez pas! Va-t-en, chien! qu'on me dit -toujours. Je croyais bien que vous me chasseriez. Aussi j'avais dit tout -de suite qui je suis. Oh! la brave femme qui m'a enseigné ici! Je vais -souper! un lit! Un lit avec des matelas et des draps! comme tout le -monde! il y a dix-neuf ans que je n'ai couché dans un lit! Vous voulez -bien que je ne m'en aille pas! Vous êtes de dignes gens! D'ailleurs j'ai -de l'argent. Je payerai bien. Pardon, monsieur l'aubergiste, comment -vous appelez-vous? Je payerai tout ce qu'on voudra. Vous êtes un brave -homme. Vous êtes aubergiste, n'est-ce pas? - ---Je suis, dit l'évêque, un prêtre qui demeure ici. - ---Un prêtre! reprit l'homme. Oh! un brave homme de prêtre! Alors vous ne -me demandez pas d'argent? Le curé, n'est-ce pas? le curé de cette grande -église? Tiens! c'est vrai, que je suis bête! je n'avais pas vu votre -calotte! - -Tout en parlant, il avait déposé son sac et son bâton dans un coin, puis -remis son passeport dans sa poche, et il s'était assis. Mademoiselle -Baptistine le considérait avec douceur. Il continua: - ---Vous êtes humain, monsieur le curé. Vous n'avez pas de mépris. C'est -bien bon un bon prêtre. Alors vous n'avez pas besoin que je paye? - ---Non, dit l'évêque, gardez votre argent. Combien avez-vous? ne -m'avez-vous pas dit cent neuf francs? - ---Quinze sous, ajouta l'homme. - ---Cent neuf francs quinze sous. Et combien de temps avez-vous mis à -gagner cela? - ---Dix-neuf ans. - ---Dix-neuf ans! - -L'évêque soupira profondément. - -L'homme poursuivit: - ---J'ai encore tout mon argent. Depuis quatre jours je n'ai dépensé que -vingt-cinq sous que j'ai gagnés en aidant à décharger des voitures à -Grasse. Puisque vous êtes abbé, je vais vous dire, nous avions un -aumônier au bagne. Et puis un jour j'ai vu un évêque. Monseigneur, qu'on -appelle. C'était l'évêque de la Majore, à Marseille. C'est le curé qui -est sur les curés. Vous savez, pardon, je dis mal cela, mais pour moi, -c'est si loin!--Vous comprenez, nous autres! Il a dit la messe au milieu -du bagne, sur un autel, il avait une chose pointue, en or, sur la tête. -Au grand jour de midi, cela brillait. Nous étions en rang. Des trois -côtés. Avec les canons, mèche allumée, en face de nous. Nous ne voyions -pas bien. Il a parlé, mais il était trop au fond, nous n'entendions pas. -Voilà ce que c'est qu'un évêque. - -Pendant qu'il parlait, l'évêque était allé pousser la porte qui était -restée toute grande ouverte. - -Madame Magloire rentra. Elle apportait un couvert qu'elle mit sur la -table. - ---Madame Magloire, dit l'évêque, mettez ce couvert le plus près possible -du feu. - -Et se tournant vers son hôte: - ---Le vent de nuit est dur dans les Alpes. Vous devez avoir froid, -monsieur? - -Chaque fois qu'il disait ce mot monsieur, avec sa voix doucement grave -et de si bonne compagnie, le visage de l'homme s'illuminait. Monsieur à -un forçat, c'est un verre d'eau à un naufragé de la Méduse. L'ignominie -a soif de considération. - ---Voici, reprit l'évêque, une lampe qui éclaire bien mal. - -Madame Magloire comprit, et elle alla chercher sur la cheminée de la -chambre à coucher de monseigneur les deux chandeliers d'argent qu'elle -posa sur la table tout allumés. - ---Monsieur le curé, dit l'homme, vous êtes bon. Vous ne me méprisez pas. -Vous me recevez chez vous. Vous allumez vos cierges pour moi. Je ne vous -ai pourtant pas caché d'où je viens et que je suis un homme malheureux. - -L'évêque, assis près de lui, lui toucha doucement la main. - ---Vous pouviez ne pas me dire qui vous étiez. - -Ce n'est pas ici ma maison, c'est la maison de Jésus-Christ. Cette porte -ne demande pas à celui qui entre s'il a un nom, mais s'il a une douleur. -Vous souffrez; vous avez faim et soif; soyez le bienvenu. Et ne me -remerciez pas, ne me dites pas que je vous reçois chez moi. Personne -n'est ici chez soi, excepté celui qui a besoin d'un asile. Je vous le -dis à vous qui passez, vous êtes ici chez vous plus que moi-même. Tout -ce qui est ici est à vous. Qu'ai-je besoin de savoir votre nom? -D'ailleurs, avant que vous me le disiez, vous en avez un que je savais. - -L'homme ouvrit des yeux étonnés. - ---Vrai? vous saviez comment je m'appelle? - ---Oui, répondit l'évêque, vous vous appelez mon frère. - ---Tenez, monsieur le curé! s'écria l'homme, j'avais bien faim en entrant -ici; mais vous êtes si bon qu'à présent je ne sais plus ce que j'ai; -cela m'a passé. - -L'évêque le regarda et lui dit: - ---Vous avez bien souffert? - ---Oh! la casaque rouge, le boulet au pied, une planche pour dormir, le -chaud, le froid, le travail, la chiourme, les coups de bâton! La double -chaîne pour rien. Le cachot pour un mot. Même malade au lit, la chaîne. -Les chiens, les chiens sont plus heureux! Dix-neuf ans! J'en ai -quarante-six. À présent, le passeport jaune! Voilà. - ---Oui, reprit l'évêque, vous sortez d'un lieu de tristesse. Écoutez. Il -y aura plus de joie au ciel pour le visage en larmes d'un pécheur -repentant que pour la robe blanche de cent justes. Si vous sortez de ce -lieu douloureux avec des pensées de haine et de colère contre les -hommes, vous êtes digne de pitié; si vous en sortez avec des pensées de -bienveillance, de douceur et de paix, vous valez mieux qu'aucun de nous. - -Cependant madame Magloire avait servi le souper. Une soupe faite avec de -l'eau, de l'huile, du pain et du sel, un peu de lard, un morceau de -viande de mouton, des figues, un fromage frais, et un gros pain de -seigle. Elle avait d'elle-même ajouté à l'ordinaire de M. l'évêque une -bouteille de vieux vin de Mauves. - -Le visage de l'évêque prit tout à coup cette expression de gaîté propre -aux natures hospitalières: - ---À table! dit-il vivement. - -Comme il en avait coutume lorsque quelque étranger soupait avec lui, il -fit asseoir l'homme à sa droite. Mademoiselle Baptistine, parfaitement -paisible et naturelle, prit place à sa gauche. - -L'évêque dit le bénédicité, puis servit lui-même la soupe, selon son -habitude. L'homme se mit à manger avidement. - -Tout à coup l'évêque dit: - ---Mais il me semble qu'il manque quelque chose sur cette table. - -Madame Magloire en effet n'avait mis que les trois couverts absolument -nécessaires. Or c'était l'usage de la maison, quand l'évêque avait -quelqu'un à souper, de disposer sur la nappe les six couverts d'argent, -étalage innocent. Ce gracieux semblant de luxe était une sorte -d'enfantillage plein de charme dans cette maison douce et sévère qui -élevait la pauvreté jusqu'à la dignité. - -Madame Magloire comprit l'observation, sortit sans dire un mot, et un -moment après les trois couverts réclamés par l'évêque brillaient sur la -nappe, symétriquement arrangés devant chacun des trois convives. - - - - -Chapitre IV - -Détails sur les fromageries de Pontarlier - - -Maintenant, pour donner une idée de ce qui se passa à cette table, nous -ne saurions mieux faire que de transcrire ici un passage d'une lettre de -mademoiselle Baptistine à madame de Boischevron, où la conversation du -forçat et de l'évêque est racontée avec une minutie naïve: - - * * * * * - -«...Cet homme ne faisait aucune attention à personne. Il mangeait avec -une voracité d'affamé. Cependant, après la soupe, il a dit: - -«--Monsieur le curé du bon Dieu, tout ceci est encore bien trop bon pour -moi, mais je dois dire que les rouliers qui n'ont pas voulu me laisser -manger avec eux font meilleure chère que vous. - -«Entre nous, l'observation m'a un peu choquée. Mon frère a répondu: - -«--Ils ont plus de fatigue que moi. - -«--Non, a repris cet homme, ils ont plus d'argent. Vous êtes pauvre. Je -vois bien. Vous n'êtes peut-être pas même curé. Êtes-vous curé -seulement? Ah! par exemple, si le bon Dieu était juste, vous devriez -bien être curé. - -«--Le bon Dieu est plus que juste, a dit mon frère. - -«Un moment après il a ajouté: - -«--Monsieur Jean Valjean, c'est à Pontarlier que vous allez? - -«--Avec itinéraire obligé. - -«Je crois bien que c'est comme cela que l'homme a dit. Puis il a -continué: - -«--Il faut que je sois en route demain à la pointe du jour. Il fait dur -voyager. Si les nuits sont froides, les journées sont chaudes. - -«--Vous allez là, a repris mon frère, dans un bon pays. À la révolution, -ma famille a été ruinée, je me suis réfugié en Franche-Comté d'abord, et -j'y ai vécu quelque temps du travail de mes bras. J'avais de la bonne -volonté. J'ai trouvé à m'y occuper. On n'a qu'à choisir. Il y a des -papeteries, des tanneries, des distilleries, des huileries, des -fabriques d'horlogerie en grand, des fabriques d'acier, des fabriques de -cuivre, au moins vingt usines de fer, dont quatre à Lods, à Châtillon, à -Audincourt et à Beure qui sont très considérables.... - -«Je crois ne pas me tromper et que ce sont bien là les noms que mon -frère a cités, puis il s'est interrompu et m'a adressé la parole: - -«--Chère soeur, n'avons-nous pas des parents dans ce pays-là? - -«J'ai répondu: - -«--Nous en avions, entre autres M. de Lucenet qui était capitaine des -portes à Pontarlier dans l'ancien régime. - -«--Oui, a repris mon frère, mais en 93 on n'avait plus de parents, on -n'avait que ses bras. J'ai travaillé. Ils ont dans le pays de -Pontarlier, où vous allez, monsieur Valjean, une industrie toute -patriarcale et toute charmante, ma soeur. Ce sont leurs fromageries -qu'ils appellent fruitières. - -«Alors mon frère, tout en faisant manger cet homme, lui a expliqué très -en détail ce que c'étaient que les fruitières de Pontarlier;--qu'on en -distinguait deux sortes:--les _grosses granges_, qui sont aux riches, et -où il y a quarante ou cinquante vaches, lesquelles produisent sept à -huit milliers de fromages par été; les _fruitières d'association_, qui -sont aux pauvres; ce sont les paysans de la moyenne montagne qui mettent -leurs vaches en commun et partagent les produits.--Ils prennent à leurs -gages un fromager qu'ils appellent le grurin;--le grurin reçoit le lait -des associés trois fois par jour et marque les quantités sur une taille -double;--c'est vers la fin d'avril que le travail des fromageries -commence; c'est vers la mi-juin que les fromagers conduisent leurs -vaches dans la montagne. - -«L'homme se ranimait tout en mangeant. Mon frère lui faisait boire de ce -bon vin de Mauves dont il ne boit pas lui-même parce qu'il dit que c'est -du vin cher. Mon frère lui disait tous ces détails avec cette gaîté -aisée que vous lui connaissez, entremêlant ses paroles de façons -gracieuses pour moi. Il est beaucoup revenu sur ce bon état de grurin, -comme s'il eût souhaité que cet homme comprît, sans le lui conseiller -directement et durement, que ce serait un asile pour lui. Une chose m'a -frappée. Cet homme était ce que je vous ai dit. Eh bien! mon frère, -pendant tout le souper, ni de toute la soirée, à l'exception de quelques -paroles sur Jésus quand il est entré, n'a pas dit un mot qui pût -rappeler à cet homme qui il était ni apprendre à cet homme qui était mon -frère. C'était bien une occasion en apparence de faire un peu de sermon -et d'appuyer l'évêque sur le galérien pour laisser la marque du passage. -Il eût paru peut-être à un autre que c'était le cas, ayant ce malheureux -sous la main, de lui nourrir l'âme en même temps que le corps et de lui -faire quelque reproche assaisonné de morale et de conseil, ou bien un -peu de commisération avec exhortation de se mieux conduire à l'avenir. -Mon frère ne lui a même pas demandé de quel pays il était, ni son -histoire. Car dans son histoire il y a sa faute, et mon frère semblait -éviter tout ce qui pouvait l'en faire souvenir. C'est au point qu'à un -certain moment, comme mon frère parlait des montagnards de Pontarlier, -qui ont _un doux travail près du ciel et qui_, ajoutait-il, _sont -heureux parce qu'ils sont innocents_, il s'est arrêté court, craignant -qu'il n'y eût dans ce mot qui lui échappait quelque chose qui pût -froisser l'homme. À force d'y réfléchir, je crois avoir compris ce qui -se passait dans le coeur de mon frère. Il pensait sans doute que cet -homme, qui s'appelle Jean Valjean, n'avait que trop sa misère présente à -l'esprit, que le mieux était de l'en distraire, et de lui faire croire, -ne fût-ce qu'un moment, qu'il était une personne comme une autre, en -étant pour lui tout ordinaire. N'est-ce pas là en effet bien entendre la -charité? N'y a-t-il pas, bonne madame, quelque chose de vraiment -évangélique dans cette délicatesse qui s'abstient de sermon, de morale -et d'allusion, et la meilleure pitié, quand un homme a un point -douloureux, n'est-ce pas de n'y point toucher du tout? Il m'a semblé que -ce pouvait être là la pensée intérieure de mon frère. Dans tous les cas, -ce que je puis dire, c'est que, s'il a eu toutes ces idées, il n'en a -rien marqué, même pour moi; il a été d'un bout à l'autre le même homme -que tous les soirs, et il a soupé avec ce Jean Valjean du même air et de -la même façon qu'il aurait soupé avec M. Gédéon Le Prévost ou avec M. le -curé de la paroisse. - -«Vers la fin, comme nous étions aux figues, on a cogné à la porte. -C'était la mère Gerbaud avec son petit dans ses bras. Mon frère a baisé -l'enfant au front, et m'a emprunté quinze sous que j'avais sur moi pour -les donner à la mère Gerbaud. L'homme pendant ce temps-là ne faisait pas -grande attention. Il ne parlait plus et paraissait très fatigué. La -pauvre vieille Gerbaud partie, mon frère a dit les grâces, puis il s'est -tourné vers cet homme, et il lui a dit: Vous devez avoir bien besoin de -votre lit. Madame Magloire a enlevé le couvert bien vite. J'ai compris -qu'il fallait nous retirer pour laisser dormir ce voyageur, et nous -sommes montées toutes les deux. J'ai cependant envoyé madame Magloire un -instant après porter sur le lit de cet homme une peau de chevreuil de la -Forêt-Noire qui est dans ma chambre. Les nuits sont glaciales, et cela -tient chaud. C'est dommage que cette peau soit vieille; tout le poil -s'en va. Mon frère l'a achetée du temps qu'il était en Allemagne, à -Tottlingen, près des sources du Danube, ainsi que le petit couteau à -manche d'ivoire dont je me sers à table. - -«Madame Magloire est remontée presque tout de suite, nous nous sommes -mises à prier Dieu dans le salon où l'on étend le linge, et puis nous -sommes rentrées chacune dans notre chambre sans nous rien dire.» - - - - -Chapitre V - -Tranquillité - - -Après avoir donné le bonsoir à sa soeur, monseigneur Bienvenu prit sur -la table un des deux flambeaux d'argent, remit l'autre à son hôte, et -lui dit: - ---Monsieur, je vais vous conduire à votre chambre. - -L'homme le suivit. - -Comme on a pu le remarquer dans ce qui a été dit plus haut, le logis -était distribué de telle sorte que, pour passer dans l'oratoire où était -l'alcôve ou pour en sortir, il fallait traverser la chambre à coucher de -l'évêque. - -Au moment où ils traversaient cette chambre, madame Magloire serrait -l'argenterie dans le placard qui était au chevet du lit. C'était le -dernier soin qu'elle prenait chaque soir avant de s'aller coucher. - -L'évêque installa son hôte dans l'alcôve. Un lit blanc et frais y était -dressé. L'homme posa le flambeau sur une petite table. - ---Allons, dit l'évêque, faites une bonne nuit. Demain matin, avant de -partir, vous boirez une tasse de lait de nos vaches tout chaud. - ---Merci, monsieur l'abbé, dit l'homme. - -À peine eut-il prononcé ces paroles pleines de paix que, tout à coup et -sans transition, il eut un mouvement étrange et qui eût glacé -d'épouvante les deux saintes filles si elles en eussent été témoins. -Aujourd'hui même il nous est difficile de nous rendre compte de ce qui -le poussait en ce moment. Voulait-il donner un avertissement ou jeter -une menace? Obéissait-il simplement à une sorte d'impulsion instinctive -et obscure pour lui-même? Il se tourna brusquement vers le vieillard, -croisa les bras, et, fixant sur son hôte un regard sauvage, il s'écria -d'une voix rauque: - ---Ah çà! décidément! vous me logez chez vous près de vous comme cela! - -Il s'interrompit et ajouta avec un rire où il y avait quelque chose de -monstrueux: - ---Avez-vous bien fait toutes vos réflexions? Qui est-ce qui vous dit que -je n'ai pas assassiné? - -L'évêque leva les yeux vers le plafond et répondit: - ---Cela regarde le bon Dieu. - -Puis, gravement et remuant les lèvres comme quelqu'un qui prie ou qui se -parle à lui-même, il dressa les deux doigts de sa main droite et bénit -l'homme qui ne se courba pas, et, sans tourner la tête et sans regarder -derrière lui, il rentra dans sa chambre. - -Quand l'alcôve était habitée, un grand rideau de serge tiré de part en -part dans l'oratoire cachait l'autel. L'évêque s'agenouilla en passant -devant ce rideau et fit une courte prière. - -Un moment après, il était dans son jardin, marchant, rêvant, -contemplant, l'âme et la pensée tout entières à ces grandes choses -mystérieuses que Dieu montre la nuit aux yeux qui restent ouverts. - -Quant à l'homme, il était vraiment si fatigué qu'il n'avait même pas -profité de ces bons draps blancs. Il avait soufflé sa bougie avec sa -narine à la manière des forçats et s'était laissé tomber tout habillé -sur le lit, où il s'était tout de suite profondément endormi. - -Minuit sonnait comme l'évêque rentrait de son jardin dans son -appartement. - -Quelques minutes après, tout dormait dans la petite maison. - - - - -Chapitre VI - -Jean Valjean - - -Vers le milieu de la nuit, Jean Valjean se réveilla. - -Jean Valjean était d'une pauvre famille de paysans de la Brie. Dans son -enfance, il n'avait pas appris à lire. Quand il eut l'âge d'homme, il -était émondeur à Faverolles. Sa mère s'appelait Jeanne Mathieu; son père -s'appelait Jean Valjean, ou Vlajean, sobriquet probablement, et -contraction de _Voilà Jean_. - -Jean Valjean était d'un caractère pensif sans être triste, ce qui est le -propre des natures affectueuses. Somme toute, pourtant, c'était quelque -chose d'assez endormi et d'assez insignifiant, en apparence du moins, -que Jean Valjean. Il avait perdu en très bas âge son père et sa mère. Sa -mère était morte d'une fièvre de lait mal soignée. Son père, émondeur -comme lui, s'était tué en tombant d'un arbre. Il n'était resté à Jean -Valjean qu'une soeur plus âgée que lui, veuve, avec sept enfants, filles -et garçons. Cette soeur avait élevé Jean Valjean, et tant qu'elle eut -son mari elle logea et nourrit son jeune frère. Le mari mourut. L'aîné -des sept enfants avait huit ans, le dernier un an. Jean Valjean venait -d'atteindre, lui, sa vingt-cinquième année. Il remplaça le père, et -soutint à son tour sa soeur qui l'avait élevé. Cela se fit simplement, -comme un devoir, même avec quelque chose de bourru de la part de Jean -Valjean. Sa jeunesse se dépensait ainsi dans un travail rude et mal -payé. On ne lui avait jamais connu de «bonne amie» dans le pays. Il -n'avait pas eu le temps d'être amoureux. - -Le soir il rentrait fatigué et mangeait sa soupe sans dire un mot. Sa -soeur, mère Jeanne, pendant qu'il mangeait, lui prenait souvent dans son -écuelle le meilleur de son repas, le morceau de viande, la tranche de -lard le coeur de chou, pour le donner à quelqu'un de ses enfants; lui, -mangeant toujours, penché sur la table, presque la tête dans sa soupe, -ses longs cheveux tombant autour de son écuelle et cachant ses yeux, -avait l'air de ne rien voir et laissait faire. Il y avait à Faverolles, -pas loin de la chaumière Valjean, de l'autre côté de la ruelle, une -fermière appelée Marie-Claude; les enfants Valjean, habituellement -affamés, allaient quelquefois emprunter au nom de leur mère une pinte de -lait à Marie-Claude, qu'ils buvaient derrière une haie ou dans quelque -coin d'allée, s'arrachant le pot, et si hâtivement que les petites -filles s'en répandaient sur leur tablier et dans leur goulotte. La mère, -si elle eût su cette maraude, eût sévèrement corrigé les délinquants. -Jean Valjean, brusque et bougon, payait en arrière de la mère la pinte -de lait à Marie-Claude, et les enfants n'étaient pas punis. - -Il gagnait dans la saison de l'émondage vingt-quatre sous par jour, puis -il se louait comme moissonneur, comme manoeuvre, comme garçon de ferme -bouvier, comme homme de peine. Il faisait ce qu'il pouvait. Sa soeur -travaillait de son côté, mais que faire avec sept petits enfants? -C'était un triste groupe que la misère enveloppa et étreignit peu à peu. -Il arriva qu'un hiver fut rude. Jean n'eut pas d'ouvrage. La famille -n'eut pas de pain. Pas de pain. À la lettre. Sept enfants! Un dimanche -soir, Maubert Isabeau, boulanger sur la place de l'Église, à Faverolles, -se disposait à se coucher, lorsqu'il entendit un coup violent dans la -devanture grillée et vitrée de sa boutique. Il arriva à temps pour voir -un bras passé à travers un trou fait d'un coup de poing dans la grille -et dans la vitre. Le bras saisit un pain et l'emporta. Isabeau sortit en -hâte; le voleur s'enfuyait à toutes jambes; Isabeau courut après lui et -l'arrêta. Le voleur avait jeté le pain, mais il avait encore le bras -ensanglanté. C'était Jean Valjean. - -Ceci se passait en 1795. Jean Valjean fut traduit devant les tribunaux -du temps «pour vol avec effraction la nuit dans une maison habitée». Il -avait un fusil dont il se servait mieux que tireur au monde, il était -quelque peu braconnier; ce qui lui nuisit. Il y a contre les braconniers -un préjugé légitime. Le braconnier, de même que le contrebandier, côtoie -de fort près le brigand. Pourtant, disons-le en passant, il y a encore -un abîme entre ces races d'hommes et le hideux assassin des villes. Le -braconnier vit dans la forêt; le contrebandier vit dans la montagne ou -sur la mer. Les villes font des hommes féroces parce qu'elles font des -hommes corrompus. La montagne, la mer, la forêt, font des hommes -sauvages. Elles développent le côté farouche, mais souvent sans détruire -le côté humain. - -Jean Valjean fut déclaré coupable. Les termes du code étaient formels. -Il y a dans notre civilisation des heures redoutables; ce sont les -moments où la pénalité prononce un naufrage. Quelle minute funèbre que -celle où la société s'éloigne et consomme l'irréparable abandon d'un -être pensant! Jean Valjean fut condamné à cinq ans de galères. - -Le 22 avril 1796, on cria dans Paris la victoire de Montenotte remportée -par le général en chef de l'année d'Italie, que le message du Directoire -aux Cinq-Cents, du 2 floréal an IV, appelle Buona-Parte; ce même jour -une grande chaîne fut ferrée à Bicêtre. Jean Valjean fit partie de cette -chaîne. Un ancien guichetier de la prison, qui a près de -quatre-vingt-dix ans aujourd'hui, se souvient encore parfaitement de ce -malheureux qui fut ferré à l'extrémité du quatrième cordon dans l'angle -nord de la cour. Il était assis à terre comme tous les autres. Il -paraissait ne rien comprendre à sa position, sinon qu'elle était -horrible. Il est probable qu'il y démêlait aussi, à travers les vagues -idées d'un pauvre homme ignorant de tout, quelque chose d'excessif. -Pendant qu'on rivait à grands coups de marteau derrière sa tête le -boulon de son carcan, il pleurait, les larmes l'étouffaient, elles -l'empêchaient de parler, il parvenait seulement à dire de temps en -temps: _J'étais émondeur à Faverolles_. Puis, tout en sanglotant, il -élevait sa main droite et l'abaissait graduellement sept fois comme s'il -touchait successivement sept têtes inégales, et par ce geste on devinait -que la chose quelconque qu'il avait faite, il l'avait faite pour vêtir -et nourrir sept petits enfants. - -Il partit pour Toulon. Il y arriva après un voyage de vingt-sept jours, -sur une charrette, la chaîne au cou. À Toulon, il fut revêtu de la -casaque rouge. Tout s'effaça de ce qui avait été sa vie, jusqu'à son -nom; il ne fut même plus Jean Valjean; il fut le numéro 24601. Que -devint la soeur? que devinrent les sept enfants? Qui est-ce qui s'occupe -de cela? Que devient la poignée de feuilles du jeune arbre scié par le -pied? - -C'est toujours la même histoire. Ces pauvres êtres vivants, ces -créatures de Dieu, sans appui désormais, sans guide, sans asile, s'en -allèrent au hasard, qui sait même? chacun de leur côté peut-être, et -s'enfoncèrent peu à peu dans cette froide brume où s'engloutissent les -destinées solitaires, moines ténèbres où disparaissent successivement -tant de têtes infortunées dans la sombre marche du genre humain. Ils -quittèrent le pays. Le clocher de ce qui avait été leur village les -oublia; la borne de ce qui avait été leur champ les oublia; après -quelques années de séjour au bagne, Jean Valjean lui-même les oublia. -Dans ce coeur où il y avait eu une plaie, il y eut une cicatrice. Voilà -tout. À peine, pendant tout le temps qu'il passa à Toulon, entendit-il -parler une seule fois de sa soeur. C'était, je crois, vers la fin de la -quatrième année de sa captivité. Je ne sais plus par quelle voie ce -renseignement lui parvint. Quelqu'un, qui les avait connus au pays, -avait vu sa soeur. Elle était à Paris. Elle habitait une pauvre rue près -de Saint-Sulpice, la rue du Geindre. Elle n'avait plus avec elle qu'un -enfant, un petit garçon, le dernier. Où étaient les six autres? Elle ne -le savait peut-être pas elle-même. Tous les matins elle allait à une -imprimerie rue du Sabot, n° 3, où elle était plieuse et brocheuse. Il -fallait être là à six heures du matin, bien avant le jour l'hiver. Dans -la maison de l'imprimerie il y avait une école, elle menait à cette -école son petit garçon qui avait sept ans. Seulement, comme elle entrait -à l'imprimerie à six heures et que l'école n'ouvrait qu'à sept, il -fallait que l'enfant attendît, dans la cour, que l'école ouvrit, une -heure; l'hiver, une heure de nuit, en plein air. On ne voulait pas que -l'enfant entrât dans l'imprimerie, parce qu'il gênait, disait-on. Les -ouvriers voyaient le matin en passant ce pauvre petit être assis sur le -pavé, tombant de sommeil, et souvent endormi dans l'ombre, accroupi et -plié sur son panier. Quand il pleuvait, une vieille femme, la portière, -en avait pitié; elle le recueillait dans son bouge où il n'y avait qu'un -grabat, un rouet et deux chaises de bois, et le petit dormait là dans un -coin, se serrant contre le chat pour avoir moins froid. À sept heures, -l'école ouvrait et il y entrait. Voilà ce qu'on dit à Jean Valjean. On -l'en entretint un jour, ce fut un moment, un éclair, comme une fenêtre -brusquement ouverte sur la destinée de ces êtres qu'il avait aimés, puis -tout se referma; il n'en entendit plus parler, et ce fut pour jamais. -Plus rien n'arriva d'eux à lui; jamais il ne les revit, jamais il ne les -rencontra, et, dans la suite de cette douloureuse histoire, on ne les -retrouvera plus. - -Vers la fin de cette quatrième année, le tour d'évasion de Jean Valjean -arriva. Ses camarades l'aidèrent comme cela se fait dans ce triste lieu. -Il s'évada. Il erra deux jours en liberté dans les champs; si c'est être -libre que d'être traqué; de tourner la tête à chaque instant; de -tressaillir au moindre bruit; d'avoir peur de tout, du toit qui fume, de -l'homme qui passe, du chien qui aboie, du cheval qui galope, de l'heure -qui sonne, du jour parce qu'on voit, de la nuit parce qu'on ne voit pas, -de la route, du sentier, du buisson, du sommeil. Le soir du second jour, -il fut repris. Il n'avait ni mangé ni dormi depuis trente-six heures. Le -tribunal maritime le condamna pour ce délit à une prolongation de trois -ans, ce qui lui fit huit ans. La sixième année, ce fut encore son tour -de s'évader; il en usa, mais il ne put consommer sa fuite. Il avait -manqué à l'appel. On tira le coup de canon, et à la nuit les gens de -ronde le trouvèrent caché sous la quille d'un vaisseau en construction; -il résista aux gardes-chiourme qui le saisirent. Évasion et rébellion. -Ce fait prévu par le code spécial fut puni d'une aggravation de cinq -ans, dont deux ans de double chaîne. Treize ans. La dixième année, son -tour revint, il en profita encore. Il ne réussit pas mieux. Trois ans -pour cette nouvelle tentative. Seize ans. Enfin, ce fut, je crois, -pendant la treizième année qu'il essaya une dernière fois et ne réussit -qu'à se faire reprendre après quatre heures d'absence. Trois ans pour -ces quatre heures. Dix-neuf ans. En octobre 1815 il fut libéré; il était -entré là en 1796 pour avoir cassé un carreau et pris un pain. - -Place pour une courte parenthèse. C'est la seconde fois que, dans ses -études sur la question pénale et sur la damnation par la loi, l'auteur -de ce livre rencontre le vol d'un pain, comme point de départ du -désastre d'une destinée. Claude Gueux avait volé un pain; Jean Valjean -avait volé un pain. Une statistique anglaise constate qu'à Londres -quatre vols sur cinq ont pour cause immédiate la faim. - -Jean Valjean était entré au bagne sanglotant et frémissant; il en sortit -impassible. Il y était entré désespéré; il en sortit sombre. - -Que s'était-il passé dans cette âme? - - - - -Chapitre VII - -Le dedans du désespoir - - -Essayons de le dire. - -Il faut bien que la société regarde ces choses puisque c'est elle qui -les fait. - -C'était, nous l'avons dit, un ignorant; mais ce n'était pas un imbécile. -La lumière naturelle était allumée en lui. Le malheur, qui a aussi sa -clarté, augmenta le peu de jour qu'il y avait dans cet esprit. Sous le -bâton, sous la chaîne, au cachot, à la fatigue, sous l'ardent soleil du -bagne, sur le lit de planches des forçats, il se replia en sa conscience -et réfléchit. - -Il se constitua tribunal. - -Il commença par se juger lui-même. - -Il reconnut qu'il n'était pas un innocent injustement puni. Il s'avoua -qu'il avait commis une action extrême et blâmable; qu'on ne lui eût -peut-être pas refusé ce pain s'il l'avait demandé; que dans tous les cas -il eût mieux valu l'attendre, soit de la pitié, soit du travail; que ce -n'est pas tout à fait une raison sans réplique de dire: peut-on attendre -quand on a faim? que d'abord il est très rare qu'on meure littéralement -de faim; ensuite que, malheureusement ou heureusement, l'homme est ainsi -fait qu'il peut souffrir longtemps et beaucoup, moralement et -physiquement, sans mourir; qu'il fallait donc de la patience; que cela -eût mieux valu même pour ces pauvres petits enfants; que c'était un acte -de folie, à lui, malheureux homme chétif, de prendre violemment au -collet la société tout entière et de se figurer qu'on sort de la misère -par le vol; que c'était, dans tous les cas, une mauvaise porte pour -sortir de la misère que celle par où l'on entre dans l'infamie; enfin -qu'il avait eu tort. - -Puis il se demanda: - -S'il était le seul qui avait eu tort dans sa fatale histoire? Si d'abord -ce n'était pas une chose grave qu'il eût, lui travailleur, manqué de -travail, lui laborieux, manqué de pain. Si, ensuite, la faute commise et -avouée, le châtiment n'avait pas été féroce et outré. S'il n'y avait pas -plus d'abus de la part de la loi dans la peine qu'il n'y avait eu d'abus -de la part du coupable dans la faute. S'il n'y avait pas excès de poids -dans un des plateaux de la balance, celui où est l'expiation. Si la -surcharge de la peine n'était point l'effacement du délit, et n'arrivait -pas à ce résultat: de retourner la situation, de remplacer la faute du -délinquant par la faute de la répression, de faire du coupable la -victime et du débiteur le créancier, et de mettre définitivement le -droit du côté de celui-là même qui l'avait violé. Si cette peine, -compliquée des aggravations successives pour les tentatives d'évasion, -ne finissait pas par être une sorte d'attentat du plus fort sur le plus -faible, un crime de la société sur l'individu, un crime qui recommençait -tous les jours, un crime qui durait dix-neuf ans. - -Il se demanda si la société humaine pouvait avoir le droit de faire -également subir à ses membres, dans un cas son imprévoyance -déraisonnable, et dans l'autre cas sa prévoyance impitoyable, et de -saisir à jamais un pauvre homme entre un défaut et un excès, défaut de -travail, excès de châtiment. S'il n'était pas exorbitant que la société -traitât ainsi précisément ses membres les plus mal dotés dans la -répartition de biens que fait le hasard, et par conséquent les plus -dignes de ménagements. - -Ces questions faites et résolues, il jugea la société et la condamna. - -Il la condamna sans haine. - -Il la fit responsable du sort qu'il subissait, et se dit qu'il -n'hésiterait peut-être pas à lui en demander compte un jour. Il se -déclara à lui-même qu'il n'y avait pas équilibre entre le dommage qu'il -avait causé et le dommage qu'on lui causait; il conclut enfin que son -châtiment n'était pas, à la vérité, une injustice, mais qu'à coup sûr -c'était une iniquité. - -La colère peut être folle et absurde; on peut être irrité à tort; on -n'est indigné que lorsqu'on a raison au fond par quelque côté. Jean -Valjean se sentait indigné. Et puis, la société humaine ne lui avait -fait que du mal. Jamais il n'avait vu d'elle que ce visage courroucé -qu'elle appelle sa justice et qu'elle montre à ceux qu'elle frappe. Les -hommes ne l'avaient touché que pour le meurtrir. Tout contact avec eux -lui avait été un coup. Jamais, depuis son enfance, depuis sa mère, -depuis sa soeur, jamais il n'avait rencontré une parole amie et un -regard bienveillant. De souffrance en souffrance il arriva peu à peu à -cette conviction que la vie était une guerre; et que dans cette guerre -il était le vaincu. Il n'avait d'autre arme que sa haine. Il résolut de -l'aiguiser au bagne et de l'emporter en s'en allant. - -Il y avait à Toulon une école pour la chiourme tenue par des frères -ignorantins où l'on enseignait le plus nécessaire à ceux de ces -malheureux qui avaient de la bonne volonté. Il fut du nombre des hommes -de bonne volonté. Il alla à l'école à quarante ans, et apprit à lire, à -écrire, à compter. Il sentit que fortifier son intelligence, c'était -fortifier sa haine. Dans certains cas, l'instruction et la lumière -peuvent servir de rallonge au mal. - -Cela est triste à dire, après avoir jugé la société qui avait fait son -malheur, il jugea la providence qui avait fait la société. - -Il la condamna aussi. - -Ainsi, pendant ces dix-neuf ans de torture et d'esclavage, cette âme -monta et tomba en même temps. Il y entra de la lumière d'un côté et des -ténèbres de l'autre. - -Jean Valjean n'était pas, on l'a vu, d'une nature mauvaise. Il était -encore bon lorsqu'il arriva au bagne. Il y condamna la société et sentit -qu'il devenait méchant, il y condamna la providence et sentit qu'il -devenait impie. - -Ici il est difficile de ne pas méditer un instant. - -La nature humaine se transforme-t-elle ainsi de fond en comble et tout à -fait? L'homme créé bon par Dieu peut-il être fait méchant par l'homme? -L'âme peut-elle être refaite tout d'une pièce par la destinée, et -devenir mauvaise, la destinée étant mauvaise? Le coeur peut-il devenir -difforme et contracter des laideurs et des infirmités incurables sous la -pression d'un malheur disproportionné, comme la colonne vertébrale sous -une voûte trop basse? N'y a-t-il pas dans toute âme humaine, n'y -avait-il pas dans l'âme de Jean Valjean en particulier, une première -étincelle, un élément divin, incorruptible dans ce monde, immortel dans -l'autre, que le bien peut développer, attiser, allumer, enflammer et -faire rayonner splendidement, et que le mal ne peut jamais entièrement -éteindre? - -Questions graves et obscures, à la dernière desquelles tout -physiologiste eût probablement répondu non, et sans hésiter, s'il eût vu -à Toulon, aux heures de repos qui étaient pour Jean Valjean des heures -de rêverie, assis, les bras croisés, sur la barre de quelque cabestan, -le bout de sa chaîne enfoncé dans sa poche pour l'empêcher de traîner, -ce galérien morne, sérieux, silencieux et pensif, paria des lois qui -regardait l'homme avec colère, damné de la civilisation qui regardait le -ciel avec sévérité. - -Certes, et nous ne voulons pas le dissimuler, le physiologiste -observateur eût vu là une misère irrémédiable, il eût plaint peut-être -ce malade du fait de la loi, mais il n'eût pas même essayé de -traitement; il eût détourné le regard des cavernes qu'il aurait -entrevues dans cette âme; et, comme Dante de la porte de l'enfer, il eût -effacé de cette existence le mot que le doigt de Dieu écrit pourtant sur -le front de tout homme: _Espérance_! - -Cet état de son âme que nous avons tenté d'analyser était-il aussi -parfaitement clair pour Jean Valjean que nous avons essayé de le rendre -pour ceux qui nous lisent? Jean Valjean voyait-il distinctement, après -leur formation, et avait-il vu distinctement, à mesure qu'ils se -formaient, tous les éléments dont se composait sa misère morale? Cet -homme rude et illettré s'était-il bien nettement rendu compte de la -succession d'idées par laquelle il était, degré à degré, monté et -descendu jusqu'aux lugubres aspects qui étaient depuis tant d'années -déjà l'horizon intérieur de son esprit? Avait-il bien conscience de tout -ce qui s'était passé en lui et de tout ce qui s'y remuait? C'est ce que -nous n'oserions dire; c'est même ce que nous ne croyons pas. Il y avait -trop d'ignorance dans Jean Valjean pour que, même après tant de malheur, -il n'y restât pas beaucoup de vague. Par moments il ne savait pas même -bien au juste ce qu'il éprouvait. Jean Valjean était dans les ténèbres; -il souffrait dans les ténèbres; il haïssait dans les ténèbres; on eût pu -dire qu'il haïssait devant lui. Il vivait habituellement dans cette -ombre, tâtonnant comme un aveugle et comme un rêveur. Seulement, par -intervalles, il lui venait tout à coup, de lui-même ou du dehors, une -secousse de colère, un surcroît de souffrance, un pâle et rapide éclair -qui illuminait toute son âme, et faisait brusquement apparaître partout -autour de lui, en avant et en arrière, aux lueurs d'une lumière -affreuse, les hideux précipices et les sombres perspectives de sa -destinée. - -L'éclair passé, la nuit retombait, et où était-il? il ne le savait plus. - -Le propre des peines de cette nature, dans lesquelles domine ce qui est -impitoyable, c'est-à-dire ce qui est abrutissant, c'est de transformer -peu à peu, par une sorte de transfiguration stupide, un homme en une -bête fauve. Quelquefois en une bête féroce. Les tentatives d'évasion de -Jean Valjean, successives et obstinées, suffiraient à prouver cet -étrange travail fait par la loi sur l'âme humaine. Jean Valjean eût -renouvelé ces tentatives, si parfaitement inutiles et folles, autant de -fois que l'occasion s'en fût présentée, sans songer un instant au -résultat, ni aux expériences déjà faites. Il s'échappait impétueusement -comme le loup qui trouve la cage ouverte. L'instinct lui disait: -sauve-toi! Le raisonnement lui eût dit: reste! Mais, devant une -tentation si violente, le raisonnement avait disparu; il n'y avait plus -que l'instinct. La bête seule agissait. Quand il était repris, les -nouvelles sévérités qu'on lui infligeait ne servaient qu'à l'effarer -davantage. - -Un détail que nous ne devons pas omettre, c'est qu'il était d'une force -physique dont n'approchait pas un des habitants du bagne. À la fatigue, -pour filer un câble, pour virer un cabestan, Jean Valjean valait quatre -hommes. Il soulevait et soutenait parfois d'énormes poids sur son dos, -et remplaçait dans l'occasion cet instrument qu'on appelle cric et qu'on -appelait jadis orgueil, d'où a pris nom, soit dit en passant, la rue -Montorgueil près des halles de Paris. Ses camarades l'avaient surnommé -Jean-le-Cric. Une fois, comme on réparait le balcon de l'hôtel de ville -de Toulon, une des admirables cariatides de Puget qui soutiennent ce -balcon se descella et faillit tomber. Jean Valjean, qui se trouvait là, -soutint de l'épaule la cariatide et donna le temps aux ouvriers -d'arriver. - -Sa souplesse dépassait encore sa vigueur. Certains forçats, rêveurs -perpétuels d'évasions, finissent par faire de la force et de l'adresse -combinées une véritable science. C'est la science des muscles. Toute une -statique mystérieuse est quotidiennement pratiquée par les prisonniers, -ces éternels envieux des mouches et des oiseaux. Gravir une verticale, -et trouver des points d'appui là où l'on voit à peine une saillie, était -un jeu pour Jean Valjean. Étant donné un angle de mur, avec la tension -de son dos et de ses jarrets, avec ses coudes et ses talons emboîtés -dans les aspérités de la pierre, il se hissait comme magiquement à un -troisième étage. Quelquefois il montait ainsi jusqu'au toit du bagne. - -Il parlait peu. Il ne riait pas. Il fallait quelque émotion extrême pour -lui arracher, une ou deux fois l'an, ce lugubre rire du forçat qui est -comme un écho du rire du démon. À le voir, il semblait occupé à regarder -continuellement quelque chose de terrible. - -Il était absorbé en effet. - -À travers les perceptions maladives d'une nature incomplète et d'une -intelligence accablée, il sentait confusément qu'une chose monstrueuse -était sur lui. Dans cette pénombre obscure et blafarde où il rampait, -chaque fois qu'il tournait le cou et qu'il essayait d'élever son regard, -il voyait, avec une terreur mêlée de rage, s'échafauder, s'étager et -monter à perte de vue au-dessus de lui, avec des escarpements horribles, -une sorte d'entassement effrayant de choses, de lois, de préjugés, -d'hommes et de faits, dont les contours lui échappaient, dont la masse -l'épouvantait, et qui n'était autre chose que cette prodigieuse pyramide -que nous appelons la civilisation. Il distinguait çà et là dans cet -ensemble fourmillant et difforme, tantôt près de lui, tantôt loin et sur -des plateaux inaccessibles, quelque groupe, quelque détail vivement -éclairé, ici l'argousin et son bâton, ici le gendarme et son sabre, -là-bas l'archevêque mitré, tout en haut, dans une sorte de soleil, -l'empereur couronné et éblouissant. Il lui semblait que ces splendeurs -lointaines, loin de dissiper sa nuit, la rendaient plus funèbre et plus -noire. Tout cela, lois, préjugés, faits, hommes, choses, allait et -venait au-dessus de lui, selon le mouvement compliqué et mystérieux que -Dieu imprime à la civilisation, marchant sur lui et l'écrasant avec je -ne sais quoi de paisible dans la cruauté et d'inexorable dans -l'indifférence. Âmes tombées au fond de l'infortune possible, malheureux -hommes perdus au plus bas de ces limbes où l'on ne regarde plus, les -réprouvés de la loi sentent peser de tout son poids sur leur tête cette -société humaine, si formidable pour qui est dehors, si effroyable pour -qui est dessous. - -Dans cette situation, Jean Valjean songeait, et quelle pouvait être la -nature de sa rêverie? - -Si le grain de mil sous la meule avait des pensées, il penserait sans -doute ce que pensait Jean Valjean. - -Toutes ces choses, réalités pleines de spectres, fantasmagories pleines -de réalités, avaient fini par lui créer une sorte d'état intérieur -presque inexprimable. - -Par moments, au milieu de son travail du bagne, il s'arrêtait. Il se -mettait à penser. Sa raison, à la fois plus mûre et plus troublée -qu'autrefois, se révoltait. Tout ce qui lui était arrivé lui paraissait -absurde; tout ce qui l'entourait lui paraissait impossible. Il se -disait: c'est un rêve. Il regardait l'argousin debout à quelques pas de -lui; l'argousin lui semblait un fantôme; tout à coup le fantôme lui -donnait un coup de bâton. - -La nature visible existait à peine pour lui. Il serait presque vrai de -dire qu'il n'y avait point pour Jean Valjean de soleil, ni de beaux -jours d'été, ni de ciel rayonnant, ni de fraîches aubes d'avril. Je ne -sais quel jour de soupirail éclairait habituellement son âme. - -Pour résumer, en terminant, ce qui peut être résumé et traduit en -résultats positifs dans tout ce que nous venons d'indiquer, nous nous -bornerons à constater qu'en dix-neuf ans, Jean Valjean, l'inoffensif -émondeur de Faverolles, le redoutable galérien de Toulon, était devenu -capable, grâce à la manière dont le bagne l'avait façonné, de deux -espèces de mauvaises actions: premièrement, d'une mauvaise action -rapide, irréfléchie, pleine d'étourdissement, toute d'instinct, sorte de -représaille pour le mal souffert; deuxièmement, d'une mauvaise action -grave, sérieuse, débattue en conscience et méditée avec les idées -fausses que peut donner un pareil malheur. Ses préméditations passaient -par les trois phases successives que les natures d'une certaine trempe -peuvent seules parcourir, raisonnement, volonté, obstination. Il avait -pour mobiles l'indignation habituelle, l'amertume de l'âme, le profond -sentiment des iniquités subies, la réaction, même contre les bons, les -innocents et les justes, s'il y en a. Le point de départ comme le point -d'arrivée de toutes ses pensées était la haine de la loi humaine; cette -haine qui, si elle n'est arrêtée dans son développement par quelque -incident providentiel, devient, dans un temps donné, la haine de la -société, puis la haine du genre humain, puis la haine de la création, et -se traduit par un vague et incessant et brutal désir de nuire, n'importe -à qui, à un être vivant quelconque. Comme on voit, ce n'était pas sans -raison que le passeport qualifiait Jean Valjean d'_homme très -dangereux_. - -D'année en année, cette âme s'était desséchée de plus en plus, -lentement, mais fatalement. À coeur sec, oeil sec. À sa sortie du bagne, -il y avait dix-neuf ans qu'il n'avait versé une larme. - - - - -Chapitre VIII - -L'onde et l'ombre - - -Un homme à la mer! - -Qu'importe! le navire ne s'arrête pas. Le vent souffle, ce sombre -navire-là a une route qu'il est forcé de continuer. Il passe. - -L'homme disparaît, puis reparaît, il plonge et remonte à la surface, il -appelle, il tend les bras, on ne l'entend pas; le navire, frissonnant -sous l'ouragan, est tout à sa manoeuvre, les matelots et les passagers -ne voient même plus l'homme submergé; sa misérable tête n'est qu'un -point dans l'énormité des vagues. Il jette des cris désespérés dans les -profondeurs. Quel spectre que cette voile qui s'en va! Il la regarde, il -la regarde frénétiquement. Elle s'éloigne, elle blêmit, elle décroît. Il -était là tout à l'heure, il était de l'équipage, il allait et venait sur -le pont avec les autres, il avait sa part de respiration et de soleil, -il était un vivant. Maintenant, que s'est-il donc passé? Il a glissé, il -est tombé, c'est fini. - -Il est dans l'eau monstrueuse. Il n'a plus sous les pieds que de la -fuite et de l'écroulement. Les flots déchirés et déchiquetés par le vent -l'environnent hideusement, les roulis de l'abîme l'emportent, tous les -haillons de l'eau s'agitent autour de sa tête, une populace de vagues -crache sur lui, de confuses ouvertures le dévorent à demi; chaque fois -qu'il enfonce, il entrevoit des précipices pleins de nuit; d'affreuses -végétations inconnues le saisissent, lui nouent les pieds, le tirent à -elles; il sent qu'il devient abîme, il fait partie de l'écume, les flots -se le jettent de l'un à l'autre, il boit l'amertume, l'océan lâche -s'acharne à le noyer, l'énormité joue avec son agonie. Il semble que -toute cette eau soit de la haine. - -Il lutte pourtant, il essaie de se défendre, il essaie de se soutenir, -il fait effort, il nage. Lui, cette pauvre force tout de suite épuisée, -il combat l'inépuisable. - -Où donc est le navire? Là-bas. À peine visible dans les pâles ténèbres -de l'horizon. - -Les rafales soufflent; toutes les écumes l'accablent. Il lève les yeux -et ne voit que les lividités des nuages. Il assiste, agonisant, à -l'immense démence de la mer. Il est supplicié par cette folie. Il entend -des bruits étrangers à l'homme qui semblent venir d'au delà de la terre -et d'on ne sait quel dehors effrayant. - -Il y a des oiseaux dans les nuées, de même qu'il y a des anges au-dessus -des détresses humaines, mais que peuvent-ils pour lui? Cela vole, chante -et plane, et lui, il râle. - -Il se sent enseveli à la fois par ces deux infinis, l'océan et le ciel; -l'un est une tombe, l'autre est un linceul. - -La nuit descend, voilà des heures qu'il nage, ses forces sont à bout; ce -navire, cette chose lointaine où il y avait des hommes, s'est effacé; il -est seul dans le formidable gouffre crépusculaire, il enfonce, il se -roidit, il se tord, il sent au-dessous de lui les vagues monstres de -l'invisible; il appelle. - -Il n'y a plus d'hommes. Où est Dieu? - -Il appelle. Quelqu'un! quelqu'un! Il appelle toujours. - -Rien à l'horizon. Rien au ciel. - -Il implore l'étendue, la vague, l'algue, l'écueil; cela est sourd. Il -supplie la tempête; la tempête imperturbable n'obéit qu'à l'infini. - -Autour de lui, l'obscurité, la brume, la solitude, le tumulte orageux et -inconscient, le plissement indéfini des eaux farouches. En lui l'horreur -et la fatigue. Sous lui la chute. Pas de point d'appui. Il songe aux -aventures ténébreuses du cadavre dans l'ombre illimitée. Le froid sans -fond le paralyse. Ses mains se crispent et se ferment et prennent du -néant. Vents, nuées, tourbillons, souffles, étoiles inutiles! Que faire? -Le désespéré s'abandonne, qui est las prend le parti de mourir, il se -laisse faire, il se laisse aller, il lâche prise, et le voilà qui roule -à jamais dans les profondeurs lugubres de l'engloutissement. - -Ô marche implacable des sociétés humaines! Pertes d'hommes et d'âmes -chemin faisant! Océan où tombe tout ce que laisse tomber la loi! -Disparition sinistre du secours! ô mort morale! - -La mer, c'est l'inexorable nuit sociale où la pénalité jette ses damnés. -La mer, c'est l'immense misère. - -L'âme, à vau-l'eau dans ce gouffre, peut devenir un cadavre. Qui la -ressuscitera? - - - - -Chapitre IX - -Nouveaux griefs - - -Quand vint l'heure de la sortie du bagne, quand Jean Valjean entendit à -son oreille ce mot étrange: _tu es libre_! le moment fut invraisemblable -et inouï, un rayon de vive lumière, un rayon de la vraie lumière des -vivants pénétra subitement en lui. Mais ce rayon ne tarda point à pâlir. -Jean Valjean avait été ébloui de l'idée de la liberté. Il avait cru à -une vie nouvelle. Il vit bien vite ce que c'était qu'une liberté à -laquelle on donne un passeport jaune. - -Et autour de cela bien des amertumes. Il avait calculé que sa masse, -pendant son séjour au bagne, aurait dû s'élever à cent soixante et onze -francs. Il est juste d'ajouter qu'il avait oublié de faire entrer dans -ses calculs le repos forcé des dimanches et fêtes qui, pour dix-neuf -ans, entraînait une diminution de vingt-quatre francs environ. Quoi -qu'il en fût, cette masse avait été réduite, par diverses retenues -locales, à la somme de cent neuf francs quinze sous, qui lui avait été -comptée à sa sortie. - -Il n'y avait rien compris, et se croyait lésé. Disons le mot, volé. - -Le lendemain de sa libération, à Grasse, il vit devant la porte d'une -distillerie de fleurs d'oranger des hommes qui déchargeaient des -ballots. Il offrit ses services. La besogne pressait, on les accepta. Il -se mit à l'ouvrage. Il était intelligent, robuste et adroit; il faisait -de son mieux; le maître paraissait content. Pendant qu'il travaillait, -un gendarme passa, le remarqua, et lui demanda ses papiers. Il fallut -montrer le passeport jaune. Cela fait, Jean Valjean reprit son travail. -Un peu auparavant, il avait questionné l'un des ouvriers sur ce qu'ils -gagnaient à cette besogne par jour; on lui avait répondu: _trente sous_. -Le soir venu, comme il était forcé de repartir le lendemain matin, il se -présenta devant le maître de la distillerie et le pria de le payer. Le -maître ne proféra pas une parole, et lui remit vingt-cinq sous. Il -réclama. On lui répondit: cela est assez bon pour toi. Il insista. Le -maître le regarda entre les deux yeux et lui dit: _Gare le bloc_. - -Là encore il se considéra comme volé. - -La société, l'état, en lui diminuant sa masse, l'avait volé en grand. -Maintenant, c'était le tour de l'individu qui le volait en petit. - -Libération n'est pas délivrance. On sort du bagne, mais non de la -condamnation. Voilà ce qui lui était arrivé à Grasse. On a vu de quelle -façon il avait été accueilli à Digne. - - - - -Chapitre X - -L'homme réveillé - - -Donc, comme deux heures du matin sonnaient à l'horloge de la cathédrale, -Jean Valjean se réveilla. - -Ce qui le réveilla, c'est que le lit était trop bon. Il y avait vingt -ans bientôt qu'il n'avait couché dans un lit, et quoiqu'il ne se fût pas -déshabillé, la sensation était trop nouvelle pour ne pas troubler son -sommeil. - -Il avait dormi plus de quatre heures. Sa fatigue était passée. Il était -accoutumé à ne pas donner beaucoup d'heures au repos. - -Il ouvrit les yeux et regarda un moment dans l'obscurité autour de lui, -puis il les referma pour se rendormir. - -Quand beaucoup de sensations diverses ont agité la journée, quand des -choses préoccupent l'esprit, on s'endort, mais on ne se rendort pas. Le -sommeil vient plus aisément qu'il ne revient. C'est ce qui arriva à Jean -Valjean. Il ne put se rendormir, et il se mit à penser. - -Il était dans un de ces moments où les idées qu'on a dans l'esprit sont -troubles. Il avait une sorte de va-et-vient obscur dans le cerveau. Ses -souvenirs anciens et ses souvenirs immédiats y flottaient pêle-mêle et -s'y croisaient confusément, perdant leurs formes, se grossissant -démesurément, puis disparaissant tout à coup comme dans une eau fangeuse -et agitée. Beaucoup de pensées lui venaient, mais il y en avait une qui -se représentait continuellement et qui chassait toutes les autres. Cette -pensée, nous allons la dire tout de suite:--Il avait remarqué les six -couverts d'argent et la grande cuiller que madame Magloire avait posés -sur la table. - -Ces six couverts d'argent l'obsédaient.--Ils étaient là.--À quelques -pas.--À l'instant où il avait traversé la chambre d'à côté pour venir -dans celle où il était, la vieille servante les mettait dans un petit -placard à la tête du lit.--Il avait bien remarqué ce placard.--À droite, -en entrant par la salle à manger.--Ils étaient massifs.--Et de vieille -argenterie.--Avec la grande cuiller, on en tirerait au moins deux cents -francs.--Le double de ce qu'il avait gagné en dix-neuf ans.--Il est -vrai qu'il eût gagné davantage si l'_administration_ ne l'avait pas -_volé_. - -Son esprit oscilla toute une grande heure dans des fluctuations -auxquelles se mêlait bien quelque lutte. Trois heures sonnèrent. Il -rouvrit les yeux, se dressa brusquement sur son séant, étendit le bras -et tâta son havresac qu'il avait jeté dans le coin de l'alcôve, puis il -laissa pendre ses jambes et poser ses pieds à terre, et se trouva, -presque sans savoir comment, assis sur son lit. - -Il resta un certain temps rêveur dans cette attitude qui eût eu quelque -chose de sinistre pour quelqu'un qui l'eût aperçu ainsi dans cette -ombre, seul éveillé dans la maison endormie. Tout à coup il se baissa, -ôta ses souliers et les posa doucement sur la natte près du lit, puis il -reprit sa posture de rêverie et redevint immobile. - -Au milieu de cette méditation hideuse, les idées que nous venons -d'indiquer remuaient sans relâche son cerveau, entraient, sortaient, -rentraient, faisaient sur lui une sorte de pesée; et puis il songeait -aussi, sans savoir pourquoi, et avec cette obstination machinale de la -rêverie, à un forçat nommé Brevet qu'il avait connu au bagne, et dont le -pantalon n'était retenu que par une seule bretelle de coton tricoté. Le -dessin en damier de cette bretelle lui revenait sans cesse à l'esprit. - -Il demeurait dans cette situation, et y fût peut-être resté indéfiniment -jusqu'au lever du jour, si l'horloge n'eût sonné un coup--le quart ou la -demie. Il sembla que ce coup lui eût dit: allons! - -Il se leva debout, hésita encore un moment, et écouta; tout se taisait -dans la maison; alors il marcha droit et à petits pas vers la fenêtre -qu'il entrevoyait. La nuit n'était pas très obscure; c'était une pleine -lune sur laquelle couraient de larges nuées chassées par le vent. Cela -faisait au dehors des alternatives d'ombre et de clarté, des éclipses, -puis des éclaircies, et au dedans une sorte de crépuscule. Ce -crépuscule, suffisant pour qu'on pût se guider, intermittent à cause des -nuages, ressemblait à l'espèce de lividité qui tombe d'un soupirail de -cave devant lequel vont et viennent des passants. Arrivé à la fenêtre, -Jean Valjean l'examina. Elle était sans barreaux, donnait sur le jardin -et n'était fermée, selon la mode du pays, que d'une petite clavette. Il -l'ouvrit, mais, comme un air froid et vif entra brusquement dans la -chambre, il la referma tout de suite. Il regarda le jardin de ce regard -attentif qui étudie plus encore qu'il ne regarde. Le jardin était enclos -d'un mur blanc assez bas, facile à escalader. Au fond, au-delà, il -distingua des têtes d'arbres également espacées, ce qui indiquait que ce -mur séparait le jardin d'une avenue ou d'une ruelle plantée. - -Ce coup d'oeil jeté, il fit le mouvement d'un homme déterminé, marcha à -son alcôve, prit son havresac, l'ouvrit, le fouilla, en tira quelque -chose qu'il posa sur le lit, mit ses souliers dans une des poches, -referma le tout, chargea le sac sur ses épaules, se couvrit de sa -casquette dont il baissa la visière sur ses yeux, chercha son bâton en -tâtonnant, et l'alla poser dans l'angle de la fenêtre, puis revint au -lit et saisit résolument l'objet qu'il y avait déposé. Cela ressemblait -à une barre de fer courte, aiguisée comme un épieu à l'une de ses -extrémités. - -Il eût été difficile de distinguer dans les ténèbres pour quel emploi -avait pu être façonné ce morceau de fer. C'était peut-être un levier? -C'était peut-être une massue? - -Au jour on eût pu reconnaître que ce n'était autre chose qu'un -chandelier de mineur. On employait alors quelquefois les forçats à -extraire de la roche des hautes collines qui environnent Toulon, et il -n'était pas rare qu'ils eussent à leur disposition des outils de mineur. -Les chandeliers des mineurs sont en fer massif, terminés à leur -extrémité inférieure par une pointe au moyen de laquelle on les enfonce -dans le rocher. - -Il prit ce chandelier dans sa main droite, et retenant son haleine, -assourdissant son pas, il se dirigea vers la porte de la chambre -voisine, celle de l'évêque, comme on sait. Arrivé à cette porte, il la -trouva entrebâillée. L'évêque ne l'avait point fermée. - - - - -Chapitre XI - -Ce qu'il fait - - -Jean Valjean écouta. Aucun bruit. - -Il poussa la porte. - -Il la poussa du bout du doigt, légèrement, avec cette douceur furtive et -inquiète d'un chat qui veut entrer. - -La porte céda à la pression et fit un mouvement imperceptible et -silencieux qui élargit un peu l'ouverture. - -Il attendit un moment, puis poussa la porte une seconde fois, plus -hardiment. Elle continua de céder en silence. L'ouverture était assez -grande maintenant pour qu'il pût passer. Mais il y avait près de la -porte une petite table qui faisait avec elle un angle gênant et qui -barrait l'entrée. - -Jean Valjean reconnut la difficulté. Il fallait à toute force que -l'ouverture fût encore élargie. - -Il prit son parti, et poussa une troisième fois la porte, plus -énergiquement que les deux premières. Cette fois il y eut un gond mal -huilé qui jeta tout à coup dans cette obscurité un cri rauque et -prolongé. - -Jean Valjean tressaillit. Le bruit de ce gond sonna dans son oreille -avec quelque chose d'éclatant et de formidable comme le clairon du -jugement dernier. Dans les grossissements fantastiques de la première -minute, il se figura presque que ce gond venait de s'animer et de -prendre tout à coup une vie terrible, et qu'il aboyait comme un chien -pour avertir tout le monde et réveiller les gens endormis. - -Il s'arrêta, frissonnant, éperdu, et retomba de la pointe du pied sur le -talon. Il entendait ses artères battre dans ses tempes comme deux -marteaux de forge, et il lui semblait que son souffle sortait de sa -poitrine avec le bruit du vent qui sort d'une caverne. Il lui paraissait -impossible que l'horrible clameur de ce gond irrité n'eût pas ébranlé -toute la maison comme une secousse de tremblement de terre; la porte, -poussée par lui, avait pris l'alarme et avait appelé; le vieillard -allait se lever, les deux vieilles femmes allaient crier, on viendrait à -l'aide; avant un quart d'heure, la ville serait en rumeur et la -gendarmerie sur pied. Un moment il se crut perdu. - -Il demeura où il était, pétrifié comme la statue de sel, n'osant faire -un mouvement. - -Quelques minutes s'écoulèrent. La porte s'était ouverte toute grande. Il -se hasarda à regarder dans la chambre. Rien n'y avait bougé. Il prêta -l'oreille. Rien ne remuait dans la maison. Le bruit du gond rouillé -n'avait éveillé personne. Ce premier danger était passé, mais il y avait -encore en lui un affreux tumulte. Il ne recula pas pourtant. Même quand -il s'était cru perdu, il n'avait pas reculé. Il ne songea plus qu'à -finir vite. Il fit un pas et entra dans la chambre. - -Cette chambre était dans un calme parfait. On y distinguait çà et là des -formes confuses et vagues qui, au jour, étaient des papiers épars sur -une table, des in-folio ouverts, des volumes empilés sur un tabouret, un -fauteuil chargé de vêtements, un prie-Dieu, et qui à cette heure -n'étaient plus que des coins ténébreux et des places blanchâtres. Jean -Valjean avança avec précaution en évitant de se heurter aux meubles. Il -entendait au fond de la chambre la respiration égale et tranquille de -l'évêque endormi. - -Il s'arrêta tout à coup. Il était près du lit. Il y était arrivé plus -tôt qu'il n'aurait cru. - -La nature mêle quelquefois ses effets et ses spectacles à nos actions -avec une espèce d'à-propos sombre et intelligent, comme si elle voulait -nous faire réfléchir. Depuis près d'une demi-heure un grand nuage -couvrait le ciel. Au moment où Jean Valjean s'arrêta en face du lit, ce -nuage se déchira, comme s'il l'eût fait exprès, et un rayon de lune, -traversant la longue fenêtre, vint éclairer subitement le visage pâle de -l'évêque. Il dormait paisiblement. Il était presque vêtu dans son lit, à -cause des nuits froides des Basses-Alpes, d'un vêtement de laine brune -qui lui couvrait les bras jusqu'aux poignets. Sa tête était renversée -sur l'oreiller dans l'attitude abandonnée du repos; il laissait pendre -hors du lit sa main ornée de l'anneau pastoral et d'où étaient tombées -tant de bonnes oeuvres et de saintes actions. Toute sa face s'illuminait -d'une vague expression de satisfaction, d'espérance et de béatitude. -C'était plus qu'un sourire et presque un rayonnement. Il y avait sur son -front l'inexprimable réverbération d'une lumière qu'on ne voyait pas. -L'âme des justes pendant le sommeil contemple un ciel mystérieux. - -Un reflet de ce ciel était sur l'évêque. - -C'était en même temps une transparence lumineuse, car ce ciel était au -dedans de lui. Ce ciel, c'était sa conscience. - -Au moment où le rayon de lune vint se superposer, pour ainsi dire, à -cette clarté intérieure, l'évêque endormi apparut comme dans une gloire. -Cela pourtant resta doux et voilé d'un demi-jour ineffable. Cette lune -dans le ciel, cette nature assoupie, ce jardin sans un frisson, cette -maison si calme, l'heure, le moment, le silence, ajoutaient je ne sais -quoi de solennel et d'indicible au vénérable repos de ce sage, et -enveloppaient d'une sorte d'auréole majestueuse et sereine ces cheveux -blancs et ces yeux fermés, cette figure où tout était espérance et où -tout était confiance, cette tête de vieillard et ce sommeil d'enfant. - -Il y avait presque de la divinité dans cet homme ainsi auguste à son -insu. Jean Valjean, lui, était dans l'ombre, son chandelier de fer à la -main, debout, immobile, effaré de ce vieillard lumineux. Jamais il -n'avait rien vu de pareil. Cette confiance l'épouvantait. Le monde moral -n'a pas de plus grand spectacle que celui-là: une conscience troublée et -inquiète, parvenue au bord d'une mauvaise action, et contemplant le -sommeil d'un juste. - -Ce sommeil, dans cet isolement, et avec un voisin tel que lui, avait -quelque chose de sublime qu'il sentait vaguement, mais impérieusement. - -Nul n'eût pu dire ce qui se passait en lui, pas même lui. Pour essayer -de s'en rendre compte, il faut rêver ce qu'il y a de plus violent en -présence de ce qu'il y a de plus doux. Sur son visage même on n'eût rien -pu distinguer avec certitude. C'était une sorte d'étonnement hagard. Il -regardait cela. Voilà tout. Mais quelle était sa pensée? Il eût été -impossible de le deviner. Ce qui était évident, c'est qu'il était ému et -bouleversé. Mais de quelle nature était cette émotion? - -Son oeil ne se détachait pas du vieillard. La seule chose qui se -dégageât clairement de son attitude et de sa physionomie, c'était une -étrange indécision. On eût dit qu'il hésitait entre les deux abîmes, -celui où l'on se perd et celui où l'on se sauve. Il semblait prêt à -briser ce crâne ou à baiser cette main. - -Au bout de quelques instants, son bras gauche se leva lentement vers son -front, et il ôta sa casquette, puis son bras retomba avec la même -lenteur, et Jean Valjean rentra dans sa contemplation, sa casquette dans -la main gauche, sa massue dans la main droite, ses cheveux hérissés sur -sa tête farouche. - -L'évêque continuait de dormir dans une paix profonde sous ce regard -effrayant. Un reflet de lune faisait confusément visible au-dessus de la -cheminée le crucifix qui semblait leur ouvrir les bras à tous les deux, -avec une bénédiction pour l'un et un pardon pour l'autre. - -Tout à coup Jean Valjean remit sa casquette sur son front, puis marcha -rapidement, le long du lit, sans regarder l'évêque, droit au placard -qu'il entrevoyait près du chevet; il leva le chandelier de fer comme -pour forcer la serrure; la clef y était; il l'ouvrit; la première chose -qui lui apparut fut le panier d'argenterie; il le prit, traversa la -chambre à grands pas sans précaution et sans s'occuper du bruit, gagna -la porte, rentra dans l'oratoire, ouvrit la fenêtre, saisit un bâton, -enjamba l'appui du rez-de-chaussée, mit l'argenterie dans son sac, jeta -le panier, franchit le jardin, sauta par-dessus le mur comme un tigre, -et s'enfuit. - - - - -Chapitre XII - -L'évêque travaille - - -Le lendemain, au soleil levant, monseigneur Bienvenu se promenait dans -son jardin. Madame Magloire accourut vers lui toute bouleversée. - ---Monseigneur, monseigneur, cria-t-elle, votre grandeur sait-elle où est -le panier d'argenterie? - ---Oui, dit l'évêque. - ---Jésus-Dieu soit béni! reprit-elle. Je ne savais ce qu'il était devenu. - -L'évêque venait de ramasser le panier dans une plate-bande. Il le -présenta à madame Magloire. - ---Le voilà. - ---Eh bien? dit-elle. Rien dedans! et l'argenterie? - ---Ah! repartit l'évêque. C'est donc l'argenterie qui vous occupe? Je ne -sais où elle est. - ---Grand bon Dieu! elle est volée! C'est l'homme d'hier soir qui l'a -volée! - -En un clin d'oeil, avec toute sa vivacité de vieille alerte, madame -Magloire courut à l'oratoire, entra dans l'alcôve et revint vers -l'évêque. L'évêque venait de se baisser et considérait en soupirant un -plant de cochléaria des Guillons que le panier avait brisé en tombant à -travers la plate-bande. Il se redressa au cri de madame Magloire. - ---Monseigneur, l'homme est parti! l'argenterie est volée! - -Tout en poussant cette exclamation, ses yeux tombaient sur un angle du -jardin où l'on voyait des traces d'escalade. Le chevron du mur avait été -arraché. - ---Tenez! c'est par là qu'il s'en est allé. Il a sauté dans la ruelle -Cochefilet! Ah! l'abomination! Il nous a volé notre argenterie! - -L'évêque resta un moment silencieux, puis leva son oeil sérieux, et dit -à madame Magloire avec douceur: - ---Et d'abord, cette argenterie était-elle à nous? - -Madame Magloire resta interdite. Il y eut encore un silence, puis -l'évêque continua: - ---Madame Magloire, je détenais à tort et depuis longtemps cette -argenterie. Elle était aux pauvres. Qu'était-ce que cet homme? Un pauvre -évidemment. - ---Hélas Jésus! repartit madame Magloire. Ce n'est pas pour moi ni pour -mademoiselle. Cela nous est bien égal. Mais c'est pour monseigneur. Dans -quoi monseigneur va-t-il manger maintenant? - -L'évêque la regarda d'un air étonné. - ---Ah çà mais! est-ce qu'il n'y a pas des couverts d'étain? - -Madame Magloire haussa les épaules. - ---L'étain a une odeur. - ---Alors, des couverts de fer. - -Madame Magloire fit une grimace significative. - ---Le fer a un goût. - ---Eh bien, dit l'évêque, des couverts de bois. - -Quelques instants après, il déjeunait à cette même table où Jean Valjean -s'était assis la veille. Tout en déjeunant, monseigneur Bienvenu faisait -gaîment remarquer à sa soeur qui ne disait rien et à madame Magloire qui -grommelait sourdement qu'il n'est nullement besoin d'une cuiller ni -d'une fourchette, même en bois, pour tremper un morceau de pain dans une -tasse de lait. - ---Aussi a-t-on idée! disait madame Magloire toute seule en allant et -venant, recevoir un homme comme cela! et le loger à côté de soi! et quel -bonheur encore qu'il n'ait fait que voler! Ah mon Dieu! cela fait frémir -quand on songe! - -Comme le frère et la soeur allaient se lever de table, on frappa à la -porte. - ---Entrez, dit l'évêque. - -La porte s'ouvrit. Un groupe étrange et violent apparut sur le seuil. -Trois hommes en tenaient un quatrième au collet. Les trois hommes -étaient des gendarmes; l'autre était Jean Valjean. - -Un brigadier de gendarmerie, qui semblait conduire le groupe, était près -de la porte. Il entra et s'avança vers l'évêque en faisant le salut -militaire. - ---Monseigneur... dit-il. - -À ce mot Jean Valjean, qui était morne et semblait abattu, releva la -tête d'un air stupéfait. - ---Monseigneur! murmura-t-il. Ce n'est donc pas le curé?... - ---Silence! dit un gendarme. C'est monseigneur l'évêque. - -Cependant monseigneur Bienvenu s'était approché aussi vivement que son -grand âge le lui permettait. - ---Ah! vous voilà! s'écria-t-il en regardant Jean Valjean. Je suis aise -de vous voir. Et bien mais! je vous avais donné les chandeliers aussi, -qui sont en argent comme le reste et dont vous pourrez bien avoir deux -cents francs. Pourquoi ne les avez-vous pas emportés avec vos couverts? - -Jean Valjean ouvrit les yeux et regarda le vénérable évêque avec une -expression qu'aucune langue humaine ne pourrait rendre. - ---Monseigneur, dit le brigadier de gendarmerie, ce que cet homme disait -était donc vrai? Nous l'avons rencontré. Il allait comme quelqu'un qui -s'en va. Nous l'avons arrêté pour voir. Il avait cette argenterie.... - ---Et il vous a dit, interrompit l'évêque en souriant, qu'elle lui avait -été donnée par un vieux bonhomme de prêtre chez lequel il avait passé la -nuit? Je vois la chose. Et vous l'avez ramené ici? C'est une méprise. - ---Comme cela, reprit le brigadier, nous pouvons le laisser aller? - ---Sans doute, répondit l'évêque. - -Les gendarmes lâchèrent Jean Valjean qui recula. - ---Est-ce que c'est vrai qu'on me laisse? dit-il d'une voix presque -inarticulée et comme s'il parlait dans le sommeil. - ---Oui, on te laisse, tu n'entends donc pas? dit un gendarme. - ---Mon ami, reprit l'évêque, avant de vous en aller, voici vos -chandeliers. Prenez-les. - -Il alla à la cheminée, prit les deux flambeaux d'argent et les apporta à -Jean Valjean. Les deux femmes le regardaient faire sans un mot, sans un -geste, sans un regard qui pût déranger l'évêque. - -Jean Valjean tremblait de tous ses membres. Il prit les deux chandeliers -machinalement et d'un air égaré. - ---Maintenant, dit l'évêque, allez en paix. - ---À propos, quand vous reviendrez, mon ami, il est inutile de passer par -le jardin. Vous pourrez toujours entrer et sortir par la porte de la -rue. Elle n'est fermée qu'au loquet jour et nuit. - -Puis se tournant vers la gendarmerie: - ---Messieurs, vous pouvez vous retirer. - -Les gendarmes s'éloignèrent. - -Jean Valjean était comme un homme qui va s'évanouir. - -L'évêque s'approcha de lui, et lui dit à voix basse: - ---N'oubliez pas, n'oubliez jamais que vous m'avez promis d'employer cet -argent à devenir honnête homme. - -Jean Valjean, qui n'avait aucun souvenir d'avoir rien promis, resta -interdit. L'évêque avait appuyé sur ces paroles en les prononçant. Il -reprit avec une sorte de solennité: - ---Jean Valjean, mon frère, vous n'appartenez plus au mal, mais au bien. -C'est votre âme que je vous achète; je la retire aux pensées noires et à -l'esprit de perdition, et je la donne à Dieu. - - - - -Chapitre XIII - -Petit-Gervais - - -Jean Valjean sortit de la ville comme s'il s'échappait. Il se mit à -marcher en toute hâte dans les champs, prenant les chemins et les -sentiers qui se présentaient sans s'apercevoir qu'il revenait à chaque -instant sur ses pas. Il erra ainsi toute la matinée, n'ayant pas mangé -et n'ayant pas faim. Il était en proie à une foule de sensations -nouvelles. Il se sentait une sorte de colère; il ne savait contre qui. -Il n'eût pu dire s'il était touché ou humilié. Il lui venait par moments -un attendrissement étrange qu'il combattait et auquel il opposait -l'endurcissement de ses vingt dernières années. Cet état le fatiguait. -Il voyait avec inquiétude s'ébranler au dedans de lui l'espèce de calme -affreux que l'injustice de son malheur lui avait donné. Il se demandait -qu'est-ce qui remplacerait cela. Parfois il eût vraiment mieux aimé être -en prison avec les gendarmes, et que les choses ne se fussent point -passées ainsi; cela l'eût moins agité. Bien que la saison fut assez -avancée, il y avait encore çà et là dans les haies quelques fleurs -tardives dont l'odeur, qu'il traversait en marchant, lui rappelait des -souvenirs d'enfance. Ces souvenirs lui étaient presque insupportables, -tant il y avait longtemps qu'ils ne lui étaient apparus. - -Des pensées inexprimables s'amoncelèrent ainsi en lui toute la journée. - -Comme le soleil déclinait au couchant, allongeant sur le sol l'ombre du -moindre caillou, Jean Valjean était assis derrière un buisson dans une -grande plaine rousse absolument déserte. Il n'y avait à l'horizon que -les Alpes. Pas même le clocher d'un village lointain. Jean Valjean -pouvait être à trois lieues de Digne. Un sentier qui coupait la plaine -passait à quelques pas du buisson. - -Au milieu de cette méditation qui n'eût pas peu contribué à rendre ses -haillons effrayants pour quelqu'un qui l'eût rencontré, il entendit un -bruit joyeux. - -Il tourna la tête, et vit venir par le sentier un petit savoyard d'une -dizaine d'années qui chantait, sa vielle au flanc et sa boîte à marmotte -sur le dos; un de ces doux et gais enfants qui vont de pays en pays, -laissant voir leurs genoux par les trous de leur pantalon. - -Tout en chantant l'enfant interrompait de temps en temps sa marche et -jouait aux osselets avec quelques pièces de monnaie qu'il avait dans sa -main, toute sa fortune probablement. Parmi cette monnaie il y avait une -pièce de quarante sous. L'enfant s'arrêta à côté du buisson sans voir -Jean Valjean et fit sauter sa poignée de sous que jusque-là il avait -reçue avec assez d'adresse tout entière sur le dos de sa main. - -Cette fois la pièce de quarante sous lui échappa, et vint rouler vers la -broussaille jusqu'à Jean Valjean. - -Jean Valjean posa le pied dessus. - -Cependant l'enfant avait suivi sa pièce du regard, et l'avait vu. - -Il ne s'étonna point et marcha droit à l'homme. - -C'était un lieu absolument solitaire. Aussi loin que le regard pouvait -s'étendre, il n'y avait personne dans la plaine ni dans le sentier. On -n'entendait que les petits cris faibles d'une nuée d'oiseaux de passage -qui traversaient le ciel à une hauteur immense. L'enfant tournait le dos -au soleil qui lui mettait des fils d'or dans les cheveux et qui -empourprait d'une lueur sanglante la face sauvage de Jean Valjean. - ---Monsieur, dit le petit savoyard, avec cette confiance de l'enfance qui -se compose d'ignorance et d'innocence,--ma pièce? - ---Comment t'appelles-tu? dit Jean Valjean. - ---Petit-Gervais, monsieur. - ---Va-t'en, dit Jean Valjean. - ---Monsieur, reprit l'enfant, rendez-moi ma pièce. - -Jean Valjean baissa la tête et ne répondit pas. - -L'enfant recommença: - ---Ma pièce, monsieur! - -L'oeil de Jean Valjean resta fixé à terre. - ---Ma pièce! cria l'enfant, ma pièce blanche! mon argent! Il semblait que -Jean Valjean n'entendit point. L'enfant le prit au collet de sa blouse -et le secoua. Et en même temps il faisait effort pour déranger le gros -soulier ferré posé sur son trésor. - ---Je veux ma pièce! ma pièce de quarante sous! - -L'enfant pleurait. La tête de Jean Valjean se releva. Il était toujours -assis. Ses yeux étaient troubles. Il considéra l'enfant avec une sorte -d'étonnement, puis il étendit la main vers son bâton et cria d'une voix -terrible: - ---Qui est là? - ---Moi, monsieur, répondit l'enfant. Petit-Gervais! moi! moi! Rendez-moi -mes quarante sous, s'il vous plaît! Ôtez votre pied, monsieur, s'il vous -plaît! - -Puis irrité, quoique tout petit, et devenant presque menaçant: - ---Ah, çà, ôterez-vous votre pied? Ôtez donc votre pied, voyons. - ---Ah! c'est encore toi! dit Jean Valjean, et se dressant brusquement -tout debout, le pied toujours sur la pièce d'argent, il ajouta:--Veux-tu -bien te sauver! - -L'enfant effaré le regarda, puis commença à trembler de la tête aux -pieds, et, après quelques secondes de stupeur, se mit à s'enfuir en -courant de toutes ses forces sans oser tourner le cou ni jeter un cri. - -Cependant à une certaine distance l'essoufflement le força de s'arrêter, -et Jean Valjean, à travers sa rêverie, l'entendit qui sanglotait. - -Au bout de quelques instants l'enfant avait disparu. Le soleil s'était -couché. L'ombre se faisait autour de Jean Valjean. Il n'avait pas mangé -de la journée; il est probable qu'il avait la fièvre. - -Il était resté debout, et n'avait pas changé d'attitude depuis que -l'enfant s'était enfui. Son souffle soulevait sa poitrine à des -intervalles longs et inégaux. Son regard, arrêté à dix ou douze pas -devant lui, semblait étudier avec une attention profonde la forme d'un -vieux tesson de faïence bleue tombé dans l'herbe. Tout à coup il -tressaillit; il venait de sentir le froid du soir. - -Il raffermit sa casquette sur son front, chercha machinalement à croiser -et à boutonner sa blouse, fit un pas, et se baissa pour reprendre à -terre son bâton. En ce moment il aperçut la pièce de quarante sous que -son pied avait à demi enfoncée dans la terre et qui brillait parmi les -cailloux. - -Ce fut comme une commotion galvanique. Qu'est-ce que c'est que ça? -dit-il entre ses dents. Il recula de trois pas, puis s'arrêta, sans -pouvoir détacher son regard de ce point que son pied avait foulé -l'instant d'auparavant, comme si cette chose qui luisait là dans -l'obscurité eût été un oeil ouvert fixé sur lui. - -Au bout de quelques minutes, il s'élança convulsivement vers la pièce -d'argent, la saisit, et, se redressant, se mit à regarder au loin dans -la plaine, jetant à la fois ses yeux vers tous les points de l'horizon, -debout et frissonnant comme une bête fauve effarée qui cherche un asile. - -Il ne vit rien. La nuit tombait, la plaine était froide et vague, de -grandes brumes violettes montaient dans la clarté crépusculaire. - -Il dit: «Ah!» et se mit à marcher rapidement dans une certaine -direction, du côté où l'enfant avait disparu. Après une centaine de pas, -il s'arrêta, regarda, et ne vit rien. - -Alors il cria de toute sa force: «Petit-Gervais! Petit-Gervais!» - -Il se tut, et attendit. - -Rien ne répondit. - -La campagne était déserte et morne. Il était environné de l'étendue. Il -n'y avait rien autour de lui qu'une ombre où se perdait son regard et un -silence où sa voix se perdait. - -Une bise glaciale soufflait, et donnait aux choses autour de lui une -sorte de vie lugubre. Des arbrisseaux secouaient leurs petits bras -maigres avec une furie incroyable. On eût dit qu'ils menaçaient et -poursuivaient quelqu'un. - -Il recommença à marcher, puis il se mit à courir, et de temps en temps -il s'arrêtait, et criait dans cette solitude, avec une voix qui était ce -qu'on pouvait entendre de plus formidable et de plus désolé: -«Petit-Gervais! Petit-Gervais!» - -Certes, si l'enfant l'eût entendu, il eût eu peur et se fût bien gardé -de se montrer. Mais l'enfant était sans doute déjà bien loin. - -Il rencontra un prêtre qui était à cheval. Il alla à lui et lui dit: - ---Monsieur le curé, avez-vous vu passer un enfant? - ---Non, dit le prêtre. - ---Un nommé Petit-Gervais? - ---Je n'ai vu personne. - -Il tira deux pièces de cinq francs de sa sacoche et les remit au prêtre. - ---Monsieur le curé, voici pour vos pauvres.--Monsieur le curé, c'est un -petit d'environ dix ans qui a une marmotte, je crois, et une vielle. Il -allait. Un de ces savoyards, vous savez? - ---Je ne l'ai point vu. - ---Petit-Gervais? il n'est point des villages d'ici? pouvez-vous me dire? - ---Si c'est comme vous dites, mon ami, c'est un petit enfant étranger. -Cela passe dans le pays. On ne les connaît pas. - -Jean Valjean prit violemment deux autres écus de cinq francs qu'il donna -au prêtre. - ---Pour vos pauvres, dit-il. - -Puis il ajouta avec égarement: - ---Monsieur l'abbé, faites-moi arrêter. Je suis un voleur. - -Le prêtre piqua des deux et s'enfuit très effrayé. - -Jean Valjean se remit à courir dans la direction qu'il avait d'abord -prise. - -Il fit de la sorte un assez long chemin, regardant, appelant, criant, -mais il ne rencontra plus personne. Deux ou trois fois il courut dans la -plaine vers quelque chose qui lui faisait l'effet d'un être couché ou -accroupi; ce n'étaient que des broussailles ou des roches à fleur de -terre. Enfin, à un endroit où trois sentiers se croisaient, il s'arrêta. -La lune s'était levée. Il promena sa vue au loin et appela une dernière -fois: «Petit-Gervais! Petit-Gervais! Petit-Gervais!» Son cri s'éteignit -dans la brume, sans même éveiller un écho. Il murmura encore: -«Petit-Gervais!» mais d'une voix faible et presque inarticulée. Ce fut -là son dernier effort; ses jarrets fléchirent brusquement sous lui comme -si une puissance invisible l'accablait tout à coup du poids de sa -mauvaise conscience; il tomba épuisé sur une grosse pierre, les poings -dans ses cheveux et le visage dans ses genoux, et il cria: «Je suis un -misérable!» - -Alors son coeur creva et il se mit à pleurer. C'était la première fois -qu'il pleurait depuis dix-neuf ans. - -Quand Jean Valjean était sorti de chez l'évêque, on l'a vu, il était -hors de tout ce qui avait été sa pensée jusque-là. Il ne pouvait se -rendre compte de ce qui se passait en lui. Il se raidissait contre -l'action angélique et contre les douces paroles du vieillard. «Vous -m'avez promis de devenir honnête homme. Je vous achète votre âme. Je la -retire à l'esprit de perversité et je la donne au bon Dieu.» Cela lui -revenait sans cesse. Il opposait à cette indulgence céleste l'orgueil, -qui est en nous comme la forteresse du mal. Il sentait indistinctement -que le pardon de ce prêtre était le plus grand assaut et la plus -formidable attaque dont il eût encore été ébranlé; que son -endurcissement serait définitif s'il résistait à cette clémence; que, -s'il cédait, il faudrait renoncer à cette haine dont les actions des -autres hommes avaient rempli son âme pendant tant d'années, et qui lui -plaisait; que cette fois il fallait vaincre ou être vaincu, et que la -lutte, une lutte colossale et décisive, était engagée entre sa -méchanceté à lui et la bonté de cet homme. - -En présence de toutes ces lueurs, il allait comme un homme ivre. Pendant -qu'il marchait ainsi, les yeux hagards, avait-il une perception -distincte de ce qui pourrait résulter pour lui de son aventure à Digne? -Entendait-il tous ces bourdonnements mystérieux qui avertissent ou -importunent l'esprit à de certains moments de la vie? Une voix lui -disait-elle à l'oreille qu'il venait de traverser l'heure solennelle de -sa destinée, qu'il n'y avait plus de milieu pour lui, que si désormais -il n'était pas le meilleur des hommes il en serait le pire, qu'il -fallait pour ainsi dire que maintenant il montât plus haut que l'évêque -ou retombât plus bas que le galérien, que s'il voulait devenir bon il -fallait qu'il devînt ange; que s'il voulait rester méchant il fallait -qu'il devînt monstre? - -Ici encore il faut se faire ces questions que nous nous sommes déjà -faites ailleurs, recueillait-il confusément quelque ombre de tout ceci -dans sa pensée? Certes, le malheur, nous l'avons dit, fait l'éducation -de l'intelligence; cependant il est douteux que Jean Valjean fût en état -de démêler tout ce que nous indiquons ici. Si ces idées lui arrivaient, -il les entrevoyait plutôt qu'il ne les voyait, et elles ne réussissaient -qu'à le jeter dans un trouble insupportable et presque douloureux. Au -sortir de cette chose difforme et noire qu'on appelle le bagne, l'évêque -lui avait fait mal à l'âme comme une clarté trop vive lui eût fait mal -aux yeux en sortant des ténèbres. La vie future, la vie possible qui -s'offrait désormais à lui toute pure et toute rayonnante le remplissait -de frémissements et d'anxiété. Il ne savait vraiment plus où il en -était. Comme une chouette qui verrait brusquement se lever le soleil, le -forçat avait été ébloui et comme aveuglé par la vertu. - -Ce qui était certain, ce dont il ne se doutait pas, c'est qu'il n'était -déjà plus le même homme, c'est que tout était changé en lui, c'est qu'il -n'était plus en son pouvoir de faire que l'évêque ne lui eût pas parlé -et ne l'eût pas touché. - -Dans cette situation d'esprit, il avait rencontré Petit-Gervais et lui -avait volé ses quarante sous. Pourquoi? Il n'eût assurément pu -l'expliquer; était-ce un dernier effet et comme un suprême effort des -mauvaises pensées qu'il avait apportées du bagne, un reste d'impulsion, -un résultat de ce qu'on appelle en statique la _force acquise_? C'était -cela, et c'était aussi peut-être moins encore que cela. Disons-le -simplement, ce n'était pas lui qui avait volé, ce n'était pas l'homme, -c'était la bête qui, par habitude et par instinct, avait stupidement -posé le pied sur cet argent, pendant que l'intelligence se débattait au -milieu de tant d'obsessions inouïes et nouvelles. Quand l'intelligence -se réveilla et vit cette action de la brute, Jean Valjean recula avec -angoisse et poussa un cri d'épouvante. - -C'est que, phénomène étrange et qui n'était possible que dans la -situation où il était, en volant cet argent à cet enfant, il avait fait -une chose dont il n'était déjà plus capable. - -Quoi qu'il en soit, cette dernière mauvaise action eut sur lui un effet -décisif; elle traversa brusquement ce chaos qu'il avait dans -l'intelligence et le dissipa, mit d'un côté les épaisseurs obscures et -de l'autre la lumière, et agit sur son âme, dans l'état où elle se -trouvait, comme de certains réactifs chimiques agissent sur un mélange -trouble en précipitant un élément et en clarifiant l'autre. - -Tout d'abord, avant même de s'examiner et de réfléchir, éperdu, comme -quelqu'un qui cherche à se sauver, il tâcha de retrouver l'enfant pour -lui rendre son argent, puis, quand il reconnut que cela était inutile et -impossible, il s'arrêta désespéré. Au moment où il s'écria: «je suis un -misérable!» il venait de s'apercevoir tel qu'il était, et il était déjà -à ce point séparé de lui-même, qu'il lui semblait qu'il n'était plus -qu'un fantôme, et qu'il avait là devant lui, en chair et en os, le bâton -à la main, la blouse sur les reins, son sac rempli d'objets volés sur le -dos, avec son visage résolu et morne, avec sa pensée pleine de projets -abominables, le hideux galérien Jean Valjean. - -L'excès du malheur, nous l'avons remarqué, l'avait fait en quelque sorte -visionnaire. Ceci fut donc comme une vision. Il vit véritablement ce -Jean Valjean, cette face sinistre devant lui. Il fut presque au moment -de se demander qui était cet homme, et il en eut horreur. - -Son cerveau était dans un de ces moments violents et pourtant -affreusement calmes où la rêverie est si profonde qu'elle absorbe la -réalité. On ne voit plus les objets qu'on a autour de soi, et l'on voit -comme en dehors de soi les figures qu'on a dans l'esprit. - -Il se contempla donc, pour ainsi dire, face à face, et en même temps, à -travers cette hallucination, il voyait dans une profondeur mystérieuse -une sorte de lumière qu'il prit d'abord pour un flambeau. En regardant -avec plus d'attention cette lumière qui apparaissait à sa conscience, il -reconnut qu'elle avait la forme humaine, et que ce flambeau était -l'évêque. - -Sa conscience considéra tour à tour ces deux hommes ainsi placés devant -elle, l'évêque et Jean Valjean. Il n'avait pas fallu moins que le -premier pour détremper le second. Par un de ces effets singuliers qui -sont propres à ces sortes d'extases, à mesure que sa rêverie se -prolongeait, l'évêque grandissait et resplendissait à ses yeux, Jean -Valjean s'amoindrissait et s'effaçait. À un certain moment il ne fut -plus qu'une ombre. Tout à coup il disparut. L'évêque seul était resté. - -Il remplissait toute l'âme de ce misérable d'un rayonnement magnifique. -Jean Valjean pleura longtemps. Il pleura à chaudes larmes, il pleura à -sanglots, avec plus de faiblesse qu'une femme, avec plus d'effroi qu'un -enfant. - -Pendant qu'il pleurait, le jour se faisait de plus en plus dans son -cerveau, un jour extraordinaire, un jour ravissant et terrible à la -fois. Sa vie passée, sa première faute, sa longue expiation, son -abrutissement extérieur, son endurcissement intérieur, sa mise en -liberté réjouie par tant de plans de vengeance, ce qui lui était arrivé -chez l'évêque, la dernière chose qu'il avait faite, ce vol de quarante -sous à un enfant, crime d'autant plus lâche et d'autant plus monstrueux -qu'il venait après le pardon de l'évêque, tout cela lui revint et lui -apparut, clairement, mais dans une clarté qu'il n'avait jamais vue -jusque-là. Il regarda sa vie, et elle lui parut horrible; son âme, et -elle lui parut affreuse. Cependant un jour doux était sur cette vie et -sur cette âme. Il lui semblait qu'il voyait Satan à la lumière du -paradis. - -Combien d'heures pleura-t-il ainsi? que fit-il après avoir pleuré? où -alla-t-il? on ne l'a jamais su. Il paraît seulement avéré que, dans -cette même nuit, le voiturier qui faisait à cette époque le service de -Grenoble et qui arrivait à Digne vers trois heures du matin, vit en -traversant la rue de l'évêché un homme dans l'attitude de la prière, à -genoux sur le pavé, dans l'ombre, devant la porte de monseigneur -Bienvenu. - - - - -Livre troisième--En l'année 1817 - - - - -Chapitre I - -L'année 1817 - - -1817 est l'année que Louis XVIII, avec un certain aplomb royal qui ne -manquait pas de fierté, qualifiait la vingt-deuxième de son règne. C'est -l'année où M. Bruguière de Sorsum était célèbre. Toutes les boutiques -des perruquiers, espérant la poudre et le retour de l'oiseau royal, -étaient badigeonnées d'azur et fleurdelysées. C'était le temps candide -où le comte Lynch siégeait tous les dimanches comme marguillier au banc -d'oeuvre de Saint-Germain-des-Prés en habit de pair de France, avec son -cordon rouge et son long nez, et cette majesté de profil particulière à -un homme qui a fait une action d'éclat. L'action d'éclat commise par M. -Lynch était ceci: avoir, étant maire de Bordeaux, le 12 mars 1814, donné -la ville un peu trop tôt à M. le duc d'Angoulême. De là sa pairie. En -1817, la mode engloutissait les petits garçons de quatre à six ans sous -de vastes casquettes en cuir maroquiné à oreillons assez ressemblantes à -des mitres d'esquimaux. L'armée française était vêtue de blanc, à -l'autrichienne; les régiments s'appelaient légions; au lieu de chiffres -ils portaient les noms des départements. Napoléon était à Sainte-Hélène, -et, comme l'Angleterre lui refusait du drap vert, il faisait retourner -ses vieux habits. En 1817, Pellegrini chantait, mademoiselle Bigottini -dansait; Potier régnait; Odry n'existait pas encore. Madame Saqui -succédait à Forioso. Il y avait encore des Prussiens en France. M. -Delalot était un personnage. La légitimité venait de s'affirmer en -coupant le poing, puis la tête, à Pleignier, à Carbonneau et à Tolleron. -Le prince de Talleyrand, grand chambellan, et l'abbé Louis, ministre -désigné des finances, se regardaient en riant du rire de deux augures; -tous deux avaient célébré, le 14 juillet 1790, la messe de la Fédération -au Champ de Mars; Talleyrand l'avait dite comme évêque, Louis l'avait -servie comme diacre. En 1817, dans les contre-allées de ce même Champ de -Mars, on apercevait de gros cylindres de bois, gisant sous la pluie, -pourrissant dans l'herbe, peints en bleu avec des traces d'aigles et -d'abeilles dédorées. C'étaient les colonnes qui, deux ans auparavant, -avaient soutenu l'estrade de l'empereur au Champ-de-Mai. Elles étaient -noircies çà et là de la brûlure du bivouac des Autrichiens baraqués près -du Gros-Caillou. Deux ou trois de ces colonnes avaient disparu dans les -feux de ces bivouacs et avaient chauffé les larges mains des -_kaiserlicks_. Le Champ de Mai avait eu cela de remarquable qu'il avait -été tenu au mois de juin et au Champ de Mars. En cette année 1817, deux -choses étaient populaires: le Voltaire-Touquet et la tabatière à la -Charte. L'émotion parisienne la plus récente était le crime de Dautun -qui avait jeté la tête de son frère dans le bassin du Marché-aux-Fleurs. -On commençait à faire au ministère de la marine une enquête sur cette -fatale frégate de la Méduse qui devait couvrir de honte Chaumareix et de -gloire Géricault. Le colonel Selves allait en Égypte pour y devenir -Soliman pacha. Le palais des Thermes, rue de la Harpe, servait de -boutique à un tonnelier. On voyait encore sur la plate-forme de la tour -octogone de l'hôtel de Cluny la petite logette en planches qui avait -servi d'observatoire à Messier, astronome de la marine sous Louis XVI. -La duchesse de Duras lisait à trois ou quatre amis, dans son boudoir -meublé d'X en satin bleu ciel, _Ourika_ inédite. On grattait les N au -Louvre. Le pont d'Austerlitz abdiquait et s'intitulait pont du Jardin du -Roi, double énigme qui déguisait à la fois le pont d'Austerlitz et le -jardin des Plantes. Louis XVIII, préoccupé, tout en annotant du coin de -l'ongle Horace, des héros qui se font empereurs et des sabotiers qui se -font dauphins, avait deux soucis: Napoléon et Mathurin Bruneau. -L'académie française donnait pour sujet de prix: _Le bonheur que procure -l'étude_. M. Bellart était officiellement éloquent. On voyait germer à -son ombre ce futur avocat général de Broè, promis aux sarcasmes de -Paul-Louis Courier. Il y avait un faux Chateaubriand appelé Marchangy, -en attendant qu'il y eut un faux Marchangy appelé d'Arlincourt. _Claire -d'Albe_ et _Malek-Adel_ étaient des chefs-d'oeuvre; madame Cottin était -déclarée le premier écrivain de l'époque. L'institut laissait rayer de -sa liste l'académicien Napoléon Bonaparte. Une ordonnance royale -érigeait Angoulême en école de marine, car, le duc d'Angoulême étant -grand amiral, il était évident que la ville d'Angoulême avait de droit -toutes les qualités d'un port de mer, sans quoi le principe monarchique -eût été entamé. On agitait en conseil des ministres la question de -savoir si l'on devait tolérer les vignettes représentant des voltiges -qui assaisonnaient les affiches de Franconi et qui attroupaient les -polissons des rues. M. Paër, auteur de l'_Agnese_, bonhomme à la face -carrée qui avait une verrue sur la joue, dirigeait les petits concerts -intimes de la marquise de Sassenaye, rue de la Ville-l'Évêque. Toutes -les jeunes filles chantaient _l'Ermite de Saint-Avelle_, paroles -d'Edmond Géraud. _Le Nain jaune_ se transformait en _Miroir_. Le café -Lemblin tenait pour l'empereur contre le café Valois qui tenait pour les -Bourbons. On venait de marier à une princesse de Sicile M. le duc de -Berry, déjà regardé du fond de l'ombre par Louvel. Il y avait un an que -madame de Staël était morte. Les gardes du corps sifflaient mademoiselle -Mars. Les grands journaux étaient tout petits. Le format était -restreint, mais la liberté était grande. _Le Constitutionnel_ était -constitutionnel. _La Minerve_ appelait Chateaubriand _Chateaubriant_. Ce -_t_ faisait beaucoup rire les bourgeois aux dépens du grand écrivain. -Dans des journaux vendus, des journalistes prostitués insultaient les -proscrits de 1815; David n'avait plus de talent, Arnault n'avait plus -d'esprit, Carnot n'avait plus de probité; Soult n'avait gagné aucune -bataille; il est vrai que Napoléon n'avait plus de génie. Personne -n'ignore qu'il est assez rare que les lettres adressées par la poste à -un exilé lui parviennent, les polices se faisant un religieux devoir de -les intercepter. Le fait n'est point nouveau; Descartes, banni, s'en -plaignait. Or, David ayant, dans un journal belge, montré quelque humeur -de ne pas recevoir les lettres qu'on lui écrivait, ceci paraissait -plaisant aux feuilles royalistes qui bafouaient à cette occasion le -proscrit. Dire: _les régicides_, ou dire: _les votants_, dire: _les -ennemis_, ou dire: _les alliés_, dire: _Napoléon_, ou dire: _Buonaparte_, -cela séparait deux hommes plus qu'un abîme. Tous les gens de bons sens -convenaient que l'ère des révolutions était à jamais fermée par le roi -Louis XVIII, surnommé «l'immortel auteur de la charte». Au terre-plein -du Pont-Neuf, on sculptait le mot _Redivivus_, sur le piédestal qui -attendait la statue de Henri IV. M. Piet ébauchait, rue Thérèse, n° 4, -son conciliabule pour consolider la monarchie. Les chefs de la droite -disaient dans les conjonctures graves: «Il faut écrire à Bacot». MM. -Canuel, O'Mahony et de Chappedelaine esquissaient, un peu approuvés de -Monsieur, ce qui devait être plus tard «la conspiration du bord de -l'eau». L'Épingle Noire complotait de son côté. Delaverderie s'abouchait -avec Trogoff. M. Decazes, esprit dans une certaine mesure libéral, -dominait. Chateaubriand, debout tous les matins devant sa fenêtre du n° -27 de la rue Saint-Dominique, en pantalon à pieds et en pantoufles, ses -cheveux gris coiffés d'un madras, les yeux fixés sur un miroir, une -trousse complète de chirurgien dentiste ouverte devant lui, se curait -les dents, qu'il avait charmantes, tout en dictant des variantes de _la -Monarchie selon la Charte_ à M. Pilorge, son secrétaire. La critique -faisant autorité préférait Lafon à Talma. M. de Féletz signait A.; M. -Hoffmann signait Z. Charles Nodier écrivait _Thérèse Aubert_. Le divorce -était aboli. Les lycées s'appelaient collèges. Les collégiens, ornés au -collet d'une fleur de lys d'or, s'y gourmaient à propos du roi de Rome. -La contre-police du château dénonçait à son altesse royale Madame le -portrait, partout exposé, de M. le duc d'Orléans, lequel avait meilleure -mine en uniforme de colonel général des houzards que M. le duc de Berry -en uniforme de colonel général des dragons; grave inconvénient. La ville -de Paris faisait redorer à ses frais le dôme des Invalides. Les hommes -sérieux se demandaient ce que ferait, dans telle ou telle occasion, M. -de Trinquelague; M. Clausel de Montals se séparait, sur divers points, -de M. Clausel de Coussergues; M. de Salaberry n'était pas content. Le -comédien Picard, qui était de l'Académie dont le comédien Molière -n'avait pu être, faisait jouer _les deux Philibert_ à l'Odéon, sur le -fronton duquel l'arrachement des lettres laissait encore lire -distinctement: THÉÂTRE DE L'IMPÉRATRICE. On prenait parti pour ou contre -Cugnet de Montarlot. Fabvier était factieux; Bavoux était -révolutionnaire. Le libraire Pélicier publiait une édition de Voltaire, -sous ce titre: _OEuvres de Voltaire_, de l'Académie française. «Cela -fait venir les acheteurs», disait cet éditeur naïf. L'opinion générale -était que M. Charles Loyson, serait le génie du siècle; l'envie -commençait à le mordre, signe de gloire; et l'on faisait sur lui ce -vers: - -_Même quand Loyson vole, on sent qu'il a des pattes._ - -Le cardinal Fesch refusant de se démettre, M. de Pins, archevêque -d'Amasie, administrait le diocèse de Lyon. La querelle de la vallée des -Dappes commençait entre la Suisse et la France par un mémoire du -capitaine Dufour, depuis général. Saint-Simon, ignoré, échafaudait son -rêve sublime. Il y avait à l'académie des sciences un Fourier célèbre -que la postérité a oublié et dans je ne sais quel grenier un Fourier -obscur dont l'avenir se souviendra. Lord Byron commençait à poindre; une -note d'un poème de Millevoye l'annonçait à la France en ces termes: _un -certain lord Baron_. David d'Angers s'essayait à pétrir le marbre. -L'abbé Caron parlait avec éloge, en petit comité de séminaristes, dans -le cul-de-sac des Feuillantines, d'un prêtre inconnu nommé Félicité -Robert qui a été plus tard Lamennais. Une chose qui fumait et clapotait -sur la Seine avec le bruit d'un chien qui nage allait et venait sous les -fenêtres des Tuileries, du pont Royal au pont Louis XV c'était une -mécanique bonne à pas grand'chose, une espèce de joujou, une rêverie -d'inventeur songe-creux, une utopie: un bateau à vapeur. Les Parisiens -regardaient cette inutilité avec indifférence. M. de Vaublanc, -réformateur de l'Institut par coup d'État, ordonnance et fournée, auteur -distingué de plusieurs académiciens, après en avoir fait, ne pouvait -parvenir à l'être. Le faubourg Saint-Germain et la pavillon Marsan -souhaitaient pour préfet de police M. Delaveau, à cause de sa dévotion. -Dupuytren et Récamier se prenaient de querelle à l'amphithéâtre de -l'École de médecine et se menaçaient du poing à propos de la divinité de -Jésus-Christ. Cuvier, un oeil sur la Genèse et l'autre sur la nature, -s'efforçait de plaire à la réaction bigote en mettant les fossiles -d'accord avec les textes et en faisant flatter Moïse par les -mastodontes. M. François de Neufchâteau, louable cultivateur de la -mémoire de Parmentier, faisait mille efforts pour que _pomme de terre_ -fût prononcée _parmentière_, et n'y réussissait point. L'abbé Grégoire, -ancien évêque, ancien conventionnel, ancien sénateur, était passé dans -la polémique royaliste à l'état «d'infâme Grégoire». Cette locution que -nous venons d'employer: _passer à l'état de_, était dénoncée comme -néologisme par M. Royer-Collard. On pouvait distinguer encore à sa -blancheur, sous la troisième arche du pont d'Iéna, la pierre neuve avec -laquelle, deux ans auparavant, on avait bouché le trou de mine pratiqué -par Blücher pour faire sauter le pont. La justice appelait à sa barre un -homme qui, en voyant entrer le comte d'Artois à Notre-Dame, avait dit -tout haut: _Sapristi! je regrette le temps où je voyais Bonaparte et -Talma entrer bras dessus bras dessous au Bal-Sauvage_. Propos séditieux. -Six mois de prison. Des traîtres se montraient déboutonnés; des hommes -qui avaient passé à l'ennemi la veille d'une bataille ne cachaient rien -de la récompense et marchaient impudiquement en plein soleil dans le -cynisme des richesses et des dignités; des déserteurs de Ligny et des -Quatre-Bras, dans le débraillé de leur turpitude payée, étalaient leur -dévouement monarchique tout nu; oubliant ce qui est écrit en Angleterre -sur la muraille intérieure des water-closets publics: _Please adjust -your dress before leaving_. - -Voilà, pêle-mêle, ce qui surnage confusément de l'année 1817, oubliée -aujourd'hui. L'histoire néglige presque toutes ces particularités, et ne -peut faire autrement; l'infini l'envahirait. Pourtant ces détails, qu'on -appelle à tort petits--il n'y a ni petits faits dans l'humanité, ni -petites feuilles dans la végétation--sont utiles. C'est de la -physionomie des années que se compose la figure des siècles. - -En cette année 1817, quatre jeunes Parisiens firent «une bonne farce». - - - - -Chapitre II - -Double quatuor - - -Ces Parisiens étaient l'un de Toulouse, l'autre de Limoges, le troisième -de Cahors et le quatrième de Montauban; mais ils étaient étudiants, et -qui dit étudiant dit parisien; étudier à Paris, c'est naître à Paris. - -Ces jeunes gens étaient insignifiants; tout le monde a vu ces -figures-là; quatre échantillons du premier venu; ni bons ni mauvais, ni -savants ni ignorants, ni des génies ni des imbéciles; beaux de ce -charmant avril qu'on appelle vingt ans. C'étaient quatre Oscars -quelconques, car à cette époque les Arthurs n'existaient pas encore. -_Brûlez pour lui les parfums d'Arabie_, s'écriait la romance, _Oscar -s'avance, Oscar, je vais le voir!_ On sortait d'Ossian, l'élégance était -scandinave et calédonienne, le genre anglais pur ne devait prévaloir que -plus tard, et le premier des Arthurs, Wellington, venait à peine de -gagner la bataille de Waterloo. - -Ces Oscars s'appelaient l'un Félix Tholomyès, de Toulouse; l'autre -Listolier, de Cahors; l'autre Fameuil, de Limoges; le dernier -Blachevelle, de Montauban. Naturellement chacun avait sa maîtresse. -Blachevelle aimait Favourite, ainsi nommée parce qu'elle était allée en -Angleterre; Listolier adorait Dahlia, qui avait pris pour nom de guerre -un nom de fleur; Fameuil idolâtrait Zéphine, abrégé de Joséphine; -Tholomyès avait Fantine, dite la Blonde à cause de ses beaux cheveux -couleur de soleil. - -Favourite, Dahlia, Zéphine et Fantine étaient quatre ravissantes filles, -parfumées et radieuses, encore un peu ouvrières, n'ayant pas tout à fait -quitté leur aiguille, dérangées par les amourettes, mais ayant sur le -visage un reste de la sérénité du travail et dans l'âme cette fleur -d'honnêteté qui dans la femme survit à la première chute. Il y avait une -des quatre qu'on appelait la jeune, parce qu'elle était la cadette; et -une qu'on appelait la vieille. La vieille avait vingt-trois ans. Pour ne -rien celer, les trois premières étaient plus expérimentées, plus -insouciantes et plus envolées dans le bruit de la vie que Fantine la -Blonde, qui en était à sa première illusion. - -Dahlia, Zéphine, et surtout Favourite, n'en auraient pu dire autant. Il -y avait déjà plus d'un épisode à leur roman à peine commencé, et -l'amoureux, qui s'appelait Adolphe au premier chapitre, se trouvait être -Alphonse au second, et Gustave au troisième. Pauvreté et coquetterie -sont deux conseillères fatales, l'une gronde, l'autre flatte; et les -belles filles du peuple les ont toutes les deux qui leur parlent bas à -l'oreille, chacune de son côté. Ces âmes mal gardées écoutent. De là les -chutes qu'elles font et les pierres qu'on leur jette. On les accable -avec la splendeur de tout ce qui est immaculé et inaccessible. Hélas! si -la _Yungfrau_ avait faim? - -Favourite, ayant été en Angleterre, avait pour admiratrices Zéphine et -Dahlia. Elle avait eu de très bonne heure un chez-soi. Son père était un -vieux professeur de mathématiques brutal et qui gasconnait; point marié, -courant le cachet malgré l'âge. Ce professeur, étant jeune, avait vu un -jour la robe d'une femme de chambre s'accrocher à un garde-cendre; il -était tombé amoureux de cet accident. Il en était résulté Favourite. -Elle rencontrait de temps en temps son père, qui la saluait. Un matin, -une vieille femme à l'air béguin était entrée chez elle et lui avait -dit: - ---Vous ne me connaissez pas, mademoiselle? - ---Non. - ---Je suis ta mère. - -Puis la vieille avait ouvert le buffet, bu et mangé, fait apporter un -matelas qu'elle avait, et s'était installée. Cette mère, grognon et -dévote, ne parlait jamais à Favourite, restait des heures sans souffler -mot, déjeunait, dînait et soupait comme quatre, et descendait faire -salon chez le portier, où elle disait du mal de sa fille. - -Ce qui avait entraîné Dahlia vers Listolier, vers d'autres peut-être, -vers l'oisiveté, c'était d'avoir de trop jolis ongles roses. Comment -faire travailler ces ongles-là? Qui veut rester vertueuse ne doit pas -avoir pitié de ses mains. Quant à Zéphine, elle avait conquis Fameuil -par sa petite manière mutine et caressante de dire: «Oui, monsieur». - -Les jeunes gens étant camarades, les jeunes filles étaient amies. Ces -amours-là sont toujours doublés de ces amitiés-là. - -Sage et philosophe, c'est deux; et ce qui le prouve, c'est que, toutes -réserves faites sur ces petits ménages irréguliers, Favourite, Zéphine -et Dahlia étaient des filles philosophes, et Fantine une fille sage. - -Sage, dira-t-on? et Tholomyès? Salomon répondrait que l'amour fait -partie de la sagesse. Nous nous bornons à dire que l'amour de Fantine -était un premier amour, un amour unique, un amour fidèle. - -Elle était la seule des quatre qui ne fût tutoyée que par un seul. - -Fantine était un de ces êtres comme il en éclôt, pour ainsi dire, au -fond du peuple. Sortie des plus insondables épaisseurs de l'ombre -sociale, elle avait au front le signe de l'anonyme et de l'inconnu. Elle -était née à Montreuil-sur-mer. De quels parents? Qui pourrait le dire? -On ne lui avait jamais connu ni père ni mère. Elle se nommait Fantine. -Pourquoi Fantine? On ne lui avait jamais connu d'autre nom. À l'époque -de sa naissance, le Directoire existait encore. Point de nom de famille, -elle n'avait pas de famille; point de nom de baptême, l'église n'était -plus là. Elle s'appela comme il plut au premier passant qui la rencontra -toute petite, allant pieds nus dans la rue. Elle reçut un nom comme elle -recevait l'eau des nuées sur son front quand il pleuvait. On l'appela la -petite Fantine. Personne n'en savait davantage. Cette créature humaine -était venue dans la vie comme cela. À dix ans, Fantine quitta la ville -et s'alla mettre en service chez des fermiers des environs. À quinze -ans, elle vint à Paris "chercher fortune". Fantine était belle et resta -pure le plus longtemps qu'elle put. C'était une jolie blonde avec de -belles dents. Elle avait de l'or et des perles pour dot, mais son or -était sur sa tête et ses perles étaient dans sa bouche. - -Elle travailla pour vivre; puis, toujours pour vivre, car le coeur a sa -faim aussi, elle aima. - -Elle aima Tholomyès. - -Amourette pour lui, passion pour elle. Les rues du quartier latin, -qu'emplit le fourmillement des étudiants et des grisettes, virent le -commencement de ce songe. Fantine, dans ces dédales de la colline du -Panthéon, où tant d'aventures se nouent et se dénouent, avait fui -longtemps Tholomyès, mais de façon à le rencontrer toujours. Il y a une -manière d'éviter qui ressemble à chercher. Bref, l'églogue eut lieu. - -Blachevelle, Listolier et Fameuil formaient une sorte de groupe dont -Tholomyès était la tête. C'était lui qui avait l'esprit. - -Tholomyès était l'antique étudiant vieux; il était riche; il avait -quatre mille francs de rente; quatre mille francs de rente, splendide -scandale sur la montagne Sainte-Geneviève. Tholomyès était un viveur de -trente ans, mal conservé. Il était ridé et édenté; et il ébauchait une -calvitie dont il disait lui-même sans tristesse: _crâne à trente ans, -genou à quarante_. Il digérait médiocrement, et il lui était venu un -larmoiement à un oeil. Mais à mesure que sa jeunesse s'éteignait, il -allumait sa gaîté; il remplaçait ses dents par des lazzis, ses cheveux -par la joie, sa santé par l'ironie, et son oeil qui pleurait riait sans -cesse. Il était délabré, mais tout en fleurs. Sa jeunesse, pliant bagage -bien avant l'âge, battait en retraite en bon ordre, éclatait de rire, et -l'on n'y voyait que du feu. Il avait eu une pièce refusée au Vaudeville. -Il faisait çà et là des vers quelconques. En outre, il doutait -supérieurement de toute chose, grande force aux yeux des faibles. Donc, -étant ironique et chauve, il était le chef. _Iron_ est un mot anglais -qui veut dire fer. Serait-ce de là que viendrait ironie? - -Un jour Tholomyès prit à part les trois autres, fît un geste d'oracle, -et leur dit: - ---Il y a bientôt un an que Fantine, Dahlia, Zéphine et Favourite nous -demandent de leur faire une surprise. Nous la leur avons promise -solennellement. Elles nous en parlent toujours, à moi surtout. De même -qu'à Naples les vieilles femmes crient à saint Janvier: _Faccia -gialluta, fa o miracolo_. Face jaune, fais ton miracle! nos belles me -disent sans cesse: «Tholomyès, quand accoucheras-tu de ta surprise?» En -même temps nos parents nous écrivent. Scie des deux côtés. Le moment me -semble venu. Causons. - -Sur ce, Tholomyès baissa la voix, et articula mystérieusement quelque -chose de si gai qu'un vaste et enthousiaste ricanement sortit des quatre -bouches à la fois et que Blachevelle s'écria: - ---Ça, c'est une idée! - -Un estaminet plein de fumée se présenta, ils y entrèrent, et le reste de -leur conférence se perdit dans l'ombre. - -Le résultat de ces ténèbres fut une éblouissante partie de plaisir qui -eut lieu le dimanche suivant, les quatre jeunes gens invitant les quatre -jeunes filles. - - - - -Chapitre III - -Quatre à quatre - - -Ce qu'était une partie de campagne d'étudiants et de grisettes, il y a -quarante-cinq ans, on se le représente malaisément aujourd'hui. Paris -n'a plus les mêmes environs; la figure de ce qu'on pourrait appeler la -vie circumparisienne a complètement changé depuis un demi-siècle; où il -y avait le coucou, il y a le wagon; où il y avait la patache, il y a le -bateau à vapeur; on dit aujourd'hui Fécamp comme on disait Saint-Cloud. -Le Paris de 1862 est une ville qui a la France pour banlieue. - -Les quatre couples accomplirent consciencieusement toutes les folies -champêtres possibles alors. On entrait dans les vacances, et c'était une -chaude et claire journée d'été. La veille, Favourite, la seule qui sût -écrire, avait écrit ceci à Tholomyès au nom des quatre: «C'est un bonne -heure de sortir de bonheur.» C'est pourquoi ils se levèrent à cinq -heures du matin. Puis ils allèrent à Saint-Cloud par le coche, -regardèrent la cascade à sec, et s'écrièrent: «Cela doit être bien beau -quand il y a de l'eau!» déjeunèrent à la _Tête-Noire_, où Castaing -n'avait pas encore passé, se payèrent une partie de bagues au quinconce -du grand bassin, montèrent à la lanterne de Diogène, jouèrent des -macarons à la roulette du pont de Sèvres, cueillirent des bouquets à -Puteaux, achetèrent des mirlitons à Neuilly, mangèrent partout des -chaussons de pommes, furent parfaitement heureux. - -Les jeunes filles bruissaient et bavardaient comme des fauvettes -échappées. C'était un délire. Elles donnaient par moments de petites -tapes aux jeunes gens. Ivresse matinale de la vie! Adorables années! -L'aile des libellules frissonne. Oh! qui que vous soyez, vous -souvenez-vous? Avez-vous marché dans les broussailles, en écartant les -branches à cause de la tête charmante qui vient derrière vous? Avez-vous -glissé en riant sur quelque talus mouillé par la pluie avec une femme -aimée qui vous retient par la main et qui s'écrie: «Ah! mes brodequins -tout neufs! dans quel état ils sont!» - -Disons tout de suite que cette joyeuse contrariété, une ondée, manqua à -cette compagnie de belle humeur, quoique Favourite eût dit en partant, -avec un accent magistral et maternel: _Les limaces se promènent dans les -sentiers. Signe de pluie, mes enfants_. - -Toutes quatre étaient follement jolies. Un bon vieux poète classique, -alors en renom, un bonhomme qui avait une Éléonore, M. le chevalier de -Labouïsse, errant ce jour-là sous les marronniers de Saint-Cloud, les -vit passer vers dix heures du matin; il s'écria: _Il y en a une de -trop_, songeant aux Grâces. Favourite, l'amie de Blachevelle, celle de -vingt-trois ans, la vieille, courait en avant sous les grandes branches -vertes, sautait les fossés, enjambait éperdument les buissons, et -présidait cette gaîté avec une verve de jeune faunesse. Zéphine et -Dahlia, que le hasard avait faites belles de façon qu'elles se faisaient -valoir en se rapprochant et se complétaient, ne se quittaient point, par -instinct de coquetterie plus encore que par amitié, et, appuyées l'une à -l'autre, prenaient des poses anglaises; les premiers _keepsakes_ -venaient de paraître, la mélancolie pointait pour les femmes, comme, -plus tard, le byronisme pour les hommes, et les cheveux du sexe tendre -commençaient à s'éplorer. Zéphine et Dahlia étaient coiffées en -rouleaux. Listolier et Fameuil, engagés dans une discussion sur leurs -professeurs, expliquaient à Fantine la différence qu'il y avait entre M. -Delvincourt et M. Blondeau. - -Blachevelle semblait avoir été créé expressément pour porter sur son -bras le dimanche le châle-ternaux boiteux de Favourite. - -Tholomyès suivait, dominant le groupe. Il était très gai, mais on -sentait en lui le gouvernement; il y avait de la dictature dans sa -jovialité; son ornement principal était un pantalon jambes-d'éléphant, -en nankin, avec sous-pieds de tresse de cuivre; il avait un puissant -rotin de deux cents francs à la main, et, comme il se permettait tout, -une chose étrange appelée cigare, à la bouche. Rien n'étant sacré pour -lui, il fumait. - ---Ce Tholomyès est étonnant, disaient les autres avec vénération. Quels -pantalons! quelle énergie! - -Quant à Fantine, c'était la joie. Ses dents splendides avaient -évidemment reçu de Dieu une fonction, le rire. Elle portait à sa main -plus volontiers que sur sa tête son petit chapeau de paille cousue, aux -longues brides blanches. Ses épais cheveux blonds, enclins à flotter et -facilement dénoués et qu'il fallait rattacher sans cesse, semblaient -faits pour la fuite de Galatée sous les saules. Ses lèvres roses -babillaient avec enchantement. Les coins de sa bouche voluptueusement -relevés, comme aux mascarons antiques d'Érigone, avaient l'air -d'encourager les audaces; mais ses longs cils pleins d'ombre -s'abaissaient discrètement sur ce brouhaha du bas du visage comme pour -mettre le holà. Toute sa toilette avait on ne sait quoi de chantant et -de flambant. Elle avait une robe de barège mauve, de petits -souliers-cothurnes mordorés dont les rubans traçaient des X sur son fin -bas blanc à jour, et cette espèce de spencer en mousseline, invention -marseillaise, dont le nom, canezou, corruption du mot _quinze août_ -prononcé à la Canebière, signifie beau temps, chaleur et midi. Les trois -autres, moins timides, nous l'avons dit, étaient décolletées tout net, -ce qui, l'été, sous des chapeaux couverts de fleurs, a beaucoup de grâce -et d'agacerie; mais, à côté de ces ajustements hardis, le canezou de la -blonde Fantine, avec ses transparences, ses indiscrétions et ses -réticences, cachant et montrant à la fois, semblait une trouvaille -provocante de la décence, et la fameuse cour d'amour, présidée par la -vicomtesse de Cette aux yeux vert de mer, eût peut-être donné le prix de -la coquetterie à ce canezou qui concourait pour la chasteté. Le plus -naïf est quelquefois le plus savant. Cela arrive. - -Éclatante de face, délicate de profil, les yeux d'un bleu profond, les -paupières grasses, les pieds cambrés et petits, les poignets et les -chevilles admirablement emboîtés, la peau blanche laissant voir çà et là -les arborescences azurées des veines, la joue puérile et franche, le cou -robuste des Junons éginétiques, la nuque forte et souple, les épaules -modelées comme par Coustou, ayant au centre une voluptueuse fossette -visible à travers la mousseline; une gaîté glacée de rêverie; -sculpturale et exquise; telle était Fantine; et l'on devinait sous ces -chiffons une statue, et dans cette statue une âme. - -Fantine était belle, sans trop le savoir. Les rares songeurs, prêtres -mystérieux du beau, qui confrontent silencieusement toute chose à la -perfection, eussent entrevu en cette petite ouvrière, à travers la -transparence de la grâce parisienne, l'antique euphonie sacrée. Cette -fille de l'ombre avait de la race. Elle était belle sous les deux -espèces, qui sont le style et le rythme. Le style est la forme de -l'idéal; le rythme en est le mouvement. - -Nous avons dit que Fantine était la joie, Fantine était aussi la pudeur. - -Pour un observateur qui l'eût étudiée attentivement, ce qui se dégageait -d'elle, à travers toute cette ivresse de l'âge, de la saison et de -l'amourette, c'était une invincible expression de retenue et de -modestie. Elle restait un peu étonnée. Ce chaste étonnement-là est la -nuance qui sépare Psyché de Vénus. Fantine avait les longs doigts blancs -et fins de la vestale qui remue les cendres du feu sacré avec une -épingle d'or. Quoiqu'elle n'eût rien refusé, on ne le verra que trop, à -Tholomyès, son visage, au repos, était souverainement virginal; une -sorte de dignité sérieuse et presque austère l'envahissait soudainement -à de certaines heures, et rien n'était singulier et troublant comme de -voir la gaîté s'y éteindre si vite et le recueillement y succéder sans -transition à l'épanouissement. Cette gravité subite, parfois sévèrement -accentuée, ressemblait au dédain d'une déesse. Son front, son nez et son -menton offraient cet équilibre de ligne, très distinct de l'équilibre de -proportion, et d'où résulte l'harmonie du visage; dans l'intervalle si -caractéristique qui sépare la base du nez de la lèvre supérieure, elle -avait ce pli imperceptible et charmant, signe mystérieux de la chasteté -qui rendit Barberousse amoureux d'une Diane trouvée dans les fouilles -d'Icône. - -L'amour est une faute; soit. Fantine était l'innocence surnageant sur la -faute. - - - - -Chapitre IV - -Tholomyès est si joyeux qu'il chante une chanson espagnole - - -Cette journée-là était d'un bout à l'autre faite d'aurore. Toute la -nature semblait avoir congé, et rire. Les parterres de Saint-Cloud -embaumaient; le souffle de la Seine remuait vaguement les feuilles; -les branches gesticulaient dans le vent; les abeilles mettaient les -jasmins au pillage; toute une bohème de papillons s'ébattait dans les -achillées, les trèfles et les folles avoines; il y avait dans l'auguste -parc du roi de France un tas de vagabonds, les oiseaux. - -Les quatre joyeux couples, mêlés au soleil, aux champs, aux fleurs, aux -arbres, resplendissaient. - -Et, dans cette communauté de paradis, parlant, chantant, courant, -dansant, chassant aux papillons, cueillant des liserons, mouillant leurs -bas à jour roses dans les hautes herbes, fraîches, folles, point -méchantes, toutes recevaient un peu çà et là les baisers de tous, -excepté Fantine, enfermée dans sa vague résistance rêveuse et farouche, -et qui aimait. - ---Toi, lui disait Favourite, tu as toujours l'air chose. - -Ce sont là les joies. Ces passages de couples heureux sont un appel -profond à la vie et à la nature, et font sortir de tout la caresse et la -lumière. Il y avait une fois une fée qui fit les prairies et les arbres -exprès pour les amoureux. De là cette éternelle école buissonnière des -amants qui recommence sans cesse et qui durera tant qu'il y aura des -buissons et des écoliers. De là la popularité du printemps parmi les -penseurs. Le patricien et le gagne-petit, le duc et pair et le robin, -les gens de la cour et les gens de la ville, comme on parlait autrefois, -tous sont sujets de cette fée. On rit, on se cherche, il y a dans l'air -une clarté d'apothéose, quelle transfiguration que d'aimer! Les clercs -de notaire sont des dieux. Et les petits cris, les poursuites dans -l'herbe, les tailles prises au vol, ces jargons qui sont des mélodies, -ces adorations qui éclatent dans la façon de dire une syllabe, ces -cerises arrachées d'une bouche à l'autre, tout cela flamboie et passe -dans des gloires célestes. Les belles filles font un doux gaspillage -d'elles-mêmes. On croit que cela ne finira jamais. Les philosophes, les -poètes, les peintres regardent ces extases et ne savent qu'en faire, -tant cela les éblouit. Le départ pour Cythère! s'écrie Watteau; Lancret, -le peintre de la roture, contemple ses bourgeois envolés dans le bleu; -Diderot tend les bras à toutes ces amourettes, et d'Urfé y mêle des -druides. - -Après le déjeuner les quatre couples étaient allés voir, dans ce qu'on -appelait alors le carré du roi, une plante nouvellement arrivée de -l'Inde, dont le nom nous échappe en ce moment, et qui à cette époque -attirait tout Paris à Saint-Cloud; c'était un bizarre et charmant -arbrisseau haut sur tige, dont les innombrables branches fines comme des -fils, ébouriffées, sans feuilles, étaient couvertes d'un million de -petites rosettes blanches; ce qui faisait que l'arbuste avait l'air -d'une chevelure pouilleuse de fleurs. Il y avait toujours foule à -l'admirer. - -L'arbuste vu, Tholomyès s'était écrié: «J'offre des ânes!» et, prix fait -avec un ânier, ils étaient revenus par Vanves et Issy. À Issy, incident. -Le parc, Bien National possédé à cette époque par le munitionnaire -Bourguin, était d'aventure tout grand ouvert. Ils avaient franchi la -grille, visité l'anachorète mannequin dans sa grotte, essayé les petits -effets mystérieux du fameux cabinet des miroirs, lascif traquenard digne -d'un satyre devenu millionnaire ou de Turcaret métamorphosé en Priape. -Ils avaient robustement secoué le grand filet balançoire attaché aux -deux châtaigniers célébrés par l'abbé de Bernis. Tout en y balançant ces -belles l'une après l'autre, ce qui faisait, parmi les rires universels, -des plis de jupe envolée où Greuze eût trouvé son compte, le toulousain -Tholomyès, quelque peu espagnol, Toulouse est cousine de Tolosa, -chantait, sur une mélopée mélancolique, la vieille chanson _gallega_ -probablement inspirée par quelque belle fille lancée à toute volée sur -une corde entre deux arbres: - - _Soy de Badajoz._ - _Amor me llama._ - _Toda mi alma_ - _Es en mi ojos_ - _Porque enseñas_ - _À tus piernas._ - -Fantine seule refusa de se balancer. - ---Je n'aime pas qu'on ait du genre comme ça, murmura assez aigrement -Favourite. - -Les ânes quittés, joie nouvelle; on passa la Seine en bateau, et de -Passy, à pied, ils gagnèrent la barrière de l'Étoile. Ils étaient, on -s'en souvient, debout depuis cinq heures du matin; mais, bah! _il n'y a -pas de lassitude le dimanche_, disait Favourite; _le dimanche, la -fatigue ne travaille pas_. Vers trois heures les quatre couples, effarés -de bonheur, dégringolaient aux montagnes russes, édifice singulier qui -occupait alors les hauteurs Beaujon et dont on apercevait la ligne -serpentante au-dessus des arbres des Champs-Élysées. - -De temps en temps Favourite s'écriait: - ---Et la surprise? je demande la surprise. - ---Patience, répondait Tholomyès. - - - - -Chapitre V - -Chez Bombarda - - -Les montagnes russes épuisées, on avait songé au dîner; et le radieux -huitain, enfin un peu las, s'était échoué au cabaret Bombarda, -succursale qu'avait établie aux Champs-Élysées ce fameux restaurateur -Bombarda, dont on voyait alors l'enseigne rue de Rivoli à côté du -passage Delorme. - -Une chambre grande, mais laide, avec alcôve et lit au fond (vu la -plénitude du cabaret le dimanche, il avait fallu accepter ce gîte); deux -fenêtres d'où l'on pouvait contempler, à travers les ormes, le quai et -la rivière; un magnifique rayon d'août effleurant les fenêtres; deux -tables; sur l'une une triomphante montagne de bouquets mêlés à des -chapeaux d'hommes et de femmes; à l'autre les quatre couples attablés -autour d'un joyeux encombrement de plats, d'assiettes, de verres et de -bouteilles; des cruchons de bière mêlés à des flacons de vin; peu -d'ordre sur la table, quelque désordre dessous; - - _Ils faisaient sous la table_ - _Un bruit, un trique-trac de pieds épouvantable_ - -dit Molière. - -Voilà où en était vers quatre heures et demie du soir la bergerade -commencée à cinq heures du matin. Le soleil déclinait, l'appétit -s'éteignait. - -Les Champs-Élysées, pleins de soleil et de foule, n'étaient que lumière -et poussière, deux choses dont se compose la gloire. Les chevaux de -Marly, ces marbres hennissants, se cabraient dans un nuage d'or. Les -carrosses allaient et venaient. Un escadron de magnifiques gardes du -corps, clairon en tête, descendait l'avenue de Neuilly; le drapeau -blanc, vaguement rose au soleil couchant, flottait sur le dôme des -Tuileries. La place de la Concorde, redevenue alors place Louis XV, -regorgeait de promeneurs contents. Beaucoup portaient la fleur de lys -d'argent suspendue au ruban blanc moiré qui, en 1817, n'avait pas encore -tout à fait disparu des boutonnières. Çà et là au milieu des passants -faisant cercle et applaudissant, des rondes de petites filles jetaient -au vent une bourrée bourbonienne alors célèbre, destinée à foudroyer les -Cent-Jours, et qui avait pour ritournelle: - - _Rendez-nous notre père de Gand,_ - _Rendez-nous notre père._ - -Des tas de faubouriens endimanchés, parfois même fleurdelysés comme les -bourgeois, épars dans le grand carré et dans le carré Marigny, jouaient -aux bagues et tournaient sur les chevaux de bois; d'autres buvaient; -quelques-uns, apprentis imprimeurs, avaient des bonnets de papier; on -entendait leurs rires. Tout était radieux. C'était un temps de paix -incontestable et de profonde sécurité royaliste; c'était l'époque où un -rapport intime et spécial du préfet de police Anglès au roi sur les -faubourgs de Paris se terminait par ces lignes: «Tout bien considéré, -sire, il n'y a rien à craindre de ces gens-là. Ils sont insouciants et -indolents comme des chats. Le bas peuple des provinces est remuant, -celui de Paris ne l'est pas. Ce sont tous petits hommes. Sire, il en -faudrait deux bout à bout pour faire un de vos grenadiers. Il n'y a -point de crainte du côté de la populace de la capitale. Il est -remarquable que la taille a encore décru dans cette population depuis -cinquante ans; et le peuple des faubourgs de Paris est plus petit -qu'avant la révolution. Il n'est point dangereux. En somme, c'est de la -canaille bonne.» - -Qu'un chat puisse se changer en lion, les préfets de police ne le -croient pas possible; cela est pourtant, et c'est là le miracle du -peuple de Paris. Le chat d'ailleurs, si méprisé du comte Anglès, avait -l'estime des républiques antiques; il incarnait à leurs yeux la liberté, -et, comme pour servir de pendant à la Minerve aptère du Pirée, il y -avait sur la place publique de Corinthe le colosse de bronze d'un chat. -La police naïve de la restauration voyait trop «en beau» le peuple de -Paris. Ce n'est point, autant qu'on le croit, de la «canaille bonne». Le -Parisien est au Français ce que l'Athénien était au Grec; personne ne -dort mieux que lui, personne n'est plus franchement frivole et paresseux -que lui, personne mieux que lui n'a l'air d'oublier; qu'on ne s'y fie -pas pourtant; il est propre à toute sorte de nonchalance, mais, quand il -y a de la gloire au bout, il est admirable à toute espèce de furie. -Donnez-lui une pique, il fera le 10 août; donnez-lui un fusil, vous -aurez Austerlitz. Il est le point d'appui de Napoléon et la ressource de -Danton. S'agit-il de la patrie? il s'enrôle; s'agit-il de la liberté? il -dépave. Gare! ses cheveux pleins de colère sont épiques; sa blouse se -drape en chlamyde. Prenez garde. De la première rue Greneta venue, il -fera des fourches caudines. Si l'heure sonne, ce faubourien va grandir, -ce petit homme va se lever, et il regardera d'une façon terrible, et son -souffle deviendra tempête, et il sortira de cette pauvre poitrine grêle -assez de vent pour déranger les plis des Alpes. C'est grâce au -faubourien de Paris que la révolution, mêlée aux armées, conquiert -l'Europe. Il chante, c'est sa joie. Proportionnez sa chanson à sa -nature, et vous verrez! Tant qu'il n'a pour refrain que la Carmagnole, -il ne renverse que Louis XVI; faites-lui chanter la Marseillaise, il -délivrera le monde. - -Cette note écrite en marge du rapport Anglès, nous revenons à nos quatre -couples. Le dîner, comme nous l'avons dit, s'achevait. - - - - -Chapitre VI - -Chapitre où l'on s'adore - - -Propos de table et propos d'amour; les uns sont aussi insaisissables que -les autres; les propos d'amour sont des nuées, les propos de table sont -des fumées. - -Fameuil et Dahlia fredonnaient; Tholomyès buvait; Zéphine riait, Fantine -souriait. Listolier soufflait dans une trompette de bois achetée à -Saint-Cloud. Favourite regardait tendrement Blachevelle et disait: - ---Blachevelle, je t'adore. - -Ceci amena une question de Blachevelle: - ---Qu'est-ce que tu ferais, Favourite, si je cessais de t'aimer? - ---Moi! s'écria Favourite. Ah! ne dis pas cela, même pour rire! Si tu -cessais de m'aimer, je te sauterais après, je te grifferais, je te -gratignerais, je te jetterais de l'eau, je te ferais arrêter. - -Blachevelle sourit avec la fatuité voluptueuse d'un homme chatouillé à -l'amour-propre. Favourite reprit: - ---Oui, je crierais à la garde! Ah! je me gênerais par exemple! Canaille! - -Blachevelle, extasié, se renversa sur sa chaise et ferma -orgueilleusement les deux yeux. - -Dahlia, tout en mangeant, dit bas à Favourite dans le brouhaha: - ---Tu l'idolâtres donc bien, ton Blachevelle? - ---Moi, je le déteste, répondit Favourite du même ton en ressaisissant sa -fourchette. Il est avare. J'aime le petit d'en face de chez moi. Il est -très bien, ce jeune homme-là, le connais-tu? On voit qu'il a le genre -d'être acteur. J'aime les acteurs. Sitôt qu'il rentre, sa mère dit: «Ah! -mon Dieu! ma tranquillité est perdue. Le voilà qui va crier. Mais, mon -ami, tu me casses la tête!» Parce qu'il va dans la maison, dans des -greniers à rats, dans des trous noirs, si haut qu'il peut monter,--et -chanter, et déclamer, est-ce que je sais, moi? qu'on l'entend d'en bas! -Il gagne déjà vingt sous par jour chez un avoué à écrire de la chicane. -Il est fils d'un ancien chantre de Saint-Jacques-du-Haut-Pas. Ah! il est -très bien. Il m'idolâtre tant qu'un jour qu'il me voyait faire de la -pâte pour des crêpes, il m'a dit: _Mamselle, faites des beignets de vos -gants et je les mangerai_. Il n'y a que les artistes pour dire des -choses comme ça. Ah! il est très bien. Je suis en train d'être insensée -de ce petit-là. C'est égal, je dis à Blachevelle que je l'adore. Comme -je mens! Hein? comme je mens! - -Favourite fit une pause, et continua: - ---Dahlia, vois-tu, je suis triste. Il n'a fait que pleuvoir tout l'été, -le vent m'agace, le vent ne décolère pas, Blachevelle est très pingre, -c'est à peine s'il y a des petits pois au marché, on ne sait que manger, -j'ai le spleen, comme disent les Anglais, le beurre est si cher! et -puis, vois, c'est une horreur, nous dînons dans un endroit où il y a un -lit, ça me dégoûte de la vie. - - - - -Chapitre VII - -Sagesse de Tholomyès - - -Cependant, tandis que quelques-uns chantaient, les autres causaient -tumultueusement, et tous ensemble; ce n'était plus que du bruit. -Tholomyès intervint: - ---Ne parlons point au hasard ni trop vite, s'écria-t-il. Méditons si -nous voulons être éblouissants. Trop d'improvisation vide bêtement -l'esprit. Bière qui coule n'amasse point de mousse. Messieurs, pas de -hâte. Mêlons la majesté à la ripaille; mangeons avec recueillement; -festinons lentement. Ne nous pressons pas. Voyez le printemps; s'il se -dépêche, il est flambé, c'est-à-dire gelé. L'excès de zèle perd les -pêchers et les abricotiers. L'excès de zèle tue la grâce et la joie des -bons dîners. Pas de zèle, messieurs! Grimod de la Reynière est de l'avis -de Talleyrand. - -Une sourde rébellion gronda dans le groupe. - ---Tholomyès, laisse-nous tranquilles, dit Blachevelle. - ---À bas le tyran! dit Fameuil. - ---Bombarda, Bombance et Bamboche! cria Listolier. - ---Le dimanche existe, reprit Fameuil. - ---Nous sommes sobres, ajouta Listolier. - ---Tholomyès, fit Blachevelle, contemple mon calme. - ---Tu en es le marquis, répondit Tholomyès. - -Ce médiocre jeu de mots fit l'effet d'une pierre dans une mare. Le -marquis de Montcalm était un royaliste alors célèbre. Toutes les -grenouilles se turent. - ---Amis, s'écria Tholomyès, de l'accent d'un homme qui ressaisit -l'empire, remettez-vous. Il ne faut pas que trop de stupeur accueille ce -calembour tombé du ciel. Tout ce qui tombe de la sorte n'est pas -nécessairement digne d'enthousiasme et de respect. Le calembour est la -fiente de l'esprit qui vole. Le lazzi tombe n'importe où; et l'esprit, -après la ponte d'une bêtise, s'enfonce dans l'azur. Une tache blanchâtre -qui s'aplatit sur le rocher n'empêche pas le condor de planer. Loin de -moi l'insulte au calembour! Je l'honore dans la proportion de ses -mérites; rien de plus. Tout ce qu'il y a de plus auguste, de plus -sublime et de plus charmant dans l'humanité, et peut-être hors de -l'humanité, a fait des jeux de mots. Jésus-Christ a fait un calembour -sur saint Pierre, Moïse sur Isaac, Eschyle sur Polynice, Cléopâtre sur -Octave. Et notez que ce calembour de Cléopâtre a précédé la bataille -d'Actium, et que, sans lui, personne ne se souviendrait de la ville de -Toryne, nom grec qui signifie cuiller à pot. Cela concédé, je reviens à -mon exhortation. Mes frères, je le répète, pas de zèle, pas de -tohu-bohu, pas d'excès, même en pointes, gaîtés, liesses et jeux de -mots. Écoutez-moi, j'ai la prudence d'Amphiaraüs et la calvitie de -César. Il faut une limite, même aux rébus. _Est modus in rebus_. Il faut -une limite, même aux dîners. Vous aimez les chaussons aux pommes, -mesdames, n'en abusez pas. Il faut, même en chaussons, du bon sens et de -l'art. La gloutonnerie châtie le glouton. Gula punit Gulax. -L'indigestion est chargée par le bon Dieu de faire de la morale aux -estomacs. Et, retenez ceci: chacune de nos passions, même l'amour, a un -estomac qu'il ne faut pas trop remplir. En toute chose il faut écrire à -temps le mot _finis_, il faut se contenir, quand cela devient urgent, -tirer le verrou sur son appétit, mettre au violon sa fantaisie et se -mener soi-même au poste. Le sage est celui qui sait à un moment donné -opérer sa propre arrestation. Ayez quelque confiance en moi. Parce que -j'ai fait un peu mon droit, à ce que me disent mes examens, parce que je -sais la différence qu'il y a entre la question mue et la question -pendante, parce que j'ai soutenu une thèse en latin sur la manière dont -on donnait la torture à Rome au temps où Munatius Demens était questeur -du Parricide, parce que je vais être docteur, à ce qu'il paraît, il ne -s'ensuit pas de toute nécessité que je sois un imbécile. Je vous -recommande la modération dans vos désirs. Vrai comme je m'appelle Félix -Tholomyès, je parle bien. Heureux celui qui, lorsque l'heure a sonné, -prend un parti héroïque, et abdique comme Sylla, ou Origène! - -Favourite écoutait avec une attention profonde. - ---Félix! dit-elle, quel joli mot! j'aime ce nom-là. C'est en latin. Ça -veut dire Prosper. - -Tholomyès poursuivit: - ---Quirites, gentlemen, Caballeros, mes amis! voulez-vous ne sentir aucun -aiguillon et vous passer de lit nuptial et braver l'amour? Rien de plus -simple. Voici la recette: la limonade, l'exercice outré, le travail -forcé, éreintez-vous, traînez des blocs, ne dormez pas, veillez, -gorgez-vous de boissons nitreuses et de tisanes de nymphaeas, savourez -des émulsions de pavots et d'agnuscastus, assaisonnez-moi cela d'une -diète sévère, crevez de faim, et joignez-y les bains froids, les -ceintures d'herbes, l'application d'une plaque de plomb, les lotions -avec la liqueur de Saturne et les fomentations avec l'oxycrat. - ---J'aime mieux une femme, dit Listolier. - ---La femme! reprit Tholomyès, méfiez-vous-en. Malheur à celui qui se -livre au coeur changeant de la femme! La femme est perfide et tortueuse. -Elle déteste le serpent par jalousie de métier. Le serpent, c'est la -boutique en face. - ---Tholomyès, cria Blachevelle, tu es ivre! - ---Pardieu! dit Tholomyès. - ---Alors sois gai, reprit Blachevelle. - -Et, remplissant son verre, il se leva: - ---Gloire au vin! _Nunc te, Bacche, canam_! Pardon, mesdemoiselles, c'est -de l'espagnol. Et la preuve, señoras, la voici: tel peuple, telle -futaille. L'arrobe de Castille contient seize litres, le cantaro -d'Alicante douze, l'almude des Canaries vingt-cinq, le cuartin des -Baléares vingt-six, la botte du czar Pierre trente. Vive ce czar qui -était grand, et vive sa botte qui était plus grande encore! Mesdames, un -conseil d'ami: trompez-vous de voisin, si bon vous semble. Le propre de -l'amour, c'est d'errer. L'amourette n'est pas faite pour s'accroupir et -s'abrutir comme une servante anglaise qui a le calus du scrobage aux -genoux. Elle n'est pas faite pour cela, elle erre gaîment, la douce -amourette! On a dit: l'erreur est humaine; moi je dis: l'erreur est -amoureuse. Mesdames, je vous idolâtre toutes. Ô Zéphine, ô Joséphine, -figure plus que chiffonnée, vous seriez charmante, si vous n'étiez de -travers. Vous avez l'air d'un joli visage sur lequel, par mégarde, on -s'est assis. Quant à Favourite, ô nymphes et muses! un jour que -Blachevelle passait le ruisseau de la rue Guérin-Boisseau, il vit une -belle fille aux bas blancs et bien tirés qui montrait ses jambes. Ce -prologue lui plut, et Blachevelle aima. Celle qu'il aima était -Favourite. Ô Favourite, tu as des lèvres ioniennes. Il y avait un -peintre grec, appelé Euphorion, qu'on avait surnommé le peintre des -lèvres. Ce Grec seul eût été digne de peindre ta bouche! Écoute! avant -toi, il n'y avait pas de créature digne de ce nom. Tu es faite pour -recevoir la pomme comme Vénus ou pour la manger comme Ève. La beauté -commence à toi. Je viens de parler d'Ève, c'est toi qui l'as créée. Tu -mérites le brevet d'invention de la jolie femme. Ô Favourite, je cesse -de vous tutoyer, parce que je passe de la poésie à la prose. Vous -parliez de mon nom tout à l'heure. Cela m'a attendri; mais, qui que nous -soyons, méfions-nous des noms. Ils peuvent se tromper. Je me nomme Félix -et ne suis pas heureux. Les mots sont des menteurs. N'acceptons pas -aveuglément les indications qu'ils nous donnent. Ce serait une erreur -d'écrire à Liège pour avoir des bouchons et à Pau pour avoir des gants. -Miss Dahlia, à votre place, je m'appellerais Rosa. Il faut que la fleur -sente bon et que la femme ait de l'esprit. Je ne dis rien de Fantine, -c'est une songeuse, une rêveuse, une pensive, une sensitive; c'est un -fantôme ayant la forme d'une nymphe et la pudeur d'une nonne, qui se -fourvoie dans la vie de grisette, mais qui se réfugie dans les -illusions, et qui chante, et qui prie, et qui regarde l'azur sans trop -savoir ce qu'elle voit ni ce qu'elle fait, et qui, les yeux au ciel, -erre dans un jardin où il y a plus d'oiseaux qu'il n'en existe! Ô -Fantine, sache ceci: moi Tholomyès, je suis une illusion; mais elle ne -m'entend même pas, la blonde fille des chimères! Du reste, tout en elle -est fraîcheur, suavité, jeunesse, douce clarté matinale. Ô Fantine, -fille digne de vous appeler marguerite ou perle, vous êtes une femme du -plus bel orient. Mesdames, un deuxième conseil: ne vous mariez point; le -mariage est une greffe; cela prend bien ou mal; fuyez ce risque. Mais, -bah! qu'est-ce que je chante là? Je perds mes paroles. Les filles sont -incurables sur l'épousaille; et tout ce que nous pouvons dire, nous -autres sages, n'empêchera point les giletières et les piqueuses de -bottines de rêver des maris enrichis de diamants. Enfin, soit; mais, -belles, retenez ceci: vous mangez trop de sucre. Vous n'avez qu'un tort, -ô femmes, c'est de grignoter du sucre. Ô sexe rongeur, tes jolies -petites dents blanches adorent le sucre. Or, écoutez bien, le sucre est -un sel. Tout sel est desséchant. Le sucre est le plus desséchant de tous -les sels. Il pompe à travers les veines les liquides du sang; de là la -coagulation, puis la solidification du sang; de là les tubercules dans -le poumon; de là la mort. Et c'est pourquoi le diabète confine à la -phthisie. Donc ne croquez pas de sucre, et vous vivrez! Je me tourne -vers les hommes. Messieurs, faites des conquêtes. Pillez-vous les uns -aux autres sans remords vos bien-aimées. Chassez-croisez. En amour, il -n'y a pas d'amis. Partout où il y a une jolie femme l'hostilité est -ouverte. Pas de quartier, guerre à outrance! Une jolie femme est un -casus belli; une jolie femme est un flagrant délit. Toutes les invasions -de l'histoire sont déterminées par des cotillons. La femme est le droit -de l'homme. Romulus a enlevé les Sabines, Guillaume a enlevé les -Saxonnes, César a enlevé les Romaines. L'homme qui n'est pas aimé plane -comme un vautour sur les amantes d'autrui; et quant à moi, à tous ces -infortunés qui sont veufs, je jette la proclamation sublime de Bonaparte -à l'armée d'Italie: «Soldats, vous manquez de tout. L'ennemi en a.» - -Tholomyès s'interrompit. - ---Souffle, Tholomyès, dit Blachevelle. - -En même temps, Blachevelle, appuyé de Listolier et de Fameuil, entonna -sur un air de complainte une de ces chansons d'atelier composées des -premiers mots venus, rimées richement et pas du tout, vides de sens -comme le geste de l'arbre et le bruit du vent, qui naissent de la vapeur -des pipes et se dissipent et s'envolent avec elle. Voici par quel -couplet le groupe donna la réplique à la harangue de Tholomyès: - -Les pères dindons donnèrent de l'argent à un agent pour que mons -Clermont-Tonnerre fût fait pape à la Saint-Jean; Mais Clermont ne put -pas être fait pape, n'étant pas prêtre. - -Alors leur agent rageant leur rapporta leur argent. - -Ceci n'était pas fait pour calmer l'improvisation de Tholomyès; il vida -son verre, le remplit, et recommença. - ---À bas la sagesse! oubliez tout ce que j'ai dit. Ne soyons ni prudes, -ni prudents, ni prud'hommes. Je porte un toast à l'allégresse; soyons -allègres! Complétons notre cours de droit par la folie et la nourriture. -Indigestion et digeste. Que Justinien soit le mâle et que Ripaille soit -la femelle! Joie dans les profondeurs! Vis, ô création! Le monde est un -gros diamant! Je suis heureux. Les oiseaux sont étonnants. Quelle fête -partout! Le rossignol est un Elleviou gratis. Été, je te salue. Ô -Luxembourg, ô Géorgiques de la rue Madame et de l'allée de -l'Observatoire! Ô pioupious rêveurs! ô toutes ces bonnes charmantes qui, -tout en gardant des enfants, s'amusent à en ébaucher! Les pampas de -l'Amérique me plairaient, si je n'avais les arcades de l'Odéon. Mon âme -s'envole dans les forêts vierges et dans les savanes. Tout est beau. Les -mouches bourdonnent dans les rayons. Le soleil a éternué le colibri. -Embrasse-moi, Fantine! - -Il se trompa, et embrassa Favourite. - - - - -Chapitre VIII - -Mort d'un cheval - - ---On dîne mieux chez Edon que chez Bombarda, s'écria Zéphine. - ---Je préfère Bombarda à Edon, déclara Blachevelle. Il a plus de luxe. -C'est plus asiatique. Voyez la salle d'en bas. Il y a des glaces sur les -murs. - ---J'en aime mieux dans mon assiette, dit Favourite. - -Blachevelle insista: - ---Regardez les couteaux. Les manches sont en argent chez Bombarda, et en -os chez Edon. Or, l'argent est plus précieux que l'os. - ---Excepté pour ceux qui ont un menton d'argent, observa Tholomyès. - -Il regardait en cet instant-là le dôme des Invalides, visible des -fenêtres de Bombarda. - -Il y eut une pause. - ---Tholomyès, cria Fameuil, tout à l'heure, Listolier et moi, nous avions -une discussion. - ---Une discussion est bonne, répondit Tholomyès, une querelle vaut mieux. - ---Nous disputions philosophie. - ---Soit. - ---Lequel préfères-tu de Descartes ou de Spinosa? - ---Désaugiers, dit Tholomyès. - -Cet arrêt rendu, il but et reprit: - ---Je consens à vivre. Tout n'est pas fini sur la terre, puisqu'on peut -encore déraisonner. J'en rends grâces aux dieux immortels. On ment, mais -on rit. On affirme, mais on doute. L'inattendu jaillit du syllogisme. -C'est beau. Il est encore ici-bas des humains qui savent joyeusement -ouvrir et fermer la boîte à surprises du paradoxe. Ceci, mesdames, que -vous buvez d'un air tranquille, est du vin de Madère, sachez-le, du cru -de Coural das Freiras qui est à trois cent dix-sept toises au-dessus du -niveau de la mer! Attention en buvant! trois cent dix-sept toises! et -monsieur Bombarda, le magnifique restaurateur, vous donne ces trois cent -dix-sept toises pour quatre francs cinquante centimes! - -Fameuil interrompit de nouveau: - ---Tholomyès, tes opinions font loi. Quel est ton auteur favori? - ---Ber.... - ---Quin? - ---Non. Choux. - -Et Tholomyès poursuivit: - ---Honneur à Bombarda! il égalerait Munophis d'Elephanta s'il pouvait me -cueillir une almée, et Thygélion de Chéronée s'il pouvait m'apporter une -hétaïre! car, ô mesdames, il y avait des Bombarda en Grèce et en Égypte. -C'est Apulée qui nous l'apprend. Hélas! toujours les mêmes choses et -rien de nouveau. Plus rien d'inédit dans la création du créateur! _Nil -sub sole novum_, dit Salomon; _amor omnibus idem_, dit Virgile; et -Carabine monte avec Carabin dans la galiote de Saint-Cloud, comme -Aspasie s'embarquait avec Périclès sur la flotte de Samos. Un dernier -mot. Savez-vous ce que c'était qu'Aspasie, mesdames? Quoiqu'elle vécût -dans un temps où les femmes n'avaient pas encore d'âme, c'était une âme; -une âme d'une nuance rose et pourpre, plus embrasée que le feu, plus -franche que l'aurore. Aspasie était une créature en qui se touchaient -les deux extrêmes de la femme; c'était la prostituée déesse. Socrate, -plus Manon Lescaut. Aspasie fut créée pour le cas où il faudrait une -catin à Prométhée. - -Tholomyès, lancé, se serait difficilement arrêté, si un cheval ne se fût -abattu sur le quai en cet instant-là même. Du choc, la charrette et -l'orateur restèrent court. C'était une jument beauceronne, vieille et -maigre et digne de l'équarrisseur, qui traînait une charrette fort -lourde. Parvenue devant Bombarda, la bête, épuisée et accablée, avait -refusé d'aller plus loin. Cet incident avait fait de la foule. À peine -le charretier, jurant et indigné, avait-il eu le temps de prononcer avec -l'énergie convenable le mot sacramentel: _mâtin_! appuyé d'un implacable -coup de fouet, que la haridelle était tombée pour ne plus se relever. Au -brouhaha des passants, les gais auditeurs de Tholomyès tournèrent la -tête, et Tholomyès en profita pour clore son allocution par cette -strophe mélancolique: - - _Elle était de ce monde où coucous et carrosses_ - _Ont le même destin,_ - _Et, rosse, elle a vécu ce que vivent les rosses,_ - _L'espace d'un: mâtin!_ - ---Pauvre cheval, soupira Fantine. - -Et Dahlia s'écria: - ---Voilà Fantine qui va se mettre à plaindre les chevaux! Peut-on être -fichue bête comme ça! - -En ce moment, Favourite, croisant les bras et renversant la tête en -arrière, regarda résolûment Tholomyès et dit: - ---Ah çà! et la surprise? - ---Justement. L'instant est arrivé, répondit Tholomyès. Messieurs, -l'heure de la surprise a sonné. Mesdames, attendez-nous un moment. - ---Cela commence par un baiser, dit Blachevelle. - ---Sur le front, ajouta Tholomyès. - -Chacun déposa gravement un baiser sur le front de sa maîtresse; puis ils -se dirigèrent vers la porte tous les quatre à la file, en mettant leur -doigt sur la bouche. - -Favourite battit des mains à leur sortie. - ---C'est déjà amusant, dit-elle. - ---Ne soyez pas trop longtemps, murmura Fantine. Nous vous attendons. - - - - -Chapitre IX - -Fin joyeuse de la joie - - -Les jeunes filles, restées seules, s'accoudèrent deux à deux sur l'appui -des fenêtres, jasant, penchant leur tête et se parlant d'une croisée à -l'autre. - -Elles virent les jeunes gens sortir du cabaret Bombarda bras dessus bras -dessous; ils se retournèrent, leur firent des signes en riant, et -disparurent dans cette poudreuse cohue du dimanche qui envahit -hebdomadairement les Champs-Élysées. - ---Ne soyez pas longtemps! cria Fantine. - ---Que vont-ils nous rapporter? dit Zéphine. - ---Pour sûr ce sera joli, dit Dahlia. - ---Moi, reprit Favourite, je veux que ce soit en or. - -Elles furent bientôt distraites par le mouvement du bord de l'eau -qu'elles distinguaient dans les branches des grands arbres et qui les -divertissait fort. C'était l'heure du départ des malles-poste et des -diligences. Presque toutes les messageries du midi et de l'ouest -passaient alors par les Champs-Élysées. La plupart suivaient le quai et -sortaient par la barrière de Passy. De minute en minute, quelque grosse -voiture peinte en jaune et en noir, pesamment chargée, bruyamment -attelée, difforme à force de malles, de bâches et de valises, pleine de -têtes tout de suite disparues, broyant la chaussée, changeant tous les -pavés en briquets, se ruait à travers la foule avec toutes les -étincelles d'une forge, de la poussière pour fumée, et un air de furie. -Ce vacarme réjouissait les jeunes filles. Favourite s'exclamait: - ---Quel tapage! on dirait des tas de chaînes qui s'envolent. - -Il arriva une fois qu'une de ces voitures qu'on distinguait -difficilement dans l'épaisseur des ormes, s'arrêta un moment, puis -repartit au galop. Cela étonna Fantine. - ---C'est particulier! dit-elle. Je croyais que la diligence ne s'arrêtait -jamais. Favourite haussa les épaules. - ---Cette Fantine est surprenante. Je viens la voir par curiosité. Elle -s'éblouit des choses les plus simples. Une supposition; je suis un -voyageur, je dis à la diligence: je vais en avant, vous me prendrez sur -le quai en passant. La diligence passe, me voit, s'arrête, et me prend. -Cela se fait tous les jours. Tu ne connais pas la vie, ma chère. - -Un certain temps s'écoula ainsi. Tout à coup Favourite eut le mouvement -de quelqu'un qui se réveille. - ---Eh bien, fit-elle, et la surprise? - ---À propos, oui, reprit Dahlia, la fameuse surprise? - ---Ils sont bien longtemps! dit Fantine. - -Comme Fantine achevait ce soupir, le garçon qui avait servi le dîner -entra. Il tenait à la main quelque chose qui ressemblait à une lettre. - ---Qu'est-ce que cela? demanda Favourite. - -Le garçon répondit: - ---C'est un papier que ces messieurs ont laissé pour ces dames. - ---Pourquoi ne l'avoir pas apporté tout de suite? - ---Parce que ces messieurs, reprit le garçon, ont commandé de ne le -remettre à ces dames qu'au bout d'une heure. - -Favourite arracha le papier des mains du garçon. C'était une lettre en -effet. - ---Tiens! dit-elle. Il n'y a pas d'adresse. Mais voici ce qui est écrit -dessus: - -Ceci est la surprise. - -Elle décacheta vivement la lettre, l'ouvrit et lut (elle savait lire): - -«Ô nos amantes! - -«Sachez que nous avons des parents. Des parents, vous ne connaissez pas -beaucoup ça. Ça s'appelle des pères et mères dans le code civil, puéril -et honnête. Or, ces parents gémissent, ces vieillards nous réclament, -ces bons hommes et ces bonnes femmes nous appellent enfants prodigues, -ils souhaitent nos retours, et nous offrent de tuer des veaux. Nous leur -obéissons, étant vertueux. À l'heure où vous lirez ceci, cinq chevaux -fougueux nous rapporteront à nos papas et à nos mamans. Nous fichons le -camp, comme dit Bossuet. Nous partons, nous sommes partis. Nous fuyons -dans les bras de Laffitte et sur les ailes de Caillard. La diligence de -Toulouse nous arrache à l'abîme, et l'abîme c'est vous, ô nos belles -petites! Nous rentrons dans la société, dans le devoir et dans l'ordre, -au grand trot, à raison de trois lieues à l'heure. Il importe à la -patrie que nous soyons, comme tout le monde, préfets, pères de famille, -gardes champêtres et conseillers d'État. Vénérez-nous. Nous nous -sacrifions. Pleurez-nous rapidement et remplacez-nous vite. Si cette -lettre vous déchire, rendez-le-lui. Adieu. - -«Pendant près de deux ans, nous vous avons rendues heureuses. Ne nous en -gardez pas rancune. - -«Signé: Blachevelle. - -«Fameuil. - -«Listolier. - -«Félix Tholomyès - -«Post-scriptum. Le dîner est payé.» - -Les quatre jeunes filles se regardèrent. - -Favourite rompit la première le silence. - ---Eh bien! s'écria-t-elle, c'est tout de même une bonne farce. - ---C'est très drôle, dit Zéphine. - ---Ce doit être Blachevelle qui a eu cette idée-là, reprit Favourite. Ça -me rend amoureuse de lui. Sitôt parti, sitôt aimé. Voilà l'histoire. - ---Non, dit Dahlia, c'est une idée à Tholomyès. Ça se reconnaît. - ---En ce cas, reprit Favourite, mort à Blachevelle et vive Tholomyès! - ---Vive Tholomyès! crièrent Dahlia et Zéphine. - -Et elles éclatèrent de rire. - -Fantine rit comme les autres. - -Une heure après, quand elle fut rentrée dans sa chambre, elle pleura. -C'était, nous l'avons dit, son premier amour; elle s'était donnée à ce -Tholomyès comme à un mari, et la pauvre fille avait un enfant. - - - - -Livre quatrième--Confier, c'est quelquefois livrer - - - - -Chapitre I - -Une mère qui en rencontre une autre - - -Il y avait, dans le premier quart de ce siècle, à Montfermeil, près de -Paris, une façon de gargote qui n'existe plus aujourd'hui. Cette gargote -était tenue par des gens appelés Thénardier, mari et femme. Elle était -située dans la ruelle du Boulanger. On voyait au-dessus de la porte une -planche clouée à plat sur le mur. Sur cette planche était peint quelque -chose qui ressemblait à un homme portant sur son dos un autre homme, -lequel avait de grosses épaulettes de général dorées avec de larges -étoiles argentées; des taches rouges figuraient du sang; le reste du -tableau était de la fumée et représentait probablement une bataille. Au -bas on lisait cette inscription: _Au Sergent de Waterloo._ - -Rien n'est plus ordinaire qu'un tombereau ou une charrette à la porte -d'une auberge. Cependant le véhicule ou, pour mieux dire, le fragment de -véhicule qui encombrait la rue devant la gargote du Sergent de Waterloo, -un soir du printemps de 1818, eût certainement attiré par sa masse -l'attention d'un peintre qui eût passé là. - -C'était l'avant-train d'un de ces fardiers, usités dans les pays de -forêts, et qui servent à charrier des madriers et des troncs d'arbres. -Cet avant-train se composait d'un massif essieu de fer à pivot où -s'emboîtait un lourd timon, et que supportaient deux roues démesurées. -Tout cet ensemble était trapu, écrasant et difforme. On eût dit l'affût -d'un canon géant. Les ornières avaient donné aux roues, aux jantes, aux -moyeux, à l'essieu et au timon, une couche de vase, hideux badigeonnage -jaunâtre assez semblable à celui dont on orne volontiers les -cathédrales. Le bois disparaissait sous la boue et le fer sous la -rouille. Sous l'essieu pendait en draperie une grosse chaîne digne de -Goliath forçat. Cette chaîne faisait songer, non aux poutres qu'elle -avait fonction de transporter, mais aux mastodontes et aux mammons -qu'elle eût pu atteler; elle avait un air de bagne, mais de bagne -cyclopéen et surhumain, et elle semblait détachée de quelque monstre. -Homère y eût lié Polyphème et Shakespeare Caliban. - -Pourquoi cet avant-train de fardier était-il à cette place dans la rue? -D'abord, pour encombrer la rue; ensuite pour achever de se rouiller. Il -y a dans le vieil ordre social une foule d'institutions qu'on trouve de -la sorte sur son passage en plein air et qui n'ont pas pour être là -d'autres raisons. - -Le centre de la chaîne pendait sous l'essieu assez près de terre, et sur -la courbure, comme sur la corde d'une balançoire, étaient assises et -groupées, ce soir-là, dans un entrelacement exquis, deux petites filles, -l'une d'environ deux ans et demi, l'autre de dix-huit mois, la plus -petite dans les bras de la plus grande. Un mouchoir savamment noué les -empêchait de tomber. Une mère avait vu cette effroyable chaîne, et avait -dit: Tiens! voilà un joujou pour mes enfants. - -Les deux enfants, du reste gracieusement attifées, et avec quelque -recherche, rayonnaient; on eût dit deux roses dans de la ferraille; -leurs yeux étaient un triomphe; leurs fraîches joues riaient. L'une -était châtain, l'autre était brune. Leurs naïfs visages étaient deux -étonnements ravis; un buisson fleuri qui était près de là envoyait aux -passants des parfums qui semblaient venir d'elles; celle de dix-huit -mois montrait son gentil ventre nu avec cette chaste indécence de la -petitesse. - -Au-dessus et autour de ces deux têtes délicates, pétries dans le bonheur -et trempées dans la lumière, le gigantesque avant-train, noir de -rouille, presque terrible, tout enchevêtré de courbes et d'angles -farouches, s'arrondissait comme un porche de caverne. À quelques pas, -accroupie sur le seuil de l'auberge, la mère, femme d'un aspect peu -avenant du reste, mais touchante en ce moment-là, balançait les deux -enfants au moyen d'une longue ficelle, les couvant des yeux de peur -d'accident avec cette expression animale et céleste propre à la -maternité; à chaque va-et-vient, les hideux anneaux jetaient un bruit -strident qui ressemblait à un cri de colère; les petites filles -s'extasiaient, le soleil couchant se mêlait à cette joie, et rien -n'était charmant comme ce caprice du hasard, qui avait fait d'une chaîne -de titans une escarpolette de chérubins. - -Tout en berçant ses deux petites, la mère chantonnait d'une voix fausse -une romance alors célèbre: - - _Il le faut, disait un guerrier._ - -Sa chanson et la contemplation de ses filles l'empêchaient d'entendre et -de voir ce qui se passait dans la rue. - -Cependant quelqu'un s'était approché d'elle, comme elle commençait le -premier couplet de la romance, et tout à coup elle entendit une voix qui -disait très près de son oreille: - ---Vous avez là deux jolis enfants, madame, répondit la mère, continuant -sa romance: - - _À la belle et tendre Imogine._ - -répondit la mère, continuant sa romance, puis elle tourna la tête. - -Une femme était devant elle, à quelques pas. Cette femme, elle aussi, -avait un enfant qu'elle portait dans ses bras. - -Elle portait en outre un assez gros sac de nuit qui semblait fort lourd. - -L'enfant de cette femme était un des plus divins êtres qu'on pût voir. -C'était une fille de deux à trois ans. Elle eût pu jouter avec les deux -autres pour la coquetterie de l'ajustement; elle avait un bavolet de -linge fin, des rubans à sa brassière et de la valenciennes à son bonnet. -Le pli de sa jupe relevée laissait voir sa cuisse blanche, potelée et -ferme. Elle était admirablement rose et bien portante. La belle petite -donnait envie de mordre dans les pommes de ses joues. On ne pouvait rien -dire de ses yeux, sinon qu'ils devaient être très grands et qu'ils -avaient des cils magnifiques. Elle dormait. - -Elle dormait de ce sommeil d'absolue confiance propre à son âge. Les -bras des mères sont faits de tendresse; les enfants y dorment -profondément. - -Quant à la mère, l'aspect en était pauvre et triste. Elle avait la mise -d'une ouvrière qui tend à redevenir paysanne. Elle était jeune. -Était-elle belle? peut-être; mais avec cette mise il n'y paraissait pas. -Ses cheveux, d'où s'échappait une mèche blonde, semblaient fort épais, -mais disparaissaient sévèrement sous une coiffe de béguine, laide, -serrée, étroite, et nouée au menton. Le rire montre les belles dents -quand on en a; mais elle ne riait point. Ses yeux ne semblaient pas être -secs depuis très longtemps. Elle était pâle; elle avait l'air très lasse -et un peu malade; elle regardait sa fille endormie dans ses bras avec -cet air particulier d'une mère qui a nourri son enfant. Un large -mouchoir bleu, comme ceux où se mouchent les invalides, plié en fichu, -masquait lourdement sa taille. Elle avait les mains hâlées et toutes -piquées de taches de rousseur, l'index durci et déchiqueté par -l'aiguille, une Mante brune de laine bourrue, une robe de toile et de -gros souliers. C'était Fantine. - -C'était Fantine. Difficile à reconnaître. Pourtant, à l'examiner -attentivement, elle avait toujours sa beauté. Un pli triste, qui -ressemblait à un commencement d'ironie, ridait sa joue droite. Quant à -sa toilette, cette aérienne toilette de mousseline et de rubans qui -semblait faite avec de la gaîté, de la folie et de la musique, pleine de -grelots et parfumée de lilas, elle s'était évanouie comme ces beaux -givres éclatants qu'on prend pour des diamants au soleil; ils fondent et -laissent la branche toute noire. - -Dix mois s'étaient écoulés depuis «la bonne farce». - -Que s'était-il passé pendant ces dix mois? on le devine. - -Après l'abandon, la gêne. Fantine avait tout de suite perdu de vue -Favourite, Zéphine et Dahlia; le lien, brisé du côté des hommes, s'était -défait du côté des femmes; on les eût bien étonnées, quinze jours après, -si on leur eût dit qu'elles étaient amies; cela n'avait plus de raison -d'être. Fantine était restée seule. Le père de son enfant parti,--hélas! -ces ruptures-là sont irrévocables,--elle se trouva absolument isolée, -avec l'habitude du travail de moins et le goût du plaisir de plus. -Entraînée par sa liaison avec Tholomyès à dédaigner le petit métier -qu'elle savait, elle avait négligé ses débouchés; ils s'étaient fermés. -Nulle ressource. Fantine savait à peine lire et ne savait pas écrire; on -lui avait seulement appris dans son enfance à signer son nom; elle avait -fait écrire par un écrivain public une lettre à Tholomyès, puis une -seconde, puis une troisième. Tholomyès n'avait répondu à aucune. Un -jour, Fantine entendit des commères dire en regardant sa fille: - ---Est-ce qu'on prend ces enfants-là au sérieux? on hausse les épaules de -ces enfants-là! - -Alors elle songea à Tholomyès qui haussait les épaules de son enfant et -qui ne prenait pas cet être innocent au sérieux; et son coeur devint -sombre à l'endroit de cet homme. Quel parti prendre pourtant? Elle ne -savait plus à qui s'adresser. Elle avait commis une faute, mais le fond -de sa nature, on s'en souvient, était pudeur et vertu. Elle sentit -vaguement qu'elle était à la veille de tomber dans la détresse, et de -glisser dans le pire. Il fallait du courage; elle en eut, et se roidit. -L'idée lui vint de retourner dans sa ville natale, à Montreuil-sur-mer. -Là quelqu'un peut-être la connaîtrait et lui donnerait du travail. Oui; -mais il faudrait cacher sa faute. Et elle entrevoyait confusément la -nécessité possible d'une séparation plus douloureuse encore que la -première. Son coeur se serra, mais elle prit sa résolution. Fantine, on -le verra, avait la farouche bravoure de la vie. - -Elle avait déjà vaillamment renoncé à la parure, s'était vêtue de toile, -et avait mis toute sa soie, tous ses chiffons, tous ses rubans et toutes -ses dentelles sur sa fille, seule vanité qui lui restât, et sainte -celle-là. Elle vendit tout ce qu'elle avait, ce qui lui produisit deux -cents francs; ses petites dettes payées, elle n'eut plus que -quatre-vingts francs environ. À vingt-deux ans, par une belle matinée de -printemps, elle quittait Paris, emportant son enfant sur son dos. -Quelqu'un qui les eût vues passer toutes les deux eût pitié. Cette femme -n'avait au monde que cet enfant, et cet enfant n'avait au monde que -cette femme. Fantine avait nourri sa fille; cela lui avait fatigué la -poitrine, et elle toussait un peu. - -Nous n'aurons plus occasion de parler de M. Félix Tholomyès. -Bornons-nous à dire que, vingt ans plus tard, sous le roi -Louis-Philippe, c'était un gros avoué de province, influent et riche, -électeur sage et juré très sévère; toujours homme de plaisir. - -Vers le milieu du jour, après avoir, pour se reposer, cheminé de temps -en temps, moyennant trois ou quatre sous par lieue, dans ce qu'on -appelait alors les Petites Voitures des Environs de Paris, Fantine se -trouvait à Montfermeil, dans la ruelle du Boulanger. - -Comme elle passait devant l'auberge Thénardier, les deux petites filles, -enchantées sur leur escarpolette monstre, avaient été pour elle une -sorte d'éblouissement, et elle s'était arrêtée devant cette vision de -joie. - -Il y a des charmes. Ces deux petites filles en furent un pour cette -mère. - -Elle les considérait, toute émue. La présence des anges est une annonce -de paradis. Elle crut voir au dessus de cette auberge le mystérieux ICI -de la providence. Ces deux petites étaient si évidemment heureuses! Elle -les regardait, elle les admirait, tellement attendrie qu'au moment où la -mère reprenait haleine entre deux vers de sa chanson, elle ne put -s'empêcher de lui dire ce mot qu'on vient de lire: - ---Vous avez là deux jolis enfants, madame. - -Les créatures les plus féroces sont désarmées par la caresse à leurs -petits. La mère leva la tête et remercia, et fit asseoir la passante sur -le banc de la porte, elle-même étant sur le seuil. Les deux femmes -causèrent. - ---Je m'appelle madame Thénardier, dit la mère des deux petites. Nous -tenons cette auberge. - -Puis, toujours à sa romance, elle reprit entre ses dents: - - _Il le faut, je suis chevalier,_ - _Et je pars pour la Palestine._ - -Cette madame Thénardier était une femme rousse, charnue, anguleuse; le -type femme-à-soldat dans toute sa disgrâce. Et, chose bizarre, avec un -air penché qu'elle devait à des lectures romanesques. C'était une -minaudière hommasse. De vieux romans qui se sont éraillés sur des -imaginations de gargotières ont de ces effets-là. Elle était jeune -encore; elle avait à peine trente ans. Si cette femme, qui était -accroupie, se fût tenue droite, peut-être sa haute taille et sa carrure -de colosse ambulant propre aux foires, eussent-elles dès l'abord -effarouché la voyageuse, troublé sa confiance, et fait évanouir ce que -nous avons à raconter. Une personne qui est assise au lieu d'être -debout, les destinées tiennent à cela. - -La voyageuse raconta son histoire, un peu modifiée: - -Qu'elle était ouvrière; que son mari était mort; que le travail lui -manquait à Paris, et qu'elle allait en chercher ailleurs; dans son pays; -qu'elle avait quitté Paris, le matin même, à pied; que, comme elle -portait son enfant, se sentant fatiguée, et ayant rencontré la voiture -de Villemomble, elle y était montée; que de Villemomble elle était venue -à Montfermeil à pied, que la petite avait un peu marché, mais pas -beaucoup, c'est si jeune, et qu'il avait fallu la prendre, et que le -bijou s'était endormi. - -Et sur ce mot elle donna à sa fille un baiser passionné qui la réveilla. -L'enfant ouvrit les yeux, de grands yeux bleus comme ceux de sa mère, et -regarda, quoi? rien, tout, avec cet air sérieux et quelquefois sévère -des petits enfants, qui est un mystère de leur lumineuse innocence -devant nos crépuscules de vertus. On dirait qu'ils se sentent anges et -qu'ils nous savent hommes. Puis l'enfant se mit à rire, et, quoique la -mère la retint, glissa à terre avec l'indomptable énergie d'un petit -être qui veut courir. Tout à coup elle aperçut les deux autres sur leur -balançoire, s'arrêta court, et tira la langue, signe d'admiration. - -La mère Thénardier détacha ses filles, les fit descendre de -l'escarpolette, et dit: - ---Amusez-vous toutes les trois. - -Ces âges-là s'apprivoisent vite, et au bout d'une minute les petites -Thénardier jouaient avec la nouvelle venue à faire des trous dans la -terre, plaisir immense. - -Cette nouvelle venue était très gaie; la bonté de la mère est écrite -dans la gaîté du marmot; elle avait pris un brin de bois qui lui servait -de pelle, et elle creusait énergiquement une fosse bonne pour une -mouche. Ce que fait le fossoyeur devient riant, fait par l'enfant. - -Les deux femmes continuaient de causer. - ---Comment s'appelle votre mioche? - ---Cosette. - -Cosette, lisez Euphrasie. La petite se nommait Euphrasie. Mais -d'Euphrasie la mère avait fait Cosette, par ce doux et gracieux instinct -des mères et du peuple qui change Josefa en Pepita et Françoise en -Sillette. C'est là un genre de dérivés qui dérange et déconcerte toute -la science des étymologistes. Nous avons connu une grand'mère qui avait -réussi à faire de Théodore, Gnon. - ---Quel âge a-t-elle? - ---Elle va sur trois ans. - ---C'est comme mon aînée. - -Cependant les trois petites filles étaient groupées dans une posture -d'anxiété profonde et de béatitude; un événement avait lieu; un gros ver -venait de sortir de terre; et elles avaient peur, et elles étaient en -extase. - -Leurs fronts radieux se touchaient; on eût dit trois têtes dans une -auréole. - ---Les enfants, s'écria la mère Thénardier, comme ça se connaît tout de -suite! les voilà qu'on jurerait trois soeurs! - -Ce mot fut l'étincelle qu'attendait probablement l'autre mère. Elle -saisit la main de la Thénardier, la regarda fixement, et lui dit: - ---Voulez-vous me garder mon enfant? - -La Thénardier eut un de ces mouvements surpris qui ne sont ni le -consentement ni le refus. - -La mère de Cosette poursuivit: - ---Voyez-vous, je ne peux pas emmener ma fille au pays. L'ouvrage ne le -permet pas. Avec un enfant, on ne trouve pas à se placer. Ils sont si -ridicules dans ce pays-là. C'est le bon Dieu qui m'a fait passer devant -votre auberge. Quand j'ai vu vos petites si jolies et si propres et si -contentes, cela m'a bouleversée. J'ai dit: voilà une bonne mère. C'est -ça; ça fera trois soeurs. Et puis, je ne serai pas longtemps à revenir. -Voulez-vous me garder mon enfant? - ---Il faudrait voir, dit la Thénardier. - ---Je donnerais six francs par mois. - -Ici une voix d'homme cria du fond de la gargote: - ---Pas à moins de sept francs. Et six mois payés d'avance. - ---Six fois sept quarante-deux, dit la Thénardier. - ---Je les donnerai, dit la mère. - ---Et quinze francs en dehors pour les premiers frais, ajouta la voix -d'homme. - ---Total cinquante-sept francs, dit la madame Thénardier. Et à travers -ces chiffres, elle chantonnait vaguement: - -_Il le faut, disait un guerrier._ - ---Je les donnerai, dit la mère, j'ai quatre-vingts francs. Il me restera -de quoi aller au pays. En allant à pied. Je gagnerai de l'argent là-bas, -et dès que j'en aurai un peu, je reviendrai chercher l'amour. - -La voix d'homme reprit: - ---La petite a un trousseau? - ---C'est mon mari, dit la Thénardier. - ---Sans doute elle a un trousseau, le pauvre trésor. J'ai bien vu que -c'était votre mari. Et un beau trousseau encore! un trousseau insensé. -Tout par douzaines; et des robes de soie comme une dame. Il est là dans -mon sac de nuit. - ---Il faudra le donner, repartit la voix d'homme. - ---Je crois bien que je le donnerai! dit la mère. Ce serait cela qui -serait drôle si je laissais ma fille toute nue! - -La face du maître apparut. - ---C'est bon, dit-il. - -Le marché fut conclu. La mère passa la nuit à l'auberge, donna son -argent et laissa son enfant, renoua son sac de nuit dégonflé du -trousseau et léger désormais, et partit le lendemain matin, comptant -revenir bientôt. On arrange tranquillement ces départs-là, mais ce sont -des désespoirs. - -Une voisine des Thénardier rencontra cette mère comme elle s'en allait, -et s'en revint en disant: - ---Je viens de voir une femme qui pleure dans la rue, que c'est un -déchirement. - -Quand la mère de Cosette fut partie, l'homme dit à la femme: - ---Cela va me payer mon effet de cent dix francs qui échoit demain. Il me -manquait cinquante francs. Sais-tu que j'aurais eu l'huissier et un -protêt? Tu as fait là une bonne souricière avec tes petites. - ---Sans m'en douter, dit la femme. - - - - -Chapitre II - -Première esquisse de deux figures louches - - -La souris prise était bien chétive; mais le chat se réjouit même d'une -souris maigre. Qu'était-ce que les Thénardier? - -Disons-en un mot dès à présent. Nous compléterons le croquis plus tard. - -Ces êtres appartenaient à cette classe bâtarde composée de gens -grossiers parvenus et de gens intelligents déchus, qui est entre la -classe dite moyenne et la classe dite inférieure, et qui combine -quelques-uns des défauts de la seconde avec presque tous les vices de la -première, sans avoir le généreux élan de l'ouvrier ni l'ordre honnête du -bourgeois. - -C'étaient de ces natures naines qui, si quelque feu sombre les chauffe -par hasard, deviennent facilement monstrueuses. Il y avait dans la femme -le fond d'une brute et dans l'homme l'étoffe d'un gueux. Tous deux -étaient au plus haut degré susceptibles de l'espèce de hideux progrès -qui se fait dans le sens du mal. Il existe des âmes écrevisses reculant -continuellement vers les ténèbres, rétrogradant dans la vie plutôt -qu'elles n'y avancent, employant l'expérience à augmenter leur -difformité, empirant sans cesse, et s'empreignant de plus en plus d'une -noirceur croissante. Cet homme et cette femme étaient de ces âmes-là. - -Le Thénardier particulièrement était gênant pour le physionomiste. On -n'a qu'à regarder certains hommes pour s'en défier, on les sent -ténébreux à leurs deux extrémités. Ils sont inquiets derrière eux et -menaçants devant eux. Il y a en eux de l'inconnu. On ne peut pas plus -répondre de ce qu'ils ont fait que de ce qu'ils feront. L'ombre qu'ils -ont dans le regard les dénonce. Rien qu'en les entendant dire un mot ou -qu'en les voyant faire un geste on entrevoit de sombres secrets dans -leur passé et de sombres mystères dans leur avenir. - -Ce Thénardier, s'il fallait l'en croire, avait été soldat; sergent, -disait-il; il avait fait probablement la campagne de 1815, et s'était -même comporté assez bravement, à ce qu'il paraît. Nous verrons plus tard -ce qu'il en était. L'enseigne de son cabaret était une allusion à l'un -de ses faits d'armes. Il l'avait peinte lui-même, car il savait faire un -peu de tout; mal. - -C'était l'époque où l'antique roman classique, qui, après avoir été -_Clélie_, n'était plus que _Lodoïska_, toujours noble, mais de plus en -plus vulgaire, tombé de mademoiselle de Scudéri à madame -Barthélemy-Hadot, et de madame de Lafayette à madame Bournon-Malarme, -incendiait l'âme aimante des portières de Paris et ravageait même un peu -la banlieue. Madame Thénardier était juste assez intelligente pour lire -ces espèces de livres. Elle s'en nourrissait. Elle y noyait ce qu'elle -avait de cervelle; cela lui avait donné, tant qu'elle avait été très -jeune, et même un peu plus tard, une sorte d'attitude pensive près de -son mari, coquin d'une certaine profondeur, ruffian lettré à la -grammaire près, grossier et fin en même temps, mais, en fait de -sentimentalisme, lisant Pigault-Lebrun, et pour «tout ce qui touche le -sexe», comme il disait dans son jargon, butor correct et sans mélange. -Sa femme avait quelque douze ou quinze ans de moins que lui. Plus tard, -quand les cheveux romanesquement pleureurs commencèrent à grisonner, -quand la Mégère se dégagea de la Paméla, la Thénardier ne fut plus -qu'une grosse méchante femme ayant savouré des romans bêtes. Or on ne -lit pas impunément des niaiseries. Il en résulta que sa fille aînée se -nomma Eponine. Quant à la cadette, la pauvre petite faillit se nommer -Gulnare; elle dut à je ne sais quelle heureuse diversion faite par un -roman de Ducray-Duminil, de ne s'appeler qu'Azelma. - -Au reste, pour le dire en passant, tout n'est pas ridicule et -superficiel dans cette curieuse époque à laquelle nous faisons ici -allusion, et qu'on pourrait appeler l'anarchie des noms de baptême. À -côté de l'élément romanesque, que nous venons d'indiquer, il y a le -symptôme social. Il n'est pas rare aujourd'hui que le garçon bouvier se -nomme Arthur, Alfred ou Alphonse, et que le vicomte--s'il y a encore des -vicomtes--se nomme Thomas, Pierre ou Jacques. Ce déplacement qui met le -nom «élégant» sur le plébéien et le nom campagnard sur l'aristocrate -n'est autre chose qu'un remous d'égalité. L'irrésistible pénétration du -souffle nouveau est là comme en tout. Sous cette discordance apparente, -il y a une chose grande et profonde: la révolution française. - - - - -Chapitre III - -L'Alouette - - -Il ne suffit pas d'être méchant pour prospérer. La gargote allait mal. - -Grâce aux cinquante-sept francs de la voyageuse, Thénardier avait pu -éviter un protêt et faire honneur à sa signature. Le mois suivant ils -eurent encore besoin d'argent; la femme porta à Paris et engagea au -Mont-de-Piété le trousseau de Cosette pour une somme de soixante francs. -Dès que cette somme fut dépensée, les Thénardier s'accoutumèrent à ne -plus voir dans la petite fille qu'un enfant qu'ils avaient chez eux par -charité, et la traitèrent en conséquence. Comme elle n'avait plus de -trousseau, on l'habilla des vieilles jupes et des vieilles chemises des -petites Thénardier, c'est-à-dire de haillons. - -On la nourrit des restes de tout le monde, un peu mieux que le chien et -un peu plus mal que le chat. Le chat et le chien étaient du reste ses -commensaux habituels; Cosette mangeait avec eux sous la table dans une -écuelle de bois pareille à la leur. La mère qui s'était fixée, comme on -le verra plus tard, à Montreuil-sur-mer, écrivait, ou, pour mieux dire, -faisait écrire tous les mois afin d'avoir des nouvelles de son enfant. -Les Thénardier répondaient invariablement: Cosette est à merveille. Les -six premiers mois révolus, la mère envoya sept francs pour le septième -mois, et continua assez exactement ses envois de mois en mois. L'année -n'était pas finie que le Thénardier dit: - ---Une belle grâce qu'elle nous fait là! que veut-elle que nous fassions -avec ses sept francs? - -Et il écrivit pour exiger douze francs. La mère, à laquelle ils -persuadaient que son enfant était heureuse "et venait bien", se soumit -et envoya les douze francs. - -Certaines natures ne peuvent aimer d'un côté sans haïr de l'autre. La -mère Thénardier aimait passionnément ses deux filles à elle, ce qui fit -qu'elle détesta l'étrangère. Il est triste de songer que l'amour d'une -mère peut avoir de vilains aspects. Si peu de place que Cosette tînt -chez elle, il lui semblait que cela était pris aux siens, et que cette -petite diminuait l'air que ses filles respiraient. Cette femme, comme -beaucoup de femmes de sa sorte, avait une somme de caresses et une somme -de coups et d'injures à dépenser chaque jour. Si elle n'avait pas eu -Cosette, il est certain que ses filles, tout idolâtrées qu'elles -étaient, auraient tout reçu; mais l'étrangère leur rendit le service de -détourner les coups sur elle. Ses filles n'eurent que les caresses. -Cosette ne faisait pas un mouvement qui ne fît pleuvoir sur sa tête une -grêle de châtiments violents et immérités. Doux être faible qui ne -devait rien comprendre à ce monde ni à Dieu, sans cesse punie, grondée, -rudoyée, battue et voyant à côté d'elle deux petites créatures comme -elle, qui vivaient dans un rayon d'aurore! - -La Thénardier étant méchante pour Cosette, Éponine et Azelma furent -méchantes. Les enfants, à cet âge, ne sont que des exemplaires de la -mère. Le format est plus petit, voilà tout. - -Une année s'écoula, puis une autre. - -On disait dans le village: - ---Ces Thénardier sont de braves gens. Ils ne sont pas riches, et ils -élèvent un pauvre enfant qu'on leur a abandonné chez eux! - -On croyait Cosette oubliée par sa mère. - -Cependant le Thénardier, ayant appris par on ne sait quelles voies -obscures que l'enfant était probablement bâtard et que la mère ne -pouvait l'avouer, exigea quinze francs par mois, disant que «la -créature» grandissait et «_mangeait_», et menaçant de la renvoyer. -«Quelle ne m'embête pas! s'écriait-il, je lui bombarde son mioche tout -au beau milieu de ses cachotteries. Il me faut de l'augmentation.» La -mère paya les quinze francs. - -D'année en année, l'enfant grandit, et sa misère aussi. - -Tant que Cosette fut toute petite, elle fut le souffre-douleur des deux -autres enfants; dès qu'elle se mit à se développer un peu, c'est-à-dire -avant même qu'elle eût cinq ans, elle devint la servante de la maison. - -Cinq ans, dira-t-on, c'est invraisemblable. Hélas, c'est vrai. La -souffrance sociale commence à tout âge. - -N'avons-nous pas vu, récemment, le procès d'un nommé Dumolard, orphelin -devenu bandit, qui, dès l'âge de cinq ans, disent les documents -officiels, étant seul au monde «travaillait pour vivre, et volait.» - -On fit faire à Cosette les commissions, balayer les chambres, la cour, -la rue, laver la vaisselle, porter même des fardeaux. Les Thénardier se -crurent d'autant plus autorisés à agir ainsi que la mère qui était -toujours à Montreuil-sur-mer commença à mal payer. Quelques mois -restèrent en souffrance. - -Si cette mère fût revenue à Montfermeil au bout de ces trois années, -elle n'eût point reconnu son enfant. Cosette, si jolie et si fraîche à -son arrivée dans cette maison, était maintenant maigre et blême. Elle -avait je ne sais quelle allure inquiète. Sournoise! disaient les -Thénardier. - -L'injustice l'avait faite hargneuse et la misère l'avait rendue laide. -Il ne lui restait plus que ses beaux yeux qui faisaient peine, parce -que, grands comme ils étaient, il semblait qu'on y vît une plus grande -quantité de tristesse. - -C'était une chose navrante de voir, l'hiver, ce pauvre enfant, qui -n'avait pas encore six ans, grelottant sous de vieilles loques de toile -trouées, balayer la rue avant le jour avec un énorme balai dans ses -petites mains rouges et une larme dans ses grands yeux. - -Dans le pays on l'appelait l'Alouette. Le peuple, qui aime les figures, -s'était plu à nommer de ce nom ce petit être pas plus gros qu'un oiseau, -tremblant, effarouché et frissonnant, éveillé le premier chaque matin -dans la maison et dans le village, toujours dans la rue ou dans les -champs avant l'aube. Seulement la pauvre Alouette ne chantait jamais. - - - - -Livre cinquième--La descente - - - - -Chapitre I - -Histoire d'un progrès dans les verroteries noires - - -Cette mère cependant qui, au dire des gens de Montfermeil, semblait -avoir abandonné son enfant, que devenait-elle? où était-elle? que -faisait-elle? - -Après avoir laissé sa petite Cosette aux Thénardier, elle avait continué -son chemin et était arrivée à Montreuil-sur-mer. - -C'était, on se le rappelle, en 1818. - -Fantine avait quitté sa province depuis une dizaine d'années. -Montreuil-sur-mer avait changé d'aspect. Tandis que Fantine descendait -lentement de misère en misère, sa ville natale avait prospéré. - -Depuis deux ans environ, il s'y était accompli un de ces faits -industriels qui sont les grands événements des petits pays. - -Ce détail importe, et nous croyons utile de le développer; nous dirions -presque, de le souligner. - -De temps immémorial, Montreuil-sur-mer avait pour industrie spéciale -l'imitation des jais anglais et des verroteries noires d'Allemagne. -Cette industrie avait toujours végété, à cause de la cherté des matières -premières qui réagissait sur la main-d'oeuvre. Au moment où Fantine -revint à Montreuil-sur-mer, une transformation inouïe s'était opérée -dans cette production des «articles noirs». Vers la fin de 1815, un -homme, un inconnu, était venu s'établir dans la ville et avait eu l'idée -de substituer, dans cette fabrication, la gomme laque à la résine et, -pour les bracelets en particulier, les coulants en tôle simplement -rapprochée aux coulants en tôle soudée. Ce tout petit changement avait -été une révolution. - -Ce tout petit changement en effet avait prodigieusement réduit le prix -de la matière première, ce qui avait permis, premièrement, d'élever le -prix de la main-d'oeuvre, bienfait pour le pays; deuxièmement, -d'améliorer la fabrication, avantage pour le consommateur; -troisièmement, de vendre à meilleur marché tout en triplant le bénéfice, -profit pour le manufacturier. - -Ainsi pour une idée trois résultats. - -En moins de trois ans, l'auteur de ce procédé était devenu riche, ce qui -est bien, et avait tout fait riche autour de lui, ce qui est mieux. Il -était étranger au département. De son origine, on ne savait rien; de ses -commencements, peu de chose. - -On contait qu'il était venu dans la ville avec fort peu d'argent, -quelques centaines de francs tout au plus. - -C'est de ce mince capital, mis au service d'une idée ingénieuse, fécondé -par l'ordre et par la pensée, qu'il avait tiré sa fortune et la fortune -de tout ce pays. - -À son arrivée à Montreuil-sur-mer, il n'avait que les vêtements, la -tournure et le langage d'un ouvrier. - -Il paraît que, le jour même où il faisait obscurément son entrée dans la -petite ville de Montreuil-sur-mer, à la tombée d'un soir de décembre, le -sac au dos et le bâton d'épine à la main, un gros incendie venait -d'éclater à la maison commune. Cet homme s'était jeté dans le feu, et -avait sauvé, au péril de sa vie, deux enfants qui se trouvaient être -ceux du capitaine de gendarmerie; ce qui fait qu'on n'avait pas songé à -lui demander son passeport. Depuis lors, on avait su son nom. Il -s'appelait le _père Madeleine_. - - - - -Chapitre II - -M. Madeleine - - -C'était un homme d'environ cinquante ans, qui avait l'air préoccupé et -qui était bon. Voilà tout ce qu'on en pouvait dire. - -Grâce aux progrès rapides de cette industrie qu'il avait si -admirablement remaniée, Montreuil-sur-mer était devenu un centre -d'affaires considérable. L'Espagne, qui consomme beaucoup de jais noir, -y commandait chaque année des achats immenses. Montreuil-sur-mer, pour -ce commerce, faisait presque concurrence à Londres et à Berlin. Les -bénéfices du père Madeleine étaient tels que, dès la deuxième année, il -avait pu bâtir une grande fabrique dans laquelle il y avait deux vastes -ateliers, l'un pour les hommes, l'autre pour les femmes. Quiconque avait -faim pouvait s'y présenter, et était sûr de trouver là de l'emploi et du -pain. Le père Madeleine demandait aux hommes de la bonne volonté, aux -femmes des moeurs pures, à tous de la probité. Il avait divisé les -ateliers afin de séparer les sexes et que les filles et les femmes -pussent rester sages. Sur ce point, il était inflexible. C'était le seul -où il fût en quelque sorte intolérant. Il était d'autant plus fondé à -cette sévérité que, Montreuil-sur-mer étant une ville de garnison, les -occasions de corruption abondaient. Du reste sa venue avait été un -bienfait, et sa présence était une providence. Avant l'arrivée du père -Madeleine, tout languissait dans le pays; maintenant tout y vivait de la -vie saine du travail. Une forte circulation échauffait tout et pénétrait -partout. Le chômage et la misère étaient inconnus. Il n'y avait pas de -poche si obscure où il n'y eût un peu d'argent, pas de logis si pauvre -où il n'y eût un peu de joie. - -Le père Madeleine employait tout le monde. Il n'exigeait qu'une chose: -soyez honnête homme! soyez honnête fille! - -Comme nous l'avons dit, au milieu de cette activité dont il était la -cause et le pivot, le père Madeleine faisait sa fortune, mais, chose -assez singulière dans un simple homme de commerce, il ne paraissait -point que ce fût là son principal souci. Il semblait qu'il songeât -beaucoup aux autres et peu à lui. En 1820, on lui connaissait une somme -de six cent trente mille francs placée à son nom chez Laffitte; mais -avant de se réserver ces six cent trente mille francs, il avait dépensé -plus d'un million pour la ville et pour les pauvres. - -L'hôpital était mal doté; il y avait fondé dix lits. Montreuil-sur-mer -est divisé en ville haute et ville basse. La ville basse, qu'il -habitait, n'avait qu'une école, méchante masure qui tombait en ruine; il -en avait construit deux, une pour les filles, l'autre pour les garçons. -Il allouait de ses deniers aux deux instituteurs une indemnité double de -leur maigre traitement officiel, et un jour, à quelqu'un qui s'en -étonnait, il dit: «Les deux premiers fonctionnaires de l'état, c'est la -nourrice et le maître d'école.» Il avait créé à ses frais une salle -d'asile, chose alors presque inconnue en France, et une caisse de -secours pour les ouvriers vieux et infirmes. Sa manufacture étant un -centre, un nouveau quartier où il y avait bon nombre de familles -indigentes avait rapidement surgi autour de lui; il y avait établi une -pharmacie gratuite. - -Dans les premiers temps, quand on le vit commencer, les bonnes âmes -dirent: C'est un gaillard qui veut s'enrichir. Quand on le vit enrichir -le pays avant de s'enrichir lui-même, les mêmes bonnes âmes dirent: -C'est un ambitieux. Cela semblait d'autant plus probable que cet homme -était religieux, et même pratiquait dans une certaine mesure, chose fort -bien vue à cette époque. Il allait régulièrement entendre une basse -messe tous les dimanches. Le député local, qui flairait partout des -concurrences, ne tarda pas à s'inquiéter de cette religion. Ce député, -qui avait été membre du corps législatif de l'empire, partageait les -idées religieuses d'un père de l'oratoire connu sous le nom de Fouché, -duc d'Otrante, dont il avait été la créature et l'ami. À huis clos il -riait de Dieu doucement. Mais quand il vit le riche manufacturier -Madeleine aller à la basse messe de sept heures, il entrevit un candidat -possible, et résolut de le dépasser; il prit un confesseur jésuite et -alla à la grand'messe et à vêpres. L'ambition en ce temps-là était, dans -l'acception directe du mot, une course au clocher. Les pauvres -profitèrent de cette terreur comme le bon Dieu, car l'honorable député -fonda aussi deux lits à l'hôpital; ce qui fit douze. - -Cependant en 1819 le bruit se répandit un matin dans la ville que, sur -la présentation de M. le préfet, et en considération des services rendus -au pays, le père Madeleine allait être nommé par le roi maire de -Montreuil-sur-mer. Ceux qui avaient déclaré ce nouveau venu «un -ambitieux», saisirent avec transport cette occasion que tous les hommes -souhaitent de s'écrier: «Là! qu'est-ce que nous avions dit?» Tout -Montreuil-sur-mer fut en rumeur. Le bruit était fondé. Quelques jours -après, la nomination parut dans _le Moniteur_. Le lendemain, le père -Madeleine refusa. - -Dans cette même année 1819, les produits du nouveau procédé inventé par -Madeleine figurèrent à l'exposition de l'industrie; sur le rapport du -jury, le roi nomma l'inventeur chevalier de la Légion d'honneur. -Nouvelle rumeur dans la petite ville. Eh bien! c'est la croix qu'il -voulait! Le père Madeleine refusa la croix. - -Décidément cet homme était une énigme. Les bonnes âmes se tirèrent -d'affaire en disant: Après tout, c'est une espèce d'aventurier. - -On l'a vu, le pays lui devait beaucoup, les pauvres lui devaient tout; -il était si utile qu'il avait bien fallu qu'on finît par l'honorer, et -il était si doux qu'il avait bien fallu qu'on finît par l'aimer; ses -ouvriers en particulier l'adoraient, et il portait cette adoration avec -une sorte de gravité mélancolique. Quand il fut constaté riche, «les -personnes de la société» le saluèrent, et on l'appela dans la ville -monsieur Madeleine; ses ouvriers et les enfants continuèrent de -l'appeler _le père Madeleine_, et c'était la chose qui le faisait le -mieux sourire. À mesure qu'il montait, les invitations pleuvaient sur -lui. «La société» le réclamait. Les petits salons guindés de -Montreuil-sur-mer qui, bien entendu, se fussent dans les premiers temps -fermés à l'artisan, s'ouvrirent à deux battants au millionnaire. On lui -fit mille avances. Il refusa. - -Cette fois encore les bonnes âmes ne furent point empêchées. - ---C'est un homme ignorant et de basse éducation. On ne sait d'où cela -sort. Il ne saurait pas se tenir dans le monde. Il n'est pas du tout -prouvé qu'il sache lire. - -Quand on l'avait vu gagner de l'argent, on avait dit: c'est un marchand. -Quand on l'avait vu semer son argent, on avait dit: c'est un ambitieux. -Quand on l'avait vu repousser les honneurs, on avait dit: c'est un -aventurier. Quand on le vit repousser le monde, on dit: c'est une brute. - -En 1820, cinq ans après son arrivée à Montreuil-sur-mer, les services -qu'il avait rendus au pays étaient si éclatants, le voeu de la contrée -fut tellement unanime, que le roi le nomma de nouveau maire de la ville. -Il refusa encore, mais le préfet résista à son refus, tous les notables -vinrent le prier, le peuple en pleine rue le suppliait, l'insistance fut -si vive qu'il finit par accepter. On remarqua que ce qui parut surtout -le déterminer, ce fut l'apostrophe presque irritée d'une vieille femme -du peuple qui lui cria du seuil de sa porte avec humeur: _Un bon maire, -c'est utile. Est-ce qu'on recule devant du bien qu'on peut faire?_ - -Ce fut là la troisième phase de son ascension. Le père Madeleine était -devenu monsieur Madeleine, monsieur Madeleine devint monsieur le maire. - - - - -Chapitre III - -Sommes déposées chez Laffitte - - -Du reste, il était demeuré aussi simple que le premier jour. Il avait -les cheveux gris, l'oeil sérieux, le teint hâlé d'un ouvrier, le visage -pensif d'un philosophe. Il portait habituellement un chapeau à bords -larges et une longue redingote de gros drap, boutonnée jusqu'au menton. -Il remplissait ses fonctions de maire, mais hors de là il vivait -solitaire. Il parlait à peu de monde. Il se dérobait aux politesses, -saluait de côté, s'esquivait vite, souriait pour se dispenser de causer, -donnait pour se dispenser de sourire. Les femmes disaient de lui: Quel -bon ours! Son plaisir était de se promener dans les champs. - -Il prenait ses repas toujours seul, avec un livre ouvert devant lui où -il lisait. Il avait une petite bibliothèque bien faite. Il aimait les -livres; les livres sont des amis froids et sûrs. À mesure que le loisir -lui venait avec la fortune, il semblait qu'il en profitât pour cultiver -son esprit. Depuis qu'il était à Montreuil-sur-mer, on remarquait que -d'année en année son langage devenait plus poli, plus choisi et plus -doux. - -Il emportait volontiers un fusil dans ses promenades, mais il s'en -servait rarement. Quand cela lui arrivait par aventure, il avait un tir -infaillible qui effrayait. Jamais il ne tuait un animal inoffensif. -Jamais il ne tirait un petit oiseau. Quoiqu'il ne fût plus jeune, on -contait qu'il était d'une force prodigieuse. Il offrait un coup de main -à qui en avait besoin, relevait un cheval, poussait à une roue -embourbée, arrêtait par les cornes un taureau échappé. Il avait toujours -ses poches pleines de monnaie en sortant et vides en rentrant. Quand il -passait dans un village, les marmots déguenillés couraient joyeusement -après lui et l'entouraient comme une nuée de moucherons. - -On croyait deviner qu'il avait dû vivre jadis de la vie des champs, car -il avait toutes sortes de secrets utiles qu'il enseignait aux paysans. -Il leur apprenait à détruire la teigne des blés en aspergeant le grenier -et en inondant les fentes du plancher d'une dissolution de sel commun, -et à chasser les charançons en suspendant partout, aux murs et aux -toits, dans les héberges et dans les maisons, de l'orviot en fleur. Il -avait des "recettes" pour extirper d'un champ la luzette, la nielle, la -vesce, la gaverolle, la queue-de-renard, toutes les herbes parasites qui -mangent le blé. Il défendait une lapinière contre les rats rien qu'avec -l'odeur d'un petit cochon de Barbarie qu'il y mettait. Un jour il voyait -des gens du pays très occupés à arracher des orties. Il regarda ce tas -de plantes déracinées et déjà desséchées, et dit: - ---C'est mort. Cela serait pourtant bon si l'on savait s'en servir. Quand -l'ortie est jeune, la feuille est un légume excellent; quand elle -vieillit, elle a des filaments et des fibres comme le chanvre et le lin. -La toile d'ortie vaut la toile de chanvre. Hachée, l'ortie est bonne -pour la volaille; broyée, elle est bonne pour les bêtes à cornes. La -graine de l'ortie mêlée au fourrage donne du luisant au poil des -animaux; la racine mêlée au sel produit une belle couleur jaune. C'est -du reste un excellent foin qu'on peut faucher deux fois. Et que faut-il -à l'ortie? Peu de terre, nul soin, nulle culture. Seulement la graine -tombe à mesure qu'elle mûrit, et est difficile à récolter. Voilà tout. -Avec quelque peine qu'on prendrait, l'ortie serait utile; on la néglige, -elle devient nuisible. Alors on la tue. Que d'hommes ressemblent à -l'ortie! - -Il ajouta après un silence: - ---Mes amis, retenez ceci, il n'y a ni mauvaises herbes ni mauvais -hommes. Il n'y a que de mauvais cultivateurs. - -Les enfants l'aimaient encore parce qu'il savait faire de charmants -petits ouvrages avec de la paille et des noix de coco. - -Quand il voyait la porte d'une église tendue de noir, il entrait; il -recherchait un enterrement comme d'autres recherchent un baptême. Le -veuvage et le malheur d'autrui l'attiraient à cause de sa grande -douceur; il se mêlait aux amis en deuil, aux familles vêtues de noir, -aux prêtres gémissant autour d'un cercueil. Il semblait donner -volontiers pour texte à ses pensées ces psalmodies funèbres pleines de -la vision d'un autre monde. L'oeil au ciel, il écoutait, avec une sorte -d'aspiration vers tous les mystères de l'infini, ces voix tristes qui -chantent sur le bord de l'abîme obscur de la mort. - -Il faisait une foule de bonnes actions en se cachant comme on se cache -pour les mauvaises. Il pénétrait à la dérobée, le soir, dans les -maisons; il montait furtivement des escaliers. Un pauvre diable, en -rentrant dans son galetas, trouvait que sa porte avait été ouverte, -quelquefois même forcée, dans son absence. Le pauvre homme se récriait: -quelque malfaiteur est venu! Il entrait, et la première chose qu'il -voyait, c'était une pièce d'or oubliée sur un meuble. "Le malfaiteur" -qui était venu, c'était le père Madeleine. - -Il était affable et triste. Le peuple disait: «Voilà un homme riche qui -n'a pas l'air fier. Voilà un homme heureux qui n'a pas l'air content.» - -Quelques-uns prétendaient que c'était un personnage mystérieux, et -affirmaient qu'on n'entrait jamais dans sa chambre, laquelle était une -vraie cellule d'anachorète meublée de sabliers ailés et enjolivée de -tibias en croix et de têtes de mort. Cela se disait beaucoup, si bien -que quelques jeunes femmes élégantes et malignes de Montreuil-sur-mer -vinrent chez lui un jour, et lui demandèrent: - ---Monsieur le maire, montrez-nous donc votre chambre. On dit que c'est -une grotte. - -Il sourit, et les introduisit sur-le-champ dans cette «grotte». Elles -furent bien punies de leur curiosité. C'était une chambre garnie tout -bonnement de meubles d'acajou assez laids comme tous les meubles de ce -genre et tapissée de papier à douze sous. Elles n'y purent rien -remarquer que deux flambeaux de forme vieillie qui étaient sur la -cheminée et qui avaient l'air d'être en argent, «car ils étaient -contrôlés». Observation pleine de l'esprit des petites villes. - -On n'en continua pas moins de dire que personne ne pénétrait dans cette -chambre et que c'était une caverne d'ermite, un rêvoir, un trou, un -tombeau. - -On se chuchotait aussi qu'il avait des sommes «immenses» déposées chez -Laffitte, avec cette particularité qu'elles étaient toujours à sa -disposition immédiate, de telle sorte, ajoutait-on, que M. Madeleine -pourrait arriver un matin chez Laffitte, signer un reçu et emporter ses -deux ou trois millions en dix minutes. Dans la réalité ces «deux ou -trois millions» se réduisaient, nous l'avons dit, à six cent trente ou -quarante mille francs. - - - - -Chapitre IV - -M. Madeleine en deuil - - -Au commencement de 1821, les journaux annoncèrent la mort de M. Myriel, -évêque de Digne, «surnommé _monseigneur Bienvenu_», et trépassé en odeur -de sainteté à l'âge de quatre-vingt-deux ans. - -L'évêque de Digne, pour ajouter ici un détail que les journaux omirent, -était, quand il mourut, depuis plusieurs années aveugle, et content -d'être aveugle, sa soeur étant près de lui. - -Disons-le en passant, être aveugle et être aimé, c'est en effet, sur -cette terre où rien n'est complet, une des formes les plus étrangement -exquises du bonheur. Avoir continuellement à ses côtés une femme, une -fille, une soeur, un être charmant, qui est là parce que vous avez -besoin d'elle et parce qu'elle ne peut se passer de vous, se savoir -indispensable à qui nous est nécessaire, pouvoir incessamment mesurer -son affection à la quantité de présence qu'elle nous donne, et se dire: -puisqu'elle me consacre tout son temps, c'est que j'ai tout son coeur; -voir la pensée à défaut de la figure, constater la fidélité d'un être -dans l'éclipse du monde, percevoir le frôlement d'une robe comme un -bruit d'ailes, l'entendre aller et venir, sortir, rentrer, parler, -chanter, et songer qu'on est le centre de ces pas, de cette parole, de -ce chant, manifester à chaque minute sa propre attraction, se sentir -d'autant plus puissant qu'on est plus infirme, devenir dans l'obscurité, -et par l'obscurité, l'astre autour duquel gravite cet ange, peu de -félicités égalent celle-là. Le suprême bonheur de la vie, c'est la -conviction qu'on est aimé; aimé pour soi-même, disons mieux, aimé malgré -soi-même; cette conviction, l'aveugle l'a. Dans cette détresse, être -servi, c'est être caressé. Lui manque-t-il quelque chose? Non. Ce n'est -point perdre la lumière qu'avoir l'amour. Et quel amour! un amour -entièrement fait de vertu. Il n'y a point de cécité où il y a certitude. -L'âme à tâtons cherche l'âme, et la trouve. Et cette âme trouvée et -prouvée est une femme. Une main vous soutient, c'est la sienne; une -bouche effleure votre front, c'est sa bouche; vous entendez une -respiration tout près de vous, c'est elle. Tout avoir d'elle, depuis son -culte jusqu'à sa pitié, n'être jamais quitté, avoir cette douce -faiblesse qui vous secourt, s'appuyer sur ce roseau inébranlable, -toucher de ses mains la providence et pouvoir la prendre dans ses bras, -Dieu palpable, quel ravissement! Le coeur, cette céleste fleur obscure, -entre dans un épanouissement mystérieux. On ne donnerait pas cette ombre -pour toute la clarté. L'âme ange est là, sans cesse là; si elle -s'éloigne, c'est pour revenir; elle s'efface comme le rêve et reparaît -comme la réalité. On sent de la chaleur qui approche, la voilà. On -déborde de sérénité, de gaîté et d'extase; on est un rayonnement dans la -nuit. Et mille petits soins. Des riens qui sont énormes dans ce vide. -Les plus ineffables accents de la voix féminine employés à vous bercer, -et suppléant pour vous à l'univers évanoui. On est caressé avec de -l'âme. On ne voit rien, mais on se sent adoré. C'est un paradis de -ténèbres. - -C'est de ce paradis que monseigneur Bienvenu était passé à l'autre. - -L'annonce de sa mort fut reproduite par le journal local de -Montreuil-sur-mer. M. Madeleine parut le lendemain tout en noir avec un -crêpe à son chapeau. - -On remarqua dans la ville ce deuil, et l'on jasa. Cela parut une lueur -sur l'origine de M. Madeleine. On en conclut qu'il avait quelque -alliance avec le vénérable évêque. _Il drape pour l'évêque de Digne_, -dirent les salons; cela rehaussa fort M. Madeleine, et lui donna -subitement et d'emblée une certaine considération dans le monde noble de -Montreuil-sur-mer. Le microscopique faubourg Saint-Germain de l'endroit -songea à faire cesser la quarantaine de M. Madeleine, parent probable -d'un évêque. M. Madeleine s'aperçut de l'avancement qu'il obtenait à -plus de révérences des vieilles femmes et à plus de sourires des jeunes. -Un soir, une doyenne de ce petit grand monde-là, curieuse par droit -d'ancienneté, se hasarda à lui demander: - ---Monsieur le maire est sans doute cousin du feu évêque de Digne? - -Il dit: - ---Non, madame. - ---Mais, reprit la douairière, vous en portez le deuil? - -Il répondit: - ---C'est que dans ma jeunesse j'ai été laquais dans sa famille. - -Une remarque qu'on faisait encore, c'est que, chaque fois qu'il passait -dans la ville un jeune savoyard courant le pays et cherchant des -cheminées à ramoner, M. le maire le faisait appeler, lui demandait son -nom, et lui donnait de l'argent. Les petits savoyards se le disaient, et -il en passait beaucoup. - - - - -Chapitre V - -Vagues éclairs à l'horizon - - -Peu à peu, et avec le temps, toutes les oppositions étaient tombées. Il -y avait eu d'abord contre M. Madeleine, sorte de loi que subissent -toujours ceux qui s'élèvent, des noirceurs et des calomnies, puis ce ne -fut plus que des méchancetés, puis ce ne fut que des malices, puis cela -s'évanouit tout à fait; le respect devint complet, unanime, cordial, et -il arriva un moment, vers 1821, où ce mot: monsieur le maire, fut -prononcé à Montreuil-sur-mer presque du même accent que ce mot: -monseigneur l'évêque, était prononcé à Digne en 1815. On venait de dix -lieues à la ronde consulter M. Madeleine. Il terminait les différends, -il empêchait les procès, il réconciliait les ennemis. Chacun le prenait -pour juge de son bon droit. Il semblait qu'il eût pour âme le livre de -la loi naturelle. Ce fut comme une contagion de vénération qui, en six -ou sept ans et de proche en proche, gagna tout le pays. - -Un seul homme, dans la ville et dans l'arrondissement, se déroba -absolument à cette contagion, et, quoi que fît le père Madeleine, y -demeura rebelle, comme si une sorte d'instinct, incorruptible et -imperturbable, l'éveillait et l'inquiétait. Il semblerait en effet qu'il -existe dans certains hommes un véritable instinct bestial, pur et -intègre comme tout instinct, qui crée les antipathies et les sympathies, -qui sépare fatalement une nature d'une autre nature, qui n'hésite pas, -qui ne se trouble, ne se tait et ne se dément jamais, clair dans son -obscurité, infaillible, impérieux, réfractaire à tous les conseils de -l'intelligence et à tous les dissolvants de la raison, et qui, de -quelque façon que les destinées soient faites, avertit secrètement -l'homme-chien de la présence de l'homme-chat, et l'homme-renard de la -présence de l'homme-lion. - -Souvent, quand M. Madeleine passait dans une rue, calme, affectueux, -entouré des bénédictions de tous, il arrivait qu'un homme de haute -taille, vêtu d'une redingote gris de fer, armé d'une grosse canne et -coiffé d'un chapeau rabattu, se retournait brusquement derrière lui, et -le suivait des yeux jusqu'à ce qu'il eût disparu, croisant les bras, -secouant lentement la tête, et haussant sa lèvre supérieure avec sa -lèvre inférieure jusqu'à son nez, sorte de grimace significative qui -pourrait se traduire par: «Mais qu'est-ce que c'est que cet -homme-là?--Pour sûr je l'ai vu quelque part.--En tout cas, je ne suis -toujours pas sa dupe.» - -Ce personnage, grave d'une gravité presque menaçante, était de ceux qui, -même rapidement entrevus, préoccupent l'observateur. - -Il se nommait Javert, et il était de la police. - -Il remplissait à Montreuil-sur-mer les fonctions pénibles, mais utiles, -d'inspecteur. Il n'avait pas vu les commencements de Madeleine. Javert -devait le poste qu'il occupait à la protection de M. Chabouillet, le -secrétaire du ministre d'État, comte Anglès, alors préfet de police à -Paris. Quand Javert était arrivé à Montreuil-sur-mer, la fortune du -grand manufacturier était déjà faite, et le père Madeleine était devenu -monsieur Madeleine. - -Certains officiers de police ont une physionomie à part et qui se -complique d'un air de bassesse mêlé à un air d'autorité. Javert avait -cette physionomie, moins la bassesse. - -Dans notre conviction, si les âmes étaient visibles aux yeux, on verrait -distinctement cette chose étrange que chacun des individus de l'espèce -humaine correspond à quelqu'une des espèces de la création animale; et -l'on pourrait reconnaître aisément cette vérité à peine entrevue par le -penseur, que, depuis l'huître jusqu'à l'aigle, depuis le porc jusqu'au -tigre, tous les animaux sont dans l'homme et que chacun d'eux est dans -un homme. Quelquefois même plusieurs d'entre eux à la fois. - -Les animaux ne sont autre chose que les figures de nos vertus et de nos -vices, errantes devant nos yeux, les fantômes visibles de nos âmes. Dieu -nous les montre pour nous faire réfléchir. Seulement, comme les animaux -ne sont que des ombres, Dieu ne les a point faits éducables dans le sens -complet du mot; à quoi bon? Au contraire, nos âmes étant des réalités et -ayant une fin qui leur est propre, Dieu leur a donné l'intelligence, -c'est-à-dire l'éducation possible. L'éducation sociale bien faite peut -toujours tirer d'une âme, quelle qu'elle soit, l'utilité qu'elle -contient. - -Ceci soit dit, bien entendu, au point de vue restreint de la vie -terrestre apparente, et sans préjuger la question profonde de la -personnalité antérieure et ultérieure des êtres qui ne sont pas l'homme. -Le moi visible n'autorise en aucune façon le penseur à nier le moi -latent. Cette réserve faite, passons. - -Maintenant, si l'on admet un moment avec nous que dans tout homme il y a -une des espèces animales de la création, il nous sera facile de dire ce -que c'était que l'officier de paix Javert. - -Les paysans asturiens sont convaincus que dans toute portée de louve il -y a un chien, lequel est tué par la mère, sans quoi en grandissant il -dévorerait les autres petits. - -Donnez une face humaine à ce chien fils d'une louve, et ce sera Javert. - -Javert était né dans une prison d'une tireuse de cartes dont le mari -était aux galères. En grandissant, il pensa qu'il était en dehors de la -société et désespéra d'y rentrer jamais. Il remarqua que la société -maintient irrémissiblement en dehors d'elle deux classes d'hommes, ceux -qui l'attaquent et ceux qui la gardent; il n'avait le choix qu'entre ces -deux classes; en même temps il se sentait je ne sais quel fond de -rigidité, de régularité et de probité, compliqué d'une inexprimable -haine pour cette race de bohèmes dont il était. Il entra dans la police. - -Il y réussit. À quarante ans il était inspecteur. - -Il avait dans sa jeunesse été employé dans les chiourmes du midi. - -Avant d'aller plus loin, entendons-nous sur ce mot face humaine que nous -appliquions tout à l'heure à Javert. - -La face humaine de Javert consistait en un nez camard, avec deux -profondes narines vers lesquelles montaient sur ses deux joues d'énormes -favoris. On se sentait mal à l'aise la première fois qu'on voyait ces -deux forêts et ces deux cavernes. Quand Javert riait, ce qui était rare -et terrible, ses lèvres minces s'écartaient, et laissaient voir, non -seulement ses dents, mais ses gencives, et il se faisait autour de son -nez un plissement épaté et sauvage comme sur un mufle de bête fauve. -Javert sérieux était un dogue; lorsqu'il riait, c'était un tigre. Du -reste, peu de crâne, beaucoup de mâchoire, les cheveux cachant le front -et tombant sur les sourcils, entre les deux yeux un froncement central -permanent comme une étoile de colère, le regard obscur, la bouche pincée -et redoutable, l'air du commandement féroce. - -Cet homme était composé de deux sentiments très simples, et relativement -très bons, mais qu'il faisait presque mauvais à force de les exagérer: -le respect de l'autorité, la haine de la rébellion; et à ses yeux le -vol, le meurtre, tous les crimes, n'étaient que des formes de la -rébellion. Il enveloppait dans une sorte de foi aveugle et profonde tout -ce qui a une fonction dans l'État, depuis le premier ministre jusqu'au -garde champêtre. Il couvrait de mépris, d'aversion et de dégoût tout ce -qui avait franchi une fois le seuil légal du mal. Il était absolu et -n'admettait pas d'exceptions. D'une part il disait: - ---Le fonctionnaire ne peut se tromper; le magistrat n'a jamais tort. - -D'autre part il disait: - ---Ceux-ci sont irrémédiablement perdus. Rien de bon n'en peut sortir. - -Il partageait pleinement l'opinion de ces esprits extrêmes qui -attribuent à la loi humaine je ne sais quel pouvoir de faire ou, si l'on -veut, de constater des damnés, et qui mettent un Styx au bas de la -société. Il était stoïque, sérieux, austère; rêveur triste; humble et -hautain comme les fanatiques. Son regard était une vrille. Cela était -froid et cela perçait. Toute sa vie tenait dans ces deux mots: veiller -et surveiller. Il avait introduit la ligne droite dans ce qu'il y a de -plus tortueux au monde; il avait la conscience de son utilité, la -religion de ses fonctions, et il était espion comme on est prêtre. -Malheur à qui tombait sous sa main! Il eût arrêté son père s'évadant du -bagne et dénoncé sa mère en rupture de ban. Et il l'eût fait avec cette -sorte de satisfaction intérieure que donne la vertu. Avec cela une vie -de privations, l'isolement, l'abnégation, la chasteté, jamais une -distraction. C'était le devoir implacable, la police comprise comme les -Spartiates comprenaient Sparte, un guet impitoyable, une honnêteté -farouche, un mouchard marmoréen, Brutus dans Vidocq. - -Toute la personne de Javert exprimait l'homme qui épie et qui se dérobe. -L'école mystique de Joseph de Maistre, laquelle à cette époque -assaisonnait de haute cosmogonie ce qu'on appelait les journaux ultras, -n'eût pas manqué de dire que Javert était un symbole. On ne voyait pas -son front qui disparaissait sous son chapeau, on ne voyait pas ses yeux -qui se perdaient sous ses sourcils, on ne voyait pas son menton qui -plongeait dans sa cravate, on ne voyait pas ses mains qui rentraient -dans ses manches, on ne voyait pas sa canne qu'il portait sous sa -redingote. Mais l'occasion venue, on voyait tout à coup sortir de toute -cette ombre, comme d'une embuscade, un front anguleux et étroit, un -regard funeste, un menton menaçant, des mains énormes; et un gourdin -monstrueux. - -À ses moments de loisir, qui étaient peu fréquents, tout en haïssant les -livres, il lisait; ce qui fait qu'il n'était pas complètement illettré. -Cela se reconnaissait à quelque emphase dans la parole. - -Il n'avait aucun vice, nous l'avons dit. Quand il était content de lui, -il s'accordait une prise de tabac. Il tenait à l'humanité par là. - -On comprendra sans peine que Javert était l'effroi de toute cette classe -que la statistique annuelle du ministère de la justice désigne sous la -rubrique: _Gens sans aveu_. Le nom de Javert prononcé les mettait en -déroute; la face de Javert apparaissant les pétrifiait. - -Tel était cet homme formidable. - -Javert était comme un oeil toujours fixé sur M. Madeleine. Oeil plein de -soupçon et de conjectures. M. Madeleine avait fini par s'en apercevoir, -mais il sembla que cela fût insignifiant pour lui. Il ne fit pas même -une question à Javert, il ne le cherchait ni ne l'évitait, et il -portait, sans paraître y faire attention, ce regard gênant et presque -pesant. Il traitait Javert comme tout le monde, avec aisance et bonté. - -À quelques paroles échappées à Javert, on devinait qu'il avait recherché -secrètement, avec cette curiosité qui tient à la race et où il entre -autant d'instinct que de volonté, toutes les traces antérieures que le -père Madeleine avait pu laisser ailleurs. Il paraissait savoir, et il -disait parfois à mots couverts, que quelqu'un avait pris certaines -informations dans un certain pays sur une certaine famille disparue. Une -fois il lui arriva de dire, se parlant à lui-même: - ---Je crois que je le tiens! - -Puis il resta trois jours pensif sans prononcer une parole. Il paraît -que le fil qu'il croyait tenir s'était rompu. Du reste, et ceci est le -correctif nécessaire à ce que le sens de certains mots pourrait -présenter de trop absolu, il ne peut y avoir rien de vraiment -infaillible dans une créature humaine, et le propre de l'instinct est -précisément de pouvoir être troublé, dépisté et dérouté. Sans quoi il -serait supérieur à l'intelligence, et la bête se trouverait avoir une -meilleure lumière que l'homme. - -Javert était évidemment quelque peu déconcerté par le complet naturel et -la tranquillité de M. Madeleine. - -Un jour pourtant son étrange manière d'être parut faire impression sur -M. Madeleine. Voici à quelle occasion. - - - - -Chapitre VI - -Le père Fauchelevent - - -M. Madeleine passait un matin dans une ruelle non pavée de -Montreuil-sur-mer. Il entendit du bruit et vit un groupe à quelque -distance. Il y alla. Un vieux homme, nommé le père Fauchelevent, venait -de tomber sous sa charrette dont le cheval s'était abattu. - -Ce Fauchelevent était un des rares ennemis qu'eût encore M. Madeleine à -cette époque. Lorsque Madeleine était arrivé dans le pays, Fauchelevent, -ancien tabellion et paysan presque lettré, avait un commerce qui -commençait à aller mal. Fauchelevent avait vu ce simple ouvrier qui -s'enrichissait, tandis que lui, maître, se ruinait. Cela l'avait rempli -de jalousie, et il avait fait ce qu'il avait pu en toute occasion pour -nuire à Madeleine. Puis la faillite était venue, et, vieux, n'ayant plus -à lui qu'une charrette et un cheval, sans famille et sans enfants du -reste, pour vivre il s'était fait charretier. - -Le cheval avait les deux cuisses cassées et ne pouvait se relever. Le -vieillard était engagé entre les roues. La chute avait été tellement -malheureuse que toute la voiture pesait sur sa poitrine. La charrette -était assez lourdement chargée. Le père Fauchelevent poussait des râles -lamentables. On avait essayé de le tirer, mais en vain. Un effort -désordonné, une aide maladroite, une secousse à faux pouvaient -l'achever. Il était impossible de le dégager autrement qu'en soulevant -la voiture par-dessous. Javert, qui était survenu au moment de -l'accident, avait envoyé chercher un cric. - -M. Madeleine arriva. On s'écarta avec respect. - ---À l'aide! criait le vieux Fauchelevent. Qui est-ce qui est bon enfant -pour sauver le vieux? - -M. Madeleine se tourna vers les assistants: - ---A-t-on un cric? - ---On en est allé quérir un, répondit un paysan. - ---Dans combien de temps l'aura-t-on? - ---On est allé au plus près, au lieu Flachot, où il y a un maréchal; mais -c'est égal, il faudra bien un bon quart d'heure. - ---Un quart d'heure! s'écria Madeleine. - -Il avait plu la veille, le sol était détrempé, la charrette s'enfonçait -dans la terre à chaque instant et comprimait de plus en plus la poitrine -du vieux charretier. Il était évident qu'avant cinq minutes il aurait -les côtes brisées. - ---Il est impossible d'attendre un quart d'heure, dit Madeleine aux -paysans qui regardaient. - ---Il faut bien! - ---Mais il ne sera plus temps! Vous ne voyez donc pas que la charrette -s'enfonce? - ---Dame! - ---Écoutez, reprit Madeleine, il y a encore assez de place sous la -voiture pour qu'un homme s'y glisse et la soulève avec son dos. Rien -qu'une demi-minute, et l'on tirera le pauvre homme. Y a-t-il ici -quelqu'un qui ait des reins et du coeur? Cinq louis d'or à gagner! - -Personne ne bougea dans le groupe. - ---Dix louis, dit Madeleine. - -Les assistants baissaient les yeux. Un d'eux murmura: - ---Il faudrait être diablement fort. Et puis, on risque de se faire -écraser! - ---Allons! recommença Madeleine, vingt louis! Même silence. - ---Ce n'est pas la bonne volonté qui leur manque, dit une voix. - -M. Madeleine se retourna, et reconnut Javert. Il ne l'avait pas aperçu -en arrivant. Javert continua: - ---C'est la force. Il faudrait être un terrible homme pour faire la chose -de lever une voiture comme cela sur son dos. - -Puis, regardant fixement M. Madeleine, il poursuivit en appuyant sur -chacun des mots qu'il prononçait: - ---Monsieur Madeleine, je n'ai jamais connu qu'un seul homme capable de -faire ce que vous demandez là. - -Madeleine tressaillit. - -Javert ajouta avec un air d'indifférence, mais sans quitter des yeux -Madeleine: - ---C'était un forçat. - ---Ah! dit Madeleine. - ---Du bagne de Toulon. - -Madeleine devint pâle. - -Cependant la charrette continuait à s'enfoncer lentement. Le père -Fauchelevent râlait et hurlait: - ---J'étouffe! Ça me brise les côtes! Un cric! quelque chose! Ah! - -Madeleine regarda autour de lui: - ---Il n'y a donc personne qui veuille gagner vingt louis et sauver la vie -à ce pauvre vieux? - -Aucun des assistants ne remua. Javert reprit: - ---Je n'ai jamais connu qu'un homme qui pût remplacer un cric. C'était ce -forçat. - ---Ah! voilà que ça m'écrase! cria le vieillard. - -Madeleine leva la tête, rencontra l'oeil de faucon de Javert toujours -attaché sur lui, regarda les paysans immobiles, et sourit tristement. -Puis, sans dire une parole, il tomba à genoux, et avant même que la -foule eût eu le temps de jeter un cri, il était sous la voiture. - -Il y eut un affreux moment d'attente et de silence. - -On vit Madeleine presque à plat ventre sous ce poids effrayant essayer -deux fois en vain de rapprocher ses coudes de ses genoux. On lui cria: - ---Père Madeleine! retirez-vous de là! - -Le vieux Fauchelevent lui-même lui dit: - ---Monsieur Madeleine! allez-vous-en! C'est qu'il faut que je meure, -voyez-vous! Laissez-moi! Vous allez vous faire écraser aussi! - -Madeleine ne répondit pas. - -Les assistants haletaient. Les roues avaient continué de s'enfoncer, et -il était déjà devenu presque impossible que Madeleine sortît de dessous -la voiture. - -Tout à coup on vit l'énorme masse s'ébranler, la charrette se soulevait -lentement, les roues sortaient à demi de l'ornière. On entendit une voix -étouffée qui criait: - ---Dépêchez-vous! aidez! - -C'était Madeleine qui venait de faire un dernier effort. - -Ils se précipitèrent. Le dévouement d'un seul avait donné de la force et -du courage à tous. La charrette fut enlevée par vingt bras. Le vieux -Fauchelevent était sauvé. - -Madeleine se releva. Il était blême, quoique ruisselant de sueur. Ses -habits étaient déchirés et couverts de boue. Tous pleuraient. Le -vieillard lui baisait les genoux et l'appelait le bon Dieu. Lui, il -avait sur le visage je ne sais quelle expression de souffrance heureuse -et céleste, et il fixait son oeil tranquille sur Javert qui le regardait -toujours. - - - - -Chapitre VII - -Fauchelevent devient jardinier à Paris - - -Fauchelevent s'était démis la rotule dans sa chute. Le père Madeleine le -fit transporter dans une infirmerie qu'il avait établie pour ses -ouvriers dans le bâtiment même de sa fabrique et qui était desservie par -deux soeurs de charité. Le lendemain matin, le vieillard trouva un -billet de mille francs sur sa table de nuit, avec ce mot de la main du -père Madeleine: _Je vous achète votre charrette et votre cheval_. La -charrette était brisée et le cheval était mort. Fauchelevent guérit, -mais son genou resta ankylosé. M. Madeleine, par les recommandations des -soeurs et de son curé, fit placer le bonhomme comme jardinier dans un -couvent de femmes du quartier Saint-Antoine à Paris. - -Quelque temps après, M. Madeleine fut nommé maire. La première fois que -Javert vit M. Madeleine revêtu de l'écharpe qui lui donnait toute -autorité sur la ville, il éprouva cette sorte de frémissement -qu'éprouverait un dogue qui flairerait un loup sous les habits de son -maître. À partir de ce moment, il l'évita le plus qu'il put. Quand les -besoins du service l'exigeaient impérieusement et qu'il ne pouvait faire -autrement que de se trouver avec M. le maire, il lui parlait avec un -respect profond. - -Cette prospérité créée à Montreuil-sur-mer par le père Madeleine avait, -outre les signes visibles que nous avons indiqués, un autre symptôme -qui, pour n'être pas visible, n'était pas moins significatif. Ceci ne -trompe jamais. - -Quand la population souffre, quand le travail manque, quand le commerce -est nul, le contribuable résiste à l'impôt par pénurie, épuise et -dépasse les délais, et l'état dépense beaucoup d'argent en frais de -contrainte et de rentrée. Quand le travail abonde, quand le pays est -heureux et riche, l'impôt se paye aisément et coûte peu à l'état. On -peut dire que la misère et la richesse publiques ont un thermomètre -infaillible, les frais de perception de l'impôt. En sept ans, les frais -de perception de l'impôt s'étaient réduits des trois quarts dans -l'arrondissement de Montreuil-sur-mer, ce qui faisait fréquemment citer -cet arrondissement entre tous par M. de Villèle, alors ministre des -finances. - -Telle était la situation du pays, lorsque Fantine y revint. Personne ne -se souvenait plus d'elle. Heureusement la porte de la fabrique de M. -Madeleine était comme un visage ami. Elle s'y présenta, et fut admise -dans l'atelier des femmes. Le métier était tout nouveau pour Fantine, -elle n'y pouvait être bien adroite, elle ne tirait donc de sa journée de -travail que peu de chose, mais enfin cela suffisait, le problème était -résolu, elle gagnait sa vie. - - - - -Chapitre VIII - -Madame Victurnien dépense trente-cinq francs pour la morale - - -Quand Fantine vit qu'elle vivait, elle eut un moment de joie. Vivre -honnêtement de son travail, quelle grâce du ciel! Le goût du travail lui -revint vraiment. Elle acheta un miroir, se réjouit d'y regarder sa -jeunesse, ses beaux cheveux et ses belles dents, oublia beaucoup de -choses, ne songea plus qu'à sa Cosette et à l'avenir possible, et fut -presque heureuse. Elle loua une petite chambre et la meubla à crédit sur -son travail futur; reste de ses habitudes de désordre. - -Ne pouvant pas dire qu'elle était mariée, elle s'était bien gardée, -comme nous l'avons déjà fait entrevoir, de parler de sa petite fille. - -En ces commencements, on l'a vu, elle payait exactement les Thénardier. -Comme elle ne savait que signer, elle était obligée de leur écrire par -un écrivain public. - -Elle écrivait souvent. Cela fut remarqué. On commença à dire tout bas -dans l'atelier des femmes que Fantine «écrivait des lettres» et qu'«elle -avait des allures». - -Il n'y a rien de tel pour épier les actions des gens que ceux qu'elles -ne regardent pas.--Pourquoi ce monsieur ne vient-il jamais qu'à la -brune? pourquoi monsieur un tel n'accroche-t-il jamais sa clef au clou -le jeudi? pourquoi prend-il toujours les petites rues? pourquoi madame -descend-elle toujours de son fiacre avant d'arriver à la maison? -pourquoi envoie-t-elle acheter un cahier de papier à lettres, quand elle -en a «plein sa papeterie?» etc., etc.--Il existe des êtres qui, pour -connaître le mot de ces énigmes, lesquelles leur sont du reste -parfaitement indifférentes, dépensent plus d'argent, prodiguent plus de -temps, se donnent plus de peine qu'il n'en faudrait pour dix bonnes -actions; et cela, gratuitement, pour le plaisir, sans être payés de la -curiosité autrement que par la curiosité. Ils suivront celui-ci ou -celle-là des jours entiers, feront faction des heures à des coins de -rue, sous des portes d'allées, la nuit, par le froid et par la pluie, -corrompront des commissionnaires, griseront des cochers de fiacre et des -laquais, achèteront une femme de chambre, feront acquisition d'un -portier. Pourquoi? pour rien. Pur acharnement de voir, de savoir et de -pénétrer. Pure démangeaison de dire. Et souvent ces secrets connus, ces -mystères publiés, ces énigmes éclairées du grand jour, entraînent des -catastrophes, des duels, des faillites, des familles ruinées, des -existences brisées, à la grande joie de ceux qui ont «tout découvert» -sans intérêt et par pur instinct. Chose triste. - -Certaines personnes sont méchantes uniquement par besoin de parler. Leur -conversation, causerie dans le salon, bavardage dans l'antichambre, est -comme ces cheminées qui usent vite le bois; il leur faut beaucoup de -combustible; et le combustible, c'est le prochain. - -On observa donc Fantine. - -Avec cela, plus d'une était jalouse de ses cheveux blonds et de ses -dents blanches. On constata que dans l'atelier, au milieu des autres, -elle se détournait souvent pour essuyer une larme. C'étaient les moments -où elle songeait à son enfant; peut-être aussi à l'homme qu'elle avait -aimé. - -C'est un douloureux labeur que la rupture des sombres attaches du passé. - -On constata qu'elle écrivait, au moins deux fois par mois, toujours à la -même adresse, et qu'elle affranchissait la lettre. On parvint à se -procurer l'adresse: _Monsieur, Monsieur Thénardier, aubergiste, à -Montfermeil_. On fit jaser au cabaret l'écrivain public, vieux bonhomme -qui ne pouvait pas emplir son estomac de vin rouge sans vider sa poche -aux secrets. Bref, on sut que Fantine avait un enfant. «Ce devait être -une espèce de fille.» Il se trouva une commère qui fit le voyage de -Montfermeil, parla aux Thénardier, et dit à son retour: «Pour mes -trente-cinq francs, j'en ai eu le coeur net. J'ai vu l'enfant!» - -La commère qui fit cela était une gorgone appelée madame Victurnien, -gardienne et portière de la vertu de tout le monde. Madame Victurnien -avait cinquante-six ans, et doublait le masque de la laideur du masque -de la vieillesse. Voix chevrotante, esprit capricant. Cette vieille -femme avait été jeune, chose étonnante. Dans sa jeunesse, en plein 93, -elle avait épousé un moine échappé du cloître en bonnet rouge et passé -des bernardins aux jacobins. Elle était sèche, rêche, revêche, pointue, -épineuse, presque venimeuse; tout en se souvenant de son moine dont elle -était veuve, et qui l'avait fort domptée et pliée. C'était une ortie où -l'on voyait le froissement du froc. À la restauration, elle s'était -faite bigote, et si énergiquement que les prêtres lui avaient pardonné -son moine. Elle avait un petit bien qu'elle léguait bruyamment à une -communauté religieuse. Elle était fort bien vue à l'évêché d'Arras. -Cette madame Victurnien donc alla à Montfermeil, et revint en disant: -«J'ai vu l'enfant». - -Tout cela prit du temps. Fantine était depuis plus d'un an à la -fabrique, lorsqu'un matin la surveillante de l'atelier lui remit, de la -part de M. le maire, cinquante francs, en lui disant qu'elle ne faisait -plus partie de l'atelier et en l'engageant, de la part de M. le maire, à -quitter le pays. - -C'était précisément dans ce même mois que les Thénardier, après avoir -demandé douze francs au lieu de six, venaient d'exiger quinze francs au -lieu de douze. - -Fantine fut atterrée. Elle ne pouvait s'en aller du pays, elle devait -son loyer et ses meubles. Cinquante francs ne suffisaient pas pour -acquitter cette dette. Elle balbutia quelques mots suppliants. La -surveillante lui signifia qu'elle eût à sortir sur-le-champ de -l'atelier. Fantine n'était du reste qu'une ouvrière médiocre. Accablée -de honte plus encore que de désespoir, elle quitta l'atelier et rentra -dans sa chambre. Sa faute était donc maintenant connue de tous! - -Elle ne se sentit plus la force de dire un mot. On lui conseilla de voir -M. le maire; elle n'osa pas. M. le maire lui donnait cinquante francs, -parce qu'il était bon, et la chassait, parce qu'il était juste. Elle -plia sous cet arrêt. - - - - -Chapitre IX - -Succès de Madame Victurnien - - -La veuve du moine fut donc bonne à quelque chose. - -Du reste, M. Madeleine n'avait rien su de tout cela. Ce sont là de ces -combinaisons d'événements dont la vie est pleine. M. Madeleine avait -pour habitude de n'entrer presque jamais dans l'atelier des femmes. Il -avait mis à la tête de cet atelier une vieille fille, que le curé lui -avait donnée, et il avait toute confiance dans cette surveillante, -personne vraiment respectable, ferme, équitable, intègre, remplie de la -charité qui consiste à donner, mais n'ayant pas au même degré la charité -qui consiste à comprendre et à pardonner. M. Madeleine se remettait de -tout sur elle. Les meilleurs hommes sont souvent forcés de déléguer leur -autorité. C'est dans cette pleine puissance et avec la conviction -qu'elle faisait bien, que la surveillante avait instruit le procès, -jugé, condamné et exécuté Fantine. - -Quant aux cinquante francs, elle les avait donnés sur une somme que M. -Madeleine lui confiait pour aumônes et secours aux ouvrières et dont -elle ne rendait pas compte. - -Fantine s'offrit comme servante dans le pays; elle alla d'une maison à -l'autre. Personne ne voulut d'elle. Elle n'avait pu quitter la ville. Le -marchand fripier auquel elle devait ses meubles, quels meubles! lui -avait dit: «Si vous vous en allez, je vous fais arrêter comme voleuse.» -Le propriétaire auquel elle devait son loyer, lui avait dit: - -«Vous êtes jeune et jolie, vous pouvez payer.» Elle partagea les -cinquante francs entre le propriétaire et le fripier, rendit au marchand -les trois quarts de son mobilier, ne garda que le nécessaire, et se -trouva sans travail, sans état, n'ayant plus que son lit, et devant -encore environ cent francs. - -Elle se mit à coudre de grosses chemises pour les soldats de la -garnison, et gagnait douze sous par jour. Sa fille lui en coûtait dix. -C'est en ce moment qu'elle commença à mal payer les Thénardier. - -Cependant une vieille femme qui lui allumait sa chandelle quand elle -rentrait le soir, lui enseigna l'art de vivre dans la misère. Derrière -vivre de peu, il y a vivre de rien. Ce sont deux chambres; la première -est obscure, la seconde est noire. - -Fantine apprit comment on se passe tout à fait de feu en hiver, comment -on renonce à un oiseau qui vous mange un liard de millet tous les deux -jours, comment on fait de son jupon sa couverture et de sa couverture -son jupon, comment on ménage sa chandelle en prenant son repas à la -lumière de la fenêtre d'en face. On ne sait pas tout ce que certains -êtres faibles, qui ont vieilli dans le dénûment et l'honnêteté, savent -tirer d'un sou. Cela finit par être un talent. Fantine acquit ce sublime -talent et reprit un peu de courage. - -À cette époque, elle disait à une voisine: - ---Bah! je me dis: en ne dormant que cinq heures et en travaillant tout -le reste à mes coutures, je parviendrai bien toujours à gagner à peu -près du pain. Et puis, quand on est triste, on mange moins. Eh bien! des -souffrances, des inquiétudes, un peu de pain d'un côté, des chagrins de -l'autre, tout cela me nourrira. - -Dans cette détresse, avoir sa petite fille eût été un étrange bonheur. -Elle songea à la faire venir. Mais quoi! lui faire partager son -dénûment! Et puis, elle devait aux Thénardier! comment s'acquitter? Et -le voyage! comment le payer? - -La vieille qui lui avait donné ce qu'on pourrait appeler des leçons de -vie indigente était une sainte fille nommée Marguerite, dévote de la -bonne dévotion, pauvre, et charitable pour les pauvres et même pour les -riches, sachant tout juste assez écrire pour signer _Margueritte_, et -croyant en Dieu, ce qui est la science. - -Il y a beaucoup de ces vertus-là en bas; un jour elles seront en haut. -Cette vie a un lendemain. - -Dans les premiers temps, Fantine avait été si honteuse qu'elle n'avait -pas osé sortir. Quand elle était dans la rue, elle devinait qu'on se -retournait derrière elle et qu'on la montrait du doigt; tout le monde la -regardait et personne ne la saluait; le mépris âcre et froid des -passants lui pénétrait dans la chair et dans l'âme comme une bise. - -Dans les petites villes, il semble qu'une malheureuse soit nue sous les -sarcasmes et la curiosité de tous. À Paris, du moins, personne ne vous -connaît, et cette obscurité est un vêtement. Oh! comme elle eût souhaité -venir à Paris! Impossible. - -Il fallut bien s'accoutumer à la déconsidération, comme elle s'était -accoutumée à l'indigence. Peu à peu elle en prit son parti. Après deux -ou trois mois elle secoua la honte et se remit à sortir comme si de rien -n'était. - ---Cela m'est bien égal, dit-elle. - -Elle alla et vint, la tête haute, avec un sourire amer, et sentit -qu'elle devenait effrontée. - -Madame Victurnien quelquefois la voyait passer de sa fenêtre, remarquait -la détresse de «cette créature», grâce à elle "remise à sa place", et se -félicitait. Les méchants ont un bonheur noir. - -L'excès du travail fatiguait Fantine, et la petite toux sèche qu'elle -avait augmenta. Elle disait quelquefois à sa voisine Marguerite: «Tâtez -donc comme mes mains sont chaudes.» - -Cependant le matin, quand elle peignait avec un vieux peigne cassé ses -beaux cheveux qui ruisselaient comme de la soie floche, elle avait une -minute de coquetterie heureuse. - - - - -Chapitre X - -Suite du succès - - -Elle avait été congédiée vers la fin de l'hiver; l'été se passa, mais -l'hiver revint. Jours courts, moins de travail. L'hiver, point de -chaleur, point de lumière, point de midi, le soir touche au matin, -brouillard, crépuscule, la fenêtre est grise, on n'y voit pas clair. Le -ciel est un soupirail. Toute la journée est une cave. Le soleil a l'air -d'un pauvre. L'affreuse saison! L'hiver change en pierre l'eau du ciel -et le coeur de l'homme. Ses créanciers la harcelaient. - -Fantine gagnait trop peu. Ses dettes avaient grossi. Les Thénardier, mal -payés, lui écrivaient à chaque instant des lettres dont le contenu la -désolait et dont le port la ruinait. Un jour ils lui écrivirent que sa -petite Cosette était toute nue par le froid qu'il faisait, qu'elle avait -besoin d'une jupe de laine, et qu'il fallait au moins que la mère -envoyât dix francs pour cela. Elle reçut la lettre, et la froissa dans -ses mains tout le jour. Le soir elle entra chez un barbier qui habitait -le coin de la rue, et défit son peigne. Ses admirables cheveux blonds -lui tombèrent jusqu'aux reins. - ---Les beaux cheveux! s'écria le barbier. - ---Combien m'en donneriez-vous? dit-elle. - ---Dix francs. - ---Coupez-les. - -Elle acheta une jupe de tricot et l'envoya aux Thénardier. - -Cette jupe fit les Thénardier furieux. C'était de l'argent qu'ils -voulaient. Ils donnèrent la jupe à Eponine. La pauvre Alouette continua -de frissonner. - -Fantine pensa: «Mon enfant n'a plus froid. Je l'ai habillée de mes -cheveux.» Elle mettait de petits bonnets ronds qui cachaient sa tête -tondue et avec lesquels elle était encore jolie. - -Un travail ténébreux se faisait dans le coeur de Fantine. Quand elle vit -qu'elle ne pouvait plus se coiffer, elle commença à tout prendre en -haine autour d'elle. Elle avait longtemps partagé la vénération de tous -pour le père Madeleine; cependant, à force de se répéter que c'était lui -qui l'avait chassée, et qu'il était la cause de son malheur, elle en -vint à le haïr lui aussi, lui surtout. Quand elle passait devant la -fabrique aux heures où les ouvriers sont sur la porte, elle affectait de -rire et de chanter. - -Une vieille ouvrière qui la vit une fois chanter et rire de cette façon -dit: - ---Voilà une fille qui finira mal. - -Elle prit un amant, le premier venu, un homme qu'elle n'aimait pas, par -bravade, avec la rage dans le coeur. C'était un misérable, une espèce de -musicien mendiant, un oisif gueux, qui la battait, et qui la quitta -comme elle l'avait pris, avec dégoût. Elle adorait son enfant. - -Plus elle descendait, plus tout devenait sombre autour d'elle plus ce -doux petit ange rayonnait dans le fond de son âme. Elle disait. Quand je -serai riche, j'aurai ma Cosette avec moi; et elle riait. La toux ne la -quittait pas, et elle avait des sueurs dans le dos. - -Un jour elle reçut des Thénardier une lettre ainsi conçue: - -«Cosette est malade d'une maladie qui est dans le pays. Une fièvre -miliaire, qu'ils appellent. Il faut des drogues chères. Cela nous ruine -et nous ne pouvons plus payer. Si vous ne nous envoyez pas quarante -francs avant huit jours, la petite est morte.» - -Elle se mit à rire aux éclats, et elle dit à sa vieille voisine: - ---Ah! ils sont bons! quarante francs! que ça! ça fait deux napoléons! Où -veulent-ils que je les prenne? Sont-ils bêtes, ces paysans! - -Cependant elle alla dans l'escalier près d'une lucarne et relut la -lettre. - -Puis elle descendit l'escalier et sortit en courant et en sautant, riant -toujours. Quelqu'un qui la rencontra lui dit: - ---Qu'est-ce que vous avez donc à être si gaie? - -Elle répondit: - ---C'est une bonne bêtise que viennent de m'écrire des gens de la -campagne. Ils me demandent quarante francs. Paysans, va! - -Comme elle passait sur la place, elle vit beaucoup de monde qui -entourait une voiture de forme bizarre sur l'impériale de laquelle -pérorait tout debout un homme vêtu de rouge. C'était un bateleur -dentiste en tournée, qui offrait au public des râteliers complets, des -opiats, des poudres et des élixirs. - -Fantine se mêla au groupe et se mit à rire comme les autres de cette -harangue où il y avait de l'argot pour la canaille et du jargon pour les -gens comme il faut. L'arracheur de dents vit cette belle fille qui -riait, et s'écria tout à coup: - ---Vous avez de jolies dents, la fille qui riez là. Si vous voulez me -vendre vos deux palettes, je vous donne de chaque un napoléon d'or. - ---Qu'est-ce que c'est que ça, mes palettes? demanda Fantine. - ---Les palettes, reprit le professeur dentiste, c'est les dents de -devant, les deux d'en haut. - ---Quelle horreur! s'écria Fantine. - ---Deux napoléons! grommela une vieille édentée qui était là. Qu'en voilà -une qui est heureuse! - -Fantine s'enfuit, et se boucha les oreilles pour ne pas entendre la voix -enrouée de l'homme qui lui criait: Réfléchissez, la belle! deux -napoléons, ça peut servir. Si le coeur vous en dit, venez ce soir à -l'auberge du _Tillac d'argent_, vous m'y trouverez. - -Fantine rentra, elle était furieuse et conta la chose à sa bonne voisine -Marguerite: - ---Comprenez-vous cela? ne voilà-t-il pas un abominable homme? comment -laisse-t-on des gens comme cela aller dans le pays! M'arracher mes deux -dents de devant! mais je serais horrible! Les cheveux repoussent, mais -les dents! Ah! le monstre d'homme! j'aimerais mieux me jeter d'un -cinquième la tête la première sur le pavé! Il m'a dit qu'il serait ce -soir au _Tillac d'argent_. - ---Et qu'est-ce qu'il offrait? demanda Marguerite. - ---Deux napoléons. - ---Cela fait quarante francs. - ---Oui, dit Fantine, cela fait quarante francs. - -Elle resta pensive, et se mit à son ouvrage. Au bout d'un quart d'heure, -elle quitta sa couture et alla relire la lettre des Thénardier sur -l'escalier. - -En rentrant, elle dit à Marguerite qui travaillait près d'elle: - ---Qu'est-ce que c'est donc que cela, une fièvre miliaire? Savez-vous? - ---Oui, répondit la vieille fille, c'est une maladie. - ---Ça a donc besoin de beaucoup de drogues? - ---Oh! des drogues terribles. - ---Où ça vous prend-il? - ---C'est une maladie qu'on a comme ça. - ---Cela attaque donc les enfants? - ---Surtout les enfants. - ---Est-ce qu'on en meurt? - ---Très bien, dit Marguerite. - -Fantine sortit et alla encore une fois relire la lettre sur l'escalier. - -Le soir elle descendit, et on la vit qui se dirigeait du côté de la rue -de Paris où sont les auberges. - -Le lendemain matin, comme Marguerite entrait dans la chambre de Fantine -avant le jour, car elles travaillaient toujours ensemble et de cette -façon n'allumaient qu'une chandelle pour deux, elle trouva Fantine -assise sur son lit, pâle, glacée. Elle ne s'était pas couchée. Son -bonnet était tombé sur ses genoux. La chandelle avait brûlé toute la -nuit et était presque entièrement consumée. - -Marguerite s'arrêta sur le seuil, pétrifiée de cet énorme désordre, et -s'écria: - ---Seigneur! la chandelle qui est toute brûlée! il s'est passé des -événements! - -Puis elle regarda Fantine qui tournait vers elle sa tête sans cheveux. - -Fantine depuis la veille avait vieilli de dix ans. - ---Jésus! fit Marguerite, qu'est-ce que vous avez, Fantine? - ---Je n'ai rien, répondit Fantine. Au contraire. Mon enfant ne mourra pas -de cette affreuse maladie, faute de secours. Je suis contente. - -En parlant ainsi, elle montrait à la vieille fille deux napoléons qui -brillaient sur la table. - ---Ah, Jésus Dieu! dit Marguerite. Mais c'est une fortune! Où avez-vous -eu ces louis d'or? - ---Je les ai eus, répondit Fantine. - -En même temps elle sourit. La chandelle éclairait son visage. C'était un -sourire sanglant. Une salive rougeâtre lui souillait le coin des lèvres, -et elle avait un trou noir dans la bouche. - -Les deux dents étaient arrachées. - -Elle envoya les quarante francs à Montfermeil. - -Du reste c'était une ruse des Thénardier pour avoir de l'argent. Cosette -n'était pas malade. - -Fantine jeta son miroir par la fenêtre. Depuis longtemps elle avait -quitté sa cellule du second pour une mansarde fermée d'un loquet sous le -toit; un de ces galetas dont le plafond fait angle avec le plancher et -vous heurte à chaque instant la tête. Le pauvre ne peut aller au fond de -sa chambre comme au fond de sa destinée qu'en se courbant de plus en -plus. Elle n'avait plus de lit, il lui restait une loque qu'elle -appelait sa couverture, un matelas à terre et une chaise dépaillée. Un -petit rosier qu'elle avait s'était désséché dans un coin, oublié. Dans -l'autre coin, il y avait un pot à beurre à mettre l'eau, qui gelait -l'hiver, et où les différents niveaux de l'eau restaient longtemps -marqués par des cercles de glace. Elle avait perdu la honte, elle perdit -la coquetterie. Dernier signe. Elle sortait avec des bonnets sales. Soit -faute de temps, soit indifférence, elle ne raccommodait plus son linge. -À mesure que les talons s'usaient, elle tirait ses bas dans ses -souliers. Cela se voyait à de certains plis perpendiculaires. Elle -rapiéçait son corset, vieux et usé, avec des morceaux de calicot qui se -déchiraient au moindre mouvement. Les gens auxquels elle devait, lui -faisaient «des scènes», et ne lui laissaient aucun repos. Elle les -trouvait dans la rue, elle les retrouvait dans son escalier. Elle -passait des nuits à pleurer et à songer. Elle avait les yeux très -brillants, et elle sentait une douleur fixe dans l'épaule, vers le haut -de l'omoplate gauche. Elle toussait beaucoup. Elle haïssait profondément -le père Madeleine, et ne se plaignait pas. Elle cousait dix-sept heures -par jour; mais un entrepreneur du travail des prisons, qui faisait -travailler les prisonnières au rabais, fit tout à coup baisser les prix, -ce qui réduisit la journée des ouvrières libres à neuf sous. Dix-sept -heures de travail, et neuf sous par jour! Ses créanciers étaient plus -impitoyables que jamais. Le fripier, qui avait repris presque tous les -meubles, lui disait sans cesse: Quand me payeras-tu, coquine? Que -voulait-on d'elle, bon Dieu! Elle se sentait traquée et il se -développait en elle quelque chose de la bête farouche. Vers le même -temps, le Thénardier lui écrivit que décidément il avait attendu avec -beaucoup trop de bonté, et qu'il lui fallait cent francs, tout de suite; -sinon qu'il mettrait à la porte la petite Cosette, toute convalescente -de sa grande maladie, par le froid, par les chemins, et qu'elle -deviendrait ce qu'elle pourrait, et qu'elle crèverait, si elle voulait. -«Cent francs, songea Fantine! Mais où y a-t-il un état à gagner cent -sous par jour?» - ---Allons! dit-elle, vendons le reste. - -L'infortunée se fit fille publique. - - - - -Chapitre XI - -_Christus nos liberavit_ - - -Qu'est-ce que c'est que cette histoire de Fantine? C'est la société -achetant une esclave. - -À qui? À la misère. - -À la faim, au froid, à l'isolement, à l'abandon, au dénûment. Marché -douloureux. Une âme pour un morceau de pain. La misère offre, la société -accepte. - -La sainte loi de Jésus-Christ gouverne notre civilisation, mais elle ne -la pénètre pas encore. On dit que l'esclavage a disparu de la -civilisation européenne. C'est une erreur. Il existe toujours, mais il -ne pèse plus que sur la femme, et il s'appelle prostitution. - -Il pèse sur la femme, c'est-à-dire sur la grâce, sur la faiblesse, sur -la beauté, sur la maternité. Ceci n'est pas une des moindres hontes de -l'homme. - -Au point de ce douloureux drame où nous sommes arrivés, il ne reste plus -rien à Fantine de ce qu'elle a été autrefois. Elle est devenue marbre en -devenant boue. Qui la touche a froid. Elle passe, elle vous subit et -elle vous ignore; elle est la figure déshonorée et sévère. La vie et -l'ordre social lui ont dit leur dernier mot. Il lui est arrivé tout ce -qui lui arrivera. Elle a tout ressenti, tout supporté, tout éprouvé, -tout souffert, tout perdu, tout pleuré. Elle est résignée de cette -résignation qui ressemble à l'indifférence comme la mort ressemble au -sommeil. Elle n'évite plus rien. Elle ne craint plus rien. Tombe sur -elle toute la nuée et passe sur elle tout l'océan! que lui importe! -c'est une éponge imbibée. - -Elle le croit du moins, mais c'est une erreur de s'imaginer qu'on épuise -le sort et qu'on touche le fond de quoi que ce soit. - -Hélas! qu'est-ce que toutes ces destinées ainsi poussées pêle-mêle? où -vont-elles? pourquoi sont-elles ainsi? - -Celui qui sait cela voit toute l'ombre. - -Il est seul. Il s'appelle Dieu. - - - - -Chapitre XII - -Le désoeuvrement de M. Bamatabois - - -Il y a dans toutes les petites villes, et il y avait à Montreuil-sur-mer -en particulier, une classe de jeunes gens qui grignotent quinze cents -livres de rente en province du même air dont leurs pareils dévorent à -Paris deux cent mille francs par an. Ce sont des êtres de la grande -espèce neutre; hongres, parasites, nuls, qui ont un peu de terre, un peu -de sottise et un peu d'esprit, qui seraient des rustres dans un salon et -se croient des gentilshommes au cabaret, qui disent: mes prés, mes bois, -mes paysans, sifflent les actrices du théâtre pour prouver qu'ils sont -gens de goût, querellent les officiers de la garnison pour montrer -qu'ils sont gens de guerre, chassent, fument, bâillent, boivent, sentent -le tabac, jouent au billard, regardent les voyageurs descendre de -diligence, vivent au café, dînent à l'auberge, ont un chien qui mange -les os sous la table et une maîtresse qui pose les plats dessus, -tiennent à un sou, exagèrent les modes, admirent la tragédie, méprisent -les femmes, usent leurs vieilles bottes, copient Londres à travers Paris -et Paris à travers Pont-à-Mousson, vieillissent hébétés, ne travaillent -pas, ne servent à rien et ne nuisent pas à grand'chose. - -M. Félix Tholomyès, resté dans sa province et n'ayant jamais vu Paris, -serait un de ces hommes-là. - -S'ils étaient plus riches, on dirait: ce sont des élégants; s'ils -étaient plus pauvres, on dirait: ce sont des fainéants. Ce sont tout -simplement des désoeuvrés. Parmi ces désoeuvrés, il y a des ennuyeux, -des ennuyés, des rêvasseurs, et quelques drôles. - -Dans ce temps-là, un élégant se composait d'un grand col, d'une grande -cravate, d'une montre à breloques, de trois gilets superposés de -couleurs différentes, le bleu et le rouge en dedans, d'un habit couleur -olive à taille courte, à queue de morue, à double rangée de boutons -d'argent serrés les uns contre les autres et montant jusque sur -l'épaule, et d'un pantalon olive plus clair, orné sur les deux coutures -d'un nombre de côtes indéterminé, mais toujours impair, variant de une à -onze, limite qui n'était jamais franchie. Ajoutez à cela des -souliers-bottes avec de petits fers au talon, un chapeau à haute forme -et à bords étroits, des cheveux en touffe, une énorme canne, et une -conversation rehaussée des calembours de Potier. Sur le tout des éperons -et des moustaches. À cette époque, des moustaches voulaient dire -bourgeois et des éperons voulaient dire piéton. - -L'élégant de province portait les éperons plus longs et les moustaches -plus farouches. C'était le temps de la lutte des républiques de -l'Amérique méridionale contre le roi d'Espagne, de Bolivar contre -Morillo. Les chapeaux à petits bords étaient royalistes et se nommaient -des morillos; les libéraux portaient des chapeaux à larges bords qui -s'appelaient des bolivars. - -Huit ou dix mois donc après ce qui a été raconté dans les pages -précédentes, vers les premiers jours de janvier 1823, un soir qu'il -avait neigé, un de ces élégants, un de ces désoeuvrés, un "bien -pensant", car il avait un morillo, de plus chaudement enveloppé d'un de -ces grands manteaux qui complétaient dans les temps froids le costume à -la mode, se divertissait à harceler une créature qui rôdait en robe de -bal et toute décolletée avec des fleurs sur la tête devant la vitre du -café des officiers. Cet élégant fumait, car c'était décidément la mode. - -Chaque fois que cette femme passait devant lui, il lui jetait, avec une -bouffée de la fumée de son cigare, quelque apostrophe qu'il croyait -spirituelle et gaie, comme:--Que tu es laide!--Veux-tu te cacher!--Tu -n'as pas de dents! etc., etc.--Ce monsieur s'appelait monsieur -Bamatabois. La femme, triste spectre paré qui allait et venait sur la -neige, ne lui répondait pas, ne le regardait même pas, et n'en -accomplissait pas moins en silence et avec une régularité sombre sa -promenade qui la ramenait de cinq minutes en cinq minutes sous le -sarcasme, comme le soldat condamné qui revient sous les verges. Ce peu -d'effet piqua sans doute l'oisif qui, profitant d'un moment où elle se -retournait, s'avança derrière elle à pas de loup et en étouffant son -rire, se baissa, prit sur le pavé une poignée de neige et la lui plongea -brusquement dans le dos entre ses deux épaules nues. La fille poussa un -rugissement, se tourna, bondit comme une panthère, et se rua sur -l'homme, lui enfonçant ses ongles dans le visage, avec les plus -effroyables paroles qui puissent tomber du corps de garde dans le -ruisseau. Ces injures, vomies d'une voix enrouée par l'eau-de-vie, -sortaient hideusement d'une bouche à laquelle manquaient en effet les -deux dents de devant. C'était la Fantine. - -Au bruit que cela fit, les officiers sortirent en foule du café, les -passants s'amassèrent, et il se forma un grand cercle riant, huant et -applaudissant, autour de ce tourbillon composé de deux êtres où l'on -avait peine à reconnaître un homme et une femme, l'homme se débattant, -son chapeau à terre, la femme frappant des pieds et des poings, -décoiffée, hurlant, sans dents et sans cheveux, livide de colère, -horrible. Tout à coup un homme de haute taille sortit vivement de la -foule, saisit la femme à son corsage de satin couvert de boue, et lui -dit: Suis-moi! - -La femme leva la tête; sa voix furieuse s'éteignit subitement. Ses yeux -étaient vitreux, de livide elle était devenue pâle, et elle tremblait -d'un tremblement de terreur. Elle avait reconnu Javert. - -L'élégant avait profité de l'incident pour s'esquiver. - - - - -Chapitre XIII - -Solution de quelques questions de police municipale - - -Javert écarta les assistants, rompit le cercle et se mit à marcher à grands -pas vers le bureau de police qui est à l'extrémité de la place, traînant -après lui la misérable. Elle se laissait faire machinalement. Ni lui ni -elle ne disaient un mot. La nuée des spectateurs, au paroxysme de la -joie, suivait avec des quolibets. La suprême misère, occasion -d'obscénités. Arrivé au bureau de police qui était une salle basse -chauffée par un poêle et gardée par un poste, avec une porte vitrée et -grillée sur la rue, Javert ouvrit la porte, entra avec Fantine, et -referma la porte derrière lui, au grand désappointement des curieux qui -se haussèrent sur la pointe du pied et allongèrent le cou devant la -vitre trouble du corps de garde, cherchant à voir. La curiosité est une -gourmandise. Voir, c'est dévorer. - -En entrant, la Fantine alla tomber dans un coin, immobile et muette, -accroupie comme une chienne qui a peur. - -Le sergent du poste apporta une chandelle allumée sur une table. Javert -s'assit, tira de sa poche une feuille de papier timbré et se mit à -écrire. - -Ces classes de femmes sont entièrement remises par nos lois à la -discrétion de la police. Elle en fait ce qu'elle veut, les punit comme -bon lui semble, et confisque à son gré ces deux tristes choses qu'elles -appellent leur industrie et leur liberté. Javert était impassible; son -visage sérieux ne trahissait aucune émotion. Pourtant il était gravement -et profondément préoccupé. C'était un de ces moments où il exerçait sans -contrôle, mais avec tous les scrupules d'une conscience sévère, son -redoutable pouvoir discrétionnaire. En cet instant, il le sentait, son -escabeau d'agent de police était un tribunal. Il jugeait. Il jugeait, et -il condamnait. Il appelait tout ce qu'il pouvait avoir d'idées dans -l'esprit autour de la grande chose qu'il faisait. Plus il examinait le -fait de cette fille, plus il se sentait révolté. Il était évident qu'il -venait de voir commettre un crime. Il venait de voir, là dans la rue, la -société, représentée par un propriétaire-électeur, insultée et attaquée -par une créature en dehors de tout. Une prostituée avait attenté à un -bourgeois. Il avait vu cela, lui Javert. Il écrivait en silence. - -Quand il eut fini, il signa, plia le papier et dit au sergent du poste, -en le lui remettant: - ---Prenez trois hommes, et menez cette fille au bloc. - -Puis se tournant vers la Fantine: - ---Tu en as pour six mois. - -La malheureuse tressaillit. - ---Six mois! six mois de prison! Six mois à gagner sept sous par jour! -Mais que deviendra Cosette? ma fille! ma fille! Mais je dois encore plus -de cent francs aux Thénardier, monsieur l'inspecteur, savez-vous cela? - -Elle se traîna sur la dalle mouillée par les bottes boueuses de tous ces -hommes, sans se lever, joignant les mains, faisant de grands pas avec -ses genoux. - ---Monsieur Javert, dit-elle, je vous demande grâce. Je vous assure que -je n'ai pas eu tort. Si vous aviez vu le commencement, vous auriez vu! -je vous jure le bon Dieu que je n'ai pas eu tort. C'est ce monsieur le -bourgeois que je ne connais pas qui m'a mis de la neige dans le dos. -Est-ce qu'on a le droit de nous mettre de la neige dans le dos quand -nous passons comme cela tranquillement sans faire de mal à personne? -Cela m'a saisie. Je suis un peu malade, voyez-vous! Et puis il y avait -déjà un peu de temps qu'il me disait des raisons. Tu es laide! tu n'as -pas de dents! Je le sais bien que je n'ai plus mes dents. Je ne faisais -rien, moi; je disais: c'est un monsieur qui s'amuse. J'étais honnête -avec lui, je ne lui parlais pas. C'est à cet instant-là qu'il m'a mis de -la neige. Monsieur Javert, mon bon monsieur l'inspecteur! est-ce qu'il -n'y a personne là qui ait vu pour vous dire que c'est bien vrai? J'ai -peut-être eu tort de me fâcher. Vous savez, dans le premier moment, on -n'est pas maître. On a des vivacités. Et puis, quelque chose de si froid -qu'on vous met dans le dos à l'heure que vous ne vous y attendez pas! -J'ai eu tort d'abîmer le chapeau de ce monsieur. Pourquoi s'est-il en -allé? Je lui demanderais pardon. Oh! mon Dieu, cela me serait bien égal -de lui demander pardon. Faites-moi grâce pour aujourd'hui cette fois, -monsieur Javert. Tenez, vous ne savez pas ça, dans les prisons on ne -gagne que sept sous, ce n'est pas la faute du gouvernement, mais on -gagne sept sous, et figurez-vous que j'ai cent francs à payer, ou -autrement on me renverra ma petite. Ô mon Dieu! je ne peux pas l'avoir -avec moi. C'est si vilain ce que je fais! Ô ma Cosette, ô mon petit ange -de la bonne sainte Vierge, qu'est-ce qu'elle deviendra, pauvre loup! Je -vais vous dire, c'est les Thénardier, des aubergistes, des paysans, ça -n'a pas de raisonnement. Il leur faut de l'argent. Ne me mettez pas en -prison! Voyez-vous, c'est une petite qu'on mettrait à même sur la grande -route, va comme tu pourras, en plein coeur d'hiver, il faut avoir pitié -de cette chose-là, mon bon monsieur Javert. Si c'était plus grand, ça -gagnerait sa vie, mais ça ne peut pas, à ces âges-là. Je ne suis pas une -mauvaise femme au fond. Ce n'est pas la lâcheté et la gourmandise qui -ont fait de moi ça. J'ai bu de l'eau-de-vie, c'est par misère. Je ne -l'aime pas, mais cela étourdit. Quand j'étais plus heureuse, on n'aurait -eu qu'à regarder dans mes armoires, on aurait bien vu que je n'étais pas -une femme coquette qui a du désordre. J'avais du linge, beaucoup de -linge. Ayez pitié de moi, monsieur Javert! - -Elle parlait ainsi, brisée en deux, secouée par les sanglots, aveuglée -par les larmes, la gorge nue, se tordant les mains, toussant d'une toux -sèche et courte, balbutiant tout doucement avec la voix de l'agonie. La -grande douleur est un rayon divin et terrible qui transfigure les -misérables. À ce moment-là, la Fantine était redevenue belle. À de -certains instants, elle s'arrêtait et baisait tendrement le bas de la -redingote du mouchard. Elle eût attendri un coeur de granit, mais on -n'attendrit pas un coeur de bois. - ---Allons! dit Javert, je t'ai écoutée. As-tu bien tout dit? Marche à -présent! Tu as tes six mois; _le Père éternel en personne n'y pourrait -plus rien_. - -À cette solennelle parole, Le Père éternel en personne n'y pourrait plus -rien, elle comprit que l'arrêt était prononcé. Elle s'affaissa sur -elle-même en murmurant: - ---Grâce! - -Javert tourna le dos. - -Les soldats la saisirent par les bras. - -Depuis quelques minutes, un homme était entré sans qu'on eût pris garde -à lui. Il avait refermé la porte, s'y était adossé, et avait entendu les -prières désespérées de la Fantine. Au moment où les soldats mirent la -main sur la malheureuse, qui ne voulait pas se lever, il fit un pas, -sortit de l'ombre, et dit: - ---Un instant, s'il vous plaît! - -Javert leva les yeux et reconnut M. Madeleine. Il ôta son chapeau, et -saluant avec une sorte de gaucherie fâchée: - ---Pardon, monsieur le maire.... - -Ce mot, monsieur le maire, fit sur la Fantine un effet étrange. Elle se -dressa debout tout d'une pièce comme un spectre qui sort de terre, -repoussa les soldats des deux bras, marcha droit à M. Madeleine avant -qu'on eût pu la retenir, et le regardant fixement, l'air égaré, elle -cria: - ---Ah! c'est donc toi qui es monsieur le maire! - -Puis elle éclata de rire et lui cracha au visage. - -M. Madeleine s'essuya le visage, et dit: - ---Inspecteur Javert, mettez cette femme en liberté. - -Javert se sentit au moment de devenir fou. Il éprouvait en cet instant, -coup sur coup, et presque mêlées ensemble, les plus violentes émotions -qu'il eût ressenties de sa vie. Voir une fille publique cracher au -visage d'un maire, cela était une chose si monstrueuse que, dans ses -suppositions les plus effroyables, il eût regardé comme un sacrilège de -le croire possible. D'un autre côté, dans le fond de sa pensée, il -faisait confusément un rapprochement hideux entre ce qu'était cette -femme et ce que pouvait être ce maire, et alors il entrevoyait avec -horreur je ne sais quoi de tout simple dans ce prodigieux attentat. Mais -quand il vit ce maire, ce magistrat, s'essuyer tranquillement le visage -et dire: _mettez cette femme en liberté_, il eut comme un éblouissement -de stupeur; la pensée et la parole lui manquèrent également; la somme de -l'étonnement possible était dépassée pour lui. Il resta muet. - -Ce mot n'avait pas porté un coup moins étrange à la Fantine. Elle leva -son bras nu et se cramponna à la clef du poêle comme une personne qui -chancelle. Cependant elle regardait tout autour d'elle et elle se mit à -parler à voix basse, comme si elle se parlait à elle-même. - ---En liberté! qu'on me laisse aller! que je n'aille pas en prison six -mois! Qui est-ce qui a dit cela? Il n'est pas possible qu'on ait dit -cela. J'ai mal entendu. Ça ne peut pas être ce monstre de maire! Est-ce -que c'est vous, mon bon monsieur Javert, qui avez dit qu'on me mette en -liberté? Oh! voyez-vous! je vais vous dire et vous me laisserez aller. -Ce monstre de maire, ce vieux gredin de maire, c'est lui qui est cause -de tout. Figurez-vous, monsieur Javert, qu'il m'a chassée! à cause d'un -tas de gueuses qui tiennent des propos dans l'atelier. Si ce n'est pas -là une horreur! renvoyer une pauvre fille qui fait honnêtement son -ouvrage! Alors je n'ai plus gagné assez, et tout le malheur est venu. -D'abord il y a une amélioration que ces messieurs de la police devraient -bien faire, ce serait d'empêcher les entrepreneurs des prisons de faire -du tort aux pauvres gens. Je vais vous expliquer cela, voyez-vous. Vous -gagnez douze sous dans les chemises, cela tombe à neuf sous, il n'y a -plus moyen de vivre. Il faut donc devenir ce qu'on peut. Moi, j'avais ma -petite Cosette, j'ai bien été forcée de devenir une mauvaise femme. Vous -comprenez à présent, que c'est ce gueux de maire qui a tout fait le mal. -Après cela, j'ai piétiné le chapeau de ce monsieur bourgeois devant le -café des officiers. Mais lui, il m'avait perdu toute ma robe avec sa -neige. Nous autres, nous n'avons qu'une robe de soie, pour le soir. -Voyez-vous, je n'ai jamais fait de mal exprès, vrai, monsieur Javert, et -je vois partout des femmes bien plus méchantes que moi qui sont bien -plus heureuses. Ô monsieur Javert, c'est vous qui avez dit qu'on me -mette dehors, n'est-ce pas? Prenez des informations, parlez à mon -propriétaire, maintenant je paye mon terme, on vous dira bien que je -suis honnête. Ah! mon Dieu, je vous demande pardon, j'ai touché, sans -faire attention, à la clef du poêle, et cela fait fumer. - -M. Madeleine l'écoutait avec une attention profonde. Pendant qu'elle -parlait, il avait fouillé dans son gilet, en avait tiré sa bourse et -l'avait ouverte. Elle était vide. Il l'avait remise dans sa poche. Il -dit à la Fantine: - ---Combien avez-vous dit que vous deviez? - -La Fantine, qui ne regardait que Javert, se retourna de son côté: - ---Est-ce que je te parle à toi! - -Puis s'adressant aux soldats: - ---Dites donc, vous autres, avez-vous vu comme je te vous lui ai craché à -la figure? Ah! vieux scélérat de maire, tu viens ici pour me faire peur, -mais je n'ai pas peur de toi. J'ai peur de monsieur Javert. J'ai peur de -mon bon monsieur Javert! - -En parlant ainsi elle se retourna vers l'inspecteur: - ---Avec ça, voyez-vous, monsieur l'inspecteur, il faut être juste. Je -comprends que vous êtes juste, monsieur l'inspecteur. Au fait, c'est -tout simple, un homme qui joue à mettre un peu de neige dans le dos -d'une femme, ça les faisait rire, les officiers, il faut bien qu'on se -divertisse à quelque chose, nous autres nous sommes là pour qu'on -s'amuse, quoi! Et puis, vous, vous venez, vous êtes bien forcé de mettre -l'ordre, vous emmenez la femme qui a tort, mais en y réfléchissant, -comme vous êtes bon, vous dites qu'on me mette en liberté, c'est pour la -petite, parce que six mois en prison, cela m'empêcherait de nourrir mon -enfant. Seulement n'y reviens plus, coquine! Oh! je n'y reviendrai plus, -monsieur Javert! on me fera tout ce qu'on voudra maintenant, je ne -bougerai plus. Seulement, aujourd'hui, voyez-vous, j'ai crié parce que -cela m'a fait mal, je ne m'attendais pas du tout à cette neige de ce -monsieur, et puis, je vous ai dit, je ne me porte pas très bien, je -tousse, j'ai là dans l'estomac comme une boule qui me brûle, que le -médecin me dit: soignez-vous. Tenez, tâtez, donnez votre main, n'ayez -pas peur, c'est ici. - -Elle ne pleurait plus, sa voix était caressante, elle appuyait sur sa -gorge blanche et délicate la grosse main rude de Javert, et elle le -regardait en souriant. - -Tout à coup elle rajusta vivement le désordre de ses vêtements, fit -retomber les plis de sa robe qui en se traînant s'était relevée presque -à la hauteur du genou, et marcha vers la porte en disant à demi-voix aux -soldats avec un signe de tête amical: - ---Les enfants, monsieur l'inspecteur a dit qu'on me lâche, je m'en vas. - -Elle mit la main sur le loquet. Un pas de plus, elle était dans la rue. - -Javert jusqu'à cet instant était resté debout, immobile, l'oeil fixé à -terre, posé de travers au milieu de cette scène comme une statue -dérangée qui attend qu'on la mette quelque part. - -Le bruit que fit le loquet le réveilla. Il releva la tête avec une -expression d'autorité souveraine, expression toujours d'autant plus -effrayante que le pouvoir se trouve placé plus bas, féroce chez la bête -fauve, atroce chez l'homme de rien. - ---Sergent, cria-t-il, vous ne voyez pas que cette drôlesse s'en va! Qui -est-ce qui vous a dit de la laisser aller? - ---Moi, dit Madeleine. - -La Fantine à la voix de Javert avait tremblé et lâché le loquet comme un -voleur pris lâche l'objet volé. À la voix de Madeleine, elle se -retourna, et à partir de ce moment, sans qu'elle prononçât un mot, sans -qu'elle osât même laisser sortir son souffle librement, son regard alla -tour à tour de Madeleine à Javert et de Javert à Madeleine, selon que -c'était l'un ou l'autre qui parlait. - -Il était évident qu'il fallait que Javert eût été, comme on dit, «jeté -hors des gonds» pour qu'il se fût permis d'apostropher le sergent comme -il l'avait fait, après l'invitation du maire de mettre Fantine en -liberté. En était-il venu à oublier la présence de monsieur le maire? -Avait-il fini par se déclarer à lui-même qu'il était impossible qu'une -«autorité» eût donné un pareil ordre, et que bien certainement monsieur -le maire avait dû dire sans le vouloir une chose pour une autre? Ou -bien, devant les énormités dont il était témoin depuis deux heures, se -disait-il qu'il fallait revenir aux suprêmes résolutions, qu'il était -nécessaire que le petit se fit grand, que le mouchard se transformât en -magistrat, que l'homme de police devînt homme de justice, et qu'en cette -extrémité prodigieuse l'ordre, la loi, la morale, le gouvernement, la -société tout entière, se personnifiaient en lui Javert? - -Quoi qu'il en soit, quand M. Madeleine eut dit ce moi qu'on vient -d'entendre, on vit l'inspecteur de police Javert se tourner vers -monsieur le maire, pâle, froid, les lèvres bleues, le regard désespéré, -tout le corps agité d'un tremblement imperceptible, et, chose inouïe, -lui dire, l'oeil baissé, mais la voix ferme: - ---Monsieur le maire, cela ne se peut pas. - ---Comment? dit M. Madeleine. - ---Cette malheureuse a insulté un bourgeois. - ---Inspecteur Javert, repartit M. Madeleine avec un accent conciliant et -calme, écoutez. Vous êtes un honnête homme, et je ne fais nulle -difficulté de m'expliquer avec vous. Voici le vrai. Je passais sur la -place comme vous emmeniez cette femme, il y avait encore des groupes, je -me suis informé, j'ai tout su, c'est le bourgeois qui a eu tort et qui, -en bonne police, eût dû être arrêté. - -Javert reprit: - ---Cette misérable vient d'insulter monsieur le maire. - ---Ceci me regarde, dit M. Madeleine. Mon injure est à moi peut-être. -J'en puis faire ce que je veux. - ---Je demande pardon à monsieur le maire. Son injure n'est pas à lui, -elle est à la justice. - ---Inspecteur Javert, répliqua M. Madeleine, la première justice, c'est -la conscience. J'ai entendu cette femme. Je sais ce que je fais. - ---Et moi, monsieur le maire, je ne sais pas ce que je vois. - ---Alors contentez-vous d'obéir. - ---J'obéis à mon devoir. Mon devoir veut que cette femme fasse six mois -de prison. - -M. Madeleine répondit avec douceur: - ---Écoutez bien ceci. Elle n'en fera pas un jour. - -À cette parole décisive, Javert osa regarder le maire fixement, et lui -dit, mais avec un son de voix toujours profondément respectueux: - ---Je suis au désespoir de résister à monsieur le maire, c'est la -première fois de ma vie, mais il daignera me permettre de lui faire -observer que je suis dans la limite de mes attributions. Je reste, -puisque monsieur le maire le veut, dans le fait du bourgeois. J'étais -là. C'est cette fille qui s'est jetée sur monsieur Bamatabois, qui est -électeur et propriétaire de cette belle maison à balcon qui fait le coin -de l'esplanade, à trois étages et toute en pierre de taille. Enfin, il y -a des choses dans ce monde! Quoi qu'il en soit, monsieur le maire, cela, -c'est un fait de police de la rue qui me regarde, et je retiens la femme -Fantine. - -Alors M. Madeleine croisa les bras et dit avec une voix sévère que -personne dans la ville n'avait encore entendue: - ---Le fait dont vous parlez est un fait de police municipale. Aux termes -des articles neuf, onze, quinze et soixante-six du code d'instruction -criminelle, j'en suis juge. J'ordonne que cette femme soit mise en -liberté. - -Javert voulut tenter un dernier effort. - ---Mais, monsieur le maire.... - ---Je vous rappelle, à vous, l'article quatre-vingt-un de la loi du 13 -décembre 1799 sur la détention arbitraire. - ---Monsieur le maire, permettez.... - ---Plus un mot. - ---Pourtant.... - ---Sortez, dit M. Madeleine. - -Javert reçut le coup, debout, de face, et en pleine poitrine comme un -soldat russe. Il salua jusqu'à terre monsieur le maire, et sortit. - -Fantine se rangea de la porte et le regarda avec stupeur passer devant -elle. - -Cependant elle aussi était en proie à un bouleversement étrange. Elle -venait de se voir en quelque sorte disputée par deux puissances -opposées. Elle avait vu lutter devant ses yeux deux hommes tenant dans -leurs mains sa liberté, sa vie, son âme, son enfant; l'un de ces hommes -la tirait du côté de l'ombre, l'autre la ramenait vers la lumière. Dans -cette lutte, entrevue à travers les grossissements de l'épouvante, ces -deux hommes lui étaient apparus comme deux géants; l'un parlait comme -son démon, l'autre parlait comme son bon ange. L'ange avait vaincu le -démon, et, chose qui la faisait frissonner de la tête aux pieds, cet -ange, ce libérateur, c'était précisément l'homme qu'elle abhorrait, ce -maire qu'elle avait si longtemps considéré comme l'auteur de tous ses -maux, ce Madeleine! et au moment même où elle venait de l'insulter d'une -façon hideuse, il la sauvait! S'était-elle donc trompée? Devait-elle -donc changer toute son âme?... Elle ne savait, elle tremblait. Elle -écoutait éperdue, elle regardait effarée, et à chaque parole que disait -M. Madeleine, elle sentait fondre et s'écrouler en elle les affreuses -ténèbres de la haine et naître dans son coeur je ne sais quoi de -réchauffant et d'ineffable qui était de la joie, de la confiance et de -l'amour. - -Quand Javert fut sorti, M. Madeleine se tourna vers elle, et lui dit -avec une voix lente, ayant peine à parler comme un homme sérieux qui ne -veut pas pleurer: - ---Je vous ai entendue. Je ne savais rien de ce que vous avez dit. Je -crois que c'est vrai, et je sens que c'est vrai. J'ignorais même que -vous eussiez quitté mes ateliers. Pourquoi ne vous êtes-vous pas -adressée à moi? Mais voici: je payerai vos dettes, je ferai venir votre -enfant, ou vous irez la rejoindre. Vous vivrez ici, à Paris, où vous -voudrez. Je me charge de votre enfant et de vous. Vous ne travaillerez -plus, si vous voulez. Je vous donnerai tout l'argent qu'il vous faudra. -Vous redeviendrez honnête en redevenant heureuse. Et même, écoutez, je -vous le déclare dès à présent, si tout est comme vous le dites, et je -n'en doute pas, vous n'avez jamais cessé d'être vertueuse et sainte -devant Dieu. Oh! pauvre femme! - -C'en était plus que la pauvre Fantine n'en pouvait supporter. Avoir -Cosette! sortir de cette vie infâme! vivre libre, riche, heureuse, -honnête, avec Cosette! voir brusquement s'épanouir au milieu de sa -misère toutes ces réalités du paradis! Elle regarda comme hébétée cet -homme qui lui parlait, et ne put que jeter deux ou trois sanglots: oh! -oh! oh! Ses jarrets plièrent, elle se mit à genoux devant M. Madeleine, -et, avant qu'il eût pu l'en empêcher, il sentit qu'elle lui prenait la -main et que ses lèvres s'y posaient. - -Puis elle s'évanouit. - - - - -Livre sixième--Javert - - - - -Chapitre I - -Commencement du repos - - -M. Madeleine fit transporter la Fantine à cette infirmerie qu'il avait -dans sa propre maison. Il la confia aux soeurs qui la mirent au lit. Une -fièvre ardente était survenue. Elle passa une partie de la nuit à -délirer et à parler haut. Cependant elle finit par s'endormir. - -Le lendemain vers midi Fantine se réveilla, elle entendit une -respiration tout près de son lit, elle écarta son rideau et vit M. -Madeleine debout qui regardait quelque chose au-dessus de sa tête. Ce -regard était plein de pitié et d'angoisse et suppliait. Elle en suivit -la direction et vit qu'il s'adressait à un crucifix cloué au mur. - -M. Madeleine était désormais transfiguré aux yeux de Fantine. Il lui -paraissait enveloppé de lumière. Il était absorbé dans une sorte de -prière. Elle le considéra longtemps sans oser l'interrompre. Enfin elle -lui dit timidement: - ---Que faites-vous donc là? - -M. Madeleine était à cette place depuis une heure. Il attendait que -Fantine se réveillât. Il lui prit la main, lui tâta le pouls, et -répondit: - ---Comment êtes-vous? - ---Bien, j'ai dormi, dit-elle, je crois que je vais mieux. Ce ne sera -rien. - -Lui reprit, répondant à la question qu'elle lui avait adressée d'abord, -comme s'il ne faisait que de l'entendre: - ---Je priais le martyr qui est là-haut. - -Et il ajouta dans sa pensée: «Pour la martyre qui est ici-bas.» - -M. Madeleine avait passé la nuit et la matinée à s'informer. Il savait -tout maintenant. Il connaissait dans tous ses poignants détails -l'histoire de Fantine. Il continua: - ---Vous avez bien souffert, pauvre mère. Oh! ne vous plaignez pas, vous -avez à présent la dot des élus. C'est de cette façon que les hommes font -des anges. Ce n'est point leur faute; ils ne savent pas s'y prendre -autrement. Voyez-vous, cet enfer dont vous sortez est la première forme -du ciel. Il fallait commencer par là. - -Il soupira profondément. Elle cependant lui souriait avec ce sublime -sourire auquel il manquait deux dents. - -Javert dans cette même nuit avait écrit une lettre. Il remit lui-même -cette lettre le lendemain matin au bureau de poste de Montreuil-sur-mer. -Elle était pour Paris, et la suscription portait: À _monsieur -Chabouillet, secrétaire de monsieur le préfet de police_. Comme -l'affaire du corps de garde s'était ébruitée, la directrice du bureau de -poste et quelques autres personnes qui virent la lettre avant le départ -et qui reconnurent l'écriture de Javert sur l'adresse, pensèrent que -c'était sa démission qu'il envoyait. - -M. Madeleine se hâta d'écrire aux Thénardier. Fantine leur devait cent -vingt francs. Il leur envoya trois cents francs en leur disant de se -payer sur cette somme, et d'amener tout de suite l'enfant à -Montreuil-sur-mer où sa mère malade la réclamait. - -Ceci éblouit le Thénardier. - ---Diable! dit-il à sa femme, ne lâchons pas l'enfant. Voilà que cette -mauviette va devenir une vache à lait. Je devine. Quelque jocrisse se -sera amouraché de la mère. - -Il riposta par un mémoire de cinq cents et quelques francs fort bien -fait. Dans ce mémoire figuraient pour plus de trois cents francs deux -notes incontestables, l'une d'un médecin, l'autre d'un apothicaire, -lesquels avaient soigné et médicamenté dans deux longues maladies -Éponine et Azelma. Cosette, nous l'avons dit, n'avait pas été malade. Ce -fut l'affaire d'une toute petite substitution de noms. Thénardier mit au -bas du mémoire: _reçu à compte trois cents francs_. - -M. Madeleine envoya tout de suite trois cents autres francs et écrivit: -Dépêchez-vous d'amener Cosette. - ---Christi! dit le Thénardier, ne lâchons pas l'enfant. - -Cependant Fantine ne se rétablissait point. Elle était toujours à -l'infirmerie. Les soeurs n'avaient d'abord reçu et soigné «cette fille» -qu'avec répugnance. Qui a vu les bas-reliefs de Reims se souvient du -gonflement de la lèvre inférieure des vierges sages regardant les -vierges folles. Cet antique mépris des vestales pour les ambulaïes est -un des plus profonds instincts de la dignité féminine; les soeurs -l'avaient éprouvé, avec le redoublement qu'ajoute la religion. Mais, en -peu de jours, Fantine les avait désarmées. Elle avait toutes sortes de -paroles humbles et douces, et la mère qui était en elle attendrissait. -Un jour les soeurs l'entendirent qui disait à travers la fièvre: - ---J'ai été une pécheresse, mais quand j'aurai mon enfant près de moi, -cela voudra dire que Dieu m'a pardonné. Pendant que j'étais dans le mal, -je n'aurais pas voulu avoir ma Cosette avec moi, je n'aurais pas pu -supporter ses yeux étonnés et tristes. C'était pour elle pourtant que je -faisais le mal, et c'est ce qui fait que Dieu me pardonne. Je sentirai -la bénédiction du bon Dieu quand Cosette sera ici. Je la regarderai, -cela me fera du bien de voir cette innocente. Elle ne sait rien du tout. -C'est un ange, voyez-vous, mes soeurs. À cet âge-là, les ailes, ça n'est -pas encore tombé. - -M. Madeleine l'allait voir deux fois par jour, et chaque fois elle lui -demandait: - ---Verrai-je bientôt ma Cosette? - -Il lui répondait: - ---Peut-être demain matin. D'un moment à l'autre elle arrivera, je -l'attends. - -Et le visage pâle de la mère rayonnait. - ---Oh! disait-elle, comme je vais être heureuse! - -Nous venons de dire qu'elle ne se rétablissait pas. Au contraire, son -état semblait s'aggraver de semaine en semaine. Cette poignée de neige -appliquée à nu sur la peau entre les deux omoplates avait déterminé une -suppression subite de transpiration à la suite de laquelle la maladie -qu'elle couvait depuis plusieurs années finit par se déclarer -violemment. On commençait alors à suivre pour l'étude et le traitement -des maladies de poitrine les belles indications de Laennec. Le médecin -ausculta Fantine et hocha la tête. - -M. Madeleine dit au médecin: - ---Eh bien? - ---N'a-t-elle pas un enfant qu'elle désire voir? dit le médecin. - ---Oui. - ---Eh bien, hâtez-vous de le faire venir. - -M. Madeleine eut un tressaillement. - -Fantine lui demanda: - ---Qu'a dit le médecin? - -M. Madeleine s'efforça de sourire. - ---Il a dit de faire venir bien vite votre enfant. Que cela vous rendra -la santé. - ---Oh! reprit-elle, il a raison! Mais qu'est-ce qu'ils ont donc ces -Thénardier à me garder ma Cosette! Oh! elle va venir. Voici enfin que je -vois le bonheur tout près de moi! - -Le Thénardier cependant ne «lâchait pas l'enfant» et donnait cent -mauvaises raisons. Cosette était un peu souffrante pour se mettre en -route l'hiver. Et puis il y avait un reste de petites dettes criardes -dans le pays dont il rassemblait les factures, etc., etc. - ---J'enverrai quelqu'un chercher Cosette, dit le père Madeleine. S'il le -faut, j'irai moi-même. - -Il écrivit sous la dictée de Fantine cette lettre qu'il lui fit signer: - -«Monsieur Thénardier, - -«Vous remettrez Cosette à la personne. - -«On vous payera toutes les petites choses. - -«J'ai l'honneur de vous saluer avec considération. - -«Fantine.» - -Sur ces entrefaites, il survint un grave incident. Nous avons beau -tailler de notre mieux le bloc mystérieux dont notre vie est faite, la -veine noire de la destinée y reparaît toujours. - - - - -Chapitre II - -Comment Jean peut devenir Champ - - -Un matin, M. Madeleine était dans son cabinet, occupé à régler d'avance -quelques affaires pressantes de la mairie pour le cas où il se -déciderait à ce voyage de Montfermeil, lorsqu'on vint lui dire que -l'inspecteur de police Javert demandait à lui parler. En entendant -prononcer ce nom, M. Madeleine ne put se défendre d'une impression -désagréable. Depuis l'aventure du bureau de police, Javert l'avait plus -que jamais évité, et M. Madeleine ne l'avait point revu. - ---Faites entrer, dit-il. - -Javert entra. - -M. Madeleine était resté assis près de la cheminée, une plume à la main, -l'oeil sur un dossier qu'il feuilletait et qu'il annotait, et qui -contenait des procès-verbaux de contraventions à la police de la voirie. -Il ne se dérangea point pour Javert. Il ne pouvait s'empêcher de songer -à la pauvre Fantine, et il lui convenait d'être glacial. - -Javert salua respectueusement M. le maire qui lui tournait le dos. M. le -maire ne le regarda pas et continua d'annoter son dossier. - -Javert fit deux ou trois pas dans le cabinet, et s'arrêta sans rompre le -silence. Un physionomiste qui eût été familier avec la nature de Javert, -qui eût étudié depuis longtemps ce sauvage au service de la -civilisation, ce composé bizarre du Romain, du Spartiate, du moine et du -caporal, cet espion incapable d'un mensonge, ce mouchard vierge, un -physionomiste qui eût su sa secrète et ancienne aversion pour M. -Madeleine, son conflit avec le maire au sujet de la Fantine, et qui eût -considéré Javert en ce moment, se fût dit: que s'est-il passé? Il était -évident, pour qui eût connu cette conscience droite, claire, sincère, -probe, austère et féroce, que Javert sortait de quelque grand événement -intérieur. Javert n'avait rien dans l'âme qu'il ne l'eût aussi sur le -visage. Il était, comme les gens violents, sujet aux revirements -brusques. Jamais sa physionomie n'avait été plus étrange et plus -inattendue. En entrant, il s'était incliné devant M. Madeleine avec un -regard où il n'y avait ni rancune, ni colère, ni défiance, il s'était -arrêté à quelques pas derrière le fauteuil du maire; et maintenant il se -tenait là, debout, dans une attitude presque disciplinaire, avec la -rudesse naïve et froide d'un homme qui n'a jamais été doux et qui a -toujours été patient; il attendait, sans dire un mot, sans faire un -mouvement, dans une humilité vraie et dans une résignation tranquille, -qu'il plût à monsieur le maire de se retourner, calme, sérieux, le -chapeau à la main, les yeux baissés, avec une expression qui tenait le -milieu entre le soldat devant son officier et le coupable devant son -juge. Tous les sentiments comme tous les souvenirs qu'on eût pu lui -supposer avaient disparu. Il n'y avait plus rien sur ce visage -impénétrable et simple comme le granit, qu'une morne tristesse. Toute sa -personne respirait l'abaissement et la fermeté, et je ne sais quel -accablement courageux. - -Enfin M. le maire posa sa plume et se tourna à demi. - ---Eh bien! qu'est-ce? qu'y a-t-il, Javert? - -Javert demeura un instant silencieux comme s'il se recueillait, puis -éleva la voix avec une sorte de solennité triste qui n'excluait pourtant -pas la simplicité: - ---Il y a, monsieur le maire, qu'un acte coupable a été commis. - ---Quel acte? - ---Un agent inférieur de l'autorité a manqué de respect à un magistrat de -la façon la plus grave. Je viens, comme c'est mon devoir, porter le fait -à votre connaissance. - ---Quel est cet agent? demanda M. Madeleine. - ---Moi, dit Javert. - ---Vous? - ---Moi. - ---Et quel est le magistrat qui aurait à se plaindre de l'agent? - ---Vous, monsieur le maire. - -M. Madeleine se dressa sur son fauteuil. Javert poursuivit, l'air sévère -et les yeux toujours baissés: - ---Monsieur le maire, je viens vous prier de vouloir bien provoquer près -de l'autorité ma destitution. - -M. Madeleine stupéfait ouvrit la bouche. Javert l'interrompit. - ---Vous direz, j'aurais pu donner ma démission, mais cela ne suffit pas. -Donner sa démission, c'est honorable. J'ai failli, je dois être puni. Il -faut que je sois chassé. - -Et après une pause, il ajouta: - ---Monsieur le maire, vous avez été sévère pour moi l'autre jour -injustement. Soyez-le aujourd'hui justement. - ---Ah çà! pourquoi? s'écria M. Madeleine. Quel est ce galimatias? -qu'est-ce que cela veut dire? où y a-t-il un acte coupable commis contre -moi par vous? qu'est-ce que vous m'avez fait? quels torts avez-vous -envers moi? Vous vous accusez, vous voulez être remplacé.... - ---Chassé, dit Javert. - ---Chassé, soit. C'est fort bien. Je ne comprends pas. - ---Vous allez comprendre, monsieur le maire. - -Javert soupira du fond de sa poitrine et reprit toujours froidement et -tristement: - ---Monsieur le maire, il y a six semaines, à la suite de cette scène pour -cette fille, j'étais furieux, je vous ai dénoncé. - ---Dénoncé! - ---À la préfecture de police de Paris. - -M. Madeleine, qui ne riait pas beaucoup plus souvent que Javert, se mit -à rire. - ---Comme maire ayant empiété sur la police? - ---Comme ancien forçat. - -Le maire devint livide. - -Javert, qui n'avait pas levé les yeux, continua: - ---Je le croyais. Depuis longtemps j'avais des idées. - -Une ressemblance, des renseignements que vous avez fait prendre à -Faverolles, votre force des reins, l'aventure du vieux Fauchelevent, -votre adresse au tir, votre jambe qui traîne un peu, est-ce que je sais, -moi? des bêtises! mais enfin je vous prenais pour un nommé Jean Valjean. - ---Un nommé?... Comment dites-vous ce nom-là? - ---Jean Valjean. C'est un forçat que j'avais vu il y a vingt ans quand -j'étais adjudant-garde-chiourme à Toulon. En sortant du bagne, ce Jean -Valjean avait, à ce qu'il paraît, volé chez un évêque, puis il avait -commis un autre vol à main armée, dans un chemin public, sur un petit -savoyard. Depuis huit ans il s'était dérobé, on ne sait comment, et on -le cherchait. Moi je m'étais figuré... Enfin, j'ai fait cette chose! La -colère m'a décidé, je vous ai dénoncé à la préfecture. - -M. Madeleine, qui avait ressaisi le dossier depuis quelques instants, -reprit avec un accent de parfaite indifférence: - ---Et que vous a-t-on répondu? - ---Que j'étais fou. - ---Eh bien? - ---Eh bien, on avait raison. - ---C'est heureux que vous le reconnaissiez! - ---Il faut bien, puisque le véritable Jean Valjean est trouvé. - -La feuille que tenait M. Madeleine lui échappa des mains, il leva la -tête, regarda fixement Javert, et dit avec un accent inexprimable: - ---Ah! - -Javert poursuivit: - ---Voilà ce que c'est, monsieur le maire. Il paraît qu'il y avait dans le -pays, du côté d'Ailly-le-Haut-Clocher, une espèce de bonhomme qu'on -appelait le père Champmathieu. C'était très misérable. On n'y faisait -pas attention. Ces gens-là, on ne sait pas de quoi cela vit. -Dernièrement, cet automne, le père Champmathieu a été arrêté pour un vol -de pommes à cidre, commis chez...--enfin n'importe! Il y a eu vol, mur -escaladé, branches de l'arbre cassées. On a arrêté mon Champmathieu. Il -avait encore la branche de pommier à la main. On coffre le drôle. -Jusqu'ici ce n'est pas beaucoup plus qu'une affaire correctionnelle. -Mais voici qui est de la providence. La geôle étant en mauvais état, -monsieur le juge d'instruction trouve à propos de faire transférer -Champmathieu à Arras où est la prison départementale. Dans cette prison -d'Arras, il y a un ancien forçat nommé Brevet qui est détenu pour je ne -sais quoi et qu'on a fait guichetier de chambrée parce qu'il se conduit -bien. Monsieur le maire, Champmathieu n'est pas plus tôt débarqué que -voilà Brevet qui s'écrie: «Eh mais! je connais cet homme-là. C'est un -fagot. Regardez-moi donc, bonhomme! Vous êtes Jean Valjean!--Jean -Valjean! qui ça Jean Valjean? Le Champmathieu joue l'étonné.--Ne fais -donc pas le sinvre, dit Brevet. Tu es Jean Valjean! Tu as été au bagne -de Toulon. Il y a vingt ans. Nous y étions ensemble.--Le Champmathieu -nie. Parbleu! vous comprenez. On approfondit. On me fouille cette -aventure-là. Voici ce qu'on trouve: ce Champmathieu, il y a une -trentaine d'années, a été ouvrier émondeur d'arbres dans plusieurs pays, -notamment à Faverolles. Là on perd sa trace. Longtemps après, on le -revoit en Auvergne, puis à Paris, où il dit avoir été charron et avoir -eu une fille blanchisseuse, mais cela n'est pas prouvé; enfin dans ce -pays-ci. Or, avant d'aller au bagne pour vol qualifié, qu'était Jean -Valjean? émondeur. Où? à Faverolles. Autre fait. Ce Valjean s'appelait -de son nom de baptême Jean et sa mère se nommait de son nom de famille -Mathieu. Quoi de plus naturel que de penser qu'en sortant du bagne il -aura pris le nom de sa mère pour se cacher et se sera fait appeler Jean -Mathieu? Il va en Auvergne. De _Jean_ la prononciation du pays fait -_Chan_, on l'appelle Chan Mathieu. Notre homme se laisse faire et le -voilà transformé en Champmathieu. Vous me suivez, n'est-ce pas? On -s'informe à Faverolles. La famille de Jean Valjean n'y est plus. On ne -sait plus où elle est. Vous savez, dans ces classes-là, il y a souvent -de ces évanouissements d'une famille. On cherche, on ne trouve plus -rien. Ces gens-là, quand ce n'est pas de la boue, c'est de la poussière. -Et puis, comme le commencement de ces histoires date de trente ans, il -n'y a plus personne à Faverolles qui ait connu Jean Valjean. On -s'informe à Toulon. Avec Brevet, il n'y a plus que deux forçats qui -aient vu Jean Valjean. Ce sont les condamnés à vie Cochepaille et -Chenildieu. On les extrait du bagne et on les fait venir. On les -confronte au prétendu Champmathieu. Ils n'hésitent pas. Pour eux comme -pour Brevet, c'est Jean Valjean. Même âge, il a cinquante-quatre ans, -même taille, même air, même homme enfin, c'est lui. C'est en ce -moment-là même que j'envoyais ma dénonciation à la préfecture de Paris. -On me répond que je perds l'esprit et que Jean Valjean est à Arras au -pouvoir de la justice. Vous concevez si cela m'étonne, moi qui croyais -tenir ici ce même Jean Valjean! J'écris à monsieur le juge -d'instruction. Il me fait venir, on m'amène le Champmathieu.... - ---Eh bien? interrompit M. Madeleine. - -Javert répondit avec son visage incorruptible et triste: - ---Monsieur le maire, la vérité est la vérité. J'en suis fâché, mais -c'est cet homme-là qui est Jean Valjean. Moi aussi je l'ai reconnu. - -M. Madeleine reprit d'une voix très basse: - ---Vous êtes sûr? - -Javert se mit à rire de ce rire douloureux qui échappe à une conviction -profonde: - ---Oh, sûr! - -Il demeura un moment pensif, prenant machinalement des pincées de poudre -de bois dans la sébille à sécher l'encre qui était sur la table, et il -ajouta: - ---Et même, maintenant que je vois le vrai Jean Valjean, je ne comprends -pas comment j'ai pu croire autre chose. Je vous demande pardon, monsieur -le maire. - -En adressant cette parole suppliante et grave à celui qui, six semaines -auparavant, l'avait humilié en plein corps de garde et lui avait dit: -«sortez!» Javert, cet homme hautain, était à son insu plein de -simplicité et de dignité. M. Madeleine ne répondit à sa prière que par -cette question brusque: - ---Et que dit cet homme? - ---Ah, dame! monsieur le maire, l'affaire est mauvaise. Si c'est Jean -Valjean, il y a récidive. Enjamber un mur, casser une branche, chiper -des pommes, pour un enfant, c'est une polissonnerie; pour un homme, -c'est un délit; pour un forçat, c'est un crime. Escalade et vol, tout y -est. Ce n'est plus la police correctionnelle, c'est la cour d'assises. -Ce n'est plus quelques jours de prison, ce sont les galères à -perpétuité. Et puis, il y a l'affaire du petit savoyard que j'espère -bien qui reviendra. Diable! il y a de quoi se débattre, n'est-ce pas? -Oui, pour un autre que Jean Valjean. Mais Jean Valjean est un sournois. -C'est encore là que je le reconnais. Un autre sentirait que cela -chauffe; il se démènerait, il crierait, la bouilloire chante devant le -feu, il ne voudrait pas être Jean Valjean, et caetera. Lui, il n'a pas -l'air de comprendre, il dit: Je suis Champmathieu, je ne sors pas de là! -Il a l'air étonné, il fait la brute, c'est bien mieux. Oh! le drôle est -habile. Mais c'est égal, les preuves sont là. Il est reconnu par quatre -personnes, le vieux coquin sera condamné. C'est porté aux assises, à -Arras. Je vais y aller pour témoigner. Je suis cité. - -M. Madeleine s'était remis à son bureau, avait ressaisi son dossier, et -le feuilletait tranquillement, lisant et écrivant tour à tour comme un -homme affairé. Il se tourna vers Javert: - ---Assez, Javert. Au fait, tous ces détails m'intéressent fort peu. Nous -perdons notre temps, et nous avons des affaires pressées. Javert, vous -allez vous rendre sur-le-champ chez la bonne femme Buseaupied qui vend -des herbes là-bas au coin de la rue Saint-Saulve. Vous lui direz de -déposer sa plainte contre le charretier Pierre Chesnelong. Cet homme est -un brutal qui a failli écraser cette femme et son enfant. Il faut qu'il -soit puni. Vous irez ensuite chez M. Charcellay, rue -Montre-de-Champigny. Il se plaint qu'il y a une gouttière de la maison -voisine qui verse l'eau de la pluie chez lui, et qui affouille les -fondations de sa maison. Après vous constaterez des contraventions de -police qu'on me signale rue Guibourg chez la veuve Doris, et rue du -Garraud-Blanc chez madame Renée Le Bossé, et vous dresserez -procès-verbal. Mais je vous donne là beaucoup de besogne. N'allez-vous -pas être absent? ne m'avez-vous pas dit que vous alliez à Arras pour -cette affaire dans huit ou dix jours?... - ---Plus tôt que cela, monsieur le maire. - ---Quel jour donc? - ---Mais je croyais avoir dit à monsieur le maire que cela se jugeait -demain et que je partais par la diligence cette nuit. - -M. Madeleine fit un mouvement imperceptible. - ---Et combien de temps durera l'affaire? - ---Un jour tout au plus. L'arrêt sera prononcé au plus tard demain dans -la nuit. Mais je n'attendrai pas l'arrêt, qui ne peut manquer. Sitôt ma -déposition faite, je reviendrai ici. - ---C'est bon, dit M. Madeleine. - -Et il congédia Javert d'un signe de main. Javert ne s'en alla pas. - ---Pardon, monsieur le maire, dit-il. - ---Qu'est-ce encore? demanda M. Madeleine. - ---Monsieur le maire, il me reste une chose à vous rappeler. - ---Laquelle? - ---C'est que je dois être destitué. - -M. Madeleine se leva. - ---Javert, vous êtes un homme d'honneur, et je vous estime. Vous vous -exagérez votre faute. Ceci d'ailleurs est encore une offense qui me -concerne. Javert, vous êtes digne de monter et non de descendre. -J'entends que vous gardiez votre place. - -Javert regarda M. Madeleine avec sa prunelle candide au fond de laquelle -il semblait qu'on vit cette conscience peu éclairée, mais rigide et -chaste, et il dit d'une voix tranquille: - ---Monsieur le maire, je ne puis vous accorder cela. - ---Je vous répète, répliqua M. Madeleine, que la chose me regarde. - -Mais Javert, attentif à sa seule pensée, continua: - ---Quant à exagérer, je n'exagère point. Voici comment je raisonne. Je -vous ai soupçonné injustement. Cela, ce n'est rien. C'est notre droit à -nous autres de soupçonner, quoiqu'il y ait pourtant abus à soupçonner -au-dessus de soi. Mais, sans preuves, dans un accès de colère, dans le -but de me venger, je vous ai dénoncé comme forçat, vous, un homme -respectable, un maire, un magistrat! ceci est grave. Très grave. J'ai -offensé l'autorité dans votre personne, moi, agent de l'autorité! Si -l'un de mes subordonnés avait fait ce que j'ai fait, je l'aurais déclaré -indigne du service, et chassé. Eh bien? - -Tenez, monsieur le maire, encore un mot. J'ai souvent été sévère dans ma -vie. Pour les autres. C'était juste. Je faisais bien. Maintenant, si je -n'étais pas sévère pour moi, tout ce que j'ai fait de juste deviendrait -injuste. - -Est-ce que je dois m'épargner plus que les autres? Non. Quoi! je -n'aurais été bon qu'à châtier autrui, et pas moi! mais je serais un -misérable! mais ceux qui disent: ce gueux de Javert! auraient raison! -Monsieur le maire, je ne souhaite pas que vous me traitiez avec bonté, -votre bonté m'a fait faire assez de mauvais sang quand elle était pour -les autres. Je n'en veux pas pour moi. La bonté qui consiste à donner -raison à la fille publique contre le bourgeois, à l'agent de police -contre le maire, à celui qui est en bas contre celui qui est en haut, -c'est ce que j'appelle de la mauvaise bonté. C'est avec cette bonté-là -que la société se désorganise. Mon Dieu! c'est bien facile d'être bon, -le malaisé c'est d'être juste. Allez! si vous aviez été ce que je -croyais, je n'aurais pas été bon pour vous, moi! vous auriez vu! -Monsieur le maire, je dois me traiter comme je traiterais tout autre. -Quand je réprimais des malfaiteurs, quand je sévissais sur des gredins, -je me suis souvent dit à moi-même: toi, si tu bronches, si jamais je te -prends en faute, sois tranquille!--J'ai bronché, je me prends en faute, -tant pis! Allons, renvoyé, cassé, chassé! c'est bon. J'ai des bras, je -travaillerai à la terre, cela m'est égal. Monsieur le maire, le bien du -service veut un exemple. Je demande simplement la destitution de -l'inspecteur Javert. - -Tout cela était prononcé d'un accent humble, fier, désespéré et -convaincu qui donnait je ne sais quelle grandeur bizarre à cet étrange -honnête homme. - ---Nous verrons, fit M. Madeleine. - -Et il lui tendit la main. - -Javert recula, et dit d'un ton farouche: - ---Pardon, monsieur le maire, mais cela ne doit pas être. Un maire ne -donne pas la main à un mouchard. - -Il ajouta entre ses dents: - ---Mouchard, oui; du moment où j'ai médusé de la police, je ne suis plus -qu'un mouchard. Puis il salua profondément, et se dirigea vers la porte. -Là il se retourna, et, les yeux toujours baissés: - ---Monsieur le maire, dit-il, je continuerai le service jusqu'à ce que je -sois remplacé. - -Il sortit. M. Madeleine resta rêveur, écoutant ce pas ferme et assuré -qui s'éloignait sur le pavé du corridor. - - - - -Livre septième--L'affaire Champmathieu - - - - -Chapitre I - -La soeur Simplice - - -Les incidents qu'on va lire n'ont pas tous été connus à -Montreuil-sur-mer, mais le peu qui en a percé a laissé dans cette ville -un tel souvenir, que ce serait une grave lacune dans ce livre si nous ne -les racontions dans leurs moindres détails. - -Dans ces détails, le lecteur rencontrera deux ou trois circonstances -invraisemblables que nous maintenons par respect pour la vérité. - -Dans l'après-midi qui suivit la visite de Javert, M. Madeleine alla voir -la Fantine comme d'habitude. - -Avant de pénétrer près de Fantine, il fit demander la soeur Simplice. -Les deux religieuses qui faisaient le service de l'infirmerie, dames -lazaristes comme toutes les soeurs de charité, s'appelaient soeur -Perpétue et soeur Simplice. - -La soeur Perpétue était la première villageoise venue, grossièrement -soeur de charité, entrée chez Dieu comme on entre en place. Elle était -religieuse comme on est cuisinière. Ce type n'est point très rare. Les -ordres monastiques acceptent volontiers cette lourde poterie paysanne, -aisément façonnée en capucin ou en ursuline. Ces rusticités s'utilisent -pour les grosses besognes de la dévotion. La transition d'un bouvier à -un carme n'a rien de heurté; l'un devient l'autre sans grand travail; le -fond commun d'ignorance du village et du cloître est une préparation -toute faite, et met tout de suite le campagnard de plain-pied avec le -moine. Un peu d'ampleur au sarrau, et voilà un froc. La soeur Perpétue -était une forte religieuse, de Marines, près Pontoise, patoisant, -psalmodiant, bougonnant, sucrant la tisane selon le bigotisme ou -l'hypocrisie du grabataire, brusquant les malades, bourrue avec les -mourants, leur jetant presque Dieu au visage, lapidant l'agonie avec des -prières en colère, hardie, honnête et rougeaude. - -La soeur Simplice était blanche d'une blancheur de cire. Près de soeur -Perpétue, c'était le cierge à côté de la chandelle. Vincent de Paul a -divinement fixé la figure de la soeur de charité dans ces admirables -paroles où il mêle tant de liberté à tant de servitude: «Elles n'auront -pour monastère que la maison des malades, pour cellule qu'une chambre de -louage, pour chapelle que l'église de leur paroisse, pour cloître que -les rues de la ville ou les salles des hôpitaux, pour clôture que -l'obéissance, pour grille que la crainte de Dieu, pour voile que la -modestie.» Cet idéal était vivant dans la soeur Simplice. Personne n'eût -pu dire l'âge de la soeur Simplice; elle n'avait jamais été jeune et -semblait ne devoir jamais être vieille. C'était une personne--nous -n'osons dire une femme--calme, austère, de bonne compagnie, froide, et -qui n'avait jamais menti. Elle était si douce qu'elle paraissait -fragile; plus solide d'ailleurs que le granit. Elle touchait aux -malheureux avec de charmants doigts fins et purs. Il y avait, pour ainsi -dire, du silence dans sa parole; elle parlait juste le nécessaire, et -elle avait un son de voix qui eût tout à la fois édifié un confessionnal -et enchanté un salon. Cette délicatesse s'accommodait de la robe de -bure, trouvant à ce rude contact un rappel continuel du ciel et de Dieu. -Insistons sur un détail. N'avoir jamais menti, n'avoir jamais dit, pour -un intérêt quelconque, même indifféremment, une chose qui ne fût la -vérité, la sainte vérité, c'était le trait distinctif de la soeur -Simplice; c'était l'accent de sa vertu. Elle était presque célèbre dans -la congrégation pour cette véracité imperturbable. L'abbé Sicard parle -de la soeur Simplice dans une lettre au sourd-muet Massieu. Si sincères, -si loyaux et si purs que nous soyons, nous avons tous sur notre candeur -au moins la fêlure du petit mensonge innocent. Elle, point. Petit -mensonge, mensonge innocent, est-ce que cela existe? Mentir, c'est -l'absolu du mal. Peu mentir n'est pas possible; celui qui ment, ment -tout le mensonge; mentir, c'est la face même du démon; Satan a deux -noms, il s'appelle Satan et il s'appelle Mensonge. Voilà ce qu'elle -pensait. Et comme elle pensait, elle pratiquait. Il en résultait cette -blancheur dont nous avons parlé, blancheur qui couvrait de son -rayonnement même ses lèvres et ses yeux. Son sourire était blanc, son -regard était blanc. Il n'y avait pas une toile d'araignée, pas un grain -de poussière à la vitre de cette conscience. En entrant dans l'obédience -de saint Vincent de Paul, elle avait pris le nom de Simplice par choix -spécial. Simplice de Sicile, on le sait, est cette sainte qui aima mieux -se laisser arracher les deux seins que de répondre, étant née à -Syracuse, qu'elle était née à Ségeste, mensonge qui la sauvait. Cette -patronne convenait à cette âme. - -La soeur Simplice, en entrant dans l'ordre, avait deux défauts dont elle -s'était peu à peu corrigée; elle avait eu le goût des friandises et elle -avait aimé à recevoir des lettres. Elle ne lisait jamais qu'un livre de -prières en gros caractères et en latin. Elle ne comprenait pas le latin, -mais elle comprenait le livre. - -La pieuse fille avait pris en affection Fantine, y sentant probablement -de la vertu latente, et s'était dévouée à la soigner presque -exclusivement. - -M. Madeleine emmena à part la soeur Simplice et lui recommanda Fantine -avec un accent singulier dont la soeur se souvint plus tard. - -En quittant la soeur, il s'approcha de Fantine. - -Fantine attendait chaque jour l'apparition de M. Madeleine comme on -attend un rayon de chaleur et de joie. Elle disait aux soeurs: - ---Je ne vis que lorsque monsieur le maire est là. - -Elle avait ce jour-là beaucoup de fièvre. Dès qu'elle vit M. Madeleine, -elle lui demanda: - ---Et Cosette? - -Il répondit en souriant: - ---Bientôt. - -M. Madeleine fut avec Fantine comme à l'ordinaire. Seulement il resta -une heure au lieu d'une demi-heure, au grand contentement de Fantine. Il -fît mille instances à tout le monde pour que rien ne manquât à la -malade. On remarqua qu'il y eut un moment où son visage devint très -sombre. Mais cela s'expliqua quand on sut que le médecin s'était penché -à son oreille et lui avait dit: - ---Elle baisse beaucoup. - -Puis il rentra à la mairie, et le garçon de bureau le vit examiner avec -attention une carte routière de France qui était suspendue dans son -cabinet. Il écrivit quelques chiffres au crayon sur un papier. - - - - -Chapitre II - -Perspicacité de maître Scaufflaire - - -De la mairie il se rendit au bout de la ville chez un Flamand, maître -Scaufflaër, francisé Scaufflaire, qui louait des chevaux et des -«cabriolets à volonté». - -Pour aller chez ce Scaufflaire, le plus court était de prendre une rue -peu fréquentée où était le presbytère de la paroisse que M. Madeleine -habitait. Le curé était, disait-on, un homme digne et respectable, et de -bon conseil. À l'instant où M. Madeleine arriva devant le presbytère, il -n'y avait dans la rue qu'un passant, et ce passant remarqua ceci: M. le -maire, après avoir dépassé la maison curiale, s'arrêta, demeura -immobile, puis revint sur ses pas et rebroussa chemin jusqu'à la porte -du presbytère, qui était une porte bâtarde avec marteau de fer. Il mit -vivement la main au marteau, et le souleva; puis il s'arrêta de nouveau, -et resta court, et comme pensif, et, après quelques secondes, au lieu de -laisser bruyamment retomber le marteau, il le reposa doucement et reprit -son chemin avec une sorte de hâte qu'il n'avait pas auparavant. - -M. Madeleine trouva maître Scaufflaire chez lui occupé à repiquer un -harnais. - ---Maître Scaufflaire, demanda-t-il, avez-vous un bon cheval? - ---Monsieur le maire, dit le Flamand, tous mes chevaux sont bons. -Qu'entendez-vous par un bon cheval? - ---J'entends un cheval qui puisse faire vingt lieues en un jour. - ---Diable! fit le Flamand, vingt lieues! - ---Oui. - ---Attelé à un cabriolet? - ---Oui. - ---Et combien de temps se reposera-t-il après la course? - ---Il faut qu'il puisse au besoin repartir le lendemain. - ---Pour refaire le même trajet? - ---Oui. - ---Diable! diable! et c'est vingt lieues? M. Madeleine tira de sa poche -le papier où il avait crayonné des chiffres. Il les montra au Flamand. -C'étaient les chiffres 5, 6, 8-1/2. - ---Vous voyez, dit-il. Total, dix-neuf et demi, autant dire vingt lieues. - ---Monsieur le maire, reprit le Flamand, j'ai votre affaire. Mon petit -cheval blanc. Vous avez dû le voir passer quelquefois. C'est une petite -bête du bas Boulonnais. C'est plein de feu. On a voulu d'abord en faire -un cheval de selle. Bah! il ruait, il flanquait tout le monde par terre. -On le croyait vicieux, on ne savait qu'en faire. Je l'ai acheté. Je l'ai -mis au cabriolet. Monsieur, c'est cela qu'il voulait; il est doux comme -une fille, il va le vent. Ah! par exemple, il ne faudrait pas lui monter -sur le dos. Ce n'est pas son idée d'être cheval de selle. Chacun a son -ambition. Tirer, oui, porter, non; il faut croire qu'il s'est dit ça. - ---Et il fera la course? - ---Vos vingt lieues. Toujours au grand trot, et en moins de huit heures. -Mais voici à quelles conditions. - ---Dites. - ---Premièrement, vous le ferez souffler une heure à moitié chemin; il -mangera, et on sera là pendant qu'il mangera pour empêcher le garçon de -l'auberge de lui voler son avoine; car j'ai remarqué que dans les -auberges l'avoine est plus souvent bue par les garçons d'écurie que -mangée par les chevaux. - ---On sera là. - ---Deuxièmement.... Est-ce pour monsieur le maire le cabriolet? - ---Oui. - ---Monsieur le maire sait conduire? - ---Oui. - ---Eh bien, monsieur le maire voyagera seul et sans bagage afin de ne -point charger le cheval. - ---Convenu. - ---Mais monsieur le maire, n'ayant personne avec lui, sera obligé de -prendre la peine de surveiller lui-même l'avoine. - ---C'est dit. - ---Il me faudra trente francs par jour. Les jours de repos payés. Pas un -liard de moins, et la nourriture de la bête à la charge de monsieur le -maire. - -M. Madeleine tira trois napoléons de sa bourse et les mit sur la table. - ---Voilà deux jours d'avance. - ---Quatrièmement, pour une course pareille sur cabriolet serait trop -lourd et fatiguerait le cheval. Il faudrait que monsieur le maire -consentît à voyager dans un petit tilbury que j'ai. - ---J'y consens. - ---C'est léger, mais c'est découvert. - ---Cela m'est égal. - ---Monsieur le maire a-t-il réfléchi que nous sommes en hiver?... - -M. Madeleine ne répondit pas. Le Flamand reprit: - ---Qu'il fait très froid? - -M. Madeleine garda le silence. Maître Scaufflaire continua: - ---Qu'il peut pleuvoir? - -M. Madeleine leva la tête et dit: - ---Le tilbury et le cheval seront devant ma porte demain à quatre heures -et demie du matin. - ---C'est entendu, monsieur le maire, répondit Scaufflaire, puis, grattant -avec l'ongle de son pouce une tache qui était dans le bois de la table, -il reprit de cet air insouciant que les Flamands savent si bien mêler à -leur finesse: - ---Mais voilà que j'y songe à présent! monsieur le maire ne me dit pas où -il va. Où est-ce que va monsieur le maire? - -Il ne songeait pas à autre chose depuis le commencement de la -conversation, mais il ne savait pourquoi il n'avait pas osé faire cette -question. - ---Votre cheval a-t-il de bonnes jambes de devant? dit M. Madeleine. - ---Oui, monsieur le maire. Vous le soutiendrez un peu dans les descentes. -Y a-t-il beaucoup de descentes d'ici où vous allez? - ---N'oubliez pas d'être à ma porte à quatre heures et demie du matin, -très précises, répondit M. Madeleine; et il sortit. - -Le Flamand resta «tout bête», comme il disait lui-même quelque temps -après. - -Monsieur le maire était sorti depuis deux ou trois minutes, lorsque la -porte se rouvrit; c'était M. le maire. Il avait toujours le même air -impassible et préoccupé. - ---Monsieur Scaufflaire, dit-il, à quelle somme estimez-vous le cheval et -le tilbury que vous me louerez, l'un portant l'autre? - ---L'un traînant l'autre, monsieur le maire, dit le Flamand avec un gros -rire. - ---Soit. Eh bien! - ---Est-ce que monsieur le maire veut me les acheter? - ---Non, mais à tout événement, je veux vous les garantir. À mon retour -vous me rendrez la somme. Combien estimez-vous cabriolet et cheval? - ---À cinq cents francs, monsieur le maire. - ---Les voici. - -M. Madeleine posa un billet de banque sur la table, puis sortit et cette -fois ne rentra plus. - -Maître Scaufflaire regretta affreusement de n'avoir point dit mille -francs. Du reste le cheval et le tilbury, en bloc, valaient cent écus. - -Le Flamand appela sa femme, et lui conta la chose. Où diable monsieur le -maire peut-il aller? Ils tinrent conseil. - ---Il va à Paris, dit la femme. - ---Je ne crois pas, dit le mari. - -M. Madeleine avait oublié sur la cheminée le papier où il avait tracé -des chiffres. Le Flamand le prit et l'étudia. - ---Cinq, six, huit et demi? cela doit marquer des relais de poste. - -Il se tourna vers sa femme. - ---J'ai trouvé. - ---Comment? - ---Il y a cinq lieues d'ici à Hesdin, six de Hesdin à Saint-Pol, huit et -demie de Saint-Pol à Arras. Il va à Arras. - -Cependant M. Madeleine était rentré chez lui. - -Pour revenir de chez maître Scaufflaire, il avait pris le plus long, -comme si la porte du presbytère avait été pour lui une tentation, et -qu'il eût voulu l'éviter. Il était monté dans sa chambre et s'y était -enfermé, ce qui n'avait rien que de simple, car il se couchait -volontiers de bonne heure. Pourtant la concierge de la fabrique, qui -était en même temps l'unique servante de M. Madeleine, observa que sa -lumière s'éteignit à huit heures et demie, et elle le dit au caissier -qui rentrait, en ajoutant: - ---Est-ce que monsieur le maire est malade? je lui ai trouvé l'air un peu -singulier. - -Ce caissier habitait une chambre située précisément au-dessous de la -chambre de M. Madeleine. Il ne prit point garde aux paroles de la -portière, se coucha et s'endormit. Vers minuit, il se réveilla -brusquement; il avait entendu à travers son sommeil un bruit au-dessus -de sa tête. Il écouta. C'était un pas qui allait et venait, comme si -l'on marchait dans la chambre en haut. Il écouta plus attentivement, et -reconnut le pas de M. Madeleine. Cela lui parut étrange; habituellement -aucun bruit ne se faisait dans la chambre de M. Madeleine avant l'heure -de son lever. Un moment après le caissier entendit quelque chose qui -ressemblait à une armoire qu'on ouvre et qu'on referme. Puis on dérangea -un meuble, il y eut un silence, et le pas recommença. Le caissier se -dressa sur son séant, s'éveilla tout à fait, regarda, et à travers les -vitres de sa croisée aperçut sur le mur d'en face la réverbération -rougeâtre d'une fenêtre éclairée. À la direction des rayons, ce ne -pouvait être que la fenêtre de la chambre de M. Madeleine. La -réverbération tremblait comme si elle venait plutôt d'un feu allumé que -d'une lumière. L'ombre des châssis vitrés ne s'y dessinait pas, ce qui -indiquait que la fenêtre était toute grande ouverte. Par le froid qu'il -faisait, cette fenêtre ouverte était surprenante. Le caissier se -rendormit. Une heure ou deux après, il se réveilla encore. Le même pas, -lent et régulier, allait et venait toujours au-dessus de sa tête. - -La réverbération se dessinait toujours sur le mur, mais elle était -maintenant pâle et paisible comme le reflet d'une lampe ou d'une bougie. -La fenêtre était toujours ouverte. Voici ce qui se passait dans la -chambre de M. Madeleine. - - - - -Chapitre III - -Une tempête sous un crâne - - -Le lecteur a sans doute deviné que M. Madeleine n'est autre que Jean -Valjean. - -Nous avons déjà regardé dans les profondeurs de cette conscience; le -moment est venu d'y regarder encore. Nous ne le faisons pas sans émotion -et sans tremblement. Il n'existe rien de plus terrifiant que cette sorte -de contemplation. L'oeil de l'esprit ne peut trouver nulle part plus -d'éblouissements ni plus de ténèbres que dans l'homme; il ne peut se -fixer sur aucune chose qui soit plus redoutable, plus compliquée, plus -mystérieuse et plus infinie. Il y a un spectacle plus grand que la mer, -c'est le ciel; il y a un spectacle plus grand que le ciel, c'est -l'intérieur de l'âme. - -Faire le poème de la conscience humaine, ne fût-ce qu'à propos d'un seul -homme, ne fût-ce qu'à propos du plus infime des hommes, ce serait fondre -toutes les épopées dans une épopée supérieure et définitive. La -conscience, c'est le chaos des chimères, des convoitises et des -tentatives, la fournaise des rêves, l'antre des idées dont on a honte; -c'est le pandémonium des sophismes, c'est le champ de bataille des -passions. À de certaines heures, pénétrez à travers la face livide d'un -être humain qui réfléchit, et regardez derrière, regardez dans cette -âme, regardez dans cette obscurité. Il y a là, sous le silence -extérieur, des combats de géants comme dans Homère, des mêlées de -dragons et d'hydres et des nuées de fantômes comme dans Milton, des -spirales visionnaires comme chez Dante. Chose sombre que cet infini que -tout homme porte en soi et auquel il mesure avec désespoir les volontés -de son cerveau et les actions de sa vie! - -Alighieri rencontra un jour une sinistre porte devant laquelle il -hésita. En voici une aussi devant nous, au seuil de laquelle nous -hésitons. Entrons pourtant. - -Nous n'avons que peu de chose à ajouter à ce que le lecteur connaît déjà -de ce qui était arrivé à Jean Valjean depuis l'aventure de -Petit-Gervais. À partir de ce moment, on l'a vu, il fut un autre homme. -Ce que l'évêque avait voulu faire de lui, il l'exécuta. Ce fut plus -qu'une transformation, ce fut une transfiguration. - -Il réussit à disparaître, vendit l'argenterie de l'évêque, ne gardant -que les flambeaux, comme souvenir, se glissa de ville en ville, traversa -la France, vint à Montreuil-sur-mer, eut l'idée que nous avons dite, -accomplit ce que nous avons raconté, parvint à se faire insaisissable et -inaccessible, et désormais, établi à Montreuil-sur-mer, heureux de -sentir sa conscience attristée par son passé et la première moitié de -son existence démentie par la dernière, il vécut paisible, rassuré et -espérant, n'ayant plus que deux pensées: cacher son nom, et sanctifier -sa vie; échapper aux hommes, et revenir à Dieu. - -Ces deux pensées étaient si étroitement mêlées dans son esprit qu'elles -n'en formaient qu'une seule; elles étaient toutes deux également -absorbantes et impérieuses, et dominaient ses moindres actions. -D'ordinaire elles étaient d'accord pour régler la conduite de sa vie; -elles le tournaient vers l'ombre; elles le faisaient bienveillant et -simple; elles lui conseillaient les mêmes choses. Quelquefois cependant -il y avait conflit entre elles. Dans ce cas-là, on s'en souvient, -l'homme que tout le pays de Montreuil-sur-mer appelait M. Madeleine ne -balançait pas à sacrifier la première à la seconde, sa sécurité à sa -vertu. Ainsi, en dépit de toute réserve et de toute prudence, il avait -gardé les chandeliers de l'évêque, porté son deuil, appelé et interrogé -tous les petits savoyards qui passaient, pris des renseignements sur les -familles de Faverolles, et sauvé la vie au vieux Fauchelevent, malgré -les inquiétantes insinuations de Javert. Il semblait, nous l'avons déjà -remarqué, qu'il pensât, à l'exemple de tous ceux qui ont été sages, -saints et justes, que son premier devoir n'était pas envers lui. - -Toutefois, il faut le dire, jamais rien de pareil ne s'était encore -présenté. Jamais les deux idées qui gouvernaient le malheureux homme -dont nous racontons les souffrances n'avaient engagé une lutte si -sérieuse. Il le comprit confusément, mais profondément, dès les -premières paroles que prononça Javert, en entrant dans son cabinet. - -Au moment où fut si étrangement articulé ce nom qu'il avait enseveli -sous tant d'épaisseurs, il fut saisi de stupeur et comme enivré par la -sinistre bizarrerie de sa destinée, et, à travers cette stupeur, il eut -ce tressaillement qui précède les grandes secousses; il se courba comme -un chêne à l'approche d'un orage, comme un soldat à l'approche d'un -assaut. Il sentit venir sur sa tête des ombres pleines de foudres et -d'éclairs. Tout en écoutant parler Javert, il eut une première pensée -d'aller, de courir, de se dénoncer, de tirer ce Champmathieu de prison -et de s'y mettre; cela fut douloureux et poignant comme une incision -dans la chair vive, puis cela passa, et il se dit: «Voyons! voyons!» Il -réprima ce premier mouvement généreux et recula devant l'héroïsme. - -Sans doute, il serait beau qu'après les saintes paroles de l'évêque, -après tant d'années de repentir et d'abnégation, au milieu d'une -pénitence admirablement commencée, cet homme, même en présence d'une si -terrible conjoncture, n'eût pas bronché un instant et eût continué de -marcher du même pas vers ce précipice ouvert au fond duquel était le -ciel; cela serait beau, mais cela ne fut pas ainsi. Il faut bien que -nous rendions compte des choses qui s'accomplissaient dans cette âme, et -nous ne pouvons dire que ce qui y était. Ce qui l'emporta tout d'abord, -ce fut l'instinct de la conservation; il rallia en hâte ses idées, -étouffa ses émotions, considéra la présence de Javert, ce grand péril, -ajourna toute résolution avec la fermeté de l'épouvante, s'étourdit sur -ce qu'il y avait à faire, et reprit son calme comme un lutteur ramasse -son bouclier. - -Le reste de la journée il fut dans cet état, un tourbillon au dedans, -une tranquillité profonde au dehors; il ne prit que ce qu'on pourrait -appeler «les mesures conservatoires». Tout était encore confus et se -heurtait dans son cerveau; le trouble y était tel qu'il ne voyait -distinctement la forme d'aucune idée; et lui-même n'aurait pu rien dire -de lui-même, si ce n'est qu'il venait de recevoir un grand coup. Il se -rendit comme d'habitude près du lit de douleur de Fantine et prolongea -sa visite, par un instinct de bonté, se disant qu'il fallait agir ainsi -et la bien recommander aux soeurs pour le cas où il arriverait qu'il eût -à s'absenter. Il sentit vaguement qu'il faudrait peut-être aller à -Arras, et, sans être le moins du monde décidé à ce voyage, il se dit -qu'à l'abri de tout soupçon comme il l'était, il n'y avait point -d'inconvénient à être témoin de ce qui se passerait, et il retint le -tilbury de Scaufflaire, afin d'être préparé à tout événement. - -Il dîna avec assez d'appétit. - -Rentré dans sa chambre il se recueillit. - -Il examina la situation et la trouva inouïe; tellement inouïe qu'au -milieu de sa rêverie, par je ne sais quelle impulsion d'anxiété presque -inexplicable, il se leva de sa chaise et ferma sa porte au verrou. Il -craignait qu'il n'entrât encore quelque chose. Il se barricadait contre -le possible. - -Un moment après il souffla sa lumière. Elle le gênait. - -Il lui semblait qu'on pouvait le voir. - -Qui, on? - -Hélas! ce qu'il voulait mettre à la porte était entré ce qu'il voulait -aveugler, le regardait. Sa conscience. - -Sa conscience, c'est-à-dire Dieu. - -Pourtant, dans le premier moment, il se fit illusion; il eut un -sentiment de sûreté et de solitude; le verrou tiré, il se crut -imprenable; la chandelle éteinte, il se sentit invisible. Alors il prit -possession de lui-même; il posa ses coudes sur la table, appuya la tête -sur sa main, et se mit à songer dans les ténèbres. - ---Où en suis-je?--Est-ce que je ne rêve pas? Que m'a-t-on dit?--Est-il -bien vrai que j'aie vu ce Javert et qu'il m'ait parlé ainsi?--Que peut -être ce Champmathieu?--Il me ressemble donc?--Est-ce possible?--Quand -je pense qu'hier j'étais si tranquille et si loin de me douter de -rien!--Qu'est-ce que je faisais donc hier à pareille heure?--Qu'y a-t-il -dans cet incident?--Comment se dénouera-t-il?--Que faire? - -Voilà dans quelle tourmente il était. Son cerveau avait perdu la force -de retenir ses idées, elles passaient comme des ondes, et il prenait son -front dans ses deux mains pour les arrêter. - -De ce tumulte qui bouleversait sa volonté et sa raison, et dont il -cherchait à tirer une évidence et une résolution, rien ne se dégageait -que l'angoisse. - -Sa tête était brûlante. Il alla à la fenêtre et l'ouvrit toute grande. -Il n'y avait pas d'étoiles au ciel. Il revint s'asseoir près de la -table. - -La première heure s'écoula ainsi. - -Peu à peu cependant des linéaments vagues commencèrent à se former et à -se fixer dans sa méditation, et il put entrevoir avec la précision de la -réalité, non l'ensemble de la situation, mais quelques détails. - -Il commença par reconnaître que, si extraordinaire et si critique que -fût cette situation, il en était tout à fait le maître. - -Sa stupeur ne fit que s'en accroître. - -Indépendamment du but sévère et religieux que se proposaient ses -actions, tout ce qu'il avait fait jusqu'à ce jour n'était autre chose -qu'un trou qu'il creusait pour y enfouir son nom. Ce qu'il avait -toujours le plus redouté, dans ses heures de repli sur lui-même, dans -ses nuits d'insomnie, c'était d'entendre jamais prononcer ce nom; il se -disait que ce serait là pour lui la fin de tout; que le jour où ce nom -reparaîtrait, il ferait évanouir autour de lui sa vie nouvelle, et qui -sait même peut-être? au dedans de lui sa nouvelle âme. Il frémissait de -la seule pensée que c'était possible. Certes, si quelqu'un lui eût dit -en ces moments-là qu'une heure viendrait où ce nom retentirait à son -oreille, où ce hideux mot, Jean Valjean, sortirait tout à coup de la -nuit et se dresserait devant lui, où cette lumière formidable faite pour -dissiper le mystère dont il s'enveloppait resplendirait subitement sur -sa tête; et que ce nom ne le menacerait pas, que cette lumière ne -produirait qu'une obscurité plus épaisse, que ce voile déchiré -accroîtrait le mystère; que ce tremblement de terre consoliderait son -édifice, que ce prodigieux incident n'aurait d'autre résultat, si bon -lui semblait, à lui, que de rendre son existence à la fois plus claire -et plus impénétrable, et que, de sa confrontation avec le fantôme de -Jean Valjean, le bon et digne bourgeois monsieur Madeleine sortirait -plus honoré, plus paisible et plus respecté que jamais,--si quelqu'un -lui eût dit cela, il eût hoché la tête et regardé ces paroles comme -insensées. Eh bien! tout cela venait précisément d'arriver, tout cet -entassement de l'impossible était un fait, et Dieu avait permis que ces -choses folles devinssent des choses réelles! - -Sa rêverie continuait de s'éclaircir. Il se rendait de plus en plus -compte de sa position. Il lui semblait qu'il venait de s'éveiller de je -ne sais quel sommeil, et qu'il se trouvait glissant sur une pente au -milieu de la nuit, debout, frissonnant, reculant en vain, sur le bord -extrême d'un abîme. Il entrevoyait distinctement dans l'ombre un -inconnu, un étranger, que la destinée prenait pour lui et poussait dans -le gouffre à sa place. Il fallait, pour que le gouffre se refermât, que -quelqu'un y tombât, lui ou l'autre. - -Il n'avait qu'à laisser faire. - -La clarté devint complète, et il s'avoua ceci:--Que sa place était vide -aux galères, qu'il avait beau faire, qu'elle l'y attendait toujours, que -le vol de Petit-Gervais l'y ramenait, que cette place vide l'attendrait -et l'attirerait jusqu'à ce qu'il y fût, que cela était inévitable et -fatal.--Et puis il se dit:--Qu'en ce moment il avait un remplaçant, -qu'il paraissait qu'un nommé Champmathieu avait cette mauvaise chance, -et que, quant à lui, présent désormais au bagne dans la personne de ce -Champmathieu, présent dans la société sous le nom de M. Madeleine, il -n'avait plus rien à redouter, pourvu qu'il n'empêchât pas les hommes de -sceller sur la tête de ce Champmathieu cette pierre de l'infamie qui, -comme la pierre du sépulcre, tombe une fois et ne se relève jamais. - -Tout cela était si violent et si étrange qu'il se fit soudain en lui -cette espèce de mouvement indescriptible qu'aucun homme n'éprouve plus -de deux ou trois fois dans sa vie, sorte de convulsion de la conscience -qui remue tout ce que le coeur a de douteux, qui se compose d'ironie, de -joie et de désespoir, et qu'on pourrait appeler un éclat de rire -intérieur. - -Il ralluma brusquement sa bougie. - ---Eh bien quoi! se dit-il, de quoi est-ce que j'ai peur? qu'est-ce que -j'ai à songer comme cela? Me voilà sauvé. Tout est fini. Je n'avais plus -qu'une porte entr'ouverte par laquelle mon passé pouvait faire irruption -dans ma vie; cette porte, la voilà murée! à jamais! Ce Javert qui me -trouble depuis si longtemps, ce redoutable instinct qui semblait m'avoir -deviné, qui m'avait deviné, pardieu! et qui me suivait partout, cet -affreux chien de chasse toujours en arrêt sur moi, le voilà dérouté, -occupé ailleurs, absolument dépisté! Il est satisfait désormais, il me -laissera tranquille, il tient son Jean Valjean! Qui sait même, il est -probable qu'il voudra quitter la ville! Et tout cela s'est fait sans -moi! Et je n'y suis pour rien! Ah çà, mais! qu'est-ce qu'il y a de -malheureux dans ceci? Des gens qui me verraient, parole d'honneur! -croiraient qu'il m'est arrivé une catastrophe! Après tout, s'il y a du -mal pour quelqu'un, ce n'est aucunement de ma faute. C'est la providence -qui a tout fait. C'est qu'elle veut cela apparemment! - -Ai-je le droit de déranger ce qu'elle arrange? Qu'est-ce que je demande -à présent? De quoi est-ce que je vais me mêler? Cela ne me regarde pas. -Comment! je ne suis pas content! Mais qu'est-ce qu'il me faut donc? Le -but auquel j'aspire depuis tant d'années, le songe de mes nuits, l'objet -de mes prières au ciel, la sécurité, je l'atteins! C'est Dieu qui le -veut. Je n'ai rien à faire contre la volonté de Dieu. Et pourquoi Dieu -le veut-il? Pour que je continue ce que j'ai commencé, pour que je fasse -le bien, pour que je sois un jour un grand et encourageant exemple, pour -qu'il soit dit qu'il y a eu enfin un peu de bonheur attaché à cette -pénitence que j'ai subie et à cette vertu où je suis revenu! Vraiment je -ne comprends pas pourquoi j'ai eu peur tantôt d'entrer chez ce brave -curé et de tout lui raconter comme à un confesseur, et de lui demander -conseil, c'est évidemment là ce qu'il m'aurait dit. C'est décidé, -laissons aller les choses! laissons faire le bon Dieu! - -Il se parlait ainsi dans les profondeurs de sa conscience, penché sur ce -qu'on pourrait appeler son propre abîme. Il se leva de sa chaise, et se -mit à marcher dans la chambre.--Allons, dit-il, n'y pensons plus. Voilà -une résolution prise!--Mais il ne sentit aucune joie. - -Au contraire. - -On n'empêche pas plus la pensée de revenir à une idée que la mer de -revenir à un rivage. Pour le matelot, cela s'appelle la marée; pour le -coupable, cela s'appelle le remords. Dieu soulève l'âme comme l'océan. - -Au bout de peu d'instants, il eut beau faire, il reprit ce sombre -dialogue dans lequel c'était lui qui parlait et lui qui écoutait, disant -ce qu'il eût voulu taire, écoutant ce qu'il n'eût pas voulu entendre, -cédant à cette puissance mystérieuse qui lui disait: pense! comme elle -disait il y a deux mille ans à un autre condamné, marche! - -Avant d'aller plus loin et pour être pleinement compris, insistons sur -une observation nécessaire. - -Il est certain qu'on se parle à soi-même, il n'est pas un être pensant -qui ne l'ait éprouvé. On peut dire même que le verbe n'est jamais un -plus magnifique mystère que lorsqu'il va, dans l'intérieur d'un homme, -de la pensée à la conscience et qu'il retourne de la conscience à la -pensée. C'est dans ce sens seulement qu'il faut entendre les mots -souvent employés dans ce chapitre, il dit, il s'écria. On se dit, on se -parle, on s'écrie en soi-même, sans que le silence extérieur soit rompu. -Il y a un grand tumulte; tout parle en nous, excepté la bouche. Les -réalités de l'âme, pour n'être point visibles et palpables, n'en sont -pas moins des réalités. - -Il se demanda donc où il en était. Il s'interrogea sur cette «résolution -prise». Il se confessa à lui-même que tout ce qu'il venait d'arranger -dans son esprit était monstrueux, que «laisser aller les choses, laisser -faire le bon Dieu», c'était tout simplement horrible. Laisser -s'accomplir cette méprise de la destinée et des hommes, ne pas -l'empêcher, s'y prêter par son silence, ne rien faire enfin, c'était -faire tout! c'était le dernier degré de l'indignité hypocrite! c'était -un crime bas, lâche, sournois, abject, hideux! - -Pour la première fois depuis huit années, le malheureux homme venait de -sentir la saveur amère d'une mauvaise pensée et d'une mauvaise action. - -Il la recracha avec dégoût. - -Il continua de se questionner. Il se demanda sévèrement ce qu'il avait -entendu par ceci: "Mon but est atteint!" Il se déclara que sa vie avait -un but en effet. Mais quel but? cacher son nom? tromper la police? -Était-ce pour une chose si petite qu'il avait fait tout ce qu'il avait -fait? Est-ce qu'il n'avait pas un autre but, qui était le grand, qui -était le vrai? Sauver, non sa personne, mais son âme. Redevenir honnête -et bon. Être un juste! est-ce que ce n'était pas là surtout, là -uniquement, ce qu'il avait toujours voulu, ce que l'évêque lui avait -ordonné?--Fermer la porte à son passé? Mais il ne la fermait pas, grand -Dieu! il la rouvrait en faisant une action infâme! mais il redevenait un -voleur, et le plus odieux des voleurs! il volait à un autre son -existence, sa vie, sa paix, sa place au soleil! il devenait un assassin! -il tuait, il tuait moralement un misérable homme, il lui infligeait -cette affreuse mort vivante, cette mort à ciel ouvert, qu'on appelle le -bagne! Au contraire, se livrer, sauver cet homme frappé d'une si lugubre -erreur, reprendre son nom, redevenir par devoir le forçat Jean Valjean, -c'était là vraiment achever sa résurrection, et fermer à jamais l'enfer -d'où il sortait! Y retomber en apparence, c'était en sortir en réalité! -Il fallait faire cela! il n'avait rien fait s'il ne faisait pas cela! -toute sa vie était inutile, toute sa pénitence était perdue, et il n'y -avait plus qu'à dire: à quoi bon? Il sentait que l'évêque était là, que -l'évêque était d'autant plus présent qu'il était mort, que l'évêque le -regardait fixement, que désormais le maire Madeleine avec toutes ses -vertus lui serait abominable, et que le galérien Jean Valjean serait -admirable et pur devant lui. Que les hommes voyaient son masque, mais -que l'évêque voyait sa face. Que les hommes voyaient sa vie, mais que -l'évêque voyait sa conscience. Il fallait donc aller à Arras, délivrer -le faux Jean Valjean, dénoncer le véritable! Hélas! c'était là le plus -grand des sacrifices, la plus poignante des victoires, le dernier pas à -franchir; mais il le fallait. Douloureuse destinée! il n'entrerait dans -la sainteté aux yeux de Dieu que s'il rentrait dans l'infamie aux yeux -des hommes! - ---Eh bien, dit-il, prenons ce parti! faisons notre devoir! sauvons cet -homme! - -Il prononça ces paroles à haute voix, sans s'apercevoir qu'il parlait -tout haut. - -Il prit ses livres, les vérifia et les mit en ordre. Il jeta au feu une -liasse de créances qu'il avait sur de petits commerçants gênés. Il -écrivit une lettre qu'il cacheta et sur l'enveloppe de laquelle on -aurait pu lire, s'il y avait eu quelqu'un dans sa chambre en cet -instant: _À Monsieur Laffitte, banquier, rue d'Artois, à Paris_. - -Il tira d'un secrétaire un portefeuille qui contenait quelques billets -de banque et le passeport dont il s'était servi cette même année pour -aller aux élections. - -Qui l'eût vu pendant qu'il accomplissait ces divers actes auxquels se -mêlait une méditation si grave, ne se fût pas douté de ce qui se passait -en lui. Seulement par moments ses lèvres remuaient; dans d'autres -instants il relevait la tête et fixait son regard sur un point -quelconque de la muraille, comme s'il y avait précisément là quelque -chose qu'il voulait éclaircir ou interroger. - -La lettre à M. Laffitte terminée, il la mit dans sa poche ainsi que le -portefeuille, et recommença à marcher. - -Sa rêverie n'avait point dévié. Il continuait de voir clairement son -devoir écrit en lettres lumineuses qui flamboyaient devant ses yeux et -se déplaçaient avec son regard:--_Va! nomme-toi! dénonce-toi!_ - -Il voyait de même, et comme si elles se fussent mues devant lui avec des -formes sensibles, les deux idées qui avaient été jusque-là la double -règle de sa vie: cacher son nom, sanctifier son âme. Pour la première -fois, elles lui apparaissaient absolument distinctes, et il voyait la -différence qui les séparait. Il reconnaissait que l'une de ces idées -était nécessairement bonne, tandis que l'autre pouvait devenir mauvaise; -que celle-là était le dévouement et que celle-ci était la personnalité; -que l'une disait: le _prochain_, et que l'autre disait: _moi_; que l'une -venait de la lumière et que l'autre venait de la nuit. - -Elles se combattaient, il les voyait se combattre. À mesure qu'il -songeait, elles avaient grandi devant l'oeil de son esprit; elles -avaient maintenant des statures colossales; et il lui semblait qu'il -voyait lutter au dedans de lui-même, dans cet infini dont nous parlions -tout à l'heure, au milieu des obscurités et des lueurs, une déesse et -une géante. - -Il était plein d'épouvante, mais il lui semblait que la bonne pensée -l'emportait. - -Il sentait qu'il touchait à l'autre moment décisif de sa conscience et -de sa destinée; que l'évêque avait marqué la première phase de sa vie -nouvelle, et que ce Champmathieu en marquait la seconde. Après la grande -crise, la grande épreuve. - -Cependant la fièvre, un instant apaisée, lui revenait peu à peu. Mille -pensées le traversaient, mais elles continuaient de le fortifier dans sa -résolution. - -Un moment il s'était dit:--qu'il prenait peut-être la chose trop -vivement, qu'après tout ce Champmathieu n'était pas intéressant, qu'en -somme il avait volé. - -Il se répondit:--Si cet homme a en effet volé quelques pommes, c'est un -mois de prison. Il y a loin de là aux galères. Et qui sait même? a-t-il -volé? est-ce prouvé? Le nom de Jean Valjean l'accable et semble -dispenser de preuves. Les procureurs du roi n'agissent-ils pas -habituellement ainsi? On le croit voleur, parce qu'on le sait forçat. - -Dans un autre instant, cette idée lui vint que, lorsqu'il se serait -dénoncé, peut-être on considérerait l'héroïsme de son action, et sa vie -honnête depuis sept ans, et ce qu'il avait fait pour le pays, et qu'on -lui ferait grâce. - -Mais cette supposition s'évanouit bien vite, et il sourit amèrement en -songeant que le vol des quarante sous à Petit-Gervais le faisait -récidiviste, que cette affaire reparaîtrait certainement et, aux termes -précis de la loi, le ferait passible des travaux forcés à perpétuité. - -Il se détourna de toute illusion, se détacha de plus en plus de la terre -et chercha la consolation et la force ailleurs. Il se dit qu'il fallait -faire son devoir; que peut-être même ne serait-il pas plus malheureux -après avoir fait son devoir qu'après l'avoir éludé; que s'il _laissait -faire_, s'il restait à Montreuil-sur-mer, sa considération, sa bonne -renommée, ses bonnes oeuvres, la déférence, la vénération, sa charité, -sa richesse, sa popularité, sa vertu, seraient assaisonnées d'un crime; -et quel goût auraient toutes ces choses saintes liées à cette chose -hideuse! tandis que, s'il accomplissait son sacrifice, au bagne, au -poteau, au carcan, au bonnet vert, au travail sans relâche, à la honte -sans pitié, il se mêlerait une idée céleste! - -Enfin il se dit qu'il y avait nécessité, que sa destinée était ainsi -faite, qu'il n'était pas maître de déranger les arrangements d'en haut, -que dans tous les cas il fallait choisir: ou la vertu au dehors et -l'abomination au dedans, ou la sainteté au dedans et l'infamie au -dehors. - -À remuer tant d'idées lugubres, son courage ne défaillait pas, mais son -cerveau se fatiguait. Il commençait à penser malgré lui à d'autres -choses, à des choses indifférentes. Ses artères battaient violemment -dans ses tempes. Il allait et venait toujours. Minuit sonna d'abord à la -paroisse, puis à la maison de ville. Il compta les douze coups aux deux -horloges, et il compara le son des deux cloches. Il se rappela à cette -occasion que quelques jours auparavant il avait vu chez un marchand de -ferrailles une vieille cloche à vendre sur laquelle ce nom était écrit: -_Antoine Albin de Romainville_. - -Il avait froid. Il alluma un peu de feu. Il ne songea pas à fermer la -fenêtre. - -Cependant il était retombé dans sa stupeur. Il lui fallait faire un -assez grand effort pour se rappeler à quoi il songeait avant que minuit -sonnât. Il y parvint enfin. - ---Ah! oui, se dit-il, j'avais pris la résolution de me dénoncer. - -Et puis tout à coup il pensa à la Fantine. - ---Tiens! dit-il, et cette pauvre femme! - -Ici une crise nouvelle se déclara. - -Fantine, apparaissant brusquement dans sa rêverie, y fut comme un rayon -d'une lumière inattendue. Il lui sembla que tout changeait d'aspect -autour de lui, il s'écria: - ---Ah çà, mais! jusqu'ici je n'ai considéré que moi! je n'ai eu égard -qu'à ma convenance! Il me convient de me taire ou de me -dénoncer,--cacher ma personne ou sauver mon âme,--être un magistrat -méprisable et respecté ou un galérien infâme et vénérable, c'est moi, -c'est toujours moi, ce n'est que moi! Mais, mon Dieu, c'est de l'égoïsme -tout cela! Ce sont des formes diverses de l'égoïsme, mais c'est de -l'égoïsme! Si je songeais un peu aux autres? La première sainteté est de -penser à autrui. Voyons, examinons. Moi excepté, moi effacé, moi oublié, -qu'arrivera-t-il de tout ceci?--Si je me dénonce? on me prend. On lâche -ce Champmathieu, on me remet aux galères, c'est bien. Et puis? Que se -passe-t-il ici? Ah! ici, il y a un pays, une ville, des fabriques, une -industrie, des ouvriers, des hommes, des femmes, des vieux grands-pères, -des enfants, des pauvres gens! J'ai créé tout ceci, je fais vivre tout -cela; partout où il y a une cheminée qui fume, c'est moi qui ai mis le -tison dans le feu et la viande dans la marmite; j'ai fait l'aisance, la -circulation, le crédit; avant moi il n'y avait rien; j'ai relevé, -vivifié, animé, fécondé, stimulé, enrichi tout le pays; moi de moins, -c'est l'âme de moins. Je m'ôte, tout meurt.--Et cette femme qui a tant -souffert, qui a tant de mérites dans sa chute, dont j'ai causé sans le -vouloir tout le malheur! Et cet enfant que je voulais aller chercher, -que j'ai promis à la mère! Est-ce que je ne dois pas aussi quelque chose -à cette femme, en réparation du mal que je lui ai fait? Si je disparais, -qu'arrive-t-il? La mère meurt. L'enfant devient ce qu'il peut. Voilà ce -qui se passe, si je me dénonce.--Si je ne me dénonce pas? Voyons, si je -ne me dénonce pas? Après s'être fait cette question, il s'arrêta; il eut -comme un moment d'hésitation et de tremblement; mais ce moment dura peu, -et il se répondit avec calme: - ---Eh bien, cet homme va aux galères, c'est vrai, mais, que diable! il a -volé! J'ai beau me dire qu'il n'a pas volé, il a volé! Moi, je reste -ici, je continue. Dans dix ans j'aurai gagné dix millions, je les -répands dans le pays, je n'ai rien à moi, qu'est-ce que cela me fait? Ce -n'est pas pour moi ce que je fais! La prospérité de tous va croissant, -les industries s'éveillent et s'excitent, les manufactures et les usines -se multiplient, les familles, cent familles, mille familles! sont -heureuses; la contrée se peuple; il naît des villages où il n'y a que -des fermes, il naît des fermes où il n'y a rien; la misère disparaît, et -avec la misère disparaissent la débauche, la prostitution, le vol, le -meurtre, tous les vices, tous les crimes! Et cette pauvre mère élève son -enfant! et voilà tout un pays riche et honnête! Ah çà, j'étais fou, -j'étais absurde, qu'est-ce que je parlais donc de me dénoncer? Il faut -faire attention, vraiment, et ne rien précipiter. Quoi! parce qu'il -m'aura plu de faire le grand et le généreux,--c'est du mélodrame, après -tout!--parce que je n'aurai songé qu'à moi, qu'à moi seul, quoi! pour -sauver d'une punition peut-être un peu exagérée, mais juste au fond, on -ne sait qui, un voleur, un drôle évidemment, il faudra que tout un pays -périsse! il faudra qu'une pauvre femme crève à l'hôpital! qu'une pauvre -petite fille crève sur le pavé! comme des chiens! Ah! mais c'est -abominable! Sans même que la mère ait revu son enfant! sans que l'enfant -ait presque connu sa mère! Et tout ça pour ce vieux gredin de voleur de -pommes qui, à coup sûr, a mérité les galères pour autre chose, si ce -n'est pour cela! Beaux scrupules qui sauvent un coupable et qui -sacrifient des innocents, qui sauvent un vieux vagabond, lequel n'a plus -que quelques années à vivre au bout du compte et ne sera guère plus -malheureux au bagne que dans sa masure, et qui sacrifient toute une -population, mères, femmes, enfants! Cette pauvre petite Cosette qui n'a -que moi au monde et qui est sans doute en ce moment toute bleue de froid -dans le bouge de ces Thénardier! Voilà encore des canailles ceux-là! Et -je manquerais à mes devoirs envers tous ces pauvres êtres! Et je m'en -irais me dénoncer! Et je ferais cette inepte sottise! Mettons tout au -pis. Supposons qu'il y ait une mauvaise action pour moi dans ceci et que -ma conscience me la reproche un jour, accepter, pour le bien d'autrui, -ces reproches qui ne chargent que moi, cette mauvaise action qui ne -compromet que mon âme, c'est là qu'est le dévouement, c'est là qu'est la -vertu. - -Il se leva, il se remit à marcher. Cette fois il lui semblait qu'il -était content. On ne trouve les diamants que dans les ténèbres de la -terre; on ne trouve les vérités que dans les profondeurs de la pensée. -Il lui semblait qu'après être descendu dans ces profondeurs, après avoir -longtemps tâtonné au plus noir de ces ténèbres, il venait enfin de -trouver un de ces diamants, une de ces vérités, et qu'il la tenait dans -sa main; et il s'éblouissait à la regarder. - ---Oui, pensa-t-il, c'est cela. Je suis dans le vrai. J'ai la solution. -Il faut finir par s'en tenir à quelque chose. Mon parti est pris. -Laissons faire! Ne vacillons plus, ne reculons plus. Ceci est dans -l'intérêt de tous, non dans le mien. Je suis Madeleine, je reste -Madeleine. Malheur à celui qui est Jean Valjean! Ce n'est plus moi. Je -ne connais pas cet homme, je ne sais plus ce que c'est, s'il se trouve -que quelqu'un est Jean Valjean à cette heure, qu'il s'arrange! cela ne -me regarde pas. C'est un nom de fatalité qui flotte dans la nuit, s'il -s'arrête et s'abat sur une tête, tant pis pour elle! - -Il se regarda dans le petit miroir qui était sur sa cheminée, et dit: - ---Tiens! cela m'a soulagé de prendre une résolution! Je suis tout autre -à présent. - -Il marcha encore quelques pas, puis il s'arrêta court: - ---Allons! dit-il, il ne faut hésiter devant aucune des conséquences de -la résolution prise. Il y a encore des fils qui m'attachent à ce Jean -Valjean. Il faut les briser! Il y a ici, dans cette chambre même, des -objets qui m'accuseraient, des choses muettes qui seraient des témoins, -c'est dit, il faut que tout cela disparaisse. - -Il fouilla dans sa poche, en tira sa bourse, l'ouvrit, et y prit une -petite clef. - -Il introduisit cette clef dans une serrure dont on voyait à peine le -trou, perdu qu'il était dans les nuances les plus sombres du dessin qui -couvrait le papier collé sur le mur. Une cachette s'ouvrit, une espèce -de fausse armoire ménagée entre l'angle de la muraille et le manteau de -la cheminée. Il n'y avait dans cette cachette que quelques guenilles, un -sarrau de toile bleue, un vieux pantalon, un vieux havresac, et un gros -bâton d'épine ferré aux deux bouts. Ceux qui avaient vu Jean Valjean à -l'époque où il traversait Digne, en octobre 1815, eussent aisément -reconnu toutes les pièces de ce misérable accoutrement. - -Il les avait conservées comme il avait conservé les chandeliers -d'argent, pour se rappeler toujours son point de départ. Seulement il -cachait ceci qui venait du bagne, et il laissait voir les flambeaux qui -venaient de l'évêque. - -Il jeta un regard furtif vers la porte, comme s'il eût craint qu'elle ne -s'ouvrît malgré le verrou qui la fermait; puis d'un mouvement vif et -brusque et d'une seule brassée, sans même donner un coup d'oeil à ces -choses qu'il avait si religieusement et si périlleusement gardées -pendant tant d'années, il prit tout, haillons, bâton, havresac, et jeta -tout au feu. Il referma la fausse armoire, et, redoublant de -précautions, désormais inutiles puisqu'elle était vide, en cacha la -porte derrière un gros meuble qu'il y poussa. - -Au bout de quelques secondes, la chambre et le mur d'en face furent -éclairés d'une grande réverbération rouge et tremblante. Tout brûlait. -Le bâton d'épine pétillait et jetait des étincelles jusqu'au milieu de -la chambre. - -Le havresac, en se consumant avec d'affreux chiffons qu'il contenait, -avait mis à nu quelque chose qui brillait dans la cendre. En se -penchant, on eût aisément reconnu une pièce d'argent. Sans doute la -pièce de quarante sous volée au petit savoyard. - -Lui ne regardait pas le feu et marchait, allant et venant toujours du -même pas. - -Tout à coup ses yeux tombèrent sur les deux flambeaux d'argent que la -réverbération faisait reluire vaguement sur la cheminée. - ---Tiens! pensa-t-il, tout Jean Valjean est encore là-dedans. Il faut -aussi détruire cela. - -Il prit les deux flambeaux. - -Il y avait assez de feu pour qu'on pût les déformer promptement et en -faire une sorte de lingot méconnaissable. - -Il se pencha sur le foyer et s'y chauffa un instant. Il eut un vrai -bien-être.--La bonne chaleur! dit-il. - -Il remua le brasier avec un des deux chandeliers. Une minute de plus, et -ils étaient dans le feu. En ce moment il lui sembla qu'il entendait une -voix qui criait au dedans de lui: - ---Jean Valjean! Jean Valjean! - -Ses cheveux se dressèrent, il devint comme un homme qui écoute une chose -terrible. - ---Oui, c'est cela, achève! disait la voix. Complète ce que tu fais! -détruis ces flambeaux! anéantis ce souvenir! oublie l'évêque! oublie -tout! perds ce Champmathieu! va, c'est bien. Applaudis-toi! Ainsi, c'est -convenu, c'est résolu, c'est dit, voilà un homme, voilà un vieillard qui -ne sait ce qu'on lui veut, qui n'a rien fait peut-être, un innocent, -dont ton nom fait tout le malheur, sur qui ton nom pèse comme un crime, -qui va être pris pour toi, qui va être condamné, qui va finir ses jours -dans l'abjection et dans l'horreur! c'est bien. Sois honnête homme, toi. -Reste monsieur le maire, reste honorable et honoré, enrichis la ville, -nourris des indigents, élève des orphelins, vis heureux, vertueux et -admiré, et pendant ce temps-là, pendant que tu seras ici dans la joie et -dans la lumière, il y aura quelqu'un qui aura ta casaque rouge, qui -portera ton nom dans l'ignominie et qui traînera ta chaîne au bagne! -Oui, c'est bien arrangé ainsi! Ah! misérable! - -La sueur lui coulait du front. Il attachait sur les flambeaux un oeil -hagard. Cependant ce qui parlait en lui n'avait pas fini. La voix -continuait: - ---Jean Valjean! il y aura autour de toi beaucoup de voix qui feront un -grand bruit, qui parleront bien haut, et qui te béniront, et une seule -que personne n'entendra et qui te maudira dans les ténèbres. Eh bien! -écoute, infâme! toutes ces bénédictions retomberont avant d'arriver au -ciel, et il n'y aura que la malédiction qui montera jusqu'à Dieu! Cette -voix, d'abord toute faible et qui s'était élevée du plus obscur de sa -conscience, était devenue par degrés éclatante et formidable, et il -l'entendait maintenant à son oreille. Il lui semblait qu'elle était -sortie de lui-même et qu'elle parlait à présent en dehors de lui. Il -crut entendre les dernières paroles si distinctement qu'il regarda dans -la chambre avec une sorte de terreur. - ---Y a-t-il quelqu'un ici? demanda-t-il à haute voix, et tout égaré. - -Puis il reprit avec un rire qui ressemblait au rire d'un idiot: - ---Que je suis bête! il ne peut y avoir personne. - -Il y avait quelqu'un; mais celui qui y était n'était pas de ceux que -l'oeil humain peut voir. - -Il posa les flambeaux sur la cheminée. - -Alors il reprit cette marche monotone et lugubre qui troublait dans ses -rêves et réveillait en sursaut l'homme endormi au-dessous de lui. - -Cette marche le soulageait et l'enivrait en même temps. Il semble que -parfois dans les occasions suprêmes on se remue pour demander conseil à -tout ce qu'on peut rencontrer en se déplaçant. Au bout de quelques -instants il ne savait plus où il en était. - -Il reculait maintenant avec une égale épouvante devant les deux -résolutions qu'il avait prises tour à tour. Les deux idées qui le -conseillaient lui paraissaient aussi funestes l'une que l'autre.--Quelle -fatalité! quelle rencontre que ce Champmathieu pris pour lui! Être -précipité justement par le moyen que la providence paraissait d'abord -avoir employé pour l'affermir! - -Il y eut un moment où il considéra l'avenir. Se dénoncer, grand Dieu! se -livrer! Il envisagea avec un immense désespoir tout ce qu'il faudrait -quitter, tout ce qu'il faudrait reprendre. Il faudrait donc dire adieu à -cette existence si bonne, si pure, si radieuse, à ce respect de tous, à -l'honneur, à la liberté! Il n'irait plus se promener dans les champs, il -n'entendrait plus chanter les oiseaux au mois de mai, il ne ferait plus -l'aumône aux petits enfants! Il ne sentirait plus la douceur des regards -de reconnaissance et d'amour fixés sur lui! Il quitterait cette maison -qu'il avait bâtie, cette chambre, cette petite chambre! Tout lui -paraissait charmant à cette heure. Il ne lirait plus dans ces livres, il -n'écrirait plus sur cette petite table de bois blanc! Sa vieille -portière, la seule servante qu'il eût, ne lui monterait plus son café le -matin. Grand Dieu! au lieu de cela, la chiourme, le carcan, la veste -rouge, la chaîne au pied, la fatigue, le cachot, le lit de camp, toutes -ces horreurs connues! À son âge, après avoir été ce qu'il était! Si -encore il était jeune! Mais, vieux, être tutoyé par le premier venu, -être fouillé par le garde-chiourme, recevoir le coup de bâton de -l'argousin! avoir les pieds nus dans des souliers ferrés! tendre matin -et soir sa jambe au marteau du rondier qui visite la manille! subir la -curiosité des étrangers auxquels on dirait: _Celui-là, c'est le fameux -Jean Valjean, qui a été maire à Montreuil-sur-mer_! Le soir, ruisselant -de sueur, accablé de lassitude, le bonnet vert sur les yeux, remonter -deux à deux, sous le fouet du sergent, l'escalier-échelle du bagne -flottant! Oh! quelle misère! La destinée peut-elle donc être méchante -comme un être intelligent et devenir monstrueuse comme le coeur humain! - -Et, quoi qu'il fît, il retombait toujours sur ce poignant dilemme qui -était au fond de sa rêverie:--rester dans le paradis, et y devenir -démon! rentrer dans l'enfer, et y devenir ange! - -Que faire, grand Dieu! que faire? - -La tourmente dont il était sorti avec tant de peine se déchaîna de -nouveau en lui. Ses idées recommencèrent à se mêler. Elles prirent ce je -ne sais quoi de stupéfié et de machinal qui est propre au désespoir. Ce -nom de Romainville lui revenait sans cesse à l'esprit avec deux vers -d'une chanson qu'il avait entendue autrefois. Il songeait que -Romainville est un petit bois près Paris où les jeunes gens amoureux -vont cueillir des lilas au mois d'avril. - -Il chancelait au dehors comme au dedans. Il marchait comme un petit -enfant qu'on laisse aller seul. - -À de certains moments, luttant contre sa lassitude, il faisait effort -pour ressaisir son intelligence. Il tâchait de se poser une dernière -fois, et définitivement, le problème sur lequel il était en quelque -sorte tombé d'épuisement. Faut-il se dénoncer? Faut-il se taire?--Il ne -réussissait à rien voir de distinct. Les vagues aspects de tous les -raisonnements ébauchés par sa rêverie tremblaient et se dissipaient l'un -après l'autre en fumée. Seulement il sentait que, à quelque parti qu'il -s'arrêtât, nécessairement, et sans qu'il fût possible d'y échapper, -quelque chose de lui allait mourir; qu'il entrait dans un sépulcre à -droite comme à gauche; qu'il accomplissait une agonie, l'agonie de son -bonheur ou l'agonie de sa vertu. - -Hélas! toutes ses irrésolutions l'avaient repris. Il n'était pas plus -avancé qu'au commencement. - -Ainsi se débattait sous l'angoisse cette malheureuse âme. Dix-huit cents -ans avant cet homme infortuné, l'être mystérieux, en qui se résument -toutes les saintetés et toutes les souffrances de l'humanité, avait -aussi lui, pendant que les oliviers frémissaient au vent farouche de -l'infini, longtemps écarté de la main l'effrayant calice qui lui -apparaissait ruisselant d'ombre et débordant de ténèbres dans des -profondeurs pleines d'étoiles. - - - - -Chapitre IV - -Formes que prend la souffrance pendant le sommeil - - -Trois heures du matin venaient de sonner, et il y avait cinq heures -qu'il marchait ainsi, presque sans interruption lorsqu'il se laissa -tomber sur sa chaise. - -Il s'y endormit et fit un rêve. - -Ce rêve, comme la plupart des rêves, ne se rapportait à la situation que -par je ne sais quoi de funeste et de poignant, mais il lui fit -impression. Ce cauchemar le frappa tellement que plus tard il l'a écrit. -C'est un des papiers écrits de sa main qu'il a laissés. Nous croyons -devoir transcrire ici cette chose textuellement. - -Quel que soit ce rêve, l'histoire de cette nuit serait incomplète si -nous l'omettions. C'est la sombre aventure d'une âme malade. - -Le voici. Sur l'enveloppe nous trouvons cette ligne écrite: _Le rêve que -j'ai eu cette nuit-là._ - -«J'étais dans une campagne. Une grande campagne triste où il n'y avait -pas d'herbe. Il ne me semblait pas qu'il fît jour ni qu'il fît nuit. - -«Je me promenais avec mon frère, le frère de mes années d'enfance, ce -frère auquel je dois dire que je ne pense jamais et dont je ne me -souviens presque plus. - -«Nous causions, et nous rencontrions des passants. Nous parlions d'une -voisine que nous avions eue autrefois, et qui, depuis qu'elle demeurait -sur la rue, travaillait la fenêtre toujours ouverte. Tout en causant, -nous avions froid à cause de cette fenêtre ouverte. - -«Il n'y avait pas d'arbres dans la campagne. - -«Nous vîmes un homme qui passa près de nous. C'était un homme tout nu, -couleur de cendre, monté sur un cheval couleur de terre. L'homme n'avait -pas de cheveux; on voyait son crâne et des veines sur son crâne. Il -tenait à la main une baguette qui était souple comme un sarment de vigne -et lourde comme du fer. Ce cavalier passa et ne nous dit rien. - -«Mon frère me dit: Prenons par le chemin creux. - -«Il y avait un chemin creux où l'on ne voyait pas une broussaille ni un -brin de mousse. Tout était couleur de terre, même le ciel. Au bout de -quelques pas, on ne me répondit plus quand je parlais. Je m'aperçus que -mon frère n'était plus avec moi. - -«J'entrai dans un village que je vis. Je songeai que ce devait être là -Romainville (pourquoi Romainville?). - -«La première rue où j'entrai était déserte. J'entrai dans une seconde -rue. Derrière l'angle que faisaient les deux rues, il y avait un homme -debout contre le mur. Je dis à cet homme:--Quel est ce pays? où suis-je? -L'homme ne répondit pas. Je vis la porte d'une maison ouverte, j'y -entrai. - -«La première chambre était déserte. J'entrai dans la seconde. Derrière -la porte de cette chambre, il y avait un homme debout contre le mur. Je -demandai à cet homme:--À qui est cette maison? où suis-je? L'homme ne -répondit pas. La maison avait un jardin. - -«Je sortis de la maison et j'entrai dans le jardin. Le jardin était -désert. Derrière le premier arbre, je trouvai un homme qui se tenait -debout. Je dis à cet homme:--Quel est ce jardin? où suis-je? L'homme ne -répondit pas. - -«J'errai dans le village, et je m'aperçus que c'était une ville. Toutes -les rues étaient désertes, toutes les portes étaient ouvertes. Aucun -être vivant ne passait dans les rues, ne marchait dans les chambres ou -ne se promenait dans les jardins. Mais il y avait derrière chaque angle -de mur, derrière chaque porte, derrière chaque arbre, un homme debout -qui se taisait. On n'en voyait jamais qu'un à la fois. Ces hommes me -regardaient passer. - -«Je sortis de la ville et je me mis à marcher dans les champs. - -«Au bout de quelque temps, je me retournai, et je vis une grande foule -qui venait derrière moi. Je reconnus tous les hommes que j'avais vus -dans la ville. Ils avaient des têtes étranges. Ils ne semblaient pas se -hâter, et cependant ils marchaient plus vite que moi. Ils ne faisaient -aucun bruit en marchant. En un instant, cette foule me rejoignit et -m'entoura. Les visages de ces hommes étaient couleur de terre. - -«Alors le premier que j'avais vu et questionné en entrant dans la ville -me dit:--Où allez-vous? Est-ce que vous ne savez pas que vous êtes mort -depuis longtemps? - -«J'ouvris la bouche pour répondre, et je m'aperçus qu'il n'y avait -personne autour de moi.» - -Il se réveilla. Il était glacé. Un vent qui était froid comme le vent du -matin faisait tourner dans leurs gonds les châssis de la croisée restée -ouverte. Le feu s'était éteint. La bougie touchait à sa fin. Il était -encore nuit noire. - -Il se leva, il alla à la fenêtre. Il n'y avait toujours pas d'étoiles au -ciel. - -De sa fenêtre on voyait la cour de la maison et la rue. Un bruit sec et -dur qui résonna tout à coup sur le sol lui fit baisser les yeux. - -Il vit au-dessous de lui deux étoiles rouges dont les rayons -s'allongeaient et se raccourcissaient bizarrement dans l'ombre. - -Comme sa pensée était encore à demi submergée dans la brume des -rêves.--tiens! songea-t-il, il n'y en a pas dans le ciel. Elles sont sur -la terre maintenant. - -Cependant ce trouble se dissipa, un second bruit pareil au premier -acheva de le réveiller; il regarda, et il reconnut que ces deux étoiles -étaient les lanternes d'une voiture. À la clarté qu'elles jetaient, il -put distinguer la forme de cette voiture. C'était un tilbury attelé d'un -petit cheval blanc. Le bruit qu'il avait entendu, c'étaient les coups de -pied du cheval sur le pavé. - ---Qu'est-ce que c'est que cette voiture? se dit-il. Qui est-ce qui vient -donc si matin? En ce moment on frappa un petit coup à la porte de sa -chambre. - -Il frissonna de la tête aux pieds, et cria d'une voix terrible: - ---Qui est là? - -Quelqu'un répondit: - ---Moi, monsieur le maire. - -Il reconnut la voix de la vieille femme, sa portière. - ---Eh bien, reprit-il, qu'est-ce que c'est? - ---Monsieur le maire, il est tout à l'heure cinq heures du matin. - ---Qu'est-ce que cela me fait? - ---Monsieur le maire, c'est le cabriolet. - ---Quel cabriolet? - ---Le tilbury. - ---Quel tilbury? - ---Est-ce que monsieur le maire n'a pas fait demander un tilbury? - ---Non, dit-il. - ---Le cocher dit qu'il vient chercher monsieur le maire. - ---Quel cocher? - ---Le cocher de M. Scaufflaire. - ---M. Scaufflaire? - -Ce nom le fit tressaillir comme si un éclair lui eût passé devant la -face. - ---Ah! oui! reprit-il, M. Scaufflaire. - -Si la vieille femme l'eût pu voir en ce moment, elle eût été épouvantée. - -Il se fit un assez long silence. Il examinait d'un air stupide la flamme -de la bougie et prenait autour de la mèche de la cire brûlante qu'il -roulait dans ses doigts. - -La vieille attendait. Elle se hasarda pourtant à élever encore la voix: - ---Monsieur le maire, que faut-il que je réponde? - ---Dites que c'est bien, et que je descends. - - - - -Chapitre V - -Bâtons dans les roues - - -Le service des postes d'Arras à Montreuil-sur-mer se faisait encore à -cette époque par de petites malles du temps de l'empire. Ces malles -étaient des cabriolets à deux roues, tapissés de cuir fauve au dedans, -suspendus sur des ressorts à pompe, et n'ayant que deux places, l'une -pour le courrier, l'autre pour le voyageur. Les roues étaient armées de -ces longs moyeux offensifs qui tiennent les autres voitures à distance -et qu'on voit encore sur les routes d'Allemagne. Le coffre aux dépêches, -immense boîte oblongue, était placé derrière le cabriolet et faisait -corps avec lui. Ce coffre était peint en noir et le cabriolet en jaune. - -Ces voitures, auxquelles rien ne ressemble aujourd'hui, avaient je ne -sais quoi de difforme et de bossu, et, quand on les voyait passer de -loin et ramper dans quelque route à l'horizon, elles ressemblaient à ces -insectes qu'on appelle, je crois, termites, et qui, avec un petit -corsage, traînent un gros arrière-train. Elles allaient, du reste, fort -vite. La malle partie d'Arras toutes les nuits à une heure, après le -passage du courrier de Paris, arrivait à Montreuil-sur-mer un peu avant -cinq heures du matin. - -Cette nuit-là, la malle qui descendait à Montreuil-sur-mer par la route -de Hesdin accrocha, au tournant d'une rue, au moment où elle entrait -dans la ville, un petit tilbury attelé d'un cheval blanc, qui venait en -sens inverse et dans lequel il n'y avait qu'une personne, un homme -enveloppé d'un manteau. La roue du tilbury reçut un choc assez rude. Le -courrier cria à cet homme d'arrêter, mais le voyageur n'écouta pas, et -continua sa route au grand trot. - ---Voilà un homme diablement pressé! dit le courrier. - -L'homme qui se hâtait ainsi, c'est celui que nous venons de voir se -débattre dans des convulsions dignes à coup sûr de pitié. - -Où allait-il? Il n'eût pu le dire. Pourquoi se hâtait-il? Il ne savait. -Il allait au hasard devant lui. Où? À Arras sans doute; mais il allait -peut-être ailleurs aussi. Par moments il le sentait, et il tressaillait. - -Il s'enfonçait dans cette nuit comme dans un gouffre. Quelque chose le -poussait, quelque chose l'attirait. Ce qui se passait en lui, personne -ne pourrait le dire, tous le comprendront. Quel homme n'est entré, au -moins une fois en sa vie, dans cette obscure caverne de l'inconnu? - -Du reste il n'avait rien résolu, rien décidé, rien arrêté, rien fait. -Aucun des actes de sa conscience n'avait été définitif. Il était plus -que jamais comme au premier moment. Pourquoi allait-il à Arras? - -Il se répétait ce qu'il s'était déjà dit en retenant le cabriolet de -Scaufflaire,--que, quel que dût être le résultat, il n'y avait aucun -inconvénient à voir de ses yeux, à juger les choses par lui-même;--que -cela même était prudent, qu'il fallait savoir ce qui se passerait; qu'on -ne pouvait rien décider sans avoir observé et scruté;--que de loin on se -faisait des montagnes de tout; qu'au bout du compte, lorsqu'il aurait vu -ce Champmathieu, quelque misérable, sa conscience serait probablement -fort soulagée de le laisser aller au bagne à sa place;--qu'à la vérité -il y aurait là Javert, et ce Brevet, ce Chenildieu, ce Cochepaille, -anciens forçats qui l'avaient connu; mais qu'à coup sûr ils ne le -reconnaîtraient pas;--bah! quelle idée!--que Javert en était à cent -lieues;--que toutes les conjectures et toutes les suppositions étaient -fixées sur ce Champmathieu, et que rien n'est entêté comme les -suppositions et les conjectures;--qu'il n'y avait donc aucun danger. Que -sans doute c'était un moment noir, mais qu'il en sortirait;--qu'après -tout il tenait sa destinée, si mauvaise qu'elle voulût être, dans sa -main;--qu'il en était le maître. Il se cramponnait à cette pensée. - -Au fond, pour tout dire, il eût mieux aimé ne point aller à Arras. - -Cependant il y allait. - -Tout en songeant, il fouettait le cheval, lequel trottait de ce bon trot -réglé et sûr qui fait deux lieues et demie à l'heure. - -À mesure que le cabriolet avançait, il sentait quelque chose en lui qui -reculait. - -Au point du jour il était en rase campagne; la ville de -Montreuil-sur-mer était assez loin derrière lui. Il regarda l'horizon -blanchir; il regarda, sans les voir, passer devant ses yeux toutes les -froides figures d'une aube d'hiver. Le matin a ses spectres comme le -soir. Il ne les voyait pas, mais, à son insu, et par une sorte de -pénétration presque physique, ces noires silhouettes d'arbres et de -collines ajoutaient à l'état violent de son âme je ne sais quoi de morne -et de sinistre. - -Chaque fois qu'il passait devant une de ces maisons isolées qui côtoient -parfois les routes, il se disait: il y a pourtant là-dedans des gens qui -dorment! - -Le trot du cheval, les grelots du harnais, les roues sur le pavé, -faisaient un bruit doux et monotone. Ces choses-là sont charmantes quand -on est joyeux et lugubres quand on est triste. Il était grand jour -lorsqu'il arriva à Hesdin. Il s'arrêta devant une auberge pour laisser -souffler le cheval et lui faire donner l'avoine. - -Ce cheval était, comme l'avait dit Scaufflaire, de cette petite race du -Boulonnais qui a trop de tête, trop de ventre et pas assez d'encolure, -mais qui a le poitrail ouvert, la croupe large, la jambe sèche et fine -et le pied solide; race laide, mais robuste et saine. L'excellente bête -avait fait cinq lieues en deux heures et n'avait pas une goutte de sueur -sur la croupe. - -Il n'était pas descendu du tilbury. Le garçon d'écurie qui apportait -l'avoine se baissa tout à coup et examina la roue de gauche. - ---Allez-vous loin comme cela? dit cet homme. - -Il répondit, presque sans sortir de sa rêverie: - ---Pourquoi? - ---Venez-vous de loin? reprit le garçon. - ---De cinq lieues d'ici. - ---Ah! - ---Pourquoi dites-vous: ah? - -Le garçon se pencha de nouveau, resta un moment silencieux, l'oeil fixé -sur la roue, puis se redressa en disant: - ---C'est que voilà une roue qui vient de faire cinq lieues, c'est -possible, mais qui à coup sûr ne fera pas maintenant un quart de lieue. - -Il sauta à bas du tilbury. - ---Que dites-vous là, mon ami? - ---Je dis que c'est un miracle que vous ayez fait cinq lieues sans -rouler, vous et votre cheval, dans quelque fossé de la grande route. -Regardez plutôt. - -La roue en effet était gravement endommagée. Le choc de la malle-poste -avait fendu deux rayons et labouré le moyeu dont l'écrou ne tenait plus. - ---Mon ami, dit-il au garçon d'écurie, il y a un charron ici? - ---Sans doute, monsieur. - ---Rendez-moi le service de l'aller chercher. - ---Il est là, à deux pas. Hé! maître Bourgaillard! - -Maître Bourgaillard, le charron, était sur le seuil de sa porte. Il vint -examiner la roue et fit la grimace d'un chirurgien qui considère une -jambe cassée. - ---Pouvez-vous raccommoder cette roue sur-le-champ? - ---Oui, monsieur. - ---Quand pourrai-je repartir? - ---Demain. - ---Demain! - ---Il y a une grande journée d'ouvrage. Est-ce que monsieur est pressé? - ---Très pressé. Il faut que je reparte dans une heure au plus tard. - ---Impossible, monsieur. - ---Je payerai tout ce qu'on voudra. - ---Impossible. - ---Eh bien! dans deux heures. - ---Impossible pour aujourd'hui. Il faut refaire deux rais et un moyeu. -Monsieur ne pourra repartir avant demain. - ---L'affaire que j'ai ne peut attendre à demain. Si, au lieu de -raccommoder cette roue, on la remplaçait? - ---Comment cela? - ---Vous êtes charron? - ---Sans doute, monsieur. - ---Est-ce que vous n'auriez pas une roue à me vendre? Je pourrais -repartir tout de suite. - ---Une roue de rechange? - ---Oui. - ---Je n'ai pas une roue toute faite pour votre cabriolet. Deux roues font -la paire. Deux roues ne vont pas ensemble au hasard. - ---En ce cas, vendez-moi une paire de roues. - ---Monsieur, toutes les roues ne vont pas à tous les essieux. - ---Essayez toujours. - ---C'est inutile, monsieur. Je n'ai à vendre que des roues de charrette. -Nous sommes un petit pays ici. - ---Auriez-vous un cabriolet à me louer? - -Le maître charron, du premier coup d'oeil, avait reconnu que le tilbury -était une voiture de louage. Il haussa les épaules. - ---Vous les arrangez bien, les cabriolets qu'on vous loue! j'en aurais un -que je ne vous le louerais pas. - ---Eh bien, à me vendre? - ---Je n'en ai pas. - ---Quoi! pas une carriole? Je ne suis pas difficile, comme vous voyez. - ---Nous sommes un petit pays. J'ai bien là sous la remise, ajouta le -charron, une vieille calèche qui est à un bourgeois de la ville qui me -l'a donnée en garde et qui s'en sert tous les trente-six du mois. Je -vous la louerais bien, qu'est-ce que cela me fait? mais il ne faudrait -pas que le bourgeois la vît passer; et puis, c'est une calèche, il -faudrait deux chevaux. - ---Je prendrai des chevaux de poste. - ---Où va monsieur? - ---À Arras. - ---Et monsieur veut arriver aujourd'hui? - ---Mais oui. - ---En prenant des chevaux de poste? - ---Pourquoi pas? - ---Est-il égal à monsieur d'arriver cette nuit à quatre heures du matin? - ---Non certes. - ---C'est que, voyez-vous bien, il y a une chose à dire, en prenant des -chevaux de poste.... - ---Monsieur a son passeport? - ---Oui. - ---Eh bien, en prenant des chevaux de poste, monsieur n'arrivera pas à -Arras avant demain. Nous sommes un chemin de traverse. Les relais sont -mal servis, les chevaux sont aux champs. C'est la saison des grandes -charrues qui commence, il faut de forts attelages, et l'on prend les -chevaux partout, à la poste comme ailleurs. Monsieur attendra au moins -trois ou quatre heures à chaque relais. Et puis on va au pas. Il y a -beaucoup de côtes à monter. - ---Allons, j'irai à cheval. Dételez le cabriolet. On me vendra bien une -selle dans le pays. - ---Sans doute. Mais ce cheval-ci endure-t-il la selle? - ---C'est vrai, vous m'y faites penser. Il ne l'endure pas. - ---Alors.... - ---Mais je trouverai bien dans le village un cheval à louer? - ---Un cheval pour aller à Arras d'une traite! - ---Oui. - ---Il faudrait un cheval comme on n'en a pas dans nos endroits. Il -faudrait l'acheter d'abord, car on ne vous connaît pas. Mais ni à vendre -ni à louer, ni pour cinq cents francs, ni pour mille, vous ne le -trouveriez pas! - ---Comment faire? - ---Le mieux, là, en honnête homme, c'est que je raccommode la roue et que -vous remettiez votre voyage à demain. - ---Demain il sera trop tard. - ---Dame! - ---N'y a-t-il pas la malle-poste qui va à Arras? Quand passe-t-elle? - ---La nuit prochaine. Les deux malles font le service la nuit, celle qui -monte comme celle qui descend. - ---Comment! il vous faut une journée pour raccommoder cette roue? - ---Une journée, et une bonne! - ---En mettant deux ouvriers? - ---En en mettant dix! - ---Si on liait les rayons avec des cordes? - ---Les rayons, oui; le moyeu, non. Et puis la jante aussi est en mauvais -état. - ---Y a-t-il un loueur de voitures dans la ville? - ---Non. - ---Y a-t-il un autre charron? - -Le garçon d'écurie et le maître charron répondirent en même temps en -hochant la tête. - ---Non. - -Il sentit une immense joie. - -Il était évident que la providence s'en mêlait. C'était elle qui avait -brisé la roue du tilbury et qui l'arrêtait en route. Il ne s'était pas -rendu à cette espèce de première sommation; il venait de faire tous les -efforts possibles pour continuer son voyage; il avait loyalement et -scrupuleusement épuisé tous les moyens; il n'avait reculé ni devant la -saison, ni devant la fatigue, ni devant la dépense; il n'avait rien à se -reprocher. S'il n'allait pas plus loin, cela ne le regardait plus. Ce -n'était plus sa faute, c'était, non le fait de sa conscience, mais le -fait de la providence. - -Il respira. Il respira librement et à pleine poitrine pour la première -fois depuis la visite de Javert. Il lui semblait que le poignet de fer -qui lui serrait le coeur depuis vingt heures venait de le lâcher. - -Il lui paraissait que maintenant Dieu était pour lui, et se déclarait. - -Il se dit qu'il avait fait tout ce qu'il pouvait, et qu'à présent il -n'avait qu'à revenir sur ses pas, tranquillement. - -Si sa conversation avec le charron eût eu lieu dans une chambre de -l'auberge, elle n'eût point eu de témoins, personne ne l'eût entendue, -les choses en fussent restées là, et il est probable que nous n'aurions -eu à raconter aucun des événements qu'on va lire; mais cette -conversation s'était faite dans la rue. Tout colloque dans la rue -produit inévitablement un cercle. Il y a toujours des gens qui ne -demandent qu'à être spectateurs. Pendant qu'il questionnait le charron, -quelques allants et venants s'étaient arrêtés autour d'eux. Après avoir -écouté pendant quelques minutes, un jeune garçon, auquel personne -n'avait pris garde, s'était détaché du groupe en courant. - -Au moment où le voyageur, après la délibération intérieure que nous -venons d'indiquer, prenait la résolution de rebrousser chemin, cet -enfant revenait. Il était accompagné d'une vieille femme. - ---Monsieur, dit la femme, mon garçon me dit que vous avez envie de louer -un cabriolet. Cette simple parole, prononcée par une vieille femme que -conduisait un enfant, lui fit ruisseler la sueur dans les reins. Il crut -voir la main qui l'avait lâché reparaître dans l'ombre derrière lui, -toute prête à le reprendre. - -Il répondit: - ---Oui, bonne femme, je cherche un cabriolet à louer. - -Et il se hâta d'ajouter: - ---Mais il n'y en a pas dans le pays. - ---Si fait, dit la vieille. - ---Où ça donc? reprit le charron. - ---Chez moi, répliqua la vieille. - -Il tressaillit. La main fatale l'avait ressaisi. - -La vieille avait en effet sous un hangar une façon de carriole en osier. -Le charron et le garçon d'auberge, désolés que le voyageur leur -échappât, intervinrent. - ---C'était une affreuse guimbarde,--cela était posé à cru sur -l'essieu,--il est vrai que les banquettes étaient suspendues à -l'intérieur avec des lanières de cuir,--il pleuvait dedans,--les roues -étaient rouillées et rongées d'humidité,--cela n'irait pas beaucoup plus -loin que le tilbury,--une vraie patache!--Ce monsieur aurait bien tort -de s'y embarquer,--etc., etc. - -Tout cela était vrai, mais cette guimbarde, cette patache, cette chose, -quelle qu'elle fût, roulait sur ses deux roues et pouvait aller à Arras. - -Il paya ce qu'on voulut, laissa le tilbury à réparer chez le charron -pour l'y retrouver à son retour, fit atteler le cheval blanc à la -carriole, y monta, et reprit la route qu'il suivait depuis le matin. - -Au moment où la carriole s'ébranla, il s'avoua qu'il avait eu l'instant -d'auparavant une certaine joie de songer qu'il n'irait point où il -allait. Il examina cette joie avec une sorte de colère et la trouva -absurde. Pourquoi de la joie à revenir en arrière? Après tout, il -faisait ce voyage librement. Personne ne l'y forçait. Et, certainement, -rien n'arriverait que ce qu'il voudrait bien. - -Comme il sortait de Hesdin, il entendit une voix qui lui criait: -arrêtez! arrêtez! Il arrêta la carriole d'un mouvement vif dans lequel -il y avait encore je ne sais quoi de fébrile et de convulsif qui -ressemblait à de l'espérance. - -C'était le petit garçon de la vieille. - ---Monsieur, dit-il, c'est moi qui vous ai procuré la carriole. - ---Eh bien! - ---Vous ne m'avez rien donné. - -Lui qui donnait à tous et si facilement, il trouva cette prétention -exorbitante et presque odieuse. - ---Ah! c'est toi, drôle? dit-il, tu n'auras rien! - -Il fouetta le cheval et repartit au grand trot. - -Il avait perdu beaucoup de temps à Hesdin, il eût voulu le rattraper. Le -petit cheval était courageux et tirait comme deux; mais on était au mois -de février, il avait plu, les routes étaient mauvaises. Et puis, ce -n'était plus le tilbury. La carriole était dure et très lourde. Avec -cela force montées. - -Il mit près de quatre heures pour aller de Hesdin à Saint-Pol. Quatre -heures pour cinq lieues. - -À Saint-Pol il détela à la première auberge venue, et fit mener le -cheval à l'écurie. Comme il l'avait promis à Scaufflaire, il se tint -près du râtelier pendant que le cheval mangeait. Il songeait à des -choses tristes et confuses. - -La femme de l'aubergiste entre dans l'écurie. - ---Est-ce que monsieur ne veut pas déjeuner? - ---Tiens, c'est vrai, dit-il, j'ai même bon appétit. Il suivit cette -femme qui avait une figure fraîche et réjouie. Elle le conduisit dans -une salle basse où il y avait des tables ayant pour nappes des toiles -cirées. - ---Dépêchez-vous, reprit-il, il faut que je reparte. Je suis pressé. - -Une grosse servante flamande mit son couvert en toute hâte. Il regardait -cette fille avec un sentiment de bien-être. - ---C'est là ce que j'avais, pensa-t-il. Je n'avais pas déjeuné. - -On le servit. Il se jeta sur le pain, mordit une bouchée, puis le reposa -lentement sur la table et n'y toucha plus. - -Un routier mangeait à une autre table. Il dit à cet homme: - ---Pourquoi leur pain est-il donc si amer? - -Le routier était allemand et n'entendit pas. - -Il retourna dans l'écurie près du cheval. - -Une heure après, il avait quitté Saint-Pol et se dirigeait vers Tinques -qui n'est qu'à cinq lieues d'Arras. - -Que faisait-il pendant ce trajet? À quoi pensait-il? Comme le matin, il -regardait passer les arbres, les toits de chaume, les champs cultivés, -et les évanouissements du paysage qui se disloque à chaque coude du -chemin. C'est là une contemplation qui suffit quelquefois à l'âme et qui -la dispense presque de penser. Voir mille objets pour la première et -pour la dernière fois, quoi de plus mélancolique et de plus profond! -Voyager, c'est naître et mourir à chaque instant. Peut-être, dans la -région la plus vague de son esprit, faisait-il des rapprochements entre -ces horizons changeants et l'existence humaine. Toutes les choses de la -vie sont perpétuellement en fuite devant nous. Les obscurcissements et -les clartés s'entremêlent: après un éblouissement, une éclipse; on -regarde, on se hâte, on tend les mains pour saisir ce qui passe; chaque -événement est un tournant de la route; et tout à coup on est vieux. On -sent comme une secousse, tout est noir, on distingue une porte obscure, -ce sombre cheval de la vie qui vous traînait s'arrête, et l'on voit -quelqu'un de voilé et d'inconnu qui le dételle dans les ténèbres. - -Le crépuscule tombait au moment où des enfants qui sortaient de l'école -regardèrent ce voyageur entrer dans Tinques. Il est vrai qu'on était -encore aux jours courts de l'année. Il ne s'arrêta pas à Tinques. Comme -il débouchait du village, un cantonnier qui empierrait la route dressa -la tête et dit: - ---Voilà un cheval bien fatigué. - -La pauvre bête en effet n'allait plus qu'au pas. - ---Est-ce que vous allez à Arras? ajouta le cantonnier. - ---Oui. - ---Si vous allez de ce train, vous n'y arriverez pas de bonne heure. - -Il arrêta le cheval et demanda au cantonnier: - ---Combien y a-t-il encore d'ici à Arras? - ---Près de sept grandes lieues. - ---Comment cela? le livre de poste ne marque que cinq lieues et un quart. - ---Ah! reprit le cantonnier, vous ne savez donc pas que la route est en -réparation? Vous allez la trouver coupée à un quart d'heure d'ici. Pas -moyen d'aller plus loin. - ---Vraiment. - ---Vous prendrez à gauche, le chemin qui va à Carency, vous passerez la -rivière; et, quand vous serez à Camblin, vous tournerez à droite; c'est -la route de Mont-Saint-Éloy qui va à Arras. - ---Mais voilà la nuit, je me perdrai. - ---Vous n'êtes pas du pays? - ---Non. - ---Avec ça, c'est tout chemins de traverse. Tenez, Monsieur, reprit le -cantonnier, voulez-vous que je vous donne un conseil? Votre cheval est -las, rentrez dans Tinques. Il y a une bonne auberge. Couchez-y. Vous -irez demain à Arras. - ---Il faut que j'y sois ce soir. - ---C'est différent. Alors allez tout de même à cette auberge et prenez-y -un cheval de renfort. Le garçon du cheval vous guidera dans la traverse. - -Il suivit le conseil du cantonnier, rebroussa chemin, et une demi-heure -après il repassait au même endroit, mais au grand trot, avec un bon -cheval de renfort. Un garçon d'écurie qui s'intitulait postillon était -assis sur le brancard de la carriole. - -Cependant il sentait qu'il perdait du temps. - -Il faisait tout à fait nuit. - -Ils s'engagèrent dans la traverse. La route devint affreuse. La carriole -tombait d'une ornière dans l'autre. Il dit au postillon: - ---Toujours au trot, et double pourboire. - -Dans un cahot le palonnier cassa. - ---Monsieur, dit le postillon, voilà le palonnier cassé, je ne sais plus -comment atteler mon cheval, cette route-ci est bien mauvaise la nuit; si -vous vouliez revenir coucher à Tinques, nous pourrions être demain matin -de bonne heure à Arras. - -Il répondit: - ---As-tu un bout de corde et un couteau? - ---Oui, monsieur. - -Il coupa une branche d'arbre et en fit un palonnier. - -Ce fut encore une perte de vingt minutes; mais ils repartirent au galop. - -La plaine était ténébreuse. Des brouillards bas, courts et noirs -rampaient sur les collines et s'en arrachaient comme des fumées. Il y -avait des lueurs blanchâtres dans les nuages. Un grand vent qui venait -de la mer faisait dans tous les coins de l'horizon le bruit de quelqu'un -qui remue des meubles. Tout ce qu'on entrevoyait avait des attitudes de -terreur. Que de choses frissonnent sous ces vastes souffles de la nuit! - -Le froid le pénétrait. Il n'avait pas mangé depuis la veille. Il se -rappelait vaguement son autre course nocturne dans la grande plaine aux -environs de Digne. Il y avait huit ans; et cela lui semblait hier. - -Une heure sonna à quelque clocher lointain. Il demanda au garçon: - ---Quelle est cette heure? - ---Sept heures, monsieur. Nous serons à Arras à huit. Nous n'avons plus -que trois lieues. En ce moment il fit pour la première fois cette -réflexion--en trouvant étrange qu'elle ne lui fût pas venue plus -tôt--que c'était peut-être inutile, toute la peine qu'il prenait; qu'il -ne savait seulement pas l'heure du procès; qu'il aurait dû au moins s'en -informer; qu'il était extravagant d'aller ainsi devant soi sans savoir -si cela servirait à quelque chose.--Puis il ébaucha quelques calculs -dans son esprit:--qu'ordinairement les séances des cours d'assises -commençaient à neuf heures du matin;--que cela ne devait pas être long, -cette affaire-là;--que le vol de pommes, ce serait très court;--qu'il -n'y aurait plus ensuite qu'une question d'identité;--quatre ou cinq -dépositions, peu de chose à dire pour les avocats;--qu'il allait -arriver lorsque tout serait fini! - -Le postillon fouettait les chevaux. Ils avaient passé la rivière et -laissé derrière eux Mont-Saint-Éloy. - -La nuit devenait de plus en plus profonde. - - - - -Chapitre VI - -La soeur Simplice mise à l'épreuve - - -Cependant, en ce moment-là même, Fantine était dans la joie. - -Elle avait passé une très mauvaise nuit. Toux affreuse, redoublement de -fièvre; elle avait eu des songes. Le matin, à la visite du médecin, elle -délirait. Il avait eu l'air alarmé et avait recommandé qu'on le prévînt -dès que M. Madeleine viendrait. - -Toute la matinée elle fut morne, parla peu, et fit des plis à ses draps -en murmurant à voix basse des calculs qui avaient l'air d'être des -calculs de distances. Ses yeux étaient caves et fixes. Ils paraissaient -presque éteints, et puis, par moments, ils se rallumaient et -resplendissaient comme des étoiles. Il semble qu'aux approches d'une -certaine heure sombre, la clarté du ciel emplisse ceux que quitte la -clarté de la terre. - -Chaque fois que la soeur Simplice lui demandait comment elle se -trouvait, elle répondait invariablement: - ---Bien. Je voudrais voir monsieur Madeleine. - -Quelques mois auparavant, à ce moment où Fantine venait de perdre sa -dernière pudeur, sa dernière honte et sa dernière joie, elle était -l'ombre d'elle-même; maintenant elle en était le spectre. Le mal -physique avait complété l'oeuvre du mal moral. Cette créature de -vingt-cinq ans avait le front ridé, les joues flasques, les narines -pincées, les dents déchaussées, le teint plombé, le cou osseux, les -clavicules saillantes, les membres chétifs, la peau terreuse, et ses -cheveux blonds poussaient mêlés de cheveux gris. Hélas! comme la maladie -improvise la vieillesse! À midi, le médecin revint, il fit quelques -prescriptions, s'informa si M. le maire avait paru à l'infirmerie, et -branla la tête. - -M. Madeleine venait d'habitude à trois heures voir la malade. Comme -l'exactitude était de la bonté, il était exact. - -Vers deux heures et demie, Fantine commença à s'agiter. Dans l'espace de -vingt minutes, elle demanda plus de dix fois à la religieuse: - ---Ma soeur, quelle heure est-il? - -Trois heures sonnèrent. Au troisième coup, Fantine se dressa sur son -séant, elle qui d'ordinaire pouvait à peine remuer dans son lit; elle -joignit dans une sorte d'étreinte convulsive ses deux mains décharnées -et jaunes, et la religieuse entendit sortir de sa poitrine un de ces -soupirs profonds qui semblent soulever un accablement. Puis Fantine se -tourna et regarda la porte. - -Personne n'entra; la porte ne s'ouvrit point. - -Elle resta ainsi un quart d'heure, l'oeil attaché sur la porte, immobile -et comme retenant son haleine. La soeur n'osait lui parler. L'église -sonna trois heures un quart. Fantine se laissa retomber sur l'oreiller. - -Elle ne dit rien et se remit à faire des plis à son drap. La demi-heure -passa, puis l'heure. Personne ne vint. - -Chaque fois que l'horloge sonnait, Fantine se dressait et regardait du -côté de la porte, puis elle retombait. - -On voyait clairement sa pensée, mais elle ne prononçait aucun nom, elle -ne se plaignait pas, elle n'accusait pas. Seulement elle toussait d'une -façon lugubre. On eût dit que quelque chose d'obscur s'abaissait sur -elle. Elle était livide et avait les lèvres bleues. Elle souriait par -moments. - -Cinq heures sonnèrent. Alors la soeur l'entendit qui disait très bas et -doucement: - ---Mais puisque je m'en vais demain, il a tort de ne pas venir -aujourd'hui! - -La soeur Simplice elle-même était surprise du retard de M. Madeleine. - -Cependant Fantine regardait le ciel de son lit. Elle avait l'air de -chercher à se rappeler quelque chose. Tout à coup elle se mit à chanter -d'une voix faible comme un souffle. La religieuse écouta. Voici ce que -Fantine chantait: - - _Nous achèterons de bien belles choses_ - _En nous promenant le long des faubourgs._ - _Les bleuets sont bleus, les roses sont roses,_ - _Les bleuets sont bleus, j'aime mes amours._ - _La vierge Marie auprès de mon poêle_ - _Est venue hier en manteau brodé,_ - _Et m'a dit:--Voici, caché sous mon voile,_ - _Le petit qu'un jour tu m'as demandé._ - _Courez à la ville, ayez de la toile,_ - _Achetez du fil, achetez un dé._ - _Nous achèterons de bien belles choses_ - _En nous promenant le long des faubourgs._ - _Bonne sainte Vierge, auprès de mon poêle_ - _J'ai mis un berceau de rubans orné_ - _Dieu me donnerait sa plus belle étoile,_ - _J'aime mieux l'enfant que tu m'as donné._ - --_Madame, que faire avec cette toile?_ - --_Faites un trousseau pour mon nouveau-né._ - _Les bleuets sont bleus, les roses sont roses,_ - _Les bleuets sont bleus, j'aime mes amours._ - --_Lavez cette toile._ - --_Où?_--_Dans la rivière._ - _Faites-en, sans rien gâter ni salir,_ - _Une belle jupe avec sa brassière_ - _Que je veux broder et de fleurs emplir._ - --_L'enfant n'est plus là, madame, qu'en faire?_ - --_Faites-en un drap pour m'ensevelir._ - _Nous achèterons de bien belles choses_ - _En nous promenant le long des faubourgs._ - _Les bleuets sont bleus, les roses sont roses,_ - _Les bleuets sont bleus, j'aime mes amours._ - -Cette chanson était une vieille romance de berceuse avec laquelle -autrefois elle endormait sa petite Cosette, et qui ne s'était pas -offerte à son esprit depuis cinq ans qu'elle n'avait plus son enfant. -Elle chantait cela d'une voix si triste et sur un air si doux que -c'était à faire pleurer, même une religieuse. La soeur, habituée aux -choses austères, sentit une larme lui venir. - -L'horloge sonna six heures. Fantine ne parut pas entendre. Elle semblait -ne plus faire attention à aucune chose autour d'elle. - -La soeur Simplice envoya une fille de service s'informer près de la -portière de la fabrique si M. le maire était rentré et s'il ne monterait -pas bientôt à l'infirmerie. La fille revint au bout de quelques minutes. - -Fantine était toujours immobile et paraissait attentive à des idées -qu'elle avait. - -La servante raconta très bas à la soeur Simplice que M. le maire était -parti le matin même avant six heures dans un petit tilbury attelé d'un -cheval blanc, par le froid qu'il faisait, qu'il était parti seul, pas -même de cocher, qu'on ne savait pas le chemin qu'il avait pris, que des -personnes disaient l'avoir vu tourner par la route d'Arras, que d'autres -assuraient l'avoir rencontré sur la route de Paris. Qu'en s'en allant il -avait été comme à l'ordinaire très doux, et qu'il avait seulement dit à -la portière qu'on ne l'attendît pas cette nuit. - -Pendant que les deux femmes, le dos tourné au lit de la Fantine, -chuchotaient, la soeur questionnant, la servante conjecturant, la -Fantine, avec cette vivacité fébrile de certaines maladies organiques -qui mêle les mouvements libres de la santé à l'effrayante maigreur de la -mort, s'était mise à genoux sur son lit, ses deux poings crispés appuyés -sur le traversin, et, la tête passée par l'intervalle des rideaux, elle -écoutait. Tout à coup elle cria: - ---Vous parlez là de monsieur Madeleine! pourquoi parlez-vous tout bas? -Qu'est-ce qu'il fait? Pourquoi ne vient-il pas? - -Sa voix était si brusque et si rauque que les deux femmes crurent -entendre une voix d'homme; elles se retournèrent effrayées. - ---Répondez donc! cria Fantine. - -La servante balbutia: - ---La portière m'a dit qu'il ne pourrait pas venir aujourd'hui. - ---Mon enfant, dit la soeur, tenez-vous tranquille, recouchez-vous. - -Fantine, sans changer d'attitude, reprit d'une voix haute et avec un -accent tout à la fois impérieux et déchirant: - ---Il ne pourra venir? Pourquoi cela? Vous savez la raison. Vous la -chuchotiez là entre vous. Je veux la savoir. - -La servante se hâta de dire à l'oreille de la religieuse: - ---Répondez qu'il est occupé au conseil municipal. - -La soeur Simplice rougit légèrement; c'était un mensonge que la servante -lui proposait. D'un autre côté il lui semblait bien que dire la vérité à -la malade ce serait sans doute lui porter un coup terrible et que cela -était grave dans l'état où était Fantine. Cette rougeur dura peu. La -soeur leva sur Fantine son oeil calme et triste, et dit: - ---Monsieur le maire est parti. - -Fantine se redressa et s'assit sur ses talons. Ses yeux étincelèrent. -Une joie inouïe rayonna sur cette physionomie douloureuse. - ---Parti! s'écria-t-elle. Il est allé chercher Cosette! - -Puis elle tendit ses deux mains vers le ciel et tout son visage devint -ineffable. Ses lèvres remuaient; elle priait à voix basse. - -Quand sa prière fut finie: - ---Ma soeur, dit-elle, je veux bien me recoucher, je vais faire tout ce -qu'on voudra; tout à l'heure j'ai été méchante, je vous demande pardon -d'avoir parlé si haut, c'est très mal de parler haut, je le sais bien, -ma bonne soeur, mais voyez-vous, je suis très contente. Le bon Dieu est -bon, monsieur Madeleine est bon, figurez-vous qu'il est allé chercher ma -petite Cosette à Montfermeil. - -Elle se recoucha, aida la religieuse à arranger l'oreiller et baisa une -petite croix d'argent qu'elle avait au cou et que la soeur Simplice lui -avait donnée. - ---Mon enfant, dit la soeur, tâchez de reposer maintenant, et ne parlez -plus. - -Fantine prit dans ses mains moites la main de la soeur, qui souffrait de -lui sentir cette sueur. - ---Il est parti ce matin pour aller à Paris. Au fait il n'a pas même -besoin de passer par Paris. Montfermeil, c'est un peu à gauche en -venant. Vous rappelez-vous comme il me disait hier quand je lui parlais -de Cosette: bientôt, bientôt? C'est une surprise qu'il veut me faire. -Vous savez? il m'avait fait signer une lettre pour la reprendre aux -Thénardier. Ils n'auront rien à dire, pas vrai? Ils rendront Cosette. -Puisqu'ils sont payés. Les autorités ne souffriraient pas qu'on garde un -enfant quand on est payé. Ma soeur, ne me faites pas signe qu'il ne faut -pas que je parle. Je suis extrêmement heureuse, je vais très bien, je -n'ai plus de mal du tout, je vais revoir Cosette, j'ai même très faim. -Il y a près de cinq ans que je ne l'ai vue. Vous ne vous figurez pas, -vous, comme cela vous tient, les enfants! Et puis elle sera si gentille, -vous verrez! Si vous saviez, elle a de si jolis petits doigts roses! -D'abord elle aura de très belles mains. À un an, elle avait des mains -ridicules. Ainsi!--Elle doit être grande à présent. Cela vous a sept -ans. C'est une demoiselle. Je l'appelle Cosette, mais elle s'appelle -Euphrasie. Tenez, ce matin, je regardais de la poussière qui était sur -la cheminée et j'avais bien l'idée comme cela que je reverrais bientôt -Cosette. Mon Dieu! comme on a tort d'être des années sans voir ses -enfants! on devrait bien réfléchir que la vie n'est pas éternelle! Oh! -comme il est bon d'être parti, monsieur le maire! C'est vrai ça, qu'il -fait bien froid? avait-il son manteau au moins? Il sera ici demain, -n'est-ce pas? Ce sera demain fête. Demain matin, ma soeur, vous me ferez -penser à mettre mon petit bonnet qui a de la dentelle. Montfermeil, -c'est un pays. J'ai fait cette route-là, à pied, dans le temps. Il y a -eu bien loin pour moi. Mais les diligences vont très vite! Il sera ici -demain avec Cosette. Combien y a-t-il d'ici Montfermeil? - -La soeur, qui n'avait aucune idée des distances, répondit: - ---Oh! je crois bien qu'il pourra être ici demain. - ---Demain! demain! dit Fantine, je verrai Cosette demain! Voyez-vous, -bonne soeur du bon Dieu, je ne suis plus malade. Je suis folle. Je -danserais, si on voulait. - -Quelqu'un qui l'eût vue un quart d'heure auparavant n'y eût rien -compris. Elle était maintenant toute rose, elle parlait d'une voix vive -et naturelle, toute sa figure n'était qu'un sourire. Par moments elle -riait en se parlant tout bas. Joie de mère, c'est presque joie d'enfant. - ---Eh bien, reprit la religieuse, vous voilà heureuse, obéissez-moi, ne -parlez plus. - -Fantine posa sa tête sur l'oreiller et dit à demi-voix: - ---Oui, recouche-toi, sois sage puisque tu vas avoir ton enfant. Elle a -raison, soeur Simplice. Tous ceux qui sont ici ont raison. - -Et puis, sans bouger, sans remuer la tête, elle se mit à regarder -partout avec ses yeux tout grands ouverts et un air joyeux, et elle ne -dit plus rien. - -La soeur referma ses rideaux, espérant qu'elle s'assoupirait. - -Entre sept et huit heures le médecin vint. N'entendant aucun bruit, il -crut que Fantine dormait, entra doucement et s'approcha du lit sur la -pointe du pied. Il entrouvrit les rideaux, et à la lueur de la veilleuse -il vit les grands yeux calmes de Fantine qui le regardaient. - -Elle lui dit: - ---Monsieur, n'est-ce pas, on me laissera la coucher à côté de moi dans -un petit lit? - -Le médecin crut qu'elle délirait. Elle ajouta: - ---Regardez plutôt, il y a juste de la place. - -Le médecin prit à part la soeur Simplice qui lui expliqua la chose, que -M. Madeleine était absent pour un jour ou deux, et que, dans le doute, -on n'avait pas cru devoir détromper la malade qui croyait monsieur le -maire parti pour Montfermeil; qu'il était possible en somme qu'elle eût -deviné juste. Le médecin approuva. - -Il se rapprocha du lit de Fantine, qui reprit: - ---C'est que, voyez-vous, le matin, quand elle s'éveillera, je lui dirai -bonjour à ce pauvre chat, et la nuit, moi qui ne dors pas, je -l'entendrai dormir. Sa petite respiration si douce, cela me fera du -bien. - ---Donnez-moi votre main, dit le médecin. - -Elle tendit son bras, et s'écria en riant. - ---Ah! tiens! au fait, c'est vrai, vous ne savez pas c'est que je suis -guérie. Cosette arrive demain. - -Le médecin fut surpris. Elle était mieux. L'oppression était moindre. Le -pouls avait repris de la force. Une sorte de vie survenue tout à coup -ranimait ce pauvre être épuisé. - ---Monsieur le docteur, reprit-elle, la soeur vous a-t-elle dit que -monsieur le maire était allé chercher le chiffon? - -Le médecin recommanda le silence et qu'on évitât toute émotion pénible. -Il prescrivit une infusion de quinquina pur, et, pour le cas où la -fièvre reprendrait dans la nuit, une potion calmante. En s'en allant, il -dit à la soeur: - ---Cela va mieux. Si le bonheur voulait qu'en effet monsieur le maire -arrivât demain avec l'enfant, qui sait? il y a des crises si étonnantes, -on a vu de grandes joies arrêter court des maladies; je sais bien que -celle-ci est une maladie organique, et bien avancée, mais c'est un tel -mystère que tout cela! Nous la sauverions peut-être. - - - - -Chapitre VII - -Le voyageur arrivé prend ses précautions pour repartir. - - -Il était près de huit heures du soir quand la carriole que nous avons -laissée en route entra sous la porte cochère de l'hôtel de la Poste -à Arras. L'homme que nous avons suivi jusqu'à ce moment en descendit, -répondit d'un air distrait aux empressements des gens de l'auberge, -renvoya le cheval de renfort, et conduisit lui-même le petit cheval -blanc à l'écurie; puis il poussa la porte d'une salle de billard qui -était au rez-de-chaussée, s'y assit, et s'accouda sur une table. Il -avait mis quatorze heures à ce trajet qu'il comptait faire en six. -Il se rendait la justice que ce n'était pas sa faute; mais au fond il -n'en était pas fâché. - -La maîtresse de l'hôtel entra. - ---Monsieur couche-t-il? monsieur soupe-t-il? - -Il fit un signe de tête négatif. - ---Le garçon d'écurie dit que le cheval de monsieur est bien fatigué! - -Ici il rompit le silence. - ---Est-ce que le cheval ne pourra pas repartir demain matin? - ---Oh! monsieur! il lui faut au moins deux jours de repos. - -Il demanda: - ---N'est-ce pas ici le bureau de poste? - ---Oui, monsieur. - -L'hôtesse le mena à ce bureau; il montra son passeport et s'informa s'il -y avait moyen de revenir cette nuit même à Montreuil-sur-mer par la -malle; la place à côté du courrier était justement vacante; il la retint -et la paya. - ---Monsieur, dit le buraliste, ne manquez pas d'être ici pour partir à -une heure précise du matin. - -Cela fait, il sortit de l'hôtel et se mit à marcher dans la ville. - -Il ne connaissait pas Arras, les rues étaient obscures, et il allait au -hasard. Cependant il semblait s'obstiner à ne pas demander son chemin -aux passants. Il traversa la petite rivière Crinchon et se trouva dans -un dédale de ruelles étroites où il se perdit. Un bourgeois cheminait -avec un falot. Après quelque hésitation, il prit le parti de s'adresser -à ce bourgeois, non sans avoir d'abord regardé devant et derrière lui, -comme s'il craignait que quelqu'un n'entendit la question qu'il allait -faire. - ---Monsieur, dit-il, le palais de justice, s'il vous plaît? - ---Vous n'êtes pas de la ville, monsieur? répondit le bourgeois qui était -un assez vieux homme, eh bien, suivez-moi. Je vais précisément du côté -du palais de justice, c'est-à-dire du côté de l'hôtel de la préfecture. -Car on répare en ce moment le palais, et provisoirement les tribunaux -ont leurs audiences à la préfecture. - ---Est-ce là, demanda-t-il, qu'on tient les assises? - ---Sans doute, monsieur. Voyez-vous, ce qui est la préfecture aujourd'hui -était l'évêché avant la révolution. Monsieur de Conzié, qui était évêque -en quatre-vingt-deux, y a fait bâtir une grande salle. C'est dans cette -grande salle qu'on juge. - -Chemin faisant, le bourgeois lui dit: - ---Si c'est un procès que monsieur veut voir, il est un peu tard. -Ordinairement les séances finissent à six heures. - -Cependant, comme ils arrivaient sur la grande place, le bourgeois lui -montra quatre longues fenêtres éclairées sur la façade d'un vaste -bâtiment ténébreux. - ---Ma foi, monsieur, vous arrivez à temps, vous avez du bonheur. -Voyez-vous ces quatre fenêtres? c'est la cour d'assises. Il y a de la -lumière. Donc ce n'est pas fini. L'affaire aura traîné en longueur et on -fait une audience du soir. Vous vous intéressez à cette affaire? Est-ce -que c'est un procès criminel? Est-ce que vous êtes témoin? - -Il répondit: - ---Je ne viens pour aucune affaire, j'ai seulement à parler à un avocat. - ---C'est différent, dit le bourgeois. Tenez, monsieur, voici la porte. Où -est le factionnaire. Vous n'aurez qu'à monter le grand escalier. - -Il se conforma aux indications du bourgeois, et, quelques minutes après, -il était dans une salle où il y avait beaucoup de monde et où des -groupes mêlés d'avocats en robe chuchotaient çà et là. - -C'est toujours une chose qui serre le coeur de voir ces attroupements -d'hommes vêtus de noir qui murmurent entre eux à voix basse sur le seuil -des chambres de justice. Il est rare que la charité et la pitié sortent -de toutes ces paroles. Ce qui en sort le plus souvent, ce sont des -condamnations faites d'avance. Tous ces groupes semblent à l'observateur -qui passe et qui rêve autant de ruches sombres où des espèces d'esprits -bourdonnants construisent en commun toutes sortes d'édifices ténébreux. - -Cette salle, spacieuse et éclairée d'une seule lampe, était une ancienne -antichambre de l'évêché et servait de salle des pas perdus. Une porte à -deux battants, fermée en ce moment, la séparait de la grande chambre où -siégeait la cour d'assises. - -L'obscurité était telle qu'il ne craignit pas de s'adresser au premier -avocat qu'il rencontra. - ---Monsieur, dit-il, où en est-on? - ---C'est fini, dit l'avocat. - ---Fini! - -Ce mot fut répété d'un tel accent que l'avocat se retourna. - ---Pardon, monsieur, vous êtes peut-être un parent? - ---Non. Je ne connais personne ici. Et y a-t-il eu condamnation? - ---Sans doute. Cela n'était guère possible autrement. - ---Aux travaux forcés?... - ---À perpétuité. - -Il reprit d'une voix tellement faible qu'on l'entendait à peine: - ---L'identité a donc été constatée? - ---Quelle identité? répondit l'avocat. Il n'y avait pas d'identité à -constater. L'affaire était simple. Cette femme avait tué son enfant, -l'infanticide a été prouvé, le jury a écarté la préméditation, on l'a -condamnée à vie. - ---C'est donc une femme? dit-il. - ---Mais sûrement. La fille Limosin. De quoi me parlez-vous donc? - ---De rien. Mais puisque c'est fini, comment se fait-il que la salle soit -encore éclairée? - ---C'est pour l'autre affaire qu'on a commencée il y a à peu près deux -heures. - ---Quelle autre affaire? - ---Oh! celle-là est claire aussi. C'est une espèce de gueux, un -récidiviste, un galérien, qui a volé. Je ne sais plus trop son nom. En -voilà un qui vous a une mine de bandit. Rien que pour avoir cette -figure-là, je l'enverrais aux galères. - ---Monsieur, demanda-t-il, y a-t-il moyen de pénétrer dans la salle? - ---Je ne crois vraiment pas. Il y a beaucoup de foule. Cependant -l'audience est suspendue. Il y a des gens qui sont sortis, et, à la -reprise de l'audience, vous pourrez essayer. - ---Par où entre-t-on? - ---Par cette grande porte. - -L'avocat le quitta. En quelques instants, il avait éprouvé, presque en -même temps, presque mêlées, toutes les émotions possibles. Les paroles -de cet indifférent lui avaient tour à tour traversé le coeur comme des -aiguilles de glace et comme des lames de feu. Quand il vit que rien -n'était terminé, il respira; mais il n'eût pu dire si ce qu'il -ressentait était du contentement ou de la douleur. - -Il s'approcha de plusieurs groupes et il écouta ce qu'on disait. Le rôle -de la session étant très chargé, le président avait indiqué pour ce même -jour deux affaires simples et courtes. On avait commencé par -l'infanticide, et maintenant on en était au forçat, au récidiviste, au -"cheval de retour". Cet homme avait volé des pommes, mais cela ne -paraissait pas bien prouvé; ce qui était prouvé, c'est qu'il avait été -déjà aux galères à Toulon. C'est ce qui faisait son affaire mauvaise. Du -reste, l'interrogatoire de l'homme était terminé et les dépositions des -témoins; mais il y avait encore les plaidoiries de l'avocat et le -réquisitoire du ministère public; cela ne devait guère finir avant -minuit. L'homme serait probablement condamné; l'avocat général était -très bon--et ne manquait pas ses accusés--c'était un garçon d'esprit qui -faisait des vers. - -Un huissier se tenait debout près de la porte qui communiquait avec la -salle des assises. Il demanda à cet huissier: - ---Monsieur, la porte va-t-elle bientôt s'ouvrir? - ---Elle ne s'ouvrira pas, dit l'huissier. - ---Comment! on ne l'ouvrira pas à la reprise de l'audience? est-ce que -l'audience n'est pas suspendue? - ---L'audience vient d'être reprise, répondit l'huissier, mais la porte ne -se rouvrira pas. - ---Pourquoi? - ---Parce que la salle est pleine. - ---Quoi? il n'y a plus une place? - ---Plus une seule. La porte est fermée. Personne ne peut plus entrer. - -L'huissier ajouta après un silence: - ---Il y a bien encore deux ou trois places derrière monsieur le -président, mais monsieur le président n'y admet que les fonctionnaires -publics. - -Cela dit, l'huissier lui tourna le dos. - -Il se retira la tête baissée, traversa l'antichambre et redescendit -l'escalier lentement, comme hésitant à chaque marche. Il est probable -qu'il tenait conseil avec lui-même. Le violent combat qui se livrait en -lui depuis la veille n'était pas fini; et, à chaque instant, il en -traversait quelque nouvelle péripétie. Arrivé sur le palier de -l'escalier, il s'adossa à la rampe et croisa les bras. Tout à coup il -ouvrit sa redingote, prit son portefeuille, en tira un crayon, déchira -une feuille, et écrivit rapidement sur cette feuille à la lueur du -réverbère cette ligne:--_M. Madeleine, maire de Montreuil-sur-mer_. -Puis il remonta l'escalier à grands pas, fendit la foule, marcha droit à -l'huissier, lui remit le papier, et lui dit avec autorité: - ---Portez ceci à monsieur le président. - -L'huissier prit le papier, y jeta un coup d'oeil et obéit. - - - - -Chapitre VIII - -Entrée de faveur - - -Sans qu'il s'en doutât, le maire de Montreuil-sur-mer avait une sorte de -célébrité. Depuis sept ans que sa réputation de vertu remplissait tout -le bas Boulonnais, elle avait fini par franchir les limites d'un petit -pays et s'était répandue dans les deux ou trois départements voisins. -Outre le service considérable qu'il avait rendu au chef-lieu en y -restaurant l'industrie des verroteries noires, il n'était pas une des -cent quarante et une communes de l'arrondissement de Montreuil-sur-mer -qui ne lui dût quelque bienfait. Il avait su même au besoin aider et -féconder les industries des autres arrondissements. C'est ainsi qu'il -avait dans l'occasion soutenu de son crédit et de ses fonds la fabrique -de tulle de Boulogne, la filature de lin à la mécanique de Frévent et la -manufacture hydraulique de toiles de Boubers-sur-Canche. Partout on -prononçait avec vénération le nom de M. Madeleine. Arras et Douai -enviaient son maire à l'heureuse petite ville de Montreuil-sur-mer. - -Le conseiller à la cour royale de Douai, qui présidait cette session des -assises à Arras, connaissait comme tout le monde ce nom si profondément -et si universellement honoré. Quand l'huissier, ouvrant discrètement la -porte qui communiquait de la chambre du conseil à l'audience, se pencha -derrière le fauteuil du président et lui remit le papier où était écrite -la ligne qu'on vient de lire, en ajoutant: _Ce monsieur désire assister -à l'audience_, le président fit un vif mouvement de déférence, saisit -une plume, écrivit quelques mots au bas du papier, et le rendit à -l'huissier en lui disant: Faites entrer. - -L'homme malheureux dont nous racontons l'histoire était resté près de la -porte de la salle à la même place et dans la même attitude où l'huissier -l'avait quitté. Il entendit, à travers sa rêverie, quelqu'un qui lui -disait: Monsieur veut-il bien me faire l'honneur de me suivre? C'était -ce même huissier qui lui avait tourné le dos l'instant d'auparavant et -qui maintenant le saluait jusqu'à terre. L'huissier en même temps lui -remit le papier. Il le déplia, et, comme il se rencontrait qu'il était -près de la lampe, il put lire: - -«Le président de la cour d'assises présente son respect à M. Madeleine.» - -Il froissa le papier entre ses mains, comme si ces quelques mots eussent -eu pour lui un arrière-goût étrange et amer. - -Il suivit l'huissier. - -Quelques minutes après, il se trouvait seul dans une espèce de cabinet -lambrissé, d'un aspect sévère, éclairé par deux bougies posées sur une -table à tapis vert. Il avait encore dans l'oreille les dernières paroles -de l'huissier qui venait de le quitter--«Monsieur, vous voici dans la -chambre du conseil; vous n'avez qu'à tourner le bouton de cuivre de -cette porte, et vous vous trouverez dans l'audience derrière le fauteuil -de monsieur le président.»--Ces paroles se mêlaient dans sa pensée à un -souvenir vague de corridors étroits et d'escaliers noirs qu'il venait de -parcourir. - -L'huissier l'avait laissé seul. Le moment suprême était arrivé. Il -cherchait à se recueillir sans pouvoir y parvenir. C'est surtout aux -heures où l'on aurait le plus besoin de les rattacher aux réalités -poignantes de la vie que tous les fils de la pensée se rompent dans le -cerveau. Il était dans l'endroit même où les juges délibèrent et -condamnent. Il regardait avec une tranquillité stupide cette chambre -paisible et redoutable où tant d'existences avaient été brisées, où son -nom allait retentir tout à l'heure, et que sa destinée traversait en ce -moment. Il regardait la muraille, puis il se regardait lui-même, -s'étonnant que ce fût cette chambre et que ce fût lui. - -Il n'avait pas mangé depuis plus de vingt-quatre heures, il était brisé -par les cahots de la carriole, mais il ne le sentait pas; il lui -semblait qu'il ne sentait rien. - -Il s'approcha d'un cadre noir qui était accroché au mur et qui contenait -sous verre une vieille lettre autographe de Jean-Nicolas Pache, maire de -Paris et ministre, datée, sans doute par erreur, du _9 juin an II_, et -dans laquelle Pache envoyait à la commune la liste des ministres et des -députés tenus en arrestation chez eux. Un témoin qui l'eût pu voir et -qui l'eût observé en cet instant eût sans doute imaginé Fantine et -Cosette. - -Tout en rêvant, il se retourna, et ses yeux rencontrèrent le bouton de -cuivre de la porte qui le séparait de la salle des assises. Il avait -presque oublié cette porte. Son regard, d'abord calme, s'y arrêta, resta -attaché à ce bouton de cuivre, puis devint effaré et fixe, et -s'empreignit peu à peu d'épouvante. Des gouttes de sueur lui sortaient -d'entre les cheveux et ruisselaient sur ses tempes. - -À un certain moment, il fit avec une sorte d'autorité mêlée de rébellion -ce geste indescriptible qui veut dire et qui dit si bien: _Pardieu! qui -est-ce qui m'y force?_ Puis il se tourna vivement, vit devant lui la -porte par laquelle il était entré, y alla, l'ouvrit, et sortit. Il -n'était plus dans cette chambre, il était dehors, dans un corridor, un -corridor long, étroit, coupé de degrés et de guichets, faisant toutes -sortes d'angles, éclairé çà et là de réverbères pareils à des veilleuses -de malades, le corridor par où il était venu. Il respira, il écouta; -aucun bruit derrière lui, aucun bruit devant lui; il se mit à fuir comme -si on le poursuivait. - -Quand il eut doublé plusieurs des coudes de ce couloir, il écouta -encore. C'était toujours le même silence et la même ombre autour de lui. -Il était essoufflé, il chancelait, il s'appuya au mur. La pierre était -froide, sa sueur était glacée sur son front, il se redressa en -frissonnant. - -Alors, là, seul, debout dans cette obscurité, tremblant de froid et -d'autre chose peut-être, il songea. - -Il avait songé toute la nuit, il avait songé toute la journée; il -n'entendait plus en lui qu'une voix qui disait: hélas! - -Un quart d'heure s'écoula ainsi. Enfin, il pencha la tête, soupira avec -angoisse, laissa pendre ses bras, et revint sur ses pas. Il marchait -lentement et comme accablé. Il semblait que quelqu'un l'eût atteint dans -sa fuite et le ramenât. - -Il rentra dans la chambre du conseil. La première chose qu'il aperçut, -ce fut la gâchette de la porte. Cette gâchette, ronde et en cuivre poli, -resplendissait pour lui comme une effroyable étoile. Il la regardait -comme une brebis regarderait l'oeil d'un tigre. - -Ses yeux ne pouvaient s'en détacher. - -De temps en temps il faisait un pas et se rapprochait de la porte. - -S'il eût écouté, il eût entendu, comme une sorte de murmure confus, le -bruit de la salle voisine; mais il n'écoutait pas, et il n'entendait -pas. - -Tout à coup, sans qu'il sût lui-même comment, il se trouva près de la -porte. Il saisit convulsivement le bouton; la porte s'ouvrit. - -Il était dans la salle d'audience. - - - - -Chapitre IX - -Un lieu où des convictions sont en train de se former - - -Il fit un pas, referma machinalement la porte derrière lui, et resta -debout, considérant ce qu'il voyait. - -C'était une assez vaste enceinte à peine éclairée, tantôt pleine de -rumeur, tantôt pleine de silence, où tout l'appareil d'un procès -criminel se développait avec sa gravité mesquine et lugubre au milieu de -la foule. - -À un bout de la salle, celui où il se trouvait, des juges à l'air -distrait, en robe usée, se rongeant les ongles ou fermant les paupières; -à l'autre bout, une foule en haillons; des avocats dans toutes sortes -d'attitudes; des soldats au visage honnête et dur; de vieilles boiseries -tachées, un plafond sale, des tables couvertes d'une serge plutôt jaune -que verte, des portes noircies par les mains; à des clous plantés dans -le lambris, des quinquets d'estaminet donnant plus de fumée que de -clarté; sur les tables, des chandelles dans des chandeliers de cuivre; -l'obscurité, la laideur, la tristesse; et de tout cela se dégageait une -impression austère et auguste, car on y sentait cette grande chose -humaine qu'on appelle la loi et cette grande chose divine qu'on appelle -la justice. - -Personne dans cette foule ne fit attention à lui. Tous les regards -convergeaient vers un point unique, un banc de bois adossé à une petite -porte, le long de la muraille, à gauche du président. Sur ce banc, que -plusieurs chandelles éclairaient, il y avait un homme entre deux -gendarmes. - -Cet homme, c'était l'homme. - -Il ne le chercha pas, il le vit. Ses yeux allèrent là naturellement, -comme s'ils avaient su d'avance où était cette figure. - -Il crut se voir lui-même, vieilli, non pas sans doute absolument -semblable de visage, mais tout pareil d'attitude et d'aspect, avec ces -cheveux hérissés, avec cette prunelle fauve et inquiète, avec cette -blouse, tel qu'il était le jour où il entrait à Digne, plein de haine et -cachant dans son âme ce hideux trésor de pensées affreuses qu'il avait -mis dix-neuf ans à ramasser sur le pavé du bagne. - -Il se dit avec un frémissement: - ---Mon Dieu! est-ce que je redeviendrai ainsi? - -Cet être paraissait au moins soixante ans. Il avait je ne sais quoi de -rude, de stupide et d'effarouché. - -Au bruit de la porte, on s'était rangé pour lui faire place, le -président avait tourné la tête, et comprenant que le personnage qui -venait d'entrer était M. le maire de Montreuil-sur-mer, il l'avait -salué. L'avocat général, qui avait vu M. Madeleine à Montreuil-sur-mer -où des opérations de son ministère l'avaient plus d'une fois appelé, le -reconnut, et salua également. Lui s'en aperçut à peine. Il était en -proie à une sorte d'hallucination; il regardait. - -Des juges, un greffier, des gendarmes, une foule de têtes cruellement -curieuses, il avait déjà vu cela une fois, autrefois, il y avait -vingt-sept ans. Ces choses funestes, il les retrouvait; elles étaient -là, elles remuaient, elles existaient. Ce n'était plus un effort de sa -mémoire, un mirage de sa pensée, c'étaient de vrais gendarmes et de -vrais juges, une vraie foule et de vrais hommes en chair et en os. C'en -était fait, il voyait reparaître et revivre autour de lui, avec tout ce -que la réalité a de formidable, les aspects monstrueux de son passé. - -Tout cela était béant devant lui. - -Il en eut horreur, il ferma les yeux, et s'écria au plus profond de son -âme: jamais! - -Et par un jeu tragique de la destinée qui faisait trembler toutes ses -idées et le rendait presque fou, c'était un autre lui-même qui était là! -Cet homme qu'on jugeait, tous l'appelaient Jean Valjean! - -Il avait sous les yeux, vision inouïe, une sorte de représentation du -moment le plus horrible de sa vie, jouée par son fantôme. - -Tout y était, c'était le même appareil, la même heure de nuit, presque -les mêmes faces de juges, de soldats et de spectateurs. Seulement, -au-dessus de la tête du président, il y avait un crucifix, chose qui -manquait aux tribunaux du temps de sa condamnation. Quand on l'avait -jugé, Dieu était absent. - -Une chaise était derrière lui; il s'y laissa tomber, terrifié de l'idée -qu'on pouvait le voir. Quand il fut assis, il profita d'une pile de -cartons qui était sur le bureau des juges pour dérober son visage à -toute la salle. Il pouvait maintenant voir sans être vu. Peu à peu il se -remit. Il rentra pleinement dans le sentiment du réel; il arriva à cette -phase de calme où l'on peut écouter. - -M. Bamatabois était au nombre des jurés. Il chercha Javert, mais il ne -le vit pas. Le banc des témoins lui était caché par la table du -greffier. Et puis, nous venons de le dire, la salle était à peine -éclairée. - -Au moment où il était entré, l'avocat de l'accusé achevait sa -plaidoirie. L'attention de tous était excitée au plus haut point; -l'affaire durait depuis trois heures. Depuis trois heures, cette foule -regardait plier peu à peu sous le poids d'une vraisemblance terrible un -homme, un inconnu, une espèce d'être misérable, profondément stupide ou -profondément habile. Cet homme, on le sait déjà, était un vagabond qui -avait été trouvé dans un champ, emportant une branche chargée de pommes -mûres, cassée à un pommier dans un clos voisin, appelé le clos Pierron. -Qui était cet homme? Une enquête avait eu lieu; des témoins venaient -d'être entendus, ils avaient été unanimes, des lumières avaient jailli -de tout le débat. L'accusation disait: - ---Nous ne tenons pas seulement un voleur de fruits, un maraudeur; nous -tenons là, dans notre main, un bandit, un relaps en rupture de ban, un -ancien forçat, un scélérat des plus dangereux, un malfaiteur appelé Jean -Valjean que la justice recherche depuis longtemps, et qui, il y a huit -ans, en sortant du bagne de Toulon, a commis un vol de grand chemin à -main armée sur la personne d'un enfant savoyard appelé Petit-Gervais, -crime prévu par l'article 383 du code pénal, pour lequel nous nous -réservons de le poursuivre ultérieurement, quand l'identité sera -judiciairement acquise. Il vient de commettre un nouveau vol. C'est un -cas de récidive. Condamnez-le pour le fait nouveau; il sera jugé plus -tard pour le fait ancien. - -Devant cette accusation, devant l'unanimité des témoins, l'accusé -paraissait surtout étonné. Il faisait des gestes et des signes qui -voulaient dire non, ou bien il considérait le plafond. Il parlait avec -peine, répondait avec embarras, mais de la tête aux pieds toute sa -personne niait. Il était comme un idiot en présence de toutes ces -intelligences rangées en bataille autour de lui, et comme un étranger au -milieu de cette société qui le saisissait. Cependant il y allait pour -lui de l'avenir le plus menaçant, la vraisemblance croissait à chaque -minute, et toute cette foule regardait avec plus d'anxiété que lui-même -cette sentence pleine de calamités qui penchait sur lui de plus en plus. -Une éventualité laissait même entrevoir, outre le bagne, la peine de -mort possible, si l'identité était reconnue et si l'affaire -Petit-Gervais se terminait plus tard par une condamnation. Qu'était-ce -que cet homme? De quelle nature était son apathie? Etait-ce imbécillité -ou ruse? Comprenait-il trop, ou ne comprenait-il pas du tout? Questions -qui divisaient la foule et semblaient partager le jury. Il y avait dans -ce procès ce qui effraye et ce qui intrigue; le drame n'était pas -seulement sombre, il était obscur. Le défenseur avait assez bien plaidé, -dans cette langue de province qui a longtemps constitué l'éloquence du -barreau et dont usaient jadis tous les avocats, aussi bien à Paris qu'à -Romorantin ou à Montbrison, et qui aujourd'hui, étant devenue classique, -n'est plus guère parlée que par les orateurs officiels du parquet, -auxquels elle convient par sa sonorité grave et son allure majestueuse; -langue où un mari s'appelle un époux, une femme, une épouse, Paris, le -centre des arts et de la civilisation, le roi, le monarque, monseigneur -l'évêque, un saint pontife, l'avocat général, l'éloquent interprète de -la vindicte, la plaidoirie, les accents qu'on vient d'entendre, le -siècle de Louis XIV, le grand siècle, un théâtre, le temple de -Melpomène, la famille régnante, l'auguste sang de nos rois, un concert, -une solennité musicale, monsieur le général commandant le département, -l'illustre guerrier qui, etc., les élèves du séminaire, ces tendres -lévites, les erreurs imputées aux journaux, l'imposture qui distille son -venin dans les colonnes de ces organes, etc., etc.--L'avocat donc avait -commencé par s'expliquer sur le vol des pommes,--chose malaisée en beau -style; mais Bénigne Bossuet lui-même a été obligé de faire allusion à -une poule en pleine oraison funèbre, et il s'en est tiré avec pompe. -L'avocat avait établi que le vol de pommes n'était pas matériellement -prouvé.--Son client, qu'en sa qualité de défenseur, il persistait à -appeler Champmathieu, n'avait été vu de personne escaladant le mur ou -cassant la branche. On l'avait arrêté nanti de cette branche (que -l'avocat appelait plus volontiers rameau); mais il disait l'avoir -trouvée à terre et ramassée. Où était la preuve du contraire?--Sans -doute cette branche avait été cassée et dérobée après escalade, puis -jetée là par le maraudeur alarmé; sans doute il y avait un voleur. Mais -qu'est-ce qui prouvait que ce voleur était Champmathieu? Une seule -chose. Sa qualité d'ancien forçat. L'avocat ne niait pas que cette -qualité ne parût malheureusement bien constatée; l'accusé avait résidé à -Faverolles; l'accusé y avait été émondeur; le nom de Champmathieu -pouvait bien avoir pour origine Jean Mathieu; tout cela était vrai; -enfin quatre témoins reconnaissaient sans hésiter et positivement -Champmathieu pour être le galérien Jean Valjean; à ces indications, à -ces témoignages, l'avocat ne pouvait opposer que la dénégation de son -client, dénégation intéressée; mais en supposant qu'il fût le forçat -Jean Valjean, cela prouvait-il qu'il fût le voleur des pommes? C'était -une présomption, tout au plus; non une preuve. L'accusé, cela était -vrai, et le défenseur «dans sa bonne foi» devait en convenir, avait -adopté «un mauvais système de défense»--Il s'obstinait à nier tout, le -vol et sa qualité de forçat. Un aveu sur ce dernier point eût mieux -valu, à coup sûr, et lui eût concilié l'indulgence de ses juges; -l'avocat le lui avait conseillé; mais l'accusé s'y était refusé -obstinément, croyant sans doute sauver tout en n'avouant rien. C'était -un tort; mais ne fallait-il pas considérer la brièveté de cette -intelligence? Cet homme était visiblement stupide. Un long malheur au -bagne, une longue misère hors du bagne, l'avaient abruti, etc., etc. Il -se défendait mal, était-ce une raison pour le condamner? Quant à -l'affaire Petit-Gervais, l'avocat n'avait pas à la discuter, elle -n'était point dans la cause. L'avocat concluait en suppliant le jury et -la cour, si l'identité de Jean Valjean leur paraissait évidente, de lui -appliquer les peines de police qui s'adressent au condamné en rupture de -ban, et non le châtiment épouvantable qui frappe le forçat récidiviste. - -L'avocat général répliqua au défenseur. Il fut violent et fleuri, comme -sont habituellement les avocats généraux. - -Il félicita le défenseur de sa «loyauté», et profita habilement de cette -loyauté. Il atteignit l'accusé par toutes les concessions que l'avocat -avait faites. L'avocat semblait accorder que l'accusé était Jean -Valjean. Il en prit acte. Cet homme était donc Jean Valjean. Ceci était -acquis à l'accusation et ne pouvait plus se contester. Ici, par une -habile antonomase, remontant aux sources et aux causes de la -criminalité, l'avocat général tonna contre l'immoralité de l'école -romantique, alors à son aurore sous le nom d'école satanique que lui -avaient décerné les critiques de l'Oriflamme et de la Quotidienne, il -attribua, non sans vraisemblance, à l'influence de cette littérature -perverse le délit de Champmathieu, ou pour mieux dire, de Jean Valjean. -Ces considérations épuisées, il passa à Jean Valjean lui-même. -Qu'était-ce que Jean Valjean? Description de Jean Valjean. Un monstre -vomi, etc. Le modèle de ces sortes de descriptions est dans le récit de -Théramène, lequel n'est pas utile à la tragédie, mais rend tous les -jours de grands services à l'éloquence judiciaire. L'auditoire et les -jurés «frémirent». La description achevée, l'avocat général reprit, dans -un mouvement oratoire fait pour exciter au plus haut point le lendemain -matin l'enthousiasme du Journal de la Préfecture: - -Et c'est un pareil homme, etc., etc., etc., vagabond, mendiant, sans -moyens d'existence, etc., etc.,--accoutumé par sa vie passée aux actions -coupables et peu corrigé par son séjour au bagne, comme le prouve le -crime commis sur Petit-Gervais, etc., etc.,--c'est un homme pareil qui, -trouvé sur la voie publique en flagrant délit de vol, à quelques pas -d'un mur escaladé, tenant encore à la main l'objet volé, nie le flagrant -délit, le vol, l'escalade, nie tout, nie jusqu'à son nom, nie jusqu'à -son identité! Outre cent autres preuves sur lesquelles nous ne revenons -pas, quatre témoins le reconnaissent, Javert, l'intègre inspecteur de -police Javert, et trois de ses anciens compagnons d'ignominie, les -forçats Brevet, Chenildieu et Cochepaille. Qu'oppose-t-il à cette -unanimité foudroyante? Il nie. Quel endurcissement! Vous ferez justice, -messieurs les jurés, etc., etc. - -Pendant que l'avocat général parlait, l'accusé écoutait, la bouche -ouverte, avec une sorte d'étonnement où il entrait bien quelque -admiration. Il était évidemment surpris qu'un homme pût parler comme -cela. De temps en temps, aux moments les plus «énergiques» du -réquisitoire, dans ces instants où l'éloquence, qui ne peut se contenir, -déborde dans un flux d'épithètes flétrissantes et enveloppe l'accusé -comme un orage, il remuait lentement la tête de droite à gauche et de -gauche à droite, sorte de protestation triste et muette dont il se -contentait depuis le commencement des débats. Deux ou trois fois les -spectateurs placés le plus près de lui l'entendirent dire à demi-voix: - ---Voilà ce que c'est, de n'avoir pas demandé à M. Baloup! - -L'avocat général fit remarquer au jury cette attitude hébétée, calculée -évidemment, qui dénotait, non l'imbécillité, mais l'adresse, la ruse, -l'habitude de tromper la justice, et qui mettait dans tout son jour «la -profonde perversité» de cet homme. Il termina en faisant ses réserves -pour l'affaire Petit-Gervais, et en réclamant une condamnation sévère. - -C'était, pour l'instant, on s'en souvient, les travaux forcés à -perpétuité. - -Le défenseur se leva, commença par complimenter «monsieur l'avocat -général» sur son «admirable parole», puis répliqua comme il put, mais il -faiblissait; le terrain évidemment se dérobait sous lui. - - - - -Chapitre X - -Le système de dénégations - - -L'instant de clore les débats était venu. Le président fit lever -l'accusé et lui adressa la question d'usage: - ---Avez-vous quelque chose à ajouter à votre défense? - -L'homme, debout, roulant dans ses mains un affreux bonnet qu'il avait, -sembla ne pas entendre. - -Le président répéta la question. - -Cette fois l'homme entendit. Il parut comprendre, il fit le mouvement de -quelqu'un qui se réveille, promena ses yeux autour de lui, regarda le -public, les gendarmes, son avocat, les jurés, la cour, posa son poing -monstrueux sur le rebord de la boiserie placée devant son banc, regarda -encore, et tout à coup, fixant sont regard sur l'avocat général, il se -mit à parler. Ce fut comme une éruption. Il sembla, à la façon dont les -paroles s'échappaient de sa bouche, incohérentes, impétueuses, heurtées, -pêle-mêle, qu'elles s'y pressaient toutes à la fois pour sortir en même -temps. Il dit: - ---J'ai à dire ça. Que j'ai été charron à Paris, même que c'était chez -monsieur Baloup. C'est un état dur. Dans la chose de charron, on -travaille toujours en plein air, dans des cours, sous des hangars chez -les bons maîtres, jamais dans des ateliers fermés, parce qu'il faut des -espaces, voyez-vous. L'hiver, on a si froid qu'on se bat les bras pour -se réchauffer; mais les maîtres ne veulent pas, ils disent que cela perd -du temps. Manier du fer quand il y a de la glace entre les pavés, c'est -rude. Ça vous use vite un homme. On est vieux tout jeune dans cet -état-là. À quarante ans, un homme est fini. Moi, j'en avais -cinquante-trois, j'avais bien du mal. Et puis c'est si méchant les -ouvriers! Quand un bonhomme n'est plus jeune, on vous l'appelle pour -tout vieux serin, vieille bête! Je ne gagnais plus que trente sous par -jour, on me payait le moins cher qu'on pouvait, les maîtres profitaient -de mon âge. Avec ça, j'avais ma fille qui était blanchisseuse à la -rivière. Elle gagnait un peu de son côté. À nous deux, cela allait. Elle -avait de la peine aussi. Toute la journée dans un baquet jusqu'à -mi-corps, à la pluie, à la neige, avec le vent qui vous coupe la figure; -quand il gèle, c'est tout de même, il faut laver; il y a des personnes -qui n'ont pas beaucoup de linge et qui attendent après; si on ne lavait -pas, on perdrait des pratiques. Les planches sont mal jointes et il vous -tombe des gouttes d'eau partout. On a ses jupes toutes mouillées, dessus -et dessous. Ça pénètre. Elle a aussi travaillé au lavoir des -Enfants-Rouges, où l'eau arrive par des robinets. On n'est pas dans le -baquet. On lave devant soi au robinet et on rince derrière soi dans le -bassin. Comme c'est fermé, on a moins froid au corps. Mais il y a une -buée d'eau chaude qui est terrible et qui vous perd les yeux. Elle -revenait à sept heures du soir, et se couchait bien vite; elle était si -fatiguée. Son mari la battait. Elle est morte. Nous n'avons pas été bien -heureux. C'était une brave fille qui n'allait pas au bal, qui était bien -tranquille. Je me rappelle un mardi gras où elle était couchée à huit -heures. Voilà. Je dis vrai. Vous n'avez qu'à demander. Ah, bien oui, -demander! que je suis bête! Paris, c'est un gouffre. Qui est-ce qui -connaît le père Champmathieu? Pourtant je vous dis monsieur Baloup. -Voyez chez monsieur Baloup. Après ça, je ne sais pas ce qu'on me veut. - -L'homme se tut, et resta debout. Il avait dit ces choses d'une voix -haute, rapide, rauque, dure et enrouée, avec une sorte de naïveté -irritée et sauvage. Une fois il s'était interrompu pour saluer quelqu'un -dans la foule. Les espèces d'affirmations qu'il semblait jeter au hasard -devant lui, lui venaient comme des hoquets, et il ajoutait à chacune -d'elles le geste d'un bûcheron qui fend du bois. Quand il eut fini, -l'auditoire éclata de rire. Il regarda le public, et voyant qu'on riait, -et ne comprenant pas, il se mit à rire lui-même. - -Cela était sinistre. - -Le président, homme attentif et bienveillant, éleva la voix. - -Il rappela à «messieurs les jurés» que «le sieur Baloup, l'ancien maître -charron chez lequel l'accusé disait avoir servi, avait été inutilement -cité. Il était en faillite, et n'avait pu être retrouvé.» Puis se -tournant vers l'accusé, il l'engagea à écouter ce qu'il allait lui dire -et ajouta: - ---Vous êtes dans une situation où il faut réfléchir. Les présomptions -les plus graves pèsent sur vous et peuvent entraîner des conséquences -capitales. Accusé, dans votre intérêt, je vous interpelle une dernière -fois, expliquez-vous clairement sur ces deux faits:--Premièrement, -avez-vous, oui ou non, franchi le mur du clos Pierron, cassé la branche -et volé les pommes, c'est-à-dire commis le crime de vol avec escalade? -Deuxièmement, oui ou non, êtes-vous le forçat libéré Jean Valjean? - -L'accusé secoua la tête d'un air capable, comme un homme qui a bien -compris et qui sait ce qu'il va répondre. Il ouvrit la bouche, se tourna -vers le président et dit: - ---D'abord.... - -Puis il regarda son bonnet, il regarda le plafond, et se tut. - ---Accusé, reprit l'avocat général d'une voix sévère, faites attention. -Vous ne répondez à rien de ce qu'on vous demande. Votre trouble vous -condamne. Il est évident que vous ne vous appelez pas Champmathieu, que -vous êtes le forçat Jean Valjean caché d'abord sous le nom de Jean -Mathieu qui était le nom de sa mère, que vous êtes allé en Auvergne, que -vous êtes né à Faverolles où vous avez été émondeur. Il est évident que -vous avez volé avec escalade des pommes mûres dans le clos Pierron. -Messieurs les jurés apprécieront. - -L'accusé avait fini par se rasseoir; il se leva brusquement quand -l'avocat général eut fini, et s'écria: - ---Vous êtes très méchant, vous! Voilà ce que je voulais dire. Je ne -trouvais pas d'abord. Je n'ai rien volé. Je suis un homme qui ne mange -pas tous les jours. Je venais d'Ailly, je marchais dans le pays après -une ondée qui avait fait la campagne toute jaune, même que les mares -débordaient et qu'il ne sortait plus des sables que de petits brins -d'herbe au bord de la route, j'ai trouvé une branche cassée par terre où -il y avait des pommes, j'ai ramassé la branche sans savoir qu'elle me -ferait arriver de la peine. Il y a trois mois que je suis en prison et -qu'on me trimballe. Après ça, je ne peux pas dire, on parle contre moi, -on me dit: répondez! le gendarme, qui est bon enfant, me pousse le coude -et me dit tout bas: réponds donc. Je ne sais pas expliquer, moi, je n'ai -pas fait les études, je suis un pauvre homme. Voilà ce qu'on a tort de -ne pas voir. Je n'ai pas volé, j'ai ramassé par terre des choses qu'il y -avait. Vous dites Jean Valjean, Jean Mathieu! Je ne connais pas ces -personnes-là. C'est des villageois. J'ai travaillé chez monsieur Baloup, -boulevard de l'Hôpital. Je m'appelle Champmathieu. Vous êtes bien malins -de me dire où je suis né. Moi, je l'ignore. Tout le monde n'a pas des -maisons pour y venir au monde. Ce serait trop commode. Je crois que mon -père et ma mère étaient des gens qui allaient sur les routes. Je ne sais -pas d'ailleurs. Quand j'étais enfant, on m'appelait Petit, maintenant, -on m'appelle Vieux. Voilà mes noms de baptême. Prenez ça comme vous -voudrez. J'ai été en Auvergne, j'ai été à Faverolles, pardi! Eh bien? -est-ce qu'on ne peut pas avoir été en Auvergne et avoir été à Faverolles -sans avoir été aux galères? Je vous dis que je n'ai pas volé, et que je -suis le père Champmathieu. J'ai été chez monsieur Baloup, j'ai été -domicilié. Vous m'ennuyez avec vos bêtises à la fin! Pourquoi donc -est-ce que le monde est après moi comme des acharnés! - -L'avocat général était demeuré debout; il s'adressa au président: - ---Monsieur le président, en présence des dénégations confuses, mais fort -habiles de l'accusé, qui voudrait bien se faire passer pour idiot, mais -qui n'y parviendra pas--nous l'en prévenons--nous requérons qu'il vous -plaise et qu'il plaise à la cour appeler de nouveau dans cette enceinte -les condamnés Brevet, Cochepaille et Chenildieu et l'inspecteur de -police Javert, et les interpeller une dernière fois sur l'identité de -l'accusé avec le forçat Jean Valjean. - ---Je fais remarquer à monsieur l'avocat général, dit le président, que -l'inspecteur de police Javert, rappelé par ses fonctions au chef-lieu -d'un arrondissement voisin, a quitté l'audience et même la ville, -aussitôt sa déposition faite. Nous lui en avons accordé l'autorisation, -avec l'agrément de monsieur l'avocat général et du défenseur de -l'accusé. - ---C'est juste, monsieur le président, reprit l'avocat général. En -l'absence du sieur Javert, je crois devoir rappeler à messieurs les -jurés ce qu'il a dit ici-même, il y a peu d'heures. Javert est un homme -estimé qui honore par sa rigoureuse et stricte probité des fonctions -inférieures, mais importantes. Voici en quels termes il a déposé:--«Je -n'ai pas même besoin des présomptions morales et des preuves matérielles -qui démentent les dénégations de l'accusé. Je le reconnais parfaitement. -Cet homme ne s'appelle pas Champmathieu; c'est un ancien forçat très -méchant et très redouté nommé Jean Valjean. On ne l'a libéré à -l'expiration de sa peine qu'avec un extrême regret. Il a subi dix-neuf -ans de travaux forcés pour vol qualifié. Il avait cinq ou six fois tenté -de s'évader. Outre le vol Petit-Gervais et le vol Pierron, je le -soupçonne encore d'un vol commis chez sa grandeur le défunt évêque de -Digne. Je l'ai souvent vu, à l'époque où j'étais adjudant garde-chiourme -au bagne de Toulon. Je répète que je le reconnais parfaitement.» Cette -déclaration si précise parut produire une vive impression sur le public -et le jury. L'avocat général termina en insistant pour qu'à défaut de -Javert, les trois témoins Brevet, Chenildieu et Cochepaille fussent -entendus de nouveau et interpellés solennellement. - -Le président transmit un ordre à un huissier, et un moment après la -porte de la chambre des témoins s'ouvrit. L'huissier, accompagné d'un -gendarme prêt à lui prêter main-forte, introduisit le condamné Brevet. -L'auditoire était en suspens et toutes les poitrines palpitaient comme -si elles n'eussent eu qu'une seule âme. - -L'ancien forçat Brevet portait la veste noire et grise des maisons -centrales. Brevet était un personnage d'une soixantaine d'années qui -avait une espèce de figure d'homme d'affaires et l'air d'un coquin. Cela -va quelquefois ensemble. Il était devenu, dans la prison où de nouveaux -méfaits l'avaient ramené, quelque chose comme guichetier. C'était un -homme dont les chefs disaient: Il cherche à se rendre utile. Les -aumôniers portaient bon témoignage de ses habitudes religieuses. Il ne -faut pas oublier que ceci se passait sous la restauration. - ---Brevet, dit le président, vous avez subi une condamnation infamante et -vous ne pouvez prêter serment.... - -Brevet baissa les yeux. - ---Cependant, reprit le président, même dans l'homme que la loi a -dégradé, il peut rester, quand la pitié divine le permet, un sentiment -d'honneur et d'équité. C'est à ce sentiment que je fais appel à cette -heure décisive. S'il existe encore en vous, et je l'espère, réfléchissez -avant de me répondre, considérez d'une part cet homme qu'un mot de vous -peut perdre, d'autre part la justice qu'un mot de vous peut éclairer. -L'instant est solennel, et il est toujours temps de vous rétracter, si -vous croyez vous être trompé.--Accusé, levez-vous. - ---Brevet, regardez bien l'accusé, recueillez vos souvenirs, et -dites-nous, en votre âme et conscience, si vous persistez à reconnaître -cet homme pour votre ancien camarade de bagne Jean Valjean. - -Brevet regarda l'accusé, puis se retourna vers la cour. - ---Oui, monsieur le président. C'est moi qui l'ai reconnu le premier et -je persiste. Cet homme est Jean Valjean. Entré à Toulon en 1796 et sorti -en 1815. Je suis sorti l'an d'après. Il a l'air d'une brute maintenant, -alors ce serait que l'âge l'a abruti; au bagne il était sournois. Je le -reconnais positivement. - ---Allez vous asseoir, dit le président. Accusé, restez debout. - -On introduisit Chenildieu, forçat à vie, comme l'indiquaient sa casaque -rouge et son bonnet vert. Il subissait sa peine au bagne de Toulon, d'où -on l'avait extrait pour cette affaire. C'était un petit homme d'environ -cinquante ans, vif, ridé, chétif, jaune, effronté, fiévreux, qui avait -dans tous ses membres et dans toute sa personne une sorte de faiblesse -maladive et dans le regard une force immense. Ses compagnons du bagne -l'avaient surnommé Je-nie-Dieu. - -Le président lui adressa à peu près les mêmes paroles qu'à Brevet. Au -moment où il lui rappela que son infamie lui ôtait le droit de prêter -serment, Chenildieu leva la tête et regarda la foule en face. Le -président l'invita à se recueillir et lui demanda, comme à Brevet, s'il -persistait à reconnaître l'accusé. - -Chenildieu éclata de rire. - ---Pardine! si je le reconnais! nous avons été cinq ans attachés à la -même chaîne. Tu boudes donc, mon vieux? - ---Allez vous asseoir, dit le président. - -L'huissier amena Cochepaille. Cet autre condamné à perpétuité, venu du -bagne et vêtu de rouge comme Chenildieu, était un paysan de Lourdes et -un demi-ours des Pyrénées. Il avait gardé des troupeaux dans la -montagne, et de pâtre il avait glissé brigand. Cochepaille n'était pas -moins sauvage et paraissait plus stupide encore que l'accusé. C'était un -de ces malheureux hommes que la nature a ébauchés en bêtes fauves et que -la société termine en galériens. - -Le président essaya de le remuer par quelques paroles pathétiques et -graves et lui demanda, comme aux deux autres, s'il persistait, sans -hésitation et sans trouble, à reconnaître l'homme debout devant lui. - ---C'est Jean Valjean, dit Cochepaille. Même qu'on l'appelait -Jean-le-Cric, tant il était fort. - -Chacune des affirmations de ces trois hommes, évidemment sincères et de -bonne foi, avait soulevé dans l'auditoire un murmure de fâcheux augure -pour l'accusé, murmure qui croissait et se prolongeait plus longtemps -chaque fois qu'une déclaration nouvelle venait s'ajouter à la -précédente. L'accusé, lui, les avait écoutées avec ce visage étonné qui, -selon l'accusation, était son principal moyen de défense. À la première, -les gendarmes ses voisins l'avaient entendu grommeler entre ses dents: -Ah bien! en voilà un! Après la seconde il dit un peu plus haut, d'un air -presque satisfait: Bon! À la troisième il s'écria: Fameux! - -Le président l'interpella. - ---Accusé, vous avez entendu. Qu'avez-vous à dire? - -Il répondit: - ---Je dis--Fameux! - -Une rumeur éclata dans le public et gagna presque le jury. Il était -évident que l'homme était perdu. - ---Huissiers, dit le président, faites faire silence. Je vais clore les -débats. - -En ce moment un mouvement se fit tout à côté du président. On entendit -une voix qui criait: - ---Brevet, Chenildieu, Cochepaille! regardez de ce côté-ci. - -Tous ceux qui entendirent cette voix se sentirent glacés, tant elle -était lamentable et terrible. Les yeux se tournèrent vers le point d'où -elle venait. Un homme, placé parmi les spectateurs privilégiés qui -étaient assis derrière la cour, venait de se lever, avait poussé la -porte à hauteur d'appui qui séparait le tribunal du prétoire, et était -debout au milieu de la salle. Le président, l'avocat général, M. -Bamatabois, vingt personnes, le reconnurent, et s'écrièrent à la fois: - ---Monsieur Madeleine! - - - - -Chapitre XI - -Champmathieu de plus en plus étonné - - -C'était lui en effet. La lampe du greffier éclairait son visage. Il -tenait son chapeau à la main, il n'y avait aucun désordre dans ses -vêtements, sa redingote était boutonnée avec soin. Il était très pâle et -il tremblait légèrement. Ses cheveux, gris encore au moment de son -arrivée à Arras, étaient maintenant tout à fait blancs. Ils avaient -blanchi depuis une heure qu'il était là. - -Toutes les têtes se dressèrent. La sensation fut indescriptible. Il y -eut dans l'auditoire un instant d'hésitation. La voix avait été si -poignante, l'homme qui était là paraissait si calme, qu'au premier abord -on ne comprit pas. On se demanda qui avait crié. On ne pouvait croire -que ce fût cet homme tranquille qui eût jeté ce cri effrayant. - -Cette indécision ne dura que quelques secondes. Avant même que le -président et l'avocat général eussent pu dire un mot, avant que les -gendarmes et les huissiers eussent pu faire un geste, l'homme que tous -appelaient encore en ce moment M. Madeleine s'était avancé vers les -témoins Cochepaille, Brevet et Chenildieu. - ---Vous ne me reconnaissez pas? dit-il. - -Tous trois demeurèrent interdits et indiquèrent par un signe de tête -qu'ils ne le connaissaient point. Cochepaille intimidé fit le salut -militaire. M. Madeleine se tourna vers les jurés et vers la cour et dit -d'une voix douce: - ---Messieurs les jurés, faites relâcher l'accusé. Monsieur le président, -faites-moi arrêter. L'homme que vous cherchez, ce n'est pas lui, c'est -moi. Je suis Jean Valjean. Pas une bouche ne respirait. À la première -commotion de l'étonnement avait succédé un silence de sépulcre. On -sentait dans la salle cette espèce de terreur religieuse qui saisit la -foule lorsque quelque chose de grand s'accomplit. - -Cependant le visage du président s'était empreint de sympathie et de -tristesse; il avait échangé un signe rapide avec l'avocat et quelques -paroles à voix basse avec les conseillers assesseurs. Il s'adressa au -public, et demanda avec un accent qui fut compris de tous: - ---Y a-t-il un médecin ici? - -L'avocat général prit la parole: - ---Messieurs les jurés, l'incident si étrange et si inattendu qui trouble -l'audience ne nous inspire, ainsi qu'à vous, qu'un sentiment que nous -n'avons pas besoin d'exprimer. Vous connaissez tous, au moins de -réputation, l'honorable M. Madeleine, maire de Montreuil-sur-mer. S'il y -a un médecin dans l'auditoire, nous nous joignons à monsieur le -président pour le prier de vouloir bien assister monsieur Madeleine et -le reconduire à sa demeure. - -M. Madeleine ne laissa point achever l'avocat général. - -Il l'interrompit d'un accent plein de mansuétude et d'autorité. Voici -les paroles qu'il prononça; les voici littéralement, telles qu'elles -furent écrites immédiatement après l'audience par un des témoins de -cette scène; telles qu'elles sont encore dans l'oreille de ceux qui les -ont entendues, il y a près de quarante ans aujourd'hui. - ---Je vous remercie, monsieur l'avocat général, mais je ne suis pas fou. -Vous allez voir. Vous étiez sur le point de commettre une grande erreur, -lâchez cet homme, j'accomplis un devoir, je suis ce malheureux condamné. -Je suis le seul qui voie clair ici, et je vous dis la vérité. Ce que je -fais en ce moment, Dieu, qui est là-haut, le regarde, et cela suffit. -Vous pouvez me prendre, puisque me voilà. J'avais pourtant fait de mon -mieux. Je me suis caché sous un nom; je suis devenu riche, je suis -devenu maire; j'ai voulu rentrer parmi les honnêtes gens. Il paraît que -cela ne se peut pas. Enfin, il y a bien des choses que je ne puis pas -dire, je ne vais pas vous raconter ma vie, un jour on saura. J'ai volé -monseigneur l'évêque, cela est vrai; j'ai volé Petit-Gervais, cela est -vrai. On a eu raison de vous dire que Jean Valjean était un malheureux -très méchant. Toute la faute n'est peut-être pas à lui. Écoutez, -messieurs les juges, un homme aussi abaissé que moi n'a pas de -remontrance à faire à la providence ni de conseil à donner à la société; -mais, voyez-vous, l'infamie d'où j'avais essayé de sortir est une chose -nuisible. Les galères font le galérien. Recueillez cela, si vous voulez. - -Avant le bagne, j'étais un pauvre paysan très peu intelligent, une -espèce d'idiot; le bagne m'a changé. J'étais stupide, je suis devenu -méchant; j'étais bûche, je suis devenu tison. Plus tard l'indulgence et -la bonté m'ont sauvé, comme la sévérité m'avait perdu. Mais, pardon, -vous ne pouvez pas comprendre ce que je dis là. Vous trouverez chez moi, -dans les cendres de la cheminée, la pièce de quarante sous que j'ai -volée il y a sept ans à Petit-Gervais. Je n'ai plus rien à ajouter. -Prenez-moi. Mon Dieu! monsieur l'avocat général remue la tête, vous -dites: M. Madeleine est devenu fou, vous ne me croyez pas! Voilà qui est -affligeant. N'allez point condamner cet homme au moins! Quoi! ceux-ci ne -me reconnaissent pas! Je voudrais que Javert fût ici. Il me -reconnaîtrait, lui! - -Rien ne pourrait rendre ce qu'il y avait de mélancolie bienveillante et -sombre dans l'accent qui accompagnait ces paroles. - -Il se tourna vers les trois forçats: - ---Eh bien, je vous reconnais, moi! Brevet! vous rappelez-vous?... - -Il s'interrompit, hésita un moment, et dit: - ---Te rappelles-tu ces bretelles en tricot à damier que tu avais au -bagne? - -Brevet eut comme une secousse de surprise et le regarda de la tête aux -pieds d'un air effrayé. Lui continua: - ---Chenildieu, qui te surnommais toi-même Je-nie-Dieu, tu as toute -l'épaule droite brûlée profondément, parce que tu t'es couché un jour -l'épaule sur un réchaud plein de braise, pour effacer les trois lettres -T. F. P., qu'on y voit toujours cependant. Réponds, est-ce vrai? - ---C'est vrai, dit Chenildieu. - -Il s'adressa à Cochepaille: - ---Cochepaille, tu as près de la saignée du bras gauche une date gravée -en lettres bleues avec de la poudre brûlée. Cette date, c'est celle du -débarquement de l'empereur à Cannes, _1er mars 1815_. Relève ta manche. - -Cochepaille releva sa manche, tous les regards se penchèrent autour de -lui sur son bras nu. Un gendarme approcha une lampe; la date y était. - -Le malheureux homme se tourna vers l'auditoire et vers les juges avec un -sourire dont ceux qui l'ont vu sont encore navrés lorsqu'ils y songent. -C'était le sourire du triomphe, c'était aussi le sourire du désespoir. - ---Vous voyez bien, dit-il, que je suis Jean Valjean. - -Il n'y avait plus dans cette enceinte ni juges, ni accusateurs, ni -gendarmes; il n'y avait que des yeux fixes et des coeurs émus. Personne -ne se rappelait plus le rôle que chacun pouvait avoir à jouer; l'avocat -général oubliait qu'il était là pour requérir, le président qu'il était -là pour présider, le défenseur qu'il était là pour défendre. Chose -frappante, aucune question ne fut faite, aucune autorité n'intervint. Le -propre des spectacles sublimes, c'est de prendre toutes les âmes et de -faire de tous les témoins des spectateurs. Aucun peut-être ne se rendait -compte de ce qu'il éprouvait; aucun, sans doute, ne se disait qu'il -voyait resplendir là une grande lumière; tous intérieurement se -sentaient éblouis. - -Il était évident qu'on avait sous les yeux Jean Valjean. Cela rayonnait. -L'apparition de cet homme avait suffi pour remplir de clarté cette -aventure si obscure le moment d'auparavant. Sans qu'il fût besoin -d'aucune explication désormais, toute cette foule, comme par une sorte -de révélation électrique, comprit tout de suite et d'un seul coup d'oeil -cette simple et magnifique histoire d'un homme qui se livrait pour qu'un -autre homme ne fût pas condamné à sa place. Les détails, les -hésitations, les petites résistances possibles se perdirent dans ce -vaste fait lumineux. - -Impression qui passa vite, mais qui dans l'instant fut irrésistible. - ---Je ne veux pas déranger davantage l'audience, reprit Jean Valjean. Je -m'en vais, puisqu'on ne m'arrête pas. J'ai plusieurs choses à faire. -Monsieur l'avocat général sait qui je suis, il sait où je vais, il me -fera arrêter quand il voudra. - -Il se dirigea vers la porte de sortie. Pas une voix ne s'éleva, pas un -bras ne s'étendit pour l'empêcher. Tous s'écartèrent. Il avait en ce -moment ce je ne sais quoi de divin qui fait que les multitudes reculent -et se rangent devant un homme. Il traversa la foule à pas lents. On n'a -jamais su qui ouvrit la porte, mais il est certain que la porte se -trouva ouverte lorsqu'il y parvint. Arrivé là, il se retourna et dit: - ---Monsieur l'avocat général, je reste à votre disposition. - -Puis il s'adressa à l'auditoire: - ---Vous tous, tous ceux qui sont ici, vous me trouvez digne de pitié, -n'est-ce pas? Mon Dieu! quand je pense à ce que j'ai été sur le point de -faire, je me trouve digne d'envie. Cependant j'aurais mieux aimé que -tout ceci n'arrivât pas. - -Il sortit, et la porte se referma comme elle avait été ouverte, car ceux -qui font de certaines choses souveraines sont toujours sûrs d'être -servis par quelqu'un dans la foule. - -Moins d'une heure après, le verdict du jury déchargeait de toute -accusation le nommé Champmathieu; et Champmathieu, mis en liberté -immédiatement, s'en allait stupéfait, croyant tous les hommes fous et ne -comprenant rien à cette vision. - - - - -Livre huitième--Contre-coup - - - - -Chapitre I - -Dans quel miroir M. Madeleine regarde ses cheveux - - -Le jour commençait à poindre. Fantine avait eu une nuit de fièvre et -d'insomnie, pleine d'ailleurs d'images heureuses; au matin, elle -s'endormit. La soeur Simplice qui l'avait veillée profita de ce sommeil -pour aller préparer une nouvelle potion de quinquina. La digne soeur -était depuis quelques instants dans le laboratoire de l'infirmerie, -penchée sur ses drogues et sur ses fioles et regardant de très près à -cause de cette brume que le crépuscule répand sur les objets. Tout à -coup elle tourna la tête et fit un léger cri. M. Madeleine était devant -elle. Il venait d'entrer silencieusement. - ---C'est vous, monsieur le maire! s'écria-t-elle. - -Il répondit, à voix basse: - ---Comment va cette pauvre femme? - ---Pas mal en ce moment. Mais nous avons été bien inquiets, allez! - -Elle lui expliqua ce qui s'était passé, que Fantine était bien mal la -veille et que maintenant elle était mieux, parce qu'elle croyait que -monsieur le maire était allé chercher son enfant à Montfermeil. La soeur -n'osa pas interroger monsieur le maire, mais elle vit bien à son air que -ce n'était point de là qu'il venait. - ---Tout cela est bien, dit-il, vous avez eu raison de ne pas la -détromper. - ---Oui, reprit la soeur, mais maintenant, monsieur le maire, qu'elle va -vous voir et qu'elle ne verra pas son enfant, que lui dirons-nous? - -Il resta un moment rêveur. - ---Dieu nous inspirera, dit-il. - ---On ne pourrait cependant pas mentir, murmura la soeur à demi-voix. - -Le plein jour s'était fait dans la chambre. Il éclairait en face le -visage de M. Madeleine. Le hasard fit que la soeur leva les yeux. - ---Mon Dieu, monsieur! s'écria-t-elle, que vous est-il donc arrivé? vos -cheveux sont tout blancs! - ---Blancs! dit-il. - -La soeur Simplice n'avait point de miroir; elle fouilla dans une trousse -et en tira une petite glace dont se servait le médecin de l'infirmerie -pour constater qu'un malade était mort et ne respirait plus. M. -Madeleine prit la glace, y considéra ses cheveux, et dit: - ---Tiens! - -Il prononça ce mot avec indifférence et comme s'il pensait à autre -chose. - -La soeur se sentit glacée par je ne sais quoi d'inconnu qu'elle -entrevoyait dans tout ceci. - -Il demanda: - ---Puis-je la voir? - ---Est-ce que monsieur le maire ne lui fera pas revenir son enfant? dit -la soeur, osant à peine hasarder une question. - ---Sans doute, mais il faut au moins deux ou trois jours. - ---Si elle ne voyait pas monsieur le maire d'ici là, reprit timidement la -soeur, elle ne saurait pas que monsieur le maire est de retour, il -serait aisé de lui faire prendre patience, et quand l'enfant arriverait -elle penserait tout naturellement que monsieur le maire est arrivé avec -l'enfant. On n'aurait pas de mensonge à faire. - -M. Madeleine parut réfléchir quelques instants, puis il dit avec sa -gravité calme: - ---Non, ma soeur, il faut que je la voie. Je suis peut-être pressé. - -La religieuse ne sembla pas remarquer ce mot «peut-être», qui donnait un -sens obscur et singulier aux paroles de M. le maire. Elle répondit en -baissant les yeux et la voix respectueusement: - ---En ce cas, elle repose, mais monsieur le maire peut entrer. - -Il fit quelques observations sur une porte qui fermait mal, et dont le -bruit pouvait réveiller la malade, puis il entra dans la chambre de -Fantine, s'approcha du lit et entrouvrit les rideaux. Elle dormait. Son -souffle sortait de sa poitrine avec ce bruit tragique qui est propre à -ces maladies, et qui navre les pauvres mères lorsqu'elles veillent la -nuit près de leur enfant condamné et endormi. Mais cette respiration -pénible troublait à peine une sorte de sérénité ineffable, répandue sur -son visage, qui la transfigurait dans son sommeil. Sa pâleur était -devenue de la blancheur; ses joues étaient vermeilles. Ses longs cils -blonds, la seule beauté qui lui fût restée de sa virginité et de sa -jeunesse, palpitaient tout en demeurant clos et baissés. Toute sa -personne tremblait de je ne sais quel déploiement d'ailes prêtes à -s'entrouvrir et à l'emporter, qu'on sentait frémir, mais qu'on ne voyait -pas. À la voir ainsi, on n'eût jamais pu croire que c'était là une -malade presque désespérée. Elle ressemblait plutôt à ce qui va s'envoler -qu'à ce qui va mourir. - -La branche, lorsqu'une main s'approche pour détacher la fleur, -frissonne, et semble à la fois se dérober et s'offrir. Le corps humain a -quelque chose de ce tressaillement, quand arrive l'instant où les doigts -mystérieux de la mort vont cueillir l'âme. - -M. Madeleine resta quelque temps immobile près de ce lit, regardant tour -à tour la malade et le crucifix, comme il faisait deux mois auparavant, -le jour où il était venu pour la première fois la voir dans cet asile. -Ils étaient encore là tous les deux dans la même attitude, elle dormant, -lui priant; seulement maintenant, depuis ces deux mois écoulés, elle -avait des cheveux gris et lui des cheveux blancs. - -La soeur n'était pas entrée avec lui. Il se tenait près de ce lit, -debout, le doigt sur la bouche, comme s'il y eût eu dans la chambre -quelqu'un à faire taire. - -Elle ouvrit les yeux, le vit, et dit paisiblement, avec un sourire: - ---Et Cosette? - - - - -Chapitre II - -Fantine heureuse - - -Elle n'eut pas un mouvement de surprise, ni un mouvement de joie; elle -était la joie même. Cette simple question: «Et Cosette?» fut faite avec -une foi si profonde, avec tant de certitude, avec une absence si -complète d'inquiétude et de doute, qu'il ne trouva pas une parole. Elle -continua: - ---Je savais que vous étiez là. Je dormais, mais je vous voyais. Il y a -longtemps que je vous vois. Je vous ai suivi des yeux toute la nuit. -Vous étiez dans une gloire et vous aviez autour de vous toutes sortes de -figures célestes. - -Il leva son regard vers le crucifix. - ---Mais, reprit-elle, dites-moi donc où est Cosette? Pourquoi ne l'avoir -pas mise sur mon lit pour le moment où je m'éveillerais? - -Il répondit machinalement quelque chose qu'il n'a jamais pu se rappeler -plus tard. - -Heureusement le médecin, averti, était survenu. Il vint en aide à M. -Madeleine. - ---Mon enfant, dit le médecin, calmez-vous. Votre enfant est là. - -Les yeux de Fantine s'illuminèrent et couvrirent de clarté tout son -visage. Elle joignit les mains avec une expression qui contenait tout ce -que la prière peut avoir à la fois de plus violent et de plus doux. - ---Oh! s'écria-t-elle, apportez-la-moi! - -Touchante illusion de mère! Cosette était toujours pour elle le petit -enfant qu'on apporte. - ---Pas encore, reprit le médecin, pas en ce moment. Vous avez un reste de -fièvre. La vue de votre enfant vous agiterait et vous ferait du mal. Il -faut d'abord vous guérir. Elle l'interrompit impétueusement. - ---Mais je suis guérie! je vous dis que je suis guérie! Est-il âne, ce -médecin! Ah çà! je veux voir mon enfant, moi! - ---Vous voyez, dit le médecin, comme vous vous emportez. Tant que vous -serez ainsi, je m'opposerai à ce que vous ayez votre enfant. Il ne -suffit pas de la voir, il faut vivre pour elle. Quand vous serez -raisonnable, je vous l'amènerai moi-même. - -La pauvre mère courba la tête. - ---Monsieur le médecin, je vous demande pardon, je vous demande vraiment -bien pardon. Autrefois, je n'aurais pas parlé comme je viens de faire, -il m'est arrivé tant de malheurs que quelquefois je ne sais plus ce que -je dis. Je comprends, vous craignez l'émotion, j'attendrai tant que vous -voudrez, mais je vous jure que cela ne m'aurait pas fait de mal de voir -ma fille. Je la vois, je ne la quitte pas des yeux depuis hier au soir. -Savez-vous? on me l'apporterait maintenant que je me mettrais à lui -parler doucement. Voilà tout. Est-ce que ce n'est pas bien naturel que -j'aie envie de voir mon enfant qu'on a été me chercher exprès à -Montfermeil? Je ne suis pas en colère. Je sais bien que je vais être -heureuse. Toute la nuit j'ai vu des choses blanches et des personnes qui -me souriaient. Quand monsieur le médecin voudra, il m'apportera ma -Cosette. Je n'ai plus de fièvre, puisque je suis guérie; je sens bien -que je n'ai plus rien du tout; mais je vais faire comme si j'étais -malade et ne pas bouger pour faire plaisir aux dames d'ici. Quand on -verra que je suis bien tranquille, on dira: il faut lui donner son -enfant. - -M. Madeleine s'était assis sur une chaise qui était à côté du lit. Elle -se tourna vers lui; elle faisait visiblement effort pour paraître calme -et «bien sage», comme elle disait dans cet affaiblissement de la maladie -qui ressemble à l'enfance, afin que, la voyant si paisible, on ne fît -pas difficulté de lui amener Cosette. Cependant, tout en se contenant, -elle ne pouvait s'empêcher d'adresser à M. Madeleine mille questions. - ---Avez-vous fait un bon voyage, monsieur le maire? Oh! comme vous êtes -bon d'avoir été me la chercher! Dites-moi seulement comment elle est. -A-t-elle bien supporté la route? Hélas! elle ne me reconnaîtra pas! -Depuis le temps, elle m'a oubliée, pauvre chou! Les enfants, cela n'a -pas de mémoire. C'est comme des oiseaux. Aujourd'hui cela voit une chose -et demain une autre, et cela ne pense plus à rien. Avait-elle du linge -blanc seulement? Ces Thénardier la tenaient-ils proprement? Comment la -nourrissait-on? Oh! comme j'ai souffert, si vous saviez! de me faire -toutes ces questions-là dans le temps de ma misère! Maintenant, c'est -passé. Je suis joyeuse. Oh! que je voudrais donc la voir! Monsieur le -maire, l'avez-vous trouvée jolie? N'est-ce pas qu'elle est belle, ma -fille? Vous devez avoir eu bien froid dans cette diligence! Est-ce qu'on -ne pourrait pas l'amener rien qu'un petit moment? On la remporterait -tout de suite après. Dites! vous qui êtes le maître, si vous vouliez! - -Il lui prit la main: - ---Cosette est belle, dit-il, Cosette se porte bien, vous la verrez -bientôt, mais apaisez-vous. Vous parlez trop vivement, et puis vous -sortez vos bras du lit, et cela vous fait tousser. - -En effet, des quintes de toux interrompaient Fantine presque à chaque -mot. - -Fantine ne murmura pas, elle craignait d'avoir compromis par quelques -plaintes trop passionnées la confiance qu'elle voulait inspirer, et elle -se mit à dire des paroles indifférentes. - ---C'est assez joli, Montfermeil, n'est-ce-pas? L'été, on va y faire des -parties de plaisir. Ces Thénardier font-ils de bonnes affaires? Il ne -passe pas grand monde dans leur pays. C'est une espèce de gargote que -cette auberge-là. - -M. Madeleine lui tenait toujours la main, il la considérait avec -anxiété; il était évident qu'il était venu pour lui dire des choses -devant lesquelles sa pensée hésitait maintenant. Le médecin, sa visite -faite, s'était retiré. La soeur Simplice était seule restée auprès -d'eux. - -Cependant, au milieu de ce silence, Fantine s'écria: - ---Je l'entends! mon Dieu! je l'entends! - -Elle étendit le bras pour qu'on se tût autour d'elle, retint son -souffle, et se mit à écouter avec ravissement. - -Il y avait un enfant qui jouait dans la cour; l'enfant de la portière ou -d'une ouvrière quelconque. C'est là un de ces hasards qu'on retrouve -toujours et qui semblent faire partie de la mystérieuse mise en scène -des événements lugubres. L'enfant, c'était une petite fille, allait, -venait, courait pour se réchauffer, riait et chantait à haute voix. -Hélas! à quoi les jeux des enfants ne se mêlent-ils pas! C'était cette -petite fille que Fantine entendait chanter. - ---Oh! reprit-elle, c'est ma Cosette! je reconnais sa voix! - -L'enfant s'éloigna comme il était venu, la voix s'éteignit, Fantine -écouta encore quelque temps, puis son visage s'assombrit, et M. -Madeleine l'entendit qui disait à voix basse: - ---Comme ce médecin est méchant de ne pas me laisser voir ma fille! Il a -une mauvaise figure, cet homme-là! - -Cependant le fond riant de ses idées revint. Elle continua de se parler -à elle-même, la tête sur l'oreiller. - ---Comme nous allons être heureuses! Nous aurons un petit jardin, -d'abord! M. Madeleine me l'a promis. Ma fille jouera dans le jardin. -Elle doit savoir ses lettres maintenant. Je la ferai épeler. Elle courra -dans l'herbe après les papillons. Je la regarderai. Et puis elle fera sa -première communion. Ah çà! quand fera-t-elle sa première communion? Elle -se mit à compter sur ses doigts. - ---... Un, deux, trois, quatre... elle a sept ans. Dans cinq ans. Elle -aura un voile blanc, des bas à jour, elle aura l'air d'une petite femme. -Ô ma bonne soeur, vous ne savez pas comme je suis bête, voilà que je -pense à la première communion de ma fille! Et elle se mit à rire. - -Il avait quitté la main de Fantine. Il écoutait ces paroles comme on -écoute un vent qui souffle, les yeux à terre, l'esprit plongé dans des -réflexions sans fond. Tout à coup elle cessa de parler, cela lui fit -lever machinalement la tête. Fantine était devenue effrayante. - -Elle ne parlait plus, elle ne respirait plus; elle s'était soulevée à -demi sur son séant, son épaule maigre sortait de sa chemise, son visage, -radieux le moment d'auparavant, était blême, et elle paraissait fixer -sur quelque chose de formidable, devant elle, à l'autre extrémité de la -chambre, son oeil agrandi par la terreur. - ---Mon Dieu! s'écria-t-il. Qu'avez-vous, Fantine? - -Elle ne répondit pas, elle ne quitta point des yeux l'objet quelconque -qu'elle semblait voir, elle lui toucha le bras d'une main et de l'autre -lui fit signe de regarder derrière lui. - -Il se retourna, et vit Javert. - - - - -Chapitre III - -Javert content - - -Voici ce qui s'était passé. - -Minuit et demi venait de sonner, quand M. Madeleine était sorti de la -salle des assises d'Arras. Il était rentré à son auberge juste à temps -pour repartir par la malle-poste où l'on se rappelle qu'il avait retenu -sa place. Un peu avant six heures du matin, il était arrivé à -Montreuil-sur-mer, et son premier soin avait été de jeter à la poste sa -lettre à M. Laffitte, puis d'entrer à l'infirmerie et de voir Fantine. - -Cependant, à peine avait-il quitté la salle d'audience de la cour -d'assises, que l'avocat général, revenu du premier saisissement, avait -pris la parole pour déplorer l'acte de folie de l'honorable maire de -Montreuil-sur-mer, déclarer que ses convictions n'étaient en rien -modifiées par cet incident bizarre qui s'éclaircirait plus tard, et -requérir, en attendant, la condamnation de ce Champmathieu, évidemment -le vrai Jean Valjean. La persistance de l'avocat général était -visiblement en contradiction avec le sentiment de tous, du public, de la -cour et du jury. Le défenseur avait eu peu de peine à réfuter cette -harangue et à établir que, par suite des révélations de M. Madeleine, -c'est-à-dire du vrai Jean Valjean, la face de l'affaire était -bouleversée de fond en comble, et que le jury n'avait plus devant les -yeux qu'un innocent. L'avocat avait tiré de là quelques épiphonèmes, -malheureusement peu neufs, sur les erreurs judiciaires, etc., etc., le -président dans son résumé s'était joint au défenseur, et le jury en -quelques minutes avait mis hors de cause Champmathieu. - -Cependant il fallait un Jean Valjean à l'avocat général, et, n'ayant -plus Champmathieu, il prit Madeleine. - -Immédiatement après la mise en liberté de Champmathieu, l'avocat général -s'enferma avec le président. Ils conférèrent «de la nécessité de se -saisir de la personne de M. le maire de Montreuil-sur-mer». Cette -phrase, où il y a beaucoup de _de_, est de M. l'avocat général, -entièrement écrite de sa main sur la minute de son rapport au procureur -général. La première émotion passée, le président fit peu d'objections. -Il fallait bien que justice eût son cours. Et puis, pour tout dire, -quoique le président fût homme bon et assez intelligent, il était en -même temps fort royaliste et presque ardent, et il avait été choqué que -le maire de Montreuil-sur-mer, en parlant du débarquement à Cannes, eût -dit l'_empereur_ et non _Buonaparte_. - -L'ordre d'arrestation fut donc expédié. L'avocat général l'envoya à -Montreuil-sur-mer par un exprès, à franc étrier, et en chargea -l'inspecteur de police Javert. - -On sait que Javert était revenu à Montreuil-sur-mer immédiatement après -avoir fait sa déposition. - -Javert se levait au moment où l'exprès lui remit l'ordre d'arrestation -et le mandat d'amener. - -L'exprès était lui-même un homme de police fort entendu qui, en deux -mots, mit Javert au fait de ce qui était arrivé à Arras. L'ordre -d'arrestation, signé de l'avocat général, était ainsi -conçu:--L'inspecteur Javert appréhendera au corps le sieur Madeleine, -maire de Montreuil-sur-mer, qui, dans l'audience de ce jour, a été -reconnu pour être le forçat libéré Jean Valjean. - -Quelqu'un qui n'eût pas connu Javert et qui l'eût vu au moment où il -pénétra dans l'antichambre de l'infirmerie n'eût pu rien deviner de ce -qui se passait, et lui eût trouvé l'air le plus ordinaire du monde. Il -était froid, calme, grave, avait ses cheveux gris parfaitement lissés -sur les tempes et venait de monter l'escalier avec sa lenteur -habituelle. Quelqu'un qui l'eût connu à fond et qui l'eût examiné -attentivement eût frémi. La boucle de son col de cuir, au lieu d'être -sur sa nuque, était sur son oreille gauche. Ceci révélait une agitation -inouïe. - -Javert était un caractère complet, ne laissant faire de pli ni à son -devoir, ni à son uniforme; méthodique avec les scélérats, rigide avec -les boutons de son habit. - -Pour qu'il eût mal mis la boucle de son col, il fallait qu'il y eût en -lui une de ces émotions qu'on pourrait appeler des tremblements de terre -intérieurs. - -Il était venu simplement, avait requis un caporal et quatre soldats au -poste voisin, avait laissé les soldats dans la cour, et s'était fait -indiquer la chambre de Fantine par la portière sans défiance, accoutumée -qu'elle était à voir des gens armés demander monsieur le maire. - -Arrivé à la chambre de Fantine, Javert tourna la clef, poussa la porte -avec une douceur de garde-malade ou de mouchard, et entra. - -À proprement parler, il n'entra pas. Il se tint debout dans la porte -entrebâillée, le chapeau sur la tête, la main gauche dans sa redingote -fermée jusqu'au menton. Dans le pli du coude on pouvait voir le pommeau -de plomb de son énorme canne, laquelle disparaissait derrière lui. - -Il resta ainsi près d'une minute sans qu'on s'aperçût de sa présence. -Tout à coup Fantine leva les yeux, le vit, et fit retourner M. -Madeleine. - -À l'instant où le regard de Madeleine rencontra le regard de Javert, -Javert, sans bouger, sans remuer, sans approcher, devint épouvantable. -Aucun sentiment humain ne réussit à être effroyable comme la joie. - -Ce fut le visage d'un démon qui vient de retrouver son damné. - -La certitude de tenir enfin Jean Valjean fit apparaître sur sa -physionomie tout ce qu'il avait dans l'âme. Le fond remué monta à la -surface. L'humiliation d'avoir un peu perdu la piste et de s'être mépris -quelques minutes sur ce Champmathieu, s'effaçait sous l'orgueil d'avoir -si bien deviné d'abord et d'avoir eu si longtemps un instinct juste. Le -contentement de Javert éclata dans son attitude souveraine. La -difformité du triomphe s'épanouit sur ce front étroit. Ce fut tout le -déploiement d'horreur que peut donner une figure satisfaite. - -Javert en ce moment était au ciel. Sans qu'il s'en rendit nettement -compte, mais pourtant avec une intuition confuse de sa nécessité et de -son succès, il personnifiait, lui Javert, la justice, la lumière et la -vérité dans leur fonction céleste d'écrasement du mal. Il avait derrière -lui et autour de lui, à une profondeur infinie, l'autorité, la raison, -la chose jugée, la conscience légale, la vindicte publique, toutes les -étoiles; il protégeait l'ordre, il faisait sortir de la loi la foudre, -il vengeait la société, il prêtait main-forte à l'absolu; il se dressait -dans une gloire; il y avait dans sa victoire un reste de défi et de -combat; debout, altier, éclatant, il étalait en plein azur la bestialité -surhumaine d'un archange féroce; l'ombre redoutable de l'action qu'il -accomplissait faisait visible à son poing crispé le vague flamboiement -de l'épée sociale; heureux et indigné, il tenait sous son talon le -crime, le vice, la rébellion, la perdition, l'enfer, il rayonnait, il -exterminait, il souriait et il y avait une incontestable grandeur dans -ce saint Michel monstrueux. - -Javert, effroyable, n'avait rien d'ignoble. - -La probité, la sincérité, la candeur, la conviction, l'idée du devoir, -sont des choses qui, en se trompant, peuvent devenir hideuses, mais qui, -même hideuses, restent grandes; leur majesté, propre à la conscience -humaine, persiste dans l'horreur. Ce sont des vertus qui ont un vice, -l'erreur. L'impitoyable joie honnête d'un fanatique en pleine atrocité -conserve on ne sait quel rayonnement lugubrement vénérable. Sans qu'il -s'en doutât, Javert, dans son bonheur formidable, était à plaindre comme -tout ignorant qui triomphe. Rien n'était poignant et terrible comme -cette figure où se montrait ce qu'on pourrait appeler tout le mauvais du -bon. - - - - -Chapitre IV - -L'autorité reprend ses droits - - -La Fantine n'avait point vu Javert depuis le jour où M. le maire l'avait -arrachée à cet homme. Son cerveau malade ne se rendit compte de rien, -seulement elle ne douta pas qu'il ne revint la chercher. Elle ne put -supporter cette figure affreuse, elle se sentit expirer, elle cacha son -visage de ses deux mains et cria avec angoisse: - ---Monsieur Madeleine, sauvez-moi! - -Jean Valjean--nous ne le nommerons plus désormais autrement--s'était -levé. Il dit à Fantine de sa voix la plus douce et la plus calme: - ---Soyez tranquille. Ce n'est pas pour vous qu'il vient. - -Puis il s'adressa à Javert et lui dit: - ---Je sais ce que vous voulez. - -Javert répondit: - ---Allons, vite! - -Il y eut dans l'inflexion qui accompagna ces deux mots je ne sais quoi -de fauve et de frénétique. Javert ne dit pas: «Allons, vite!» il dit: -«Allonouaite!» Aucune orthographe ne pourrait rendre l'accent dont cela -fut prononcé; ce n'était plus une parole humaine, c'était un -rugissement. - -Il ne fit point comme d'habitude; il n'entra point en matière; il -n'exhiba point de mandat d'amener. Pour lui, Jean Valjean était une -sorte de combattant mystérieux et insaisissable, un lutteur ténébreux -qu'il étreignait depuis cinq ans sans pouvoir le renverser. Cette -arrestation n'était pas un commencement, mais une fin. Il se borna à -dire: «Allons, vite!» - -En parlant ainsi, il ne fit point un pas; il lança sur Jean Valjean ce -regard qu'il jetait comme un crampon, et avec lequel il avait coutume de -tirer violemment les misérables à lui. - -C'était ce regard que la Fantine avait senti pénétrer jusque dans la -moelle de ses os deux mois auparavant. - -Au cri de Javert, Fantine avait rouvert les yeux. Mais M. le maire était -là. Que pouvait-elle craindre? - -Javert avança au milieu de la chambre et cria: - ---Ah çà! viendras-tu? - -La malheureuse regarda autour d'elle. Il n'y avait personne que la -religieuse et monsieur le maire. À qui pouvait s'adresser ce tutoiement -abject? elle seulement. Elle frissonna. - -Alors elle vit une chose inouïe, tellement inouïe que jamais rien de -pareil ne lui était apparu dans les plus noirs délires de la fièvre. - -Elle vit le mouchard Javert saisir au collet monsieur le maire; elle vit -monsieur le maire courber la tête. Il lui sembla que le monde -s'évanouissait. - -Javert, en effet, avait pris Jean Valjean au collet. - ---Monsieur le maire! cria Fantine. - -Javert éclata de rire, de cet affreux rire qui lui déchaussait toutes -les dents. - ---Il n'y a plus de monsieur le maire ici! - -Jean Valjean n'essaya pas de déranger la main qui tenait le col de sa -redingote. Il dit: - ---Javert.... - -Javert l'interrompit: - ---Appelle-moi monsieur l'inspecteur. - ---Monsieur, reprit Jean Valjean, je voudrais vous dire un mot en -particulier. - ---Tout haut! parle tout haut! répondit Javert; on me parle tout haut à -moi! - -Jean Valjean continua en baissant la voix: - ---C'est une prière que j'ai à vous faire.... - ---Je te dis de parler tout haut. - ---Mais cela ne doit être entendu que de vous seul.... - ---Qu'est-ce que cela me fait? je n'écoute pas! - -Jean Valjean se tourna vers lui et lui dit rapidement et très bas: - ---Accordez-moi trois jours! trois jours pour aller chercher l'enfant de -cette malheureuse femme! Je payerai ce qu'il faudra. Vous -m'accompagnerez si vous voulez. - ---Tu veux rire! cria Javert. Ah çà! je ne te croyais pas bête! Tu me -demandes trois jours pour t'en aller! Tu dis que c'est pour aller -chercher l'enfant de cette fille! Ah! ah! c'est bon! voilà qui est bon! -Fantine eut un tremblement. - ---Mon enfant! s'écria-t-elle, aller chercher mon enfant! Elle n'est donc -pas ici! Ma soeur, répondez-moi, où est Cosette? Je veux mon enfant! -Monsieur Madeleine! monsieur le maire! - -Javert frappa du pied. - ---Voilà l'autre, à présent! Te tairas-tu, drôlesse! Gredin de pays où -les galériens sont magistrats et où les filles publiques sont soignées -comme des comtesses! Ah mais! tout ça va changer; il était temps! - -Il regarda fixement Fantine et ajouta en reprenant à poignée la cravate, -la chemise et le collet de Jean Valjean: - ---Je te dis qu'il n'y a point de monsieur Madeleine et qu'il n'y a point -de monsieur le maire. Il y a un voleur, il y a un brigand, il y a un -forçat appelé Jean Valjean! c'est lui que je tiens! voilà ce qu'il y a! - -Fantine se dressa en sursaut, appuyée sur ses bras roides et sur ses -deux mains, elle regarda Jean Valjean, elle regarda Javert, elle regarda -la religieuse, elle ouvrit la bouche comme pour parler, un râle sortit -du fond de sa gorge, ses dents claquèrent, elle étendit les bras avec -angoisse, ouvrant convulsivement les mains, et cherchant autour d'elle -comme quelqu'un qui se noie, puis elle s'affaissa subitement sur -l'oreiller. Sa tête heurta le chevet du lit et vint retomber sur sa -poitrine, la bouche béante, les yeux ouverts et éteints. - -Elle était morte. - -Jean Valjean posa sa main sur la main de Javert qui le tenait, et -l'ouvrit comme il eût ouvert la main d'un enfant, puis il dit à Javert: - ---Vous avez tué cette femme. - ---Finirons-nous! cria Javert furieux. Je ne suis pas ici pour entendre -des raisons. Économisons tout ça. La garde est en bas. Marchons tout de -suite, ou les poucettes! - -Il y avait dans un coin de la chambre un vieux lit en fer en assez -mauvais état qui servait de lit de camp aux soeurs quand elles -veillaient. Jean Valjean alla à ce lit, disloqua en un clin d'oeil le -chevet déjà fort délabré, chose facile à des muscles comme les siens, -saisit à poigne-main la maîtresse-tringle, et considéra Javert. Javert -recula vers la porte. - -Jean Valjean, sa barre de fer au poing, marcha lentement vers le lit de -Fantine. Quand il y fut parvenu, il se retourna, et dit à Javert d'une -voix qu'on entendait à peine: - ---Je ne vous conseille pas de me déranger en ce moment. - -Ce qui est certain, c'est que Javert tremblait. - -Il eut l'idée d'aller appeler la garde, mais Jean Valjean pouvait -profiter de cette minute pour s'évader. Il resta donc, saisit sa canne -par le petit bout, et s'adossa au chambranle de la porte sans quitter du -regard Jean Valjean. - -Jean Valjean posa son coude sur la pomme du chevet du lit et son front -sur sa main, et se mit à contempler Fantine immobile et étendue. Il -demeura ainsi, absorbé, muet, et ne songeant évidemment plus à aucune -chose de cette vie. Il n'y avait plus rien sur son visage et dans son -attitude qu'une inexprimable pitié. Après quelques instants de cette -rêverie, il se pencha vers Fantine et lui parla à voix basse. - -Que lui dit-il? Que pouvait dire cet homme qui était réprouvé à cette -femme qui était morte? Qu'était-ce que ces paroles? Personne sur la -terre ne les a entendues. La morte les entendit-elle? Il y a des -illusions touchantes qui sont peut-être des réalités sublimes. Ce qui -est hors de doute, c'est que la soeur Simplice, unique témoin de la -chose qui se passait, a souvent raconté qu'au moment où Jean Valjean -parla à l'oreille de Fantine, elle vit distinctement poindre un -ineffable sourire sur ces lèvres pâles et dans ces prunelles vagues, -pleines de l'étonnement du tombeau. - -Jean Valjean prit dans ses deux mains la tête de Fantine et l'arrangea -sur l'oreiller comme une mère eût fait pour son enfant, il lui rattacha -le cordon de sa chemise et rentra ses cheveux sous son bonnet. Cela -fait, il lui ferma les yeux. - -La face de Fantine en cet instant semblait étrangement éclairée. - -La mort, c'est l'entrée dans la grande lueur. - -La main de Fantine pendait hors du lit. Jean Valjean s'agenouilla devant -cette main, la souleva doucement, et la baisa. - -Puis il se redressa, et, se tournant vers Javert: - ---Maintenant, dit-il, je suis à vous. - - - - -Chapitre V - -Tombeau convenable - - -Javert déposa Jean Valjean à la prison de la ville. - -L'arrestation de M. Madeleine produisit à Montreuil-sur-mer une -sensation, ou pour mieux dire une commotion extraordinaire. Nous sommes -triste de ne pouvoir dissimuler que sur ce seul mot: _c'était un -galérien_, tout le monde à peu près l'abandonna. En moins de deux heures -tout le bien qu'il avait fait fut oublié, et ce ne fut plus «qu'un -galérien». Il est juste de dire qu'on ne connaissait pas encore les -détails de l'événement d'Arras. Toute la journée on entendait dans -toutes les parties de la ville des conversations comme celle-ci: - ---Vous ne savez pas? c'était un forçat libéré! Qui ça?--Le maire.--Bah! -M. Madeleine?--Oui. Vraiment?--Il ne s'appelait pas Madeleine, il a un -affreux nom, Béjean, Bojean, Boujean.--Ah, mon Dieu!--Il est -arrêté.--Arrêté!--En prison à la prison de la ville, en attendant qu'on -le transfère.--Qu'on le transfère! On va le transférer! Où va-t-on le -transférer?--Il va passer aux assises pour un vol de grand chemin qu'il -a fait autrefois.--Eh bien! je m'en doutais. Cet homme était trop bon, -trop parfait, trop confit. Il refusait la croix, il donnait des sous à -tous les petits drôles qu'il rencontrait. J'ai toujours pensé qu'il y -avait là-dessous quelque mauvaise histoire. - -«Les salons» surtout abondèrent dans ce sens. - -Une vieille dame, abonnée au _Drapeau blanc_, fit cette réflexion dont -il est presque impossible de sonder la profondeur: - ---Je n'en suis pas fâchée. Cela apprendra aux buonapartistes! - -C'est ainsi que ce fantôme qui s'était appelé M. Madeleine se dissipa à -Montreuil-sur-mer. Trois ou quatre personnes seulement dans toute la -ville restèrent fidèles à cette mémoire. La vieille portière qui l'avait -servi fut du nombre. Le soir de ce même jour, cette digne vieille était -assise dans sa loge, encore tout effarée et réfléchissant tristement. La -fabrique avait été fermée toute la journée, la porte cochère était -verrouillée, la rue était déserte. Il n'y avait dans la maison que deux -religieuses, soeur Perpétue et soeur Simplice, qui veillaient près du -corps de Fantine. - -Vers l'heure où M. Madeleine avait coutume de rentrer, la brave portière -se leva machinalement, prit la clef de la chambre de M. Madeleine dans -un tiroir et le bougeoir dont il se servait tous les soirs pour monter -chez lui, puis elle accrocha la clef au clou où il la prenait -d'habitude, et plaça le bougeoir à côté, comme si elle l'attendait. -Ensuite elle se rassit sur sa chaise et se remit à songer. La pauvre -bonne vieille avait fait tout cela sans en avoir conscience. - -Ce ne fut qu'au bout de plus de deux heures qu'elle sortit de sa rêverie -et s'écria: «Tiens! mon bon Dieu Jésus! moi qui ai mis sa clef au clou!» - -En ce moment la vitre de la loge s'ouvrit, une main passa par -l'ouverture, saisit la clef et le bougeoir et alluma la bougie à la -chandelle qui brûlait. - -La portière leva les yeux et resta béante, avec un cri dans le gosier -qu'elle retint. Elle connaissait cette main, ce bras, cette manche de -redingote. - -C'était M. Madeleine. - -Elle fut quelques secondes avant de pouvoir parler, saisie, comme elle -le disait elle-même plus tard en racontant son aventure. - ---Mon Dieu, monsieur le maire, s'écria-t-elle enfin, je vous croyais.... - -Elle s'arrêta, la fin de sa phrase eût manqué de respect au -commencement. Jean Valjean était toujours pour elle monsieur le maire. - -Il acheva sa pensée. - ---En prison, dit-il. J'y étais. J'ai brisé un barreau d'une fenêtre, je -me suis laissé tomber du haut d'un toit, et me voici. Je monte à ma -chambre, allez me chercher la soeur Simplice. Elle est sans doute près -de cette pauvre femme. - -La vieille obéit en toute hâte. - -Il ne lui fit aucune recommandation; il était bien sûr qu'elle le -garderait mieux qu'il ne se garderait lui-même. - -On n'a jamais su comment il avait réussi à pénétrer dans la cour sans -faire ouvrir la porte cochère. Il avait, et portait toujours sur lui, un -passe-partout qui ouvrait une petite porte latérale; mais on avait dû le -fouiller et lui prendre son passe-partout. Ce point n'a pas été -éclairci. - -Il monta l'escalier qui conduisait à sa chambre. Arrivé en haut, il -laissa son bougeoir sur les dernières marches de l'escalier, ouvrit sa -porte avec peu de bruit, et alla fermer à tâtons sa fenêtre et son -volet, puis il revint prendre sa bougie et rentra dans sa chambre. - -La précaution était utile; on se souvient que sa fenêtre pouvait être -aperçue de la rue. Il jeta un coup d'oeil autour de lui, sur sa table, -sur sa chaise, sur son lit qui n'avait pas été défait depuis trois -jours. Il ne restait aucune trace du désordre de l'avant-dernière nuit. -La portière avait «fait la chambre». Seulement elle avait ramassé dans -les cendres et posé proprement sur la table les deux bouts du bâton -ferré et la pièce de quarante sous noircie par le feu. - -Il prit une feuille de papier sur laquelle il écrivit: _Voici les deux -bouts de mon bâton ferré et la pièce de quarante sous volée à -Petit-Gervais dont j'ai parlé à la cour d'assises_, et il posa sur cette -feuille la pièce d'argent et les deux morceaux de fer, de façon que ce -fût la première chose qu'on aperçût en entrant dans la chambre. Il tira -d'une armoire une vieille chemise à lui qu'il déchira. Cela fit quelques -morceaux de toile dans lesquels il emballa les deux flambeaux d'argent. -Du reste il n'avait ni hâte ni agitation, et, tout en emballant les -chandeliers de l'évêque, il mordait dans un morceau de pain noir. Il est -probable que c'était le pain de la prison qu'il avait emporté en -s'évadant. - -Ceci a été constaté par les miettes de pain qui furent trouvées sur le -carreau de la chambre, lorsque la justice plus tard fit une -perquisition. - -On frappa deux petits coups à la porte. - ---Entrez, dit-il. - -C'était la soeur Simplice. - -Elle était pâle, elle avait les yeux rouges, la chandelle qu'elle tenait -vacillait dans sa main. Les violences de la destinée ont cela de -particulier que, si perfectionnés ou si refroidis que nous soyons, elles -nous tirent du fond des entrailles la nature humaine et la forcent de -reparaître au dehors. Dans les émotions de cette journée, la religieuse -était redevenue femme. Elle avait pleuré, et elle tremblait. - -Jean Valjean venait d'écrire quelques lignes sur un papier qu'il tendit -à la religieuse en disant: - ---Ma soeur, vous remettrez ceci à monsieur le curé. - -Le papier était déplié. Elle y jeta les yeux. - ---Vous pouvez lire, dit-il. - -Elle lut.--«Je prie monsieur le curé de veiller sur tout ce que je -laisse ici. Il voudra bien payer là-dessus les frais de mon procès et -l'enterrement de la femme qui est morte aujourd'hui. Le reste sera aux -pauvres.» - -La soeur voulut parler, mais elle put à peine balbutier quelques sons -inarticulés. Elle parvint cependant à dire: - ---Est-ce que monsieur le maire ne désire pas revoir une dernière fois -cette pauvre malheureuse? - ---Non, dit-il, on est à ma poursuite, on n'aurait qu'à m'arrêter dans sa -chambre, cela la troublerait. - -Il achevait à peine qu'un grand bruit se fit dans l'escalier. Ils -entendirent un tumulte de pas qui montaient, et la vieille portière qui -disait de sa voix la plus haute et la plus perçante: - ---Mon bon monsieur, je vous jure le bon Dieu qu'il n'est entré personne -ici de toute la journée ni de toute la soirée, que même je n'ai pas -quitté ma porte! - -Un homme répondit: - ---Cependant il y a de la lumière dans cette chambre. - -Ils reconnurent la voix de Javert. - -La chambre était disposée de façon que la porte en s'ouvrant masquait -l'angle du mur à droite. Jean Valjean souffla la bougie et se mit dans -cet angle. - -La soeur Simplice tomba à genoux près de la table. - -La porte s'ouvrit. - -Javert entra. - -On entendait le chuchotement de plusieurs hommes et les protestations de -la portière dans le corridor. - -La religieuse ne leva pas les yeux. Elle priait. - -La chandelle était sur la cheminée et ne donnait que peu de clarté. - -Javert aperçut la soeur et s'arrêta interdit. - -On se rappelle que le fond même de Javert, son élément, son milieu -respirable, c'était la vénération de toute autorité. Il était tout d'une -pièce et n'admettait ni objection, ni restriction. Pour lui, bien -entendu, l'autorité ecclésiastique était la première de toutes. Il était -religieux, superficiel et correct sur ce point comme sur tous. À ses -yeux un prêtre était un esprit qui ne se trompe pas, une religieuse -était une créature qui ne pèche pas. C'étaient des âmes murées à ce -monde avec une seule porte qui ne s'ouvrait jamais que pour laisser -sortir la vérité. - -En apercevant la soeur, son premier mouvement fut de se retirer. - -Cependant il y avait aussi un autre devoir qui le tenait, et qui le -poussait impérieusement en sens inverse. Son second mouvement fut de -rester, et de hasarder au moins une question. - -C'était cette soeur Simplice qui n'avait menti de sa vie. Javert le -savait, et la vénérait particulièrement à cause de cela. - ---Ma soeur, dit-il, êtes-vous seule dans cette chambre? - -Il y eut un moment affreux pendant lequel la pauvre portière se sentit -défaillir. - -La soeur leva les yeux et répondit: - ---Oui. - ---Ainsi, reprit Javert, excusez-moi si j'insiste, c'est mon devoir, vous -n'avez pas vu ce soir une personne, un homme. Il s'est évadé, nous le -cherchons, ce nommé Jean Valjean, vous ne l'avez pas vu? - -La soeur répondit: - ---Non. - -Elle mentit. Elle mentit deux fois de suite, coup sur coup, sans -hésiter, rapidement, comme on se dévoue. - ---Pardon, dit Javert, et il se retira en saluant profondément. - -Ô sainte fille! vous n'êtes plus de ce monde depuis beaucoup d'années; -vous avez rejoint dans la lumière vos soeurs les vierges et vos frères -les anges; que ce mensonge vous soit compté dans le paradis! - -L'affirmation de la soeur fut pour Javert quelque chose de si décisif -qu'il ne remarqua même pas la singularité de cette bougie qu'on venait -de souffler et qui fumait sur la table. - -Une heure après, un homme, marchant à travers les arbres et les brumes, -s'éloignait rapidement de Montreuil-sur-mer dans la direction de Paris. -Cet homme était Jean Valjean. Il a été établi, par le témoignage de deux -ou trois rouliers qui l'avaient rencontré, qu'il portait un paquet et -qu'il était vêtu d'une blouse. Où avait-il pris cette blouse? On ne l'a -jamais su. Cependant un vieux ouvrier était mort quelques jours -auparavant à l'infirmerie de la fabrique, ne laissant que sa blouse. -C'était peut-être celle-là. - -Un dernier mot sur Fantine. - -Nous avons tous une mère, la terre. On rendit Fantine à cette mère. - -Le curé crut bien faire, et fit bien peut-être, en réservant, sur ce que -Jean Valjean avait laissé, le plus d'argent possible aux pauvres. Après -tout, de qui s'agissait-il? d'un forçat et d'une fille publique. C'est -pourquoi il simplifia l'enterrement de Fantine, et le réduisit à ce -strict nécessaire qu'on appelle la fosse commune. - -Fantine fut donc enterrée dans ce coin gratis du cimetière qui est à -tous et à personne, et où l'on perd les pauvres. Heureusement Dieu sait -où retrouver l'âme. On coucha Fantine dans les ténèbres parmi les -premiers os venus; elle subit la promiscuité des cendres. Elle fut jetée -à la fosse publique. Sa tombe ressembla à son lit. - - - - - -End of the Project Gutenberg EBook of Les misérables Tome I, by Victor Hugo - -*** END OF THIS PROJECT GUTENBERG EBOOK LES MISÉRABLES TOME I *** - -***** This file should be named 17489-8.txt or 17489-8.zip ***** -This and all associated files of various formats will be found in: - http://www.gutenberg.org/1/7/4/8/17489/ - -Produced by www.ebooksgratuits.com and Chuck Greif - -Updated editions will replace the previous one--the old editions -will be renamed. - -Creating the works from public domain print editions means that no -one owns a United States copyright in these works, so the Foundation -(and you!) can copy and distribute it in the United States without -permission and without paying copyright royalties. Special rules, -set forth in the General Terms of Use part of this license, apply to -copying and distributing Project Gutenberg-tm electronic works to -protect the PROJECT GUTENBERG-tm concept and trademark. Project -Gutenberg is a registered trademark, and may not be used if you -charge for the eBooks, unless you receive specific permission. If you -do not charge anything for copies of this eBook, complying with the -rules is very easy. You may use this eBook for nearly any purpose -such as creation of derivative works, reports, performances and -research. They may be modified and printed and given away--you may do -practically ANYTHING with public domain eBooks. Redistribution is -subject to the trademark license, especially commercial -redistribution. - - - -*** START: FULL LICENSE *** - -THE FULL PROJECT GUTENBERG LICENSE -PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK - -To protect the Project Gutenberg-tm mission of promoting the free -distribution of electronic works, by using or distributing this work -(or any other work associated in any way with the phrase "Project -Gutenberg"), you agree to comply with all the terms of the Full Project -Gutenberg-tm License (available with this file or online at -http://gutenberg.org/license). - - -Section 1. General Terms of Use and Redistributing Project Gutenberg-tm -electronic works - -1.A. By reading or using any part of this Project Gutenberg-tm -electronic work, you indicate that you have read, understand, agree to -and accept all the terms of this license and intellectual property -(trademark/copyright) agreement. If you do not agree to abide by all -the terms of this agreement, you must cease using and return or destroy -all copies of Project Gutenberg-tm electronic works in your possession. -If you paid a fee for obtaining a copy of or access to a Project -Gutenberg-tm electronic work and you do not agree to be bound by the -terms of this agreement, you may obtain a refund from the person or -entity to whom you paid the fee as set forth in paragraph 1.E.8. - -1.B. "Project Gutenberg" is a registered trademark. It may only be -used on or associated in any way with an electronic work by people who -agree to be bound by the terms of this agreement. There are a few -things that you can do with most Project Gutenberg-tm electronic works -even without complying with the full terms of this agreement. See -paragraph 1.C below. There are a lot of things you can do with Project -Gutenberg-tm electronic works if you follow the terms of this agreement -and help preserve free future access to Project Gutenberg-tm electronic -works. See paragraph 1.E below. - -1.C. The Project Gutenberg Literary Archive Foundation ("the Foundation" -or PGLAF), owns a compilation copyright in the collection of Project -Gutenberg-tm electronic works. Nearly all the individual works in the -collection are in the public domain in the United States. If an -individual work is in the public domain in the United States and you are -located in the United States, we do not claim a right to prevent you from -copying, distributing, performing, displaying or creating derivative -works based on the work as long as all references to Project Gutenberg -are removed. Of course, we hope that you will support the Project -Gutenberg-tm mission of promoting free access to electronic works by -freely sharing Project Gutenberg-tm works in compliance with the terms of -this agreement for keeping the Project Gutenberg-tm name associated with -the work. You can easily comply with the terms of this agreement by -keeping this work in the same format with its attached full Project -Gutenberg-tm License when you share it without charge with others. - -1.D. The copyright laws of the place where you are located also govern -what you can do with this work. Copyright laws in most countries are in -a constant state of change. If you are outside the United States, check -the laws of your country in addition to the terms of this agreement -before downloading, copying, displaying, performing, distributing or -creating derivative works based on this work or any other Project -Gutenberg-tm work. The Foundation makes no representations concerning -the copyright status of any work in any country outside the United -States. - -1.E. Unless you have removed all references to Project Gutenberg: - -1.E.1. The following sentence, with active links to, or other immediate -access to, the full Project Gutenberg-tm License must appear prominently -whenever any copy of a Project Gutenberg-tm work (any work on which the -phrase "Project Gutenberg" appears, or with which the phrase "Project -Gutenberg" is associated) is accessed, displayed, performed, viewed, -copied or distributed: - -This eBook is for the use of anyone anywhere at no cost and with -almost no restrictions whatsoever. You may copy it, give it away or -re-use it under the terms of the Project Gutenberg License included -with this eBook or online at www.gutenberg.org - -1.E.2. If an individual Project Gutenberg-tm electronic work is derived -from the public domain (does not contain a notice indicating that it is -posted with permission of the copyright holder), the work can be copied -and distributed to anyone in the United States without paying any fees -or charges. If you are redistributing or providing access to a work -with the phrase "Project Gutenberg" associated with or appearing on the -work, you must comply either with the requirements of paragraphs 1.E.1 -through 1.E.7 or obtain permission for the use of the work and the -Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or -1.E.9. - -1.E.3. If an individual Project Gutenberg-tm electronic work is posted -with the permission of the copyright holder, your use and distribution -must comply with both paragraphs 1.E.1 through 1.E.7 and any additional -terms imposed by the copyright holder. Additional terms will be linked -to the Project Gutenberg-tm License for all works posted with the -permission of the copyright holder found at the beginning of this work. - -1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm -License terms from this work, or any files containing a part of this -work or any other work associated with Project Gutenberg-tm. - -1.E.5. Do not copy, display, perform, distribute or redistribute this -electronic work, or any part of this electronic work, without -prominently displaying the sentence set forth in paragraph 1.E.1 with -active links or immediate access to the full terms of the Project -Gutenberg-tm License. - -1.E.6. You may convert to and distribute this work in any binary, -compressed, marked up, nonproprietary or proprietary form, including any -word processing or hypertext form. However, if you provide access to or -distribute copies of a Project Gutenberg-tm work in a format other than -"Plain Vanilla ASCII" or other format used in the official version -posted on the official Project Gutenberg-tm web site (www.gutenberg.org), -you must, at no additional cost, fee or expense to the user, provide a -copy, a means of exporting a copy, or a means of obtaining a copy upon -request, of the work in its original "Plain Vanilla ASCII" or other -form. Any alternate format must include the full Project Gutenberg-tm -License as specified in paragraph 1.E.1. - -1.E.7. Do not charge a fee for access to, viewing, displaying, -performing, copying or distributing any Project Gutenberg-tm works -unless you comply with paragraph 1.E.8 or 1.E.9. - -1.E.8. You may charge a reasonable fee for copies of or providing -access to or distributing Project Gutenberg-tm electronic works provided -that - -- You pay a royalty fee of 20% of the gross profits you derive from - the use of Project Gutenberg-tm works calculated using the method - you already use to calculate your applicable taxes. The fee is - owed to the owner of the Project Gutenberg-tm trademark, but he - has agreed to donate royalties under this paragraph to the - Project Gutenberg Literary Archive Foundation. Royalty payments - must be paid within 60 days following each date on which you - prepare (or are legally required to prepare) your periodic tax - returns. Royalty payments should be clearly marked as such and - sent to the Project Gutenberg Literary Archive Foundation at the - address specified in Section 4, "Information about donations to - the Project Gutenberg Literary Archive Foundation." - -- You provide a full refund of any money paid by a user who notifies - you in writing (or by e-mail) within 30 days of receipt that s/he - does not agree to the terms of the full Project Gutenberg-tm - License. You must require such a user to return or - destroy all copies of the works possessed in a physical medium - and discontinue all use of and all access to other copies of - Project Gutenberg-tm works. - -- You provide, in accordance with paragraph 1.F.3, a full refund of any - money paid for a work or a replacement copy, if a defect in the - electronic work is discovered and reported to you within 90 days - of receipt of the work. - -- You comply with all other terms of this agreement for free - distribution of Project Gutenberg-tm works. - -1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm -electronic work or group of works on different terms than are set -forth in this agreement, you must obtain permission in writing from -both the Project Gutenberg Literary Archive Foundation and Michael -Hart, the owner of the Project Gutenberg-tm trademark. Contact the -Foundation as set forth in Section 3 below. - -1.F. - -1.F.1. Project Gutenberg volunteers and employees expend considerable -effort to identify, do copyright research on, transcribe and proofread -public domain works in creating the Project Gutenberg-tm -collection. Despite these efforts, Project Gutenberg-tm electronic -works, and the medium on which they may be stored, may contain -"Defects," such as, but not limited to, incomplete, inaccurate or -corrupt data, transcription errors, a copyright or other intellectual -property infringement, a defective or damaged disk or other medium, a -computer virus, or computer codes that damage or cannot be read by -your equipment. - -1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the "Right -of Replacement or Refund" described in paragraph 1.F.3, the Project -Gutenberg Literary Archive Foundation, the owner of the Project -Gutenberg-tm trademark, and any other party distributing a Project -Gutenberg-tm electronic work under this agreement, disclaim all -liability to you for damages, costs and expenses, including legal -fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT -LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE -PROVIDED IN PARAGRAPH F3. YOU AGREE THAT THE FOUNDATION, THE -TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE -LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR -INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH -DAMAGE. - -1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a -defect in this electronic work within 90 days of receiving it, you can -receive a refund of the money (if any) you paid for it by sending a -written explanation to the person you received the work from. If you -received the work on a physical medium, you must return the medium with -your written explanation. The person or entity that provided you with -the defective work may elect to provide a replacement copy in lieu of a -refund. If you received the work electronically, the person or entity -providing it to you may choose to give you a second opportunity to -receive the work electronically in lieu of a refund. If the second copy -is also defective, you may demand a refund in writing without further -opportunities to fix the problem. - -1.F.4. Except for the limited right of replacement or refund set forth -in paragraph 1.F.3, this work is provided to you 'AS-IS', WITH NO OTHER -WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR ANY PURPOSE. - -1.F.5. Some states do not allow disclaimers of certain implied -warranties or the exclusion or limitation of certain types of damages. -If any disclaimer or limitation set forth in this agreement violates the -law of the state applicable to this agreement, the agreement shall be -interpreted to make the maximum disclaimer or limitation permitted by -the applicable state law. The invalidity or unenforceability of any -provision of this agreement shall not void the remaining provisions. - -1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the -trademark owner, any agent or employee of the Foundation, anyone -providing copies of Project Gutenberg-tm electronic works in accordance -with this agreement, and any volunteers associated with the production, -promotion and distribution of Project Gutenberg-tm electronic works, -harmless from all liability, costs and expenses, including legal fees, -that arise directly or indirectly from any of the following which you do -or cause to occur: (a) distribution of this or any Project Gutenberg-tm -work, (b) alteration, modification, or additions or deletions to any -Project Gutenberg-tm work, and (c) any Defect you cause. - - -Section 2. Information about the Mission of Project Gutenberg-tm - -Project Gutenberg-tm is synonymous with the free distribution of -electronic works in formats readable by the widest variety of computers -including obsolete, old, middle-aged and new computers. It exists -because of the efforts of hundreds of volunteers and donations from -people in all walks of life. - -Volunteers and financial support to provide volunteers with the -assistance they need, is critical to reaching Project Gutenberg-tm's -goals and ensuring that the Project Gutenberg-tm collection will -remain freely available for generations to come. In 2001, the Project -Gutenberg Literary Archive Foundation was created to provide a secure -and permanent future for Project Gutenberg-tm and future generations. -To learn more about the Project Gutenberg Literary Archive Foundation -and how your efforts and donations can help, see Sections 3 and 4 -and the Foundation web page at http://www.pglaf.org. - - -Section 3. Information about the Project Gutenberg Literary Archive -Foundation - -The Project Gutenberg Literary Archive Foundation is a non profit -501(c)(3) educational corporation organized under the laws of the -state of Mississippi and granted tax exempt status by the Internal -Revenue Service. The Foundation's EIN or federal tax identification -number is 64-6221541. Its 501(c)(3) letter is posted at -http://pglaf.org/fundraising. Contributions to the Project Gutenberg -Literary Archive Foundation are tax deductible to the full extent -permitted by U.S. federal laws and your state's laws. - -The Foundation's principal office is located at 4557 Melan Dr. S. -Fairbanks, AK, 99712., but its volunteers and employees are scattered -throughout numerous locations. Its business office is located at -809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email -business@pglaf.org. Email contact links and up to date contact -information can be found at the Foundation's web site and official -page at http://pglaf.org - -For additional contact information: - Dr. Gregory B. Newby - Chief Executive and Director - gbnewby@pglaf.org - -Section 4. Information about Donations to the Project Gutenberg -Literary Archive Foundation - -Project Gutenberg-tm depends upon and cannot survive without wide -spread public support and donations to carry out its mission of -increasing the number of public domain and licensed works that can be -freely distributed in machine readable form accessible by the widest -array of equipment including outdated equipment. Many small donations -($1 to $5,000) are particularly important to maintaining tax exempt -status with the IRS. - -The Foundation is committed to complying with the laws regulating -charities and charitable donations in all 50 states of the United -States. Compliance requirements are not uniform and it takes a -considerable effort, much paperwork and many fees to meet and keep up -with these requirements. We do not solicit donations in locations -where we have not received written confirmation of compliance. To -SEND DONATIONS or determine the status of compliance for any -particular state visit http://pglaf.org - -While we cannot and do not solicit contributions from states where we -have not met the solicitation requirements, we know of no prohibition -against accepting unsolicited donations from donors in such states who -approach us with offers to donate. - -International donations are gratefully accepted, but we cannot make -any statements concerning tax treatment of donations received from -outside the United States. U.S. laws alone swamp our small staff. - -Please check the Project Gutenberg Web pages for current donation -methods and addresses. Donations are accepted in a number of other -ways including checks, online payments and credit card -donations. To donate, please visit: http://pglaf.org/donate - - -Section 5. General Information About Project Gutenberg-tm electronic -works. - -Professor Michael S. Hart is the originator of the Project Gutenberg-tm -concept of a library of electronic works that could be freely shared -with anyone. For thirty years, he produced and distributed Project -Gutenberg-tm eBooks with only a loose network of volunteer support. - -Project Gutenberg-tm eBooks are often created from several printed -editions, all of which are confirmed as Public Domain in the U.S. -unless a copyright notice is included. Thus, we do not necessarily -keep eBooks in compliance with any particular paper edition. - -Most people start at our Web site which has the main PG search facility: - - http://www.gutenberg.org - -This Web site includes information about Project Gutenberg-tm, -including how to make donations to the Project Gutenberg Literary -Archive Foundation, how to help produce our new eBooks, and how to -subscribe to our email newsletter to hear about new eBooks. - -*** END: FULL LICENSE *** diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,230 +0,0 @@ -/* - * lws-api-test-fts - lws full-text search api test - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32) -#include -#endif -#include - -#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32) -static struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "createindex", no_argument, NULL, 'c' }, - { "index", required_argument, NULL, 'i' }, - { "debug", required_argument, NULL, 'd' }, - { "file", required_argument, NULL, 'f' }, - { "lines", required_argument, NULL, 'l' }, - { NULL, 0, 0, 0 } -}; -#endif - -static const char *index_filepath = "/tmp/lws-fts-test-index"; -static char filepath[256]; - -int main(int argc, char **argv) -{ - int n, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - int fd, fi, ft, createindex = 0, flags = LWSFTS_F_QUERY_AUTOCOMPLETE; - struct lws_fts_search_params params; - struct lws_fts_result *result; - struct lws_fts_file *jtf; - struct lws_fts *t; - char buf[16384]; - - do { -#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32) - n = getopt_long(argc, argv, "hd:i:cfl", options, NULL); -#else - n = getopt(argc, argv, "hd:i:cfl"); -#endif - if (n < 0) - continue; - switch (n) { - case 'i': - strncpy(filepath, optarg, sizeof(filepath) - 1); - filepath[sizeof(filepath) - 1] = '\0'; - index_filepath = filepath; - break; - case 'd': - logs = atoi(optarg); - break; - case 'c': - createindex = 1; - break; - case 'f': - flags &= ~LWSFTS_F_QUERY_AUTOCOMPLETE; - flags |= LWSFTS_F_QUERY_FILES; - break; - case 'l': - flags |= LWSFTS_F_QUERY_FILES | - LWSFTS_F_QUERY_FILE_LINES; - break; - case 'h': - fprintf(stderr, - "Usage: %s [--createindex]" - "[--index=] " - "[-d ] file1 file2 \n", - argv[0]); - exit(1); - } - } while (n >= 0); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS API selftest: full-text search\n"); - - if (createindex) { - - lwsl_notice("Creating index\n"); - - /* - * create an index by shifting through argv and indexing each - * file given there into a single combined index - */ - - ft = open(index_filepath, O_CREAT | O_WRONLY | O_TRUNC, 0600); - if (ft < 0) { - lwsl_err("%s: can't open index %s\n", __func__, - index_filepath); - - goto bail; - } - - t = lws_fts_create(ft); - if (!t) { - lwsl_err("%s: Unable to allocate trie\n", __func__); - - goto bail1; - } - - while (optind < argc) { - - fi = lws_fts_file_index(t, argv[optind], - strlen(argv[optind]), 1); - if (fi < 0) { - lwsl_err("%s: Failed to get file idx for %s\n", - __func__, argv[optind]); - - goto bail1; - } - - fd = open(argv[optind], O_RDONLY); - if (fd < 0) { - lwsl_err("unable to open %s for read\n", - argv[optind]); - goto bail; - } - - do { - int n = read(fd, buf, sizeof(buf)); - - if (n <= 0) - break; - - if (lws_fts_fill(t, fi, buf, n)) { - lwsl_err("%s: lws_fts_fill failed\n", - __func__); - close(fd); - - goto bail; - } - - } while (1); - - close(fd); - optind++; - } - - if (lws_fts_serialize(t)) { - lwsl_err("%s: serialize failed\n", __func__); - - goto bail; - } - - lws_fts_destroy(&t); - close(ft); - - return 0; - } - - /* - * shift through argv searching for each token - */ - - jtf = lws_fts_open(index_filepath); - if (!jtf) - goto bail; - - while (optind < argc) { - - struct lws_fts_result_autocomplete *ac; - struct lws_fts_result_filepath *fp; - uint32_t *l, n; - - memset(¶ms, 0, sizeof(params)); - - params.needle = argv[optind]; - params.flags = flags; - params.max_autocomplete = 20; - params.max_files = 20; - - result = lws_fts_search(jtf, ¶ms); - - if (!result) { - lwsl_err("%s: search failed\n", __func__); - lws_fts_close(jtf); - goto bail; - } - - ac = result->autocomplete_head; - fp = result->filepath_head; - - if (!ac) - lwsl_notice("%s: no autocomplete results\n", __func__); - - while (ac) { - lwsl_notice("%s: AC %s: %d agg hits\n", __func__, - ((char *)(ac + 1)), ac->instances); - - ac = ac->next; - } - - if (!fp) - lwsl_notice("%s: no filepath results\n", __func__); - - while (fp) { - lwsl_notice("%s: %s: (%d lines) %d hits \n", __func__, - (((char *)(fp + 1)) + fp->matches_length), - fp->lines_in_file, fp->matches); - - if (fp->matches_length) { - l = (uint32_t *)(fp + 1); - n = 0; - while ((int)n++ < fp->matches) - lwsl_notice(" %d\n", *l++); - } - fp = fp->next; - } - - lwsac_free(¶ms.results_head); - - optind++; - } - - lws_fts_close(jtf); - - return 0; - -bail1: - close(ft); -bail: - lwsl_user("FAILED\n"); - - return 1; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/README.md libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -# lws api test fts - -Demonstrates how to create indexes and perform full-text searches. - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --c / --createindex|Create an index file, instead of searching --i / --index |Use this file as the index - -The two modes are: - - - create an index: `--createindex inputfile [inputfile...]` - -``` - $ ./lws-api-test-fts -c ./the-picture-of-dorian-gray.txt -[2018/10/15 07:14:15:1175] USER: LWS API selftest: full-text search -[2018/10/15 07:14:15:1531] NOTICE: lws_fts_serialize: index 1 files (0MiB) cpu time 32ms, alloc: 1024KiB + 1024KiB, serialize: 3ms, file: 325KiB -``` - - - perform search[es]: `searchterm [searchterm...]` - -``` - $ ./lws-api-test-fts b -[2018/10/15 07:15:44:1442] USER: LWS API selftest: full-text search -[2018/10/15 07:15:44:1442] NOTICE: lws_fts_search: 'b' Matched: 3 instances, 8 children, 0ms -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC b: 3 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC be: 472 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC bee: 3 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC been: 236 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC beaut: 1 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC beauty: 55 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC because: 40 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC believe: 49 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC better: 54 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC before: 75 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC beg: 5 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC began: 44 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC but: 401 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC basil: 158 agg hits -[2018/10/15 07:15:44:1443] NOTICE: lws_fts_results_dump: AC broke: 22 agg hits -[2018/10/15 07:15:44:1444] NOTICE: lws_fts_results_dump: AC by: 242 agg hits -[2018/10/15 07:15:44:1444] NOTICE: lws_fts_results_dump: AC boy: 36 agg hits -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/selftest.sh libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/selftest.sh --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=4 - -FAILS=0 - -# -# let's make an index with just Dorian first -# -dotest $1 $2 apitest -c -i /tmp/lws-fts-dorian.index \ - "../minimal-examples/api-tests/api-test-fts/the-picture-of-dorian-gray.txt" - -# and let's hear about autocompletes for "b" - -dotest $1 $2 apitest -i /tmp/lws-fts-dorian.index b -cat $2/api-test-fts/apitest.log | cut -d' ' -f5- > /tmp/fts1 -diff -urN /tmp/fts1 "../minimal-examples/api-tests/api-test-fts/canned-1.txt" -if [ $? -ne 0 ] ; then - echo "Test 1 failed" - FAILS=$(( $FAILS + 1 )) -fi - -# -# let's make an index with Dorian + Les Mis in French (ie, UTF-8) as well -# -dotest $1 $2 apitest -c -i /tmp/lws-fts-both.index \ - "../minimal-examples/api-tests/api-test-fts/the-picture-of-dorian-gray.txt" \ - "../minimal-examples/api-tests/api-test-fts/les-mis-utf8.txt" - -# and let's hear about "help", which appears in both - -dotest $1 $2 apitest -i /tmp/lws-fts-both.index -f -l help -cat $2/api-test-fts/apitest.log | cut -d' ' -f5- > /tmp/fts2 -diff -urN /tmp/fts2 "../minimal-examples/api-tests/api-test-fts/canned-2.txt" -if [ $? -ne 0 ] ; then - echo "Test 1 failed" - FAILS=$(( $FAILS + 1 )) -fi - -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/the-picture-of-dorian-gray.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/the-picture-of-dorian-gray.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-fts/the-picture-of-dorian-gray.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-fts/the-picture-of-dorian-gray.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,8904 +0,0 @@ -The Project Gutenberg EBook of The Picture of Dorian Gray, by Oscar Wilde - -This eBook is for the use of anyone anywhere at no cost and with -almost no restrictions whatsoever. You may copy it, give it away or -re-use it under the terms of the Project Gutenberg License included -with this eBook or online at www.gutenberg.net - - -Title: The Picture of Dorian Gray - -Author: Oscar Wilde - -Release Date: June 9, 2008 [EBook #174] -[This file last updated on July 2, 2011] -[This file last updated on July 23, 2014] - - -Language: English - - -*** START OF THIS PROJECT GUTENBERG EBOOK THE PICTURE OF DORIAN GRAY *** - - - - -Produced by Judith Boss. HTML version by Al Haines. - - - - - - - - - - -The Picture of Dorian Gray - -by - -Oscar Wilde - - - - -THE PREFACE - -The artist is the creator of beautiful things. To reveal art and -conceal the artist is art's aim. The critic is he who can translate -into another manner or a new material his impression of beautiful -things. - -The highest as the lowest form of criticism is a mode of autobiography. -Those who find ugly meanings in beautiful things are corrupt without -being charming. This is a fault. - -Those who find beautiful meanings in beautiful things are the -cultivated. For these there is hope. They are the elect to whom -beautiful things mean only beauty. - -There is no such thing as a moral or an immoral book. Books are well -written, or badly written. That is all. - -The nineteenth century dislike of realism is the rage of Caliban seeing -his own face in a glass. - -The nineteenth century dislike of romanticism is the rage of Caliban -not seeing his own face in a glass. The moral life of man forms part -of the subject-matter of the artist, but the morality of art consists -in the perfect use of an imperfect medium. No artist desires to prove -anything. Even things that are true can be proved. No artist has -ethical sympathies. An ethical sympathy in an artist is an -unpardonable mannerism of style. No artist is ever morbid. The artist -can express everything. Thought and language are to the artist -instruments of an art. Vice and virtue are to the artist materials for -an art. From the point of view of form, the type of all the arts is -the art of the musician. From the point of view of feeling, the -actor's craft is the type. All art is at once surface and symbol. -Those who go beneath the surface do so at their peril. Those who read -the symbol do so at their peril. It is the spectator, and not life, -that art really mirrors. Diversity of opinion about a work of art -shows that the work is new, complex, and vital. When critics disagree, -the artist is in accord with himself. We can forgive a man for making -a useful thing as long as he does not admire it. The only excuse for -making a useless thing is that one admires it intensely. - - All art is quite useless. - - OSCAR WILDE - - - - -CHAPTER 1 - -The studio was filled with the rich odour of roses, and when the light -summer wind stirred amidst the trees of the garden, there came through -the open door the heavy scent of the lilac, or the more delicate -perfume of the pink-flowering thorn. - -From the corner of the divan of Persian saddle-bags on which he was -lying, smoking, as was his custom, innumerable cigarettes, Lord Henry -Wotton could just catch the gleam of the honey-sweet and honey-coloured -blossoms of a laburnum, whose tremulous branches seemed hardly able to -bear the burden of a beauty so flamelike as theirs; and now and then -the fantastic shadows of birds in flight flitted across the long -tussore-silk curtains that were stretched in front of the huge window, -producing a kind of momentary Japanese effect, and making him think of -those pallid, jade-faced painters of Tokyo who, through the medium of -an art that is necessarily immobile, seek to convey the sense of -swiftness and motion. The sullen murmur of the bees shouldering their -way through the long unmown grass, or circling with monotonous -insistence round the dusty gilt horns of the straggling woodbine, -seemed to make the stillness more oppressive. The dim roar of London -was like the bourdon note of a distant organ. - -In the centre of the room, clamped to an upright easel, stood the -full-length portrait of a young man of extraordinary personal beauty, -and in front of it, some little distance away, was sitting the artist -himself, Basil Hallward, whose sudden disappearance some years ago -caused, at the time, such public excitement and gave rise to so many -strange conjectures. - -As the painter looked at the gracious and comely form he had so -skilfully mirrored in his art, a smile of pleasure passed across his -face, and seemed about to linger there. But he suddenly started up, -and closing his eyes, placed his fingers upon the lids, as though he -sought to imprison within his brain some curious dream from which he -feared he might awake. - -"It is your best work, Basil, the best thing you have ever done," said -Lord Henry languidly. "You must certainly send it next year to the -Grosvenor. The Academy is too large and too vulgar. Whenever I have -gone there, there have been either so many people that I have not been -able to see the pictures, which was dreadful, or so many pictures that -I have not been able to see the people, which was worse. The Grosvenor -is really the only place." - -"I don't think I shall send it anywhere," he answered, tossing his head -back in that odd way that used to make his friends laugh at him at -Oxford. "No, I won't send it anywhere." - -Lord Henry elevated his eyebrows and looked at him in amazement through -the thin blue wreaths of smoke that curled up in such fanciful whorls -from his heavy, opium-tainted cigarette. "Not send it anywhere? My -dear fellow, why? Have you any reason? What odd chaps you painters -are! You do anything in the world to gain a reputation. As soon as -you have one, you seem to want to throw it away. It is silly of you, -for there is only one thing in the world worse than being talked about, -and that is not being talked about. A portrait like this would set you -far above all the young men in England, and make the old men quite -jealous, if old men are ever capable of any emotion." - -"I know you will laugh at me," he replied, "but I really can't exhibit -it. I have put too much of myself into it." - -Lord Henry stretched himself out on the divan and laughed. - -"Yes, I knew you would; but it is quite true, all the same." - -"Too much of yourself in it! Upon my word, Basil, I didn't know you -were so vain; and I really can't see any resemblance between you, with -your rugged strong face and your coal-black hair, and this young -Adonis, who looks as if he was made out of ivory and rose-leaves. Why, -my dear Basil, he is a Narcissus, and you--well, of course you have an -intellectual expression and all that. But beauty, real beauty, ends -where an intellectual expression begins. Intellect is in itself a mode -of exaggeration, and destroys the harmony of any face. The moment one -sits down to think, one becomes all nose, or all forehead, or something -horrid. Look at the successful men in any of the learned professions. -How perfectly hideous they are! Except, of course, in the Church. But -then in the Church they don't think. A bishop keeps on saying at the -age of eighty what he was told to say when he was a boy of eighteen, -and as a natural consequence he always looks absolutely delightful. -Your mysterious young friend, whose name you have never told me, but -whose picture really fascinates me, never thinks. I feel quite sure of -that. He is some brainless beautiful creature who should be always -here in winter when we have no flowers to look at, and always here in -summer when we want something to chill our intelligence. Don't flatter -yourself, Basil: you are not in the least like him." - -"You don't understand me, Harry," answered the artist. "Of course I am -not like him. I know that perfectly well. Indeed, I should be sorry -to look like him. You shrug your shoulders? I am telling you the -truth. There is a fatality about all physical and intellectual -distinction, the sort of fatality that seems to dog through history the -faltering steps of kings. It is better not to be different from one's -fellows. The ugly and the stupid have the best of it in this world. -They can sit at their ease and gape at the play. If they know nothing -of victory, they are at least spared the knowledge of defeat. They -live as we all should live--undisturbed, indifferent, and without -disquiet. They neither bring ruin upon others, nor ever receive it -from alien hands. Your rank and wealth, Harry; my brains, such as they -are--my art, whatever it may be worth; Dorian Gray's good looks--we -shall all suffer for what the gods have given us, suffer terribly." - -"Dorian Gray? Is that his name?" asked Lord Henry, walking across the -studio towards Basil Hallward. - -"Yes, that is his name. I didn't intend to tell it to you." - -"But why not?" - -"Oh, I can't explain. When I like people immensely, I never tell their -names to any one. It is like surrendering a part of them. I have -grown to love secrecy. It seems to be the one thing that can make -modern life mysterious or marvellous to us. The commonest thing is -delightful if one only hides it. When I leave town now I never tell my -people where I am going. If I did, I would lose all my pleasure. It -is a silly habit, I dare say, but somehow it seems to bring a great -deal of romance into one's life. I suppose you think me awfully -foolish about it?" - -"Not at all," answered Lord Henry, "not at all, my dear Basil. You -seem to forget that I am married, and the one charm of marriage is that -it makes a life of deception absolutely necessary for both parties. I -never know where my wife is, and my wife never knows what I am doing. -When we meet--we do meet occasionally, when we dine out together, or go -down to the Duke's--we tell each other the most absurd stories with the -most serious faces. My wife is very good at it--much better, in fact, -than I am. She never gets confused over her dates, and I always do. -But when she does find me out, she makes no row at all. I sometimes -wish she would; but she merely laughs at me." - -"I hate the way you talk about your married life, Harry," said Basil -Hallward, strolling towards the door that led into the garden. "I -believe that you are really a very good husband, but that you are -thoroughly ashamed of your own virtues. You are an extraordinary -fellow. You never say a moral thing, and you never do a wrong thing. -Your cynicism is simply a pose." - -"Being natural is simply a pose, and the most irritating pose I know," -cried Lord Henry, laughing; and the two young men went out into the -garden together and ensconced themselves on a long bamboo seat that -stood in the shade of a tall laurel bush. The sunlight slipped over -the polished leaves. In the grass, white daisies were tremulous. - -After a pause, Lord Henry pulled out his watch. "I am afraid I must be -going, Basil," he murmured, "and before I go, I insist on your -answering a question I put to you some time ago." - -"What is that?" said the painter, keeping his eyes fixed on the ground. - -"You know quite well." - -"I do not, Harry." - -"Well, I will tell you what it is. I want you to explain to me why you -won't exhibit Dorian Gray's picture. I want the real reason." - -"I told you the real reason." - -"No, you did not. You said it was because there was too much of -yourself in it. Now, that is childish." - -"Harry," said Basil Hallward, looking him straight in the face, "every -portrait that is painted with feeling is a portrait of the artist, not -of the sitter. The sitter is merely the accident, the occasion. It is -not he who is revealed by the painter; it is rather the painter who, on -the coloured canvas, reveals himself. The reason I will not exhibit -this picture is that I am afraid that I have shown in it the secret of -my own soul." - -Lord Henry laughed. "And what is that?" he asked. - -"I will tell you," said Hallward; but an expression of perplexity came -over his face. - -"I am all expectation, Basil," continued his companion, glancing at him. - -"Oh, there is really very little to tell, Harry," answered the painter; -"and I am afraid you will hardly understand it. Perhaps you will -hardly believe it." - -Lord Henry smiled, and leaning down, plucked a pink-petalled daisy from -the grass and examined it. "I am quite sure I shall understand it," he -replied, gazing intently at the little golden, white-feathered disk, -"and as for believing things, I can believe anything, provided that it -is quite incredible." - -The wind shook some blossoms from the trees, and the heavy -lilac-blooms, with their clustering stars, moved to and fro in the -languid air. A grasshopper began to chirrup by the wall, and like a -blue thread a long thin dragon-fly floated past on its brown gauze -wings. Lord Henry felt as if he could hear Basil Hallward's heart -beating, and wondered what was coming. - -"The story is simply this," said the painter after some time. "Two -months ago I went to a crush at Lady Brandon's. You know we poor -artists have to show ourselves in society from time to time, just to -remind the public that we are not savages. With an evening coat and a -white tie, as you told me once, anybody, even a stock-broker, can gain -a reputation for being civilized. Well, after I had been in the room -about ten minutes, talking to huge overdressed dowagers and tedious -academicians, I suddenly became conscious that some one was looking at -me. I turned half-way round and saw Dorian Gray for the first time. -When our eyes met, I felt that I was growing pale. A curious sensation -of terror came over me. I knew that I had come face to face with some -one whose mere personality was so fascinating that, if I allowed it to -do so, it would absorb my whole nature, my whole soul, my very art -itself. I did not want any external influence in my life. You know -yourself, Harry, how independent I am by nature. I have always been my -own master; had at least always been so, till I met Dorian Gray. -Then--but I don't know how to explain it to you. Something seemed to -tell me that I was on the verge of a terrible crisis in my life. I had -a strange feeling that fate had in store for me exquisite joys and -exquisite sorrows. I grew afraid and turned to quit the room. It was -not conscience that made me do so: it was a sort of cowardice. I take -no credit to myself for trying to escape." - -"Conscience and cowardice are really the same things, Basil. -Conscience is the trade-name of the firm. That is all." - -"I don't believe that, Harry, and I don't believe you do either. -However, whatever was my motive--and it may have been pride, for I used -to be very proud--I certainly struggled to the door. There, of course, -I stumbled against Lady Brandon. 'You are not going to run away so -soon, Mr. Hallward?' she screamed out. You know her curiously shrill -voice?" - -"Yes; she is a peacock in everything but beauty," said Lord Henry, -pulling the daisy to bits with his long nervous fingers. - -"I could not get rid of her. She brought me up to royalties, and -people with stars and garters, and elderly ladies with gigantic tiaras -and parrot noses. She spoke of me as her dearest friend. I had only -met her once before, but she took it into her head to lionize me. I -believe some picture of mine had made a great success at the time, at -least had been chattered about in the penny newspapers, which is the -nineteenth-century standard of immortality. Suddenly I found myself -face to face with the young man whose personality had so strangely -stirred me. We were quite close, almost touching. Our eyes met again. -It was reckless of me, but I asked Lady Brandon to introduce me to him. -Perhaps it was not so reckless, after all. It was simply inevitable. -We would have spoken to each other without any introduction. I am sure -of that. Dorian told me so afterwards. He, too, felt that we were -destined to know each other." - -"And how did Lady Brandon describe this wonderful young man?" asked his -companion. "I know she goes in for giving a rapid _precis_ of all her -guests. I remember her bringing me up to a truculent and red-faced old -gentleman covered all over with orders and ribbons, and hissing into my -ear, in a tragic whisper which must have been perfectly audible to -everybody in the room, the most astounding details. I simply fled. I -like to find out people for myself. But Lady Brandon treats her guests -exactly as an auctioneer treats his goods. She either explains them -entirely away, or tells one everything about them except what one wants -to know." - -"Poor Lady Brandon! You are hard on her, Harry!" said Hallward -listlessly. - -"My dear fellow, she tried to found a _salon_, and only succeeded in -opening a restaurant. How could I admire her? But tell me, what did -she say about Mr. Dorian Gray?" - -"Oh, something like, 'Charming boy--poor dear mother and I absolutely -inseparable. Quite forget what he does--afraid he--doesn't do -anything--oh, yes, plays the piano--or is it the violin, dear Mr. -Gray?' Neither of us could help laughing, and we became friends at -once." - -"Laughter is not at all a bad beginning for a friendship, and it is far -the best ending for one," said the young lord, plucking another daisy. - -Hallward shook his head. "You don't understand what friendship is, -Harry," he murmured--"or what enmity is, for that matter. You like -every one; that is to say, you are indifferent to every one." - -"How horribly unjust of you!" cried Lord Henry, tilting his hat back -and looking up at the little clouds that, like ravelled skeins of -glossy white silk, were drifting across the hollowed turquoise of the -summer sky. "Yes; horribly unjust of you. I make a great difference -between people. I choose my friends for their good looks, my -acquaintances for their good characters, and my enemies for their good -intellects. A man cannot be too careful in the choice of his enemies. -I have not got one who is a fool. They are all men of some -intellectual power, and consequently they all appreciate me. Is that -very vain of me? I think it is rather vain." - -"I should think it was, Harry. But according to your category I must -be merely an acquaintance." - -"My dear old Basil, you are much more than an acquaintance." - -"And much less than a friend. A sort of brother, I suppose?" - -"Oh, brothers! I don't care for brothers. My elder brother won't die, -and my younger brothers seem never to do anything else." - -"Harry!" exclaimed Hallward, frowning. - -"My dear fellow, I am not quite serious. But I can't help detesting my -relations. I suppose it comes from the fact that none of us can stand -other people having the same faults as ourselves. I quite sympathize -with the rage of the English democracy against what they call the vices -of the upper orders. The masses feel that drunkenness, stupidity, and -immorality should be their own special property, and that if any one of -us makes an ass of himself, he is poaching on their preserves. When -poor Southwark got into the divorce court, their indignation was quite -magnificent. And yet I don't suppose that ten per cent of the -proletariat live correctly." - -"I don't agree with a single word that you have said, and, what is -more, Harry, I feel sure you don't either." - -Lord Henry stroked his pointed brown beard and tapped the toe of his -patent-leather boot with a tasselled ebony cane. "How English you are -Basil! That is the second time you have made that observation. If one -puts forward an idea to a true Englishman--always a rash thing to -do--he never dreams of considering whether the idea is right or wrong. -The only thing he considers of any importance is whether one believes -it oneself. Now, the value of an idea has nothing whatsoever to do -with the sincerity of the man who expresses it. Indeed, the -probabilities are that the more insincere the man is, the more purely -intellectual will the idea be, as in that case it will not be coloured -by either his wants, his desires, or his prejudices. However, I don't -propose to discuss politics, sociology, or metaphysics with you. I -like persons better than principles, and I like persons with no -principles better than anything else in the world. Tell me more about -Mr. Dorian Gray. How often do you see him?" - -"Every day. I couldn't be happy if I didn't see him every day. He is -absolutely necessary to me." - -"How extraordinary! I thought you would never care for anything but -your art." - -"He is all my art to me now," said the painter gravely. "I sometimes -think, Harry, that there are only two eras of any importance in the -world's history. The first is the appearance of a new medium for art, -and the second is the appearance of a new personality for art also. -What the invention of oil-painting was to the Venetians, the face of -Antinous was to late Greek sculpture, and the face of Dorian Gray will -some day be to me. It is not merely that I paint from him, draw from -him, sketch from him. Of course, I have done all that. But he is much -more to me than a model or a sitter. I won't tell you that I am -dissatisfied with what I have done of him, or that his beauty is such -that art cannot express it. There is nothing that art cannot express, -and I know that the work I have done, since I met Dorian Gray, is good -work, is the best work of my life. But in some curious way--I wonder -will you understand me?--his personality has suggested to me an -entirely new manner in art, an entirely new mode of style. I see -things differently, I think of them differently. I can now recreate -life in a way that was hidden from me before. 'A dream of form in days -of thought'--who is it who says that? I forget; but it is what Dorian -Gray has been to me. The merely visible presence of this lad--for he -seems to me little more than a lad, though he is really over -twenty--his merely visible presence--ah! I wonder can you realize all -that that means? Unconsciously he defines for me the lines of a fresh -school, a school that is to have in it all the passion of the romantic -spirit, all the perfection of the spirit that is Greek. The harmony of -soul and body--how much that is! We in our madness have separated the -two, and have invented a realism that is vulgar, an ideality that is -void. Harry! if you only knew what Dorian Gray is to me! You remember -that landscape of mine, for which Agnew offered me such a huge price -but which I would not part with? It is one of the best things I have -ever done. And why is it so? Because, while I was painting it, Dorian -Gray sat beside me. Some subtle influence passed from him to me, and -for the first time in my life I saw in the plain woodland the wonder I -had always looked for and always missed." - -"Basil, this is extraordinary! I must see Dorian Gray." - -Hallward got up from the seat and walked up and down the garden. After -some time he came back. "Harry," he said, "Dorian Gray is to me simply -a motive in art. You might see nothing in him. I see everything in -him. He is never more present in my work than when no image of him is -there. He is a suggestion, as I have said, of a new manner. I find -him in the curves of certain lines, in the loveliness and subtleties of -certain colours. That is all." - -"Then why won't you exhibit his portrait?" asked Lord Henry. - -"Because, without intending it, I have put into it some expression of -all this curious artistic idolatry, of which, of course, I have never -cared to speak to him. He knows nothing about it. He shall never know -anything about it. But the world might guess it, and I will not bare -my soul to their shallow prying eyes. My heart shall never be put -under their microscope. There is too much of myself in the thing, -Harry--too much of myself!" - -"Poets are not so scrupulous as you are. They know how useful passion -is for publication. Nowadays a broken heart will run to many editions." - -"I hate them for it," cried Hallward. "An artist should create -beautiful things, but should put nothing of his own life into them. We -live in an age when men treat art as if it were meant to be a form of -autobiography. We have lost the abstract sense of beauty. Some day I -will show the world what it is; and for that reason the world shall -never see my portrait of Dorian Gray." - -"I think you are wrong, Basil, but I won't argue with you. It is only -the intellectually lost who ever argue. Tell me, is Dorian Gray very -fond of you?" - -The painter considered for a few moments. "He likes me," he answered -after a pause; "I know he likes me. Of course I flatter him -dreadfully. I find a strange pleasure in saying things to him that I -know I shall be sorry for having said. As a rule, he is charming to -me, and we sit in the studio and talk of a thousand things. Now and -then, however, he is horribly thoughtless, and seems to take a real -delight in giving me pain. Then I feel, Harry, that I have given away -my whole soul to some one who treats it as if it were a flower to put -in his coat, a bit of decoration to charm his vanity, an ornament for a -summer's day." - -"Days in summer, Basil, are apt to linger," murmured Lord Henry. -"Perhaps you will tire sooner than he will. It is a sad thing to think -of, but there is no doubt that genius lasts longer than beauty. That -accounts for the fact that we all take such pains to over-educate -ourselves. In the wild struggle for existence, we want to have -something that endures, and so we fill our minds with rubbish and -facts, in the silly hope of keeping our place. The thoroughly -well-informed man--that is the modern ideal. And the mind of the -thoroughly well-informed man is a dreadful thing. It is like a -_bric-a-brac_ shop, all monsters and dust, with everything priced above -its proper value. I think you will tire first, all the same. Some day -you will look at your friend, and he will seem to you to be a little -out of drawing, or you won't like his tone of colour, or something. -You will bitterly reproach him in your own heart, and seriously think -that he has behaved very badly to you. The next time he calls, you -will be perfectly cold and indifferent. It will be a great pity, for -it will alter you. What you have told me is quite a romance, a romance -of art one might call it, and the worst of having a romance of any kind -is that it leaves one so unromantic." - -"Harry, don't talk like that. As long as I live, the personality of -Dorian Gray will dominate me. You can't feel what I feel. You change -too often." - -"Ah, my dear Basil, that is exactly why I can feel it. Those who are -faithful know only the trivial side of love: it is the faithless who -know love's tragedies." And Lord Henry struck a light on a dainty -silver case and began to smoke a cigarette with a self-conscious and -satisfied air, as if he had summed up the world in a phrase. There was -a rustle of chirruping sparrows in the green lacquer leaves of the ivy, -and the blue cloud-shadows chased themselves across the grass like -swallows. How pleasant it was in the garden! And how delightful other -people's emotions were!--much more delightful than their ideas, it -seemed to him. One's own soul, and the passions of one's -friends--those were the fascinating things in life. He pictured to -himself with silent amusement the tedious luncheon that he had missed -by staying so long with Basil Hallward. Had he gone to his aunt's, he -would have been sure to have met Lord Goodbody there, and the whole -conversation would have been about the feeding of the poor and the -necessity for model lodging-houses. Each class would have preached the -importance of those virtues, for whose exercise there was no necessity -in their own lives. The rich would have spoken on the value of thrift, -and the idle grown eloquent over the dignity of labour. It was -charming to have escaped all that! As he thought of his aunt, an idea -seemed to strike him. He turned to Hallward and said, "My dear fellow, -I have just remembered." - -"Remembered what, Harry?" - -"Where I heard the name of Dorian Gray." - -"Where was it?" asked Hallward, with a slight frown. - -"Don't look so angry, Basil. It was at my aunt, Lady Agatha's. She -told me she had discovered a wonderful young man who was going to help -her in the East End, and that his name was Dorian Gray. I am bound to -state that she never told me he was good-looking. Women have no -appreciation of good looks; at least, good women have not. She said -that he was very earnest and had a beautiful nature. I at once -pictured to myself a creature with spectacles and lank hair, horribly -freckled, and tramping about on huge feet. I wish I had known it was -your friend." - -"I am very glad you didn't, Harry." - -"Why?" - -"I don't want you to meet him." - -"You don't want me to meet him?" - -"No." - -"Mr. Dorian Gray is in the studio, sir," said the butler, coming into -the garden. - -"You must introduce me now," cried Lord Henry, laughing. - -The painter turned to his servant, who stood blinking in the sunlight. -"Ask Mr. Gray to wait, Parker: I shall be in in a few moments." The -man bowed and went up the walk. - -Then he looked at Lord Henry. "Dorian Gray is my dearest friend," he -said. "He has a simple and a beautiful nature. Your aunt was quite -right in what she said of him. Don't spoil him. Don't try to -influence him. Your influence would be bad. The world is wide, and -has many marvellous people in it. Don't take away from me the one -person who gives to my art whatever charm it possesses: my life as an -artist depends on him. Mind, Harry, I trust you." He spoke very -slowly, and the words seemed wrung out of him almost against his will. - -"What nonsense you talk!" said Lord Henry, smiling, and taking Hallward -by the arm, he almost led him into the house. - - - -CHAPTER 2 - -As they entered they saw Dorian Gray. He was seated at the piano, with -his back to them, turning over the pages of a volume of Schumann's -"Forest Scenes." "You must lend me these, Basil," he cried. "I want -to learn them. They are perfectly charming." - -"That entirely depends on how you sit to-day, Dorian." - -"Oh, I am tired of sitting, and I don't want a life-sized portrait of -myself," answered the lad, swinging round on the music-stool in a -wilful, petulant manner. When he caught sight of Lord Henry, a faint -blush coloured his cheeks for a moment, and he started up. "I beg your -pardon, Basil, but I didn't know you had any one with you." - -"This is Lord Henry Wotton, Dorian, an old Oxford friend of mine. I -have just been telling him what a capital sitter you were, and now you -have spoiled everything." - -"You have not spoiled my pleasure in meeting you, Mr. Gray," said Lord -Henry, stepping forward and extending his hand. "My aunt has often -spoken to me about you. You are one of her favourites, and, I am -afraid, one of her victims also." - -"I am in Lady Agatha's black books at present," answered Dorian with a -funny look of penitence. "I promised to go to a club in Whitechapel -with her last Tuesday, and I really forgot all about it. We were to -have played a duet together--three duets, I believe. I don't know what -she will say to me. I am far too frightened to call." - -"Oh, I will make your peace with my aunt. She is quite devoted to you. -And I don't think it really matters about your not being there. The -audience probably thought it was a duet. When Aunt Agatha sits down to -the piano, she makes quite enough noise for two people." - -"That is very horrid to her, and not very nice to me," answered Dorian, -laughing. - -Lord Henry looked at him. Yes, he was certainly wonderfully handsome, -with his finely curved scarlet lips, his frank blue eyes, his crisp -gold hair. There was something in his face that made one trust him at -once. All the candour of youth was there, as well as all youth's -passionate purity. One felt that he had kept himself unspotted from -the world. No wonder Basil Hallward worshipped him. - -"You are too charming to go in for philanthropy, Mr. Gray--far too -charming." And Lord Henry flung himself down on the divan and opened -his cigarette-case. - -The painter had been busy mixing his colours and getting his brushes -ready. He was looking worried, and when he heard Lord Henry's last -remark, he glanced at him, hesitated for a moment, and then said, -"Harry, I want to finish this picture to-day. Would you think it -awfully rude of me if I asked you to go away?" - -Lord Henry smiled and looked at Dorian Gray. "Am I to go, Mr. Gray?" -he asked. - -"Oh, please don't, Lord Henry. I see that Basil is in one of his sulky -moods, and I can't bear him when he sulks. Besides, I want you to tell -me why I should not go in for philanthropy." - -"I don't know that I shall tell you that, Mr. Gray. It is so tedious a -subject that one would have to talk seriously about it. But I -certainly shall not run away, now that you have asked me to stop. You -don't really mind, Basil, do you? You have often told me that you -liked your sitters to have some one to chat to." - -Hallward bit his lip. "If Dorian wishes it, of course you must stay. -Dorian's whims are laws to everybody, except himself." - -Lord Henry took up his hat and gloves. "You are very pressing, Basil, -but I am afraid I must go. I have promised to meet a man at the -Orleans. Good-bye, Mr. Gray. Come and see me some afternoon in Curzon -Street. I am nearly always at home at five o'clock. Write to me when -you are coming. I should be sorry to miss you." - -"Basil," cried Dorian Gray, "if Lord Henry Wotton goes, I shall go, -too. You never open your lips while you are painting, and it is -horribly dull standing on a platform and trying to look pleasant. Ask -him to stay. I insist upon it." - -"Stay, Harry, to oblige Dorian, and to oblige me," said Hallward, -gazing intently at his picture. "It is quite true, I never talk when I -am working, and never listen either, and it must be dreadfully tedious -for my unfortunate sitters. I beg you to stay." - -"But what about my man at the Orleans?" - -The painter laughed. "I don't think there will be any difficulty about -that. Sit down again, Harry. And now, Dorian, get up on the platform, -and don't move about too much, or pay any attention to what Lord Henry -says. He has a very bad influence over all his friends, with the -single exception of myself." - -Dorian Gray stepped up on the dais with the air of a young Greek -martyr, and made a little _moue_ of discontent to Lord Henry, to whom he -had rather taken a fancy. He was so unlike Basil. They made a -delightful contrast. And he had such a beautiful voice. After a few -moments he said to him, "Have you really a very bad influence, Lord -Henry? As bad as Basil says?" - -"There is no such thing as a good influence, Mr. Gray. All influence -is immoral--immoral from the scientific point of view." - -"Why?" - -"Because to influence a person is to give him one's own soul. He does -not think his natural thoughts, or burn with his natural passions. His -virtues are not real to him. His sins, if there are such things as -sins, are borrowed. He becomes an echo of some one else's music, an -actor of a part that has not been written for him. The aim of life is -self-development. To realize one's nature perfectly--that is what each -of us is here for. People are afraid of themselves, nowadays. They -have forgotten the highest of all duties, the duty that one owes to -one's self. Of course, they are charitable. They feed the hungry and -clothe the beggar. But their own souls starve, and are naked. Courage -has gone out of our race. Perhaps we never really had it. The terror -of society, which is the basis of morals, the terror of God, which is -the secret of religion--these are the two things that govern us. And -yet--" - -"Just turn your head a little more to the right, Dorian, like a good -boy," said the painter, deep in his work and conscious only that a look -had come into the lad's face that he had never seen there before. - -"And yet," continued Lord Henry, in his low, musical voice, and with -that graceful wave of the hand that was always so characteristic of -him, and that he had even in his Eton days, "I believe that if one man -were to live out his life fully and completely, were to give form to -every feeling, expression to every thought, reality to every dream--I -believe that the world would gain such a fresh impulse of joy that we -would forget all the maladies of mediaevalism, and return to the -Hellenic ideal--to something finer, richer than the Hellenic ideal, it -may be. But the bravest man amongst us is afraid of himself. The -mutilation of the savage has its tragic survival in the self-denial -that mars our lives. We are punished for our refusals. Every impulse -that we strive to strangle broods in the mind and poisons us. The body -sins once, and has done with its sin, for action is a mode of -purification. Nothing remains then but the recollection of a pleasure, -or the luxury of a regret. The only way to get rid of a temptation is -to yield to it. Resist it, and your soul grows sick with longing for -the things it has forbidden to itself, with desire for what its -monstrous laws have made monstrous and unlawful. It has been said that -the great events of the world take place in the brain. It is in the -brain, and the brain only, that the great sins of the world take place -also. You, Mr. Gray, you yourself, with your rose-red youth and your -rose-white boyhood, you have had passions that have made you afraid, -thoughts that have filled you with terror, day-dreams and sleeping -dreams whose mere memory might stain your cheek with shame--" - -"Stop!" faltered Dorian Gray, "stop! you bewilder me. I don't know -what to say. There is some answer to you, but I cannot find it. Don't -speak. Let me think. Or, rather, let me try not to think." - -For nearly ten minutes he stood there, motionless, with parted lips and -eyes strangely bright. He was dimly conscious that entirely fresh -influences were at work within him. Yet they seemed to him to have -come really from himself. The few words that Basil's friend had said -to him--words spoken by chance, no doubt, and with wilful paradox in -them--had touched some secret chord that had never been touched before, -but that he felt was now vibrating and throbbing to curious pulses. - -Music had stirred him like that. Music had troubled him many times. -But music was not articulate. It was not a new world, but rather -another chaos, that it created in us. Words! Mere words! How -terrible they were! How clear, and vivid, and cruel! One could not -escape from them. And yet what a subtle magic there was in them! They -seemed to be able to give a plastic form to formless things, and to -have a music of their own as sweet as that of viol or of lute. Mere -words! Was there anything so real as words? - -Yes; there had been things in his boyhood that he had not understood. -He understood them now. Life suddenly became fiery-coloured to him. -It seemed to him that he had been walking in fire. Why had he not -known it? - -With his subtle smile, Lord Henry watched him. He knew the precise -psychological moment when to say nothing. He felt intensely -interested. He was amazed at the sudden impression that his words had -produced, and, remembering a book that he had read when he was sixteen, -a book which had revealed to him much that he had not known before, he -wondered whether Dorian Gray was passing through a similar experience. -He had merely shot an arrow into the air. Had it hit the mark? How -fascinating the lad was! - -Hallward painted away with that marvellous bold touch of his, that had -the true refinement and perfect delicacy that in art, at any rate comes -only from strength. He was unconscious of the silence. - -"Basil, I am tired of standing," cried Dorian Gray suddenly. "I must -go out and sit in the garden. The air is stifling here." - -"My dear fellow, I am so sorry. When I am painting, I can't think of -anything else. But you never sat better. You were perfectly still. -And I have caught the effect I wanted--the half-parted lips and the -bright look in the eyes. I don't know what Harry has been saying to -you, but he has certainly made you have the most wonderful expression. -I suppose he has been paying you compliments. You mustn't believe a -word that he says." - -"He has certainly not been paying me compliments. Perhaps that is the -reason that I don't believe anything he has told me." - -"You know you believe it all," said Lord Henry, looking at him with his -dreamy languorous eyes. "I will go out to the garden with you. It is -horribly hot in the studio. Basil, let us have something iced to -drink, something with strawberries in it." - -"Certainly, Harry. Just touch the bell, and when Parker comes I will -tell him what you want. I have got to work up this background, so I -will join you later on. Don't keep Dorian too long. I have never been -in better form for painting than I am to-day. This is going to be my -masterpiece. It is my masterpiece as it stands." - -Lord Henry went out to the garden and found Dorian Gray burying his -face in the great cool lilac-blossoms, feverishly drinking in their -perfume as if it had been wine. He came close to him and put his hand -upon his shoulder. "You are quite right to do that," he murmured. -"Nothing can cure the soul but the senses, just as nothing can cure the -senses but the soul." - -The lad started and drew back. He was bareheaded, and the leaves had -tossed his rebellious curls and tangled all their gilded threads. -There was a look of fear in his eyes, such as people have when they are -suddenly awakened. His finely chiselled nostrils quivered, and some -hidden nerve shook the scarlet of his lips and left them trembling. - -"Yes," continued Lord Henry, "that is one of the great secrets of -life--to cure the soul by means of the senses, and the senses by means -of the soul. You are a wonderful creation. You know more than you -think you know, just as you know less than you want to know." - -Dorian Gray frowned and turned his head away. He could not help liking -the tall, graceful young man who was standing by him. His romantic, -olive-coloured face and worn expression interested him. There was -something in his low languid voice that was absolutely fascinating. -His cool, white, flowerlike hands, even, had a curious charm. They -moved, as he spoke, like music, and seemed to have a language of their -own. But he felt afraid of him, and ashamed of being afraid. Why had -it been left for a stranger to reveal him to himself? He had known -Basil Hallward for months, but the friendship between them had never -altered him. Suddenly there had come some one across his life who -seemed to have disclosed to him life's mystery. And, yet, what was -there to be afraid of? He was not a schoolboy or a girl. It was -absurd to be frightened. - -"Let us go and sit in the shade," said Lord Henry. "Parker has brought -out the drinks, and if you stay any longer in this glare, you will be -quite spoiled, and Basil will never paint you again. You really must -not allow yourself to become sunburnt. It would be unbecoming." - -"What can it matter?" cried Dorian Gray, laughing, as he sat down on -the seat at the end of the garden. - -"It should matter everything to you, Mr. Gray." - -"Why?" - -"Because you have the most marvellous youth, and youth is the one thing -worth having." - -"I don't feel that, Lord Henry." - -"No, you don't feel it now. Some day, when you are old and wrinkled -and ugly, when thought has seared your forehead with its lines, and -passion branded your lips with its hideous fires, you will feel it, you -will feel it terribly. Now, wherever you go, you charm the world. -Will it always be so? ... You have a wonderfully beautiful face, Mr. -Gray. Don't frown. You have. And beauty is a form of genius--is -higher, indeed, than genius, as it needs no explanation. It is of the -great facts of the world, like sunlight, or spring-time, or the -reflection in dark waters of that silver shell we call the moon. It -cannot be questioned. It has its divine right of sovereignty. It -makes princes of those who have it. You smile? Ah! when you have lost -it you won't smile.... People say sometimes that beauty is only -superficial. That may be so, but at least it is not so superficial as -thought is. To me, beauty is the wonder of wonders. It is only -shallow people who do not judge by appearances. The true mystery of -the world is the visible, not the invisible.... Yes, Mr. Gray, the -gods have been good to you. But what the gods give they quickly take -away. You have only a few years in which to live really, perfectly, -and fully. When your youth goes, your beauty will go with it, and then -you will suddenly discover that there are no triumphs left for you, or -have to content yourself with those mean triumphs that the memory of -your past will make more bitter than defeats. Every month as it wanes -brings you nearer to something dreadful. Time is jealous of you, and -wars against your lilies and your roses. You will become sallow, and -hollow-cheeked, and dull-eyed. You will suffer horribly.... Ah! -realize your youth while you have it. Don't squander the gold of your -days, listening to the tedious, trying to improve the hopeless failure, -or giving away your life to the ignorant, the common, and the vulgar. -These are the sickly aims, the false ideals, of our age. Live! Live -the wonderful life that is in you! Let nothing be lost upon you. Be -always searching for new sensations. Be afraid of nothing.... A new -Hedonism--that is what our century wants. You might be its visible -symbol. With your personality there is nothing you could not do. The -world belongs to you for a season.... The moment I met you I saw that -you were quite unconscious of what you really are, of what you really -might be. There was so much in you that charmed me that I felt I must -tell you something about yourself. I thought how tragic it would be if -you were wasted. For there is such a little time that your youth will -last--such a little time. The common hill-flowers wither, but they -blossom again. The laburnum will be as yellow next June as it is now. -In a month there will be purple stars on the clematis, and year after -year the green night of its leaves will hold its purple stars. But we -never get back our youth. The pulse of joy that beats in us at twenty -becomes sluggish. Our limbs fail, our senses rot. We degenerate into -hideous puppets, haunted by the memory of the passions of which we were -too much afraid, and the exquisite temptations that we had not the -courage to yield to. Youth! Youth! There is absolutely nothing in -the world but youth!" - -Dorian Gray listened, open-eyed and wondering. The spray of lilac fell -from his hand upon the gravel. A furry bee came and buzzed round it -for a moment. Then it began to scramble all over the oval stellated -globe of the tiny blossoms. He watched it with that strange interest -in trivial things that we try to develop when things of high import -make us afraid, or when we are stirred by some new emotion for which we -cannot find expression, or when some thought that terrifies us lays -sudden siege to the brain and calls on us to yield. After a time the -bee flew away. He saw it creeping into the stained trumpet of a Tyrian -convolvulus. The flower seemed to quiver, and then swayed gently to -and fro. - -Suddenly the painter appeared at the door of the studio and made -staccato signs for them to come in. They turned to each other and -smiled. - -"I am waiting," he cried. "Do come in. The light is quite perfect, -and you can bring your drinks." - -They rose up and sauntered down the walk together. Two green-and-white -butterflies fluttered past them, and in the pear-tree at the corner of -the garden a thrush began to sing. - -"You are glad you have met me, Mr. Gray," said Lord Henry, looking at -him. - -"Yes, I am glad now. I wonder shall I always be glad?" - -"Always! That is a dreadful word. It makes me shudder when I hear it. -Women are so fond of using it. They spoil every romance by trying to -make it last for ever. It is a meaningless word, too. The only -difference between a caprice and a lifelong passion is that the caprice -lasts a little longer." - -As they entered the studio, Dorian Gray put his hand upon Lord Henry's -arm. "In that case, let our friendship be a caprice," he murmured, -flushing at his own boldness, then stepped up on the platform and -resumed his pose. - -Lord Henry flung himself into a large wicker arm-chair and watched him. -The sweep and dash of the brush on the canvas made the only sound that -broke the stillness, except when, now and then, Hallward stepped back -to look at his work from a distance. In the slanting beams that -streamed through the open doorway the dust danced and was golden. The -heavy scent of the roses seemed to brood over everything. - -After about a quarter of an hour Hallward stopped painting, looked for -a long time at Dorian Gray, and then for a long time at the picture, -biting the end of one of his huge brushes and frowning. "It is quite -finished," he cried at last, and stooping down he wrote his name in -long vermilion letters on the left-hand corner of the canvas. - -Lord Henry came over and examined the picture. It was certainly a -wonderful work of art, and a wonderful likeness as well. - -"My dear fellow, I congratulate you most warmly," he said. "It is the -finest portrait of modern times. Mr. Gray, come over and look at -yourself." - -The lad started, as if awakened from some dream. - -"Is it really finished?" he murmured, stepping down from the platform. - -"Quite finished," said the painter. "And you have sat splendidly -to-day. I am awfully obliged to you." - -"That is entirely due to me," broke in Lord Henry. "Isn't it, Mr. -Gray?" - -Dorian made no answer, but passed listlessly in front of his picture -and turned towards it. When he saw it he drew back, and his cheeks -flushed for a moment with pleasure. A look of joy came into his eyes, -as if he had recognized himself for the first time. He stood there -motionless and in wonder, dimly conscious that Hallward was speaking to -him, but not catching the meaning of his words. The sense of his own -beauty came on him like a revelation. He had never felt it before. -Basil Hallward's compliments had seemed to him to be merely the -charming exaggeration of friendship. He had listened to them, laughed -at them, forgotten them. They had not influenced his nature. Then had -come Lord Henry Wotton with his strange panegyric on youth, his -terrible warning of its brevity. That had stirred him at the time, and -now, as he stood gazing at the shadow of his own loveliness, the full -reality of the description flashed across him. Yes, there would be a -day when his face would be wrinkled and wizen, his eyes dim and -colourless, the grace of his figure broken and deformed. The scarlet -would pass away from his lips and the gold steal from his hair. The -life that was to make his soul would mar his body. He would become -dreadful, hideous, and uncouth. - -As he thought of it, a sharp pang of pain struck through him like a -knife and made each delicate fibre of his nature quiver. His eyes -deepened into amethyst, and across them came a mist of tears. He felt -as if a hand of ice had been laid upon his heart. - -"Don't you like it?" cried Hallward at last, stung a little by the -lad's silence, not understanding what it meant. - -"Of course he likes it," said Lord Henry. "Who wouldn't like it? It -is one of the greatest things in modern art. I will give you anything -you like to ask for it. I must have it." - -"It is not my property, Harry." - -"Whose property is it?" - -"Dorian's, of course," answered the painter. - -"He is a very lucky fellow." - -"How sad it is!" murmured Dorian Gray with his eyes still fixed upon -his own portrait. "How sad it is! I shall grow old, and horrible, and -dreadful. But this picture will remain always young. It will never be -older than this particular day of June.... If it were only the other -way! If it were I who was to be always young, and the picture that was -to grow old! For that--for that--I would give everything! Yes, there -is nothing in the whole world I would not give! I would give my soul -for that!" - -"You would hardly care for such an arrangement, Basil," cried Lord -Henry, laughing. "It would be rather hard lines on your work." - -"I should object very strongly, Harry," said Hallward. - -Dorian Gray turned and looked at him. "I believe you would, Basil. -You like your art better than your friends. I am no more to you than a -green bronze figure. Hardly as much, I dare say." - -The painter stared in amazement. It was so unlike Dorian to speak like -that. What had happened? He seemed quite angry. His face was flushed -and his cheeks burning. - -"Yes," he continued, "I am less to you than your ivory Hermes or your -silver Faun. You will like them always. How long will you like me? -Till I have my first wrinkle, I suppose. I know, now, that when one -loses one's good looks, whatever they may be, one loses everything. -Your picture has taught me that. Lord Henry Wotton is perfectly right. -Youth is the only thing worth having. When I find that I am growing -old, I shall kill myself." - -Hallward turned pale and caught his hand. "Dorian! Dorian!" he cried, -"don't talk like that. I have never had such a friend as you, and I -shall never have such another. You are not jealous of material things, -are you?--you who are finer than any of them!" - -"I am jealous of everything whose beauty does not die. I am jealous of -the portrait you have painted of me. Why should it keep what I must -lose? Every moment that passes takes something from me and gives -something to it. Oh, if it were only the other way! If the picture -could change, and I could be always what I am now! Why did you paint -it? It will mock me some day--mock me horribly!" The hot tears welled -into his eyes; he tore his hand away and, flinging himself on the -divan, he buried his face in the cushions, as though he was praying. - -"This is your doing, Harry," said the painter bitterly. - -Lord Henry shrugged his shoulders. "It is the real Dorian Gray--that -is all." - -"It is not." - -"If it is not, what have I to do with it?" - -"You should have gone away when I asked you," he muttered. - -"I stayed when you asked me," was Lord Henry's answer. - -"Harry, I can't quarrel with my two best friends at once, but between -you both you have made me hate the finest piece of work I have ever -done, and I will destroy it. What is it but canvas and colour? I will -not let it come across our three lives and mar them." - -Dorian Gray lifted his golden head from the pillow, and with pallid -face and tear-stained eyes, looked at him as he walked over to the deal -painting-table that was set beneath the high curtained window. What -was he doing there? His fingers were straying about among the litter -of tin tubes and dry brushes, seeking for something. Yes, it was for -the long palette-knife, with its thin blade of lithe steel. He had -found it at last. He was going to rip up the canvas. - -With a stifled sob the lad leaped from the couch, and, rushing over to -Hallward, tore the knife out of his hand, and flung it to the end of -the studio. "Don't, Basil, don't!" he cried. "It would be murder!" - -"I am glad you appreciate my work at last, Dorian," said the painter -coldly when he had recovered from his surprise. "I never thought you -would." - -"Appreciate it? I am in love with it, Basil. It is part of myself. I -feel that." - -"Well, as soon as you are dry, you shall be varnished, and framed, and -sent home. Then you can do what you like with yourself." And he walked -across the room and rang the bell for tea. "You will have tea, of -course, Dorian? And so will you, Harry? Or do you object to such -simple pleasures?" - -"I adore simple pleasures," said Lord Henry. "They are the last refuge -of the complex. But I don't like scenes, except on the stage. What -absurd fellows you are, both of you! I wonder who it was defined man -as a rational animal. It was the most premature definition ever given. -Man is many things, but he is not rational. I am glad he is not, after -all--though I wish you chaps would not squabble over the picture. You -had much better let me have it, Basil. This silly boy doesn't really -want it, and I really do." - -"If you let any one have it but me, Basil, I shall never forgive you!" -cried Dorian Gray; "and I don't allow people to call me a silly boy." - -"You know the picture is yours, Dorian. I gave it to you before it -existed." - -"And you know you have been a little silly, Mr. Gray, and that you -don't really object to being reminded that you are extremely young." - -"I should have objected very strongly this morning, Lord Henry." - -"Ah! this morning! You have lived since then." - -There came a knock at the door, and the butler entered with a laden -tea-tray and set it down upon a small Japanese table. There was a -rattle of cups and saucers and the hissing of a fluted Georgian urn. -Two globe-shaped china dishes were brought in by a page. Dorian Gray -went over and poured out the tea. The two men sauntered languidly to -the table and examined what was under the covers. - -"Let us go to the theatre to-night," said Lord Henry. "There is sure -to be something on, somewhere. I have promised to dine at White's, but -it is only with an old friend, so I can send him a wire to say that I -am ill, or that I am prevented from coming in consequence of a -subsequent engagement. I think that would be a rather nice excuse: it -would have all the surprise of candour." - -"It is such a bore putting on one's dress-clothes," muttered Hallward. -"And, when one has them on, they are so horrid." - -"Yes," answered Lord Henry dreamily, "the costume of the nineteenth -century is detestable. It is so sombre, so depressing. Sin is the -only real colour-element left in modern life." - -"You really must not say things like that before Dorian, Harry." - -"Before which Dorian? The one who is pouring out tea for us, or the -one in the picture?" - -"Before either." - -"I should like to come to the theatre with you, Lord Henry," said the -lad. - -"Then you shall come; and you will come, too, Basil, won't you?" - -"I can't, really. I would sooner not. I have a lot of work to do." - -"Well, then, you and I will go alone, Mr. Gray." - -"I should like that awfully." - -The painter bit his lip and walked over, cup in hand, to the picture. -"I shall stay with the real Dorian," he said, sadly. - -"Is it the real Dorian?" cried the original of the portrait, strolling -across to him. "Am I really like that?" - -"Yes; you are just like that." - -"How wonderful, Basil!" - -"At least you are like it in appearance. But it will never alter," -sighed Hallward. "That is something." - -"What a fuss people make about fidelity!" exclaimed Lord Henry. "Why, -even in love it is purely a question for physiology. It has nothing to -do with our own will. Young men want to be faithful, and are not; old -men want to be faithless, and cannot: that is all one can say." - -"Don't go to the theatre to-night, Dorian," said Hallward. "Stop and -dine with me." - -"I can't, Basil." - -"Why?" - -"Because I have promised Lord Henry Wotton to go with him." - -"He won't like you the better for keeping your promises. He always -breaks his own. I beg you not to go." - -Dorian Gray laughed and shook his head. - -"I entreat you." - -The lad hesitated, and looked over at Lord Henry, who was watching them -from the tea-table with an amused smile. - -"I must go, Basil," he answered. - -"Very well," said Hallward, and he went over and laid down his cup on -the tray. "It is rather late, and, as you have to dress, you had -better lose no time. Good-bye, Harry. Good-bye, Dorian. Come and see -me soon. Come to-morrow." - -"Certainly." - -"You won't forget?" - -"No, of course not," cried Dorian. - -"And ... Harry!" - -"Yes, Basil?" - -"Remember what I asked you, when we were in the garden this morning." - -"I have forgotten it." - -"I trust you." - -"I wish I could trust myself," said Lord Henry, laughing. "Come, Mr. -Gray, my hansom is outside, and I can drop you at your own place. -Good-bye, Basil. It has been a most interesting afternoon." - -As the door closed behind them, the painter flung himself down on a -sofa, and a look of pain came into his face. - - - -CHAPTER 3 - -At half-past twelve next day Lord Henry Wotton strolled from Curzon -Street over to the Albany to call on his uncle, Lord Fermor, a genial -if somewhat rough-mannered old bachelor, whom the outside world called -selfish because it derived no particular benefit from him, but who was -considered generous by Society as he fed the people who amused him. -His father had been our ambassador at Madrid when Isabella was young -and Prim unthought of, but had retired from the diplomatic service in a -capricious moment of annoyance on not being offered the Embassy at -Paris, a post to which he considered that he was fully entitled by -reason of his birth, his indolence, the good English of his dispatches, -and his inordinate passion for pleasure. The son, who had been his -father's secretary, had resigned along with his chief, somewhat -foolishly as was thought at the time, and on succeeding some months -later to the title, had set himself to the serious study of the great -aristocratic art of doing absolutely nothing. He had two large town -houses, but preferred to live in chambers as it was less trouble, and -took most of his meals at his club. He paid some attention to the -management of his collieries in the Midland counties, excusing himself -for this taint of industry on the ground that the one advantage of -having coal was that it enabled a gentleman to afford the decency of -burning wood on his own hearth. In politics he was a Tory, except when -the Tories were in office, during which period he roundly abused them -for being a pack of Radicals. He was a hero to his valet, who bullied -him, and a terror to most of his relations, whom he bullied in turn. -Only England could have produced him, and he always said that the -country was going to the dogs. His principles were out of date, but -there was a good deal to be said for his prejudices. - -When Lord Henry entered the room, he found his uncle sitting in a rough -shooting-coat, smoking a cheroot and grumbling over _The Times_. "Well, -Harry," said the old gentleman, "what brings you out so early? I -thought you dandies never got up till two, and were not visible till -five." - -"Pure family affection, I assure you, Uncle George. I want to get -something out of you." - -"Money, I suppose," said Lord Fermor, making a wry face. "Well, sit -down and tell me all about it. Young people, nowadays, imagine that -money is everything." - -"Yes," murmured Lord Henry, settling his button-hole in his coat; "and -when they grow older they know it. But I don't want money. It is only -people who pay their bills who want that, Uncle George, and I never pay -mine. Credit is the capital of a younger son, and one lives charmingly -upon it. Besides, I always deal with Dartmoor's tradesmen, and -consequently they never bother me. What I want is information: not -useful information, of course; useless information." - -"Well, I can tell you anything that is in an English Blue Book, Harry, -although those fellows nowadays write a lot of nonsense. When I was in -the Diplomatic, things were much better. But I hear they let them in -now by examination. What can you expect? Examinations, sir, are pure -humbug from beginning to end. If a man is a gentleman, he knows quite -enough, and if he is not a gentleman, whatever he knows is bad for him." - -"Mr. Dorian Gray does not belong to Blue Books, Uncle George," said -Lord Henry languidly. - -"Mr. Dorian Gray? Who is he?" asked Lord Fermor, knitting his bushy -white eyebrows. - -"That is what I have come to learn, Uncle George. Or rather, I know -who he is. He is the last Lord Kelso's grandson. His mother was a -Devereux, Lady Margaret Devereux. I want you to tell me about his -mother. What was she like? Whom did she marry? You have known nearly -everybody in your time, so you might have known her. I am very much -interested in Mr. Gray at present. I have only just met him." - -"Kelso's grandson!" echoed the old gentleman. "Kelso's grandson! ... -Of course.... I knew his mother intimately. I believe I was at her -christening. She was an extraordinarily beautiful girl, Margaret -Devereux, and made all the men frantic by running away with a penniless -young fellow--a mere nobody, sir, a subaltern in a foot regiment, or -something of that kind. Certainly. I remember the whole thing as if -it happened yesterday. The poor chap was killed in a duel at Spa a few -months after the marriage. There was an ugly story about it. They -said Kelso got some rascally adventurer, some Belgian brute, to insult -his son-in-law in public--paid him, sir, to do it, paid him--and that -the fellow spitted his man as if he had been a pigeon. The thing was -hushed up, but, egad, Kelso ate his chop alone at the club for some -time afterwards. He brought his daughter back with him, I was told, -and she never spoke to him again. Oh, yes; it was a bad business. The -girl died, too, died within a year. So she left a son, did she? I had -forgotten that. What sort of boy is he? If he is like his mother, he -must be a good-looking chap." - -"He is very good-looking," assented Lord Henry. - -"I hope he will fall into proper hands," continued the old man. "He -should have a pot of money waiting for him if Kelso did the right thing -by him. His mother had money, too. All the Selby property came to -her, through her grandfather. Her grandfather hated Kelso, thought him -a mean dog. He was, too. Came to Madrid once when I was there. Egad, -I was ashamed of him. The Queen used to ask me about the English noble -who was always quarrelling with the cabmen about their fares. They -made quite a story of it. I didn't dare show my face at Court for a -month. I hope he treated his grandson better than he did the jarvies." - -"I don't know," answered Lord Henry. "I fancy that the boy will be -well off. He is not of age yet. He has Selby, I know. He told me so. -And ... his mother was very beautiful?" - -"Margaret Devereux was one of the loveliest creatures I ever saw, -Harry. What on earth induced her to behave as she did, I never could -understand. She could have married anybody she chose. Carlington was -mad after her. She was romantic, though. All the women of that family -were. The men were a poor lot, but, egad! the women were wonderful. -Carlington went on his knees to her. Told me so himself. She laughed -at him, and there wasn't a girl in London at the time who wasn't after -him. And by the way, Harry, talking about silly marriages, what is -this humbug your father tells me about Dartmoor wanting to marry an -American? Ain't English girls good enough for him?" - -"It is rather fashionable to marry Americans just now, Uncle George." - -"I'll back English women against the world, Harry," said Lord Fermor, -striking the table with his fist. - -"The betting is on the Americans." - -"They don't last, I am told," muttered his uncle. - -"A long engagement exhausts them, but they are capital at a -steeplechase. They take things flying. I don't think Dartmoor has a -chance." - -"Who are her people?" grumbled the old gentleman. "Has she got any?" - -Lord Henry shook his head. "American girls are as clever at concealing -their parents, as English women are at concealing their past," he said, -rising to go. - -"They are pork-packers, I suppose?" - -"I hope so, Uncle George, for Dartmoor's sake. I am told that -pork-packing is the most lucrative profession in America, after -politics." - -"Is she pretty?" - -"She behaves as if she was beautiful. Most American women do. It is -the secret of their charm." - -"Why can't these American women stay in their own country? They are -always telling us that it is the paradise for women." - -"It is. That is the reason why, like Eve, they are so excessively -anxious to get out of it," said Lord Henry. "Good-bye, Uncle George. -I shall be late for lunch, if I stop any longer. Thanks for giving me -the information I wanted. I always like to know everything about my -new friends, and nothing about my old ones." - -"Where are you lunching, Harry?" - -"At Aunt Agatha's. I have asked myself and Mr. Gray. He is her latest -_protege_." - -"Humph! tell your Aunt Agatha, Harry, not to bother me any more with -her charity appeals. I am sick of them. Why, the good woman thinks -that I have nothing to do but to write cheques for her silly fads." - -"All right, Uncle George, I'll tell her, but it won't have any effect. -Philanthropic people lose all sense of humanity. It is their -distinguishing characteristic." - -The old gentleman growled approvingly and rang the bell for his -servant. Lord Henry passed up the low arcade into Burlington Street -and turned his steps in the direction of Berkeley Square. - -So that was the story of Dorian Gray's parentage. Crudely as it had -been told to him, it had yet stirred him by its suggestion of a -strange, almost modern romance. A beautiful woman risking everything -for a mad passion. A few wild weeks of happiness cut short by a -hideous, treacherous crime. Months of voiceless agony, and then a -child born in pain. The mother snatched away by death, the boy left to -solitude and the tyranny of an old and loveless man. Yes; it was an -interesting background. It posed the lad, made him more perfect, as it -were. Behind every exquisite thing that existed, there was something -tragic. Worlds had to be in travail, that the meanest flower might -blow.... And how charming he had been at dinner the night before, as -with startled eyes and lips parted in frightened pleasure he had sat -opposite to him at the club, the red candleshades staining to a richer -rose the wakening wonder of his face. Talking to him was like playing -upon an exquisite violin. He answered to every touch and thrill of the -bow.... There was something terribly enthralling in the exercise of -influence. No other activity was like it. To project one's soul into -some gracious form, and let it tarry there for a moment; to hear one's -own intellectual views echoed back to one with all the added music of -passion and youth; to convey one's temperament into another as though -it were a subtle fluid or a strange perfume: there was a real joy in -that--perhaps the most satisfying joy left to us in an age so limited -and vulgar as our own, an age grossly carnal in its pleasures, and -grossly common in its aims.... He was a marvellous type, too, this lad, -whom by so curious a chance he had met in Basil's studio, or could be -fashioned into a marvellous type, at any rate. Grace was his, and the -white purity of boyhood, and beauty such as old Greek marbles kept for -us. There was nothing that one could not do with him. He could be -made a Titan or a toy. What a pity it was that such beauty was -destined to fade! ... And Basil? From a psychological point of view, -how interesting he was! The new manner in art, the fresh mode of -looking at life, suggested so strangely by the merely visible presence -of one who was unconscious of it all; the silent spirit that dwelt in -dim woodland, and walked unseen in open field, suddenly showing -herself, Dryadlike and not afraid, because in his soul who sought for -her there had been wakened that wonderful vision to which alone are -wonderful things revealed; the mere shapes and patterns of things -becoming, as it were, refined, and gaining a kind of symbolical value, -as though they were themselves patterns of some other and more perfect -form whose shadow they made real: how strange it all was! He -remembered something like it in history. Was it not Plato, that artist -in thought, who had first analyzed it? Was it not Buonarotti who had -carved it in the coloured marbles of a sonnet-sequence? But in our own -century it was strange.... Yes; he would try to be to Dorian Gray -what, without knowing it, the lad was to the painter who had fashioned -the wonderful portrait. He would seek to dominate him--had already, -indeed, half done so. He would make that wonderful spirit his own. -There was something fascinating in this son of love and death. - -Suddenly he stopped and glanced up at the houses. He found that he had -passed his aunt's some distance, and, smiling to himself, turned back. -When he entered the somewhat sombre hall, the butler told him that they -had gone in to lunch. He gave one of the footmen his hat and stick and -passed into the dining-room. - -"Late as usual, Harry," cried his aunt, shaking her head at him. - -He invented a facile excuse, and having taken the vacant seat next to -her, looked round to see who was there. Dorian bowed to him shyly from -the end of the table, a flush of pleasure stealing into his cheek. -Opposite was the Duchess of Harley, a lady of admirable good-nature and -good temper, much liked by every one who knew her, and of those ample -architectural proportions that in women who are not duchesses are -described by contemporary historians as stoutness. Next to her sat, on -her right, Sir Thomas Burdon, a Radical member of Parliament, who -followed his leader in public life and in private life followed the -best cooks, dining with the Tories and thinking with the Liberals, in -accordance with a wise and well-known rule. The post on her left was -occupied by Mr. Erskine of Treadley, an old gentleman of considerable -charm and culture, who had fallen, however, into bad habits of silence, -having, as he explained once to Lady Agatha, said everything that he -had to say before he was thirty. His own neighbour was Mrs. Vandeleur, -one of his aunt's oldest friends, a perfect saint amongst women, but so -dreadfully dowdy that she reminded one of a badly bound hymn-book. -Fortunately for him she had on the other side Lord Faudel, a most -intelligent middle-aged mediocrity, as bald as a ministerial statement -in the House of Commons, with whom she was conversing in that intensely -earnest manner which is the one unpardonable error, as he remarked once -himself, that all really good people fall into, and from which none of -them ever quite escape. - -"We are talking about poor Dartmoor, Lord Henry," cried the duchess, -nodding pleasantly to him across the table. "Do you think he will -really marry this fascinating young person?" - -"I believe she has made up her mind to propose to him, Duchess." - -"How dreadful!" exclaimed Lady Agatha. "Really, some one should -interfere." - -"I am told, on excellent authority, that her father keeps an American -dry-goods store," said Sir Thomas Burdon, looking supercilious. - -"My uncle has already suggested pork-packing, Sir Thomas." - -"Dry-goods! What are American dry-goods?" asked the duchess, raising -her large hands in wonder and accentuating the verb. - -"American novels," answered Lord Henry, helping himself to some quail. - -The duchess looked puzzled. - -"Don't mind him, my dear," whispered Lady Agatha. "He never means -anything that he says." - -"When America was discovered," said the Radical member--and he began to -give some wearisome facts. Like all people who try to exhaust a -subject, he exhausted his listeners. The duchess sighed and exercised -her privilege of interruption. "I wish to goodness it never had been -discovered at all!" she exclaimed. "Really, our girls have no chance -nowadays. It is most unfair." - -"Perhaps, after all, America never has been discovered," said Mr. -Erskine; "I myself would say that it had merely been detected." - -"Oh! but I have seen specimens of the inhabitants," answered the -duchess vaguely. "I must confess that most of them are extremely -pretty. And they dress well, too. They get all their dresses in -Paris. I wish I could afford to do the same." - -"They say that when good Americans die they go to Paris," chuckled Sir -Thomas, who had a large wardrobe of Humour's cast-off clothes. - -"Really! And where do bad Americans go to when they die?" inquired the -duchess. - -"They go to America," murmured Lord Henry. - -Sir Thomas frowned. "I am afraid that your nephew is prejudiced -against that great country," he said to Lady Agatha. "I have travelled -all over it in cars provided by the directors, who, in such matters, -are extremely civil. I assure you that it is an education to visit it." - -"But must we really see Chicago in order to be educated?" asked Mr. -Erskine plaintively. "I don't feel up to the journey." - -Sir Thomas waved his hand. "Mr. Erskine of Treadley has the world on -his shelves. We practical men like to see things, not to read about -them. The Americans are an extremely interesting people. They are -absolutely reasonable. I think that is their distinguishing -characteristic. Yes, Mr. Erskine, an absolutely reasonable people. I -assure you there is no nonsense about the Americans." - -"How dreadful!" cried Lord Henry. "I can stand brute force, but brute -reason is quite unbearable. There is something unfair about its use. -It is hitting below the intellect." - -"I do not understand you," said Sir Thomas, growing rather red. - -"I do, Lord Henry," murmured Mr. Erskine, with a smile. - -"Paradoxes are all very well in their way...." rejoined the baronet. - -"Was that a paradox?" asked Mr. Erskine. "I did not think so. Perhaps -it was. Well, the way of paradoxes is the way of truth. To test -reality we must see it on the tight rope. When the verities become -acrobats, we can judge them." - -"Dear me!" said Lady Agatha, "how you men argue! I am sure I never can -make out what you are talking about. Oh! Harry, I am quite vexed with -you. Why do you try to persuade our nice Mr. Dorian Gray to give up -the East End? I assure you he would be quite invaluable. They would -love his playing." - -"I want him to play to me," cried Lord Henry, smiling, and he looked -down the table and caught a bright answering glance. - -"But they are so unhappy in Whitechapel," continued Lady Agatha. - -"I can sympathize with everything except suffering," said Lord Henry, -shrugging his shoulders. "I cannot sympathize with that. It is too -ugly, too horrible, too distressing. There is something terribly -morbid in the modern sympathy with pain. One should sympathize with -the colour, the beauty, the joy of life. The less said about life's -sores, the better." - -"Still, the East End is a very important problem," remarked Sir Thomas -with a grave shake of the head. - -"Quite so," answered the young lord. "It is the problem of slavery, -and we try to solve it by amusing the slaves." - -The politician looked at him keenly. "What change do you propose, -then?" he asked. - -Lord Henry laughed. "I don't desire to change anything in England -except the weather," he answered. "I am quite content with philosophic -contemplation. But, as the nineteenth century has gone bankrupt -through an over-expenditure of sympathy, I would suggest that we should -appeal to science to put us straight. The advantage of the emotions is -that they lead us astray, and the advantage of science is that it is -not emotional." - -"But we have such grave responsibilities," ventured Mrs. Vandeleur -timidly. - -"Terribly grave," echoed Lady Agatha. - -Lord Henry looked over at Mr. Erskine. "Humanity takes itself too -seriously. It is the world's original sin. If the caveman had known -how to laugh, history would have been different." - -"You are really very comforting," warbled the duchess. "I have always -felt rather guilty when I came to see your dear aunt, for I take no -interest at all in the East End. For the future I shall be able to -look her in the face without a blush." - -"A blush is very becoming, Duchess," remarked Lord Henry. - -"Only when one is young," she answered. "When an old woman like myself -blushes, it is a very bad sign. Ah! Lord Henry, I wish you would tell -me how to become young again." - -He thought for a moment. "Can you remember any great error that you -committed in your early days, Duchess?" he asked, looking at her across -the table. - -"A great many, I fear," she cried. - -"Then commit them over again," he said gravely. "To get back one's -youth, one has merely to repeat one's follies." - -"A delightful theory!" she exclaimed. "I must put it into practice." - -"A dangerous theory!" came from Sir Thomas's tight lips. Lady Agatha -shook her head, but could not help being amused. Mr. Erskine listened. - -"Yes," he continued, "that is one of the great secrets of life. -Nowadays most people die of a sort of creeping common sense, and -discover when it is too late that the only things one never regrets are -one's mistakes." - -A laugh ran round the table. - -He played with the idea and grew wilful; tossed it into the air and -transformed it; let it escape and recaptured it; made it iridescent -with fancy and winged it with paradox. The praise of folly, as he went -on, soared into a philosophy, and philosophy herself became young, and -catching the mad music of pleasure, wearing, one might fancy, her -wine-stained robe and wreath of ivy, danced like a Bacchante over the -hills of life, and mocked the slow Silenus for being sober. Facts fled -before her like frightened forest things. Her white feet trod the huge -press at which wise Omar sits, till the seething grape-juice rose round -her bare limbs in waves of purple bubbles, or crawled in red foam over -the vat's black, dripping, sloping sides. It was an extraordinary -improvisation. He felt that the eyes of Dorian Gray were fixed on him, -and the consciousness that amongst his audience there was one whose -temperament he wished to fascinate seemed to give his wit keenness and -to lend colour to his imagination. He was brilliant, fantastic, -irresponsible. He charmed his listeners out of themselves, and they -followed his pipe, laughing. Dorian Gray never took his gaze off him, -but sat like one under a spell, smiles chasing each other over his lips -and wonder growing grave in his darkening eyes. - -At last, liveried in the costume of the age, reality entered the room -in the shape of a servant to tell the duchess that her carriage was -waiting. She wrung her hands in mock despair. "How annoying!" she -cried. "I must go. I have to call for my husband at the club, to take -him to some absurd meeting at Willis's Rooms, where he is going to be -in the chair. If I am late he is sure to be furious, and I couldn't -have a scene in this bonnet. It is far too fragile. A harsh word -would ruin it. No, I must go, dear Agatha. Good-bye, Lord Henry, you -are quite delightful and dreadfully demoralizing. I am sure I don't -know what to say about your views. You must come and dine with us some -night. Tuesday? Are you disengaged Tuesday?" - -"For you I would throw over anybody, Duchess," said Lord Henry with a -bow. - -"Ah! that is very nice, and very wrong of you," she cried; "so mind you -come"; and she swept out of the room, followed by Lady Agatha and the -other ladies. - -When Lord Henry had sat down again, Mr. Erskine moved round, and taking -a chair close to him, placed his hand upon his arm. - -"You talk books away," he said; "why don't you write one?" - -"I am too fond of reading books to care to write them, Mr. Erskine. I -should like to write a novel certainly, a novel that would be as lovely -as a Persian carpet and as unreal. But there is no literary public in -England for anything except newspapers, primers, and encyclopaedias. -Of all people in the world the English have the least sense of the -beauty of literature." - -"I fear you are right," answered Mr. Erskine. "I myself used to have -literary ambitions, but I gave them up long ago. And now, my dear -young friend, if you will allow me to call you so, may I ask if you -really meant all that you said to us at lunch?" - -"I quite forget what I said," smiled Lord Henry. "Was it all very bad?" - -"Very bad indeed. In fact I consider you extremely dangerous, and if -anything happens to our good duchess, we shall all look on you as being -primarily responsible. But I should like to talk to you about life. -The generation into which I was born was tedious. Some day, when you -are tired of London, come down to Treadley and expound to me your -philosophy of pleasure over some admirable Burgundy I am fortunate -enough to possess." - -"I shall be charmed. A visit to Treadley would be a great privilege. -It has a perfect host, and a perfect library." - -"You will complete it," answered the old gentleman with a courteous -bow. "And now I must bid good-bye to your excellent aunt. I am due at -the Athenaeum. It is the hour when we sleep there." - -"All of you, Mr. Erskine?" - -"Forty of us, in forty arm-chairs. We are practising for an English -Academy of Letters." - -Lord Henry laughed and rose. "I am going to the park," he cried. - -As he was passing out of the door, Dorian Gray touched him on the arm. -"Let me come with you," he murmured. - -"But I thought you had promised Basil Hallward to go and see him," -answered Lord Henry. - -"I would sooner come with you; yes, I feel I must come with you. Do -let me. And you will promise to talk to me all the time? No one talks -so wonderfully as you do." - -"Ah! I have talked quite enough for to-day," said Lord Henry, smiling. -"All I want now is to look at life. You may come and look at it with -me, if you care to." - - - -CHAPTER 4 - -One afternoon, a month later, Dorian Gray was reclining in a luxurious -arm-chair, in the little library of Lord Henry's house in Mayfair. It -was, in its way, a very charming room, with its high panelled -wainscoting of olive-stained oak, its cream-coloured frieze and ceiling -of raised plasterwork, and its brickdust felt carpet strewn with silk, -long-fringed Persian rugs. On a tiny satinwood table stood a statuette -by Clodion, and beside it lay a copy of Les Cent Nouvelles, bound for -Margaret of Valois by Clovis Eve and powdered with the gilt daisies -that Queen had selected for her device. Some large blue china jars and -parrot-tulips were ranged on the mantelshelf, and through the small -leaded panes of the window streamed the apricot-coloured light of a -summer day in London. - -Lord Henry had not yet come in. He was always late on principle, his -principle being that punctuality is the thief of time. So the lad was -looking rather sulky, as with listless fingers he turned over the pages -of an elaborately illustrated edition of Manon Lescaut that he had -found in one of the book-cases. The formal monotonous ticking of the -Louis Quatorze clock annoyed him. Once or twice he thought of going -away. - -At last he heard a step outside, and the door opened. "How late you -are, Harry!" he murmured. - -"I am afraid it is not Harry, Mr. Gray," answered a shrill voice. - -He glanced quickly round and rose to his feet. "I beg your pardon. I -thought--" - -"You thought it was my husband. It is only his wife. You must let me -introduce myself. I know you quite well by your photographs. I think -my husband has got seventeen of them." - -"Not seventeen, Lady Henry?" - -"Well, eighteen, then. And I saw you with him the other night at the -opera." She laughed nervously as she spoke, and watched him with her -vague forget-me-not eyes. She was a curious woman, whose dresses -always looked as if they had been designed in a rage and put on in a -tempest. She was usually in love with somebody, and, as her passion -was never returned, she had kept all her illusions. She tried to look -picturesque, but only succeeded in being untidy. Her name was -Victoria, and she had a perfect mania for going to church. - -"That was at Lohengrin, Lady Henry, I think?" - -"Yes; it was at dear Lohengrin. I like Wagner's music better than -anybody's. It is so loud that one can talk the whole time without other -people hearing what one says. That is a great advantage, don't you -think so, Mr. Gray?" - -The same nervous staccato laugh broke from her thin lips, and her -fingers began to play with a long tortoise-shell paper-knife. - -Dorian smiled and shook his head: "I am afraid I don't think so, Lady -Henry. I never talk during music--at least, during good music. If one -hears bad music, it is one's duty to drown it in conversation." - -"Ah! that is one of Harry's views, isn't it, Mr. Gray? I always hear -Harry's views from his friends. It is the only way I get to know of -them. But you must not think I don't like good music. I adore it, but -I am afraid of it. It makes me too romantic. I have simply worshipped -pianists--two at a time, sometimes, Harry tells me. I don't know what -it is about them. Perhaps it is that they are foreigners. They all -are, ain't they? Even those that are born in England become foreigners -after a time, don't they? It is so clever of them, and such a -compliment to art. Makes it quite cosmopolitan, doesn't it? You have -never been to any of my parties, have you, Mr. Gray? You must come. I -can't afford orchids, but I spare no expense in foreigners. They make -one's rooms look so picturesque. But here is Harry! Harry, I came in -to look for you, to ask you something--I forget what it was--and I -found Mr. Gray here. We have had such a pleasant chat about music. We -have quite the same ideas. No; I think our ideas are quite different. -But he has been most pleasant. I am so glad I've seen him." - -"I am charmed, my love, quite charmed," said Lord Henry, elevating his -dark, crescent-shaped eyebrows and looking at them both with an amused -smile. "So sorry I am late, Dorian. I went to look after a piece of -old brocade in Wardour Street and had to bargain for hours for it. -Nowadays people know the price of everything and the value of nothing." - -"I am afraid I must be going," exclaimed Lady Henry, breaking an -awkward silence with her silly sudden laugh. "I have promised to drive -with the duchess. Good-bye, Mr. Gray. Good-bye, Harry. You are -dining out, I suppose? So am I. Perhaps I shall see you at Lady -Thornbury's." - -"I dare say, my dear," said Lord Henry, shutting the door behind her -as, looking like a bird of paradise that had been out all night in the -rain, she flitted out of the room, leaving a faint odour of -frangipanni. Then he lit a cigarette and flung himself down on the -sofa. - -"Never marry a woman with straw-coloured hair, Dorian," he said after a -few puffs. - -"Why, Harry?" - -"Because they are so sentimental." - -"But I like sentimental people." - -"Never marry at all, Dorian. Men marry because they are tired; women, -because they are curious: both are disappointed." - -"I don't think I am likely to marry, Harry. I am too much in love. -That is one of your aphorisms. I am putting it into practice, as I do -everything that you say." - -"Who are you in love with?" asked Lord Henry after a pause. - -"With an actress," said Dorian Gray, blushing. - -Lord Henry shrugged his shoulders. "That is a rather commonplace -_debut_." - -"You would not say so if you saw her, Harry." - -"Who is she?" - -"Her name is Sibyl Vane." - -"Never heard of her." - -"No one has. People will some day, however. She is a genius." - -"My dear boy, no woman is a genius. Women are a decorative sex. They -never have anything to say, but they say it charmingly. Women -represent the triumph of matter over mind, just as men represent the -triumph of mind over morals." - -"Harry, how can you?" - -"My dear Dorian, it is quite true. I am analysing women at present, so -I ought to know. The subject is not so abstruse as I thought it was. -I find that, ultimately, there are only two kinds of women, the plain -and the coloured. The plain women are very useful. If you want to -gain a reputation for respectability, you have merely to take them down -to supper. The other women are very charming. They commit one -mistake, however. They paint in order to try and look young. Our -grandmothers painted in order to try and talk brilliantly. _Rouge_ and -_esprit_ used to go together. That is all over now. As long as a woman -can look ten years younger than her own daughter, she is perfectly -satisfied. As for conversation, there are only five women in London -worth talking to, and two of these can't be admitted into decent -society. However, tell me about your genius. How long have you known -her?" - -"Ah! Harry, your views terrify me." - -"Never mind that. How long have you known her?" - -"About three weeks." - -"And where did you come across her?" - -"I will tell you, Harry, but you mustn't be unsympathetic about it. -After all, it never would have happened if I had not met you. You -filled me with a wild desire to know everything about life. For days -after I met you, something seemed to throb in my veins. As I lounged -in the park, or strolled down Piccadilly, I used to look at every one -who passed me and wonder, with a mad curiosity, what sort of lives they -led. Some of them fascinated me. Others filled me with terror. There -was an exquisite poison in the air. I had a passion for sensations.... -Well, one evening about seven o'clock, I determined to go out in search -of some adventure. I felt that this grey monstrous London of ours, -with its myriads of people, its sordid sinners, and its splendid sins, -as you once phrased it, must have something in store for me. I fancied -a thousand things. The mere danger gave me a sense of delight. I -remembered what you had said to me on that wonderful evening when we -first dined together, about the search for beauty being the real secret -of life. I don't know what I expected, but I went out and wandered -eastward, soon losing my way in a labyrinth of grimy streets and black -grassless squares. About half-past eight I passed by an absurd little -theatre, with great flaring gas-jets and gaudy play-bills. A hideous -Jew, in the most amazing waistcoat I ever beheld in my life, was -standing at the entrance, smoking a vile cigar. He had greasy -ringlets, and an enormous diamond blazed in the centre of a soiled -shirt. 'Have a box, my Lord?' he said, when he saw me, and he took off -his hat with an air of gorgeous servility. There was something about -him, Harry, that amused me. He was such a monster. You will laugh at -me, I know, but I really went in and paid a whole guinea for the -stage-box. To the present day I can't make out why I did so; and yet if -I hadn't--my dear Harry, if I hadn't--I should have missed the greatest -romance of my life. I see you are laughing. It is horrid of you!" - -"I am not laughing, Dorian; at least I am not laughing at you. But you -should not say the greatest romance of your life. You should say the -first romance of your life. You will always be loved, and you will -always be in love with love. A _grande passion_ is the privilege of -people who have nothing to do. That is the one use of the idle classes -of a country. Don't be afraid. There are exquisite things in store -for you. This is merely the beginning." - -"Do you think my nature so shallow?" cried Dorian Gray angrily. - -"No; I think your nature so deep." - -"How do you mean?" - -"My dear boy, the people who love only once in their lives are really -the shallow people. What they call their loyalty, and their fidelity, -I call either the lethargy of custom or their lack of imagination. -Faithfulness is to the emotional life what consistency is to the life -of the intellect--simply a confession of failure. Faithfulness! I -must analyse it some day. The passion for property is in it. There -are many things that we would throw away if we were not afraid that -others might pick them up. But I don't want to interrupt you. Go on -with your story." - -"Well, I found myself seated in a horrid little private box, with a -vulgar drop-scene staring me in the face. I looked out from behind the -curtain and surveyed the house. It was a tawdry affair, all Cupids and -cornucopias, like a third-rate wedding-cake. The gallery and pit were -fairly full, but the two rows of dingy stalls were quite empty, and -there was hardly a person in what I suppose they called the -dress-circle. Women went about with oranges and ginger-beer, and there -was a terrible consumption of nuts going on." - -"It must have been just like the palmy days of the British drama." - -"Just like, I should fancy, and very depressing. I began to wonder -what on earth I should do when I caught sight of the play-bill. What -do you think the play was, Harry?" - -"I should think 'The Idiot Boy', or 'Dumb but Innocent'. Our fathers -used to like that sort of piece, I believe. The longer I live, Dorian, -the more keenly I feel that whatever was good enough for our fathers is -not good enough for us. In art, as in politics, _les grandperes ont -toujours tort_." - -"This play was good enough for us, Harry. It was Romeo and Juliet. I -must admit that I was rather annoyed at the idea of seeing Shakespeare -done in such a wretched hole of a place. Still, I felt interested, in -a sort of way. At any rate, I determined to wait for the first act. -There was a dreadful orchestra, presided over by a young Hebrew who sat -at a cracked piano, that nearly drove me away, but at last the -drop-scene was drawn up and the play began. Romeo was a stout elderly -gentleman, with corked eyebrows, a husky tragedy voice, and a figure -like a beer-barrel. Mercutio was almost as bad. He was played by the -low-comedian, who had introduced gags of his own and was on most -friendly terms with the pit. They were both as grotesque as the -scenery, and that looked as if it had come out of a country-booth. But -Juliet! Harry, imagine a girl, hardly seventeen years of age, with a -little, flowerlike face, a small Greek head with plaited coils of -dark-brown hair, eyes that were violet wells of passion, lips that were -like the petals of a rose. She was the loveliest thing I had ever seen -in my life. You said to me once that pathos left you unmoved, but that -beauty, mere beauty, could fill your eyes with tears. I tell you, -Harry, I could hardly see this girl for the mist of tears that came -across me. And her voice--I never heard such a voice. It was very low -at first, with deep mellow notes that seemed to fall singly upon one's -ear. Then it became a little louder, and sounded like a flute or a -distant hautboy. In the garden-scene it had all the tremulous ecstasy -that one hears just before dawn when nightingales are singing. There -were moments, later on, when it had the wild passion of violins. You -know how a voice can stir one. Your voice and the voice of Sibyl Vane -are two things that I shall never forget. When I close my eyes, I hear -them, and each of them says something different. I don't know which to -follow. Why should I not love her? Harry, I do love her. She is -everything to me in life. Night after night I go to see her play. One -evening she is Rosalind, and the next evening she is Imogen. I have -seen her die in the gloom of an Italian tomb, sucking the poison from -her lover's lips. I have watched her wandering through the forest of -Arden, disguised as a pretty boy in hose and doublet and dainty cap. -She has been mad, and has come into the presence of a guilty king, and -given him rue to wear and bitter herbs to taste of. She has been -innocent, and the black hands of jealousy have crushed her reedlike -throat. I have seen her in every age and in every costume. Ordinary -women never appeal to one's imagination. They are limited to their -century. No glamour ever transfigures them. One knows their minds as -easily as one knows their bonnets. One can always find them. There is -no mystery in any of them. They ride in the park in the morning and -chatter at tea-parties in the afternoon. They have their stereotyped -smile and their fashionable manner. They are quite obvious. But an -actress! How different an actress is! Harry! why didn't you tell me -that the only thing worth loving is an actress?" - -"Because I have loved so many of them, Dorian." - -"Oh, yes, horrid people with dyed hair and painted faces." - -"Don't run down dyed hair and painted faces. There is an extraordinary -charm in them, sometimes," said Lord Henry. - -"I wish now I had not told you about Sibyl Vane." - -"You could not have helped telling me, Dorian. All through your life -you will tell me everything you do." - -"Yes, Harry, I believe that is true. I cannot help telling you things. -You have a curious influence over me. If I ever did a crime, I would -come and confess it to you. You would understand me." - -"People like you--the wilful sunbeams of life--don't commit crimes, -Dorian. But I am much obliged for the compliment, all the same. And -now tell me--reach me the matches, like a good boy--thanks--what are -your actual relations with Sibyl Vane?" - -Dorian Gray leaped to his feet, with flushed cheeks and burning eyes. -"Harry! Sibyl Vane is sacred!" - -"It is only the sacred things that are worth touching, Dorian," said -Lord Henry, with a strange touch of pathos in his voice. "But why -should you be annoyed? I suppose she will belong to you some day. -When one is in love, one always begins by deceiving one's self, and one -always ends by deceiving others. That is what the world calls a -romance. You know her, at any rate, I suppose?" - -"Of course I know her. On the first night I was at the theatre, the -horrid old Jew came round to the box after the performance was over and -offered to take me behind the scenes and introduce me to her. I was -furious with him, and told him that Juliet had been dead for hundreds -of years and that her body was lying in a marble tomb in Verona. I -think, from his blank look of amazement, that he was under the -impression that I had taken too much champagne, or something." - -"I am not surprised." - -"Then he asked me if I wrote for any of the newspapers. I told him I -never even read them. He seemed terribly disappointed at that, and -confided to me that all the dramatic critics were in a conspiracy -against him, and that they were every one of them to be bought." - -"I should not wonder if he was quite right there. But, on the other -hand, judging from their appearance, most of them cannot be at all -expensive." - -"Well, he seemed to think they were beyond his means," laughed Dorian. -"By this time, however, the lights were being put out in the theatre, -and I had to go. He wanted me to try some cigars that he strongly -recommended. I declined. The next night, of course, I arrived at the -place again. When he saw me, he made me a low bow and assured me that -I was a munificent patron of art. He was a most offensive brute, -though he had an extraordinary passion for Shakespeare. He told me -once, with an air of pride, that his five bankruptcies were entirely -due to 'The Bard,' as he insisted on calling him. He seemed to think -it a distinction." - -"It was a distinction, my dear Dorian--a great distinction. Most -people become bankrupt through having invested too heavily in the prose -of life. To have ruined one's self over poetry is an honour. But when -did you first speak to Miss Sibyl Vane?" - -"The third night. She had been playing Rosalind. I could not help -going round. I had thrown her some flowers, and she had looked at -me--at least I fancied that she had. The old Jew was persistent. He -seemed determined to take me behind, so I consented. It was curious my -not wanting to know her, wasn't it?" - -"No; I don't think so." - -"My dear Harry, why?" - -"I will tell you some other time. Now I want to know about the girl." - -"Sibyl? Oh, she was so shy and so gentle. There is something of a -child about her. Her eyes opened wide in exquisite wonder when I told -her what I thought of her performance, and she seemed quite unconscious -of her power. I think we were both rather nervous. The old Jew stood -grinning at the doorway of the dusty greenroom, making elaborate -speeches about us both, while we stood looking at each other like -children. He would insist on calling me 'My Lord,' so I had to assure -Sibyl that I was not anything of the kind. She said quite simply to -me, 'You look more like a prince. I must call you Prince Charming.'" - -"Upon my word, Dorian, Miss Sibyl knows how to pay compliments." - -"You don't understand her, Harry. She regarded me merely as a person -in a play. She knows nothing of life. She lives with her mother, a -faded tired woman who played Lady Capulet in a sort of magenta -dressing-wrapper on the first night, and looks as if she had seen -better days." - -"I know that look. It depresses me," murmured Lord Henry, examining -his rings. - -"The Jew wanted to tell me her history, but I said it did not interest -me." - -"You were quite right. There is always something infinitely mean about -other people's tragedies." - -"Sibyl is the only thing I care about. What is it to me where she came -from? From her little head to her little feet, she is absolutely and -entirely divine. Every night of my life I go to see her act, and every -night she is more marvellous." - -"That is the reason, I suppose, that you never dine with me now. I -thought you must have some curious romance on hand. You have; but it -is not quite what I expected." - -"My dear Harry, we either lunch or sup together every day, and I have -been to the opera with you several times," said Dorian, opening his -blue eyes in wonder. - -"You always come dreadfully late." - -"Well, I can't help going to see Sibyl play," he cried, "even if it is -only for a single act. I get hungry for her presence; and when I think -of the wonderful soul that is hidden away in that little ivory body, I -am filled with awe." - -"You can dine with me to-night, Dorian, can't you?" - -He shook his head. "To-night she is Imogen," he answered, "and -to-morrow night she will be Juliet." - -"When is she Sibyl Vane?" - -"Never." - -"I congratulate you." - -"How horrid you are! She is all the great heroines of the world in -one. She is more than an individual. You laugh, but I tell you she -has genius. I love her, and I must make her love me. You, who know -all the secrets of life, tell me how to charm Sibyl Vane to love me! I -want to make Romeo jealous. I want the dead lovers of the world to -hear our laughter and grow sad. I want a breath of our passion to stir -their dust into consciousness, to wake their ashes into pain. My God, -Harry, how I worship her!" He was walking up and down the room as he -spoke. Hectic spots of red burned on his cheeks. He was terribly -excited. - -Lord Henry watched him with a subtle sense of pleasure. How different -he was now from the shy frightened boy he had met in Basil Hallward's -studio! His nature had developed like a flower, had borne blossoms of -scarlet flame. Out of its secret hiding-place had crept his soul, and -desire had come to meet it on the way. - -"And what do you propose to do?" said Lord Henry at last. - -"I want you and Basil to come with me some night and see her act. I -have not the slightest fear of the result. You are certain to -acknowledge her genius. Then we must get her out of the Jew's hands. -She is bound to him for three years--at least for two years and eight -months--from the present time. I shall have to pay him something, of -course. When all that is settled, I shall take a West End theatre and -bring her out properly. She will make the world as mad as she has made -me." - -"That would be impossible, my dear boy." - -"Yes, she will. She has not merely art, consummate art-instinct, in -her, but she has personality also; and you have often told me that it -is personalities, not principles, that move the age." - -"Well, what night shall we go?" - -"Let me see. To-day is Tuesday. Let us fix to-morrow. She plays -Juliet to-morrow." - -"All right. The Bristol at eight o'clock; and I will get Basil." - -"Not eight, Harry, please. Half-past six. We must be there before the -curtain rises. You must see her in the first act, where she meets -Romeo." - -"Half-past six! What an hour! It will be like having a meat-tea, or -reading an English novel. It must be seven. No gentleman dines before -seven. Shall you see Basil between this and then? Or shall I write to -him?" - -"Dear Basil! I have not laid eyes on him for a week. It is rather -horrid of me, as he has sent me my portrait in the most wonderful -frame, specially designed by himself, and, though I am a little jealous -of the picture for being a whole month younger than I am, I must admit -that I delight in it. Perhaps you had better write to him. I don't -want to see him alone. He says things that annoy me. He gives me good -advice." - -Lord Henry smiled. "People are very fond of giving away what they need -most themselves. It is what I call the depth of generosity." - -"Oh, Basil is the best of fellows, but he seems to me to be just a bit -of a Philistine. Since I have known you, Harry, I have discovered -that." - -"Basil, my dear boy, puts everything that is charming in him into his -work. The consequence is that he has nothing left for life but his -prejudices, his principles, and his common sense. The only artists I -have ever known who are personally delightful are bad artists. Good -artists exist simply in what they make, and consequently are perfectly -uninteresting in what they are. A great poet, a really great poet, is -the most unpoetical of all creatures. But inferior poets are -absolutely fascinating. The worse their rhymes are, the more -picturesque they look. The mere fact of having published a book of -second-rate sonnets makes a man quite irresistible. He lives the -poetry that he cannot write. The others write the poetry that they -dare not realize." - -"I wonder is that really so, Harry?" said Dorian Gray, putting some -perfume on his handkerchief out of a large, gold-topped bottle that -stood on the table. "It must be, if you say it. And now I am off. -Imogen is waiting for me. Don't forget about to-morrow. Good-bye." - -As he left the room, Lord Henry's heavy eyelids drooped, and he began -to think. Certainly few people had ever interested him so much as -Dorian Gray, and yet the lad's mad adoration of some one else caused -him not the slightest pang of annoyance or jealousy. He was pleased by -it. It made him a more interesting study. He had been always -enthralled by the methods of natural science, but the ordinary -subject-matter of that science had seemed to him trivial and of no -import. And so he had begun by vivisecting himself, as he had ended by -vivisecting others. Human life--that appeared to him the one thing -worth investigating. Compared to it there was nothing else of any -value. It was true that as one watched life in its curious crucible of -pain and pleasure, one could not wear over one's face a mask of glass, -nor keep the sulphurous fumes from troubling the brain and making the -imagination turbid with monstrous fancies and misshapen dreams. There -were poisons so subtle that to know their properties one had to sicken -of them. There were maladies so strange that one had to pass through -them if one sought to understand their nature. And, yet, what a great -reward one received! How wonderful the whole world became to one! To -note the curious hard logic of passion, and the emotional coloured life -of the intellect--to observe where they met, and where they separated, -at what point they were in unison, and at what point they were at -discord--there was a delight in that! What matter what the cost was? -One could never pay too high a price for any sensation. - -He was conscious--and the thought brought a gleam of pleasure into his -brown agate eyes--that it was through certain words of his, musical -words said with musical utterance, that Dorian Gray's soul had turned -to this white girl and bowed in worship before her. To a large extent -the lad was his own creation. He had made him premature. That was -something. Ordinary people waited till life disclosed to them its -secrets, but to the few, to the elect, the mysteries of life were -revealed before the veil was drawn away. Sometimes this was the effect -of art, and chiefly of the art of literature, which dealt immediately -with the passions and the intellect. But now and then a complex -personality took the place and assumed the office of art, was indeed, -in its way, a real work of art, life having its elaborate masterpieces, -just as poetry has, or sculpture, or painting. - -Yes, the lad was premature. He was gathering his harvest while it was -yet spring. The pulse and passion of youth were in him, but he was -becoming self-conscious. It was delightful to watch him. With his -beautiful face, and his beautiful soul, he was a thing to wonder at. -It was no matter how it all ended, or was destined to end. He was like -one of those gracious figures in a pageant or a play, whose joys seem -to be remote from one, but whose sorrows stir one's sense of beauty, -and whose wounds are like red roses. - -Soul and body, body and soul--how mysterious they were! There was -animalism in the soul, and the body had its moments of spirituality. -The senses could refine, and the intellect could degrade. Who could -say where the fleshly impulse ceased, or the psychical impulse began? -How shallow were the arbitrary definitions of ordinary psychologists! -And yet how difficult to decide between the claims of the various -schools! Was the soul a shadow seated in the house of sin? Or was the -body really in the soul, as Giordano Bruno thought? The separation of -spirit from matter was a mystery, and the union of spirit with matter -was a mystery also. - -He began to wonder whether we could ever make psychology so absolute a -science that each little spring of life would be revealed to us. As it -was, we always misunderstood ourselves and rarely understood others. -Experience was of no ethical value. It was merely the name men gave to -their mistakes. Moralists had, as a rule, regarded it as a mode of -warning, had claimed for it a certain ethical efficacy in the formation -of character, had praised it as something that taught us what to follow -and showed us what to avoid. But there was no motive power in -experience. It was as little of an active cause as conscience itself. -All that it really demonstrated was that our future would be the same -as our past, and that the sin we had done once, and with loathing, we -would do many times, and with joy. - -It was clear to him that the experimental method was the only method by -which one could arrive at any scientific analysis of the passions; and -certainly Dorian Gray was a subject made to his hand, and seemed to -promise rich and fruitful results. His sudden mad love for Sibyl Vane -was a psychological phenomenon of no small interest. There was no -doubt that curiosity had much to do with it, curiosity and the desire -for new experiences, yet it was not a simple, but rather a very complex -passion. What there was in it of the purely sensuous instinct of -boyhood had been transformed by the workings of the imagination, -changed into something that seemed to the lad himself to be remote from -sense, and was for that very reason all the more dangerous. It was the -passions about whose origin we deceived ourselves that tyrannized most -strongly over us. Our weakest motives were those of whose nature we -were conscious. It often happened that when we thought we were -experimenting on others we were really experimenting on ourselves. - -While Lord Henry sat dreaming on these things, a knock came to the -door, and his valet entered and reminded him it was time to dress for -dinner. He got up and looked out into the street. The sunset had -smitten into scarlet gold the upper windows of the houses opposite. -The panes glowed like plates of heated metal. The sky above was like a -faded rose. He thought of his friend's young fiery-coloured life and -wondered how it was all going to end. - -When he arrived home, about half-past twelve o'clock, he saw a telegram -lying on the hall table. He opened it and found it was from Dorian -Gray. It was to tell him that he was engaged to be married to Sibyl -Vane. - - - -CHAPTER 5 - -"Mother, Mother, I am so happy!" whispered the girl, burying her face -in the lap of the faded, tired-looking woman who, with back turned to -the shrill intrusive light, was sitting in the one arm-chair that their -dingy sitting-room contained. "I am so happy!" she repeated, "and you -must be happy, too!" - -Mrs. Vane winced and put her thin, bismuth-whitened hands on her -daughter's head. "Happy!" she echoed, "I am only happy, Sibyl, when I -see you act. You must not think of anything but your acting. Mr. -Isaacs has been very good to us, and we owe him money." - -The girl looked up and pouted. "Money, Mother?" she cried, "what does -money matter? Love is more than money." - -"Mr. Isaacs has advanced us fifty pounds to pay off our debts and to -get a proper outfit for James. You must not forget that, Sibyl. Fifty -pounds is a very large sum. Mr. Isaacs has been most considerate." - -"He is not a gentleman, Mother, and I hate the way he talks to me," -said the girl, rising to her feet and going over to the window. - -"I don't know how we could manage without him," answered the elder -woman querulously. - -Sibyl Vane tossed her head and laughed. "We don't want him any more, -Mother. Prince Charming rules life for us now." Then she paused. A -rose shook in her blood and shadowed her cheeks. Quick breath parted -the petals of her lips. They trembled. Some southern wind of passion -swept over her and stirred the dainty folds of her dress. "I love -him," she said simply. - -"Foolish child! foolish child!" was the parrot-phrase flung in answer. -The waving of crooked, false-jewelled fingers gave grotesqueness to the -words. - -The girl laughed again. The joy of a caged bird was in her voice. Her -eyes caught the melody and echoed it in radiance, then closed for a -moment, as though to hide their secret. When they opened, the mist of -a dream had passed across them. - -Thin-lipped wisdom spoke at her from the worn chair, hinted at -prudence, quoted from that book of cowardice whose author apes the name -of common sense. She did not listen. She was free in her prison of -passion. Her prince, Prince Charming, was with her. She had called on -memory to remake him. She had sent her soul to search for him, and it -had brought him back. His kiss burned again upon her mouth. Her -eyelids were warm with his breath. - -Then wisdom altered its method and spoke of espial and discovery. This -young man might be rich. If so, marriage should be thought of. -Against the shell of her ear broke the waves of worldly cunning. The -arrows of craft shot by her. She saw the thin lips moving, and smiled. - -Suddenly she felt the need to speak. The wordy silence troubled her. -"Mother, Mother," she cried, "why does he love me so much? I know why -I love him. I love him because he is like what love himself should be. -But what does he see in me? I am not worthy of him. And yet--why, I -cannot tell--though I feel so much beneath him, I don't feel humble. I -feel proud, terribly proud. Mother, did you love my father as I love -Prince Charming?" - -The elder woman grew pale beneath the coarse powder that daubed her -cheeks, and her dry lips twitched with a spasm of pain. Sybil rushed -to her, flung her arms round her neck, and kissed her. "Forgive me, -Mother. I know it pains you to talk about our father. But it only -pains you because you loved him so much. Don't look so sad. I am as -happy to-day as you were twenty years ago. Ah! let me be happy for -ever!" - -"My child, you are far too young to think of falling in love. Besides, -what do you know of this young man? You don't even know his name. The -whole thing is most inconvenient, and really, when James is going away -to Australia, and I have so much to think of, I must say that you -should have shown more consideration. However, as I said before, if he -is rich ..." - -"Ah! Mother, Mother, let me be happy!" - -Mrs. Vane glanced at her, and with one of those false theatrical -gestures that so often become a mode of second nature to a -stage-player, clasped her in her arms. At this moment, the door opened -and a young lad with rough brown hair came into the room. He was -thick-set of figure, and his hands and feet were large and somewhat -clumsy in movement. He was not so finely bred as his sister. One -would hardly have guessed the close relationship that existed between -them. Mrs. Vane fixed her eyes on him and intensified her smile. She -mentally elevated her son to the dignity of an audience. She felt sure -that the _tableau_ was interesting. - -"You might keep some of your kisses for me, Sibyl, I think," said the -lad with a good-natured grumble. - -"Ah! but you don't like being kissed, Jim," she cried. "You are a -dreadful old bear." And she ran across the room and hugged him. - -James Vane looked into his sister's face with tenderness. "I want you -to come out with me for a walk, Sibyl. I don't suppose I shall ever -see this horrid London again. I am sure I don't want to." - -"My son, don't say such dreadful things," murmured Mrs. Vane, taking up -a tawdry theatrical dress, with a sigh, and beginning to patch it. She -felt a little disappointed that he had not joined the group. It would -have increased the theatrical picturesqueness of the situation. - -"Why not, Mother? I mean it." - -"You pain me, my son. I trust you will return from Australia in a -position of affluence. I believe there is no society of any kind in -the Colonies--nothing that I would call society--so when you have made -your fortune, you must come back and assert yourself in London." - -"Society!" muttered the lad. "I don't want to know anything about -that. I should like to make some money to take you and Sibyl off the -stage. I hate it." - -"Oh, Jim!" said Sibyl, laughing, "how unkind of you! But are you -really going for a walk with me? That will be nice! I was afraid you -were going to say good-bye to some of your friends--to Tom Hardy, who -gave you that hideous pipe, or Ned Langton, who makes fun of you for -smoking it. It is very sweet of you to let me have your last -afternoon. Where shall we go? Let us go to the park." - -"I am too shabby," he answered, frowning. "Only swell people go to the -park." - -"Nonsense, Jim," she whispered, stroking the sleeve of his coat. - -He hesitated for a moment. "Very well," he said at last, "but don't be -too long dressing." She danced out of the door. One could hear her -singing as she ran upstairs. Her little feet pattered overhead. - -He walked up and down the room two or three times. Then he turned to -the still figure in the chair. "Mother, are my things ready?" he asked. - -"Quite ready, James," she answered, keeping her eyes on her work. For -some months past she had felt ill at ease when she was alone with this -rough stern son of hers. Her shallow secret nature was troubled when -their eyes met. She used to wonder if he suspected anything. The -silence, for he made no other observation, became intolerable to her. -She began to complain. Women defend themselves by attacking, just as -they attack by sudden and strange surrenders. "I hope you will be -contented, James, with your sea-faring life," she said. "You must -remember that it is your own choice. You might have entered a -solicitor's office. Solicitors are a very respectable class, and in -the country often dine with the best families." - -"I hate offices, and I hate clerks," he replied. "But you are quite -right. I have chosen my own life. All I say is, watch over Sibyl. -Don't let her come to any harm. Mother, you must watch over her." - -"James, you really talk very strangely. Of course I watch over Sibyl." - -"I hear a gentleman comes every night to the theatre and goes behind to -talk to her. Is that right? What about that?" - -"You are speaking about things you don't understand, James. In the -profession we are accustomed to receive a great deal of most gratifying -attention. I myself used to receive many bouquets at one time. That -was when acting was really understood. As for Sibyl, I do not know at -present whether her attachment is serious or not. But there is no -doubt that the young man in question is a perfect gentleman. He is -always most polite to me. Besides, he has the appearance of being -rich, and the flowers he sends are lovely." - -"You don't know his name, though," said the lad harshly. - -"No," answered his mother with a placid expression in her face. "He -has not yet revealed his real name. I think it is quite romantic of -him. He is probably a member of the aristocracy." - -James Vane bit his lip. "Watch over Sibyl, Mother," he cried, "watch -over her." - -"My son, you distress me very much. Sibyl is always under my special -care. Of course, if this gentleman is wealthy, there is no reason why -she should not contract an alliance with him. I trust he is one of the -aristocracy. He has all the appearance of it, I must say. It might be -a most brilliant marriage for Sibyl. They would make a charming -couple. His good looks are really quite remarkable; everybody notices -them." - -The lad muttered something to himself and drummed on the window-pane -with his coarse fingers. He had just turned round to say something -when the door opened and Sibyl ran in. - -"How serious you both are!" she cried. "What is the matter?" - -"Nothing," he answered. "I suppose one must be serious sometimes. -Good-bye, Mother; I will have my dinner at five o'clock. Everything is -packed, except my shirts, so you need not trouble." - -"Good-bye, my son," she answered with a bow of strained stateliness. - -She was extremely annoyed at the tone he had adopted with her, and -there was something in his look that had made her feel afraid. - -"Kiss me, Mother," said the girl. Her flowerlike lips touched the -withered cheek and warmed its frost. - -"My child! my child!" cried Mrs. Vane, looking up to the ceiling in -search of an imaginary gallery. - -"Come, Sibyl," said her brother impatiently. He hated his mother's -affectations. - -They went out into the flickering, wind-blown sunlight and strolled -down the dreary Euston Road. The passersby glanced in wonder at the -sullen heavy youth who, in coarse, ill-fitting clothes, was in the -company of such a graceful, refined-looking girl. He was like a common -gardener walking with a rose. - -Jim frowned from time to time when he caught the inquisitive glance of -some stranger. He had that dislike of being stared at, which comes on -geniuses late in life and never leaves the commonplace. Sibyl, -however, was quite unconscious of the effect she was producing. Her -love was trembling in laughter on her lips. She was thinking of Prince -Charming, and, that she might think of him all the more, she did not -talk of him, but prattled on about the ship in which Jim was going to -sail, about the gold he was certain to find, about the wonderful -heiress whose life he was to save from the wicked, red-shirted -bushrangers. For he was not to remain a sailor, or a supercargo, or -whatever he was going to be. Oh, no! A sailor's existence was -dreadful. Fancy being cooped up in a horrid ship, with the hoarse, -hump-backed waves trying to get in, and a black wind blowing the masts -down and tearing the sails into long screaming ribands! He was to -leave the vessel at Melbourne, bid a polite good-bye to the captain, -and go off at once to the gold-fields. Before a week was over he was to -come across a large nugget of pure gold, the largest nugget that had -ever been discovered, and bring it down to the coast in a waggon -guarded by six mounted policemen. The bushrangers were to attack them -three times, and be defeated with immense slaughter. Or, no. He was -not to go to the gold-fields at all. They were horrid places, where -men got intoxicated, and shot each other in bar-rooms, and used bad -language. He was to be a nice sheep-farmer, and one evening, as he was -riding home, he was to see the beautiful heiress being carried off by a -robber on a black horse, and give chase, and rescue her. Of course, -she would fall in love with him, and he with her, and they would get -married, and come home, and live in an immense house in London. Yes, -there were delightful things in store for him. But he must be very -good, and not lose his temper, or spend his money foolishly. She was -only a year older than he was, but she knew so much more of life. He -must be sure, also, to write to her by every mail, and to say his -prayers each night before he went to sleep. God was very good, and -would watch over him. She would pray for him, too, and in a few years -he would come back quite rich and happy. - -The lad listened sulkily to her and made no answer. He was heart-sick -at leaving home. - -Yet it was not this alone that made him gloomy and morose. -Inexperienced though he was, he had still a strong sense of the danger -of Sibyl's position. This young dandy who was making love to her could -mean her no good. He was a gentleman, and he hated him for that, hated -him through some curious race-instinct for which he could not account, -and which for that reason was all the more dominant within him. He was -conscious also of the shallowness and vanity of his mother's nature, -and in that saw infinite peril for Sibyl and Sibyl's happiness. -Children begin by loving their parents; as they grow older they judge -them; sometimes they forgive them. - -His mother! He had something on his mind to ask of her, something that -he had brooded on for many months of silence. A chance phrase that he -had heard at the theatre, a whispered sneer that had reached his ears -one night as he waited at the stage-door, had set loose a train of -horrible thoughts. He remembered it as if it had been the lash of a -hunting-crop across his face. His brows knit together into a wedge-like -furrow, and with a twitch of pain he bit his underlip. - -"You are not listening to a word I am saying, Jim," cried Sibyl, "and I -am making the most delightful plans for your future. Do say something." - -"What do you want me to say?" - -"Oh! that you will be a good boy and not forget us," she answered, -smiling at him. - -He shrugged his shoulders. "You are more likely to forget me than I am -to forget you, Sibyl." - -She flushed. "What do you mean, Jim?" she asked. - -"You have a new friend, I hear. Who is he? Why have you not told me -about him? He means you no good." - -"Stop, Jim!" she exclaimed. "You must not say anything against him. I -love him." - -"Why, you don't even know his name," answered the lad. "Who is he? I -have a right to know." - -"He is called Prince Charming. Don't you like the name. Oh! you silly -boy! you should never forget it. If you only saw him, you would think -him the most wonderful person in the world. Some day you will meet -him--when you come back from Australia. You will like him so much. -Everybody likes him, and I ... love him. I wish you could come to the -theatre to-night. He is going to be there, and I am to play Juliet. -Oh! how I shall play it! Fancy, Jim, to be in love and play Juliet! -To have him sitting there! To play for his delight! I am afraid I may -frighten the company, frighten or enthrall them. To be in love is to -surpass one's self. Poor dreadful Mr. Isaacs will be shouting 'genius' -to his loafers at the bar. He has preached me as a dogma; to-night he -will announce me as a revelation. I feel it. And it is all his, his -only, Prince Charming, my wonderful lover, my god of graces. But I am -poor beside him. Poor? What does that matter? When poverty creeps in -at the door, love flies in through the window. Our proverbs want -rewriting. They were made in winter, and it is summer now; spring-time -for me, I think, a very dance of blossoms in blue skies." - -"He is a gentleman," said the lad sullenly. - -"A prince!" she cried musically. "What more do you want?" - -"He wants to enslave you." - -"I shudder at the thought of being free." - -"I want you to beware of him." - -"To see him is to worship him; to know him is to trust him." - -"Sibyl, you are mad about him." - -She laughed and took his arm. "You dear old Jim, you talk as if you -were a hundred. Some day you will be in love yourself. Then you will -know what it is. Don't look so sulky. Surely you should be glad to -think that, though you are going away, you leave me happier than I have -ever been before. Life has been hard for us both, terribly hard and -difficult. But it will be different now. You are going to a new -world, and I have found one. Here are two chairs; let us sit down and -see the smart people go by." - -They took their seats amidst a crowd of watchers. The tulip-beds -across the road flamed like throbbing rings of fire. A white -dust--tremulous cloud of orris-root it seemed--hung in the panting air. -The brightly coloured parasols danced and dipped like monstrous -butterflies. - -She made her brother talk of himself, his hopes, his prospects. He -spoke slowly and with effort. They passed words to each other as -players at a game pass counters. Sibyl felt oppressed. She could not -communicate her joy. A faint smile curving that sullen mouth was all -the echo she could win. After some time she became silent. Suddenly -she caught a glimpse of golden hair and laughing lips, and in an open -carriage with two ladies Dorian Gray drove past. - -She started to her feet. "There he is!" she cried. - -"Who?" said Jim Vane. - -"Prince Charming," she answered, looking after the victoria. - -He jumped up and seized her roughly by the arm. "Show him to me. -Which is he? Point him out. I must see him!" he exclaimed; but at -that moment the Duke of Berwick's four-in-hand came between, and when -it had left the space clear, the carriage had swept out of the park. - -"He is gone," murmured Sibyl sadly. "I wish you had seen him." - -"I wish I had, for as sure as there is a God in heaven, if he ever does -you any wrong, I shall kill him." - -She looked at him in horror. He repeated his words. They cut the air -like a dagger. The people round began to gape. A lady standing close -to her tittered. - -"Come away, Jim; come away," she whispered. He followed her doggedly -as she passed through the crowd. He felt glad at what he had said. - -When they reached the Achilles Statue, she turned round. There was -pity in her eyes that became laughter on her lips. She shook her head -at him. "You are foolish, Jim, utterly foolish; a bad-tempered boy, -that is all. How can you say such horrible things? You don't know -what you are talking about. You are simply jealous and unkind. Ah! I -wish you would fall in love. Love makes people good, and what you said -was wicked." - -"I am sixteen," he answered, "and I know what I am about. Mother is no -help to you. She doesn't understand how to look after you. I wish now -that I was not going to Australia at all. I have a great mind to chuck -the whole thing up. I would, if my articles hadn't been signed." - -"Oh, don't be so serious, Jim. You are like one of the heroes of those -silly melodramas Mother used to be so fond of acting in. I am not -going to quarrel with you. I have seen him, and oh! to see him is -perfect happiness. We won't quarrel. I know you would never harm any -one I love, would you?" - -"Not as long as you love him, I suppose," was the sullen answer. - -"I shall love him for ever!" she cried. - -"And he?" - -"For ever, too!" - -"He had better." - -She shrank from him. Then she laughed and put her hand on his arm. He -was merely a boy. - -At the Marble Arch they hailed an omnibus, which left them close to -their shabby home in the Euston Road. It was after five o'clock, and -Sibyl had to lie down for a couple of hours before acting. Jim -insisted that she should do so. He said that he would sooner part with -her when their mother was not present. She would be sure to make a -scene, and he detested scenes of every kind. - -In Sybil's own room they parted. There was jealousy in the lad's -heart, and a fierce murderous hatred of the stranger who, as it seemed -to him, had come between them. Yet, when her arms were flung round his -neck, and her fingers strayed through his hair, he softened and kissed -her with real affection. There were tears in his eyes as he went -downstairs. - -His mother was waiting for him below. She grumbled at his -unpunctuality, as he entered. He made no answer, but sat down to his -meagre meal. The flies buzzed round the table and crawled over the -stained cloth. Through the rumble of omnibuses, and the clatter of -street-cabs, he could hear the droning voice devouring each minute that -was left to him. - -After some time, he thrust away his plate and put his head in his -hands. He felt that he had a right to know. It should have been told -to him before, if it was as he suspected. Leaden with fear, his mother -watched him. Words dropped mechanically from her lips. A tattered -lace handkerchief twitched in her fingers. When the clock struck six, -he got up and went to the door. Then he turned back and looked at her. -Their eyes met. In hers he saw a wild appeal for mercy. It enraged -him. - -"Mother, I have something to ask you," he said. Her eyes wandered -vaguely about the room. She made no answer. "Tell me the truth. I -have a right to know. Were you married to my father?" - -She heaved a deep sigh. It was a sigh of relief. The terrible moment, -the moment that night and day, for weeks and months, she had dreaded, -had come at last, and yet she felt no terror. Indeed, in some measure -it was a disappointment to her. The vulgar directness of the question -called for a direct answer. The situation had not been gradually led -up to. It was crude. It reminded her of a bad rehearsal. - -"No," she answered, wondering at the harsh simplicity of life. - -"My father was a scoundrel then!" cried the lad, clenching his fists. - -She shook her head. "I knew he was not free. We loved each other very -much. If he had lived, he would have made provision for us. Don't -speak against him, my son. He was your father, and a gentleman. -Indeed, he was highly connected." - -An oath broke from his lips. "I don't care for myself," he exclaimed, -"but don't let Sibyl.... It is a gentleman, isn't it, who is in love -with her, or says he is? Highly connected, too, I suppose." - -For a moment a hideous sense of humiliation came over the woman. Her -head drooped. She wiped her eyes with shaking hands. "Sibyl has a -mother," she murmured; "I had none." - -The lad was touched. He went towards her, and stooping down, he kissed -her. "I am sorry if I have pained you by asking about my father," he -said, "but I could not help it. I must go now. Good-bye. Don't forget -that you will have only one child now to look after, and believe me -that if this man wrongs my sister, I will find out who he is, track him -down, and kill him like a dog. I swear it." - -The exaggerated folly of the threat, the passionate gesture that -accompanied it, the mad melodramatic words, made life seem more vivid -to her. She was familiar with the atmosphere. She breathed more -freely, and for the first time for many months she really admired her -son. She would have liked to have continued the scene on the same -emotional scale, but he cut her short. Trunks had to be carried down -and mufflers looked for. The lodging-house drudge bustled in and out. -There was the bargaining with the cabman. The moment was lost in -vulgar details. It was with a renewed feeling of disappointment that -she waved the tattered lace handkerchief from the window, as her son -drove away. She was conscious that a great opportunity had been -wasted. She consoled herself by telling Sibyl how desolate she felt -her life would be, now that she had only one child to look after. She -remembered the phrase. It had pleased her. Of the threat she said -nothing. It was vividly and dramatically expressed. She felt that -they would all laugh at it some day. - - - -CHAPTER 6 - -"I suppose you have heard the news, Basil?" said Lord Henry that -evening as Hallward was shown into a little private room at the Bristol -where dinner had been laid for three. - -"No, Harry," answered the artist, giving his hat and coat to the bowing -waiter. "What is it? Nothing about politics, I hope! They don't -interest me. There is hardly a single person in the House of Commons -worth painting, though many of them would be the better for a little -whitewashing." - -"Dorian Gray is engaged to be married," said Lord Henry, watching him -as he spoke. - -Hallward started and then frowned. "Dorian engaged to be married!" he -cried. "Impossible!" - -"It is perfectly true." - -"To whom?" - -"To some little actress or other." - -"I can't believe it. Dorian is far too sensible." - -"Dorian is far too wise not to do foolish things now and then, my dear -Basil." - -"Marriage is hardly a thing that one can do now and then, Harry." - -"Except in America," rejoined Lord Henry languidly. "But I didn't say -he was married. I said he was engaged to be married. There is a great -difference. I have a distinct remembrance of being married, but I have -no recollection at all of being engaged. I am inclined to think that I -never was engaged." - -"But think of Dorian's birth, and position, and wealth. It would be -absurd for him to marry so much beneath him." - -"If you want to make him marry this girl, tell him that, Basil. He is -sure to do it, then. Whenever a man does a thoroughly stupid thing, it -is always from the noblest motives." - -"I hope the girl is good, Harry. I don't want to see Dorian tied to -some vile creature, who might degrade his nature and ruin his -intellect." - -"Oh, she is better than good--she is beautiful," murmured Lord Henry, -sipping a glass of vermouth and orange-bitters. "Dorian says she is -beautiful, and he is not often wrong about things of that kind. Your -portrait of him has quickened his appreciation of the personal -appearance of other people. It has had that excellent effect, amongst -others. We are to see her to-night, if that boy doesn't forget his -appointment." - -"Are you serious?" - -"Quite serious, Basil. I should be miserable if I thought I should -ever be more serious than I am at the present moment." - -"But do you approve of it, Harry?" asked the painter, walking up and -down the room and biting his lip. "You can't approve of it, possibly. -It is some silly infatuation." - -"I never approve, or disapprove, of anything now. It is an absurd -attitude to take towards life. We are not sent into the world to air -our moral prejudices. I never take any notice of what common people -say, and I never interfere with what charming people do. If a -personality fascinates me, whatever mode of expression that personality -selects is absolutely delightful to me. Dorian Gray falls in love with -a beautiful girl who acts Juliet, and proposes to marry her. Why not? -If he wedded Messalina, he would be none the less interesting. You -know I am not a champion of marriage. The real drawback to marriage is -that it makes one unselfish. And unselfish people are colourless. -They lack individuality. Still, there are certain temperaments that -marriage makes more complex. They retain their egotism, and add to it -many other egos. They are forced to have more than one life. They -become more highly organized, and to be highly organized is, I should -fancy, the object of man's existence. Besides, every experience is of -value, and whatever one may say against marriage, it is certainly an -experience. I hope that Dorian Gray will make this girl his wife, -passionately adore her for six months, and then suddenly become -fascinated by some one else. He would be a wonderful study." - -"You don't mean a single word of all that, Harry; you know you don't. -If Dorian Gray's life were spoiled, no one would be sorrier than -yourself. You are much better than you pretend to be." - -Lord Henry laughed. "The reason we all like to think so well of others -is that we are all afraid for ourselves. The basis of optimism is -sheer terror. We think that we are generous because we credit our -neighbour with the possession of those virtues that are likely to be a -benefit to us. We praise the banker that we may overdraw our account, -and find good qualities in the highwayman in the hope that he may spare -our pockets. I mean everything that I have said. I have the greatest -contempt for optimism. As for a spoiled life, no life is spoiled but -one whose growth is arrested. If you want to mar a nature, you have -merely to reform it. As for marriage, of course that would be silly, -but there are other and more interesting bonds between men and women. -I will certainly encourage them. They have the charm of being -fashionable. But here is Dorian himself. He will tell you more than I -can." - -"My dear Harry, my dear Basil, you must both congratulate me!" said the -lad, throwing off his evening cape with its satin-lined wings and -shaking each of his friends by the hand in turn. "I have never been so -happy. Of course, it is sudden--all really delightful things are. And -yet it seems to me to be the one thing I have been looking for all my -life." He was flushed with excitement and pleasure, and looked -extraordinarily handsome. - -"I hope you will always be very happy, Dorian," said Hallward, "but I -don't quite forgive you for not having let me know of your engagement. -You let Harry know." - -"And I don't forgive you for being late for dinner," broke in Lord -Henry, putting his hand on the lad's shoulder and smiling as he spoke. -"Come, let us sit down and try what the new _chef_ here is like, and then -you will tell us how it all came about." - -"There is really not much to tell," cried Dorian as they took their -seats at the small round table. "What happened was simply this. After -I left you yesterday evening, Harry, I dressed, had some dinner at that -little Italian restaurant in Rupert Street you introduced me to, and -went down at eight o'clock to the theatre. Sibyl was playing Rosalind. -Of course, the scenery was dreadful and the Orlando absurd. But Sibyl! -You should have seen her! When she came on in her boy's clothes, she -was perfectly wonderful. She wore a moss-coloured velvet jerkin with -cinnamon sleeves, slim, brown, cross-gartered hose, a dainty little -green cap with a hawk's feather caught in a jewel, and a hooded cloak -lined with dull red. She had never seemed to me more exquisite. She -had all the delicate grace of that Tanagra figurine that you have in -your studio, Basil. Her hair clustered round her face like dark leaves -round a pale rose. As for her acting--well, you shall see her -to-night. She is simply a born artist. I sat in the dingy box -absolutely enthralled. I forgot that I was in London and in the -nineteenth century. I was away with my love in a forest that no man -had ever seen. After the performance was over, I went behind and spoke -to her. As we were sitting together, suddenly there came into her eyes -a look that I had never seen there before. My lips moved towards hers. -We kissed each other. I can't describe to you what I felt at that -moment. It seemed to me that all my life had been narrowed to one -perfect point of rose-coloured joy. She trembled all over and shook -like a white narcissus. Then she flung herself on her knees and kissed -my hands. I feel that I should not tell you all this, but I can't help -it. Of course, our engagement is a dead secret. She has not even told -her own mother. I don't know what my guardians will say. Lord Radley -is sure to be furious. I don't care. I shall be of age in less than a -year, and then I can do what I like. I have been right, Basil, haven't -I, to take my love out of poetry and to find my wife in Shakespeare's -plays? Lips that Shakespeare taught to speak have whispered their -secret in my ear. I have had the arms of Rosalind around me, and -kissed Juliet on the mouth." - -"Yes, Dorian, I suppose you were right," said Hallward slowly. - -"Have you seen her to-day?" asked Lord Henry. - -Dorian Gray shook his head. "I left her in the forest of Arden; I -shall find her in an orchard in Verona." - -Lord Henry sipped his champagne in a meditative manner. "At what -particular point did you mention the word marriage, Dorian? And what -did she say in answer? Perhaps you forgot all about it." - -"My dear Harry, I did not treat it as a business transaction, and I did -not make any formal proposal. I told her that I loved her, and she -said she was not worthy to be my wife. Not worthy! Why, the whole -world is nothing to me compared with her." - -"Women are wonderfully practical," murmured Lord Henry, "much more -practical than we are. In situations of that kind we often forget to -say anything about marriage, and they always remind us." - -Hallward laid his hand upon his arm. "Don't, Harry. You have annoyed -Dorian. He is not like other men. He would never bring misery upon -any one. His nature is too fine for that." - -Lord Henry looked across the table. "Dorian is never annoyed with me," -he answered. "I asked the question for the best reason possible, for -the only reason, indeed, that excuses one for asking any -question--simple curiosity. I have a theory that it is always the -women who propose to us, and not we who propose to the women. Except, -of course, in middle-class life. But then the middle classes are not -modern." - -Dorian Gray laughed, and tossed his head. "You are quite incorrigible, -Harry; but I don't mind. It is impossible to be angry with you. When -you see Sibyl Vane, you will feel that the man who could wrong her -would be a beast, a beast without a heart. I cannot understand how any -one can wish to shame the thing he loves. I love Sibyl Vane. I want -to place her on a pedestal of gold and to see the world worship the -woman who is mine. What is marriage? An irrevocable vow. You mock at -it for that. Ah! don't mock. It is an irrevocable vow that I want to -take. Her trust makes me faithful, her belief makes me good. When I -am with her, I regret all that you have taught me. I become different -from what you have known me to be. I am changed, and the mere touch of -Sibyl Vane's hand makes me forget you and all your wrong, fascinating, -poisonous, delightful theories." - -"And those are ...?" asked Lord Henry, helping himself to some salad. - -"Oh, your theories about life, your theories about love, your theories -about pleasure. All your theories, in fact, Harry." - -"Pleasure is the only thing worth having a theory about," he answered -in his slow melodious voice. "But I am afraid I cannot claim my theory -as my own. It belongs to Nature, not to me. Pleasure is Nature's -test, her sign of approval. When we are happy, we are always good, but -when we are good, we are not always happy." - -"Ah! but what do you mean by good?" cried Basil Hallward. - -"Yes," echoed Dorian, leaning back in his chair and looking at Lord -Henry over the heavy clusters of purple-lipped irises that stood in the -centre of the table, "what do you mean by good, Harry?" - -"To be good is to be in harmony with one's self," he replied, touching -the thin stem of his glass with his pale, fine-pointed fingers. -"Discord is to be forced to be in harmony with others. One's own -life--that is the important thing. As for the lives of one's -neighbours, if one wishes to be a prig or a Puritan, one can flaunt -one's moral views about them, but they are not one's concern. Besides, -individualism has really the higher aim. Modern morality consists in -accepting the standard of one's age. I consider that for any man of -culture to accept the standard of his age is a form of the grossest -immorality." - -"But, surely, if one lives merely for one's self, Harry, one pays a -terrible price for doing so?" suggested the painter. - -"Yes, we are overcharged for everything nowadays. I should fancy that -the real tragedy of the poor is that they can afford nothing but -self-denial. Beautiful sins, like beautiful things, are the privilege -of the rich." - -"One has to pay in other ways but money." - -"What sort of ways, Basil?" - -"Oh! I should fancy in remorse, in suffering, in ... well, in the -consciousness of degradation." - -Lord Henry shrugged his shoulders. "My dear fellow, mediaeval art is -charming, but mediaeval emotions are out of date. One can use them in -fiction, of course. But then the only things that one can use in -fiction are the things that one has ceased to use in fact. Believe me, -no civilized man ever regrets a pleasure, and no uncivilized man ever -knows what a pleasure is." - -"I know what pleasure is," cried Dorian Gray. "It is to adore some -one." - -"That is certainly better than being adored," he answered, toying with -some fruits. "Being adored is a nuisance. Women treat us just as -humanity treats its gods. They worship us, and are always bothering us -to do something for them." - -"I should have said that whatever they ask for they had first given to -us," murmured the lad gravely. "They create love in our natures. They -have a right to demand it back." - -"That is quite true, Dorian," cried Hallward. - -"Nothing is ever quite true," said Lord Henry. - -"This is," interrupted Dorian. "You must admit, Harry, that women give -to men the very gold of their lives." - -"Possibly," he sighed, "but they invariably want it back in such very -small change. That is the worry. Women, as some witty Frenchman once -put it, inspire us with the desire to do masterpieces and always -prevent us from carrying them out." - -"Harry, you are dreadful! I don't know why I like you so much." - -"You will always like me, Dorian," he replied. "Will you have some -coffee, you fellows? Waiter, bring coffee, and _fine-champagne_, and -some cigarettes. No, don't mind the cigarettes--I have some. Basil, I -can't allow you to smoke cigars. You must have a cigarette. A -cigarette is the perfect type of a perfect pleasure. It is exquisite, -and it leaves one unsatisfied. What more can one want? Yes, Dorian, -you will always be fond of me. I represent to you all the sins you -have never had the courage to commit." - -"What nonsense you talk, Harry!" cried the lad, taking a light from a -fire-breathing silver dragon that the waiter had placed on the table. -"Let us go down to the theatre. When Sibyl comes on the stage you will -have a new ideal of life. She will represent something to you that you -have never known." - -"I have known everything," said Lord Henry, with a tired look in his -eyes, "but I am always ready for a new emotion. I am afraid, however, -that, for me at any rate, there is no such thing. Still, your -wonderful girl may thrill me. I love acting. It is so much more real -than life. Let us go. Dorian, you will come with me. I am so sorry, -Basil, but there is only room for two in the brougham. You must follow -us in a hansom." - -They got up and put on their coats, sipping their coffee standing. The -painter was silent and preoccupied. There was a gloom over him. He -could not bear this marriage, and yet it seemed to him to be better -than many other things that might have happened. After a few minutes, -they all passed downstairs. He drove off by himself, as had been -arranged, and watched the flashing lights of the little brougham in -front of him. A strange sense of loss came over him. He felt that -Dorian Gray would never again be to him all that he had been in the -past. Life had come between them.... His eyes darkened, and the -crowded flaring streets became blurred to his eyes. When the cab drew -up at the theatre, it seemed to him that he had grown years older. - - - -CHAPTER 7 - -For some reason or other, the house was crowded that night, and the fat -Jew manager who met them at the door was beaming from ear to ear with -an oily tremulous smile. He escorted them to their box with a sort of -pompous humility, waving his fat jewelled hands and talking at the top -of his voice. Dorian Gray loathed him more than ever. He felt as if -he had come to look for Miranda and had been met by Caliban. Lord -Henry, upon the other hand, rather liked him. At least he declared he -did, and insisted on shaking him by the hand and assuring him that he -was proud to meet a man who had discovered a real genius and gone -bankrupt over a poet. Hallward amused himself with watching the faces -in the pit. The heat was terribly oppressive, and the huge sunlight -flamed like a monstrous dahlia with petals of yellow fire. The youths -in the gallery had taken off their coats and waistcoats and hung them -over the side. They talked to each other across the theatre and shared -their oranges with the tawdry girls who sat beside them. Some women -were laughing in the pit. Their voices were horribly shrill and -discordant. The sound of the popping of corks came from the bar. - -"What a place to find one's divinity in!" said Lord Henry. - -"Yes!" answered Dorian Gray. "It was here I found her, and she is -divine beyond all living things. When she acts, you will forget -everything. These common rough people, with their coarse faces and -brutal gestures, become quite different when she is on the stage. They -sit silently and watch her. They weep and laugh as she wills them to -do. She makes them as responsive as a violin. She spiritualizes them, -and one feels that they are of the same flesh and blood as one's self." - -"The same flesh and blood as one's self! Oh, I hope not!" exclaimed -Lord Henry, who was scanning the occupants of the gallery through his -opera-glass. - -"Don't pay any attention to him, Dorian," said the painter. "I -understand what you mean, and I believe in this girl. Any one you love -must be marvellous, and any girl who has the effect you describe must -be fine and noble. To spiritualize one's age--that is something worth -doing. If this girl can give a soul to those who have lived without -one, if she can create the sense of beauty in people whose lives have -been sordid and ugly, if she can strip them of their selfishness and -lend them tears for sorrows that are not their own, she is worthy of -all your adoration, worthy of the adoration of the world. This -marriage is quite right. I did not think so at first, but I admit it -now. The gods made Sibyl Vane for you. Without her you would have -been incomplete." - -"Thanks, Basil," answered Dorian Gray, pressing his hand. "I knew that -you would understand me. Harry is so cynical, he terrifies me. But -here is the orchestra. It is quite dreadful, but it only lasts for -about five minutes. Then the curtain rises, and you will see the girl -to whom I am going to give all my life, to whom I have given everything -that is good in me." - -A quarter of an hour afterwards, amidst an extraordinary turmoil of -applause, Sibyl Vane stepped on to the stage. Yes, she was certainly -lovely to look at--one of the loveliest creatures, Lord Henry thought, -that he had ever seen. There was something of the fawn in her shy -grace and startled eyes. A faint blush, like the shadow of a rose in a -mirror of silver, came to her cheeks as she glanced at the crowded -enthusiastic house. She stepped back a few paces and her lips seemed -to tremble. Basil Hallward leaped to his feet and began to applaud. -Motionless, and as one in a dream, sat Dorian Gray, gazing at her. -Lord Henry peered through his glasses, murmuring, "Charming! charming!" - -The scene was the hall of Capulet's house, and Romeo in his pilgrim's -dress had entered with Mercutio and his other friends. The band, such -as it was, struck up a few bars of music, and the dance began. Through -the crowd of ungainly, shabbily dressed actors, Sibyl Vane moved like a -creature from a finer world. Her body swayed, while she danced, as a -plant sways in the water. The curves of her throat were the curves of -a white lily. Her hands seemed to be made of cool ivory. - -Yet she was curiously listless. She showed no sign of joy when her -eyes rested on Romeo. The few words she had to speak-- - - Good pilgrim, you do wrong your hand too much, - Which mannerly devotion shows in this; - For saints have hands that pilgrims' hands do touch, - And palm to palm is holy palmers' kiss-- - -with the brief dialogue that follows, were spoken in a thoroughly -artificial manner. The voice was exquisite, but from the point of view -of tone it was absolutely false. It was wrong in colour. It took away -all the life from the verse. It made the passion unreal. - -Dorian Gray grew pale as he watched her. He was puzzled and anxious. -Neither of his friends dared to say anything to him. She seemed to -them to be absolutely incompetent. They were horribly disappointed. - -Yet they felt that the true test of any Juliet is the balcony scene of -the second act. They waited for that. If she failed there, there was -nothing in her. - -She looked charming as she came out in the moonlight. That could not -be denied. But the staginess of her acting was unbearable, and grew -worse as she went on. Her gestures became absurdly artificial. She -overemphasized everything that she had to say. The beautiful passage-- - - Thou knowest the mask of night is on my face, - Else would a maiden blush bepaint my cheek - For that which thou hast heard me speak to-night-- - -was declaimed with the painful precision of a schoolgirl who has been -taught to recite by some second-rate professor of elocution. When she -leaned over the balcony and came to those wonderful lines-- - - Although I joy in thee, - I have no joy of this contract to-night: - It is too rash, too unadvised, too sudden; - Too like the lightning, which doth cease to be - Ere one can say, "It lightens." Sweet, good-night! - This bud of love by summer's ripening breath - May prove a beauteous flower when next we meet-- - -she spoke the words as though they conveyed no meaning to her. It was -not nervousness. Indeed, so far from being nervous, she was absolutely -self-contained. It was simply bad art. She was a complete failure. - -Even the common uneducated audience of the pit and gallery lost their -interest in the play. They got restless, and began to talk loudly and -to whistle. The Jew manager, who was standing at the back of the -dress-circle, stamped and swore with rage. The only person unmoved was -the girl herself. - -When the second act was over, there came a storm of hisses, and Lord -Henry got up from his chair and put on his coat. "She is quite -beautiful, Dorian," he said, "but she can't act. Let us go." - -"I am going to see the play through," answered the lad, in a hard -bitter voice. "I am awfully sorry that I have made you waste an -evening, Harry. I apologize to you both." - -"My dear Dorian, I should think Miss Vane was ill," interrupted -Hallward. "We will come some other night." - -"I wish she were ill," he rejoined. "But she seems to me to be simply -callous and cold. She has entirely altered. Last night she was a -great artist. This evening she is merely a commonplace mediocre -actress." - -"Don't talk like that about any one you love, Dorian. Love is a more -wonderful thing than art." - -"They are both simply forms of imitation," remarked Lord Henry. "But -do let us go. Dorian, you must not stay here any longer. It is not -good for one's morals to see bad acting. Besides, I don't suppose you -will want your wife to act, so what does it matter if she plays Juliet -like a wooden doll? She is very lovely, and if she knows as little -about life as she does about acting, she will be a delightful -experience. There are only two kinds of people who are really -fascinating--people who know absolutely everything, and people who know -absolutely nothing. Good heavens, my dear boy, don't look so tragic! -The secret of remaining young is never to have an emotion that is -unbecoming. Come to the club with Basil and myself. We will smoke -cigarettes and drink to the beauty of Sibyl Vane. She is beautiful. -What more can you want?" - -"Go away, Harry," cried the lad. "I want to be alone. Basil, you must -go. Ah! can't you see that my heart is breaking?" The hot tears came -to his eyes. His lips trembled, and rushing to the back of the box, he -leaned up against the wall, hiding his face in his hands. - -"Let us go, Basil," said Lord Henry with a strange tenderness in his -voice, and the two young men passed out together. - -A few moments afterwards the footlights flared up and the curtain rose -on the third act. Dorian Gray went back to his seat. He looked pale, -and proud, and indifferent. The play dragged on, and seemed -interminable. Half of the audience went out, tramping in heavy boots -and laughing. The whole thing was a _fiasco_. The last act was played -to almost empty benches. The curtain went down on a titter and some -groans. - -As soon as it was over, Dorian Gray rushed behind the scenes into the -greenroom. The girl was standing there alone, with a look of triumph -on her face. Her eyes were lit with an exquisite fire. There was a -radiance about her. Her parted lips were smiling over some secret of -their own. - -When he entered, she looked at him, and an expression of infinite joy -came over her. "How badly I acted to-night, Dorian!" she cried. - -"Horribly!" he answered, gazing at her in amazement. "Horribly! It -was dreadful. Are you ill? You have no idea what it was. You have no -idea what I suffered." - -The girl smiled. "Dorian," she answered, lingering over his name with -long-drawn music in her voice, as though it were sweeter than honey to -the red petals of her mouth. "Dorian, you should have understood. But -you understand now, don't you?" - -"Understand what?" he asked, angrily. - -"Why I was so bad to-night. Why I shall always be bad. Why I shall -never act well again." - -He shrugged his shoulders. "You are ill, I suppose. When you are ill -you shouldn't act. You make yourself ridiculous. My friends were -bored. I was bored." - -She seemed not to listen to him. She was transfigured with joy. An -ecstasy of happiness dominated her. - -"Dorian, Dorian," she cried, "before I knew you, acting was the one -reality of my life. It was only in the theatre that I lived. I -thought that it was all true. I was Rosalind one night and Portia the -other. The joy of Beatrice was my joy, and the sorrows of Cordelia -were mine also. I believed in everything. The common people who acted -with me seemed to me to be godlike. The painted scenes were my world. -I knew nothing but shadows, and I thought them real. You came--oh, my -beautiful love!--and you freed my soul from prison. You taught me what -reality really is. To-night, for the first time in my life, I saw -through the hollowness, the sham, the silliness of the empty pageant in -which I had always played. To-night, for the first time, I became -conscious that the Romeo was hideous, and old, and painted, that the -moonlight in the orchard was false, that the scenery was vulgar, and -that the words I had to speak were unreal, were not my words, were not -what I wanted to say. You had brought me something higher, something -of which all art is but a reflection. You had made me understand what -love really is. My love! My love! Prince Charming! Prince of life! -I have grown sick of shadows. You are more to me than all art can ever -be. What have I to do with the puppets of a play? When I came on -to-night, I could not understand how it was that everything had gone -from me. I thought that I was going to be wonderful. I found that I -could do nothing. Suddenly it dawned on my soul what it all meant. -The knowledge was exquisite to me. I heard them hissing, and I smiled. -What could they know of love such as ours? Take me away, Dorian--take -me away with you, where we can be quite alone. I hate the stage. I -might mimic a passion that I do not feel, but I cannot mimic one that -burns me like fire. Oh, Dorian, Dorian, you understand now what it -signifies? Even if I could do it, it would be profanation for me to -play at being in love. You have made me see that." - -He flung himself down on the sofa and turned away his face. "You have -killed my love," he muttered. - -She looked at him in wonder and laughed. He made no answer. She came -across to him, and with her little fingers stroked his hair. She knelt -down and pressed his hands to her lips. He drew them away, and a -shudder ran through him. - -Then he leaped up and went to the door. "Yes," he cried, "you have -killed my love. You used to stir my imagination. Now you don't even -stir my curiosity. You simply produce no effect. I loved you because -you were marvellous, because you had genius and intellect, because you -realized the dreams of great poets and gave shape and substance to the -shadows of art. You have thrown it all away. You are shallow and -stupid. My God! how mad I was to love you! What a fool I have been! -You are nothing to me now. I will never see you again. I will never -think of you. I will never mention your name. You don't know what you -were to me, once. Why, once ... Oh, I can't bear to think of it! I -wish I had never laid eyes upon you! You have spoiled the romance of -my life. How little you can know of love, if you say it mars your art! -Without your art, you are nothing. I would have made you famous, -splendid, magnificent. The world would have worshipped you, and you -would have borne my name. What are you now? A third-rate actress with -a pretty face." - -The girl grew white, and trembled. She clenched her hands together, -and her voice seemed to catch in her throat. "You are not serious, -Dorian?" she murmured. "You are acting." - -"Acting! I leave that to you. You do it so well," he answered -bitterly. - -She rose from her knees and, with a piteous expression of pain in her -face, came across the room to him. She put her hand upon his arm and -looked into his eyes. He thrust her back. "Don't touch me!" he cried. - -A low moan broke from her, and she flung herself at his feet and lay -there like a trampled flower. "Dorian, Dorian, don't leave me!" she -whispered. "I am so sorry I didn't act well. I was thinking of you -all the time. But I will try--indeed, I will try. It came so suddenly -across me, my love for you. I think I should never have known it if -you had not kissed me--if we had not kissed each other. Kiss me again, -my love. Don't go away from me. I couldn't bear it. Oh! don't go -away from me. My brother ... No; never mind. He didn't mean it. He -was in jest.... But you, oh! can't you forgive me for to-night? I will -work so hard and try to improve. Don't be cruel to me, because I love -you better than anything in the world. After all, it is only once that -I have not pleased you. But you are quite right, Dorian. I should -have shown myself more of an artist. It was foolish of me, and yet I -couldn't help it. Oh, don't leave me, don't leave me." A fit of -passionate sobbing choked her. She crouched on the floor like a -wounded thing, and Dorian Gray, with his beautiful eyes, looked down at -her, and his chiselled lips curled in exquisite disdain. There is -always something ridiculous about the emotions of people whom one has -ceased to love. Sibyl Vane seemed to him to be absurdly melodramatic. -Her tears and sobs annoyed him. - -"I am going," he said at last in his calm clear voice. "I don't wish -to be unkind, but I can't see you again. You have disappointed me." - -She wept silently, and made no answer, but crept nearer. Her little -hands stretched blindly out, and appeared to be seeking for him. He -turned on his heel and left the room. In a few moments he was out of -the theatre. - -Where he went to he hardly knew. He remembered wandering through dimly -lit streets, past gaunt, black-shadowed archways and evil-looking -houses. Women with hoarse voices and harsh laughter had called after -him. Drunkards had reeled by, cursing and chattering to themselves -like monstrous apes. He had seen grotesque children huddled upon -door-steps, and heard shrieks and oaths from gloomy courts. - -As the dawn was just breaking, he found himself close to Covent Garden. -The darkness lifted, and, flushed with faint fires, the sky hollowed -itself into a perfect pearl. Huge carts filled with nodding lilies -rumbled slowly down the polished empty street. The air was heavy with -the perfume of the flowers, and their beauty seemed to bring him an -anodyne for his pain. He followed into the market and watched the men -unloading their waggons. A white-smocked carter offered him some -cherries. He thanked him, wondered why he refused to accept any money -for them, and began to eat them listlessly. They had been plucked at -midnight, and the coldness of the moon had entered into them. A long -line of boys carrying crates of striped tulips, and of yellow and red -roses, defiled in front of him, threading their way through the huge, -jade-green piles of vegetables. Under the portico, with its grey, -sun-bleached pillars, loitered a troop of draggled bareheaded girls, -waiting for the auction to be over. Others crowded round the swinging -doors of the coffee-house in the piazza. The heavy cart-horses slipped -and stamped upon the rough stones, shaking their bells and trappings. -Some of the drivers were lying asleep on a pile of sacks. Iris-necked -and pink-footed, the pigeons ran about picking up seeds. - -After a little while, he hailed a hansom and drove home. For a few -moments he loitered upon the doorstep, looking round at the silent -square, with its blank, close-shuttered windows and its staring blinds. -The sky was pure opal now, and the roofs of the houses glistened like -silver against it. From some chimney opposite a thin wreath of smoke -was rising. It curled, a violet riband, through the nacre-coloured air. - -In the huge gilt Venetian lantern, spoil of some Doge's barge, that -hung from the ceiling of the great, oak-panelled hall of entrance, -lights were still burning from three flickering jets: thin blue petals -of flame they seemed, rimmed with white fire. He turned them out and, -having thrown his hat and cape on the table, passed through the library -towards the door of his bedroom, a large octagonal chamber on the -ground floor that, in his new-born feeling for luxury, he had just had -decorated for himself and hung with some curious Renaissance tapestries -that had been discovered stored in a disused attic at Selby Royal. As -he was turning the handle of the door, his eye fell upon the portrait -Basil Hallward had painted of him. He started back as if in surprise. -Then he went on into his own room, looking somewhat puzzled. After he -had taken the button-hole out of his coat, he seemed to hesitate. -Finally, he came back, went over to the picture, and examined it. In -the dim arrested light that struggled through the cream-coloured silk -blinds, the face appeared to him to be a little changed. The -expression looked different. One would have said that there was a -touch of cruelty in the mouth. It was certainly strange. - -He turned round and, walking to the window, drew up the blind. The -bright dawn flooded the room and swept the fantastic shadows into dusky -corners, where they lay shuddering. But the strange expression that he -had noticed in the face of the portrait seemed to linger there, to be -more intensified even. The quivering ardent sunlight showed him the -lines of cruelty round the mouth as clearly as if he had been looking -into a mirror after he had done some dreadful thing. - -He winced and, taking up from the table an oval glass framed in ivory -Cupids, one of Lord Henry's many presents to him, glanced hurriedly -into its polished depths. No line like that warped his red lips. What -did it mean? - -He rubbed his eyes, and came close to the picture, and examined it -again. There were no signs of any change when he looked into the -actual painting, and yet there was no doubt that the whole expression -had altered. It was not a mere fancy of his own. The thing was -horribly apparent. - -He threw himself into a chair and began to think. Suddenly there -flashed across his mind what he had said in Basil Hallward's studio the -day the picture had been finished. Yes, he remembered it perfectly. -He had uttered a mad wish that he himself might remain young, and the -portrait grow old; that his own beauty might be untarnished, and the -face on the canvas bear the burden of his passions and his sins; that -the painted image might be seared with the lines of suffering and -thought, and that he might keep all the delicate bloom and loveliness -of his then just conscious boyhood. Surely his wish had not been -fulfilled? Such things were impossible. It seemed monstrous even to -think of them. And, yet, there was the picture before him, with the -touch of cruelty in the mouth. - -Cruelty! Had he been cruel? It was the girl's fault, not his. He had -dreamed of her as a great artist, had given his love to her because he -had thought her great. Then she had disappointed him. She had been -shallow and unworthy. And, yet, a feeling of infinite regret came over -him, as he thought of her lying at his feet sobbing like a little -child. He remembered with what callousness he had watched her. Why -had he been made like that? Why had such a soul been given to him? -But he had suffered also. During the three terrible hours that the -play had lasted, he had lived centuries of pain, aeon upon aeon of -torture. His life was well worth hers. She had marred him for a -moment, if he had wounded her for an age. Besides, women were better -suited to bear sorrow than men. They lived on their emotions. They -only thought of their emotions. When they took lovers, it was merely -to have some one with whom they could have scenes. Lord Henry had told -him that, and Lord Henry knew what women were. Why should he trouble -about Sibyl Vane? She was nothing to him now. - -But the picture? What was he to say of that? It held the secret of -his life, and told his story. It had taught him to love his own -beauty. Would it teach him to loathe his own soul? Would he ever look -at it again? - -No; it was merely an illusion wrought on the troubled senses. The -horrible night that he had passed had left phantoms behind it. -Suddenly there had fallen upon his brain that tiny scarlet speck that -makes men mad. The picture had not changed. It was folly to think so. - -Yet it was watching him, with its beautiful marred face and its cruel -smile. Its bright hair gleamed in the early sunlight. Its blue eyes -met his own. A sense of infinite pity, not for himself, but for the -painted image of himself, came over him. It had altered already, and -would alter more. Its gold would wither into grey. Its red and white -roses would die. For every sin that he committed, a stain would fleck -and wreck its fairness. But he would not sin. The picture, changed or -unchanged, would be to him the visible emblem of conscience. He would -resist temptation. He would not see Lord Henry any more--would not, at -any rate, listen to those subtle poisonous theories that in Basil -Hallward's garden had first stirred within him the passion for -impossible things. He would go back to Sibyl Vane, make her amends, -marry her, try to love her again. Yes, it was his duty to do so. She -must have suffered more than he had. Poor child! He had been selfish -and cruel to her. The fascination that she had exercised over him -would return. They would be happy together. His life with her would -be beautiful and pure. - -He got up from his chair and drew a large screen right in front of the -portrait, shuddering as he glanced at it. "How horrible!" he murmured -to himself, and he walked across to the window and opened it. When he -stepped out on to the grass, he drew a deep breath. The fresh morning -air seemed to drive away all his sombre passions. He thought only of -Sibyl. A faint echo of his love came back to him. He repeated her -name over and over again. The birds that were singing in the -dew-drenched garden seemed to be telling the flowers about her. - - - -CHAPTER 8 - -It was long past noon when he awoke. His valet had crept several times -on tiptoe into the room to see if he was stirring, and had wondered -what made his young master sleep so late. Finally his bell sounded, -and Victor came in softly with a cup of tea, and a pile of letters, on -a small tray of old Sevres china, and drew back the olive-satin -curtains, with their shimmering blue lining, that hung in front of the -three tall windows. - -"Monsieur has well slept this morning," he said, smiling. - -"What o'clock is it, Victor?" asked Dorian Gray drowsily. - -"One hour and a quarter, Monsieur." - -How late it was! He sat up, and having sipped some tea, turned over -his letters. One of them was from Lord Henry, and had been brought by -hand that morning. He hesitated for a moment, and then put it aside. -The others he opened listlessly. They contained the usual collection -of cards, invitations to dinner, tickets for private views, programmes -of charity concerts, and the like that are showered on fashionable -young men every morning during the season. There was a rather heavy -bill for a chased silver Louis-Quinze toilet-set that he had not yet -had the courage to send on to his guardians, who were extremely -old-fashioned people and did not realize that we live in an age when -unnecessary things are our only necessities; and there were several -very courteously worded communications from Jermyn Street money-lenders -offering to advance any sum of money at a moment's notice and at the -most reasonable rates of interest. - -After about ten minutes he got up, and throwing on an elaborate -dressing-gown of silk-embroidered cashmere wool, passed into the -onyx-paved bathroom. The cool water refreshed him after his long -sleep. He seemed to have forgotten all that he had gone through. A -dim sense of having taken part in some strange tragedy came to him once -or twice, but there was the unreality of a dream about it. - -As soon as he was dressed, he went into the library and sat down to a -light French breakfast that had been laid out for him on a small round -table close to the open window. It was an exquisite day. The warm air -seemed laden with spices. A bee flew in and buzzed round the -blue-dragon bowl that, filled with sulphur-yellow roses, stood before -him. He felt perfectly happy. - -Suddenly his eye fell on the screen that he had placed in front of the -portrait, and he started. - -"Too cold for Monsieur?" asked his valet, putting an omelette on the -table. "I shut the window?" - -Dorian shook his head. "I am not cold," he murmured. - -Was it all true? Had the portrait really changed? Or had it been -simply his own imagination that had made him see a look of evil where -there had been a look of joy? Surely a painted canvas could not alter? -The thing was absurd. It would serve as a tale to tell Basil some day. -It would make him smile. - -And, yet, how vivid was his recollection of the whole thing! First in -the dim twilight, and then in the bright dawn, he had seen the touch of -cruelty round the warped lips. He almost dreaded his valet leaving the -room. He knew that when he was alone he would have to examine the -portrait. He was afraid of certainty. When the coffee and cigarettes -had been brought and the man turned to go, he felt a wild desire to -tell him to remain. As the door was closing behind him, he called him -back. The man stood waiting for his orders. Dorian looked at him for -a moment. "I am not at home to any one, Victor," he said with a sigh. -The man bowed and retired. - -Then he rose from the table, lit a cigarette, and flung himself down on -a luxuriously cushioned couch that stood facing the screen. The screen -was an old one, of gilt Spanish leather, stamped and wrought with a -rather florid Louis-Quatorze pattern. He scanned it curiously, -wondering if ever before it had concealed the secret of a man's life. - -Should he move it aside, after all? Why not let it stay there? What -was the use of knowing? If the thing was true, it was terrible. If it -was not true, why trouble about it? But what if, by some fate or -deadlier chance, eyes other than his spied behind and saw the horrible -change? What should he do if Basil Hallward came and asked to look at -his own picture? Basil would be sure to do that. No; the thing had to -be examined, and at once. Anything would be better than this dreadful -state of doubt. - -He got up and locked both doors. At least he would be alone when he -looked upon the mask of his shame. Then he drew the screen aside and -saw himself face to face. It was perfectly true. The portrait had -altered. - -As he often remembered afterwards, and always with no small wonder, he -found himself at first gazing at the portrait with a feeling of almost -scientific interest. That such a change should have taken place was -incredible to him. And yet it was a fact. Was there some subtle -affinity between the chemical atoms that shaped themselves into form -and colour on the canvas and the soul that was within him? Could it be -that what that soul thought, they realized?--that what it dreamed, they -made true? Or was there some other, more terrible reason? He -shuddered, and felt afraid, and, going back to the couch, lay there, -gazing at the picture in sickened horror. - -One thing, however, he felt that it had done for him. It had made him -conscious how unjust, how cruel, he had been to Sibyl Vane. It was not -too late to make reparation for that. She could still be his wife. -His unreal and selfish love would yield to some higher influence, would -be transformed into some nobler passion, and the portrait that Basil -Hallward had painted of him would be a guide to him through life, would -be to him what holiness is to some, and conscience to others, and the -fear of God to us all. There were opiates for remorse, drugs that -could lull the moral sense to sleep. But here was a visible symbol of -the degradation of sin. Here was an ever-present sign of the ruin men -brought upon their souls. - -Three o'clock struck, and four, and the half-hour rang its double -chime, but Dorian Gray did not stir. He was trying to gather up the -scarlet threads of life and to weave them into a pattern; to find his -way through the sanguine labyrinth of passion through which he was -wandering. He did not know what to do, or what to think. Finally, he -went over to the table and wrote a passionate letter to the girl he had -loved, imploring her forgiveness and accusing himself of madness. He -covered page after page with wild words of sorrow and wilder words of -pain. There is a luxury in self-reproach. When we blame ourselves, we -feel that no one else has a right to blame us. It is the confession, -not the priest, that gives us absolution. When Dorian had finished the -letter, he felt that he had been forgiven. - -Suddenly there came a knock to the door, and he heard Lord Henry's -voice outside. "My dear boy, I must see you. Let me in at once. I -can't bear your shutting yourself up like this." - -He made no answer at first, but remained quite still. The knocking -still continued and grew louder. Yes, it was better to let Lord Henry -in, and to explain to him the new life he was going to lead, to quarrel -with him if it became necessary to quarrel, to part if parting was -inevitable. He jumped up, drew the screen hastily across the picture, -and unlocked the door. - -"I am so sorry for it all, Dorian," said Lord Henry as he entered. -"But you must not think too much about it." - -"Do you mean about Sibyl Vane?" asked the lad. - -"Yes, of course," answered Lord Henry, sinking into a chair and slowly -pulling off his yellow gloves. "It is dreadful, from one point of -view, but it was not your fault. Tell me, did you go behind and see -her, after the play was over?" - -"Yes." - -"I felt sure you had. Did you make a scene with her?" - -"I was brutal, Harry--perfectly brutal. But it is all right now. I am -not sorry for anything that has happened. It has taught me to know -myself better." - -"Ah, Dorian, I am so glad you take it in that way! I was afraid I -would find you plunged in remorse and tearing that nice curly hair of -yours." - -"I have got through all that," said Dorian, shaking his head and -smiling. "I am perfectly happy now. I know what conscience is, to -begin with. It is not what you told me it was. It is the divinest -thing in us. Don't sneer at it, Harry, any more--at least not before -me. I want to be good. I can't bear the idea of my soul being -hideous." - -"A very charming artistic basis for ethics, Dorian! I congratulate you -on it. But how are you going to begin?" - -"By marrying Sibyl Vane." - -"Marrying Sibyl Vane!" cried Lord Henry, standing up and looking at him -in perplexed amazement. "But, my dear Dorian--" - -"Yes, Harry, I know what you are going to say. Something dreadful -about marriage. Don't say it. Don't ever say things of that kind to -me again. Two days ago I asked Sibyl to marry me. I am not going to -break my word to her. She is to be my wife." - -"Your wife! Dorian! ... Didn't you get my letter? I wrote to you this -morning, and sent the note down by my own man." - -"Your letter? Oh, yes, I remember. I have not read it yet, Harry. I -was afraid there might be something in it that I wouldn't like. You -cut life to pieces with your epigrams." - -"You know nothing then?" - -"What do you mean?" - -Lord Henry walked across the room, and sitting down by Dorian Gray, -took both his hands in his own and held them tightly. "Dorian," he -said, "my letter--don't be frightened--was to tell you that Sibyl Vane -is dead." - -A cry of pain broke from the lad's lips, and he leaped to his feet, -tearing his hands away from Lord Henry's grasp. "Dead! Sibyl dead! -It is not true! It is a horrible lie! How dare you say it?" - -"It is quite true, Dorian," said Lord Henry, gravely. "It is in all -the morning papers. I wrote down to you to ask you not to see any one -till I came. There will have to be an inquest, of course, and you must -not be mixed up in it. Things like that make a man fashionable in -Paris. But in London people are so prejudiced. Here, one should never -make one's _debut_ with a scandal. One should reserve that to give an -interest to one's old age. I suppose they don't know your name at the -theatre? If they don't, it is all right. Did any one see you going -round to her room? That is an important point." - -Dorian did not answer for a few moments. He was dazed with horror. -Finally he stammered, in a stifled voice, "Harry, did you say an -inquest? What did you mean by that? Did Sibyl--? Oh, Harry, I can't -bear it! But be quick. Tell me everything at once." - -"I have no doubt it was not an accident, Dorian, though it must be put -in that way to the public. It seems that as she was leaving the -theatre with her mother, about half-past twelve or so, she said she had -forgotten something upstairs. They waited some time for her, but she -did not come down again. They ultimately found her lying dead on the -floor of her dressing-room. She had swallowed something by mistake, -some dreadful thing they use at theatres. I don't know what it was, -but it had either prussic acid or white lead in it. I should fancy it -was prussic acid, as she seems to have died instantaneously." - -"Harry, Harry, it is terrible!" cried the lad. - -"Yes; it is very tragic, of course, but you must not get yourself mixed -up in it. I see by _The Standard_ that she was seventeen. I should have -thought she was almost younger than that. She looked such a child, and -seemed to know so little about acting. Dorian, you mustn't let this -thing get on your nerves. You must come and dine with me, and -afterwards we will look in at the opera. It is a Patti night, and -everybody will be there. You can come to my sister's box. She has got -some smart women with her." - -"So I have murdered Sibyl Vane," said Dorian Gray, half to himself, -"murdered her as surely as if I had cut her little throat with a knife. -Yet the roses are not less lovely for all that. The birds sing just as -happily in my garden. And to-night I am to dine with you, and then go -on to the opera, and sup somewhere, I suppose, afterwards. How -extraordinarily dramatic life is! If I had read all this in a book, -Harry, I think I would have wept over it. Somehow, now that it has -happened actually, and to me, it seems far too wonderful for tears. -Here is the first passionate love-letter I have ever written in my -life. Strange, that my first passionate love-letter should have been -addressed to a dead girl. Can they feel, I wonder, those white silent -people we call the dead? Sibyl! Can she feel, or know, or listen? -Oh, Harry, how I loved her once! It seems years ago to me now. She -was everything to me. Then came that dreadful night--was it really -only last night?--when she played so badly, and my heart almost broke. -She explained it all to me. It was terribly pathetic. But I was not -moved a bit. I thought her shallow. Suddenly something happened that -made me afraid. I can't tell you what it was, but it was terrible. I -said I would go back to her. I felt I had done wrong. And now she is -dead. My God! My God! Harry, what shall I do? You don't know the -danger I am in, and there is nothing to keep me straight. She would -have done that for me. She had no right to kill herself. It was -selfish of her." - -"My dear Dorian," answered Lord Henry, taking a cigarette from his case -and producing a gold-latten matchbox, "the only way a woman can ever -reform a man is by boring him so completely that he loses all possible -interest in life. If you had married this girl, you would have been -wretched. Of course, you would have treated her kindly. One can -always be kind to people about whom one cares nothing. But she would -have soon found out that you were absolutely indifferent to her. And -when a woman finds that out about her husband, she either becomes -dreadfully dowdy, or wears very smart bonnets that some other woman's -husband has to pay for. I say nothing about the social mistake, which -would have been abject--which, of course, I would not have allowed--but -I assure you that in any case the whole thing would have been an -absolute failure." - -"I suppose it would," muttered the lad, walking up and down the room -and looking horribly pale. "But I thought it was my duty. It is not -my fault that this terrible tragedy has prevented my doing what was -right. I remember your saying once that there is a fatality about good -resolutions--that they are always made too late. Mine certainly were." - -"Good resolutions are useless attempts to interfere with scientific -laws. Their origin is pure vanity. Their result is absolutely _nil_. -They give us, now and then, some of those luxurious sterile emotions -that have a certain charm for the weak. That is all that can be said -for them. They are simply cheques that men draw on a bank where they -have no account." - -"Harry," cried Dorian Gray, coming over and sitting down beside him, -"why is it that I cannot feel this tragedy as much as I want to? I -don't think I am heartless. Do you?" - -"You have done too many foolish things during the last fortnight to be -entitled to give yourself that name, Dorian," answered Lord Henry with -his sweet melancholy smile. - -The lad frowned. "I don't like that explanation, Harry," he rejoined, -"but I am glad you don't think I am heartless. I am nothing of the -kind. I know I am not. And yet I must admit that this thing that has -happened does not affect me as it should. It seems to me to be simply -like a wonderful ending to a wonderful play. It has all the terrible -beauty of a Greek tragedy, a tragedy in which I took a great part, but -by which I have not been wounded." - -"It is an interesting question," said Lord Henry, who found an -exquisite pleasure in playing on the lad's unconscious egotism, "an -extremely interesting question. I fancy that the true explanation is -this: It often happens that the real tragedies of life occur in such -an inartistic manner that they hurt us by their crude violence, their -absolute incoherence, their absurd want of meaning, their entire lack -of style. They affect us just as vulgarity affects us. They give us -an impression of sheer brute force, and we revolt against that. -Sometimes, however, a tragedy that possesses artistic elements of -beauty crosses our lives. If these elements of beauty are real, the -whole thing simply appeals to our sense of dramatic effect. Suddenly -we find that we are no longer the actors, but the spectators of the -play. Or rather we are both. We watch ourselves, and the mere wonder -of the spectacle enthralls us. In the present case, what is it that -has really happened? Some one has killed herself for love of you. I -wish that I had ever had such an experience. It would have made me in -love with love for the rest of my life. The people who have adored -me--there have not been very many, but there have been some--have -always insisted on living on, long after I had ceased to care for them, -or they to care for me. They have become stout and tedious, and when I -meet them, they go in at once for reminiscences. That awful memory of -woman! What a fearful thing it is! And what an utter intellectual -stagnation it reveals! One should absorb the colour of life, but one -should never remember its details. Details are always vulgar." - -"I must sow poppies in my garden," sighed Dorian. - -"There is no necessity," rejoined his companion. "Life has always -poppies in her hands. Of course, now and then things linger. I once -wore nothing but violets all through one season, as a form of artistic -mourning for a romance that would not die. Ultimately, however, it did -die. I forget what killed it. I think it was her proposing to -sacrifice the whole world for me. That is always a dreadful moment. -It fills one with the terror of eternity. Well--would you believe -it?--a week ago, at Lady Hampshire's, I found myself seated at dinner -next the lady in question, and she insisted on going over the whole -thing again, and digging up the past, and raking up the future. I had -buried my romance in a bed of asphodel. She dragged it out again and -assured me that I had spoiled her life. I am bound to state that she -ate an enormous dinner, so I did not feel any anxiety. But what a lack -of taste she showed! The one charm of the past is that it is the past. -But women never know when the curtain has fallen. They always want a -sixth act, and as soon as the interest of the play is entirely over, -they propose to continue it. If they were allowed their own way, every -comedy would have a tragic ending, and every tragedy would culminate in -a farce. They are charmingly artificial, but they have no sense of -art. You are more fortunate than I am. I assure you, Dorian, that not -one of the women I have known would have done for me what Sibyl Vane -did for you. Ordinary women always console themselves. Some of them -do it by going in for sentimental colours. Never trust a woman who -wears mauve, whatever her age may be, or a woman over thirty-five who -is fond of pink ribbons. It always means that they have a history. -Others find a great consolation in suddenly discovering the good -qualities of their husbands. They flaunt their conjugal felicity in -one's face, as if it were the most fascinating of sins. Religion -consoles some. Its mysteries have all the charm of a flirtation, a -woman once told me, and I can quite understand it. Besides, nothing -makes one so vain as being told that one is a sinner. Conscience makes -egotists of us all. Yes; there is really no end to the consolations -that women find in modern life. Indeed, I have not mentioned the most -important one." - -"What is that, Harry?" said the lad listlessly. - -"Oh, the obvious consolation. Taking some one else's admirer when one -loses one's own. In good society that always whitewashes a woman. But -really, Dorian, how different Sibyl Vane must have been from all the -women one meets! There is something to me quite beautiful about her -death. I am glad I am living in a century when such wonders happen. -They make one believe in the reality of the things we all play with, -such as romance, passion, and love." - -"I was terribly cruel to her. You forget that." - -"I am afraid that women appreciate cruelty, downright cruelty, more -than anything else. They have wonderfully primitive instincts. We -have emancipated them, but they remain slaves looking for their -masters, all the same. They love being dominated. I am sure you were -splendid. I have never seen you really and absolutely angry, but I can -fancy how delightful you looked. And, after all, you said something to -me the day before yesterday that seemed to me at the time to be merely -fanciful, but that I see now was absolutely true, and it holds the key -to everything." - -"What was that, Harry?" - -"You said to me that Sibyl Vane represented to you all the heroines of -romance--that she was Desdemona one night, and Ophelia the other; that -if she died as Juliet, she came to life as Imogen." - -"She will never come to life again now," muttered the lad, burying his -face in his hands. - -"No, she will never come to life. She has played her last part. But -you must think of that lonely death in the tawdry dressing-room simply -as a strange lurid fragment from some Jacobean tragedy, as a wonderful -scene from Webster, or Ford, or Cyril Tourneur. The girl never really -lived, and so she has never really died. To you at least she was -always a dream, a phantom that flitted through Shakespeare's plays and -left them lovelier for its presence, a reed through which Shakespeare's -music sounded richer and more full of joy. The moment she touched -actual life, she marred it, and it marred her, and so she passed away. -Mourn for Ophelia, if you like. Put ashes on your head because -Cordelia was strangled. Cry out against Heaven because the daughter of -Brabantio died. But don't waste your tears over Sibyl Vane. She was -less real than they are." - -There was a silence. The evening darkened in the room. Noiselessly, -and with silver feet, the shadows crept in from the garden. The -colours faded wearily out of things. - -After some time Dorian Gray looked up. "You have explained me to -myself, Harry," he murmured with something of a sigh of relief. "I -felt all that you have said, but somehow I was afraid of it, and I -could not express it to myself. How well you know me! But we will not -talk again of what has happened. It has been a marvellous experience. -That is all. I wonder if life has still in store for me anything as -marvellous." - -"Life has everything in store for you, Dorian. There is nothing that -you, with your extraordinary good looks, will not be able to do." - -"But suppose, Harry, I became haggard, and old, and wrinkled? What -then?" - -"Ah, then," said Lord Henry, rising to go, "then, my dear Dorian, you -would have to fight for your victories. As it is, they are brought to -you. No, you must keep your good looks. We live in an age that reads -too much to be wise, and that thinks too much to be beautiful. We -cannot spare you. And now you had better dress and drive down to the -club. We are rather late, as it is." - -"I think I shall join you at the opera, Harry. I feel too tired to eat -anything. What is the number of your sister's box?" - -"Twenty-seven, I believe. It is on the grand tier. You will see her -name on the door. But I am sorry you won't come and dine." - -"I don't feel up to it," said Dorian listlessly. "But I am awfully -obliged to you for all that you have said to me. You are certainly my -best friend. No one has ever understood me as you have." - -"We are only at the beginning of our friendship, Dorian," answered Lord -Henry, shaking him by the hand. "Good-bye. I shall see you before -nine-thirty, I hope. Remember, Patti is singing." - -As he closed the door behind him, Dorian Gray touched the bell, and in -a few minutes Victor appeared with the lamps and drew the blinds down. -He waited impatiently for him to go. The man seemed to take an -interminable time over everything. - -As soon as he had left, he rushed to the screen and drew it back. No; -there was no further change in the picture. It had received the news -of Sibyl Vane's death before he had known of it himself. It was -conscious of the events of life as they occurred. The vicious cruelty -that marred the fine lines of the mouth had, no doubt, appeared at the -very moment that the girl had drunk the poison, whatever it was. Or -was it indifferent to results? Did it merely take cognizance of what -passed within the soul? He wondered, and hoped that some day he would -see the change taking place before his very eyes, shuddering as he -hoped it. - -Poor Sibyl! What a romance it had all been! She had often mimicked -death on the stage. Then Death himself had touched her and taken her -with him. How had she played that dreadful last scene? Had she cursed -him, as she died? No; she had died for love of him, and love would -always be a sacrament to him now. She had atoned for everything by the -sacrifice she had made of her life. He would not think any more of -what she had made him go through, on that horrible night at the -theatre. When he thought of her, it would be as a wonderful tragic -figure sent on to the world's stage to show the supreme reality of -love. A wonderful tragic figure? Tears came to his eyes as he -remembered her childlike look, and winsome fanciful ways, and shy -tremulous grace. He brushed them away hastily and looked again at the -picture. - -He felt that the time had really come for making his choice. Or had -his choice already been made? Yes, life had decided that for -him--life, and his own infinite curiosity about life. Eternal youth, -infinite passion, pleasures subtle and secret, wild joys and wilder -sins--he was to have all these things. The portrait was to bear the -burden of his shame: that was all. - -A feeling of pain crept over him as he thought of the desecration that -was in store for the fair face on the canvas. Once, in boyish mockery -of Narcissus, he had kissed, or feigned to kiss, those painted lips -that now smiled so cruelly at him. Morning after morning he had sat -before the portrait wondering at its beauty, almost enamoured of it, as -it seemed to him at times. Was it to alter now with every mood to -which he yielded? Was it to become a monstrous and loathsome thing, to -be hidden away in a locked room, to be shut out from the sunlight that -had so often touched to brighter gold the waving wonder of its hair? -The pity of it! the pity of it! - -For a moment, he thought of praying that the horrible sympathy that -existed between him and the picture might cease. It had changed in -answer to a prayer; perhaps in answer to a prayer it might remain -unchanged. And yet, who, that knew anything about life, would -surrender the chance of remaining always young, however fantastic that -chance might be, or with what fateful consequences it might be fraught? -Besides, was it really under his control? Had it indeed been prayer -that had produced the substitution? Might there not be some curious -scientific reason for it all? If thought could exercise its influence -upon a living organism, might not thought exercise an influence upon -dead and inorganic things? Nay, without thought or conscious desire, -might not things external to ourselves vibrate in unison with our moods -and passions, atom calling to atom in secret love or strange affinity? -But the reason was of no importance. He would never again tempt by a -prayer any terrible power. If the picture was to alter, it was to -alter. That was all. Why inquire too closely into it? - -For there would be a real pleasure in watching it. He would be able to -follow his mind into its secret places. This portrait would be to him -the most magical of mirrors. As it had revealed to him his own body, -so it would reveal to him his own soul. And when winter came upon it, -he would still be standing where spring trembles on the verge of -summer. When the blood crept from its face, and left behind a pallid -mask of chalk with leaden eyes, he would keep the glamour of boyhood. -Not one blossom of his loveliness would ever fade. Not one pulse of -his life would ever weaken. Like the gods of the Greeks, he would be -strong, and fleet, and joyous. What did it matter what happened to the -coloured image on the canvas? He would be safe. That was everything. - -He drew the screen back into its former place in front of the picture, -smiling as he did so, and passed into his bedroom, where his valet was -already waiting for him. An hour later he was at the opera, and Lord -Henry was leaning over his chair. - - - -CHAPTER 9 - -As he was sitting at breakfast next morning, Basil Hallward was shown -into the room. - -"I am so glad I have found you, Dorian," he said gravely. "I called -last night, and they told me you were at the opera. Of course, I knew -that was impossible. But I wish you had left word where you had really -gone to. I passed a dreadful evening, half afraid that one tragedy -might be followed by another. I think you might have telegraphed for -me when you heard of it first. I read of it quite by chance in a late -edition of _The Globe_ that I picked up at the club. I came here at once -and was miserable at not finding you. I can't tell you how -heart-broken I am about the whole thing. I know what you must suffer. -But where were you? Did you go down and see the girl's mother? For a -moment I thought of following you there. They gave the address in the -paper. Somewhere in the Euston Road, isn't it? But I was afraid of -intruding upon a sorrow that I could not lighten. Poor woman! What a -state she must be in! And her only child, too! What did she say about -it all?" - -"My dear Basil, how do I know?" murmured Dorian Gray, sipping some -pale-yellow wine from a delicate, gold-beaded bubble of Venetian glass -and looking dreadfully bored. "I was at the opera. You should have -come on there. I met Lady Gwendolen, Harry's sister, for the first -time. We were in her box. She is perfectly charming; and Patti sang -divinely. Don't talk about horrid subjects. If one doesn't talk about -a thing, it has never happened. It is simply expression, as Harry -says, that gives reality to things. I may mention that she was not the -woman's only child. There is a son, a charming fellow, I believe. But -he is not on the stage. He is a sailor, or something. And now, tell -me about yourself and what you are painting." - -"You went to the opera?" said Hallward, speaking very slowly and with a -strained touch of pain in his voice. "You went to the opera while -Sibyl Vane was lying dead in some sordid lodging? You can talk to me -of other women being charming, and of Patti singing divinely, before -the girl you loved has even the quiet of a grave to sleep in? Why, -man, there are horrors in store for that little white body of hers!" - -"Stop, Basil! I won't hear it!" cried Dorian, leaping to his feet. -"You must not tell me about things. What is done is done. What is -past is past." - -"You call yesterday the past?" - -"What has the actual lapse of time got to do with it? It is only -shallow people who require years to get rid of an emotion. A man who -is master of himself can end a sorrow as easily as he can invent a -pleasure. I don't want to be at the mercy of my emotions. I want to -use them, to enjoy them, and to dominate them." - -"Dorian, this is horrible! Something has changed you completely. You -look exactly the same wonderful boy who, day after day, used to come -down to my studio to sit for his picture. But you were simple, -natural, and affectionate then. You were the most unspoiled creature -in the whole world. Now, I don't know what has come over you. You -talk as if you had no heart, no pity in you. It is all Harry's -influence. I see that." - -The lad flushed up and, going to the window, looked out for a few -moments on the green, flickering, sun-lashed garden. "I owe a great -deal to Harry, Basil," he said at last, "more than I owe to you. You -only taught me to be vain." - -"Well, I am punished for that, Dorian--or shall be some day." - -"I don't know what you mean, Basil," he exclaimed, turning round. "I -don't know what you want. What do you want?" - -"I want the Dorian Gray I used to paint," said the artist sadly. - -"Basil," said the lad, going over to him and putting his hand on his -shoulder, "you have come too late. Yesterday, when I heard that Sibyl -Vane had killed herself--" - -"Killed herself! Good heavens! is there no doubt about that?" cried -Hallward, looking up at him with an expression of horror. - -"My dear Basil! Surely you don't think it was a vulgar accident? Of -course she killed herself." - -The elder man buried his face in his hands. "How fearful," he -muttered, and a shudder ran through him. - -"No," said Dorian Gray, "there is nothing fearful about it. It is one -of the great romantic tragedies of the age. As a rule, people who act -lead the most commonplace lives. They are good husbands, or faithful -wives, or something tedious. You know what I mean--middle-class virtue -and all that kind of thing. How different Sibyl was! She lived her -finest tragedy. She was always a heroine. The last night she -played--the night you saw her--she acted badly because she had known -the reality of love. When she knew its unreality, she died, as Juliet -might have died. She passed again into the sphere of art. There is -something of the martyr about her. Her death has all the pathetic -uselessness of martyrdom, all its wasted beauty. But, as I was saying, -you must not think I have not suffered. If you had come in yesterday -at a particular moment--about half-past five, perhaps, or a quarter to -six--you would have found me in tears. Even Harry, who was here, who -brought me the news, in fact, had no idea what I was going through. I -suffered immensely. Then it passed away. I cannot repeat an emotion. -No one can, except sentimentalists. And you are awfully unjust, Basil. -You come down here to console me. That is charming of you. You find -me consoled, and you are furious. How like a sympathetic person! You -remind me of a story Harry told me about a certain philanthropist who -spent twenty years of his life in trying to get some grievance -redressed, or some unjust law altered--I forget exactly what it was. -Finally he succeeded, and nothing could exceed his disappointment. He -had absolutely nothing to do, almost died of _ennui_, and became a -confirmed misanthrope. And besides, my dear old Basil, if you really -want to console me, teach me rather to forget what has happened, or to -see it from a proper artistic point of view. Was it not Gautier who -used to write about _la consolation des arts_? I remember picking up a -little vellum-covered book in your studio one day and chancing on that -delightful phrase. Well, I am not like that young man you told me of -when we were down at Marlow together, the young man who used to say -that yellow satin could console one for all the miseries of life. I -love beautiful things that one can touch and handle. Old brocades, -green bronzes, lacquer-work, carved ivories, exquisite surroundings, -luxury, pomp--there is much to be got from all these. But the artistic -temperament that they create, or at any rate reveal, is still more to -me. To become the spectator of one's own life, as Harry says, is to -escape the suffering of life. I know you are surprised at my talking -to you like this. You have not realized how I have developed. I was a -schoolboy when you knew me. I am a man now. I have new passions, new -thoughts, new ideas. I am different, but you must not like me less. I -am changed, but you must always be my friend. Of course, I am very -fond of Harry. But I know that you are better than he is. You are not -stronger--you are too much afraid of life--but you are better. And how -happy we used to be together! Don't leave me, Basil, and don't quarrel -with me. I am what I am. There is nothing more to be said." - -The painter felt strangely moved. The lad was infinitely dear to him, -and his personality had been the great turning point in his art. He -could not bear the idea of reproaching him any more. After all, his -indifference was probably merely a mood that would pass away. There -was so much in him that was good, so much in him that was noble. - -"Well, Dorian," he said at length, with a sad smile, "I won't speak to -you again about this horrible thing, after to-day. I only trust your -name won't be mentioned in connection with it. The inquest is to take -place this afternoon. Have they summoned you?" - -Dorian shook his head, and a look of annoyance passed over his face at -the mention of the word "inquest." There was something so crude and -vulgar about everything of the kind. "They don't know my name," he -answered. - -"But surely she did?" - -"Only my Christian name, and that I am quite sure she never mentioned -to any one. She told me once that they were all rather curious to -learn who I was, and that she invariably told them my name was Prince -Charming. It was pretty of her. You must do me a drawing of Sibyl, -Basil. I should like to have something more of her than the memory of -a few kisses and some broken pathetic words." - -"I will try and do something, Dorian, if it would please you. But you -must come and sit to me yourself again. I can't get on without you." - -"I can never sit to you again, Basil. It is impossible!" he exclaimed, -starting back. - -The painter stared at him. "My dear boy, what nonsense!" he cried. -"Do you mean to say you don't like what I did of you? Where is it? -Why have you pulled the screen in front of it? Let me look at it. It -is the best thing I have ever done. Do take the screen away, Dorian. -It is simply disgraceful of your servant hiding my work like that. I -felt the room looked different as I came in." - -"My servant has nothing to do with it, Basil. You don't imagine I let -him arrange my room for me? He settles my flowers for me -sometimes--that is all. No; I did it myself. The light was too strong -on the portrait." - -"Too strong! Surely not, my dear fellow? It is an admirable place for -it. Let me see it." And Hallward walked towards the corner of the -room. - -A cry of terror broke from Dorian Gray's lips, and he rushed between -the painter and the screen. "Basil," he said, looking very pale, "you -must not look at it. I don't wish you to." - -"Not look at my own work! You are not serious. Why shouldn't I look -at it?" exclaimed Hallward, laughing. - -"If you try to look at it, Basil, on my word of honour I will never -speak to you again as long as I live. I am quite serious. I don't -offer any explanation, and you are not to ask for any. But, remember, -if you touch this screen, everything is over between us." - -Hallward was thunderstruck. He looked at Dorian Gray in absolute -amazement. He had never seen him like this before. The lad was -actually pallid with rage. His hands were clenched, and the pupils of -his eyes were like disks of blue fire. He was trembling all over. - -"Dorian!" - -"Don't speak!" - -"But what is the matter? Of course I won't look at it if you don't -want me to," he said, rather coldly, turning on his heel and going over -towards the window. "But, really, it seems rather absurd that I -shouldn't see my own work, especially as I am going to exhibit it in -Paris in the autumn. I shall probably have to give it another coat of -varnish before that, so I must see it some day, and why not to-day?" - -"To exhibit it! You want to exhibit it?" exclaimed Dorian Gray, a -strange sense of terror creeping over him. Was the world going to be -shown his secret? Were people to gape at the mystery of his life? -That was impossible. Something--he did not know what--had to be done -at once. - -"Yes; I don't suppose you will object to that. Georges Petit is going -to collect all my best pictures for a special exhibition in the Rue de -Seze, which will open the first week in October. The portrait will -only be away a month. I should think you could easily spare it for -that time. In fact, you are sure to be out of town. And if you keep -it always behind a screen, you can't care much about it." - -Dorian Gray passed his hand over his forehead. There were beads of -perspiration there. He felt that he was on the brink of a horrible -danger. "You told me a month ago that you would never exhibit it," he -cried. "Why have you changed your mind? You people who go in for -being consistent have just as many moods as others have. The only -difference is that your moods are rather meaningless. You can't have -forgotten that you assured me most solemnly that nothing in the world -would induce you to send it to any exhibition. You told Harry exactly -the same thing." He stopped suddenly, and a gleam of light came into -his eyes. He remembered that Lord Henry had said to him once, half -seriously and half in jest, "If you want to have a strange quarter of -an hour, get Basil to tell you why he won't exhibit your picture. He -told me why he wouldn't, and it was a revelation to me." Yes, perhaps -Basil, too, had his secret. He would ask him and try. - -"Basil," he said, coming over quite close and looking him straight in -the face, "we have each of us a secret. Let me know yours, and I shall -tell you mine. What was your reason for refusing to exhibit my -picture?" - -The painter shuddered in spite of himself. "Dorian, if I told you, you -might like me less than you do, and you would certainly laugh at me. I -could not bear your doing either of those two things. If you wish me -never to look at your picture again, I am content. I have always you -to look at. If you wish the best work I have ever done to be hidden -from the world, I am satisfied. Your friendship is dearer to me than -any fame or reputation." - -"No, Basil, you must tell me," insisted Dorian Gray. "I think I have a -right to know." His feeling of terror had passed away, and curiosity -had taken its place. He was determined to find out Basil Hallward's -mystery. - -"Let us sit down, Dorian," said the painter, looking troubled. "Let us -sit down. And just answer me one question. Have you noticed in the -picture something curious?--something that probably at first did not -strike you, but that revealed itself to you suddenly?" - -"Basil!" cried the lad, clutching the arms of his chair with trembling -hands and gazing at him with wild startled eyes. - -"I see you did. Don't speak. Wait till you hear what I have to say. -Dorian, from the moment I met you, your personality had the most -extraordinary influence over me. I was dominated, soul, brain, and -power, by you. You became to me the visible incarnation of that unseen -ideal whose memory haunts us artists like an exquisite dream. I -worshipped you. I grew jealous of every one to whom you spoke. I -wanted to have you all to myself. I was only happy when I was with -you. When you were away from me, you were still present in my art.... -Of course, I never let you know anything about this. It would have -been impossible. You would not have understood it. I hardly -understood it myself. I only knew that I had seen perfection face to -face, and that the world had become wonderful to my eyes--too -wonderful, perhaps, for in such mad worships there is peril, the peril -of losing them, no less than the peril of keeping them.... Weeks and -weeks went on, and I grew more and more absorbed in you. Then came a -new development. I had drawn you as Paris in dainty armour, and as -Adonis with huntsman's cloak and polished boar-spear. Crowned with -heavy lotus-blossoms you had sat on the prow of Adrian's barge, gazing -across the green turbid Nile. You had leaned over the still pool of -some Greek woodland and seen in the water's silent silver the marvel of -your own face. And it had all been what art should be--unconscious, -ideal, and remote. One day, a fatal day I sometimes think, I -determined to paint a wonderful portrait of you as you actually are, -not in the costume of dead ages, but in your own dress and in your own -time. Whether it was the realism of the method, or the mere wonder of -your own personality, thus directly presented to me without mist or -veil, I cannot tell. But I know that as I worked at it, every flake -and film of colour seemed to me to reveal my secret. I grew afraid -that others would know of my idolatry. I felt, Dorian, that I had told -too much, that I had put too much of myself into it. Then it was that -I resolved never to allow the picture to be exhibited. You were a -little annoyed; but then you did not realize all that it meant to me. -Harry, to whom I talked about it, laughed at me. But I did not mind -that. When the picture was finished, and I sat alone with it, I felt -that I was right.... Well, after a few days the thing left my studio, -and as soon as I had got rid of the intolerable fascination of its -presence, it seemed to me that I had been foolish in imagining that I -had seen anything in it, more than that you were extremely good-looking -and that I could paint. Even now I cannot help feeling that it is a -mistake to think that the passion one feels in creation is ever really -shown in the work one creates. Art is always more abstract than we -fancy. Form and colour tell us of form and colour--that is all. It -often seems to me that art conceals the artist far more completely than -it ever reveals him. And so when I got this offer from Paris, I -determined to make your portrait the principal thing in my exhibition. -It never occurred to me that you would refuse. I see now that you were -right. The picture cannot be shown. You must not be angry with me, -Dorian, for what I have told you. As I said to Harry, once, you are -made to be worshipped." - -Dorian Gray drew a long breath. The colour came back to his cheeks, -and a smile played about his lips. The peril was over. He was safe -for the time. Yet he could not help feeling infinite pity for the -painter who had just made this strange confession to him, and wondered -if he himself would ever be so dominated by the personality of a -friend. Lord Henry had the charm of being very dangerous. But that -was all. He was too clever and too cynical to be really fond of. -Would there ever be some one who would fill him with a strange -idolatry? Was that one of the things that life had in store? - -"It is extraordinary to me, Dorian," said Hallward, "that you should -have seen this in the portrait. Did you really see it?" - -"I saw something in it," he answered, "something that seemed to me very -curious." - -"Well, you don't mind my looking at the thing now?" - -Dorian shook his head. "You must not ask me that, Basil. I could not -possibly let you stand in front of that picture." - -"You will some day, surely?" - -"Never." - -"Well, perhaps you are right. And now good-bye, Dorian. You have been -the one person in my life who has really influenced my art. Whatever I -have done that is good, I owe to you. Ah! you don't know what it cost -me to tell you all that I have told you." - -"My dear Basil," said Dorian, "what have you told me? Simply that you -felt that you admired me too much. That is not even a compliment." - -"It was not intended as a compliment. It was a confession. Now that I -have made it, something seems to have gone out of me. Perhaps one -should never put one's worship into words." - -"It was a very disappointing confession." - -"Why, what did you expect, Dorian? You didn't see anything else in the -picture, did you? There was nothing else to see?" - -"No; there was nothing else to see. Why do you ask? But you mustn't -talk about worship. It is foolish. You and I are friends, Basil, and -we must always remain so." - -"You have got Harry," said the painter sadly. - -"Oh, Harry!" cried the lad, with a ripple of laughter. "Harry spends -his days in saying what is incredible and his evenings in doing what is -improbable. Just the sort of life I would like to lead. But still I -don't think I would go to Harry if I were in trouble. I would sooner -go to you, Basil." - -"You will sit to me again?" - -"Impossible!" - -"You spoil my life as an artist by refusing, Dorian. No man comes -across two ideal things. Few come across one." - -"I can't explain it to you, Basil, but I must never sit to you again. -There is something fatal about a portrait. It has a life of its own. -I will come and have tea with you. That will be just as pleasant." - -"Pleasanter for you, I am afraid," murmured Hallward regretfully. "And -now good-bye. I am sorry you won't let me look at the picture once -again. But that can't be helped. I quite understand what you feel -about it." - -As he left the room, Dorian Gray smiled to himself. Poor Basil! How -little he knew of the true reason! And how strange it was that, -instead of having been forced to reveal his own secret, he had -succeeded, almost by chance, in wresting a secret from his friend! How -much that strange confession explained to him! The painter's absurd -fits of jealousy, his wild devotion, his extravagant panegyrics, his -curious reticences--he understood them all now, and he felt sorry. -There seemed to him to be something tragic in a friendship so coloured -by romance. - -He sighed and touched the bell. The portrait must be hidden away at -all costs. He could not run such a risk of discovery again. It had -been mad of him to have allowed the thing to remain, even for an hour, -in a room to which any of his friends had access. - - - -CHAPTER 10 - -When his servant entered, he looked at him steadfastly and wondered if -he had thought of peering behind the screen. The man was quite -impassive and waited for his orders. Dorian lit a cigarette and walked -over to the glass and glanced into it. He could see the reflection of -Victor's face perfectly. It was like a placid mask of servility. -There was nothing to be afraid of, there. Yet he thought it best to be -on his guard. - -Speaking very slowly, he told him to tell the house-keeper that he -wanted to see her, and then to go to the frame-maker and ask him to -send two of his men round at once. It seemed to him that as the man -left the room his eyes wandered in the direction of the screen. Or was -that merely his own fancy? - -After a few moments, in her black silk dress, with old-fashioned thread -mittens on her wrinkled hands, Mrs. Leaf bustled into the library. He -asked her for the key of the schoolroom. - -"The old schoolroom, Mr. Dorian?" she exclaimed. "Why, it is full of -dust. I must get it arranged and put straight before you go into it. -It is not fit for you to see, sir. It is not, indeed." - -"I don't want it put straight, Leaf. I only want the key." - -"Well, sir, you'll be covered with cobwebs if you go into it. Why, it -hasn't been opened for nearly five years--not since his lordship died." - -He winced at the mention of his grandfather. He had hateful memories -of him. "That does not matter," he answered. "I simply want to see -the place--that is all. Give me the key." - -"And here is the key, sir," said the old lady, going over the contents -of her bunch with tremulously uncertain hands. "Here is the key. I'll -have it off the bunch in a moment. But you don't think of living up -there, sir, and you so comfortable here?" - -"No, no," he cried petulantly. "Thank you, Leaf. That will do." - -She lingered for a few moments, and was garrulous over some detail of -the household. He sighed and told her to manage things as she thought -best. She left the room, wreathed in smiles. - -As the door closed, Dorian put the key in his pocket and looked round -the room. His eye fell on a large, purple satin coverlet heavily -embroidered with gold, a splendid piece of late seventeenth-century -Venetian work that his grandfather had found in a convent near Bologna. -Yes, that would serve to wrap the dreadful thing in. It had perhaps -served often as a pall for the dead. Now it was to hide something that -had a corruption of its own, worse than the corruption of death -itself--something that would breed horrors and yet would never die. -What the worm was to the corpse, his sins would be to the painted image -on the canvas. They would mar its beauty and eat away its grace. They -would defile it and make it shameful. And yet the thing would still -live on. It would be always alive. - -He shuddered, and for a moment he regretted that he had not told Basil -the true reason why he had wished to hide the picture away. Basil -would have helped him to resist Lord Henry's influence, and the still -more poisonous influences that came from his own temperament. The love -that he bore him--for it was really love--had nothing in it that was -not noble and intellectual. It was not that mere physical admiration -of beauty that is born of the senses and that dies when the senses -tire. It was such love as Michelangelo had known, and Montaigne, and -Winckelmann, and Shakespeare himself. Yes, Basil could have saved him. -But it was too late now. The past could always be annihilated. -Regret, denial, or forgetfulness could do that. But the future was -inevitable. There were passions in him that would find their terrible -outlet, dreams that would make the shadow of their evil real. - -He took up from the couch the great purple-and-gold texture that -covered it, and, holding it in his hands, passed behind the screen. -Was the face on the canvas viler than before? It seemed to him that it -was unchanged, and yet his loathing of it was intensified. Gold hair, -blue eyes, and rose-red lips--they all were there. It was simply the -expression that had altered. That was horrible in its cruelty. -Compared to what he saw in it of censure or rebuke, how shallow Basil's -reproaches about Sibyl Vane had been!--how shallow, and of what little -account! His own soul was looking out at him from the canvas and -calling him to judgement. A look of pain came across him, and he flung -the rich pall over the picture. As he did so, a knock came to the -door. He passed out as his servant entered. - -"The persons are here, Monsieur." - -He felt that the man must be got rid of at once. He must not be -allowed to know where the picture was being taken to. There was -something sly about him, and he had thoughtful, treacherous eyes. -Sitting down at the writing-table he scribbled a note to Lord Henry, -asking him to send him round something to read and reminding him that -they were to meet at eight-fifteen that evening. - -"Wait for an answer," he said, handing it to him, "and show the men in -here." - -In two or three minutes there was another knock, and Mr. Hubbard -himself, the celebrated frame-maker of South Audley Street, came in -with a somewhat rough-looking young assistant. Mr. Hubbard was a -florid, red-whiskered little man, whose admiration for art was -considerably tempered by the inveterate impecuniosity of most of the -artists who dealt with him. As a rule, he never left his shop. He -waited for people to come to him. But he always made an exception in -favour of Dorian Gray. There was something about Dorian that charmed -everybody. It was a pleasure even to see him. - -"What can I do for you, Mr. Gray?" he said, rubbing his fat freckled -hands. "I thought I would do myself the honour of coming round in -person. I have just got a beauty of a frame, sir. Picked it up at a -sale. Old Florentine. Came from Fonthill, I believe. Admirably -suited for a religious subject, Mr. Gray." - -"I am so sorry you have given yourself the trouble of coming round, Mr. -Hubbard. I shall certainly drop in and look at the frame--though I -don't go in much at present for religious art--but to-day I only want a -picture carried to the top of the house for me. It is rather heavy, so -I thought I would ask you to lend me a couple of your men." - -"No trouble at all, Mr. Gray. I am delighted to be of any service to -you. Which is the work of art, sir?" - -"This," replied Dorian, moving the screen back. "Can you move it, -covering and all, just as it is? I don't want it to get scratched -going upstairs." - -"There will be no difficulty, sir," said the genial frame-maker, -beginning, with the aid of his assistant, to unhook the picture from -the long brass chains by which it was suspended. "And, now, where -shall we carry it to, Mr. Gray?" - -"I will show you the way, Mr. Hubbard, if you will kindly follow me. -Or perhaps you had better go in front. I am afraid it is right at the -top of the house. We will go up by the front staircase, as it is -wider." - -He held the door open for them, and they passed out into the hall and -began the ascent. The elaborate character of the frame had made the -picture extremely bulky, and now and then, in spite of the obsequious -protests of Mr. Hubbard, who had the true tradesman's spirited dislike -of seeing a gentleman doing anything useful, Dorian put his hand to it -so as to help them. - -"Something of a load to carry, sir," gasped the little man when they -reached the top landing. And he wiped his shiny forehead. - -"I am afraid it is rather heavy," murmured Dorian as he unlocked the -door that opened into the room that was to keep for him the curious -secret of his life and hide his soul from the eyes of men. - -He had not entered the place for more than four years--not, indeed, -since he had used it first as a play-room when he was a child, and then -as a study when he grew somewhat older. It was a large, -well-proportioned room, which had been specially built by the last Lord -Kelso for the use of the little grandson whom, for his strange likeness -to his mother, and also for other reasons, he had always hated and -desired to keep at a distance. It appeared to Dorian to have but -little changed. There was the huge Italian _cassone_, with its -fantastically painted panels and its tarnished gilt mouldings, in which -he had so often hidden himself as a boy. There the satinwood book-case -filled with his dog-eared schoolbooks. On the wall behind it was -hanging the same ragged Flemish tapestry where a faded king and queen -were playing chess in a garden, while a company of hawkers rode by, -carrying hooded birds on their gauntleted wrists. How well he -remembered it all! Every moment of his lonely childhood came back to -him as he looked round. He recalled the stainless purity of his boyish -life, and it seemed horrible to him that it was here the fatal portrait -was to be hidden away. How little he had thought, in those dead days, -of all that was in store for him! - -But there was no other place in the house so secure from prying eyes as -this. He had the key, and no one else could enter it. Beneath its -purple pall, the face painted on the canvas could grow bestial, sodden, -and unclean. What did it matter? No one could see it. He himself -would not see it. Why should he watch the hideous corruption of his -soul? He kept his youth--that was enough. And, besides, might not -his nature grow finer, after all? There was no reason that the future -should be so full of shame. Some love might come across his life, and -purify him, and shield him from those sins that seemed to be already -stirring in spirit and in flesh--those curious unpictured sins whose -very mystery lent them their subtlety and their charm. Perhaps, some -day, the cruel look would have passed away from the scarlet sensitive -mouth, and he might show to the world Basil Hallward's masterpiece. - -No; that was impossible. Hour by hour, and week by week, the thing -upon the canvas was growing old. It might escape the hideousness of -sin, but the hideousness of age was in store for it. The cheeks would -become hollow or flaccid. Yellow crow's feet would creep round the -fading eyes and make them horrible. The hair would lose its -brightness, the mouth would gape or droop, would be foolish or gross, -as the mouths of old men are. There would be the wrinkled throat, the -cold, blue-veined hands, the twisted body, that he remembered in the -grandfather who had been so stern to him in his boyhood. The picture -had to be concealed. There was no help for it. - -"Bring it in, Mr. Hubbard, please," he said, wearily, turning round. -"I am sorry I kept you so long. I was thinking of something else." - -"Always glad to have a rest, Mr. Gray," answered the frame-maker, who -was still gasping for breath. "Where shall we put it, sir?" - -"Oh, anywhere. Here: this will do. I don't want to have it hung up. -Just lean it against the wall. Thanks." - -"Might one look at the work of art, sir?" - -Dorian started. "It would not interest you, Mr. Hubbard," he said, -keeping his eye on the man. He felt ready to leap upon him and fling -him to the ground if he dared to lift the gorgeous hanging that -concealed the secret of his life. "I shan't trouble you any more now. -I am much obliged for your kindness in coming round." - -"Not at all, not at all, Mr. Gray. Ever ready to do anything for you, -sir." And Mr. Hubbard tramped downstairs, followed by the assistant, -who glanced back at Dorian with a look of shy wonder in his rough -uncomely face. He had never seen any one so marvellous. - -When the sound of their footsteps had died away, Dorian locked the door -and put the key in his pocket. He felt safe now. No one would ever -look upon the horrible thing. No eye but his would ever see his shame. - -On reaching the library, he found that it was just after five o'clock -and that the tea had been already brought up. On a little table of -dark perfumed wood thickly incrusted with nacre, a present from Lady -Radley, his guardian's wife, a pretty professional invalid who had -spent the preceding winter in Cairo, was lying a note from Lord Henry, -and beside it was a book bound in yellow paper, the cover slightly torn -and the edges soiled. A copy of the third edition of _The St. James's -Gazette_ had been placed on the tea-tray. It was evident that Victor had -returned. He wondered if he had met the men in the hall as they were -leaving the house and had wormed out of them what they had been doing. -He would be sure to miss the picture--had no doubt missed it already, -while he had been laying the tea-things. The screen had not been set -back, and a blank space was visible on the wall. Perhaps some night he -might find him creeping upstairs and trying to force the door of the -room. It was a horrible thing to have a spy in one's house. He had -heard of rich men who had been blackmailed all their lives by some -servant who had read a letter, or overheard a conversation, or picked -up a card with an address, or found beneath a pillow a withered flower -or a shred of crumpled lace. - -He sighed, and having poured himself out some tea, opened Lord Henry's -note. It was simply to say that he sent him round the evening paper, -and a book that might interest him, and that he would be at the club at -eight-fifteen. He opened _The St. James's_ languidly, and looked through -it. A red pencil-mark on the fifth page caught his eye. It drew -attention to the following paragraph: - - -INQUEST ON AN ACTRESS.--An inquest was held this morning at the Bell -Tavern, Hoxton Road, by Mr. Danby, the District Coroner, on the body of -Sibyl Vane, a young actress recently engaged at the Royal Theatre, -Holborn. A verdict of death by misadventure was returned. -Considerable sympathy was expressed for the mother of the deceased, who -was greatly affected during the giving of her own evidence, and that of -Dr. Birrell, who had made the post-mortem examination of the deceased. - - -He frowned, and tearing the paper in two, went across the room and -flung the pieces away. How ugly it all was! And how horribly real -ugliness made things! He felt a little annoyed with Lord Henry for -having sent him the report. And it was certainly stupid of him to have -marked it with red pencil. Victor might have read it. The man knew -more than enough English for that. - -Perhaps he had read it and had begun to suspect something. And, yet, -what did it matter? What had Dorian Gray to do with Sibyl Vane's -death? There was nothing to fear. Dorian Gray had not killed her. - -His eye fell on the yellow book that Lord Henry had sent him. What was -it, he wondered. He went towards the little, pearl-coloured octagonal -stand that had always looked to him like the work of some strange -Egyptian bees that wrought in silver, and taking up the volume, flung -himself into an arm-chair and began to turn over the leaves. After a -few minutes he became absorbed. It was the strangest book that he had -ever read. It seemed to him that in exquisite raiment, and to the -delicate sound of flutes, the sins of the world were passing in dumb -show before him. Things that he had dimly dreamed of were suddenly -made real to him. Things of which he had never dreamed were gradually -revealed. - -It was a novel without a plot and with only one character, being, -indeed, simply a psychological study of a certain young Parisian who -spent his life trying to realize in the nineteenth century all the -passions and modes of thought that belonged to every century except his -own, and to sum up, as it were, in himself the various moods through -which the world-spirit had ever passed, loving for their mere -artificiality those renunciations that men have unwisely called virtue, -as much as those natural rebellions that wise men still call sin. The -style in which it was written was that curious jewelled style, vivid -and obscure at once, full of _argot_ and of archaisms, of technical -expressions and of elaborate paraphrases, that characterizes the work -of some of the finest artists of the French school of _Symbolistes_. -There were in it metaphors as monstrous as orchids and as subtle in -colour. The life of the senses was described in the terms of mystical -philosophy. One hardly knew at times whether one was reading the -spiritual ecstasies of some mediaeval saint or the morbid confessions -of a modern sinner. It was a poisonous book. The heavy odour of -incense seemed to cling about its pages and to trouble the brain. The -mere cadence of the sentences, the subtle monotony of their music, so -full as it was of complex refrains and movements elaborately repeated, -produced in the mind of the lad, as he passed from chapter to chapter, -a form of reverie, a malady of dreaming, that made him unconscious of -the falling day and creeping shadows. - -Cloudless, and pierced by one solitary star, a copper-green sky gleamed -through the windows. He read on by its wan light till he could read no -more. Then, after his valet had reminded him several times of the -lateness of the hour, he got up, and going into the next room, placed -the book on the little Florentine table that always stood at his -bedside and began to dress for dinner. - -It was almost nine o'clock before he reached the club, where he found -Lord Henry sitting alone, in the morning-room, looking very much bored. - -"I am so sorry, Harry," he cried, "but really it is entirely your -fault. That book you sent me so fascinated me that I forgot how the -time was going." - -"Yes, I thought you would like it," replied his host, rising from his -chair. - -"I didn't say I liked it, Harry. I said it fascinated me. There is a -great difference." - -"Ah, you have discovered that?" murmured Lord Henry. And they passed -into the dining-room. - - - -CHAPTER 11 - -For years, Dorian Gray could not free himself from the influence of -this book. Or perhaps it would be more accurate to say that he never -sought to free himself from it. He procured from Paris no less than -nine large-paper copies of the first edition, and had them bound in -different colours, so that they might suit his various moods and the -changing fancies of a nature over which he seemed, at times, to have -almost entirely lost control. The hero, the wonderful young Parisian -in whom the romantic and the scientific temperaments were so strangely -blended, became to him a kind of prefiguring type of himself. And, -indeed, the whole book seemed to him to contain the story of his own -life, written before he had lived it. - -In one point he was more fortunate than the novel's fantastic hero. He -never knew--never, indeed, had any cause to know--that somewhat -grotesque dread of mirrors, and polished metal surfaces, and still -water which came upon the young Parisian so early in his life, and was -occasioned by the sudden decay of a beau that had once, apparently, -been so remarkable. It was with an almost cruel joy--and perhaps in -nearly every joy, as certainly in every pleasure, cruelty has its -place--that he used to read the latter part of the book, with its -really tragic, if somewhat overemphasized, account of the sorrow and -despair of one who had himself lost what in others, and the world, he -had most dearly valued. - -For the wonderful beauty that had so fascinated Basil Hallward, and -many others besides him, seemed never to leave him. Even those who had -heard the most evil things against him--and from time to time strange -rumours about his mode of life crept through London and became the -chatter of the clubs--could not believe anything to his dishonour when -they saw him. He had always the look of one who had kept himself -unspotted from the world. Men who talked grossly became silent when -Dorian Gray entered the room. There was something in the purity of his -face that rebuked them. His mere presence seemed to recall to them the -memory of the innocence that they had tarnished. They wondered how one -so charming and graceful as he was could have escaped the stain of an -age that was at once sordid and sensual. - -Often, on returning home from one of those mysterious and prolonged -absences that gave rise to such strange conjecture among those who were -his friends, or thought that they were so, he himself would creep -upstairs to the locked room, open the door with the key that never left -him now, and stand, with a mirror, in front of the portrait that Basil -Hallward had painted of him, looking now at the evil and aging face on -the canvas, and now at the fair young face that laughed back at him -from the polished glass. The very sharpness of the contrast used to -quicken his sense of pleasure. He grew more and more enamoured of his -own beauty, more and more interested in the corruption of his own soul. -He would examine with minute care, and sometimes with a monstrous and -terrible delight, the hideous lines that seared the wrinkling forehead -or crawled around the heavy sensual mouth, wondering sometimes which -were the more horrible, the signs of sin or the signs of age. He would -place his white hands beside the coarse bloated hands of the picture, -and smile. He mocked the misshapen body and the failing limbs. - -There were moments, indeed, at night, when, lying sleepless in his own -delicately scented chamber, or in the sordid room of the little -ill-famed tavern near the docks which, under an assumed name and in -disguise, it was his habit to frequent, he would think of the ruin he -had brought upon his soul with a pity that was all the more poignant -because it was purely selfish. But moments such as these were rare. -That curiosity about life which Lord Henry had first stirred in him, as -they sat together in the garden of their friend, seemed to increase -with gratification. The more he knew, the more he desired to know. He -had mad hungers that grew more ravenous as he fed them. - -Yet he was not really reckless, at any rate in his relations to -society. Once or twice every month during the winter, and on each -Wednesday evening while the season lasted, he would throw open to the -world his beautiful house and have the most celebrated musicians of the -day to charm his guests with the wonders of their art. His little -dinners, in the settling of which Lord Henry always assisted him, were -noted as much for the careful selection and placing of those invited, -as for the exquisite taste shown in the decoration of the table, with -its subtle symphonic arrangements of exotic flowers, and embroidered -cloths, and antique plate of gold and silver. Indeed, there were many, -especially among the very young men, who saw, or fancied that they saw, -in Dorian Gray the true realization of a type of which they had often -dreamed in Eton or Oxford days, a type that was to combine something of -the real culture of the scholar with all the grace and distinction and -perfect manner of a citizen of the world. To them he seemed to be of -the company of those whom Dante describes as having sought to "make -themselves perfect by the worship of beauty." Like Gautier, he was one -for whom "the visible world existed." - -And, certainly, to him life itself was the first, the greatest, of the -arts, and for it all the other arts seemed to be but a preparation. -Fashion, by which what is really fantastic becomes for a moment -universal, and dandyism, which, in its own way, is an attempt to assert -the absolute modernity of beauty, had, of course, their fascination for -him. His mode of dressing, and the particular styles that from time to -time he affected, had their marked influence on the young exquisites of -the Mayfair balls and Pall Mall club windows, who copied him in -everything that he did, and tried to reproduce the accidental charm of -his graceful, though to him only half-serious, fopperies. - -For, while he was but too ready to accept the position that was almost -immediately offered to him on his coming of age, and found, indeed, a -subtle pleasure in the thought that he might really become to the -London of his own day what to imperial Neronian Rome the author of the -Satyricon once had been, yet in his inmost heart he desired to be -something more than a mere _arbiter elegantiarum_, to be consulted on the -wearing of a jewel, or the knotting of a necktie, or the conduct of a -cane. He sought to elaborate some new scheme of life that would have -its reasoned philosophy and its ordered principles, and find in the -spiritualizing of the senses its highest realization. - -The worship of the senses has often, and with much justice, been -decried, men feeling a natural instinct of terror about passions and -sensations that seem stronger than themselves, and that they are -conscious of sharing with the less highly organized forms of existence. -But it appeared to Dorian Gray that the true nature of the senses had -never been understood, and that they had remained savage and animal -merely because the world had sought to starve them into submission or -to kill them by pain, instead of aiming at making them elements of a -new spirituality, of which a fine instinct for beauty was to be the -dominant characteristic. As he looked back upon man moving through -history, he was haunted by a feeling of loss. So much had been -surrendered! and to such little purpose! There had been mad wilful -rejections, monstrous forms of self-torture and self-denial, whose -origin was fear and whose result was a degradation infinitely more -terrible than that fancied degradation from which, in their ignorance, -they had sought to escape; Nature, in her wonderful irony, driving out -the anchorite to feed with the wild animals of the desert and giving to -the hermit the beasts of the field as his companions. - -Yes: there was to be, as Lord Henry had prophesied, a new Hedonism -that was to recreate life and to save it from that harsh uncomely -puritanism that is having, in our own day, its curious revival. It was -to have its service of the intellect, certainly, yet it was never to -accept any theory or system that would involve the sacrifice of any -mode of passionate experience. Its aim, indeed, was to be experience -itself, and not the fruits of experience, sweet or bitter as they might -be. Of the asceticism that deadens the senses, as of the vulgar -profligacy that dulls them, it was to know nothing. But it was to -teach man to concentrate himself upon the moments of a life that is -itself but a moment. - -There are few of us who have not sometimes wakened before dawn, either -after one of those dreamless nights that make us almost enamoured of -death, or one of those nights of horror and misshapen joy, when through -the chambers of the brain sweep phantoms more terrible than reality -itself, and instinct with that vivid life that lurks in all grotesques, -and that lends to Gothic art its enduring vitality, this art being, one -might fancy, especially the art of those whose minds have been troubled -with the malady of reverie. Gradually white fingers creep through the -curtains, and they appear to tremble. In black fantastic shapes, dumb -shadows crawl into the corners of the room and crouch there. Outside, -there is the stirring of birds among the leaves, or the sound of men -going forth to their work, or the sigh and sob of the wind coming down -from the hills and wandering round the silent house, as though it -feared to wake the sleepers and yet must needs call forth sleep from -her purple cave. Veil after veil of thin dusky gauze is lifted, and by -degrees the forms and colours of things are restored to them, and we -watch the dawn remaking the world in its antique pattern. The wan -mirrors get back their mimic life. The flameless tapers stand where we -had left them, and beside them lies the half-cut book that we had been -studying, or the wired flower that we had worn at the ball, or the -letter that we had been afraid to read, or that we had read too often. -Nothing seems to us changed. Out of the unreal shadows of the night -comes back the real life that we had known. We have to resume it where -we had left off, and there steals over us a terrible sense of the -necessity for the continuance of energy in the same wearisome round of -stereotyped habits, or a wild longing, it may be, that our eyelids -might open some morning upon a world that had been refashioned anew in -the darkness for our pleasure, a world in which things would have fresh -shapes and colours, and be changed, or have other secrets, a world in -which the past would have little or no place, or survive, at any rate, -in no conscious form of obligation or regret, the remembrance even of -joy having its bitterness and the memories of pleasure their pain. - -It was the creation of such worlds as these that seemed to Dorian Gray -to be the true object, or amongst the true objects, of life; and in his -search for sensations that would be at once new and delightful, and -possess that element of strangeness that is so essential to romance, he -would often adopt certain modes of thought that he knew to be really -alien to his nature, abandon himself to their subtle influences, and -then, having, as it were, caught their colour and satisfied his -intellectual curiosity, leave them with that curious indifference that -is not incompatible with a real ardour of temperament, and that, -indeed, according to certain modern psychologists, is often a condition -of it. - -It was rumoured of him once that he was about to join the Roman -Catholic communion, and certainly the Roman ritual had always a great -attraction for him. The daily sacrifice, more awful really than all -the sacrifices of the antique world, stirred him as much by its superb -rejection of the evidence of the senses as by the primitive simplicity -of its elements and the eternal pathos of the human tragedy that it -sought to symbolize. He loved to kneel down on the cold marble -pavement and watch the priest, in his stiff flowered dalmatic, slowly -and with white hands moving aside the veil of the tabernacle, or -raising aloft the jewelled, lantern-shaped monstrance with that pallid -wafer that at times, one would fain think, is indeed the "_panis -caelestis_," the bread of angels, or, robed in the garments of the -Passion of Christ, breaking the Host into the chalice and smiting his -breast for his sins. The fuming censers that the grave boys, in their -lace and scarlet, tossed into the air like great gilt flowers had their -subtle fascination for him. As he passed out, he used to look with -wonder at the black confessionals and long to sit in the dim shadow of -one of them and listen to men and women whispering through the worn -grating the true story of their lives. - -But he never fell into the error of arresting his intellectual -development by any formal acceptance of creed or system, or of -mistaking, for a house in which to live, an inn that is but suitable -for the sojourn of a night, or for a few hours of a night in which -there are no stars and the moon is in travail. Mysticism, with its -marvellous power of making common things strange to us, and the subtle -antinomianism that always seems to accompany it, moved him for a -season; and for a season he inclined to the materialistic doctrines of -the _Darwinismus_ movement in Germany, and found a curious pleasure in -tracing the thoughts and passions of men to some pearly cell in the -brain, or some white nerve in the body, delighting in the conception of -the absolute dependence of the spirit on certain physical conditions, -morbid or healthy, normal or diseased. Yet, as has been said of him -before, no theory of life seemed to him to be of any importance -compared with life itself. He felt keenly conscious of how barren all -intellectual speculation is when separated from action and experiment. -He knew that the senses, no less than the soul, have their spiritual -mysteries to reveal. - -And so he would now study perfumes and the secrets of their -manufacture, distilling heavily scented oils and burning odorous gums -from the East. He saw that there was no mood of the mind that had not -its counterpart in the sensuous life, and set himself to discover their -true relations, wondering what there was in frankincense that made one -mystical, and in ambergris that stirred one's passions, and in violets -that woke the memory of dead romances, and in musk that troubled the -brain, and in champak that stained the imagination; and seeking often -to elaborate a real psychology of perfumes, and to estimate the several -influences of sweet-smelling roots and scented, pollen-laden flowers; -of aromatic balms and of dark and fragrant woods; of spikenard, that -sickens; of hovenia, that makes men mad; and of aloes, that are said to -be able to expel melancholy from the soul. - -At another time he devoted himself entirely to music, and in a long -latticed room, with a vermilion-and-gold ceiling and walls of -olive-green lacquer, he used to give curious concerts in which mad -gipsies tore wild music from little zithers, or grave, yellow-shawled -Tunisians plucked at the strained strings of monstrous lutes, while -grinning Negroes beat monotonously upon copper drums and, crouching -upon scarlet mats, slim turbaned Indians blew through long pipes of -reed or brass and charmed--or feigned to charm--great hooded snakes and -horrible horned adders. The harsh intervals and shrill discords of -barbaric music stirred him at times when Schubert's grace, and Chopin's -beautiful sorrows, and the mighty harmonies of Beethoven himself, fell -unheeded on his ear. He collected together from all parts of the world -the strangest instruments that could be found, either in the tombs of -dead nations or among the few savage tribes that have survived contact -with Western civilizations, and loved to touch and try them. He had -the mysterious _juruparis_ of the Rio Negro Indians, that women are not -allowed to look at and that even youths may not see till they have been -subjected to fasting and scourging, and the earthen jars of the -Peruvians that have the shrill cries of birds, and flutes of human -bones such as Alfonso de Ovalle heard in Chile, and the sonorous green -jaspers that are found near Cuzco and give forth a note of singular -sweetness. He had painted gourds filled with pebbles that rattled when -they were shaken; the long _clarin_ of the Mexicans, into which the -performer does not blow, but through which he inhales the air; the -harsh _ture_ of the Amazon tribes, that is sounded by the sentinels who -sit all day long in high trees, and can be heard, it is said, at a -distance of three leagues; the _teponaztli_, that has two vibrating -tongues of wood and is beaten with sticks that are smeared with an -elastic gum obtained from the milky juice of plants; the _yotl_-bells of -the Aztecs, that are hung in clusters like grapes; and a huge -cylindrical drum, covered with the skins of great serpents, like the -one that Bernal Diaz saw when he went with Cortes into the Mexican -temple, and of whose doleful sound he has left us so vivid a -description. The fantastic character of these instruments fascinated -him, and he felt a curious delight in the thought that art, like -Nature, has her monsters, things of bestial shape and with hideous -voices. Yet, after some time, he wearied of them, and would sit in his -box at the opera, either alone or with Lord Henry, listening in rapt -pleasure to "Tannhauser" and seeing in the prelude to that great work -of art a presentation of the tragedy of his own soul. - -On one occasion he took up the study of jewels, and appeared at a -costume ball as Anne de Joyeuse, Admiral of France, in a dress covered -with five hundred and sixty pearls. This taste enthralled him for -years, and, indeed, may be said never to have left him. He would often -spend a whole day settling and resettling in their cases the various -stones that he had collected, such as the olive-green chrysoberyl that -turns red by lamplight, the cymophane with its wirelike line of silver, -the pistachio-coloured peridot, rose-pink and wine-yellow topazes, -carbuncles of fiery scarlet with tremulous, four-rayed stars, flame-red -cinnamon-stones, orange and violet spinels, and amethysts with their -alternate layers of ruby and sapphire. He loved the red gold of the -sunstone, and the moonstone's pearly whiteness, and the broken rainbow -of the milky opal. He procured from Amsterdam three emeralds of -extraordinary size and richness of colour, and had a turquoise _de la -vieille roche_ that was the envy of all the connoisseurs. - -He discovered wonderful stories, also, about jewels. In Alphonso's -Clericalis Disciplina a serpent was mentioned with eyes of real -jacinth, and in the romantic history of Alexander, the Conqueror of -Emathia was said to have found in the vale of Jordan snakes "with -collars of real emeralds growing on their backs." There was a gem in -the brain of the dragon, Philostratus told us, and "by the exhibition -of golden letters and a scarlet robe" the monster could be thrown into -a magical sleep and slain. According to the great alchemist, Pierre de -Boniface, the diamond rendered a man invisible, and the agate of India -made him eloquent. The cornelian appeased anger, and the hyacinth -provoked sleep, and the amethyst drove away the fumes of wine. The -garnet cast out demons, and the hydropicus deprived the moon of her -colour. The selenite waxed and waned with the moon, and the meloceus, -that discovers thieves, could be affected only by the blood of kids. -Leonardus Camillus had seen a white stone taken from the brain of a -newly killed toad, that was a certain antidote against poison. The -bezoar, that was found in the heart of the Arabian deer, was a charm -that could cure the plague. In the nests of Arabian birds was the -aspilates, that, according to Democritus, kept the wearer from any -danger by fire. - -The King of Ceilan rode through his city with a large ruby in his hand, -as the ceremony of his coronation. The gates of the palace of John the -Priest were "made of sardius, with the horn of the horned snake -inwrought, so that no man might bring poison within." Over the gable -were "two golden apples, in which were two carbuncles," so that the -gold might shine by day and the carbuncles by night. In Lodge's -strange romance 'A Margarite of America', it was stated that in the -chamber of the queen one could behold "all the chaste ladies of the -world, inchased out of silver, looking through fair mirrours of -chrysolites, carbuncles, sapphires, and greene emeraults." Marco Polo -had seen the inhabitants of Zipangu place rose-coloured pearls in the -mouths of the dead. A sea-monster had been enamoured of the pearl that -the diver brought to King Perozes, and had slain the thief, and mourned -for seven moons over its loss. When the Huns lured the king into the -great pit, he flung it away--Procopius tells the story--nor was it ever -found again, though the Emperor Anastasius offered five hundred-weight -of gold pieces for it. The King of Malabar had shown to a certain -Venetian a rosary of three hundred and four pearls, one for every god -that he worshipped. - -When the Duke de Valentinois, son of Alexander VI, visited Louis XII of -France, his horse was loaded with gold leaves, according to Brantome, -and his cap had double rows of rubies that threw out a great light. -Charles of England had ridden in stirrups hung with four hundred and -twenty-one diamonds. Richard II had a coat, valued at thirty thousand -marks, which was covered with balas rubies. Hall described Henry VIII, -on his way to the Tower previous to his coronation, as wearing "a -jacket of raised gold, the placard embroidered with diamonds and other -rich stones, and a great bauderike about his neck of large balasses." -The favourites of James I wore ear-rings of emeralds set in gold -filigrane. Edward II gave to Piers Gaveston a suit of red-gold armour -studded with jacinths, a collar of gold roses set with -turquoise-stones, and a skull-cap _parseme_ with pearls. Henry II wore -jewelled gloves reaching to the elbow, and had a hawk-glove sewn with -twelve rubies and fifty-two great orients. The ducal hat of Charles -the Rash, the last Duke of Burgundy of his race, was hung with -pear-shaped pearls and studded with sapphires. - -How exquisite life had once been! How gorgeous in its pomp and -decoration! Even to read of the luxury of the dead was wonderful. - -Then he turned his attention to embroideries and to the tapestries that -performed the office of frescoes in the chill rooms of the northern -nations of Europe. As he investigated the subject--and he always had -an extraordinary faculty of becoming absolutely absorbed for the moment -in whatever he took up--he was almost saddened by the reflection of the -ruin that time brought on beautiful and wonderful things. He, at any -rate, had escaped that. Summer followed summer, and the yellow -jonquils bloomed and died many times, and nights of horror repeated the -story of their shame, but he was unchanged. No winter marred his face -or stained his flowerlike bloom. How different it was with material -things! Where had they passed to? Where was the great crocus-coloured -robe, on which the gods fought against the giants, that had been worked -by brown girls for the pleasure of Athena? Where the huge velarium -that Nero had stretched across the Colosseum at Rome, that Titan sail -of purple on which was represented the starry sky, and Apollo driving a -chariot drawn by white, gilt-reined steeds? He longed to see the -curious table-napkins wrought for the Priest of the Sun, on which were -displayed all the dainties and viands that could be wanted for a feast; -the mortuary cloth of King Chilperic, with its three hundred golden -bees; the fantastic robes that excited the indignation of the Bishop of -Pontus and were figured with "lions, panthers, bears, dogs, forests, -rocks, hunters--all, in fact, that a painter can copy from nature"; and -the coat that Charles of Orleans once wore, on the sleeves of which -were embroidered the verses of a song beginning "_Madame, je suis tout -joyeux_," the musical accompaniment of the words being wrought in gold -thread, and each note, of square shape in those days, formed with four -pearls. He read of the room that was prepared at the palace at Rheims -for the use of Queen Joan of Burgundy and was decorated with "thirteen -hundred and twenty-one parrots, made in broidery, and blazoned with the -king's arms, and five hundred and sixty-one butterflies, whose wings -were similarly ornamented with the arms of the queen, the whole worked -in gold." Catherine de Medicis had a mourning-bed made for her of -black velvet powdered with crescents and suns. Its curtains were of -damask, with leafy wreaths and garlands, figured upon a gold and silver -ground, and fringed along the edges with broideries of pearls, and it -stood in a room hung with rows of the queen's devices in cut black -velvet upon cloth of silver. Louis XIV had gold embroidered caryatides -fifteen feet high in his apartment. The state bed of Sobieski, King of -Poland, was made of Smyrna gold brocade embroidered in turquoises with -verses from the Koran. Its supports were of silver gilt, beautifully -chased, and profusely set with enamelled and jewelled medallions. It -had been taken from the Turkish camp before Vienna, and the standard of -Mohammed had stood beneath the tremulous gilt of its canopy. - -And so, for a whole year, he sought to accumulate the most exquisite -specimens that he could find of textile and embroidered work, getting -the dainty Delhi muslins, finely wrought with gold-thread palmates and -stitched over with iridescent beetles' wings; the Dacca gauzes, that -from their transparency are known in the East as "woven air," and -"running water," and "evening dew"; strange figured cloths from Java; -elaborate yellow Chinese hangings; books bound in tawny satins or fair -blue silks and wrought with _fleurs-de-lis_, birds and images; veils of -_lacis_ worked in Hungary point; Sicilian brocades and stiff Spanish -velvets; Georgian work, with its gilt coins, and Japanese _Foukousas_, -with their green-toned golds and their marvellously plumaged birds. - -He had a special passion, also, for ecclesiastical vestments, as indeed -he had for everything connected with the service of the Church. In the -long cedar chests that lined the west gallery of his house, he had -stored away many rare and beautiful specimens of what is really the -raiment of the Bride of Christ, who must wear purple and jewels and -fine linen that she may hide the pallid macerated body that is worn by -the suffering that she seeks for and wounded by self-inflicted pain. -He possessed a gorgeous cope of crimson silk and gold-thread damask, -figured with a repeating pattern of golden pomegranates set in -six-petalled formal blossoms, beyond which on either side was the -pine-apple device wrought in seed-pearls. The orphreys were divided -into panels representing scenes from the life of the Virgin, and the -coronation of the Virgin was figured in coloured silks upon the hood. -This was Italian work of the fifteenth century. Another cope was of -green velvet, embroidered with heart-shaped groups of acanthus-leaves, -from which spread long-stemmed white blossoms, the details of which -were picked out with silver thread and coloured crystals. The morse -bore a seraph's head in gold-thread raised work. The orphreys were -woven in a diaper of red and gold silk, and were starred with -medallions of many saints and martyrs, among whom was St. Sebastian. -He had chasubles, also, of amber-coloured silk, and blue silk and gold -brocade, and yellow silk damask and cloth of gold, figured with -representations of the Passion and Crucifixion of Christ, and -embroidered with lions and peacocks and other emblems; dalmatics of -white satin and pink silk damask, decorated with tulips and dolphins -and _fleurs-de-lis_; altar frontals of crimson velvet and blue linen; and -many corporals, chalice-veils, and sudaria. In the mystic offices to -which such things were put, there was something that quickened his -imagination. - -For these treasures, and everything that he collected in his lovely -house, were to be to him means of forgetfulness, modes by which he -could escape, for a season, from the fear that seemed to him at times -to be almost too great to be borne. Upon the walls of the lonely -locked room where he had spent so much of his boyhood, he had hung with -his own hands the terrible portrait whose changing features showed him -the real degradation of his life, and in front of it had draped the -purple-and-gold pall as a curtain. For weeks he would not go there, -would forget the hideous painted thing, and get back his light heart, -his wonderful joyousness, his passionate absorption in mere existence. -Then, suddenly, some night he would creep out of the house, go down to -dreadful places near Blue Gate Fields, and stay there, day after day, -until he was driven away. On his return he would sit in front of the -picture, sometimes loathing it and himself, but filled, at other -times, with that pride of individualism that is half the -fascination of sin, and smiling with secret pleasure at the misshapen -shadow that had to bear the burden that should have been his own. - -After a few years he could not endure to be long out of England, and -gave up the villa that he had shared at Trouville with Lord Henry, as -well as the little white walled-in house at Algiers where they had more -than once spent the winter. He hated to be separated from the picture -that was such a part of his life, and was also afraid that during his -absence some one might gain access to the room, in spite of the -elaborate bars that he had caused to be placed upon the door. - -He was quite conscious that this would tell them nothing. It was true -that the portrait still preserved, under all the foulness and ugliness -of the face, its marked likeness to himself; but what could they learn -from that? He would laugh at any one who tried to taunt him. He had -not painted it. What was it to him how vile and full of shame it -looked? Even if he told them, would they believe it? - -Yet he was afraid. Sometimes when he was down at his great house in -Nottinghamshire, entertaining the fashionable young men of his own rank -who were his chief companions, and astounding the county by the wanton -luxury and gorgeous splendour of his mode of life, he would suddenly -leave his guests and rush back to town to see that the door had not -been tampered with and that the picture was still there. What if it -should be stolen? The mere thought made him cold with horror. Surely -the world would know his secret then. Perhaps the world already -suspected it. - -For, while he fascinated many, there were not a few who distrusted him. -He was very nearly blackballed at a West End club of which his birth -and social position fully entitled him to become a member, and it was -said that on one occasion, when he was brought by a friend into the -smoking-room of the Churchill, the Duke of Berwick and another -gentleman got up in a marked manner and went out. Curious stories -became current about him after he had passed his twenty-fifth year. It -was rumoured that he had been seen brawling with foreign sailors in a -low den in the distant parts of Whitechapel, and that he consorted with -thieves and coiners and knew the mysteries of their trade. His -extraordinary absences became notorious, and, when he used to reappear -again in society, men would whisper to each other in corners, or pass -him with a sneer, or look at him with cold searching eyes, as though -they were determined to discover his secret. - -Of such insolences and attempted slights he, of course, took no notice, -and in the opinion of most people his frank debonair manner, his -charming boyish smile, and the infinite grace of that wonderful youth -that seemed never to leave him, were in themselves a sufficient answer -to the calumnies, for so they termed them, that were circulated about -him. It was remarked, however, that some of those who had been most -intimate with him appeared, after a time, to shun him. Women who had -wildly adored him, and for his sake had braved all social censure and -set convention at defiance, were seen to grow pallid with shame or -horror if Dorian Gray entered the room. - -Yet these whispered scandals only increased in the eyes of many his -strange and dangerous charm. His great wealth was a certain element of -security. Society--civilized society, at least--is never very ready to -believe anything to the detriment of those who are both rich and -fascinating. It feels instinctively that manners are of more -importance than morals, and, in its opinion, the highest respectability -is of much less value than the possession of a good _chef_. And, after -all, it is a very poor consolation to be told that the man who has -given one a bad dinner, or poor wine, is irreproachable in his private -life. Even the cardinal virtues cannot atone for half-cold _entrees_, as -Lord Henry remarked once, in a discussion on the subject, and there is -possibly a good deal to be said for his view. For the canons of good -society are, or should be, the same as the canons of art. Form is -absolutely essential to it. It should have the dignity of a ceremony, -as well as its unreality, and should combine the insincere character of -a romantic play with the wit and beauty that make such plays delightful -to us. Is insincerity such a terrible thing? I think not. It is -merely a method by which we can multiply our personalities. - -Such, at any rate, was Dorian Gray's opinion. He used to wonder at the -shallow psychology of those who conceive the ego in man as a thing -simple, permanent, reliable, and of one essence. To him, man was a -being with myriad lives and myriad sensations, a complex multiform -creature that bore within itself strange legacies of thought and -passion, and whose very flesh was tainted with the monstrous maladies -of the dead. He loved to stroll through the gaunt cold picture-gallery -of his country house and look at the various portraits of those whose -blood flowed in his veins. Here was Philip Herbert, described by -Francis Osborne, in his Memoires on the Reigns of Queen Elizabeth and -King James, as one who was "caressed by the Court for his handsome -face, which kept him not long company." Was it young Herbert's life -that he sometimes led? Had some strange poisonous germ crept from body -to body till it had reached his own? Was it some dim sense of that -ruined grace that had made him so suddenly, and almost without cause, -give utterance, in Basil Hallward's studio, to the mad prayer that had -so changed his life? Here, in gold-embroidered red doublet, jewelled -surcoat, and gilt-edged ruff and wristbands, stood Sir Anthony Sherard, -with his silver-and-black armour piled at his feet. What had this -man's legacy been? Had the lover of Giovanna of Naples bequeathed him -some inheritance of sin and shame? Were his own actions merely the -dreams that the dead man had not dared to realize? Here, from the -fading canvas, smiled Lady Elizabeth Devereux, in her gauze hood, pearl -stomacher, and pink slashed sleeves. A flower was in her right hand, -and her left clasped an enamelled collar of white and damask roses. On -a table by her side lay a mandolin and an apple. There were large -green rosettes upon her little pointed shoes. He knew her life, and -the strange stories that were told about her lovers. Had he something -of her temperament in him? These oval, heavy-lidded eyes seemed to -look curiously at him. What of George Willoughby, with his powdered -hair and fantastic patches? How evil he looked! The face was -saturnine and swarthy, and the sensual lips seemed to be twisted with -disdain. Delicate lace ruffles fell over the lean yellow hands that -were so overladen with rings. He had been a macaroni of the eighteenth -century, and the friend, in his youth, of Lord Ferrars. What of the -second Lord Beckenham, the companion of the Prince Regent in his -wildest days, and one of the witnesses at the secret marriage with Mrs. -Fitzherbert? How proud and handsome he was, with his chestnut curls -and insolent pose! What passions had he bequeathed? The world had -looked upon him as infamous. He had led the orgies at Carlton House. -The star of the Garter glittered upon his breast. Beside him hung the -portrait of his wife, a pallid, thin-lipped woman in black. Her blood, -also, stirred within him. How curious it all seemed! And his mother -with her Lady Hamilton face and her moist, wine-dashed lips--he knew -what he had got from her. He had got from her his beauty, and his -passion for the beauty of others. She laughed at him in her loose -Bacchante dress. There were vine leaves in her hair. The purple -spilled from the cup she was holding. The carnations of the painting -had withered, but the eyes were still wonderful in their depth and -brilliancy of colour. They seemed to follow him wherever he went. - -Yet one had ancestors in literature as well as in one's own race, -nearer perhaps in type and temperament, many of them, and certainly -with an influence of which one was more absolutely conscious. There -were times when it appeared to Dorian Gray that the whole of history -was merely the record of his own life, not as he had lived it in act -and circumstance, but as his imagination had created it for him, as it -had been in his brain and in his passions. He felt that he had known -them all, those strange terrible figures that had passed across the -stage of the world and made sin so marvellous and evil so full of -subtlety. It seemed to him that in some mysterious way their lives had -been his own. - -The hero of the wonderful novel that had so influenced his life had -himself known this curious fancy. In the seventh chapter he tells how, -crowned with laurel, lest lightning might strike him, he had sat, as -Tiberius, in a garden at Capri, reading the shameful books of -Elephantis, while dwarfs and peacocks strutted round him and the -flute-player mocked the swinger of the censer; and, as Caligula, had -caroused with the green-shirted jockeys in their stables and supped in -an ivory manger with a jewel-frontleted horse; and, as Domitian, had -wandered through a corridor lined with marble mirrors, looking round -with haggard eyes for the reflection of the dagger that was to end his -days, and sick with that ennui, that terrible _taedium vitae_, that comes -on those to whom life denies nothing; and had peered through a clear -emerald at the red shambles of the circus and then, in a litter of -pearl and purple drawn by silver-shod mules, been carried through the -Street of Pomegranates to a House of Gold and heard men cry on Nero -Caesar as he passed by; and, as Elagabalus, had painted his face with -colours, and plied the distaff among the women, and brought the Moon -from Carthage and given her in mystic marriage to the Sun. - -Over and over again Dorian used to read this fantastic chapter, and the -two chapters immediately following, in which, as in some curious -tapestries or cunningly wrought enamels, were pictured the awful and -beautiful forms of those whom vice and blood and weariness had made -monstrous or mad: Filippo, Duke of Milan, who slew his wife and -painted her lips with a scarlet poison that her lover might suck death -from the dead thing he fondled; Pietro Barbi, the Venetian, known as -Paul the Second, who sought in his vanity to assume the title of -Formosus, and whose tiara, valued at two hundred thousand florins, was -bought at the price of a terrible sin; Gian Maria Visconti, who used -hounds to chase living men and whose murdered body was covered with -roses by a harlot who had loved him; the Borgia on his white horse, -with Fratricide riding beside him and his mantle stained with the blood -of Perotto; Pietro Riario, the young Cardinal Archbishop of Florence, -child and minion of Sixtus IV, whose beauty was equalled only by his -debauchery, and who received Leonora of Aragon in a pavilion of white -and crimson silk, filled with nymphs and centaurs, and gilded a boy -that he might serve at the feast as Ganymede or Hylas; Ezzelin, whose -melancholy could be cured only by the spectacle of death, and who had a -passion for red blood, as other men have for red wine--the son of the -Fiend, as was reported, and one who had cheated his father at dice when -gambling with him for his own soul; Giambattista Cibo, who in mockery -took the name of Innocent and into whose torpid veins the blood of -three lads was infused by a Jewish doctor; Sigismondo Malatesta, the -lover of Isotta and the lord of Rimini, whose effigy was burned at Rome -as the enemy of God and man, who strangled Polyssena with a napkin, and -gave poison to Ginevra d'Este in a cup of emerald, and in honour of a -shameful passion built a pagan church for Christian worship; Charles -VI, who had so wildly adored his brother's wife that a leper had warned -him of the insanity that was coming on him, and who, when his brain had -sickened and grown strange, could only be soothed by Saracen cards -painted with the images of love and death and madness; and, in his -trimmed jerkin and jewelled cap and acanthuslike curls, Grifonetto -Baglioni, who slew Astorre with his bride, and Simonetto with his page, -and whose comeliness was such that, as he lay dying in the yellow -piazza of Perugia, those who had hated him could not choose but weep, -and Atalanta, who had cursed him, blessed him. - -There was a horrible fascination in them all. He saw them at night, -and they troubled his imagination in the day. The Renaissance knew of -strange manners of poisoning--poisoning by a helmet and a lighted -torch, by an embroidered glove and a jewelled fan, by a gilded pomander -and by an amber chain. Dorian Gray had been poisoned by a book. There -were moments when he looked on evil simply as a mode through which he -could realize his conception of the beautiful. - - - -CHAPTER 12 - -It was on the ninth of November, the eve of his own thirty-eighth -birthday, as he often remembered afterwards. - -He was walking home about eleven o'clock from Lord Henry's, where he -had been dining, and was wrapped in heavy furs, as the night was cold -and foggy. At the corner of Grosvenor Square and South Audley Street, -a man passed him in the mist, walking very fast and with the collar of -his grey ulster turned up. He had a bag in his hand. Dorian -recognized him. It was Basil Hallward. A strange sense of fear, for -which he could not account, came over him. He made no sign of -recognition and went on quickly in the direction of his own house. - -But Hallward had seen him. Dorian heard him first stopping on the -pavement and then hurrying after him. In a few moments, his hand was -on his arm. - -"Dorian! What an extraordinary piece of luck! I have been waiting for -you in your library ever since nine o'clock. Finally I took pity on -your tired servant and told him to go to bed, as he let me out. I am -off to Paris by the midnight train, and I particularly wanted to see -you before I left. I thought it was you, or rather your fur coat, as -you passed me. But I wasn't quite sure. Didn't you recognize me?" - -"In this fog, my dear Basil? Why, I can't even recognize Grosvenor -Square. I believe my house is somewhere about here, but I don't feel -at all certain about it. I am sorry you are going away, as I have not -seen you for ages. But I suppose you will be back soon?" - -"No: I am going to be out of England for six months. I intend to take -a studio in Paris and shut myself up till I have finished a great -picture I have in my head. However, it wasn't about myself I wanted to -talk. Here we are at your door. Let me come in for a moment. I have -something to say to you." - -"I shall be charmed. But won't you miss your train?" said Dorian Gray -languidly as he passed up the steps and opened the door with his -latch-key. - -The lamplight struggled out through the fog, and Hallward looked at his -watch. "I have heaps of time," he answered. "The train doesn't go -till twelve-fifteen, and it is only just eleven. In fact, I was on my -way to the club to look for you, when I met you. You see, I shan't -have any delay about luggage, as I have sent on my heavy things. All I -have with me is in this bag, and I can easily get to Victoria in twenty -minutes." - -Dorian looked at him and smiled. "What a way for a fashionable painter -to travel! A Gladstone bag and an ulster! Come in, or the fog will -get into the house. And mind you don't talk about anything serious. -Nothing is serious nowadays. At least nothing should be." - -Hallward shook his head, as he entered, and followed Dorian into the -library. There was a bright wood fire blazing in the large open -hearth. The lamps were lit, and an open Dutch silver spirit-case -stood, with some siphons of soda-water and large cut-glass tumblers, on -a little marqueterie table. - -"You see your servant made me quite at home, Dorian. He gave me -everything I wanted, including your best gold-tipped cigarettes. He is -a most hospitable creature. I like him much better than the Frenchman -you used to have. What has become of the Frenchman, by the bye?" - -Dorian shrugged his shoulders. "I believe he married Lady Radley's -maid, and has established her in Paris as an English dressmaker. -Anglomania is very fashionable over there now, I hear. It seems silly -of the French, doesn't it? But--do you know?--he was not at all a bad -servant. I never liked him, but I had nothing to complain about. One -often imagines things that are quite absurd. He was really very -devoted to me and seemed quite sorry when he went away. Have another -brandy-and-soda? Or would you like hock-and-seltzer? I always take -hock-and-seltzer myself. There is sure to be some in the next room." - -"Thanks, I won't have anything more," said the painter, taking his cap -and coat off and throwing them on the bag that he had placed in the -corner. "And now, my dear fellow, I want to speak to you seriously. -Don't frown like that. You make it so much more difficult for me." - -"What is it all about?" cried Dorian in his petulant way, flinging -himself down on the sofa. "I hope it is not about myself. I am tired -of myself to-night. I should like to be somebody else." - -"It is about yourself," answered Hallward in his grave deep voice, "and -I must say it to you. I shall only keep you half an hour." - -Dorian sighed and lit a cigarette. "Half an hour!" he murmured. - -"It is not much to ask of you, Dorian, and it is entirely for your own -sake that I am speaking. I think it right that you should know that -the most dreadful things are being said against you in London." - -"I don't wish to know anything about them. I love scandals about other -people, but scandals about myself don't interest me. They have not got -the charm of novelty." - -"They must interest you, Dorian. Every gentleman is interested in his -good name. You don't want people to talk of you as something vile and -degraded. Of course, you have your position, and your wealth, and all -that kind of thing. But position and wealth are not everything. Mind -you, I don't believe these rumours at all. At least, I can't believe -them when I see you. Sin is a thing that writes itself across a man's -face. It cannot be concealed. People talk sometimes of secret vices. -There are no such things. If a wretched man has a vice, it shows -itself in the lines of his mouth, the droop of his eyelids, the -moulding of his hands even. Somebody--I won't mention his name, but -you know him--came to me last year to have his portrait done. I had -never seen him before, and had never heard anything about him at the -time, though I have heard a good deal since. He offered an extravagant -price. I refused him. There was something in the shape of his fingers -that I hated. I know now that I was quite right in what I fancied -about him. His life is dreadful. But you, Dorian, with your pure, -bright, innocent face, and your marvellous untroubled youth--I can't -believe anything against you. And yet I see you very seldom, and you -never come down to the studio now, and when I am away from you, and I -hear all these hideous things that people are whispering about you, I -don't know what to say. Why is it, Dorian, that a man like the Duke of -Berwick leaves the room of a club when you enter it? Why is it that so -many gentlemen in London will neither go to your house or invite you to -theirs? You used to be a friend of Lord Staveley. I met him at dinner -last week. Your name happened to come up in conversation, in -connection with the miniatures you have lent to the exhibition at the -Dudley. Staveley curled his lip and said that you might have the most -artistic tastes, but that you were a man whom no pure-minded girl -should be allowed to know, and whom no chaste woman should sit in the -same room with. I reminded him that I was a friend of yours, and asked -him what he meant. He told me. He told me right out before everybody. -It was horrible! Why is your friendship so fatal to young men? There -was that wretched boy in the Guards who committed suicide. You were -his great friend. There was Sir Henry Ashton, who had to leave England -with a tarnished name. You and he were inseparable. What about Adrian -Singleton and his dreadful end? What about Lord Kent's only son and -his career? I met his father yesterday in St. James's Street. He -seemed broken with shame and sorrow. What about the young Duke of -Perth? What sort of life has he got now? What gentleman would -associate with him?" - -"Stop, Basil. You are talking about things of which you know nothing," -said Dorian Gray, biting his lip, and with a note of infinite contempt -in his voice. "You ask me why Berwick leaves a room when I enter it. -It is because I know everything about his life, not because he knows -anything about mine. With such blood as he has in his veins, how could -his record be clean? You ask me about Henry Ashton and young Perth. -Did I teach the one his vices, and the other his debauchery? If Kent's -silly son takes his wife from the streets, what is that to me? If -Adrian Singleton writes his friend's name across a bill, am I his -keeper? I know how people chatter in England. The middle classes air -their moral prejudices over their gross dinner-tables, and whisper -about what they call the profligacies of their betters in order to try -and pretend that they are in smart society and on intimate terms with -the people they slander. In this country, it is enough for a man to -have distinction and brains for every common tongue to wag against him. -And what sort of lives do these people, who pose as being moral, lead -themselves? My dear fellow, you forget that we are in the native land -of the hypocrite." - -"Dorian," cried Hallward, "that is not the question. England is bad -enough I know, and English society is all wrong. That is the reason -why I want you to be fine. You have not been fine. One has a right to -judge of a man by the effect he has over his friends. Yours seem to -lose all sense of honour, of goodness, of purity. You have filled them -with a madness for pleasure. They have gone down into the depths. You -led them there. Yes: you led them there, and yet you can smile, as -you are smiling now. And there is worse behind. I know you and Harry -are inseparable. Surely for that reason, if for none other, you should -not have made his sister's name a by-word." - -"Take care, Basil. You go too far." - -"I must speak, and you must listen. You shall listen. When you met -Lady Gwendolen, not a breath of scandal had ever touched her. Is there -a single decent woman in London now who would drive with her in the -park? Why, even her children are not allowed to live with her. Then -there are other stories--stories that you have been seen creeping at -dawn out of dreadful houses and slinking in disguise into the foulest -dens in London. Are they true? Can they be true? When I first heard -them, I laughed. I hear them now, and they make me shudder. What -about your country-house and the life that is led there? Dorian, you -don't know what is said about you. I won't tell you that I don't want -to preach to you. I remember Harry saying once that every man who -turned himself into an amateur curate for the moment always began by -saying that, and then proceeded to break his word. I do want to preach -to you. I want you to lead such a life as will make the world respect -you. I want you to have a clean name and a fair record. I want you to -get rid of the dreadful people you associate with. Don't shrug your -shoulders like that. Don't be so indifferent. You have a wonderful -influence. Let it be for good, not for evil. They say that you -corrupt every one with whom you become intimate, and that it is quite -sufficient for you to enter a house for shame of some kind to follow -after. I don't know whether it is so or not. How should I know? But -it is said of you. I am told things that it seems impossible to doubt. -Lord Gloucester was one of my greatest friends at Oxford. He showed me -a letter that his wife had written to him when she was dying alone in -her villa at Mentone. Your name was implicated in the most terrible -confession I ever read. I told him that it was absurd--that I knew you -thoroughly and that you were incapable of anything of the kind. Know -you? I wonder do I know you? Before I could answer that, I should -have to see your soul." - -"To see my soul!" muttered Dorian Gray, starting up from the sofa and -turning almost white from fear. - -"Yes," answered Hallward gravely, and with deep-toned sorrow in his -voice, "to see your soul. But only God can do that." - -A bitter laugh of mockery broke from the lips of the younger man. "You -shall see it yourself, to-night!" he cried, seizing a lamp from the -table. "Come: it is your own handiwork. Why shouldn't you look at -it? You can tell the world all about it afterwards, if you choose. -Nobody would believe you. If they did believe you, they would like me -all the better for it. I know the age better than you do, though you -will prate about it so tediously. Come, I tell you. You have -chattered enough about corruption. Now you shall look on it face to -face." - -There was the madness of pride in every word he uttered. He stamped -his foot upon the ground in his boyish insolent manner. He felt a -terrible joy at the thought that some one else was to share his secret, -and that the man who had painted the portrait that was the origin of -all his shame was to be burdened for the rest of his life with the -hideous memory of what he had done. - -"Yes," he continued, coming closer to him and looking steadfastly into -his stern eyes, "I shall show you my soul. You shall see the thing -that you fancy only God can see." - -Hallward started back. "This is blasphemy, Dorian!" he cried. "You -must not say things like that. They are horrible, and they don't mean -anything." - -"You think so?" He laughed again. - -"I know so. As for what I said to you to-night, I said it for your -good. You know I have been always a stanch friend to you." - -"Don't touch me. Finish what you have to say." - -A twisted flash of pain shot across the painter's face. He paused for -a moment, and a wild feeling of pity came over him. After all, what -right had he to pry into the life of Dorian Gray? If he had done a -tithe of what was rumoured about him, how much he must have suffered! -Then he straightened himself up, and walked over to the fire-place, and -stood there, looking at the burning logs with their frostlike ashes and -their throbbing cores of flame. - -"I am waiting, Basil," said the young man in a hard clear voice. - -He turned round. "What I have to say is this," he cried. "You must -give me some answer to these horrible charges that are made against -you. If you tell me that they are absolutely untrue from beginning to -end, I shall believe you. Deny them, Dorian, deny them! Can't you see -what I am going through? My God! don't tell me that you are bad, and -corrupt, and shameful." - -Dorian Gray smiled. There was a curl of contempt in his lips. "Come -upstairs, Basil," he said quietly. "I keep a diary of my life from day -to day, and it never leaves the room in which it is written. I shall -show it to you if you come with me." - -"I shall come with you, Dorian, if you wish it. I see I have missed my -train. That makes no matter. I can go to-morrow. But don't ask me to -read anything to-night. All I want is a plain answer to my question." - -"That shall be given to you upstairs. I could not give it here. You -will not have to read long." - - - -CHAPTER 13 - -He passed out of the room and began the ascent, Basil Hallward -following close behind. They walked softly, as men do instinctively at -night. The lamp cast fantastic shadows on the wall and staircase. A -rising wind made some of the windows rattle. - -When they reached the top landing, Dorian set the lamp down on the -floor, and taking out the key, turned it in the lock. "You insist on -knowing, Basil?" he asked in a low voice. - -"Yes." - -"I am delighted," he answered, smiling. Then he added, somewhat -harshly, "You are the one man in the world who is entitled to know -everything about me. You have had more to do with my life than you -think"; and, taking up the lamp, he opened the door and went in. A -cold current of air passed them, and the light shot up for a moment in -a flame of murky orange. He shuddered. "Shut the door behind you," he -whispered, as he placed the lamp on the table. - -Hallward glanced round him with a puzzled expression. The room looked -as if it had not been lived in for years. A faded Flemish tapestry, a -curtained picture, an old Italian _cassone_, and an almost empty -book-case--that was all that it seemed to contain, besides a chair and -a table. As Dorian Gray was lighting a half-burned candle that was -standing on the mantelshelf, he saw that the whole place was covered -with dust and that the carpet was in holes. A mouse ran scuffling -behind the wainscoting. There was a damp odour of mildew. - -"So you think that it is only God who sees the soul, Basil? Draw that -curtain back, and you will see mine." - -The voice that spoke was cold and cruel. "You are mad, Dorian, or -playing a part," muttered Hallward, frowning. - -"You won't? Then I must do it myself," said the young man, and he tore -the curtain from its rod and flung it on the ground. - -An exclamation of horror broke from the painter's lips as he saw in the -dim light the hideous face on the canvas grinning at him. There was -something in its expression that filled him with disgust and loathing. -Good heavens! it was Dorian Gray's own face that he was looking at! -The horror, whatever it was, had not yet entirely spoiled that -marvellous beauty. There was still some gold in the thinning hair and -some scarlet on the sensual mouth. The sodden eyes had kept something -of the loveliness of their blue, the noble curves had not yet -completely passed away from chiselled nostrils and from plastic throat. -Yes, it was Dorian himself. But who had done it? He seemed to -recognize his own brushwork, and the frame was his own design. The -idea was monstrous, yet he felt afraid. He seized the lighted candle, -and held it to the picture. In the left-hand corner was his own name, -traced in long letters of bright vermilion. - -It was some foul parody, some infamous ignoble satire. He had never -done that. Still, it was his own picture. He knew it, and he felt as -if his blood had changed in a moment from fire to sluggish ice. His -own picture! What did it mean? Why had it altered? He turned and -looked at Dorian Gray with the eyes of a sick man. His mouth twitched, -and his parched tongue seemed unable to articulate. He passed his hand -across his forehead. It was dank with clammy sweat. - -The young man was leaning against the mantelshelf, watching him with -that strange expression that one sees on the faces of those who are -absorbed in a play when some great artist is acting. There was neither -real sorrow in it nor real joy. There was simply the passion of the -spectator, with perhaps a flicker of triumph in his eyes. He had taken -the flower out of his coat, and was smelling it, or pretending to do so. - -"What does this mean?" cried Hallward, at last. His own voice sounded -shrill and curious in his ears. - -"Years ago, when I was a boy," said Dorian Gray, crushing the flower in -his hand, "you met me, flattered me, and taught me to be vain of my -good looks. One day you introduced me to a friend of yours, who -explained to me the wonder of youth, and you finished a portrait of me -that revealed to me the wonder of beauty. In a mad moment that, even -now, I don't know whether I regret or not, I made a wish, perhaps you -would call it a prayer...." - -"I remember it! Oh, how well I remember it! No! the thing is -impossible. The room is damp. Mildew has got into the canvas. The -paints I used had some wretched mineral poison in them. I tell you the -thing is impossible." - -"Ah, what is impossible?" murmured the young man, going over to the -window and leaning his forehead against the cold, mist-stained glass. - -"You told me you had destroyed it." - -"I was wrong. It has destroyed me." - -"I don't believe it is my picture." - -"Can't you see your ideal in it?" said Dorian bitterly. - -"My ideal, as you call it..." - -"As you called it." - -"There was nothing evil in it, nothing shameful. You were to me such -an ideal as I shall never meet again. This is the face of a satyr." - -"It is the face of my soul." - -"Christ! what a thing I must have worshipped! It has the eyes of a -devil." - -"Each of us has heaven and hell in him, Basil," cried Dorian with a -wild gesture of despair. - -Hallward turned again to the portrait and gazed at it. "My God! If it -is true," he exclaimed, "and this is what you have done with your life, -why, you must be worse even than those who talk against you fancy you -to be!" He held the light up again to the canvas and examined it. The -surface seemed to be quite undisturbed and as he had left it. It was -from within, apparently, that the foulness and horror had come. -Through some strange quickening of inner life the leprosies of sin were -slowly eating the thing away. The rotting of a corpse in a watery -grave was not so fearful. - -His hand shook, and the candle fell from its socket on the floor and -lay there sputtering. He placed his foot on it and put it out. Then -he flung himself into the rickety chair that was standing by the table -and buried his face in his hands. - -"Good God, Dorian, what a lesson! What an awful lesson!" There was no -answer, but he could hear the young man sobbing at the window. "Pray, -Dorian, pray," he murmured. "What is it that one was taught to say in -one's boyhood? 'Lead us not into temptation. Forgive us our sins. -Wash away our iniquities.' Let us say that together. The prayer of -your pride has been answered. The prayer of your repentance will be -answered also. I worshipped you too much. I am punished for it. You -worshipped yourself too much. We are both punished." - -Dorian Gray turned slowly around and looked at him with tear-dimmed -eyes. "It is too late, Basil," he faltered. - -"It is never too late, Dorian. Let us kneel down and try if we cannot -remember a prayer. Isn't there a verse somewhere, 'Though your sins be -as scarlet, yet I will make them as white as snow'?" - -"Those words mean nothing to me now." - -"Hush! Don't say that. You have done enough evil in your life. My -God! Don't you see that accursed thing leering at us?" - -Dorian Gray glanced at the picture, and suddenly an uncontrollable -feeling of hatred for Basil Hallward came over him, as though it had -been suggested to him by the image on the canvas, whispered into his -ear by those grinning lips. The mad passions of a hunted animal -stirred within him, and he loathed the man who was seated at the table, -more than in his whole life he had ever loathed anything. He glanced -wildly around. Something glimmered on the top of the painted chest -that faced him. His eye fell on it. He knew what it was. It was a -knife that he had brought up, some days before, to cut a piece of cord, -and had forgotten to take away with him. He moved slowly towards it, -passing Hallward as he did so. As soon as he got behind him, he seized -it and turned round. Hallward stirred in his chair as if he was going -to rise. He rushed at him and dug the knife into the great vein that -is behind the ear, crushing the man's head down on the table and -stabbing again and again. - -There was a stifled groan and the horrible sound of some one choking -with blood. Three times the outstretched arms shot up convulsively, -waving grotesque, stiff-fingered hands in the air. He stabbed him -twice more, but the man did not move. Something began to trickle on -the floor. He waited for a moment, still pressing the head down. Then -he threw the knife on the table, and listened. - -He could hear nothing, but the drip, drip on the threadbare carpet. He -opened the door and went out on the landing. The house was absolutely -quiet. No one was about. For a few seconds he stood bending over the -balustrade and peering down into the black seething well of darkness. -Then he took out the key and returned to the room, locking himself in -as he did so. - -The thing was still seated in the chair, straining over the table with -bowed head, and humped back, and long fantastic arms. Had it not been -for the red jagged tear in the neck and the clotted black pool that was -slowly widening on the table, one would have said that the man was -simply asleep. - -How quickly it had all been done! He felt strangely calm, and walking -over to the window, opened it and stepped out on the balcony. The wind -had blown the fog away, and the sky was like a monstrous peacock's -tail, starred with myriads of golden eyes. He looked down and saw the -policeman going his rounds and flashing the long beam of his lantern on -the doors of the silent houses. The crimson spot of a prowling hansom -gleamed at the corner and then vanished. A woman in a fluttering shawl -was creeping slowly by the railings, staggering as she went. Now and -then she stopped and peered back. Once, she began to sing in a hoarse -voice. The policeman strolled over and said something to her. She -stumbled away, laughing. A bitter blast swept across the square. The -gas-lamps flickered and became blue, and the leafless trees shook their -black iron branches to and fro. He shivered and went back, closing the -window behind him. - -Having reached the door, he turned the key and opened it. He did not -even glance at the murdered man. He felt that the secret of the whole -thing was not to realize the situation. The friend who had painted the -fatal portrait to which all his misery had been due had gone out of his -life. That was enough. - -Then he remembered the lamp. It was a rather curious one of Moorish -workmanship, made of dull silver inlaid with arabesques of burnished -steel, and studded with coarse turquoises. Perhaps it might be missed -by his servant, and questions would be asked. He hesitated for a -moment, then he turned back and took it from the table. He could not -help seeing the dead thing. How still it was! How horribly white the -long hands looked! It was like a dreadful wax image. - -Having locked the door behind him, he crept quietly downstairs. The -woodwork creaked and seemed to cry out as if in pain. He stopped -several times and waited. No: everything was still. It was merely -the sound of his own footsteps. - -When he reached the library, he saw the bag and coat in the corner. -They must be hidden away somewhere. He unlocked a secret press that -was in the wainscoting, a press in which he kept his own curious -disguises, and put them into it. He could easily burn them afterwards. -Then he pulled out his watch. It was twenty minutes to two. - -He sat down and began to think. Every year--every month, almost--men -were strangled in England for what he had done. There had been a -madness of murder in the air. Some red star had come too close to the -earth.... And yet, what evidence was there against him? Basil Hallward -had left the house at eleven. No one had seen him come in again. Most -of the servants were at Selby Royal. His valet had gone to bed.... -Paris! Yes. It was to Paris that Basil had gone, and by the midnight -train, as he had intended. With his curious reserved habits, it would -be months before any suspicions would be roused. Months! Everything -could be destroyed long before then. - -A sudden thought struck him. He put on his fur coat and hat and went -out into the hall. There he paused, hearing the slow heavy tread of -the policeman on the pavement outside and seeing the flash of the -bull's-eye reflected in the window. He waited and held his breath. - -After a few moments he drew back the latch and slipped out, shutting -the door very gently behind him. Then he began ringing the bell. In -about five minutes his valet appeared, half-dressed and looking very -drowsy. - -"I am sorry to have had to wake you up, Francis," he said, stepping in; -"but I had forgotten my latch-key. What time is it?" - -"Ten minutes past two, sir," answered the man, looking at the clock and -blinking. - -"Ten minutes past two? How horribly late! You must wake me at nine -to-morrow. I have some work to do." - -"All right, sir." - -"Did any one call this evening?" - -"Mr. Hallward, sir. He stayed here till eleven, and then he went away -to catch his train." - -"Oh! I am sorry I didn't see him. Did he leave any message?" - -"No, sir, except that he would write to you from Paris, if he did not -find you at the club." - -"That will do, Francis. Don't forget to call me at nine to-morrow." - -"No, sir." - -The man shambled down the passage in his slippers. - -Dorian Gray threw his hat and coat upon the table and passed into the -library. For a quarter of an hour he walked up and down the room, -biting his lip and thinking. Then he took down the Blue Book from one -of the shelves and began to turn over the leaves. "Alan Campbell, 152, -Hertford Street, Mayfair." Yes; that was the man he wanted. - - - -CHAPTER 14 - -At nine o'clock the next morning his servant came in with a cup of -chocolate on a tray and opened the shutters. Dorian was sleeping quite -peacefully, lying on his right side, with one hand underneath his -cheek. He looked like a boy who had been tired out with play, or study. - -The man had to touch him twice on the shoulder before he woke, and as -he opened his eyes a faint smile passed across his lips, as though he -had been lost in some delightful dream. Yet he had not dreamed at all. -His night had been untroubled by any images of pleasure or of pain. -But youth smiles without any reason. It is one of its chiefest charms. - -He turned round, and leaning upon his elbow, began to sip his -chocolate. The mellow November sun came streaming into the room. The -sky was bright, and there was a genial warmth in the air. It was -almost like a morning in May. - -Gradually the events of the preceding night crept with silent, -blood-stained feet into his brain and reconstructed themselves there -with terrible distinctness. He winced at the memory of all that he had -suffered, and for a moment the same curious feeling of loathing for -Basil Hallward that had made him kill him as he sat in the chair came -back to him, and he grew cold with passion. The dead man was still -sitting there, too, and in the sunlight now. How horrible that was! -Such hideous things were for the darkness, not for the day. - -He felt that if he brooded on what he had gone through he would sicken -or grow mad. There were sins whose fascination was more in the memory -than in the doing of them, strange triumphs that gratified the pride -more than the passions, and gave to the intellect a quickened sense of -joy, greater than any joy they brought, or could ever bring, to the -senses. But this was not one of them. It was a thing to be driven out -of the mind, to be drugged with poppies, to be strangled lest it might -strangle one itself. - -When the half-hour struck, he passed his hand across his forehead, and -then got up hastily and dressed himself with even more than his usual -care, giving a good deal of attention to the choice of his necktie and -scarf-pin and changing his rings more than once. He spent a long time -also over breakfast, tasting the various dishes, talking to his valet -about some new liveries that he was thinking of getting made for the -servants at Selby, and going through his correspondence. At some of -the letters, he smiled. Three of them bored him. One he read several -times over and then tore up with a slight look of annoyance in his -face. "That awful thing, a woman's memory!" as Lord Henry had once -said. - -After he had drunk his cup of black coffee, he wiped his lips slowly -with a napkin, motioned to his servant to wait, and going over to the -table, sat down and wrote two letters. One he put in his pocket, the -other he handed to the valet. - -"Take this round to 152, Hertford Street, Francis, and if Mr. Campbell -is out of town, get his address." - -As soon as he was alone, he lit a cigarette and began sketching upon a -piece of paper, drawing first flowers and bits of architecture, and -then human faces. Suddenly he remarked that every face that he drew -seemed to have a fantastic likeness to Basil Hallward. He frowned, and -getting up, went over to the book-case and took out a volume at hazard. -He was determined that he would not think about what had happened until -it became absolutely necessary that he should do so. - -When he had stretched himself on the sofa, he looked at the title-page -of the book. It was Gautier's Emaux et Camees, Charpentier's -Japanese-paper edition, with the Jacquemart etching. The binding was -of citron-green leather, with a design of gilt trellis-work and dotted -pomegranates. It had been given to him by Adrian Singleton. As he -turned over the pages, his eye fell on the poem about the hand of -Lacenaire, the cold yellow hand "_du supplice encore mal lavee_," with -its downy red hairs and its "_doigts de faune_." He glanced at his own -white taper fingers, shuddering slightly in spite of himself, and -passed on, till he came to those lovely stanzas upon Venice: - - Sur une gamme chromatique, - Le sein de perles ruisselant, - La Venus de l'Adriatique - Sort de l'eau son corps rose et blanc. - - Les domes, sur l'azur des ondes - Suivant la phrase au pur contour, - S'enflent comme des gorges rondes - Que souleve un soupir d'amour. - - L'esquif aborde et me depose, - Jetant son amarre au pilier, - Devant une facade rose, - Sur le marbre d'un escalier. - - -How exquisite they were! As one read them, one seemed to be floating -down the green water-ways of the pink and pearl city, seated in a black -gondola with silver prow and trailing curtains. The mere lines looked -to him like those straight lines of turquoise-blue that follow one as -one pushes out to the Lido. The sudden flashes of colour reminded him -of the gleam of the opal-and-iris-throated birds that flutter round the -tall honeycombed Campanile, or stalk, with such stately grace, through -the dim, dust-stained arcades. Leaning back with half-closed eyes, he -kept saying over and over to himself: - - "Devant une facade rose, - Sur le marbre d'un escalier." - -The whole of Venice was in those two lines. He remembered the autumn -that he had passed there, and a wonderful love that had stirred him to -mad delightful follies. There was romance in every place. But Venice, -like Oxford, had kept the background for romance, and, to the true -romantic, background was everything, or almost everything. Basil had -been with him part of the time, and had gone wild over Tintoret. Poor -Basil! What a horrible way for a man to die! - -He sighed, and took up the volume again, and tried to forget. He read -of the swallows that fly in and out of the little _cafe_ at Smyrna where -the Hadjis sit counting their amber beads and the turbaned merchants -smoke their long tasselled pipes and talk gravely to each other; he -read of the Obelisk in the Place de la Concorde that weeps tears of -granite in its lonely sunless exile and longs to be back by the hot, -lotus-covered Nile, where there are Sphinxes, and rose-red ibises, and -white vultures with gilded claws, and crocodiles with small beryl eyes -that crawl over the green steaming mud; he began to brood over those -verses which, drawing music from kiss-stained marble, tell of that -curious statue that Gautier compares to a contralto voice, the "_monstre -charmant_" that couches in the porphyry-room of the Louvre. But after a -time the book fell from his hand. He grew nervous, and a horrible fit -of terror came over him. What if Alan Campbell should be out of -England? Days would elapse before he could come back. Perhaps he -might refuse to come. What could he do then? Every moment was of -vital importance. - -They had been great friends once, five years before--almost -inseparable, indeed. Then the intimacy had come suddenly to an end. -When they met in society now, it was only Dorian Gray who smiled: Alan -Campbell never did. - -He was an extremely clever young man, though he had no real -appreciation of the visible arts, and whatever little sense of the -beauty of poetry he possessed he had gained entirely from Dorian. His -dominant intellectual passion was for science. At Cambridge he had -spent a great deal of his time working in the laboratory, and had taken -a good class in the Natural Science Tripos of his year. Indeed, he was -still devoted to the study of chemistry, and had a laboratory of his -own in which he used to shut himself up all day long, greatly to the -annoyance of his mother, who had set her heart on his standing for -Parliament and had a vague idea that a chemist was a person who made up -prescriptions. He was an excellent musician, however, as well, and -played both the violin and the piano better than most amateurs. In -fact, it was music that had first brought him and Dorian Gray -together--music and that indefinable attraction that Dorian seemed to -be able to exercise whenever he wished--and, indeed, exercised often -without being conscious of it. They had met at Lady Berkshire's the -night that Rubinstein played there, and after that used to be always -seen together at the opera and wherever good music was going on. For -eighteen months their intimacy lasted. Campbell was always either at -Selby Royal or in Grosvenor Square. To him, as to many others, Dorian -Gray was the type of everything that is wonderful and fascinating in -life. Whether or not a quarrel had taken place between them no one -ever knew. But suddenly people remarked that they scarcely spoke when -they met and that Campbell seemed always to go away early from any -party at which Dorian Gray was present. He had changed, too--was -strangely melancholy at times, appeared almost to dislike hearing -music, and would never himself play, giving as his excuse, when he was -called upon, that he was so absorbed in science that he had no time -left in which to practise. And this was certainly true. Every day he -seemed to become more interested in biology, and his name appeared once -or twice in some of the scientific reviews in connection with certain -curious experiments. - -This was the man Dorian Gray was waiting for. Every second he kept -glancing at the clock. As the minutes went by he became horribly -agitated. At last he got up and began to pace up and down the room, -looking like a beautiful caged thing. He took long stealthy strides. -His hands were curiously cold. - -The suspense became unbearable. Time seemed to him to be crawling with -feet of lead, while he by monstrous winds was being swept towards the -jagged edge of some black cleft of precipice. He knew what was waiting -for him there; saw it, indeed, and, shuddering, crushed with dank hands -his burning lids as though he would have robbed the very brain of sight -and driven the eyeballs back into their cave. It was useless. The -brain had its own food on which it battened, and the imagination, made -grotesque by terror, twisted and distorted as a living thing by pain, -danced like some foul puppet on a stand and grinned through moving -masks. Then, suddenly, time stopped for him. Yes: that blind, -slow-breathing thing crawled no more, and horrible thoughts, time being -dead, raced nimbly on in front, and dragged a hideous future from its -grave, and showed it to him. He stared at it. Its very horror made -him stone. - -At last the door opened and his servant entered. He turned glazed eyes -upon him. - -"Mr. Campbell, sir," said the man. - -A sigh of relief broke from his parched lips, and the colour came back -to his cheeks. - -"Ask him to come in at once, Francis." He felt that he was himself -again. His mood of cowardice had passed away. - -The man bowed and retired. In a few moments, Alan Campbell walked in, -looking very stern and rather pale, his pallor being intensified by his -coal-black hair and dark eyebrows. - -"Alan! This is kind of you. I thank you for coming." - -"I had intended never to enter your house again, Gray. But you said it -was a matter of life and death." His voice was hard and cold. He -spoke with slow deliberation. There was a look of contempt in the -steady searching gaze that he turned on Dorian. He kept his hands in -the pockets of his Astrakhan coat, and seemed not to have noticed the -gesture with which he had been greeted. - -"Yes: it is a matter of life and death, Alan, and to more than one -person. Sit down." - -Campbell took a chair by the table, and Dorian sat opposite to him. -The two men's eyes met. In Dorian's there was infinite pity. He knew -that what he was going to do was dreadful. - -After a strained moment of silence, he leaned across and said, very -quietly, but watching the effect of each word upon the face of him he -had sent for, "Alan, in a locked room at the top of this house, a room -to which nobody but myself has access, a dead man is seated at a table. -He has been dead ten hours now. Don't stir, and don't look at me like -that. Who the man is, why he died, how he died, are matters that do -not concern you. What you have to do is this--" - -"Stop, Gray. I don't want to know anything further. Whether what you -have told me is true or not true doesn't concern me. I entirely -decline to be mixed up in your life. Keep your horrible secrets to -yourself. They don't interest me any more." - -"Alan, they will have to interest you. This one will have to interest -you. I am awfully sorry for you, Alan. But I can't help myself. You -are the one man who is able to save me. I am forced to bring you into -the matter. I have no option. Alan, you are scientific. You know -about chemistry and things of that kind. You have made experiments. -What you have got to do is to destroy the thing that is upstairs--to -destroy it so that not a vestige of it will be left. Nobody saw this -person come into the house. Indeed, at the present moment he is -supposed to be in Paris. He will not be missed for months. When he is -missed, there must be no trace of him found here. You, Alan, you must -change him, and everything that belongs to him, into a handful of ashes -that I may scatter in the air." - -"You are mad, Dorian." - -"Ah! I was waiting for you to call me Dorian." - -"You are mad, I tell you--mad to imagine that I would raise a finger to -help you, mad to make this monstrous confession. I will have nothing -to do with this matter, whatever it is. Do you think I am going to -peril my reputation for you? What is it to me what devil's work you -are up to?" - -"It was suicide, Alan." - -"I am glad of that. But who drove him to it? You, I should fancy." - -"Do you still refuse to do this for me?" - -"Of course I refuse. I will have absolutely nothing to do with it. I -don't care what shame comes on you. You deserve it all. I should not -be sorry to see you disgraced, publicly disgraced. How dare you ask -me, of all men in the world, to mix myself up in this horror? I should -have thought you knew more about people's characters. Your friend Lord -Henry Wotton can't have taught you much about psychology, whatever else -he has taught you. Nothing will induce me to stir a step to help you. -You have come to the wrong man. Go to some of your friends. Don't -come to me." - -"Alan, it was murder. I killed him. You don't know what he had made -me suffer. Whatever my life is, he had more to do with the making or -the marring of it than poor Harry has had. He may not have intended -it, the result was the same." - -"Murder! Good God, Dorian, is that what you have come to? I shall not -inform upon you. It is not my business. Besides, without my stirring -in the matter, you are certain to be arrested. Nobody ever commits a -crime without doing something stupid. But I will have nothing to do -with it." - -"You must have something to do with it. Wait, wait a moment; listen to -me. Only listen, Alan. All I ask of you is to perform a certain -scientific experiment. You go to hospitals and dead-houses, and the -horrors that you do there don't affect you. If in some hideous -dissecting-room or fetid laboratory you found this man lying on a -leaden table with red gutters scooped out in it for the blood to flow -through, you would simply look upon him as an admirable subject. You -would not turn a hair. You would not believe that you were doing -anything wrong. On the contrary, you would probably feel that you were -benefiting the human race, or increasing the sum of knowledge in the -world, or gratifying intellectual curiosity, or something of that kind. -What I want you to do is merely what you have often done before. -Indeed, to destroy a body must be far less horrible than what you are -accustomed to work at. And, remember, it is the only piece of evidence -against me. If it is discovered, I am lost; and it is sure to be -discovered unless you help me." - -"I have no desire to help you. You forget that. I am simply -indifferent to the whole thing. It has nothing to do with me." - -"Alan, I entreat you. Think of the position I am in. Just before you -came I almost fainted with terror. You may know terror yourself some -day. No! don't think of that. Look at the matter purely from the -scientific point of view. You don't inquire where the dead things on -which you experiment come from. Don't inquire now. I have told you -too much as it is. But I beg of you to do this. We were friends once, -Alan." - -"Don't speak about those days, Dorian--they are dead." - -"The dead linger sometimes. The man upstairs will not go away. He is -sitting at the table with bowed head and outstretched arms. Alan! -Alan! If you don't come to my assistance, I am ruined. Why, they will -hang me, Alan! Don't you understand? They will hang me for what I -have done." - -"There is no good in prolonging this scene. I absolutely refuse to do -anything in the matter. It is insane of you to ask me." - -"You refuse?" - -"Yes." - -"I entreat you, Alan." - -"It is useless." - -The same look of pity came into Dorian Gray's eyes. Then he stretched -out his hand, took a piece of paper, and wrote something on it. He -read it over twice, folded it carefully, and pushed it across the -table. Having done this, he got up and went over to the window. - -Campbell looked at him in surprise, and then took up the paper, and -opened it. As he read it, his face became ghastly pale and he fell -back in his chair. A horrible sense of sickness came over him. He -felt as if his heart was beating itself to death in some empty hollow. - -After two or three minutes of terrible silence, Dorian turned round and -came and stood behind him, putting his hand upon his shoulder. - -"I am so sorry for you, Alan," he murmured, "but you leave me no -alternative. I have a letter written already. Here it is. You see -the address. If you don't help me, I must send it. If you don't help -me, I will send it. You know what the result will be. But you are -going to help me. It is impossible for you to refuse now. I tried to -spare you. You will do me the justice to admit that. You were stern, -harsh, offensive. You treated me as no man has ever dared to treat -me--no living man, at any rate. I bore it all. Now it is for me to -dictate terms." - -Campbell buried his face in his hands, and a shudder passed through him. - -"Yes, it is my turn to dictate terms, Alan. You know what they are. -The thing is quite simple. Come, don't work yourself into this fever. -The thing has to be done. Face it, and do it." - -A groan broke from Campbell's lips and he shivered all over. The -ticking of the clock on the mantelpiece seemed to him to be dividing -time into separate atoms of agony, each of which was too terrible to be -borne. He felt as if an iron ring was being slowly tightened round his -forehead, as if the disgrace with which he was threatened had already -come upon him. The hand upon his shoulder weighed like a hand of lead. -It was intolerable. It seemed to crush him. - -"Come, Alan, you must decide at once." - -"I cannot do it," he said, mechanically, as though words could alter -things. - -"You must. You have no choice. Don't delay." - -He hesitated a moment. "Is there a fire in the room upstairs?" - -"Yes, there is a gas-fire with asbestos." - -"I shall have to go home and get some things from the laboratory." - -"No, Alan, you must not leave the house. Write out on a sheet of -notepaper what you want and my servant will take a cab and bring the -things back to you." - -Campbell scrawled a few lines, blotted them, and addressed an envelope -to his assistant. Dorian took the note up and read it carefully. Then -he rang the bell and gave it to his valet, with orders to return as -soon as possible and to bring the things with him. - -As the hall door shut, Campbell started nervously, and having got up -from the chair, went over to the chimney-piece. He was shivering with a -kind of ague. For nearly twenty minutes, neither of the men spoke. A -fly buzzed noisily about the room, and the ticking of the clock was -like the beat of a hammer. - -As the chime struck one, Campbell turned round, and looking at Dorian -Gray, saw that his eyes were filled with tears. There was something in -the purity and refinement of that sad face that seemed to enrage him. -"You are infamous, absolutely infamous!" he muttered. - -"Hush, Alan. You have saved my life," said Dorian. - -"Your life? Good heavens! what a life that is! You have gone from -corruption to corruption, and now you have culminated in crime. In -doing what I am going to do--what you force me to do--it is not of your -life that I am thinking." - -"Ah, Alan," murmured Dorian with a sigh, "I wish you had a thousandth -part of the pity for me that I have for you." He turned away as he -spoke and stood looking out at the garden. Campbell made no answer. - -After about ten minutes a knock came to the door, and the servant -entered, carrying a large mahogany chest of chemicals, with a long coil -of steel and platinum wire and two rather curiously shaped iron clamps. - -"Shall I leave the things here, sir?" he asked Campbell. - -"Yes," said Dorian. "And I am afraid, Francis, that I have another -errand for you. What is the name of the man at Richmond who supplies -Selby with orchids?" - -"Harden, sir." - -"Yes--Harden. You must go down to Richmond at once, see Harden -personally, and tell him to send twice as many orchids as I ordered, -and to have as few white ones as possible. In fact, I don't want any -white ones. It is a lovely day, Francis, and Richmond is a very pretty -place--otherwise I wouldn't bother you about it." - -"No trouble, sir. At what time shall I be back?" - -Dorian looked at Campbell. "How long will your experiment take, Alan?" -he said in a calm indifferent voice. The presence of a third person in -the room seemed to give him extraordinary courage. - -Campbell frowned and bit his lip. "It will take about five hours," he -answered. - -"It will be time enough, then, if you are back at half-past seven, -Francis. Or stay: just leave my things out for dressing. You can -have the evening to yourself. I am not dining at home, so I shall not -want you." - -"Thank you, sir," said the man, leaving the room. - -"Now, Alan, there is not a moment to be lost. How heavy this chest is! -I'll take it for you. You bring the other things." He spoke rapidly -and in an authoritative manner. Campbell felt dominated by him. They -left the room together. - -When they reached the top landing, Dorian took out the key and turned -it in the lock. Then he stopped, and a troubled look came into his -eyes. He shuddered. "I don't think I can go in, Alan," he murmured. - -"It is nothing to me. I don't require you," said Campbell coldly. - -Dorian half opened the door. As he did so, he saw the face of his -portrait leering in the sunlight. On the floor in front of it the torn -curtain was lying. He remembered that the night before he had -forgotten, for the first time in his life, to hide the fatal canvas, -and was about to rush forward, when he drew back with a shudder. - -What was that loathsome red dew that gleamed, wet and glistening, on -one of the hands, as though the canvas had sweated blood? How horrible -it was!--more horrible, it seemed to him for the moment, than the -silent thing that he knew was stretched across the table, the thing -whose grotesque misshapen shadow on the spotted carpet showed him that -it had not stirred, but was still there, as he had left it. - -He heaved a deep breath, opened the door a little wider, and with -half-closed eyes and averted head, walked quickly in, determined that -he would not look even once upon the dead man. Then, stooping down and -taking up the gold-and-purple hanging, he flung it right over the -picture. - -There he stopped, feeling afraid to turn round, and his eyes fixed -themselves on the intricacies of the pattern before him. He heard -Campbell bringing in the heavy chest, and the irons, and the other -things that he had required for his dreadful work. He began to wonder -if he and Basil Hallward had ever met, and, if so, what they had -thought of each other. - -"Leave me now," said a stern voice behind him. - -He turned and hurried out, just conscious that the dead man had been -thrust back into the chair and that Campbell was gazing into a -glistening yellow face. As he was going downstairs, he heard the key -being turned in the lock. - -It was long after seven when Campbell came back into the library. He -was pale, but absolutely calm. "I have done what you asked me to do," -he muttered. "And now, good-bye. Let us never see each other again." - -"You have saved me from ruin, Alan. I cannot forget that," said Dorian -simply. - -As soon as Campbell had left, he went upstairs. There was a horrible -smell of nitric acid in the room. But the thing that had been sitting -at the table was gone. - - - -CHAPTER 15 - -That evening, at eight-thirty, exquisitely dressed and wearing a large -button-hole of Parma violets, Dorian Gray was ushered into Lady -Narborough's drawing-room by bowing servants. His forehead was -throbbing with maddened nerves, and he felt wildly excited, but his -manner as he bent over his hostess's hand was as easy and graceful as -ever. Perhaps one never seems so much at one's ease as when one has to -play a part. Certainly no one looking at Dorian Gray that night could -have believed that he had passed through a tragedy as horrible as any -tragedy of our age. Those finely shaped fingers could never have -clutched a knife for sin, nor those smiling lips have cried out on God -and goodness. He himself could not help wondering at the calm of his -demeanour, and for a moment felt keenly the terrible pleasure of a -double life. - -It was a small party, got up rather in a hurry by Lady Narborough, who -was a very clever woman with what Lord Henry used to describe as the -remains of really remarkable ugliness. She had proved an excellent -wife to one of our most tedious ambassadors, and having buried her -husband properly in a marble mausoleum, which she had herself designed, -and married off her daughters to some rich, rather elderly men, she -devoted herself now to the pleasures of French fiction, French cookery, -and French _esprit_ when she could get it. - -Dorian was one of her especial favourites, and she always told him that -she was extremely glad she had not met him in early life. "I know, my -dear, I should have fallen madly in love with you," she used to say, -"and thrown my bonnet right over the mills for your sake. It is most -fortunate that you were not thought of at the time. As it was, our -bonnets were so unbecoming, and the mills were so occupied in trying to -raise the wind, that I never had even a flirtation with anybody. -However, that was all Narborough's fault. He was dreadfully -short-sighted, and there is no pleasure in taking in a husband who -never sees anything." - -Her guests this evening were rather tedious. The fact was, as she -explained to Dorian, behind a very shabby fan, one of her married -daughters had come up quite suddenly to stay with her, and, to make -matters worse, had actually brought her husband with her. "I think it -is most unkind of her, my dear," she whispered. "Of course I go and -stay with them every summer after I come from Homburg, but then an old -woman like me must have fresh air sometimes, and besides, I really wake -them up. You don't know what an existence they lead down there. It is -pure unadulterated country life. They get up early, because they have -so much to do, and go to bed early, because they have so little to -think about. There has not been a scandal in the neighbourhood since -the time of Queen Elizabeth, and consequently they all fall asleep -after dinner. You shan't sit next either of them. You shall sit by me -and amuse me." - -Dorian murmured a graceful compliment and looked round the room. Yes: -it was certainly a tedious party. Two of the people he had never seen -before, and the others consisted of Ernest Harrowden, one of those -middle-aged mediocrities so common in London clubs who have no enemies, -but are thoroughly disliked by their friends; Lady Ruxton, an -overdressed woman of forty-seven, with a hooked nose, who was always -trying to get herself compromised, but was so peculiarly plain that to -her great disappointment no one would ever believe anything against -her; Mrs. Erlynne, a pushing nobody, with a delightful lisp and -Venetian-red hair; Lady Alice Chapman, his hostess's daughter, a dowdy -dull girl, with one of those characteristic British faces that, once -seen, are never remembered; and her husband, a red-cheeked, -white-whiskered creature who, like so many of his class, was under the -impression that inordinate joviality can atone for an entire lack of -ideas. - -He was rather sorry he had come, till Lady Narborough, looking at the -great ormolu gilt clock that sprawled in gaudy curves on the -mauve-draped mantelshelf, exclaimed: "How horrid of Henry Wotton to be -so late! I sent round to him this morning on chance and he promised -faithfully not to disappoint me." - -It was some consolation that Harry was to be there, and when the door -opened and he heard his slow musical voice lending charm to some -insincere apology, he ceased to feel bored. - -But at dinner he could not eat anything. Plate after plate went away -untasted. Lady Narborough kept scolding him for what she called "an -insult to poor Adolphe, who invented the _menu_ specially for you," and -now and then Lord Henry looked across at him, wondering at his silence -and abstracted manner. From time to time the butler filled his glass -with champagne. He drank eagerly, and his thirst seemed to increase. - -"Dorian," said Lord Henry at last, as the _chaud-froid_ was being handed -round, "what is the matter with you to-night? You are quite out of -sorts." - -"I believe he is in love," cried Lady Narborough, "and that he is -afraid to tell me for fear I should be jealous. He is quite right. I -certainly should." - -"Dear Lady Narborough," murmured Dorian, smiling, "I have not been in -love for a whole week--not, in fact, since Madame de Ferrol left town." - -"How you men can fall in love with that woman!" exclaimed the old lady. -"I really cannot understand it." - -"It is simply because she remembers you when you were a little girl, -Lady Narborough," said Lord Henry. "She is the one link between us and -your short frocks." - -"She does not remember my short frocks at all, Lord Henry. But I -remember her very well at Vienna thirty years ago, and how _decolletee_ -she was then." - -"She is still _decolletee_," he answered, taking an olive in his long -fingers; "and when she is in a very smart gown she looks like an -_edition de luxe_ of a bad French novel. She is really wonderful, and -full of surprises. Her capacity for family affection is extraordinary. -When her third husband died, her hair turned quite gold from grief." - -"How can you, Harry!" cried Dorian. - -"It is a most romantic explanation," laughed the hostess. "But her -third husband, Lord Henry! You don't mean to say Ferrol is the fourth?" - -"Certainly, Lady Narborough." - -"I don't believe a word of it." - -"Well, ask Mr. Gray. He is one of her most intimate friends." - -"Is it true, Mr. Gray?" - -"She assures me so, Lady Narborough," said Dorian. "I asked her -whether, like Marguerite de Navarre, she had their hearts embalmed and -hung at her girdle. She told me she didn't, because none of them had -had any hearts at all." - -"Four husbands! Upon my word that is _trop de zele_." - -"_Trop d'audace_, I tell her," said Dorian. - -"Oh! she is audacious enough for anything, my dear. And what is Ferrol -like? I don't know him." - -"The husbands of very beautiful women belong to the criminal classes," -said Lord Henry, sipping his wine. - -Lady Narborough hit him with her fan. "Lord Henry, I am not at all -surprised that the world says that you are extremely wicked." - -"But what world says that?" asked Lord Henry, elevating his eyebrows. -"It can only be the next world. This world and I are on excellent -terms." - -"Everybody I know says you are very wicked," cried the old lady, -shaking her head. - -Lord Henry looked serious for some moments. "It is perfectly -monstrous," he said, at last, "the way people go about nowadays saying -things against one behind one's back that are absolutely and entirely -true." - -"Isn't he incorrigible?" cried Dorian, leaning forward in his chair. - -"I hope so," said his hostess, laughing. "But really, if you all -worship Madame de Ferrol in this ridiculous way, I shall have to marry -again so as to be in the fashion." - -"You will never marry again, Lady Narborough," broke in Lord Henry. -"You were far too happy. When a woman marries again, it is because she -detested her first husband. When a man marries again, it is because he -adored his first wife. Women try their luck; men risk theirs." - -"Narborough wasn't perfect," cried the old lady. - -"If he had been, you would not have loved him, my dear lady," was the -rejoinder. "Women love us for our defects. If we have enough of them, -they will forgive us everything, even our intellects. You will never -ask me to dinner again after saying this, I am afraid, Lady Narborough, -but it is quite true." - -"Of course it is true, Lord Henry. If we women did not love you for -your defects, where would you all be? Not one of you would ever be -married. You would be a set of unfortunate bachelors. Not, however, -that that would alter you much. Nowadays all the married men live like -bachelors, and all the bachelors like married men." - -"_Fin de siecle_," murmured Lord Henry. - -"_Fin du globe_," answered his hostess. - -"I wish it were _fin du globe_," said Dorian with a sigh. "Life is a -great disappointment." - -"Ah, my dear," cried Lady Narborough, putting on her gloves, "don't -tell me that you have exhausted life. When a man says that one knows -that life has exhausted him. Lord Henry is very wicked, and I -sometimes wish that I had been; but you are made to be good--you look -so good. I must find you a nice wife. Lord Henry, don't you think -that Mr. Gray should get married?" - -"I am always telling him so, Lady Narborough," said Lord Henry with a -bow. - -"Well, we must look out for a suitable match for him. I shall go -through Debrett carefully to-night and draw out a list of all the -eligible young ladies." - -"With their ages, Lady Narborough?" asked Dorian. - -"Of course, with their ages, slightly edited. But nothing must be done -in a hurry. I want it to be what _The Morning Post_ calls a suitable -alliance, and I want you both to be happy." - -"What nonsense people talk about happy marriages!" exclaimed Lord -Henry. "A man can be happy with any woman, as long as he does not love -her." - -"Ah! what a cynic you are!" cried the old lady, pushing back her chair -and nodding to Lady Ruxton. "You must come and dine with me soon -again. You are really an admirable tonic, much better than what Sir -Andrew prescribes for me. You must tell me what people you would like -to meet, though. I want it to be a delightful gathering." - -"I like men who have a future and women who have a past," he answered. -"Or do you think that would make it a petticoat party?" - -"I fear so," she said, laughing, as she stood up. "A thousand pardons, -my dear Lady Ruxton," she added, "I didn't see you hadn't finished your -cigarette." - -"Never mind, Lady Narborough. I smoke a great deal too much. I am -going to limit myself, for the future." - -"Pray don't, Lady Ruxton," said Lord Henry. "Moderation is a fatal -thing. Enough is as bad as a meal. More than enough is as good as a -feast." - -Lady Ruxton glanced at him curiously. "You must come and explain that -to me some afternoon, Lord Henry. It sounds a fascinating theory," she -murmured, as she swept out of the room. - -"Now, mind you don't stay too long over your politics and scandal," -cried Lady Narborough from the door. "If you do, we are sure to -squabble upstairs." - -The men laughed, and Mr. Chapman got up solemnly from the foot of the -table and came up to the top. Dorian Gray changed his seat and went -and sat by Lord Henry. Mr. Chapman began to talk in a loud voice about -the situation in the House of Commons. He guffawed at his adversaries. -The word _doctrinaire_--word full of terror to the British -mind--reappeared from time to time between his explosions. An -alliterative prefix served as an ornament of oratory. He hoisted the -Union Jack on the pinnacles of thought. The inherited stupidity of the -race--sound English common sense he jovially termed it--was shown to be -the proper bulwark for society. - -A smile curved Lord Henry's lips, and he turned round and looked at -Dorian. - -"Are you better, my dear fellow?" he asked. "You seemed rather out of -sorts at dinner." - -"I am quite well, Harry. I am tired. That is all." - -"You were charming last night. The little duchess is quite devoted to -you. She tells me she is going down to Selby." - -"She has promised to come on the twentieth." - -"Is Monmouth to be there, too?" - -"Oh, yes, Harry." - -"He bores me dreadfully, almost as much as he bores her. She is very -clever, too clever for a woman. She lacks the indefinable charm of -weakness. It is the feet of clay that make the gold of the image -precious. Her feet are very pretty, but they are not feet of clay. -White porcelain feet, if you like. They have been through the fire, -and what fire does not destroy, it hardens. She has had experiences." - -"How long has she been married?" asked Dorian. - -"An eternity, she tells me. I believe, according to the peerage, it is -ten years, but ten years with Monmouth must have been like eternity, -with time thrown in. Who else is coming?" - -"Oh, the Willoughbys, Lord Rugby and his wife, our hostess, Geoffrey -Clouston, the usual set. I have asked Lord Grotrian." - -"I like him," said Lord Henry. "A great many people don't, but I find -him charming. He atones for being occasionally somewhat overdressed by -being always absolutely over-educated. He is a very modern type." - -"I don't know if he will be able to come, Harry. He may have to go to -Monte Carlo with his father." - -"Ah! what a nuisance people's people are! Try and make him come. By -the way, Dorian, you ran off very early last night. You left before -eleven. What did you do afterwards? Did you go straight home?" - -Dorian glanced at him hurriedly and frowned. - -"No, Harry," he said at last, "I did not get home till nearly three." - -"Did you go to the club?" - -"Yes," he answered. Then he bit his lip. "No, I don't mean that. I -didn't go to the club. I walked about. I forget what I did.... How -inquisitive you are, Harry! You always want to know what one has been -doing. I always want to forget what I have been doing. I came in at -half-past two, if you wish to know the exact time. I had left my -latch-key at home, and my servant had to let me in. If you want any -corroborative evidence on the subject, you can ask him." - -Lord Henry shrugged his shoulders. "My dear fellow, as if I cared! -Let us go up to the drawing-room. No sherry, thank you, Mr. Chapman. -Something has happened to you, Dorian. Tell me what it is. You are -not yourself to-night." - -"Don't mind me, Harry. I am irritable, and out of temper. I shall -come round and see you to-morrow, or next day. Make my excuses to Lady -Narborough. I shan't go upstairs. I shall go home. I must go home." - -"All right, Dorian. I dare say I shall see you to-morrow at tea-time. -The duchess is coming." - -"I will try to be there, Harry," he said, leaving the room. As he -drove back to his own house, he was conscious that the sense of terror -he thought he had strangled had come back to him. Lord Henry's casual -questioning had made him lose his nerve for the moment, and he wanted -his nerve still. Things that were dangerous had to be destroyed. He -winced. He hated the idea of even touching them. - -Yet it had to be done. He realized that, and when he had locked the -door of his library, he opened the secret press into which he had -thrust Basil Hallward's coat and bag. A huge fire was blazing. He -piled another log on it. The smell of the singeing clothes and burning -leather was horrible. It took him three-quarters of an hour to consume -everything. At the end he felt faint and sick, and having lit some -Algerian pastilles in a pierced copper brazier, he bathed his hands and -forehead with a cool musk-scented vinegar. - -Suddenly he started. His eyes grew strangely bright, and he gnawed -nervously at his underlip. Between two of the windows stood a large -Florentine cabinet, made out of ebony and inlaid with ivory and blue -lapis. He watched it as though it were a thing that could fascinate -and make afraid, as though it held something that he longed for and yet -almost loathed. His breath quickened. A mad craving came over him. -He lit a cigarette and then threw it away. His eyelids drooped till -the long fringed lashes almost touched his cheek. But he still watched -the cabinet. At last he got up from the sofa on which he had been -lying, went over to it, and having unlocked it, touched some hidden -spring. A triangular drawer passed slowly out. His fingers moved -instinctively towards it, dipped in, and closed on something. It was a -small Chinese box of black and gold-dust lacquer, elaborately wrought, -the sides patterned with curved waves, and the silken cords hung with -round crystals and tasselled in plaited metal threads. He opened it. -Inside was a green paste, waxy in lustre, the odour curiously heavy and -persistent. - -He hesitated for some moments, with a strangely immobile smile upon his -face. Then shivering, though the atmosphere of the room was terribly -hot, he drew himself up and glanced at the clock. It was twenty -minutes to twelve. He put the box back, shutting the cabinet doors as -he did so, and went into his bedroom. - -As midnight was striking bronze blows upon the dusky air, Dorian Gray, -dressed commonly, and with a muffler wrapped round his throat, crept -quietly out of his house. In Bond Street he found a hansom with a good -horse. He hailed it and in a low voice gave the driver an address. - -The man shook his head. "It is too far for me," he muttered. - -"Here is a sovereign for you," said Dorian. "You shall have another if -you drive fast." - -"All right, sir," answered the man, "you will be there in an hour," and -after his fare had got in he turned his horse round and drove rapidly -towards the river. - - - -CHAPTER 16 - -A cold rain began to fall, and the blurred street-lamps looked ghastly -in the dripping mist. The public-houses were just closing, and dim men -and women were clustering in broken groups round their doors. From -some of the bars came the sound of horrible laughter. In others, -drunkards brawled and screamed. - -Lying back in the hansom, with his hat pulled over his forehead, Dorian -Gray watched with listless eyes the sordid shame of the great city, and -now and then he repeated to himself the words that Lord Henry had said -to him on the first day they had met, "To cure the soul by means of the -senses, and the senses by means of the soul." Yes, that was the -secret. He had often tried it, and would try it again now. There were -opium dens where one could buy oblivion, dens of horror where the -memory of old sins could be destroyed by the madness of sins that were -new. - -The moon hung low in the sky like a yellow skull. From time to time a -huge misshapen cloud stretched a long arm across and hid it. The -gas-lamps grew fewer, and the streets more narrow and gloomy. Once the -man lost his way and had to drive back half a mile. A steam rose from -the horse as it splashed up the puddles. The sidewindows of the hansom -were clogged with a grey-flannel mist. - -"To cure the soul by means of the senses, and the senses by means of -the soul!" How the words rang in his ears! His soul, certainly, was -sick to death. Was it true that the senses could cure it? Innocent -blood had been spilled. What could atone for that? Ah! for that there -was no atonement; but though forgiveness was impossible, forgetfulness -was possible still, and he was determined to forget, to stamp the thing -out, to crush it as one would crush the adder that had stung one. -Indeed, what right had Basil to have spoken to him as he had done? Who -had made him a judge over others? He had said things that were -dreadful, horrible, not to be endured. - -On and on plodded the hansom, going slower, it seemed to him, at each -step. He thrust up the trap and called to the man to drive faster. -The hideous hunger for opium began to gnaw at him. His throat burned -and his delicate hands twitched nervously together. He struck at the -horse madly with his stick. The driver laughed and whipped up. He -laughed in answer, and the man was silent. - -The way seemed interminable, and the streets like the black web of some -sprawling spider. The monotony became unbearable, and as the mist -thickened, he felt afraid. - -Then they passed by lonely brickfields. The fog was lighter here, and -he could see the strange, bottle-shaped kilns with their orange, -fanlike tongues of fire. A dog barked as they went by, and far away in -the darkness some wandering sea-gull screamed. The horse stumbled in a -rut, then swerved aside and broke into a gallop. - -After some time they left the clay road and rattled again over -rough-paven streets. Most of the windows were dark, but now and then -fantastic shadows were silhouetted against some lamplit blind. He -watched them curiously. They moved like monstrous marionettes and made -gestures like live things. He hated them. A dull rage was in his -heart. As they turned a corner, a woman yelled something at them from -an open door, and two men ran after the hansom for about a hundred -yards. The driver beat at them with his whip. - -It is said that passion makes one think in a circle. Certainly with -hideous iteration the bitten lips of Dorian Gray shaped and reshaped -those subtle words that dealt with soul and sense, till he had found in -them the full expression, as it were, of his mood, and justified, by -intellectual approval, passions that without such justification would -still have dominated his temper. From cell to cell of his brain crept -the one thought; and the wild desire to live, most terrible of all -man's appetites, quickened into force each trembling nerve and fibre. -Ugliness that had once been hateful to him because it made things real, -became dear to him now for that very reason. Ugliness was the one -reality. The coarse brawl, the loathsome den, the crude violence of -disordered life, the very vileness of thief and outcast, were more -vivid, in their intense actuality of impression, than all the gracious -shapes of art, the dreamy shadows of song. They were what he needed -for forgetfulness. In three days he would be free. - -Suddenly the man drew up with a jerk at the top of a dark lane. Over -the low roofs and jagged chimney-stacks of the houses rose the black -masts of ships. Wreaths of white mist clung like ghostly sails to the -yards. - -"Somewhere about here, sir, ain't it?" he asked huskily through the -trap. - -Dorian started and peered round. "This will do," he answered, and -having got out hastily and given the driver the extra fare he had -promised him, he walked quickly in the direction of the quay. Here and -there a lantern gleamed at the stern of some huge merchantman. The -light shook and splintered in the puddles. A red glare came from an -outward-bound steamer that was coaling. The slimy pavement looked like -a wet mackintosh. - -He hurried on towards the left, glancing back now and then to see if he -was being followed. In about seven or eight minutes he reached a small -shabby house that was wedged in between two gaunt factories. In one of -the top-windows stood a lamp. He stopped and gave a peculiar knock. - -After a little time he heard steps in the passage and the chain being -unhooked. The door opened quietly, and he went in without saying a -word to the squat misshapen figure that flattened itself into the -shadow as he passed. At the end of the hall hung a tattered green -curtain that swayed and shook in the gusty wind which had followed him -in from the street. He dragged it aside and entered a long low room -which looked as if it had once been a third-rate dancing-saloon. Shrill -flaring gas-jets, dulled and distorted in the fly-blown mirrors that -faced them, were ranged round the walls. Greasy reflectors of ribbed -tin backed them, making quivering disks of light. The floor was -covered with ochre-coloured sawdust, trampled here and there into mud, -and stained with dark rings of spilled liquor. Some Malays were -crouching by a little charcoal stove, playing with bone counters and -showing their white teeth as they chattered. In one corner, with his -head buried in his arms, a sailor sprawled over a table, and by the -tawdrily painted bar that ran across one complete side stood two -haggard women, mocking an old man who was brushing the sleeves of his -coat with an expression of disgust. "He thinks he's got red ants on -him," laughed one of them, as Dorian passed by. The man looked at her -in terror and began to whimper. - -At the end of the room there was a little staircase, leading to a -darkened chamber. As Dorian hurried up its three rickety steps, the -heavy odour of opium met him. He heaved a deep breath, and his -nostrils quivered with pleasure. When he entered, a young man with -smooth yellow hair, who was bending over a lamp lighting a long thin -pipe, looked up at him and nodded in a hesitating manner. - -"You here, Adrian?" muttered Dorian. - -"Where else should I be?" he answered, listlessly. "None of the chaps -will speak to me now." - -"I thought you had left England." - -"Darlington is not going to do anything. My brother paid the bill at -last. George doesn't speak to me either.... I don't care," he added -with a sigh. "As long as one has this stuff, one doesn't want friends. -I think I have had too many friends." - -Dorian winced and looked round at the grotesque things that lay in such -fantastic postures on the ragged mattresses. The twisted limbs, the -gaping mouths, the staring lustreless eyes, fascinated him. He knew in -what strange heavens they were suffering, and what dull hells were -teaching them the secret of some new joy. They were better off than he -was. He was prisoned in thought. Memory, like a horrible malady, was -eating his soul away. From time to time he seemed to see the eyes of -Basil Hallward looking at him. Yet he felt he could not stay. The -presence of Adrian Singleton troubled him. He wanted to be where no -one would know who he was. He wanted to escape from himself. - -"I am going on to the other place," he said after a pause. - -"On the wharf?" - -"Yes." - -"That mad-cat is sure to be there. They won't have her in this place -now." - -Dorian shrugged his shoulders. "I am sick of women who love one. -Women who hate one are much more interesting. Besides, the stuff is -better." - -"Much the same." - -"I like it better. Come and have something to drink. I must have -something." - -"I don't want anything," murmured the young man. - -"Never mind." - -Adrian Singleton rose up wearily and followed Dorian to the bar. A -half-caste, in a ragged turban and a shabby ulster, grinned a hideous -greeting as he thrust a bottle of brandy and two tumblers in front of -them. The women sidled up and began to chatter. Dorian turned his -back on them and said something in a low voice to Adrian Singleton. - -A crooked smile, like a Malay crease, writhed across the face of one of -the women. "We are very proud to-night," she sneered. - -"For God's sake don't talk to me," cried Dorian, stamping his foot on -the ground. "What do you want? Money? Here it is. Don't ever talk -to me again." - -Two red sparks flashed for a moment in the woman's sodden eyes, then -flickered out and left them dull and glazed. She tossed her head and -raked the coins off the counter with greedy fingers. Her companion -watched her enviously. - -"It's no use," sighed Adrian Singleton. "I don't care to go back. -What does it matter? I am quite happy here." - -"You will write to me if you want anything, won't you?" said Dorian, -after a pause. - -"Perhaps." - -"Good night, then." - -"Good night," answered the young man, passing up the steps and wiping -his parched mouth with a handkerchief. - -Dorian walked to the door with a look of pain in his face. As he drew -the curtain aside, a hideous laugh broke from the painted lips of the -woman who had taken his money. "There goes the devil's bargain!" she -hiccoughed, in a hoarse voice. - -"Curse you!" he answered, "don't call me that." - -She snapped her fingers. "Prince Charming is what you like to be -called, ain't it?" she yelled after him. - -The drowsy sailor leaped to his feet as she spoke, and looked wildly -round. The sound of the shutting of the hall door fell on his ear. He -rushed out as if in pursuit. - -Dorian Gray hurried along the quay through the drizzling rain. His -meeting with Adrian Singleton had strangely moved him, and he wondered -if the ruin of that young life was really to be laid at his door, as -Basil Hallward had said to him with such infamy of insult. He bit his -lip, and for a few seconds his eyes grew sad. Yet, after all, what did -it matter to him? One's days were too brief to take the burden of -another's errors on one's shoulders. Each man lived his own life and -paid his own price for living it. The only pity was one had to pay so -often for a single fault. One had to pay over and over again, indeed. -In her dealings with man, destiny never closed her accounts. - -There are moments, psychologists tell us, when the passion for sin, or -for what the world calls sin, so dominates a nature that every fibre of -the body, as every cell of the brain, seems to be instinct with fearful -impulses. Men and women at such moments lose the freedom of their -will. They move to their terrible end as automatons move. Choice is -taken from them, and conscience is either killed, or, if it lives at -all, lives but to give rebellion its fascination and disobedience its -charm. For all sins, as theologians weary not of reminding us, are -sins of disobedience. When that high spirit, that morning star of -evil, fell from heaven, it was as a rebel that he fell. - -Callous, concentrated on evil, with stained mind, and soul hungry for -rebellion, Dorian Gray hastened on, quickening his step as he went, but -as he darted aside into a dim archway, that had served him often as a -short cut to the ill-famed place where he was going, he felt himself -suddenly seized from behind, and before he had time to defend himself, -he was thrust back against the wall, with a brutal hand round his -throat. - -He struggled madly for life, and by a terrible effort wrenched the -tightening fingers away. In a second he heard the click of a revolver, -and saw the gleam of a polished barrel, pointing straight at his head, -and the dusky form of a short, thick-set man facing him. - -"What do you want?" he gasped. - -"Keep quiet," said the man. "If you stir, I shoot you." - -"You are mad. What have I done to you?" - -"You wrecked the life of Sibyl Vane," was the answer, "and Sibyl Vane -was my sister. She killed herself. I know it. Her death is at your -door. I swore I would kill you in return. For years I have sought -you. I had no clue, no trace. The two people who could have described -you were dead. I knew nothing of you but the pet name she used to call -you. I heard it to-night by chance. Make your peace with God, for -to-night you are going to die." - -Dorian Gray grew sick with fear. "I never knew her," he stammered. "I -never heard of her. You are mad." - -"You had better confess your sin, for as sure as I am James Vane, you -are going to die." There was a horrible moment. Dorian did not know -what to say or do. "Down on your knees!" growled the man. "I give you -one minute to make your peace--no more. I go on board to-night for -India, and I must do my job first. One minute. That's all." - -Dorian's arms fell to his side. Paralysed with terror, he did not know -what to do. Suddenly a wild hope flashed across his brain. "Stop," he -cried. "How long ago is it since your sister died? Quick, tell me!" - -"Eighteen years," said the man. "Why do you ask me? What do years -matter?" - -"Eighteen years," laughed Dorian Gray, with a touch of triumph in his -voice. "Eighteen years! Set me under the lamp and look at my face!" - -James Vane hesitated for a moment, not understanding what was meant. -Then he seized Dorian Gray and dragged him from the archway. - -Dim and wavering as was the wind-blown light, yet it served to show him -the hideous error, as it seemed, into which he had fallen, for the face -of the man he had sought to kill had all the bloom of boyhood, all the -unstained purity of youth. He seemed little more than a lad of twenty -summers, hardly older, if older indeed at all, than his sister had been -when they had parted so many years ago. It was obvious that this was -not the man who had destroyed her life. - -He loosened his hold and reeled back. "My God! my God!" he cried, "and -I would have murdered you!" - -Dorian Gray drew a long breath. "You have been on the brink of -committing a terrible crime, my man," he said, looking at him sternly. -"Let this be a warning to you not to take vengeance into your own -hands." - -"Forgive me, sir," muttered James Vane. "I was deceived. A chance -word I heard in that damned den set me on the wrong track." - -"You had better go home and put that pistol away, or you may get into -trouble," said Dorian, turning on his heel and going slowly down the -street. - -James Vane stood on the pavement in horror. He was trembling from head -to foot. After a little while, a black shadow that had been creeping -along the dripping wall moved out into the light and came close to him -with stealthy footsteps. He felt a hand laid on his arm and looked -round with a start. It was one of the women who had been drinking at -the bar. - -"Why didn't you kill him?" she hissed out, putting haggard face quite -close to his. "I knew you were following him when you rushed out from -Daly's. You fool! You should have killed him. He has lots of money, -and he's as bad as bad." - -"He is not the man I am looking for," he answered, "and I want no man's -money. I want a man's life. The man whose life I want must be nearly -forty now. This one is little more than a boy. Thank God, I have not -got his blood upon my hands." - -The woman gave a bitter laugh. "Little more than a boy!" she sneered. -"Why, man, it's nigh on eighteen years since Prince Charming made me -what I am." - -"You lie!" cried James Vane. - -She raised her hand up to heaven. "Before God I am telling the truth," -she cried. - -"Before God?" - -"Strike me dumb if it ain't so. He is the worst one that comes here. -They say he has sold himself to the devil for a pretty face. It's nigh -on eighteen years since I met him. He hasn't changed much since then. -I have, though," she added, with a sickly leer. - -"You swear this?" - -"I swear it," came in hoarse echo from her flat mouth. "But don't give -me away to him," she whined; "I am afraid of him. Let me have some -money for my night's lodging." - -He broke from her with an oath and rushed to the corner of the street, -but Dorian Gray had disappeared. When he looked back, the woman had -vanished also. - - - -CHAPTER 17 - -A week later Dorian Gray was sitting in the conservatory at Selby -Royal, talking to the pretty Duchess of Monmouth, who with her husband, -a jaded-looking man of sixty, was amongst his guests. It was tea-time, -and the mellow light of the huge, lace-covered lamp that stood on the -table lit up the delicate china and hammered silver of the service at -which the duchess was presiding. Her white hands were moving daintily -among the cups, and her full red lips were smiling at something that -Dorian had whispered to her. Lord Henry was lying back in a -silk-draped wicker chair, looking at them. On a peach-coloured divan -sat Lady Narborough, pretending to listen to the duke's description of -the last Brazilian beetle that he had added to his collection. Three -young men in elaborate smoking-suits were handing tea-cakes to some of -the women. The house-party consisted of twelve people, and there were -more expected to arrive on the next day. - -"What are you two talking about?" said Lord Henry, strolling over to -the table and putting his cup down. "I hope Dorian has told you about -my plan for rechristening everything, Gladys. It is a delightful idea." - -"But I don't want to be rechristened, Harry," rejoined the duchess, -looking up at him with her wonderful eyes. "I am quite satisfied with -my own name, and I am sure Mr. Gray should be satisfied with his." - -"My dear Gladys, I would not alter either name for the world. They are -both perfect. I was thinking chiefly of flowers. Yesterday I cut an -orchid, for my button-hole. It was a marvellous spotted thing, as -effective as the seven deadly sins. In a thoughtless moment I asked -one of the gardeners what it was called. He told me it was a fine -specimen of _Robinsoniana_, or something dreadful of that kind. It is a -sad truth, but we have lost the faculty of giving lovely names to -things. Names are everything. I never quarrel with actions. My one -quarrel is with words. That is the reason I hate vulgar realism in -literature. The man who could call a spade a spade should be compelled -to use one. It is the only thing he is fit for." - -"Then what should we call you, Harry?" she asked. - -"His name is Prince Paradox," said Dorian. - -"I recognize him in a flash," exclaimed the duchess. - -"I won't hear of it," laughed Lord Henry, sinking into a chair. "From -a label there is no escape! I refuse the title." - -"Royalties may not abdicate," fell as a warning from pretty lips. - -"You wish me to defend my throne, then?" - -"Yes." - -"I give the truths of to-morrow." - -"I prefer the mistakes of to-day," she answered. - -"You disarm me, Gladys," he cried, catching the wilfulness of her mood. - -"Of your shield, Harry, not of your spear." - -"I never tilt against beauty," he said, with a wave of his hand. - -"That is your error, Harry, believe me. You value beauty far too much." - -"How can you say that? I admit that I think that it is better to be -beautiful than to be good. But on the other hand, no one is more ready -than I am to acknowledge that it is better to be good than to be ugly." - -"Ugliness is one of the seven deadly sins, then?" cried the duchess. -"What becomes of your simile about the orchid?" - -"Ugliness is one of the seven deadly virtues, Gladys. You, as a good -Tory, must not underrate them. Beer, the Bible, and the seven deadly -virtues have made our England what she is." - -"You don't like your country, then?" she asked. - -"I live in it." - -"That you may censure it the better." - -"Would you have me take the verdict of Europe on it?" he inquired. - -"What do they say of us?" - -"That Tartuffe has emigrated to England and opened a shop." - -"Is that yours, Harry?" - -"I give it to you." - -"I could not use it. It is too true." - -"You need not be afraid. Our countrymen never recognize a description." - -"They are practical." - -"They are more cunning than practical. When they make up their ledger, -they balance stupidity by wealth, and vice by hypocrisy." - -"Still, we have done great things." - -"Great things have been thrust on us, Gladys." - -"We have carried their burden." - -"Only as far as the Stock Exchange." - -She shook her head. "I believe in the race," she cried. - -"It represents the survival of the pushing." - -"It has development." - -"Decay fascinates me more." - -"What of art?" she asked. - -"It is a malady." - -"Love?" - -"An illusion." - -"Religion?" - -"The fashionable substitute for belief." - -"You are a sceptic." - -"Never! Scepticism is the beginning of faith." - -"What are you?" - -"To define is to limit." - -"Give me a clue." - -"Threads snap. You would lose your way in the labyrinth." - -"You bewilder me. Let us talk of some one else." - -"Our host is a delightful topic. Years ago he was christened Prince -Charming." - -"Ah! don't remind me of that," cried Dorian Gray. - -"Our host is rather horrid this evening," answered the duchess, -colouring. "I believe he thinks that Monmouth married me on purely -scientific principles as the best specimen he could find of a modern -butterfly." - -"Well, I hope he won't stick pins into you, Duchess," laughed Dorian. - -"Oh! my maid does that already, Mr. Gray, when she is annoyed with me." - -"And what does she get annoyed with you about, Duchess?" - -"For the most trivial things, Mr. Gray, I assure you. Usually because -I come in at ten minutes to nine and tell her that I must be dressed by -half-past eight." - -"How unreasonable of her! You should give her warning." - -"I daren't, Mr. Gray. Why, she invents hats for me. You remember the -one I wore at Lady Hilstone's garden-party? You don't, but it is nice -of you to pretend that you do. Well, she made it out of nothing. All -good hats are made out of nothing." - -"Like all good reputations, Gladys," interrupted Lord Henry. "Every -effect that one produces gives one an enemy. To be popular one must be -a mediocrity." - -"Not with women," said the duchess, shaking her head; "and women rule -the world. I assure you we can't bear mediocrities. We women, as some -one says, love with our ears, just as you men love with your eyes, if -you ever love at all." - -"It seems to me that we never do anything else," murmured Dorian. - -"Ah! then, you never really love, Mr. Gray," answered the duchess with -mock sadness. - -"My dear Gladys!" cried Lord Henry. "How can you say that? Romance -lives by repetition, and repetition converts an appetite into an art. -Besides, each time that one loves is the only time one has ever loved. -Difference of object does not alter singleness of passion. It merely -intensifies it. We can have in life but one great experience at best, -and the secret of life is to reproduce that experience as often as -possible." - -"Even when one has been wounded by it, Harry?" asked the duchess after -a pause. - -"Especially when one has been wounded by it," answered Lord Henry. - -The duchess turned and looked at Dorian Gray with a curious expression -in her eyes. "What do you say to that, Mr. Gray?" she inquired. - -Dorian hesitated for a moment. Then he threw his head back and -laughed. "I always agree with Harry, Duchess." - -"Even when he is wrong?" - -"Harry is never wrong, Duchess." - -"And does his philosophy make you happy?" - -"I have never searched for happiness. Who wants happiness? I have -searched for pleasure." - -"And found it, Mr. Gray?" - -"Often. Too often." - -The duchess sighed. "I am searching for peace," she said, "and if I -don't go and dress, I shall have none this evening." - -"Let me get you some orchids, Duchess," cried Dorian, starting to his -feet and walking down the conservatory. - -"You are flirting disgracefully with him," said Lord Henry to his -cousin. "You had better take care. He is very fascinating." - -"If he were not, there would be no battle." - -"Greek meets Greek, then?" - -"I am on the side of the Trojans. They fought for a woman." - -"They were defeated." - -"There are worse things than capture," she answered. - -"You gallop with a loose rein." - -"Pace gives life," was the _riposte_. - -"I shall write it in my diary to-night." - -"What?" - -"That a burnt child loves the fire." - -"I am not even singed. My wings are untouched." - -"You use them for everything, except flight." - -"Courage has passed from men to women. It is a new experience for us." - -"You have a rival." - -"Who?" - -He laughed. "Lady Narborough," he whispered. "She perfectly adores -him." - -"You fill me with apprehension. The appeal to antiquity is fatal to us -who are romanticists." - -"Romanticists! You have all the methods of science." - -"Men have educated us." - -"But not explained you." - -"Describe us as a sex," was her challenge. - -"Sphinxes without secrets." - -She looked at him, smiling. "How long Mr. Gray is!" she said. "Let us -go and help him. I have not yet told him the colour of my frock." - -"Ah! you must suit your frock to his flowers, Gladys." - -"That would be a premature surrender." - -"Romantic art begins with its climax." - -"I must keep an opportunity for retreat." - -"In the Parthian manner?" - -"They found safety in the desert. I could not do that." - -"Women are not always allowed a choice," he answered, but hardly had he -finished the sentence before from the far end of the conservatory came -a stifled groan, followed by the dull sound of a heavy fall. Everybody -started up. The duchess stood motionless in horror. And with fear in -his eyes, Lord Henry rushed through the flapping palms to find Dorian -Gray lying face downwards on the tiled floor in a deathlike swoon. - -He was carried at once into the blue drawing-room and laid upon one of -the sofas. After a short time, he came to himself and looked round -with a dazed expression. - -"What has happened?" he asked. "Oh! I remember. Am I safe here, -Harry?" He began to tremble. - -"My dear Dorian," answered Lord Henry, "you merely fainted. That was -all. You must have overtired yourself. You had better not come down -to dinner. I will take your place." - -"No, I will come down," he said, struggling to his feet. "I would -rather come down. I must not be alone." - -He went to his room and dressed. There was a wild recklessness of -gaiety in his manner as he sat at table, but now and then a thrill of -terror ran through him when he remembered that, pressed against the -window of the conservatory, like a white handkerchief, he had seen the -face of James Vane watching him. - - - -CHAPTER 18 - -The next day he did not leave the house, and, indeed, spent most of the -time in his own room, sick with a wild terror of dying, and yet -indifferent to life itself. The consciousness of being hunted, snared, -tracked down, had begun to dominate him. If the tapestry did but -tremble in the wind, he shook. The dead leaves that were blown against -the leaded panes seemed to him like his own wasted resolutions and wild -regrets. When he closed his eyes, he saw again the sailor's face -peering through the mist-stained glass, and horror seemed once more to -lay its hand upon his heart. - -But perhaps it had been only his fancy that had called vengeance out of -the night and set the hideous shapes of punishment before him. Actual -life was chaos, but there was something terribly logical in the -imagination. It was the imagination that set remorse to dog the feet -of sin. It was the imagination that made each crime bear its misshapen -brood. In the common world of fact the wicked were not punished, nor -the good rewarded. Success was given to the strong, failure thrust -upon the weak. That was all. Besides, had any stranger been prowling -round the house, he would have been seen by the servants or the -keepers. Had any foot-marks been found on the flower-beds, the -gardeners would have reported it. Yes, it had been merely fancy. -Sibyl Vane's brother had not come back to kill him. He had sailed away -in his ship to founder in some winter sea. From him, at any rate, he -was safe. Why, the man did not know who he was, could not know who he -was. The mask of youth had saved him. - -And yet if it had been merely an illusion, how terrible it was to think -that conscience could raise such fearful phantoms, and give them -visible form, and make them move before one! What sort of life would -his be if, day and night, shadows of his crime were to peer at him from -silent corners, to mock him from secret places, to whisper in his ear -as he sat at the feast, to wake him with icy fingers as he lay asleep! -As the thought crept through his brain, he grew pale with terror, and -the air seemed to him to have become suddenly colder. Oh! in what a -wild hour of madness he had killed his friend! How ghastly the mere -memory of the scene! He saw it all again. Each hideous detail came -back to him with added horror. Out of the black cave of time, terrible -and swathed in scarlet, rose the image of his sin. When Lord Henry -came in at six o'clock, he found him crying as one whose heart will -break. - -It was not till the third day that he ventured to go out. There was -something in the clear, pine-scented air of that winter morning that -seemed to bring him back his joyousness and his ardour for life. But -it was not merely the physical conditions of environment that had -caused the change. His own nature had revolted against the excess of -anguish that had sought to maim and mar the perfection of its calm. -With subtle and finely wrought temperaments it is always so. Their -strong passions must either bruise or bend. They either slay the man, -or themselves die. Shallow sorrows and shallow loves live on. The -loves and sorrows that are great are destroyed by their own plenitude. -Besides, he had convinced himself that he had been the victim of a -terror-stricken imagination, and looked back now on his fears with -something of pity and not a little of contempt. - -After breakfast, he walked with the duchess for an hour in the garden -and then drove across the park to join the shooting-party. The crisp -frost lay like salt upon the grass. The sky was an inverted cup of -blue metal. A thin film of ice bordered the flat, reed-grown lake. - -At the corner of the pine-wood he caught sight of Sir Geoffrey -Clouston, the duchess's brother, jerking two spent cartridges out of -his gun. He jumped from the cart, and having told the groom to take -the mare home, made his way towards his guest through the withered -bracken and rough undergrowth. - -"Have you had good sport, Geoffrey?" he asked. - -"Not very good, Dorian. I think most of the birds have gone to the -open. I dare say it will be better after lunch, when we get to new -ground." - -Dorian strolled along by his side. The keen aromatic air, the brown -and red lights that glimmered in the wood, the hoarse cries of the -beaters ringing out from time to time, and the sharp snaps of the guns -that followed, fascinated him and filled him with a sense of delightful -freedom. He was dominated by the carelessness of happiness, by the -high indifference of joy. - -Suddenly from a lumpy tussock of old grass some twenty yards in front -of them, with black-tipped ears erect and long hinder limbs throwing it -forward, started a hare. It bolted for a thicket of alders. Sir -Geoffrey put his gun to his shoulder, but there was something in the -animal's grace of movement that strangely charmed Dorian Gray, and he -cried out at once, "Don't shoot it, Geoffrey. Let it live." - -"What nonsense, Dorian!" laughed his companion, and as the hare bounded -into the thicket, he fired. There were two cries heard, the cry of a -hare in pain, which is dreadful, the cry of a man in agony, which is -worse. - -"Good heavens! I have hit a beater!" exclaimed Sir Geoffrey. "What an -ass the man was to get in front of the guns! Stop shooting there!" he -called out at the top of his voice. "A man is hurt." - -The head-keeper came running up with a stick in his hand. - -"Where, sir? Where is he?" he shouted. At the same time, the firing -ceased along the line. - -"Here," answered Sir Geoffrey angrily, hurrying towards the thicket. -"Why on earth don't you keep your men back? Spoiled my shooting for -the day." - -Dorian watched them as they plunged into the alder-clump, brushing the -lithe swinging branches aside. In a few moments they emerged, dragging -a body after them into the sunlight. He turned away in horror. It -seemed to him that misfortune followed wherever he went. He heard Sir -Geoffrey ask if the man was really dead, and the affirmative answer of -the keeper. The wood seemed to him to have become suddenly alive with -faces. There was the trampling of myriad feet and the low buzz of -voices. A great copper-breasted pheasant came beating through the -boughs overhead. - -After a few moments--that were to him, in his perturbed state, like -endless hours of pain--he felt a hand laid on his shoulder. He started -and looked round. - -"Dorian," said Lord Henry, "I had better tell them that the shooting is -stopped for to-day. It would not look well to go on." - -"I wish it were stopped for ever, Harry," he answered bitterly. "The -whole thing is hideous and cruel. Is the man ...?" - -He could not finish the sentence. - -"I am afraid so," rejoined Lord Henry. "He got the whole charge of -shot in his chest. He must have died almost instantaneously. Come; -let us go home." - -They walked side by side in the direction of the avenue for nearly -fifty yards without speaking. Then Dorian looked at Lord Henry and -said, with a heavy sigh, "It is a bad omen, Harry, a very bad omen." - -"What is?" asked Lord Henry. "Oh! this accident, I suppose. My dear -fellow, it can't be helped. It was the man's own fault. Why did he -get in front of the guns? Besides, it is nothing to us. It is rather -awkward for Geoffrey, of course. It does not do to pepper beaters. It -makes people think that one is a wild shot. And Geoffrey is not; he -shoots very straight. But there is no use talking about the matter." - -Dorian shook his head. "It is a bad omen, Harry. I feel as if -something horrible were going to happen to some of us. To myself, -perhaps," he added, passing his hand over his eyes, with a gesture of -pain. - -The elder man laughed. "The only horrible thing in the world is _ennui_, -Dorian. That is the one sin for which there is no forgiveness. But we -are not likely to suffer from it unless these fellows keep chattering -about this thing at dinner. I must tell them that the subject is to be -tabooed. As for omens, there is no such thing as an omen. Destiny -does not send us heralds. She is too wise or too cruel for that. -Besides, what on earth could happen to you, Dorian? You have -everything in the world that a man can want. There is no one who would -not be delighted to change places with you." - -"There is no one with whom I would not change places, Harry. Don't -laugh like that. I am telling you the truth. The wretched peasant who -has just died is better off than I am. I have no terror of death. It -is the coming of death that terrifies me. Its monstrous wings seem to -wheel in the leaden air around me. Good heavens! don't you see a man -moving behind the trees there, watching me, waiting for me?" - -Lord Henry looked in the direction in which the trembling gloved hand -was pointing. "Yes," he said, smiling, "I see the gardener waiting for -you. I suppose he wants to ask you what flowers you wish to have on -the table to-night. How absurdly nervous you are, my dear fellow! You -must come and see my doctor, when we get back to town." - -Dorian heaved a sigh of relief as he saw the gardener approaching. The -man touched his hat, glanced for a moment at Lord Henry in a hesitating -manner, and then produced a letter, which he handed to his master. -"Her Grace told me to wait for an answer," he murmured. - -Dorian put the letter into his pocket. "Tell her Grace that I am -coming in," he said, coldly. The man turned round and went rapidly in -the direction of the house. - -"How fond women are of doing dangerous things!" laughed Lord Henry. -"It is one of the qualities in them that I admire most. A woman will -flirt with anybody in the world as long as other people are looking on." - -"How fond you are of saying dangerous things, Harry! In the present -instance, you are quite astray. I like the duchess very much, but I -don't love her." - -"And the duchess loves you very much, but she likes you less, so you -are excellently matched." - -"You are talking scandal, Harry, and there is never any basis for -scandal." - -"The basis of every scandal is an immoral certainty," said Lord Henry, -lighting a cigarette. - -"You would sacrifice anybody, Harry, for the sake of an epigram." - -"The world goes to the altar of its own accord," was the answer. - -"I wish I could love," cried Dorian Gray with a deep note of pathos in -his voice. "But I seem to have lost the passion and forgotten the -desire. I am too much concentrated on myself. My own personality has -become a burden to me. I want to escape, to go away, to forget. It -was silly of me to come down here at all. I think I shall send a wire -to Harvey to have the yacht got ready. On a yacht one is safe." - -"Safe from what, Dorian? You are in some trouble. Why not tell me -what it is? You know I would help you." - -"I can't tell you, Harry," he answered sadly. "And I dare say it is -only a fancy of mine. This unfortunate accident has upset me. I have -a horrible presentiment that something of the kind may happen to me." - -"What nonsense!" - -"I hope it is, but I can't help feeling it. Ah! here is the duchess, -looking like Artemis in a tailor-made gown. You see we have come back, -Duchess." - -"I have heard all about it, Mr. Gray," she answered. "Poor Geoffrey is -terribly upset. And it seems that you asked him not to shoot the hare. -How curious!" - -"Yes, it was very curious. I don't know what made me say it. Some -whim, I suppose. It looked the loveliest of little live things. But I -am sorry they told you about the man. It is a hideous subject." - -"It is an annoying subject," broke in Lord Henry. "It has no -psychological value at all. Now if Geoffrey had done the thing on -purpose, how interesting he would be! I should like to know some one -who had committed a real murder." - -"How horrid of you, Harry!" cried the duchess. "Isn't it, Mr. Gray? -Harry, Mr. Gray is ill again. He is going to faint." - -Dorian drew himself up with an effort and smiled. "It is nothing, -Duchess," he murmured; "my nerves are dreadfully out of order. That is -all. I am afraid I walked too far this morning. I didn't hear what -Harry said. Was it very bad? You must tell me some other time. I -think I must go and lie down. You will excuse me, won't you?" - -They had reached the great flight of steps that led from the -conservatory on to the terrace. As the glass door closed behind -Dorian, Lord Henry turned and looked at the duchess with his slumberous -eyes. "Are you very much in love with him?" he asked. - -She did not answer for some time, but stood gazing at the landscape. -"I wish I knew," she said at last. - -He shook his head. "Knowledge would be fatal. It is the uncertainty -that charms one. A mist makes things wonderful." - -"One may lose one's way." - -"All ways end at the same point, my dear Gladys." - -"What is that?" - -"Disillusion." - -"It was my _debut_ in life," she sighed. - -"It came to you crowned." - -"I am tired of strawberry leaves." - -"They become you." - -"Only in public." - -"You would miss them," said Lord Henry. - -"I will not part with a petal." - -"Monmouth has ears." - -"Old age is dull of hearing." - -"Has he never been jealous?" - -"I wish he had been." - -He glanced about as if in search of something. "What are you looking -for?" she inquired. - -"The button from your foil," he answered. "You have dropped it." - -She laughed. "I have still the mask." - -"It makes your eyes lovelier," was his reply. - -She laughed again. Her teeth showed like white seeds in a scarlet -fruit. - -Upstairs, in his own room, Dorian Gray was lying on a sofa, with terror -in every tingling fibre of his body. Life had suddenly become too -hideous a burden for him to bear. The dreadful death of the unlucky -beater, shot in the thicket like a wild animal, had seemed to him to -pre-figure death for himself also. He had nearly swooned at what Lord -Henry had said in a chance mood of cynical jesting. - -At five o'clock he rang his bell for his servant and gave him orders to -pack his things for the night-express to town, and to have the brougham -at the door by eight-thirty. He was determined not to sleep another -night at Selby Royal. It was an ill-omened place. Death walked there -in the sunlight. The grass of the forest had been spotted with blood. - -Then he wrote a note to Lord Henry, telling him that he was going up to -town to consult his doctor and asking him to entertain his guests in -his absence. As he was putting it into the envelope, a knock came to -the door, and his valet informed him that the head-keeper wished to see -him. He frowned and bit his lip. "Send him in," he muttered, after -some moments' hesitation. - -As soon as the man entered, Dorian pulled his chequebook out of a -drawer and spread it out before him. - -"I suppose you have come about the unfortunate accident of this -morning, Thornton?" he said, taking up a pen. - -"Yes, sir," answered the gamekeeper. - -"Was the poor fellow married? Had he any people dependent on him?" -asked Dorian, looking bored. "If so, I should not like them to be left -in want, and will send them any sum of money you may think necessary." - -"We don't know who he is, sir. That is what I took the liberty of -coming to you about." - -"Don't know who he is?" said Dorian, listlessly. "What do you mean? -Wasn't he one of your men?" - -"No, sir. Never saw him before. Seems like a sailor, sir." - -The pen dropped from Dorian Gray's hand, and he felt as if his heart -had suddenly stopped beating. "A sailor?" he cried out. "Did you say -a sailor?" - -"Yes, sir. He looks as if he had been a sort of sailor; tattooed on -both arms, and that kind of thing." - -"Was there anything found on him?" said Dorian, leaning forward and -looking at the man with startled eyes. "Anything that would tell his -name?" - -"Some money, sir--not much, and a six-shooter. There was no name of any -kind. A decent-looking man, sir, but rough-like. A sort of sailor we -think." - -Dorian started to his feet. A terrible hope fluttered past him. He -clutched at it madly. "Where is the body?" he exclaimed. "Quick! I -must see it at once." - -"It is in an empty stable in the Home Farm, sir. The folk don't like -to have that sort of thing in their houses. They say a corpse brings -bad luck." - -"The Home Farm! Go there at once and meet me. Tell one of the grooms -to bring my horse round. No. Never mind. I'll go to the stables -myself. It will save time." - -In less than a quarter of an hour, Dorian Gray was galloping down the -long avenue as hard as he could go. The trees seemed to sweep past him -in spectral procession, and wild shadows to fling themselves across his -path. Once the mare swerved at a white gate-post and nearly threw him. -He lashed her across the neck with his crop. She cleft the dusky air -like an arrow. The stones flew from her hoofs. - -At last he reached the Home Farm. Two men were loitering in the yard. -He leaped from the saddle and threw the reins to one of them. In the -farthest stable a light was glimmering. Something seemed to tell him -that the body was there, and he hurried to the door and put his hand -upon the latch. - -There he paused for a moment, feeling that he was on the brink of a -discovery that would either make or mar his life. Then he thrust the -door open and entered. - -On a heap of sacking in the far corner was lying the dead body of a man -dressed in a coarse shirt and a pair of blue trousers. A spotted -handkerchief had been placed over the face. A coarse candle, stuck in -a bottle, sputtered beside it. - -Dorian Gray shuddered. He felt that his could not be the hand to take -the handkerchief away, and called out to one of the farm-servants to -come to him. - -"Take that thing off the face. I wish to see it," he said, clutching -at the door-post for support. - -When the farm-servant had done so, he stepped forward. A cry of joy -broke from his lips. The man who had been shot in the thicket was -James Vane. - -He stood there for some minutes looking at the dead body. As he rode -home, his eyes were full of tears, for he knew he was safe. - - - -CHAPTER 19 - -"There is no use your telling me that you are going to be good," cried -Lord Henry, dipping his white fingers into a red copper bowl filled -with rose-water. "You are quite perfect. Pray, don't change." - -Dorian Gray shook his head. "No, Harry, I have done too many dreadful -things in my life. I am not going to do any more. I began my good -actions yesterday." - -"Where were you yesterday?" - -"In the country, Harry. I was staying at a little inn by myself." - -"My dear boy," said Lord Henry, smiling, "anybody can be good in the -country. There are no temptations there. That is the reason why -people who live out of town are so absolutely uncivilized. -Civilization is not by any means an easy thing to attain to. There are -only two ways by which man can reach it. One is by being cultured, the -other by being corrupt. Country people have no opportunity of being -either, so they stagnate." - -"Culture and corruption," echoed Dorian. "I have known something of -both. It seems terrible to me now that they should ever be found -together. For I have a new ideal, Harry. I am going to alter. I -think I have altered." - -"You have not yet told me what your good action was. Or did you say -you had done more than one?" asked his companion as he spilled into his -plate a little crimson pyramid of seeded strawberries and, through a -perforated, shell-shaped spoon, snowed white sugar upon them. - -"I can tell you, Harry. It is not a story I could tell to any one -else. I spared somebody. It sounds vain, but you understand what I -mean. She was quite beautiful and wonderfully like Sibyl Vane. I -think it was that which first attracted me to her. You remember Sibyl, -don't you? How long ago that seems! Well, Hetty was not one of our -own class, of course. She was simply a girl in a village. But I -really loved her. I am quite sure that I loved her. All during this -wonderful May that we have been having, I used to run down and see her -two or three times a week. Yesterday she met me in a little orchard. -The apple-blossoms kept tumbling down on her hair, and she was -laughing. We were to have gone away together this morning at dawn. -Suddenly I determined to leave her as flowerlike as I had found her." - -"I should think the novelty of the emotion must have given you a thrill -of real pleasure, Dorian," interrupted Lord Henry. "But I can finish -your idyll for you. You gave her good advice and broke her heart. -That was the beginning of your reformation." - -"Harry, you are horrible! You mustn't say these dreadful things. -Hetty's heart is not broken. Of course, she cried and all that. But -there is no disgrace upon her. She can live, like Perdita, in her -garden of mint and marigold." - -"And weep over a faithless Florizel," said Lord Henry, laughing, as he -leaned back in his chair. "My dear Dorian, you have the most curiously -boyish moods. Do you think this girl will ever be really content now -with any one of her own rank? I suppose she will be married some day -to a rough carter or a grinning ploughman. Well, the fact of having -met you, and loved you, will teach her to despise her husband, and she -will be wretched. From a moral point of view, I cannot say that I -think much of your great renunciation. Even as a beginning, it is -poor. Besides, how do you know that Hetty isn't floating at the -present moment in some starlit mill-pond, with lovely water-lilies -round her, like Ophelia?" - -"I can't bear this, Harry! You mock at everything, and then suggest -the most serious tragedies. I am sorry I told you now. I don't care -what you say to me. I know I was right in acting as I did. Poor -Hetty! As I rode past the farm this morning, I saw her white face at -the window, like a spray of jasmine. Don't let us talk about it any -more, and don't try to persuade me that the first good action I have -done for years, the first little bit of self-sacrifice I have ever -known, is really a sort of sin. I want to be better. I am going to be -better. Tell me something about yourself. What is going on in town? -I have not been to the club for days." - -"The people are still discussing poor Basil's disappearance." - -"I should have thought they had got tired of that by this time," said -Dorian, pouring himself out some wine and frowning slightly. - -"My dear boy, they have only been talking about it for six weeks, and -the British public are really not equal to the mental strain of having -more than one topic every three months. They have been very fortunate -lately, however. They have had my own divorce-case and Alan Campbell's -suicide. Now they have got the mysterious disappearance of an artist. -Scotland Yard still insists that the man in the grey ulster who left -for Paris by the midnight train on the ninth of November was poor -Basil, and the French police declare that Basil never arrived in Paris -at all. I suppose in about a fortnight we shall be told that he has -been seen in San Francisco. It is an odd thing, but every one who -disappears is said to be seen at San Francisco. It must be a -delightful city, and possess all the attractions of the next world." - -"What do you think has happened to Basil?" asked Dorian, holding up his -Burgundy against the light and wondering how it was that he could -discuss the matter so calmly. - -"I have not the slightest idea. If Basil chooses to hide himself, it -is no business of mine. If he is dead, I don't want to think about -him. Death is the only thing that ever terrifies me. I hate it." - -"Why?" said the younger man wearily. - -"Because," said Lord Henry, passing beneath his nostrils the gilt -trellis of an open vinaigrette box, "one can survive everything -nowadays except that. Death and vulgarity are the only two facts in -the nineteenth century that one cannot explain away. Let us have our -coffee in the music-room, Dorian. You must play Chopin to me. The man -with whom my wife ran away played Chopin exquisitely. Poor Victoria! -I was very fond of her. The house is rather lonely without her. Of -course, married life is merely a habit, a bad habit. But then one -regrets the loss even of one's worst habits. Perhaps one regrets them -the most. They are such an essential part of one's personality." - -Dorian said nothing, but rose from the table, and passing into the next -room, sat down to the piano and let his fingers stray across the white -and black ivory of the keys. After the coffee had been brought in, he -stopped, and looking over at Lord Henry, said, "Harry, did it ever -occur to you that Basil was murdered?" - -Lord Henry yawned. "Basil was very popular, and always wore a -Waterbury watch. Why should he have been murdered? He was not clever -enough to have enemies. Of course, he had a wonderful genius for -painting. But a man can paint like Velasquez and yet be as dull as -possible. Basil was really rather dull. He only interested me once, -and that was when he told me, years ago, that he had a wild adoration -for you and that you were the dominant motive of his art." - -"I was very fond of Basil," said Dorian with a note of sadness in his -voice. "But don't people say that he was murdered?" - -"Oh, some of the papers do. It does not seem to me to be at all -probable. I know there are dreadful places in Paris, but Basil was not -the sort of man to have gone to them. He had no curiosity. It was his -chief defect." - -"What would you say, Harry, if I told you that I had murdered Basil?" -said the younger man. He watched him intently after he had spoken. - -"I would say, my dear fellow, that you were posing for a character that -doesn't suit you. All crime is vulgar, just as all vulgarity is crime. -It is not in you, Dorian, to commit a murder. I am sorry if I hurt -your vanity by saying so, but I assure you it is true. Crime belongs -exclusively to the lower orders. I don't blame them in the smallest -degree. I should fancy that crime was to them what art is to us, -simply a method of procuring extraordinary sensations." - -"A method of procuring sensations? Do you think, then, that a man who -has once committed a murder could possibly do the same crime again? -Don't tell me that." - -"Oh! anything becomes a pleasure if one does it too often," cried Lord -Henry, laughing. "That is one of the most important secrets of life. -I should fancy, however, that murder is always a mistake. One should -never do anything that one cannot talk about after dinner. But let us -pass from poor Basil. I wish I could believe that he had come to such -a really romantic end as you suggest, but I can't. I dare say he fell -into the Seine off an omnibus and that the conductor hushed up the -scandal. Yes: I should fancy that was his end. I see him lying now -on his back under those dull-green waters, with the heavy barges -floating over him and long weeds catching in his hair. Do you know, I -don't think he would have done much more good work. During the last -ten years his painting had gone off very much." - -Dorian heaved a sigh, and Lord Henry strolled across the room and began -to stroke the head of a curious Java parrot, a large, grey-plumaged -bird with pink crest and tail, that was balancing itself upon a bamboo -perch. As his pointed fingers touched it, it dropped the white scurf -of crinkled lids over black, glasslike eyes and began to sway backwards -and forwards. - -"Yes," he continued, turning round and taking his handkerchief out of -his pocket; "his painting had quite gone off. It seemed to me to have -lost something. It had lost an ideal. When you and he ceased to be -great friends, he ceased to be a great artist. What was it separated -you? I suppose he bored you. If so, he never forgave you. It's a -habit bores have. By the way, what has become of that wonderful -portrait he did of you? I don't think I have ever seen it since he -finished it. Oh! I remember your telling me years ago that you had -sent it down to Selby, and that it had got mislaid or stolen on the -way. You never got it back? What a pity! it was really a -masterpiece. I remember I wanted to buy it. I wish I had now. It -belonged to Basil's best period. Since then, his work was that curious -mixture of bad painting and good intentions that always entitles a man -to be called a representative British artist. Did you advertise for -it? You should." - -"I forget," said Dorian. "I suppose I did. But I never really liked -it. I am sorry I sat for it. The memory of the thing is hateful to -me. Why do you talk of it? It used to remind me of those curious -lines in some play--Hamlet, I think--how do they run?-- - - "Like the painting of a sorrow, - A face without a heart." - -Yes: that is what it was like." - -Lord Henry laughed. "If a man treats life artistically, his brain is -his heart," he answered, sinking into an arm-chair. - -Dorian Gray shook his head and struck some soft chords on the piano. -"'Like the painting of a sorrow,'" he repeated, "'a face without a -heart.'" - -The elder man lay back and looked at him with half-closed eyes. "By -the way, Dorian," he said after a pause, "'what does it profit a man if -he gain the whole world and lose--how does the quotation run?--his own -soul'?" - -The music jarred, and Dorian Gray started and stared at his friend. -"Why do you ask me that, Harry?" - -"My dear fellow," said Lord Henry, elevating his eyebrows in surprise, -"I asked you because I thought you might be able to give me an answer. -That is all. I was going through the park last Sunday, and close by -the Marble Arch there stood a little crowd of shabby-looking people -listening to some vulgar street-preacher. As I passed by, I heard the -man yelling out that question to his audience. It struck me as being -rather dramatic. London is very rich in curious effects of that kind. -A wet Sunday, an uncouth Christian in a mackintosh, a ring of sickly -white faces under a broken roof of dripping umbrellas, and a wonderful -phrase flung into the air by shrill hysterical lips--it was really very -good in its way, quite a suggestion. I thought of telling the prophet -that art had a soul, but that man had not. I am afraid, however, he -would not have understood me." - -"Don't, Harry. The soul is a terrible reality. It can be bought, and -sold, and bartered away. It can be poisoned, or made perfect. There -is a soul in each one of us. I know it." - -"Do you feel quite sure of that, Dorian?" - -"Quite sure." - -"Ah! then it must be an illusion. The things one feels absolutely -certain about are never true. That is the fatality of faith, and the -lesson of romance. How grave you are! Don't be so serious. What have -you or I to do with the superstitions of our age? No: we have given -up our belief in the soul. Play me something. Play me a nocturne, -Dorian, and, as you play, tell me, in a low voice, how you have kept -your youth. You must have some secret. I am only ten years older than -you are, and I am wrinkled, and worn, and yellow. You are really -wonderful, Dorian. You have never looked more charming than you do -to-night. You remind me of the day I saw you first. You were rather -cheeky, very shy, and absolutely extraordinary. You have changed, of -course, but not in appearance. I wish you would tell me your secret. -To get back my youth I would do anything in the world, except take -exercise, get up early, or be respectable. Youth! There is nothing -like it. It's absurd to talk of the ignorance of youth. The only -people to whose opinions I listen now with any respect are people much -younger than myself. They seem in front of me. Life has revealed to -them her latest wonder. As for the aged, I always contradict the aged. -I do it on principle. If you ask them their opinion on something that -happened yesterday, they solemnly give you the opinions current in -1820, when people wore high stocks, believed in everything, and knew -absolutely nothing. How lovely that thing you are playing is! I -wonder, did Chopin write it at Majorca, with the sea weeping round the -villa and the salt spray dashing against the panes? It is marvellously -romantic. What a blessing it is that there is one art left to us that -is not imitative! Don't stop. I want music to-night. It seems to me -that you are the young Apollo and that I am Marsyas listening to you. -I have sorrows, Dorian, of my own, that even you know nothing of. The -tragedy of old age is not that one is old, but that one is young. I am -amazed sometimes at my own sincerity. Ah, Dorian, how happy you are! -What an exquisite life you have had! You have drunk deeply of -everything. You have crushed the grapes against your palate. Nothing -has been hidden from you. And it has all been to you no more than the -sound of music. It has not marred you. You are still the same." - -"I am not the same, Harry." - -"Yes, you are the same. I wonder what the rest of your life will be. -Don't spoil it by renunciations. At present you are a perfect type. -Don't make yourself incomplete. You are quite flawless now. You need -not shake your head: you know you are. Besides, Dorian, don't deceive -yourself. Life is not governed by will or intention. Life is a -question of nerves, and fibres, and slowly built-up cells in which -thought hides itself and passion has its dreams. You may fancy -yourself safe and think yourself strong. But a chance tone of colour -in a room or a morning sky, a particular perfume that you had once -loved and that brings subtle memories with it, a line from a forgotten -poem that you had come across again, a cadence from a piece of music -that you had ceased to play--I tell you, Dorian, that it is on things -like these that our lives depend. Browning writes about that -somewhere; but our own senses will imagine them for us. There are -moments when the odour of _lilas blanc_ passes suddenly across me, and I -have to live the strangest month of my life over again. I wish I could -change places with you, Dorian. The world has cried out against us -both, but it has always worshipped you. It always will worship you. -You are the type of what the age is searching for, and what it is -afraid it has found. I am so glad that you have never done anything, -never carved a statue, or painted a picture, or produced anything -outside of yourself! Life has been your art. You have set yourself to -music. Your days are your sonnets." - -Dorian rose up from the piano and passed his hand through his hair. -"Yes, life has been exquisite," he murmured, "but I am not going to -have the same life, Harry. And you must not say these extravagant -things to me. You don't know everything about me. I think that if you -did, even you would turn from me. You laugh. Don't laugh." - -"Why have you stopped playing, Dorian? Go back and give me the -nocturne over again. Look at that great, honey-coloured moon that -hangs in the dusky air. She is waiting for you to charm her, and if -you play she will come closer to the earth. You won't? Let us go to -the club, then. It has been a charming evening, and we must end it -charmingly. There is some one at White's who wants immensely to know -you--young Lord Poole, Bournemouth's eldest son. He has already copied -your neckties, and has begged me to introduce him to you. He is quite -delightful and rather reminds me of you." - -"I hope not," said Dorian with a sad look in his eyes. "But I am tired -to-night, Harry. I shan't go to the club. It is nearly eleven, and I -want to go to bed early." - -"Do stay. You have never played so well as to-night. There was -something in your touch that was wonderful. It had more expression -than I had ever heard from it before." - -"It is because I am going to be good," he answered, smiling. "I am a -little changed already." - -"You cannot change to me, Dorian," said Lord Henry. "You and I will -always be friends." - -"Yet you poisoned me with a book once. I should not forgive that. -Harry, promise me that you will never lend that book to any one. It -does harm." - -"My dear boy, you are really beginning to moralize. You will soon be -going about like the converted, and the revivalist, warning people -against all the sins of which you have grown tired. You are much too -delightful to do that. Besides, it is no use. You and I are what we -are, and will be what we will be. As for being poisoned by a book, -there is no such thing as that. Art has no influence upon action. It -annihilates the desire to act. It is superbly sterile. The books that -the world calls immoral are books that show the world its own shame. -That is all. But we won't discuss literature. Come round to-morrow. I -am going to ride at eleven. We might go together, and I will take you -to lunch afterwards with Lady Branksome. She is a charming woman, and -wants to consult you about some tapestries she is thinking of buying. -Mind you come. Or shall we lunch with our little duchess? She says -she never sees you now. Perhaps you are tired of Gladys? I thought -you would be. Her clever tongue gets on one's nerves. Well, in any -case, be here at eleven." - -"Must I really come, Harry?" - -"Certainly. The park is quite lovely now. I don't think there have -been such lilacs since the year I met you." - -"Very well. I shall be here at eleven," said Dorian. "Good night, -Harry." As he reached the door, he hesitated for a moment, as if he -had something more to say. Then he sighed and went out. - - - -CHAPTER 20 - -It was a lovely night, so warm that he threw his coat over his arm and -did not even put his silk scarf round his throat. As he strolled home, -smoking his cigarette, two young men in evening dress passed him. He -heard one of them whisper to the other, "That is Dorian Gray." He -remembered how pleased he used to be when he was pointed out, or stared -at, or talked about. He was tired of hearing his own name now. Half -the charm of the little village where he had been so often lately was -that no one knew who he was. He had often told the girl whom he had -lured to love him that he was poor, and she had believed him. He had -told her once that he was wicked, and she had laughed at him and -answered that wicked people were always very old and very ugly. What a -laugh she had!--just like a thrush singing. And how pretty she had -been in her cotton dresses and her large hats! She knew nothing, but -she had everything that he had lost. - -When he reached home, he found his servant waiting up for him. He sent -him to bed, and threw himself down on the sofa in the library, and -began to think over some of the things that Lord Henry had said to him. - -Was it really true that one could never change? He felt a wild longing -for the unstained purity of his boyhood--his rose-white boyhood, as -Lord Henry had once called it. He knew that he had tarnished himself, -filled his mind with corruption and given horror to his fancy; that he -had been an evil influence to others, and had experienced a terrible -joy in being so; and that of the lives that had crossed his own, it had -been the fairest and the most full of promise that he had brought to -shame. But was it all irretrievable? Was there no hope for him? - -Ah! in what a monstrous moment of pride and passion he had prayed that -the portrait should bear the burden of his days, and he keep the -unsullied splendour of eternal youth! All his failure had been due to -that. Better for him that each sin of his life had brought its sure -swift penalty along with it. There was purification in punishment. -Not "Forgive us our sins" but "Smite us for our iniquities" should be -the prayer of man to a most just God. - -The curiously carved mirror that Lord Henry had given to him, so many -years ago now, was standing on the table, and the white-limbed Cupids -laughed round it as of old. He took it up, as he had done on that -night of horror when he had first noted the change in the fatal -picture, and with wild, tear-dimmed eyes looked into its polished -shield. Once, some one who had terribly loved him had written to him a -mad letter, ending with these idolatrous words: "The world is changed -because you are made of ivory and gold. The curves of your lips -rewrite history." The phrases came back to his memory, and he repeated -them over and over to himself. Then he loathed his own beauty, and -flinging the mirror on the floor, crushed it into silver splinters -beneath his heel. It was his beauty that had ruined him, his beauty -and the youth that he had prayed for. But for those two things, his -life might have been free from stain. His beauty had been to him but a -mask, his youth but a mockery. What was youth at best? A green, an -unripe time, a time of shallow moods, and sickly thoughts. Why had he -worn its livery? Youth had spoiled him. - -It was better not to think of the past. Nothing could alter that. It -was of himself, and of his own future, that he had to think. James -Vane was hidden in a nameless grave in Selby churchyard. Alan Campbell -had shot himself one night in his laboratory, but had not revealed the -secret that he had been forced to know. The excitement, such as it -was, over Basil Hallward's disappearance would soon pass away. It was -already waning. He was perfectly safe there. Nor, indeed, was it the -death of Basil Hallward that weighed most upon his mind. It was the -living death of his own soul that troubled him. Basil had painted the -portrait that had marred his life. He could not forgive him that. It -was the portrait that had done everything. Basil had said things to -him that were unbearable, and that he had yet borne with patience. The -murder had been simply the madness of a moment. As for Alan Campbell, -his suicide had been his own act. He had chosen to do it. It was -nothing to him. - -A new life! That was what he wanted. That was what he was waiting -for. Surely he had begun it already. He had spared one innocent -thing, at any rate. He would never again tempt innocence. He would be -good. - -As he thought of Hetty Merton, he began to wonder if the portrait in -the locked room had changed. Surely it was not still so horrible as it -had been? Perhaps if his life became pure, he would be able to expel -every sign of evil passion from the face. Perhaps the signs of evil -had already gone away. He would go and look. - -He took the lamp from the table and crept upstairs. As he unbarred the -door, a smile of joy flitted across his strangely young-looking face -and lingered for a moment about his lips. Yes, he would be good, and -the hideous thing that he had hidden away would no longer be a terror -to him. He felt as if the load had been lifted from him already. - -He went in quietly, locking the door behind him, as was his custom, and -dragged the purple hanging from the portrait. A cry of pain and -indignation broke from him. He could see no change, save that in the -eyes there was a look of cunning and in the mouth the curved wrinkle of -the hypocrite. The thing was still loathsome--more loathsome, if -possible, than before--and the scarlet dew that spotted the hand seemed -brighter, and more like blood newly spilled. Then he trembled. Had it -been merely vanity that had made him do his one good deed? Or the -desire for a new sensation, as Lord Henry had hinted, with his mocking -laugh? Or that passion to act a part that sometimes makes us do things -finer than we are ourselves? Or, perhaps, all these? And why was the -red stain larger than it had been? It seemed to have crept like a -horrible disease over the wrinkled fingers. There was blood on the -painted feet, as though the thing had dripped--blood even on the hand -that had not held the knife. Confess? Did it mean that he was to -confess? To give himself up and be put to death? He laughed. He felt -that the idea was monstrous. Besides, even if he did confess, who -would believe him? There was no trace of the murdered man anywhere. -Everything belonging to him had been destroyed. He himself had burned -what had been below-stairs. The world would simply say that he was mad. -They would shut him up if he persisted in his story.... Yet it was -his duty to confess, to suffer public shame, and to make public -atonement. There was a God who called upon men to tell their sins to -earth as well as to heaven. Nothing that he could do would cleanse him -till he had told his own sin. His sin? He shrugged his shoulders. -The death of Basil Hallward seemed very little to him. He was thinking -of Hetty Merton. For it was an unjust mirror, this mirror of his soul -that he was looking at. Vanity? Curiosity? Hypocrisy? Had there -been nothing more in his renunciation than that? There had been -something more. At least he thought so. But who could tell? ... No. -There had been nothing more. Through vanity he had spared her. In -hypocrisy he had worn the mask of goodness. For curiosity's sake he -had tried the denial of self. He recognized that now. - -But this murder--was it to dog him all his life? Was he always to be -burdened by his past? Was he really to confess? Never. There was -only one bit of evidence left against him. The picture itself--that -was evidence. He would destroy it. Why had he kept it so long? Once -it had given him pleasure to watch it changing and growing old. Of -late he had felt no such pleasure. It had kept him awake at night. -When he had been away, he had been filled with terror lest other eyes -should look upon it. It had brought melancholy across his passions. -Its mere memory had marred many moments of joy. It had been like -conscience to him. Yes, it had been conscience. He would destroy it. - -He looked round and saw the knife that had stabbed Basil Hallward. He -had cleaned it many times, till there was no stain left upon it. It -was bright, and glistened. As it had killed the painter, so it would -kill the painter's work, and all that that meant. It would kill the -past, and when that was dead, he would be free. It would kill this -monstrous soul-life, and without its hideous warnings, he would be at -peace. He seized the thing, and stabbed the picture with it. - -There was a cry heard, and a crash. The cry was so horrible in its -agony that the frightened servants woke and crept out of their rooms. -Two gentlemen, who were passing in the square below, stopped and looked -up at the great house. They walked on till they met a policeman and -brought him back. The man rang the bell several times, but there was -no answer. Except for a light in one of the top windows, the house was -all dark. After a time, he went away and stood in an adjoining portico -and watched. - -"Whose house is that, Constable?" asked the elder of the two gentlemen. - -"Mr. Dorian Gray's, sir," answered the policeman. - -They looked at each other, as they walked away, and sneered. One of -them was Sir Henry Ashton's uncle. - -Inside, in the servants' part of the house, the half-clad domestics -were talking in low whispers to each other. Old Mrs. Leaf was crying -and wringing her hands. Francis was as pale as death. - -After about a quarter of an hour, he got the coachman and one of the -footmen and crept upstairs. They knocked, but there was no reply. -They called out. Everything was still. Finally, after vainly trying -to force the door, they got on the roof and dropped down on to the -balcony. The windows yielded easily--their bolts were old. - -When they entered, they found hanging upon the wall a splendid portrait -of their master as they had last seen him, in all the wonder of his -exquisite youth and beauty. Lying on the floor was a dead man, in -evening dress, with a knife in his heart. He was withered, wrinkled, -and loathsome of visage. It was not till they had examined the rings -that they recognized who it was. - - - - - - - - - -End of Project Gutenberg's The Picture of Dorian Gray, by Oscar Wilde - -*** END OF THIS PROJECT GUTENBERG EBOOK THE PICTURE OF DORIAN GRAY *** - -***** This file should be named 174.txt or 174.zip ***** -This and all associated files of various formats will be found in: - http://www.gutenberg.org/1/7/174/ - -Produced by Judith Boss. HTML version by Al Haines. - -Updated editions will replace the previous one--the old editions -will be renamed. - -Creating the works from public domain print editions means that no -one owns a United States copyright in these works, so the Foundation -(and you!) can copy and distribute it in the United States without -permission and without paying copyright royalties. Special rules, -set forth in the General Terms of Use part of this license, apply to -copying and distributing Project Gutenberg-tm electronic works to -protect the PROJECT GUTENBERG-tm concept and trademark. Project -Gutenberg is a registered trademark, and may not be used if you -charge for the eBooks, unless you receive specific permission. If you -do not charge anything for copies of this eBook, complying with the -rules is very easy. You may use this eBook for nearly any purpose -such as creation of derivative works, reports, performances and -research. They may be modified and printed and given away--you may do -practically ANYTHING with public domain eBooks. Redistribution is -subject to the trademark license, especially commercial -redistribution. - - - -*** START: FULL LICENSE *** - -THE FULL PROJECT GUTENBERG LICENSE -PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK - -To protect the Project Gutenberg-tm mission of promoting the free -distribution of electronic works, by using or distributing this work -(or any other work associated in any way with the phrase "Project -Gutenberg"), you agree to comply with all the terms of the Full Project -Gutenberg-tm License (available with this file or online at -http://gutenberg.net/license). - - -Section 1. General Terms of Use and Redistributing Project Gutenberg-tm -electronic works - -1.A. By reading or using any part of this Project Gutenberg-tm -electronic work, you indicate that you have read, understand, agree to -and accept all the terms of this license and intellectual property -(trademark/copyright) agreement. If you do not agree to abide by all -the terms of this agreement, you must cease using and return or destroy -all copies of Project Gutenberg-tm electronic works in your possession. -If you paid a fee for obtaining a copy of or access to a Project -Gutenberg-tm electronic work and you do not agree to be bound by the -terms of this agreement, you may obtain a refund from the person or -entity to whom you paid the fee as set forth in paragraph 1.E.8. - -1.B. "Project Gutenberg" is a registered trademark. It may only be -used on or associated in any way with an electronic work by people who -agree to be bound by the terms of this agreement. There are a few -things that you can do with most Project Gutenberg-tm electronic works -even without complying with the full terms of this agreement. See -paragraph 1.C below. There are a lot of things you can do with Project -Gutenberg-tm electronic works if you follow the terms of this agreement -and help preserve free future access to Project Gutenberg-tm electronic -works. See paragraph 1.E below. - -1.C. The Project Gutenberg Literary Archive Foundation ("the Foundation" -or PGLAF), owns a compilation copyright in the collection of Project -Gutenberg-tm electronic works. Nearly all the individual works in the -collection are in the public domain in the United States. If an -individual work is in the public domain in the United States and you are -located in the United States, we do not claim a right to prevent you from -copying, distributing, performing, displaying or creating derivative -works based on the work as long as all references to Project Gutenberg -are removed. Of course, we hope that you will support the Project -Gutenberg-tm mission of promoting free access to electronic works by -freely sharing Project Gutenberg-tm works in compliance with the terms of -this agreement for keeping the Project Gutenberg-tm name associated with -the work. You can easily comply with the terms of this agreement by -keeping this work in the same format with its attached full Project -Gutenberg-tm License when you share it without charge with others. - -1.D. The copyright laws of the place where you are located also govern -what you can do with this work. Copyright laws in most countries are in -a constant state of change. If you are outside the United States, check -the laws of your country in addition to the terms of this agreement -before downloading, copying, displaying, performing, distributing or -creating derivative works based on this work or any other Project -Gutenberg-tm work. The Foundation makes no representations concerning -the copyright status of any work in any country outside the United -States. - -1.E. Unless you have removed all references to Project Gutenberg: - -1.E.1. The following sentence, with active links to, or other immediate -access to, the full Project Gutenberg-tm License must appear prominently -whenever any copy of a Project Gutenberg-tm work (any work on which the -phrase "Project Gutenberg" appears, or with which the phrase "Project -Gutenberg" is associated) is accessed, displayed, performed, viewed, -copied or distributed: - -This eBook is for the use of anyone anywhere at no cost and with -almost no restrictions whatsoever. You may copy it, give it away or -re-use it under the terms of the Project Gutenberg License included -with this eBook or online at www.gutenberg.net - -1.E.2. If an individual Project Gutenberg-tm electronic work is derived -from the public domain (does not contain a notice indicating that it is -posted with permission of the copyright holder), the work can be copied -and distributed to anyone in the United States without paying any fees -or charges. If you are redistributing or providing access to a work -with the phrase "Project Gutenberg" associated with or appearing on the -work, you must comply either with the requirements of paragraphs 1.E.1 -through 1.E.7 or obtain permission for the use of the work and the -Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or -1.E.9. - -1.E.3. If an individual Project Gutenberg-tm electronic work is posted -with the permission of the copyright holder, your use and distribution -must comply with both paragraphs 1.E.1 through 1.E.7 and any additional -terms imposed by the copyright holder. Additional terms will be linked -to the Project Gutenberg-tm License for all works posted with the -permission of the copyright holder found at the beginning of this work. - -1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm -License terms from this work, or any files containing a part of this -work or any other work associated with Project Gutenberg-tm. - -1.E.5. Do not copy, display, perform, distribute or redistribute this -electronic work, or any part of this electronic work, without -prominently displaying the sentence set forth in paragraph 1.E.1 with -active links or immediate access to the full terms of the Project -Gutenberg-tm License. - -1.E.6. You may convert to and distribute this work in any binary, -compressed, marked up, nonproprietary or proprietary form, including any -word processing or hypertext form. However, if you provide access to or -distribute copies of a Project Gutenberg-tm work in a format other than -"Plain Vanilla ASCII" or other format used in the official version -posted on the official Project Gutenberg-tm web site (www.gutenberg.net), -you must, at no additional cost, fee or expense to the user, provide a -copy, a means of exporting a copy, or a means of obtaining a copy upon -request, of the work in its original "Plain Vanilla ASCII" or other -form. Any alternate format must include the full Project Gutenberg-tm -License as specified in paragraph 1.E.1. - -1.E.7. Do not charge a fee for access to, viewing, displaying, -performing, copying or distributing any Project Gutenberg-tm works -unless you comply with paragraph 1.E.8 or 1.E.9. - -1.E.8. You may charge a reasonable fee for copies of or providing -access to or distributing Project Gutenberg-tm electronic works provided -that - -- You pay a royalty fee of 20% of the gross profits you derive from - the use of Project Gutenberg-tm works calculated using the method - you already use to calculate your applicable taxes. The fee is - owed to the owner of the Project Gutenberg-tm trademark, but he - has agreed to donate royalties under this paragraph to the - Project Gutenberg Literary Archive Foundation. Royalty payments - must be paid within 60 days following each date on which you - prepare (or are legally required to prepare) your periodic tax - returns. Royalty payments should be clearly marked as such and - sent to the Project Gutenberg Literary Archive Foundation at the - address specified in Section 4, "Information about donations to - the Project Gutenberg Literary Archive Foundation." - -- You provide a full refund of any money paid by a user who notifies - you in writing (or by e-mail) within 30 days of receipt that s/he - does not agree to the terms of the full Project Gutenberg-tm - License. You must require such a user to return or - destroy all copies of the works possessed in a physical medium - and discontinue all use of and all access to other copies of - Project Gutenberg-tm works. - -- You provide, in accordance with paragraph 1.F.3, a full refund of any - money paid for a work or a replacement copy, if a defect in the - electronic work is discovered and reported to you within 90 days - of receipt of the work. - -- You comply with all other terms of this agreement for free - distribution of Project Gutenberg-tm works. - -1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm -electronic work or group of works on different terms than are set -forth in this agreement, you must obtain permission in writing from -both the Project Gutenberg Literary Archive Foundation and Michael -Hart, the owner of the Project Gutenberg-tm trademark. Contact the -Foundation as set forth in Section 3 below. - -1.F. - -1.F.1. Project Gutenberg volunteers and employees expend considerable -effort to identify, do copyright research on, transcribe and proofread -public domain works in creating the Project Gutenberg-tm -collection. Despite these efforts, Project Gutenberg-tm electronic -works, and the medium on which they may be stored, may contain -"Defects," such as, but not limited to, incomplete, inaccurate or -corrupt data, transcription errors, a copyright or other intellectual -property infringement, a defective or damaged disk or other medium, a -computer virus, or computer codes that damage or cannot be read by -your equipment. - -1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the "Right -of Replacement or Refund" described in paragraph 1.F.3, the Project -Gutenberg Literary Archive Foundation, the owner of the Project -Gutenberg-tm trademark, and any other party distributing a Project -Gutenberg-tm electronic work under this agreement, disclaim all -liability to you for damages, costs and expenses, including legal -fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT -LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE -PROVIDED IN PARAGRAPH F3. YOU AGREE THAT THE FOUNDATION, THE -TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE -LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR -INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH -DAMAGE. - -1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a -defect in this electronic work within 90 days of receiving it, you can -receive a refund of the money (if any) you paid for it by sending a -written explanation to the person you received the work from. If you -received the work on a physical medium, you must return the medium with -your written explanation. The person or entity that provided you with -the defective work may elect to provide a replacement copy in lieu of a -refund. If you received the work electronically, the person or entity -providing it to you may choose to give you a second opportunity to -receive the work electronically in lieu of a refund. If the second copy -is also defective, you may demand a refund in writing without further -opportunities to fix the problem. - -1.F.4. Except for the limited right of replacement or refund set forth -in paragraph 1.F.3, this work is provided to you 'AS-IS' WITH NO OTHER -WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR ANY PURPOSE. - -1.F.5. Some states do not allow disclaimers of certain implied -warranties or the exclusion or limitation of certain types of damages. -If any disclaimer or limitation set forth in this agreement violates the -law of the state applicable to this agreement, the agreement shall be -interpreted to make the maximum disclaimer or limitation permitted by -the applicable state law. The invalidity or unenforceability of any -provision of this agreement shall not void the remaining provisions. - -1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the -trademark owner, any agent or employee of the Foundation, anyone -providing copies of Project Gutenberg-tm electronic works in accordance -with this agreement, and any volunteers associated with the production, -promotion and distribution of Project Gutenberg-tm electronic works, -harmless from all liability, costs and expenses, including legal fees, -that arise directly or indirectly from any of the following which you do -or cause to occur: (a) distribution of this or any Project Gutenberg-tm -work, (b) alteration, modification, or additions or deletions to any -Project Gutenberg-tm work, and (c) any Defect you cause. - - -Section 2. Information about the Mission of Project Gutenberg-tm - -Project Gutenberg-tm is synonymous with the free distribution of -electronic works in formats readable by the widest variety of computers -including obsolete, old, middle-aged and new computers. It exists -because of the efforts of hundreds of volunteers and donations from -people in all walks of life. - -Volunteers and financial support to provide volunteers with the -assistance they need, is critical to reaching Project Gutenberg-tm's -goals and ensuring that the Project Gutenberg-tm collection will -remain freely available for generations to come. In 2001, the Project -Gutenberg Literary Archive Foundation was created to provide a secure -and permanent future for Project Gutenberg-tm and future generations. -To learn more about the Project Gutenberg Literary Archive Foundation -and how your efforts and donations can help, see Sections 3 and 4 -and the Foundation web page at http://www.pglaf.org. - - -Section 3. Information about the Project Gutenberg Literary Archive -Foundation - -The Project Gutenberg Literary Archive Foundation is a non profit -501(c)(3) educational corporation organized under the laws of the -state of Mississippi and granted tax exempt status by the Internal -Revenue Service. The Foundation's EIN or federal tax identification -number is 64-6221541. Its 501(c)(3) letter is posted at -http://pglaf.org/fundraising. Contributions to the Project Gutenberg -Literary Archive Foundation are tax deductible to the full extent -permitted by U.S. federal laws and your state's laws. - -The Foundation's principal office is located at 4557 Melan Dr. S. -Fairbanks, AK, 99712., but its volunteers and employees are scattered -throughout numerous locations. Its business office is located at -809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email -business@pglaf.org. Email contact links and up to date contact -information can be found at the Foundation's web site and official -page at http://pglaf.org - -For additional contact information: - Dr. Gregory B. Newby - Chief Executive and Director - gbnewby@pglaf.org - - -Section 4. Information about Donations to the Project Gutenberg -Literary Archive Foundation - -Project Gutenberg-tm depends upon and cannot survive without wide -spread public support and donations to carry out its mission of -increasing the number of public domain and licensed works that can be -freely distributed in machine readable form accessible by the widest -array of equipment including outdated equipment. Many small donations -($1 to $5,000) are particularly important to maintaining tax exempt -status with the IRS. - -The Foundation is committed to complying with the laws regulating -charities and charitable donations in all 50 states of the United -States. Compliance requirements are not uniform and it takes a -considerable effort, much paperwork and many fees to meet and keep up -with these requirements. We do not solicit donations in locations -where we have not received written confirmation of compliance. To -SEND DONATIONS or determine the status of compliance for any -particular state visit http://pglaf.org - -While we cannot and do not solicit contributions from states where we -have not met the solicitation requirements, we know of no prohibition -against accepting unsolicited donations from donors in such states who -approach us with offers to donate. - -International donations are gratefully accepted, but we cannot make -any statements concerning tax treatment of donations received from -outside the United States. U.S. laws alone swamp our small staff. - -Please check the Project Gutenberg Web pages for current donation -methods and addresses. Donations are accepted in a number of other -ways including including checks, online payments and credit card -donations. To donate, please visit: http://pglaf.org/donate - - -Section 5. General Information About Project Gutenberg-tm electronic -works. - -Professor Michael S. Hart is the originator of the Project Gutenberg-tm -concept of a library of electronic works that could be freely shared -with anyone. For thirty years, he produced and distributed Project -Gutenberg-tm eBooks with only a loose network of volunteer support. - - -Project Gutenberg-tm eBooks are often created from several printed -editions, all of which are confirmed as Public Domain in the U.S. -unless a copyright notice is included. Thus, we do not necessarily -keep eBooks in compliance with any particular paper edition. - - -Most people start at our Web site which has the main PG search facility: - - http://www.gutenberg.net - -This Web site includes information about Project Gutenberg-tm, -including how to make donations to the Project Gutenberg Literary -Archive Foundation, how to help produce our new eBooks, and how to -subscribe to our email newsletter to hear about new eBooks. diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -project(lws-api-test-gencrypto) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-gencrypto) -set(SRCS main.c lws-genaes.c lws-genec.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_GENCRYPTO 1 requirements) -require_lws_config(LWS_WITH_JOSE 1 requirements) - - -if (requirements) - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/lws-genaes.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/lws-genaes.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/lws-genaes.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/lws-genaes.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,801 +0,0 @@ -/* - * lws-api-test-gencrypto - lws-genaes - * - * Written in 2010-2018 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include - -static const uint8_t - /* - * produced with (plaintext.txt contains "test plaintext\0\0") - * - * openssl enc -aes256 \ - * -K "0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210" \ - * -iv "0123456789abcdeffedcba9876543210" - * -in plaintext.txt -out out.enc - * - */ - *cbc256 = (uint8_t *)"test plaintext\0\0", - cbc256_enc[] = { - 0x2b, 0x5d, 0xb2, 0xa8, 0x5a, 0x5a, 0xf4, 0x2e, - 0xf7, 0xf9, 0xc5, 0x3c, 0x73, 0xef, 0x40, 0x88, - }, cbc256_iv[] = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, - }, cbc256_key[] = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, - } -; - -static int -test_genaes_cbc(void) -{ - struct lws_genaes_ctx ctx; - struct lws_gencrypto_keyelem e; - uint8_t res[32], res1[32]; - - /* - * As part of a jwk, these are allocated. But here we just use one as - * a wrapper on a static binary key. - */ - e.buf = (uint8_t *)cbc256_key; - e.len = sizeof(cbc256_key); - - if (lws_genaes_create(&ctx, LWS_GAESO_ENC, LWS_GAESM_CBC, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - return 1; - } - - if (lws_genaes_crypt(&ctx, cbc256, 16, res, (uint8_t *)cbc256_iv, - NULL, NULL, 0)) { - lwsl_err("%s: lws_genaes_crypt failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy enc failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(cbc256_enc, res, 16)) { - lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - return -1; - } - - - if (lws_genaes_create(&ctx, LWS_GAESO_DEC, LWS_GAESM_CBC, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create dec failed\n", __func__); - return -1; - } - - if (lws_genaes_crypt(&ctx, res, 16, res1, (uint8_t *)cbc256_iv, - NULL, NULL, 0)) { - lwsl_err("%s: lws_genaes_crypt dec failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy dec failed\n", __func__); - lwsl_hexdump_notice(res1, 16); - return -1; - } - - if (lws_timingsafe_bcmp(cbc256, res1, 16)) { - lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - return -1; - } - - return 0; - -bail: - lws_genaes_destroy(&ctx, NULL, 0); - - return -1; -} - -static const uint8_t -/* - * produced with (plaintext.txt contains "test plaintext\0\0") - * - * openssl enc -aes-128-cfb \ - * -K "0123456789abcdeffedcba9876543210" \ - * -iv "0123456789abcdeffedcba9876543210" - * -in plaintext.txt -out out.enc - * - */ -*cfb128 = (uint8_t *)"test plaintext\0\0", -cfb128_enc[] = { - 0xd2, 0x11, 0x86, 0xd7, 0xa9, 0x55, 0x59, 0x04, - 0x4f, 0x63, 0x7c, 0xb9, 0xc6, 0xa1, 0xc9, 0x71 -}, cfb128_iv[] = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, -}, cfb128_key[] = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, -}; - -static int -test_genaes_cfb128(void) -{ - struct lws_genaes_ctx ctx; - struct lws_gencrypto_keyelem e; - uint8_t res[32], res1[32]; - size_t iv_off = 0; - - e.buf = (uint8_t *)cfb128_key; - e.len = sizeof(cfb128_key); - - if (lws_genaes_create(&ctx, LWS_GAESO_ENC, LWS_GAESM_CFB128, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - return 1; - } - - if (lws_genaes_crypt(&ctx, cfb128, 16, res, (uint8_t *)cfb128_iv, - NULL, &iv_off, 0)) { - lwsl_err("%s: lws_genaes_crypt failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(cfb128_enc, res, 16)) { - lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - return -1; - } - - iv_off = 0; - - if (lws_genaes_create(&ctx, LWS_GAESO_DEC, LWS_GAESM_CFB128, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create dec failed\n", __func__); - return -1; - } - - if (lws_genaes_crypt(&ctx, res, 16, res1, (uint8_t *)cfb128_iv, - NULL, &iv_off, 0)) { - lwsl_err("%s: lws_genaes_crypt dec failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(cfb128, res1, 16)) { - lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__); - lwsl_hexdump_notice(res1, 16); - return -1; - } - - return 0; - -bail: - lws_genaes_destroy(&ctx, NULL, 0); - - return -1; -} - -static const uint8_t -/* - * produced with (plaintext.txt contains "test plaintext\0\0") - * - * openssl enc -aes-128-cfb8 \ - * -K "0123456789abcdeffedcba9876543210" \ - * -iv "0123456789abcdeffedcba9876543210" - * -in plaintext.txt -out out.enc - * - */ -*cfb8 = (uint8_t *)"test plaintext\0\0", -cfb8_enc[] = { - 0xd2, 0x91, 0x06, 0x2d, 0x1b, 0x1e, 0x9b, 0x39, - 0xa6, 0x65, 0x8e, 0xbe, 0x68, 0x32, 0x3d, 0xab -}, cfb8_iv[] = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, -}, cfb8_key[] = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, -}; - -static int -test_genaes_cfb8(void) -{ - struct lws_genaes_ctx ctx; - struct lws_gencrypto_keyelem e; - uint8_t res[32], res1[32]; - - e.buf = (uint8_t *)cfb8_key; - e.len = sizeof(cfb8_key); - - if (lws_genaes_create(&ctx, LWS_GAESO_ENC, LWS_GAESM_CFB8, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - return 1; - } - - if (lws_genaes_crypt(&ctx, cfb8, 16, res, (uint8_t *)cfb8_iv, - NULL, NULL, 0)) { - lwsl_err("%s: lws_genaes_crypt failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(cfb8_enc, res, 16)) { - lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - return -1; - } - - if (lws_genaes_create(&ctx, LWS_GAESO_DEC, LWS_GAESM_CFB8, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create dec failed\n", __func__); - return -1; - } - - if (lws_genaes_crypt(&ctx, res, 16, res1, (uint8_t *)cfb8_iv, - NULL, NULL, 0)) { - lwsl_err("%s: lws_genaes_crypt dec failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(cfb8, res1, 16)) { - lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__); - lwsl_hexdump_notice(res1, 16); - return -1; - } - - return 0; - -bail: - lws_genaes_destroy(&ctx, NULL, 0); - - return -1; -} - -static const uint8_t -/* - * produced with (plaintext.txt contains "test plaintext\0\0") - * - * openssl enc -aes-128-ctr \ - * -K "0123456789abcdeffedcba9876543210" \ - * -iv "0123456789abcdeffedcba9876543210" - * -in plaintext.txt -out out.enc - * - */ -*ctr = (uint8_t *)"test plaintext\0\0", -ctr_enc[] = { - 0xd2, 0x11, 0x86, 0xd7, 0xa9, 0x55, 0x59, 0x04, - 0x4f, 0x63, 0x7c, 0xb9, 0xc6, 0xa1, 0xc9, 0x71 -}, ctr_iv[] = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, -}, ctr_key[] = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, -}; - -static int -test_genaes_ctr(void) -{ - uint8_t nonce_counter[16], sb[16]; - struct lws_genaes_ctx ctx; - struct lws_gencrypto_keyelem e; - uint8_t res[32], res1[32]; - size_t nc_off = 0; - - e.buf = (uint8_t *)ctr_key; - e.len = sizeof(ctr_key); - - memset(sb, 0, sizeof(nonce_counter)); - memcpy(nonce_counter, ctr_iv, sizeof(ctr_iv)); - - if (lws_genaes_create(&ctx, LWS_GAESO_ENC, LWS_GAESM_CTR, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - return 1; - } - - if (lws_genaes_crypt(&ctx, ctr, 16, res, nonce_counter, sb, &nc_off, 0)) { - lwsl_err("%s: lws_genaes_crypt failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(ctr_enc, res, 16)) { - lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - return -1; - } - - nc_off = 0; - memset(sb , 0, sizeof(nonce_counter)); - memcpy(nonce_counter, ctr_iv, sizeof(ctr_iv)); - - if (lws_genaes_create(&ctx, LWS_GAESO_DEC, LWS_GAESM_CTR, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create dec failed\n", __func__); - return -1; - } - - if (lws_genaes_crypt(&ctx, res, 16, res1, nonce_counter, sb, &nc_off, 0)) { - lwsl_err("%s: lws_genaes_crypt dec failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(ctr, res1, 16)) { - lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__); - lwsl_hexdump_notice(res1, 16); - return -1; - } - - lws_explicit_bzero(sb, sizeof(sb)); - - return 0; - -bail: - lws_genaes_destroy(&ctx, NULL, 0); - - return -1; -} - -static const uint8_t -/* - * produced with (plaintext.txt contains "test plaintext\0\0") - * - * openssl enc -aes-128-ecb \ - * -K "0123456789abcdeffedcba9876543210" \ - * -in plaintext.txt -out out.enc - * - */ -*ecb = (uint8_t *)"test plaintext\0\0", -ecb_enc[] = { - 0xf3, 0xe5, 0x6c, 0x80, 0x3a, 0xf1, 0xc4, 0xa0, - 0x7e, 0xdf, 0x86, 0x0f, 0x6d, 0xca, 0x5d, 0x36, - 0x17, 0x22, 0x37, 0x42, 0x47, 0x41, 0x67, 0x7d, - 0x99, 0x25, 0x02, 0x6b, 0x6b, 0x8f, 0x9c, 0x7f -}, ecb_key[] = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, -}; - -static int -test_genaes_ecb(void) -{ - struct lws_genaes_ctx ctx; - struct lws_gencrypto_keyelem e; - uint8_t res[32], res1[32]; - - /* - * As part of a jwk, these are allocated. But here we just use one as - * a wrapper on a static binary key. - */ - e.buf = (uint8_t *)ecb_key; - e.len = sizeof(ecb_key); - - if (lws_genaes_create(&ctx, LWS_GAESO_ENC, LWS_GAESM_ECB, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - return 1; - } - - if (lws_genaes_crypt(&ctx, ecb, 16, res, NULL, NULL, NULL, 0)) { - lwsl_err("%s: lws_genaes_crypt failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(ecb_enc, res, 16)) { - lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - return -1; - } - - if (lws_genaes_create(&ctx, LWS_GAESO_DEC, LWS_GAESM_ECB, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create dec failed\n", __func__); - return -1; - } - - if (lws_genaes_crypt(&ctx, res, 16, res1, NULL, NULL, NULL, 0)) { - lwsl_err("%s: lws_genaes_crypt dec failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(ecb, res1, 16)) { - lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - return -1; - } - - return 0; - -bail: - lws_genaes_destroy(&ctx, NULL, 0); - - return -1; -} - -#if defined(MBEDTLS_CONFIG_H) && !defined(MBEDTLS_CIPHER_MODE_OFB) -#else - -static const uint8_t - /* - * produced with (plaintext.txt contains "test plaintext\0\0") - * - * openssl enc -aes-128-ofb \ - * -K "0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210" \ - * -iv "0123456789abcdeffedcba9876543210" - * -in plaintext.txt -out out.enc - * - */ - *ofb = (uint8_t *)"test plaintext\0\0", - ofb_enc[] = { - /* !!! ugh... openssl app produces this... */ - // 0xd2, 0x11, 0x86, 0xd7, 0xa9, 0x55, 0x59, 0x04, - // 0x4f, 0x63, 0x7c, 0xb9, 0xc6, 0xa1, 0xc9, 0x71, - /* but both OpenSSL and mbedTLS produce this */ - 0x11, 0x33, 0x6D, 0xFC, 0x88, 0x4C, 0x28, 0xBA, - 0xD0, 0xF2, 0x6C, 0xBC, 0xDE, 0x4A, 0x56, 0x20 - }, ofb_iv[] = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, - }, ofb_key[] = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, - } -; - -static int -test_genaes_ofb(void) -{ - struct lws_genaes_ctx ctx; - struct lws_gencrypto_keyelem e; - uint8_t res[32], res1[32]; - size_t iv_off = 0; - - e.buf = (uint8_t *)ofb_key; - e.len = sizeof(ofb_key); - - if (lws_genaes_create(&ctx, LWS_GAESO_ENC, LWS_GAESM_OFB, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - return 1; - } - - if (lws_genaes_crypt(&ctx, ofb, 16, res, (uint8_t *)ofb_iv, NULL, - &iv_off, 0)) { - lwsl_err("%s: lws_genaes_crypt failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(ofb_enc, res, 16)) { - lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - return -1; - } - - iv_off = 0; - - if (lws_genaes_create(&ctx, LWS_GAESO_DEC, LWS_GAESM_OFB, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create dec failed\n", __func__); - return -1; - } - - if (lws_genaes_crypt(&ctx, res, 16, res1, (uint8_t *)ofb_iv, NULL, - &iv_off, 0)) { - lwsl_err("%s: lws_genaes_crypt dec failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(ofb, res1, 16)) { - lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - return -1; - } - - return 0; - -bail: - lws_genaes_destroy(&ctx, NULL, 0); - - return -1; -} - -#endif - -#if defined(MBEDTLS_CONFIG_H) && !defined(MBEDTLS_CIPHER_MODE_XTS) -#else - -static const uint8_t - /* - * Fedora openssl tool doesn't support xts... this data produced - * by testing on mbedtls + OpenSSL and getting the same result - * - * NOTICE that xts requires a double-length key... OpenSSL now checks - * the key for duplication so we use a random key - */ - *xts = (uint8_t *)"test plaintext\0\0", - xts_enc[] = { - 0x87, 0x83, 0x20, 0x8B, 0x15, 0x89, 0xA1, 0x13, - 0xDC, 0xEA, 0x82, 0xB6, 0xFF, 0x8D, 0x76, 0x3A - }, xts_key[] = { - 0xa4, 0xd6, 0xa2, 0x1a, 0x3b, 0x34, 0x34, 0x43, - 0x9a, 0xe2, 0x6a, 0x01, 0x1c, 0x73, 0x80, 0x3b, - 0xdd, 0xf6, 0xd4, 0x37, 0x5e, 0x0e, 0x1c, 0x72, - 0x8e, 0xe5, 0x18, 0x69, 0xfd, 0x08, 0x40, 0x2b, - 0x98, 0xf9, 0x75, 0xa8, 0x36, 0xd5, 0x0f, 0xa2, - 0x20, 0x04, 0x43, 0xa7, 0x3a, 0xa6, 0x4a, 0xdc, - 0xe9, 0x54, 0x50, 0xfa, 0x38, 0xad, 0x6d, 0x96, - 0x5f, 0x31, 0x9e, 0xcd, 0x33, 0x08, 0xa0, 0x44 - } -; - -static int -test_genaes_xts(void) -{ - struct lws_genaes_ctx ctx; - struct lws_gencrypto_keyelem e; - uint8_t res[32], res1[32], data_unit[16]; - - memset(data_unit, 0, sizeof(data_unit)); - - e.buf = (uint8_t *)xts_key; - e.len = sizeof(xts_key); - - if (lws_genaes_create(&ctx, LWS_GAESO_ENC, LWS_GAESM_XTS, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - return 1; - } - - if (lws_genaes_crypt(&ctx, xts, 16, res, data_unit, NULL, NULL, 0)) { - lwsl_err("%s: lws_genaes_crypt failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(xts_enc, res, 16)) { - lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - return -1; - } - - if (lws_genaes_create(&ctx, LWS_GAESO_DEC, LWS_GAESM_XTS, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create dec failed\n", __func__); - return -1; - } - - if (lws_genaes_crypt(&ctx, res, 16, res1, data_unit, NULL, NULL, 0)) { - lwsl_err("%s: lws_genaes_crypt dec failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, NULL, 0)) { - lwsl_err("%s: lws_genaes_destroy failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(xts, res1, 16)) { - lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - return -1; - } - - return 0; - -bail: - lws_genaes_destroy(&ctx, NULL, 0); - - return -1; -} -#endif - -static const uint8_t - /* - * https://csrc.nist.gov/CSRC/media/Projects/ - * Cryptographic-Algorithm-Validation-Program/ - * documents/mac/gcmtestvectors.zip - */ - - gcm_ct[] = { - 0xf7, 0x26, 0x44, 0x13, 0xa8, 0x4c, 0x0e, 0x7c, - 0xd5, 0x36, 0x86, 0x7e, 0xb9, 0xf2, 0x17, 0x36 - }, gcm_iv[] = { - 0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, - 0xee, 0xd0, 0x66, 0x84 - }, gcm_key[] = { - 0xee, 0xbc, 0x1f, 0x57, 0x48, 0x7f, 0x51, 0x92, - 0x1c, 0x04, 0x65, 0x66, 0x5f, 0x8a, 0xe6, 0xd1, - 0x65, 0x8b, 0xb2, 0x6d, 0xe6, 0xf8, 0xa0, 0x69, - 0xa3, 0x52, 0x02, 0x93, 0xa5, 0x72, 0x07, 0x8f - }, gcm_pt[] = { - 0xf5, 0x6e, 0x87, 0x05, 0x5b, 0xc3, 0x2d, 0x0e, - 0xeb, 0x31, 0xb2, 0xea, 0xcc, 0x2b, 0xf2, 0xa5 - }, gcm_aad[] = { - 0x4d, 0x23, 0xc3, 0xce, 0xc3, 0x34, 0xb4, 0x9b, - 0xdb, 0x37, 0x0c, 0x43, 0x7f, 0xec, 0x78, 0xde - }, gcm_tag[] = { - 0x67, 0xba, 0x05, 0x10, 0x26, 0x2a, 0xe4, 0x87, - 0xd7, 0x37, 0xee, 0x62, 0x98, 0xf7, 0x7e, 0x0c - }; - -static int -test_genaes_gcm(void) -{ - uint8_t res[sizeof(gcm_ct)], tag[sizeof(gcm_tag)]; - struct lws_genaes_ctx ctx; - struct lws_gencrypto_keyelem e; - size_t iv_off = 0; - - e.buf = (uint8_t *)gcm_key; - e.len = sizeof(gcm_key); - - /* Encrypt */ - - if (lws_genaes_create(&ctx, LWS_GAESO_ENC, LWS_GAESM_GCM, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - return 1; - } - - /* first we set the iv and aad */ - - iv_off = sizeof(gcm_iv); - if (lws_genaes_crypt(&ctx, gcm_aad, sizeof(gcm_aad), NULL, - (uint8_t *)gcm_iv, (uint8_t *)gcm_tag, - &iv_off, sizeof(gcm_tag))) { - lwsl_err("%s: lws_genaes_crypt 1 failed\n", __func__); - goto bail; - } - - if (lws_genaes_crypt(&ctx, gcm_pt, sizeof(gcm_pt), res, - NULL, NULL, NULL, 0)) { - lwsl_err("%s: lws_genaes_crypt 2 failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, tag, sizeof(tag))) { - lwsl_err("%s: lws_genaes_destroy enc failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(gcm_ct, res, sizeof(gcm_ct))) { - lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__); - lwsl_hexdump_notice(res, sizeof(gcm_ct)); - return -1; - } - - - /* Decrypt */ - - if (lws_genaes_create(&ctx, LWS_GAESO_DEC, LWS_GAESM_GCM, &e, 0, NULL)) { - lwsl_err("%s: lws_genaes_create failed\n", __func__); - return 1; - } - - iv_off = sizeof(gcm_iv); /* initial call sets iv + aad + tag */ - if (lws_genaes_crypt(&ctx, gcm_aad, sizeof(gcm_aad), NULL, - (uint8_t *)gcm_iv, (uint8_t *)gcm_tag, - &iv_off, sizeof(gcm_tag))) { - lwsl_err("%s: lws_genaes_crypt 1 failed\n", __func__); - goto bail; - } - - if (lws_genaes_crypt(&ctx, gcm_ct, sizeof(gcm_ct), res, - NULL, NULL, NULL, 0)) { - lwsl_err("%s: lws_genaes_crypt 2 failed\n", __func__); - goto bail; - } - - if (lws_genaes_destroy(&ctx, tag, sizeof(tag))) { - lwsl_err("%s: lws_genaes_destroy dec failed\n", __func__); - return -1; - } - - if (lws_timingsafe_bcmp(gcm_pt, res, sizeof(gcm_pt))) { - lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__); - lwsl_hexdump_notice(res, sizeof(gcm_ct)); - return -1; - } - - return 0; - -bail: - lws_genaes_destroy(&ctx, NULL, 0); - - return -1; -} - -int -test_genaes(struct lws_context *context) -{ - - if (test_genaes_cbc()) - goto bail; - - if (test_genaes_cfb128()) - goto bail; - - if (test_genaes_cfb8()) - goto bail; - - if (test_genaes_ctr()) - goto bail; - - if (test_genaes_ecb()) - goto bail; - -#if defined(MBEDTLS_CONFIG_H) && !defined(MBEDTLS_CIPHER_MODE_OFB) -#else - if (test_genaes_ofb()) - goto bail; -#endif - -#if defined(MBEDTLS_CONFIG_H) && !defined(MBEDTLS_CIPHER_MODE_XTS) -#else - if (test_genaes_xts()) - goto bail; -#endif - - if (test_genaes_gcm()) - goto bail; - - /* end */ - - lwsl_notice("%s: selftest OK\n", __func__); - - return 0; - -bail: - lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__); - - return 1; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/lws-genec.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/lws-genec.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/lws-genec.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/lws-genec.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,132 +0,0 @@ -/* - * lws-api-test-gencrypto - lws-genec - * - * Written in 2010-2018 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include - -static const uint8_t - *jwk_ec1 = (uint8_t *) - "{\"kty\":\"EC\"," - "\"crv\":\"P-256\"," - "\"x\":\"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\"," - "\"y\":\"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\"," - "\"d\":\"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE\"," - "\"use\":\"enc\"," - "\"kid\":\"rfc7517-A.2-example private key\"}" -; - -static int -test_genec1(struct lws_context *context) -{ - struct lws_genec_ctx ctx; - struct lws_jwk jwk; - struct lws_gencrypto_keyelem el[LWS_GENCRYPTO_EC_KEYEL_COUNT]; - //uint8_t res[32], res1[32]; - int n; - - memset(el, 0, sizeof(el)); - - if (lws_genecdh_create(&ctx, context, NULL)) - return 1; - - /* let's create a new key */ - - if (lws_genecdh_new_keypair(&ctx, LDHS_OURS, "P-256", el)) { - lwsl_err("%s: lws_genec_new_keypair failed\n", __func__); - return 1; - } - - lws_genec_dump(el); - lws_genec_destroy_elements(el); - - lws_genec_destroy(&ctx); - - if (lws_jwk_import(&jwk, NULL, NULL, (char *)jwk_ec1, - strlen((char *)jwk_ec1)) < 0) { - lwsl_notice("Failed to decode JWK test key\n"); - return 1; - } - - lws_jwk_dump(&jwk); - - if (jwk.kty != LWS_GENCRYPTO_KTY_EC) { - lws_jwk_destroy(&jwk); - lwsl_err("%s: jwk is not an EC key\n", __func__); - return 1; - } - - if (lws_genecdh_create(&ctx, context, NULL)) - return 1; - - n = lws_genecdh_set_key(&ctx, jwk.e, LDHS_OURS); - if (n) { - lws_jwk_destroy(&jwk); - lwsl_err("%s: lws_genec_create failed: %d\n", __func__, n); - return 1; - } -#if 0 - if (lws_genec_crypt(&ctx, cbc256, 16, res, (uint8_t *)cbc256_iv, - NULL, NULL)) { - lwsl_err("%s: lws_genec_crypt failed\n", __func__); - goto bail; - } - - if (lws_timingsafe_bcmp(cbc256_enc, res, 16)) { - lwsl_err("%s: lws_genec_crypt encoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - goto bail; - } - - lws_genec_destroy(&ctx); - - if (lws_genec_create(&ctx, LWS_GAESO_DEC, LWS_GAESM_CBC, &e, NULL)) { - lwsl_err("%s: lws_genec_create dec failed\n", __func__); - return -1; - } - - if (lws_genec_crypt(&ctx, res, 16, res1, (uint8_t *)cbc256_iv, - NULL, NULL)) { - lwsl_err("%s: lws_genec_crypt dec failed\n", __func__); - goto bail; - } - - if (lws_timingsafe_bcmp(cbc256, res1, 16)) { - lwsl_err("%s: lws_genec_crypt decoding mismatch\n", __func__); - lwsl_hexdump_notice(res, 16); - goto bail; - } -#endif - lws_genec_destroy(&ctx); - - lws_jwk_destroy(&jwk); - - return 0; - -//bail: -// lws_genec_destroy(&ctx); - -// return -1; -} - -int -test_genec(struct lws_context *context) -{ - if (test_genec1(context)) - goto bail; - - /* end */ - - lwsl_notice("%s: selftest OK\n", __func__); - - return 0; - -bail: - lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__); - - return 1; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * lws-api-test-gencrypto - * - * Written in 2010-2018 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include - -int -test_genaes(struct lws_context *context); -int -test_genec(struct lws_context *context); - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int result = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS gencrypto apis tests\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - result |= test_genaes(context); - result |= test_genec(context); - - lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS"); - - lws_context_destroy(context); - - return result; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/README.md libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -# lws api test gencrypto - -Demonstrates how to use and performs selftests for Generic Crypto, -which works the same whether the tls backend is OpenSSL or mbedTLS - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 - -``` - $ ./lws-api-test-gencrypto -[2018/12/05 08:30:27:1342] USER: LWS gencrypto apis tests -[2018/12/05 08:30:27:1343] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off -[2018/12/05 08:30:27:1343] NOTICE: created client ssl context for default -[2018/12/05 08:30:27:1344] NOTICE: test_genaes: selftest OK -[2018/12/05 08:30:27:1344] USER: Completed: PASS -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/selftest.sh libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/selftest.sh --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-gencrypto/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-gencrypto/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=1 - -dotest $1 $2 apiselftest -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-api-test-jose) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-jose) -set(SRCS main.c jwk.c jws.c jwe.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_JOSE 1 requirements) - -if (requirements) - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/jwe.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/jwe.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/jwe.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/jwe.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2296 +0,0 @@ -/* - * lws-api-test-jose - RFC7516 jwe tests - * - * Written in 2010-2018 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include - -/* - * These are the inputs and outputs from the worked example in RFC7516 - * Appendix A.1 {"alg":"RSA-OAEP","enc":"A256GCM"} - */ - - -static char - -*ex_a1_ptext = - "The true sign of intelligence is not knowledge but imagination.", - -*ex_a1_compact = - "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ." - "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGe" - "ipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDb" - "Sv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaV" - "mqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je8" - "1860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi" - "6UklfCpIMfIjf7iGdXKHzg." - "48V1_ALb6US04U3b." - "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6ji" - "SdiwkIr3ajwQzaBtQD_A." - "XFBoMYUZodetZdvTiFvSkQ", - - *ex_a1_jwk_json = - "{\"kty\":\"RSA\"," - "\"n\":\"oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW" - "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" - "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" - "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" - "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" - "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw\"," - "\"e\":\"AQAB\"," - "\"d\":\"kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N" - "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" - "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" - "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" - "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" - "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ\"," - "\"p\":\"1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-" - "SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lf" - "fNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0\"," - "\"q\":\"wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBm" - "UDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aX" - "IWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc\"," - "\"dp\":\"ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KL" - "hMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827" - "rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE\"," - "\"dq\":\"Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCj" - "ywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDB" - "UfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis\"," - "\"qi\":\"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7" - "AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3" - "eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY\"" - "}" -; - -static int -test_jwe_a1(struct lws_context *context) -{ - struct lws_jwe jwe; - char temp[2048], compact[2048]; - int n, ret = -1, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, ex_a1_jwk_json, - strlen(ex_a1_jwk_json)) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - /* converts a compact serialization to jws b64 + decoded maps */ - if (lws_jws_compact_decode(ex_a1_compact, strlen(ex_a1_compact), - &jwe.jws.map, &jwe.jws.map_b64, temp, - &temp_len) != 5) { - lwsl_err("%s: lws_jws_compact_decode failed\n", __func__); - goto bail; - } - - n = lws_jwe_auth_and_decrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (jwe.jws.map.len[LJWE_CTXT] < strlen(ex_a1_ptext) || - lws_timingsafe_bcmp(jwe.jws.map.buf[LJWE_CTXT], ex_a1_ptext, - strlen(ex_a1_ptext))) { - lwsl_err("%s: plaintext AES decrypt wrong\n", __func__); - lwsl_hexdump_notice(ex_a1_ptext, strlen(ex_a1_ptext)); - lwsl_hexdump_notice(jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]); - goto bail; - } - - /* - * Canned decrypt worked properly... let's also try encoding the - * plaintext ourselves and decoding that... - */ - lws_jwe_destroy(&jwe); - temp_len = sizeof(temp); - lws_jwe_init(&jwe, context); - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, ex_a1_jwk_json, - strlen(ex_a1_jwk_json)) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - if (lws_gencrypto_jwe_alg_to_definition("RSA-OAEP", &jwe.jose.alg)) { - lwsl_err("Unknown cipher alg \"RSA-OAEP\"\n"); - goto bail; - } - if (lws_gencrypto_jwe_enc_to_definition("A256GCM", &jwe.jose.enc_alg)) { - lwsl_err("Unknown payload enc alg \"A256GCM\"\n"); - goto bail; - } - - /* we require a JOSE-formatted header to do the encryption */ - - jwe.jws.map.buf[LJWS_JOSE] = temp; - jwe.jws.map.len[LJWS_JOSE] = lws_snprintf(temp, temp_len, - "{\"alg\":\"%s\",\"enc\":\"%s\"}", "RSA-OAEP", "A256GCM"); - temp_len -= jwe.jws.map.len[LJWS_JOSE]; - - /* - * dup the plaintext into the ciphertext element, it will be - * encrypted in-place to a ciphertext of the same length - */ - - if (lws_jws_dup_element(&jwe.jws.map, LJWE_CTXT, - lws_concat_temp(temp, temp_len), &temp_len, - ex_a1_ptext, strlen(ex_a1_ptext), 0)) { - lwsl_notice("%s: Not enough temp space for ptext\n", __func__); - goto bail; - } - - /* CEK size is determined by hash / hmac size */ - - n = lws_gencrypto_bits_to_bytes(jwe.jose.enc_alg->keybits_fixed); - if (lws_jws_randomize_element(context, &jwe.jws.map, LJWE_EKEY, - lws_concat_temp(temp, temp_len), - &temp_len, n, - LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) { - lwsl_err("Problem getting random\n"); - goto bail; - } - - n = lws_jwe_encrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_encrypt failed\n", __func__); - goto bail; - } - n = lws_jwe_render_compact(&jwe, compact, sizeof(compact)); - if (n < 0) { - lwsl_err("%s: lws_jwe_render_compact failed: %d\n", - __func__, n); - goto bail; - } - - // puts(compact); - - /* - * Okay... what happens when we try to decode what we created? - */ - - lws_jwe_destroy(&jwe); - lws_jwe_init(&jwe, context); - temp_len = sizeof(temp); - - /* converts a compact serialization to jws b64 + decoded maps */ - if (lws_jws_compact_decode(compact, strlen(compact), &jwe.jws.map, - &jwe.jws.map_b64, temp, &temp_len) != 5) { - lwsl_err("%s: lws_jws_compact_decode failed\n", __func__); - goto bail; - } - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, ex_a1_jwk_json, - strlen(ex_a1_jwk_json)) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - n = lws_jwe_auth_and_decrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: generated lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - if (ret) - lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - - -/* A.2. Example JWE using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256 - * - * This example encrypts the plaintext "Live long and prosper." to the - * recipient using RSAES-PKCS1-v1_5 for key encryption and - * AES_128_CBC_HMAC_SHA_256 for content encryption. - */ - -/* "Live long and prosper." */ -static uint8_t - -ex_a2_ptext[] = { - 76, 105, 118, 101, 32, 108, 111, 110, - 103, 32, 97, 110, 100, 32, 112, 114, - 111, 115, 112, 101, 114, 46 -}, *lws_jwe_ex_a2_jwk_json = (uint8_t *) - "{" - "\"kty\":\"RSA\"," - "\"n\":\"sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1Wl" - "UzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDpre" - "cbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_" - "7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBI" - "Y2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU" - "7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw\"," - "\"e\":\"AQAB\"," - "\"d\":\"VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq" - "1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-ry" - "nq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_" - "0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj" - "-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-Kyvj" - "T1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ\"," - "\"p\":\"9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68" - "ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEP" - "krdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM\"," - "\"q\":\"uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-y" - "BhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN" - "-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0\"," - "\"dp\":\"w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuv" - "ngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcra" - "Hawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs\"," - "\"dq\":\"o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff" - "7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_" - "odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU\"," - "\"qi\":\"eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlC" - "tUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZ" - "B9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo\"" - "}", - -*ex_a2_compact = (uint8_t *) - "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0" - "." - "UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm" - "1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc" - "HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF" - "NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8" - "rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv" - "-B3oWh2TbqmScqXMR4gp_A" - "." - "AxY8DCtDaGlsbGljb3RoZQ" - "." - "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY" - "." - "9hH0vgRfYgPnAHOd8stkvw" -; - -static int -test_jwe_a2(struct lws_context *context) -{ - struct lws_jwe jwe; - char temp[2048]; - int n, ret = -1, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, (char *)lws_jwe_ex_a2_jwk_json, - strlen((char *)lws_jwe_ex_a2_jwk_json)) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - /* converts a compact serialization to jws b64 + decoded maps */ - if (lws_jws_compact_decode((const char *)ex_a2_compact, - strlen((char *)ex_a2_compact), - &jwe.jws.map, &jwe.jws.map_b64, - (char *)temp, &temp_len) != 5) { - lwsl_err("%s: lws_jws_compact_decode failed\n", __func__); - goto bail; - } - - n = lws_jwe_auth_and_decrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (jwe.jws.map.len[LJWE_CTXT] < sizeof(ex_a2_ptext) || - lws_timingsafe_bcmp(jwe.jws.map.buf[LJWE_CTXT], ex_a2_ptext, - sizeof(ex_a2_ptext))) { - lwsl_err("%s: plaintext AES decrypt wrong\n", __func__); - lwsl_hexdump_notice(ex_a2_ptext, sizeof(ex_a2_ptext)); - lwsl_hexdump_notice(jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - if (ret) - lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - -/* JWE creation using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256 - * - * This example encrypts a different, larger plaintext using the jwk key from - * the test above, and AES_128_CBC_HMAC_SHA_256 for content encryption. - */ - -static const char *rsa256a128_jose = - "{ \"alg\":\"RSA1_5\",\"enc\":\"A128CBC-HS256\"}"; - -static uint8_t - - /* plaintext is 1024 bytes from /dev/urandom */ - -ra_ptext_1024[] = { - 0xfe, 0xc6, 0x4f, 0x3e, 0x4a, 0x19, 0xe9, 0xd7, - 0xc2, 0x13, 0xe7, 0xc5, 0x78, 0x6e, 0x71, 0xf6, - 0x6e, 0xdd, 0x04, 0xaf, 0xaa, 0x4e, 0xa8, 0xad, - 0xd8, 0xe0, 0xb3, 0x32, 0x97, 0x43, 0x7c, 0xd8, - 0xd1, 0x5f, 0x56, 0xac, 0x70, 0xaf, 0x7d, 0x0b, - 0x40, 0xa1, 0x96, 0x71, 0x7c, 0xc4, 0x4a, 0x37, - 0x0b, 0xa6, 0x06, 0xb3, 0x8c, 0x87, 0xee, 0xb6, - 0x15, 0xfe, 0xaa, 0x60, 0x7e, 0x7f, 0xdc, 0xb0, - 0xff, 0x96, 0x4b, 0x30, 0x60, 0xcf, 0xc6, 0x5d, - 0x09, 0x6a, 0x6f, 0x66, 0x0c, 0x5f, 0xb0, 0x6f, - 0x61, 0xa6, 0x26, 0x02, 0xbd, 0x46, 0xda, 0xa3, - 0x73, 0x19, 0x17, 0xff, 0xe0, 0x5f, 0x30, 0x72, - 0x7d, 0x17, 0xd8, 0xb2, 0xbe, 0x84, 0x3e, 0x4d, - 0x76, 0xbd, 0x62, 0x5d, 0x63, 0xfe, 0x11, 0x32, - 0x11, 0x41, 0xdc, 0xed, 0x96, 0xfd, 0x31, 0x38, - 0x6a, 0x84, 0x55, 0x7a, 0x33, 0x3f, 0x37, 0xc3, - 0x37, 0x7b, 0xc1, 0xb7, 0x89, 0x00, 0x39, 0xa6, - 0x94, 0x91, 0xb7, 0x19, 0x6b, 0x1d, 0x99, 0xeb, - 0xf6, 0x10, 0xb9, 0xd2, 0xcd, 0x15, 0x0d, 0xbc, - 0x24, 0x34, 0x9a, 0x52, 0x64, 0x21, 0x72, 0x1e, - 0x9a, 0x00, 0xf2, 0xcf, 0xf1, 0x7d, 0x1a, 0x12, - 0x8d, 0x39, 0xbc, 0xf9, 0x09, 0xfd, 0xd9, 0x22, - 0x27, 0x28, 0xe1, 0x3a, 0x0b, 0x82, 0xba, 0x9a, - 0xe5, 0x9d, 0xa8, 0x12, 0x6e, 0xf5, 0x4b, 0xc7, - 0x2b, 0x9c, 0xdc, 0xfe, 0xf3, 0xe8, 0x74, 0x65, - 0x3d, 0xe0, 0xaa, 0x64, 0xf3, 0x43, 0xa4, 0x88, - 0xa8, 0xbe, 0x60, 0xdb, 0xfd, 0x2d, 0x3b, 0x84, - 0x82, 0x8f, 0x4d, 0xbb, 0xe4, 0xa9, 0x59, 0xe3, - 0x6c, 0x52, 0x45, 0xe4, 0x34, 0xdb, 0x28, 0x0e, - 0x4a, 0x44, 0xb6, 0x9a, 0x25, 0x9b, 0x3b, 0xae, - 0xe1, 0x12, 0x1d, 0x1c, 0x66, 0x7d, 0xb9, 0x5b, - 0x5f, 0xc2, 0x4a, 0xaa, 0xd2, 0xe9, 0x65, 0xe2, - 0x85, 0x6f, 0xf6, 0x67, 0x66, 0x8e, 0x0b, 0xd2, - 0x60, 0xf8, 0x43, 0x60, 0x04, 0x9b, 0xa9, 0x3a, - 0x6a, 0x3c, 0x02, 0x3c, 0x08, 0x9d, 0x60, 0x1c, - 0xc4, 0x27, 0x3e, 0xff, 0xd0, 0x70, 0x94, 0x43, - 0x3e, 0x9e, 0x69, 0x19, 0x22, 0xf0, 0xec, 0x26, - 0x2d, 0xa5, 0x71, 0xf3, 0x92, 0x61, 0x95, 0xce, - 0xc3, 0xc0, 0xa0, 0xc3, 0x98, 0x22, 0xdd, 0x32, - 0x3c, 0x48, 0xcb, 0xd1, 0x61, 0xa0, 0xaa, 0x9a, - 0x7e, 0x5a, 0xfa, 0x26, 0x46, 0x49, 0xfc, 0x9c, - 0xaa, 0x21, 0x06, 0x45, 0xf1, 0xa0, 0xc9, 0xef, - 0x6b, 0x89, 0xf2, 0x01, 0x20, 0x54, 0xfa, 0x0a, - 0x23, 0xff, 0xbd, 0x64, 0x35, 0x94, 0xfd, 0x35, - 0x70, 0x52, 0x94, 0x66, 0xc5, 0xd0, 0x27, 0xc1, - 0x8f, 0x6d, 0xc4, 0xa3, 0x34, 0xc2, 0xea, 0xf0, - 0xb3, 0x0d, 0x6c, 0x13, 0xb5, 0xc9, 0x6e, 0x5c, - 0xeb, 0x8b, 0x7b, 0xf5, 0x21, 0x4c, 0xe3, 0xb7, - 0x73, 0x6d, 0x07, 0xaa, 0x44, 0xc4, 0xba, 0xc5, - 0xa5, 0x0e, 0x75, 0x28, 0xb7, 0x50, 0x22, 0x54, - 0xa7, 0xe1, 0x2e, 0xfd, 0x20, 0xcd, 0xa4, 0x31, - 0xa3, 0xb2, 0x73, 0x98, 0x7c, 0x3c, 0x8f, 0xa3, - 0x40, 0x8a, 0xaf, 0x31, 0xfa, 0xf9, 0x70, 0x4d, - 0x83, 0x10, 0xc4, 0xa0, 0x9c, 0xd6, 0xa3, 0xd5, - 0x07, 0xaf, 0xaf, 0x35, 0x15, 0xd0, 0x84, 0x09, - 0x20, 0x36, 0x88, 0xac, 0x6f, 0x16, 0x5e, 0x03, - 0xa9, 0xfc, 0xb3, 0x2d, 0x01, 0x57, 0xb3, 0xed, - 0x4b, 0x55, 0x2b, 0xbc, 0x92, 0x87, 0x3e, 0x27, - 0xc4, 0x2c, 0x44, 0xac, 0x05, 0x5f, 0x26, 0xe7, - 0xe9, 0xb0, 0x2d, 0x6b, 0x3c, 0x8c, 0xd2, 0xb4, - 0x3c, 0xb4, 0x86, 0xfe, 0x68, 0x99, 0x2a, 0x42, - 0xac, 0xa4, 0xb3, 0x89, 0x61, 0xb3, 0xd1, 0xdf, - 0x9b, 0x58, 0xc7, 0x81, 0x62, 0x87, 0x26, 0x52, - 0x51, 0xe7, 0x7d, 0x7c, 0x37, 0x14, 0xe5, 0x19, - 0x28, 0x34, 0x3e, 0x95, 0x17, 0x36, 0x12, 0xf9, - 0x5e, 0xc1, 0x3c, 0x9c, 0x28, 0x70, 0x06, 0xdf, - 0xc4, 0x6d, 0x25, 0x04, 0x46, 0xe0, 0x95, 0xf0, - 0xc8, 0x57, 0x48, 0x27, 0x26, 0xf3, 0xf7, 0x19, - 0xbe, 0xea, 0xb4, 0xd4, 0x64, 0xaf, 0x67, 0x7c, - 0xf5, 0xa9, 0xfb, 0x85, 0x4a, 0x43, 0x9c, 0x62, - 0x06, 0x5e, 0x28, 0x2a, 0x7b, 0x1e, 0xb3, 0x07, - 0xe7, 0x19, 0x32, 0xa4, 0x4e, 0xb4, 0xce, 0xe0, - 0x92, 0x56, 0xf5, 0x10, 0xcb, 0x56, 0x34, 0x4b, - 0x0d, 0xe1, 0xd3, 0x6d, 0xfe, 0xf0, 0x44, 0xf7, - 0x22, 0x1d, 0x5e, 0x6b, 0xa7, 0xa5, 0x83, 0x2e, - 0xeb, 0x14, 0xf2, 0xd7, 0x27, 0x5a, 0x2a, 0xd2, - 0x55, 0x35, 0xe6, 0x7e, 0xd9, 0x3b, 0xac, 0x4e, - 0x5a, 0x22, 0x46, 0xd5, 0x7b, 0x57, 0x9c, 0x58, - 0xfe, 0xd0, 0xda, 0xbf, 0x7d, 0xe9, 0x8c, 0xb7, - 0xba, 0x88, 0xf1, 0xc3, 0x82, 0x53, 0xc3, 0x66, - 0x20, 0x51, 0x12, 0xd3, 0xf9, 0xaf, 0xe9, 0xcb, - 0xc1, 0x7a, 0xe6, 0x22, 0x44, 0xa5, 0xdf, 0x18, - 0xb3, 0x6e, 0x6c, 0xba, 0xf3, 0xc6, 0x24, 0x5a, - 0x1c, 0x67, 0xa6, 0xa5, 0xb4, 0xb1, 0x35, 0xdf, - 0x5a, 0x60, 0x5c, 0x0b, 0x66, 0xd3, 0x1f, 0x4e, - 0x7c, 0xcb, 0x93, 0x7e, 0x2f, 0x6d, 0xbd, 0xce, - 0x26, 0x52, 0x44, 0xee, 0xbb, 0xd8, 0x8f, 0xf2, - 0x67, 0x38, 0x0d, 0x3b, 0xaa, 0x21, 0x73, 0xf8, - 0x3b, 0x54, 0x9d, 0x4e, 0x5e, 0xf1, 0xa2, 0x18, - 0x5a, 0xf1, 0x6c, 0x32, 0xbf, 0x0a, 0x73, 0x14, - 0x48, 0x4f, 0x56, 0xc0, 0x87, 0x6d, 0x3b, 0x16, - 0xcc, 0x3f, 0x44, 0x19, 0x85, 0x22, 0x43, 0x5f, - 0x8c, 0x29, 0xbd, 0xa0, 0xce, 0x84, 0xd9, 0x4a, - 0xcf, 0x00, 0x6b, 0x37, 0x35, 0xe0, 0xb3, 0xc9, - 0xd1, 0x58, 0xd1, 0x1b, 0xc3, 0x6f, 0xe3, 0x50, - 0xdb, 0xa6, 0x5e, 0x03, 0x18, 0xe5, 0xe2, 0xc1, - 0x97, 0xd5, 0xf8, 0x42, 0x6f, 0xe6, 0x61, 0x80, - 0xc9, 0x7c, 0xc6, 0x83, 0xf0, 0xad, 0x70, 0x13, - 0x0e, 0x26, 0x75, 0xc0, 0x12, 0x23, 0x14, 0xef, - 0x1f, 0xdf, 0xfd, 0x47, 0x99, 0x9f, 0x22, 0xf3, - 0x57, 0x21, 0xdc, 0x38, 0xe4, 0x79, 0x87, 0x5b, - 0x67, 0x66, 0xdd, 0x0b, 0xe0, 0xae, 0xb5, 0x97, - 0xd8, 0xa6, 0x5d, 0x02, 0xcf, 0x6b, 0x84, 0x19, - 0xc1, 0xbb, 0x25, 0xd2, 0x10, 0xb9, 0x63, 0xeb, - 0x4b, 0x27, 0x8d, 0x05, 0x31, 0xce, 0x3b, 0x0c, - 0x5f, 0xd4, 0x83, 0x47, 0xa4, 0x8b, 0xc4, 0x76, - 0x33, 0x74, 0x1a, 0x07, 0xf8, 0x18, 0x82, 0x1c, - 0x8e, 0x01, 0x75, 0x78, 0xea, 0xd9, 0x72, 0x61, - 0x71, 0xa9, 0x09, 0x44, 0x7b, 0x0f, 0x12, 0xcf, - 0x4c, 0x76, 0x7b, 0x69, 0xc8, 0x64, 0x98, 0x60, - 0x45, 0xb6, 0xc7, 0x6b, 0xd8, 0x43, 0x99, 0x08, - 0xc9, 0xd3, 0x6f, 0x01, 0x4f, 0x57, 0x6f, 0x49, - 0x4f, 0x4f, 0x72, 0xa4, 0xa2, 0x45, 0xe1, 0x0e, - 0xf2, 0x08, 0x3e, 0x67, 0xc3, 0x83, 0x5b, 0xb1, - 0x24, 0xc0, 0xe0, 0x3a, 0xf5, 0x1f, 0xf2, 0x06, - 0x4b, 0xa7, 0x6f, 0xd2, 0xb2, 0x81, 0x96, 0x91, - 0x42, 0xb1, 0x53, 0x65, 0x3a, 0x12, 0xcd, 0x33, - 0xb3, 0x7e, 0x79, 0xc0, 0x46, 0xf6, 0xd8, 0x4a, - 0x22, 0x35, 0xb8, 0x3f, 0xe4, 0x08, 0x88, 0x49, - 0x3c, 0x73, 0x9a, 0x44, 0xe3, 0x3b, 0xcc, 0xc4, - 0xae, 0x7c, 0xbe, 0xfd, 0xa6, 0x4a, 0xd4, 0x26, - 0x52, 0x58, 0x81, 0x30, 0x66, 0x44, 0x54, 0xc8, - 0xe4, 0x7c, 0x5b, 0x63, 0x06, 0x60, 0x94, 0x62, - 0xe5, 0x47, 0x45, 0xfb, 0x58, 0xf5, 0x6a, 0x7c, - 0xb2, 0x35, 0x08, 0x03, 0x15, 0x68, 0xb3, 0x13, - 0xa5, 0xbd, 0xf2, 0x1e, 0x2e, 0x1c, 0x8f, 0xc6, - 0xc7, 0xd1, 0xa9, 0x64, 0x37, 0x2b, 0x23, 0xfa, - 0x7e, 0x56, 0x22, 0xf0, 0x8a, 0xbd, 0xeb, 0x04 -}, - -r256a128_cek[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f -} -; - -static int -test_jwe_ra_ptext_1024(struct lws_context *context, char *jwk_txt, int jwk_len) -{ - char temp[4096], compact[4096]; - struct lws_jwe jwe; - int n, ret = -1, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - /* reuse the rsa private key from the JWE Appendix 2 test above */ - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, jwk_txt, jwk_len) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - /* dup the plaintext, it will be replaced in-situ by the ciphertext */ - - if (lws_jws_dup_element(&jwe.jws.map, LJWE_CTXT, - lws_concat_temp(temp, temp_len), &temp_len, - ra_ptext_1024, sizeof(ra_ptext_1024), - lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN, - sizeof(ra_ptext_1024)))) { - lwsl_notice("%s: Not enough temp space for ptext\n", __func__); - goto bail; - } - - /* dup the cek, since it will be replaced by the encrypted key */ - - if (lws_jws_dup_element(&jwe.jws.map, LJWE_EKEY, - lws_concat_temp(temp, temp_len), &temp_len, - r256a128_cek, sizeof(r256a128_cek), - LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) { - lwsl_notice("%s: Not enough temp space for EKEY\n", __func__); - goto bail; - } - - jwe.jws.map.buf[LJWE_JOSE] = rsa256a128_jose; - jwe.jws.map.len[LJWE_JOSE] = strlen(rsa256a128_jose); - - n = lws_jwe_parse_jose(&jwe.jose, jwe.jws.map.buf[LJWE_JOSE], - jwe.jws.map.len[LJWE_JOSE], - lws_concat_temp(temp, temp_len), &temp_len); - if (n < 0) { - lwsl_err("%s: JOSE parse failed\n", __func__); - - goto bail; - } - - n = lws_jwe_encrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_encrypt failed\n", __func__); - goto bail; - } - - n = lws_jwe_render_compact(&jwe, compact, sizeof(compact)); - if (n < 0) { - lwsl_err("%s: lws_jwe_render_compact failed: %d\n", __func__, n); - goto bail; - } - - // puts(compact); - - lws_jwe_destroy(&jwe); - lws_jwe_init(&jwe, context); - temp_len = sizeof(temp); - - /* now we created the encrypted version, see if we can decrypt it */ - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, jwk_txt, jwk_len) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - if (lws_jws_compact_decode(compact, n, &jwe.jws.map, &jwe.jws.map_b64, - temp, &temp_len) != 5) { - lwsl_err("%s: failed to parse generated compact\n", __func__); - - goto bail; - } - - n = lws_jwe_auth_and_decrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (jwe.jws.map.len[LJWE_CTXT] < sizeof(ra_ptext_1024) || - lws_timingsafe_bcmp(jwe.jws.map.buf[LJWE_CTXT], ra_ptext_1024, - sizeof(ra_ptext_1024))) { - lwsl_err("%s: plaintext AES decrypt wrong\n", __func__); - lwsl_hexdump_notice(ra_ptext_1024, sizeof(ra_ptext_1024)); - lwsl_hexdump_notice(jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - - if (ret) - lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - -static const char *rsa256a192_jose = - "{ \"alg\":\"RSA1_5\",\"enc\":\"A192CBC-HS384\"}"; - -static const uint8_t r256a192_cek[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f -} -; - -static int -test_jwe_r256a192_ptext(struct lws_context *context, char *jwk_txt, int jwk_len) -{ - struct lws_jwe jwe; - char temp[4096], compact[4096]; - int n, ret = -1, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - /* reuse the rsa private key from the JWE Appendix 2 test above */ - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, jwk_txt, jwk_len) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - /* - * dup the plaintext into the ciphertext element, it will be - * encrypted in-place to a ciphertext of the same length + padding - */ - - if (lws_jws_dup_element(&jwe.jws.map, LJWE_CTXT, - lws_concat_temp(temp, temp_len), &temp_len, - ra_ptext_1024, sizeof(ra_ptext_1024), - lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN, - sizeof(ra_ptext_1024)))) { - lwsl_notice("%s: Not enough temp space for ptext\n", __func__); - goto bail; - } - - /* copy the cek, since it will be replaced by the encrypted key */ - - if (lws_jws_dup_element(&jwe.jws.map, LJWE_EKEY, - lws_concat_temp(temp, temp_len), &temp_len, - r256a192_cek, sizeof(r256a192_cek), - LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) { - lwsl_err("Problem getting random\n"); - goto bail; - } - - jwe.jws.map.buf[LJWE_JOSE] = rsa256a192_jose; - jwe.jws.map.len[LJWE_JOSE] = strlen(rsa256a192_jose); - - n = lws_jwe_parse_jose(&jwe.jose, jwe.jws.map.buf[LJWE_JOSE], - jwe.jws.map.len[LJWE_JOSE], - lws_concat_temp(temp, temp_len), &temp_len); - if (n < 0) { - lwsl_err("%s: JOSE parse failed\n", __func__); - - goto bail; - } - - n = lws_jwe_encrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_encrypt failed\n", __func__); - goto bail; - } - - n = lws_jwe_render_compact(&jwe, compact, sizeof(compact)); - if (n < 0) { - lwsl_err("%s: lws_jwe_render_compact failed: %d\n", __func__, n); - goto bail; - } - - // puts(compact); - - /* now we created the encrypted version, see if we can decrypt it */ - - lws_jwe_destroy(&jwe); - lws_jwe_init(&jwe, context); - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, jwk_txt, jwk_len) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - if (lws_jws_compact_decode(compact, n, &jwe.jws.map, &jwe.jws.map_b64, - temp, &temp_len) != 5) { - lwsl_err("%s: failed to parse generated compact\n", __func__); - - goto bail; - } - - n = lws_jwe_auth_and_decrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (jwe.jws.map.len[LJWE_CTXT] < sizeof(ra_ptext_1024) || - lws_timingsafe_bcmp(jwe.jws.map.buf[LJWE_CTXT], ra_ptext_1024, - sizeof(ra_ptext_1024))) { - lwsl_err("%s: plaintext AES decrypt wrong\n", __func__); - lwsl_hexdump_notice(ra_ptext_1024, sizeof(ra_ptext_1024)); - lwsl_hexdump_notice(jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - - if (ret) - lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - - -static const char *rsa256a256_jose = - "{ \"alg\":\"RSA1_5\",\"enc\":\"A256CBC-HS512\"}"; - -static const uint8_t r256a256_cek[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f -} -; - -static int -test_jwe_r256a256_ptext(struct lws_context *context, char *jwk_txt, int jwk_len) -{ - struct lws_jwe jwe; - char temp[4096], compact[4096]; - int n, ret = -1, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - /* reuse the rsa private key from the JWE Appendix 2 test above */ - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, jwk_txt, jwk_len) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - /* - * dup the plaintext into the ciphertext element, it will be - * encrypted in-place to a ciphertext of the same length + padding - */ - - if (lws_jws_dup_element(&jwe.jws.map, LJWE_CTXT, - lws_concat_temp(temp, temp_len), &temp_len, - ra_ptext_1024, sizeof(ra_ptext_1024), - lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN, - sizeof(ra_ptext_1024)))) { - lwsl_notice("%s: Not enough temp space for ptext\n", __func__); - goto bail; - } - - /* copy the cek, since it will be replaced by the encrypted key */ - - if (lws_jws_dup_element(&jwe.jws.map, LJWE_EKEY, - lws_concat_temp(temp, temp_len), &temp_len, - r256a256_cek, sizeof(r256a256_cek), - LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) { - lwsl_err("Problem getting random\n"); - goto bail; - } - - jwe.jws.map.buf[LJWE_JOSE] = rsa256a256_jose; - jwe.jws.map.len[LJWE_JOSE] = strlen(rsa256a256_jose); - - n = lws_jwe_parse_jose(&jwe.jose, rsa256a256_jose, strlen(rsa256a256_jose), - lws_concat_temp(temp, temp_len), &temp_len); - if (n < 0) { - lwsl_err("%s: JOSE parse failed\n", __func__); - - goto bail; - } - - n = lws_jwe_encrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_encrypt failed\n", __func__); - goto bail; - } - - n = lws_jwe_render_compact(&jwe, compact, sizeof(compact)); - if (n < 0) { - lwsl_err("%s: lws_jwe_render_compact failed: %d\n", __func__, n); - goto bail; - } - - // puts(compact); - - /* now we created the encrypted version, see if we can decrypt it */ - - lws_jwe_destroy(&jwe); - lws_jwe_init(&jwe, context); - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, jwk_txt, jwk_len) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - if (lws_jws_compact_decode(compact, n, &jwe.jws.map, &jwe.jws.map_b64, - temp, &temp_len) != 5) { - lwsl_err("%s: failed to parse generated compact\n", __func__); - - goto bail; - } - - n = lws_jwe_auth_and_decrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (jwe.jws.map.len[LJWE_CTXT] < sizeof(ra_ptext_1024) || - lws_timingsafe_bcmp(jwe.jws.map.buf[LJWE_CTXT], ra_ptext_1024, - sizeof(ra_ptext_1024))) { - lwsl_err("%s: plaintext AES decrypt wrong\n", __func__); - lwsl_hexdump_notice(ra_ptext_1024, sizeof(ra_ptext_1024)); - lwsl_hexdump_notice(jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - - if (ret) - lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - -/* produced by running the minimal example `lws-crypto-jwk -t RSA -b 2048 -c` */ - -static const char *rsa_key_2048 = - "{" - "\"e\":\"AQAB\"," - "\"kty\":\"RSA\"," - "\"n\":\"lBJdvUq-9_8hlcduIWuBjRb0tGzzAvS4foqoNCO7g-rOXMdeAcmq" - "aSzWTbkaGIc3L1I4-Q3TOZtxn2UhuDlShZRIhM6JCQuUVNVAF3TD7oXxHtZ" - "LJ7y_BqCUlrAmW31lu-nVmhY2G3xW26yXWUsDbCxz0hfLbVnXRSvVKLzYWm" - "_yyrFyEWfxB8peDocvKGh879z_aPCKE3PDOEl2AsgzYfpnWCLytkgnrTeL6" - "qY8HXxvvV-Jw-XMaRiwH0VldpIjs4DaoN35Kj1Ex7QOZznTkbYtMIqse8bR" - "LoR8Irkxbc5ncUAuX1KSV6lpPtelsA3RtEjJ4NHV-5eEABiYh8_CFQ\"," - "\"d\":\"DDpguQ9RVQFMoJC5z2hlkvq91kvsXPv2Y9Dcki256xYlg55H7Pre" - "p__hahrABR2Jg6QVJhArt5ABjUnDQ_JL69HH6VvLD6RVVBTQ-FRBZ_3HYKY" - "Oynx5BA7tJm1BRatF5FkBCvq27i8nAc4vfjAb22o9CFvEW3FLaKAgOCncQ3" - "Tnbz9CddH89n7DXw4kBFI8q5ugF_aRIg5-i42W_hQinLaBhZ_zhAuE-nvlt" - "ZnhDal8cX3T60lNoUrDOlirqEOXKO3gXCHpm3csZ6nabHYD1UCyHOmi2RsR" - "pzjaiqjXdPbwPzQoh2DcYpavNrf1mtHiqTwLZDTJIRHWHufJzHf-sw\"," - "\"p\":\"ySeC3FtvzduDEL-FX4JqbRN06PdBhUmosCkymmbBjriuLNpkGkG-" - "1ex7r-M8neUBZbctmDdih6cpLZ8hjZv3eEDZ4b5Z2LqZnja4QvVoWLUs4Fb" - "NN_PxJCR5H28uUfT6ThxqT0Nb2enb8Dyp0Qxvd7eJUeYz6jOt7pEK-ErTB4" - "M\"," - "\"q\":\"vHG2Pd6QUH7vFZjJtXwmlVnrz5tdJvUPQvz7ggeM69cqhf4vLajz" - "sqP9GhJr7bEkp6vKVdZGmfEdiFRD8cssIZq651oAO5Wr7zZd2mR_hG9jZx7" - "8Davfuxr4SZNN-bmoxO6dbDi-X2c7fvMI2YeJwL4groNKyiosdUYILTrYRI" - "c\"," - "\"dp\":\"h5Gqf2rcokgEQGBjuigCJDtNuskRjoxDNV6-rRL99nt_X9lcR9n" - "xjOnRvowOyXeTBoN7JjCFpllBxm6ORYtNMO28KomIsimo6NmGPBJ7XfXVJe" - "k6bDBrX-l4_HeJJ1FM9SHvgDYsjGQxh-rKpIqWAYBf-yOD758e5T85vndnX" - "JM\"," - "\"dq\":\"K9LiB-dfdmjenw4mMp-JtYfw8Bn4gtvQzcpZjzbETgB-8iRXwm2" - "dJvk-HjcUhHWCyb-I0YeAacKKFK9MEconHDWIq87haPn4vyvMjcJ7aUgiPN" - "QW1_MVl8TA4xNvudi0Z__5-jYEB9nRG0fX0gbUQU-19_-uf-9o4WkE88fQj" - "bc\"," - "\"qi\":\"LEkTRqmomn9UiASeRfAKw-Z5q7cye9CSL4luSexFvA3Du7Oin-s" - "L9a7F3nJN4CuYzhtNMxQ0hM7k6ExzhDhXDlNRHxnNEDt81-CFRV98v7GVWV" - "SH1KnaKf9wgegxSSm-x536ki2SI8EN4k4qkqRF0iLVHZK7CgnWMbtt6tnpp" - "3k\"" - "}"; -/* produced by running the minimal example `lws-crypto-jwk -t RSA -b 4096 -c` */ - -static const char *rsa_key_4096 = - "{" - "\"e\":\"AQAB\"," - "\"kty\":\"RSA\"," - "\"n\":\"uiLBz1SUgd4eQ0okg6tlPdk9QUhTsqXmiJXygWVFgzT45E5_Rfkq" - "vZ2fwAqQ8DvxkDTUWiKpeXMpPRNWG5GxuBuq9n7xdA1vn1eQi8LoekB28dg" - "3MwMfozVSKCzyxG1f81xPE5x3EMVhCcx6hshhlMEHkzNNhE07d-oRO87ZC0" - "z_5L3Vh03uJBXaDKVlsgHAazoHLhn6G4odqv-ro54T6Nx1eEtyTnMmFY5ND" - "V4rN0SjQvSefbZZtsrtby8Z0JmeyvynmDwOINj7FpmPmpFLoWGXntc2yxPP" - "8SHnqfT9ESh94fxCMxRhDNohgpegRHyiYwj3M5ZYY6reCZYfOQONSWmc8yp" - "NBMJqj4LuJ2bTMGAFS17ZP4ZZWm5RP9ax100Dgk0yxP1UrybG5dCfJRQvHC" - "ncxG_aL6cSQu2o4fXqlJsNHxk3FjHtV_CMZ3tqvGTvwrs4yxvKwKv6r3fRh" - "KL01bGOePzp9THkHW2-lzVj6kUwnxBdHGZE6fcAnczOdp8ZIEdV1w6ThimC" - "m3Bw_TIyl3tkuxRWXpc_d6Q4iiSVKGKCvUvfAlESpTA4tIhQkij-T9FEoj2" - "WE2H1D35AKmjcfLCh6yszu8cmDNedn862pwnawE2RvRFAyuI113fLQeCbCz" - "tQ1JHuD8cnQt0hpGzReTa5UJ8OEOGIlyXNdWZyTpk\"," - "\"d\":\"G2ZW582AT-6xvz-IiP5fuJ9EMloygeuEeEo0aMJO3X3cfoUknJkN" - "ZtyvYa5cgBSe3la8hKkyD9_5K9WvGP9VLTAbdk4g_m-k5QyXiU9PeAGJ0Nd" - "-Zqq4y0Zj2eil8u7Tz0fhFxay-zvG6VGZnsIcBTD2C7_jUwyoaqJA17A_CH" - "gU-ifMqS56VgMGdlKZmf7Cg7ZGzM1DoS6vZ9bbfgoczaw4OZVHlg9Cxa0NI" - "CDi1S-sJcTLGN_RLISKN5H0J54ZfzF6fUEn5kNykLTZrAvj2XV7g4UUOogn" - "1cvjJYRcBVzTzQKcfxbqo2DvymDGFZbQM6pj80rYJ5HFPh2EapjggPN8hXp" - "NlTNDEvC84QFv0lo2E-0nVWQqcyHtXd431O1JH2h5X822zKjXxkaztQSCj9" - "YP7AdAeoxIaWOa3aO1vcwURH2WWaNV-_KXVkPJNzfo9-bGYwblMw_RIqIkN" - "BDayTb8rBuQHTCE_tSEHgoSnkityGpr8j_vgA-Fa-SqmdqUlbklVpwA_Mq_" - "UH7RCaqe91dWxRhS_7c85tFMRFCKOcaRXkwxEpP2LD1AYe8yvVQlr0Se8_d" - "RefuQcC-BECwMW-TCgR3VxAuL7ExNTYe4bhBD8WYXsHP7wDXWX2Q4v7IRzj" - "cfVIdpTNYuWEd69PvXBCuy75hmDniSmS3Xps3ItGU\"," - "\"p\":\"961BtLSIZkHO7Vu1KfaA3urcwGpISKJiTSB5Nh6npxJr9mSjzv_f" - "e8VoxCX6CWGY0SEeQNUQ6ceTnAAxkSHtZJQGed598jBtxIexAWEE7oc9s9d" - "b0cWu4QWIVZYXrcOTEWmK1kWN4PXmnnQknrWQF49adn81BaOXqoL-tahe7f" - "faXzXe0RXuohK543ZKbuuHQ2TxqFG7CZpXiH_qn1Syao32u0V3iDFpmmCUV" - "h9O2JCzfo8sAosTrnQwC0pXz3Nvr_9Cnk6bMluJoMrwB1Ywg_DPQ1WvpYHO" - "URezEOqVC8Y3zrko199TMX2COKGNFgutVpnzxs2_h0PyINUmwrY4zQ\"," - "\"q\":\"wGQRaxy_gBafbrVJy4f32O0a2FQHzmS--WgHhoteDoF6ZAajLcV0" - "GEvb-AVmFER1Wii62BFaFJOYQIegELvnBFFzD6oHJRX7bM4m36G8J_TC1o9" - "T1IFnxOpaoFDf4JWf2k7DCXClGg_zueyOD8fj8F6j2nqpOfytuLmikHcWMc" - "dGTHTCRtQmvOk3pm0uk2qR0cQb5L3Ocv45tCKr55tMc6Zx3DKkMt1kmUwd2" - "HFfk_0WM6R7q4LNGIjwl8dwiERppLKA8xao9i3jOOdFEfAD-Zqv8H-32cyH" - "Mg6Guo4tPNAYSzcsz8nbEYPtKVVm-PDuM2cx0iaKnS8BIK2XTbzc_Q\"," - "\"dp\":\"ZXLWIwp_hEMYWyjhP9r0VlqlKTtfeEDrOuQ-Qei0iz6EclwurK8" - "p_yyRCSb1D7qmOaLzHWMollllINUDeIsJDdWEAY8cz4L-sy1RV1tCBeHnaC" - "6iMX5jb1Aw072y3T3qk4tDjxjWUHroh6bTCR8dckkJqNfaBAFKMlGNuyLIH" - "3kSPUV3ivUM1d4NvhnJyz02HmjOgz9W-Uv65rJei_zJR9P2aCbAG00CEHXW" - "zJ_uT86VdxV11WTaHu8Abt94sER8Tv6jbuyLrUjJSs9VGew32xNcEhya4ZQ" - "VyimG8zri6fu7CDXXgPS8wtzB5ihl_c2ypnJQ4_GKrgEqwEAOrFqvUQ\"," - "\"dq\":\"uzlmngcm8R6S3qi7fL7_2fG7uyPjSN5P3uR21l8QFCu6kFbJO8S" - "4muBP20hds4F_dlLGqXgRYo7TjpCtmztQsKoWv_ql41hGCfeAawa41WViqm" - "xmlxmrgzzRHsw1YhgZrNgTAz_E290EQT3Mbd0HnCZtbDMMNisIYAj_A3lwd" - "tbHOaYyXb0dSZ_nkSUVO05tQ2aGAo8Xtl5ih0NqaQR_XNhwW2pI0lsTB__D" - "15tU-O5FSdJaq2ip8KNrBzmF8IYrDKTNykKWAKRdSEX_uFoLdD8t0mxn3SM" - "luffa8vdjXJfh3GiASmHUt3HcPOooQEAufoWBPVJWeGqCvWtRH8yYfQ\"," - "\"qi\":\"h-e9es5J49OUF48gSXUI8cynZ8ydv5cThXc1deV3mil_7_7Hg8E" - "jV3gAErO4l-irHJplFmHFZvU1ud4zs1gtBt5TA-EeeepYOHMSssWDvDK3WI" - "zsM6C3vcNTSkT-ihaSFmPWHCVwJ1R3auWfeI2In3at0jd4t-OK-cCcGZXb7" - "90-EnyyDcdFTU9WfwVSOJffRGjoUYX8DexavClv7CBzPhpdUzGoeyarNaG4" - "z9MI8Q8txHyHgc_D70lZUum1cj0bZwgEj6yDzOPzSgUmICFJiLDDj93oPaI" - "v-5CQ_Ckju7icexc_kuuYTKBOLTj_vfaURnV3KCHul2UljUYOxkfeNQ\"" - "}"; - -static const char *rsa_key_4096_no_optional = - "{" - "\"e\":\"AQAB\"," - "\"kty\":\"RSA\"," - "\"n\":\"uiLBz1SUgd4eQ0okg6tlPdk9QUhTsqXmiJXygWVFgzT45E5_Rfkq" - "vZ2fwAqQ8DvxkDTUWiKpeXMpPRNWG5GxuBuq9n7xdA1vn1eQi8LoekB28dg" - "3MwMfozVSKCzyxG1f81xPE5x3EMVhCcx6hshhlMEHkzNNhE07d-oRO87ZC0" - "z_5L3Vh03uJBXaDKVlsgHAazoHLhn6G4odqv-ro54T6Nx1eEtyTnMmFY5ND" - "V4rN0SjQvSefbZZtsrtby8Z0JmeyvynmDwOINj7FpmPmpFLoWGXntc2yxPP" - "8SHnqfT9ESh94fxCMxRhDNohgpegRHyiYwj3M5ZYY6reCZYfOQONSWmc8yp" - "NBMJqj4LuJ2bTMGAFS17ZP4ZZWm5RP9ax100Dgk0yxP1UrybG5dCfJRQvHC" - "ncxG_aL6cSQu2o4fXqlJsNHxk3FjHtV_CMZ3tqvGTvwrs4yxvKwKv6r3fRh" - "KL01bGOePzp9THkHW2-lzVj6kUwnxBdHGZE6fcAnczOdp8ZIEdV1w6ThimC" - "m3Bw_TIyl3tkuxRWXpc_d6Q4iiSVKGKCvUvfAlESpTA4tIhQkij-T9FEoj2" - "WE2H1D35AKmjcfLCh6yszu8cmDNedn862pwnawE2RvRFAyuI113fLQeCbCz" - "tQ1JHuD8cnQt0hpGzReTa5UJ8OEOGIlyXNdWZyTpk\"," - "\"d\":\"G2ZW582AT-6xvz-IiP5fuJ9EMloygeuEeEo0aMJO3X3cfoUknJkN" - "ZtyvYa5cgBSe3la8hKkyD9_5K9WvGP9VLTAbdk4g_m-k5QyXiU9PeAGJ0Nd" - "-Zqq4y0Zj2eil8u7Tz0fhFxay-zvG6VGZnsIcBTD2C7_jUwyoaqJA17A_CH" - "gU-ifMqS56VgMGdlKZmf7Cg7ZGzM1DoS6vZ9bbfgoczaw4OZVHlg9Cxa0NI" - "CDi1S-sJcTLGN_RLISKN5H0J54ZfzF6fUEn5kNykLTZrAvj2XV7g4UUOogn" - "1cvjJYRcBVzTzQKcfxbqo2DvymDGFZbQM6pj80rYJ5HFPh2EapjggPN8hXp" - "NlTNDEvC84QFv0lo2E-0nVWQqcyHtXd431O1JH2h5X822zKjXxkaztQSCj9" - "YP7AdAeoxIaWOa3aO1vcwURH2WWaNV-_KXVkPJNzfo9-bGYwblMw_RIqIkN" - "BDayTb8rBuQHTCE_tSEHgoSnkityGpr8j_vgA-Fa-SqmdqUlbklVpwA_Mq_" - "UH7RCaqe91dWxRhS_7c85tFMRFCKOcaRXkwxEpP2LD1AYe8yvVQlr0Se8_d" - "RefuQcC-BECwMW-TCgR3VxAuL7ExNTYe4bhBD8WYXsHP7wDXWX2Q4v7IRzj" - "cfVIdpTNYuWEd69PvXBCuy75hmDniSmS3Xps3ItGU\"," - "\"p\":\"961BtLSIZkHO7Vu1KfaA3urcwGpISKJiTSB5Nh6npxJr9mSjzv_f" - "e8VoxCX6CWGY0SEeQNUQ6ceTnAAxkSHtZJQGed598jBtxIexAWEE7oc9s9d" - "b0cWu4QWIVZYXrcOTEWmK1kWN4PXmnnQknrWQF49adn81BaOXqoL-tahe7f" - "faXzXe0RXuohK543ZKbuuHQ2TxqFG7CZpXiH_qn1Syao32u0V3iDFpmmCUV" - "h9O2JCzfo8sAosTrnQwC0pXz3Nvr_9Cnk6bMluJoMrwB1Ywg_DPQ1WvpYHO" - "URezEOqVC8Y3zrko199TMX2COKGNFgutVpnzxs2_h0PyINUmwrY4zQ\"," - "\"q\":\"wGQRaxy_gBafbrVJy4f32O0a2FQHzmS--WgHhoteDoF6ZAajLcV0" - "GEvb-AVmFER1Wii62BFaFJOYQIegELvnBFFzD6oHJRX7bM4m36G8J_TC1o9" - "T1IFnxOpaoFDf4JWf2k7DCXClGg_zueyOD8fj8F6j2nqpOfytuLmikHcWMc" - "dGTHTCRtQmvOk3pm0uk2qR0cQb5L3Ocv45tCKr55tMc6Zx3DKkMt1kmUwd2" - "HFfk_0WM6R7q4LNGIjwl8dwiERppLKA8xao9i3jOOdFEfAD-Zqv8H-32cyH" - "Mg6Guo4tPNAYSzcsz8nbEYPtKVVm-PDuM2cx0iaKnS8BIK2XTbzc_Q\"" - "}"; - -/* This is a compact JWE containing the plaintext ra_ptext_1024 for the key - * lws_jwe_ex_a2_jwk_json... produced by test test above running on OpenSSL. - */ - -static char *jwe_compact_rsa_cbc_openssl = - "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0." - "HgQBfAg2IUpExcs74-gtuSOciw6rxh2cc4x9MVRWDZKSvMqoBYKAxl6ebSxGTReHhbvhv2Rm8stKq" - "OIROhWzTNGTQPnSRMzAm9x6ms39hAIGh1KCEZ47MRYkCN7hElfDVtczOCX3ZRsG9_qYEmzcY1aJ-4" - "_LS3yeP0HfqKBmcfvjVLw-KqcUuHp5YXsZEvt28nA9uBlCN0ROWy_2Fs-zlQj8pDMWo5pZrffKTUX" - "gUh_UJ9eC3qNyXtQSqUH-5vDeHPhxNnIJOsmJ5ZUAjxXPm-RJZRC9THg0DzGRZn9IqfP9qcanbcZ8" - "iow7gjFh1EPp3MKlpZqDKbLLei1WZvz2_A." - "q4STtyu4yxZfH1HNDYlYcA." - "_uRfuwWO22_auSqXHORw_e_Q6PmbpC0sv0tefVKsj3Zqnh2qUBlj10kiWBMWoMMjqsClBO0nUoup4" - "c7i1YSqxlCHliXru3athv_EYtg5qvC-z2co9NiFABHCHmBDrhj7CuKN5gqFDt1EbYMLwWtU3gOnQy" - "dvnzfFcQs4_jKi6tRpQzbobrkkZ2p7Y_ltjA1Wmwqrp9O8DGSRnvcomqzGHcshuyxTkjLDzD8TSMR" - "S1kp-miy5eDGAcp-ymWiUKN7gswy5FPjPQYzgs7Vc0n0R1ljepRHJiHaP61z_DKWXrCE6RqAVqnaw" - "TjjVOXXKKF9pz9W7pZL8diLZ0Ueev6xk8wzRRsUChM5toQNzFLXsnzSDQSzfSKpRnLjYvosiEegyx" - "RrwtQwEcNCXRj0aGxG6e_W79JdUJoi4blpTtrAVn_pk7SgRiU3aly1vso5tV_0kvMOcS6Hn38mqRQ" - "PQxbdIpohi8C7FFabluZqGoiji8ZTM3v-2ib2vrBFj1YvoyPG1HXJsABINzo0xOkrMFNfN_oQrCSM" - "Ij49N86GXmYOnu5jtZeSMXZIR2BAXnu0upXMsvtSjU8D-LJJChy0XNYoyuJar5P3YhDStdTfmn0z-" - "XLwaIHWc1L9-rmW9CZey3HxCLKEnr7-FjXsXqzAArsFqn1X_sVR5HRHng5ioc7sUaRoC1S_k0XPVC" - "qCjZvkbRry2cp2313DNwjl8SK-iZA0fVUZVPM7_eZfpEgB3bBTyamtAaqQeES6lcVEtpg176Mlh64" - "3JCAjroJPP4eqAA3JHnDgwlO-XhlLPTNNQ5FMLBC_dp41A-H3HFlbQUR6jX3k_H4Ggqtit50EIye3" - "nnKb3emFn9KVyeZCYaBecYbicEIMKW7sWLbcE_cDGqkHZcMGTOQKRiLp-xwyEu89oDGAcGBYpmC_f" - "iQ2qyFfe6tQK_5nPZbtW2mudiYZ-d0YIURSTp58S_n6w3wLDUEcuZtv-nhCaFVy8oUbAztkBIK6pu" - "VamKhHVLkCtOGIdNJYbLKAedhK1lQVPbrvfcSDPPkhxSx9AjKqhKA3ZPMA_UXQb6p9c33tgi_MdZX" - "-jRGXwGKWBCrv4UjttFLV-a5U7NgxQIIjwfAoutXtYardFw2d5nTJRqBrw06PSqaLzQi616_b-U0g" - "6bWxrFObIWrKODkGfQcXPXIQxW_4Vh6gR2GaHSi_A_5SGH0zsBtYxisbKXLK2HiZJOXBew4-am6c0" - "R1jBh7QtOWpwrYWt0d_xxrWtKezeEp3FkrFkwWCgY9dT1uV8tKUuxeeGqshkrXifT4axttpkbi-qA" - "eG_C6J-H29CPqScclD-A5LIg7k-KmA9hsWrXttAvoCSawNj1tv9JHq0jgP1yZytDW1DkWdCBY0au5" - "4." - "qqYQEaGx-lUHoO43fOXvKQ" -; - - -static int -test_jwe_r256a128_jwe_openssl(struct lws_context *context) -{ - struct lws_jwe jwe; - char temp[2048]; - int n, ret = -1, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, (char *)lws_jwe_ex_a2_jwk_json, - strlen((char *)lws_jwe_ex_a2_jwk_json)) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - /* converts a compact serialization to jws b64 + decoded maps */ - if (lws_jws_compact_decode((const char *)jwe_compact_rsa_cbc_openssl, - strlen((char *)jwe_compact_rsa_cbc_openssl), - &jwe.jws.map, &jwe.jws.map_b64, - temp, &temp_len) != 5) { - lwsl_err("%s: lws_jws_compact_decode failed\n", __func__); - goto bail; - } - - n = lws_jwe_auth_and_decrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (jwe.jws.map.len[LJWE_CTXT] < sizeof(ra_ptext_1024) || - lws_timingsafe_bcmp(jwe.jws.map.buf[LJWE_CTXT], ra_ptext_1024, - sizeof(ra_ptext_1024))) { - lwsl_err("%s: plaintext RSA/AES decrypt wrong\n", __func__); - lwsl_hexdump_notice(ra_ptext_1024, sizeof(ra_ptext_1024)); - lwsl_hexdump_notice(jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - if (ret) - lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - - -/* This is a compact JWE containing the plaintext ra_ptext_1024 for the key - * lws_jwe_ex_a2_jwk_json... produced by test test above running on mbedTLS. - */ - -static char -*jwe_compact_rsa_cbc_mbedtls = - "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.Ptg-RZjEhY1dWg7T" - "v72rqRoHXBeory9ePCC5cfASTz8BH7IdSLQcPeBWHQsmd13TXPjo-dxnmkWkx9AyKdvy" - "K0A6oCnsP7KfbGzPwTy5eadpaaDZ3UyXIGmusLgiXGgDZ4d13voirbRQV9nQTPsagG_k" - "FGRI5dKzenG2WcbUhKG-uCmypzYqjNM3LqUzdH42jjjHFOBkmK_sWSZL7Uxqq8s08hkO" - "aXeUQyeM7Z8wm1bsZAvIfGri5LMcBS8P70AyENchlchZpTeACIOWk19ddLPUt-ruEcm0" - "zZV7Gjap7uG06a0m3VyR3vMpKkXnBHQxko-RICU2PDmELVXg0pZWTw.-VaaDaUiynH_t" - "sh2HqKISQ.vVE8j1TQinb4anJk0ArV9ccYdlUIO20vnMa7b-JGfQ7CFi_WVt6xNaqytB" - "QqiTHLtAxBDIV4m9Kwid-8Kcx7BmRqu-memxHztBSvBNOYWVPTxy5I2ORGLNEbPVrFYp" - "c2nm3TnHfr-_2fuw6_HEnyqv_c6ZyzU0-lHZ1pE5Cs-lrjnj4ibNcK6XHhrO3nxUjPMZ" - "rO-2B_tZwCxzKsFAqD_XGROvNGWXEgxgIr09MyuwKJnw2oZ0xOF_d3FVYjK5XMONgWPo" - "lyDmbP_XLSIUXbHmLxpDB5NPLN8SKRHbMV3_qw5rFRlak2C_XlR58P-Im1PQ8gMg7lgE" - "IFz2DrqF4sJA5TYbQG5KCdas0SfONlP1V692dufH5D30RGsiWNSrhiyDmUNC0SeB8VqA" - "bmc02pPGgzZHxa5-_xIHKm4h6fmnZFScjliBQ5W6smxQ6m2Kby0MkOdqlRYFn8qLYLmF" - "vmVNe_Q5-iLNobx-hyyeeExRXfzNOY0HHEKw67ipBWwqA0JGIggCWAFa0fpA-Wt7sNl_" - "gPy96nbwuXIuRoC3wuboUlDp9k2F1vC7VY6R9jdRk1VXT_O3liBIiUIRhZiqZZ75H2RV" - "pLYXGrvL5G9THdRcbsg3XUt-kF4vvGQAdNmPdRmuIG1DfGDmOZnXfrG8ckTvxoKBXdQZ" - "gfwfAQFgeHjltiWZTCSBV4464sn2qLZ1MP3Ku9bOjb72RCpIF60Cqssb8gTQyXQf48ZR" - "OBd242Q7Ae6PePmb_TcnG3jOguNUgmhj8iTU7QUz0uJWpJjMRPJ8vK8SnYEguGHer4qT" - "EocdMzRTTZB-Pr4-Ey0Hm0zeiFvjU0Qy6crjna6SKrgms4VAJT9LiicTYFPsmFBFQ0L1" - "BVDiZ3NTBIv_ajvzRpBNZ0IxEH5t6W3OY0223xUF3cq8c9HhwIxMf9a2-PmZ3mVWIRnU" - "nGegoVkzd2l6el8aw57v5KKYas4-EkovHntCZZ_hkZ1uHtezKq0EvjnT5xGWjPFjOZnh" - "veiozAsaMSSyTny6mcI-hjvcgd--7qlqWpt_BEkp9XVkP2k7eHLM9v4rL6hhk_n6yK3w" - "qKi0xDboxU5xjuBiGKb-E8um1MUEjuLqZanKSBsgU-Vwvw0gx1r-MG6BSlrgUlT2if5k" - "-Wfs6iVdpK7x1zZSsetp3NEjT4DUrfmp_E_CTXhOEP0AgzpQ4Ukx5bFN3gm5gyBZw1E8" - "q20Hs01OBcMJ9wenLEQVMvO_IEIkRNBMWEgoZ148As14LNOgdh1UBrF6W4pAUjYvA3WG" - "Zp7uG9ooDB1RF2aaeBqoLJflqIegsvsfaNNBDJ-U6i_jLG1FSlttEhJVdXll0gMSYlXD" - "O3BBil4eiUPfiksfOmsbwoIxc-3yPTivU3DPM.O_IaktJRbdV66zfhD0LQmw" -; - -static int -test_jwe_r256a128_jwe_mbedtls(struct lws_context *context) -{ - struct lws_jwe jwe; - char temp[2048]; - int n, ret = -1, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, (char *)lws_jwe_ex_a2_jwk_json, - strlen((char *)lws_jwe_ex_a2_jwk_json)) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - /* converts a compact serialization to jws b64 + decoded maps */ - if (lws_jws_compact_decode((const char *)jwe_compact_rsa_cbc_mbedtls, - strlen((char *)jwe_compact_rsa_cbc_mbedtls), - &jwe.jws.map, &jwe.jws.map_b64, - temp, &temp_len) != 5) { - lwsl_err("%s: lws_jws_compact_decode failed\n", __func__); - goto bail; - } - - n = lws_jwe_auth_and_decrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (jwe.jws.map.len[LJWE_CTXT] < sizeof(ra_ptext_1024) || - lws_timingsafe_bcmp(jwe.jws.map.buf[LJWE_CTXT], ra_ptext_1024, - sizeof(ra_ptext_1024))) { - lwsl_err("%s: plaintext RSA/AES decrypt wrong\n", __func__); - lwsl_hexdump_notice(ra_ptext_1024, sizeof(ra_ptext_1024)); - lwsl_hexdump_notice(jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - - if (ret) - lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - - - -/* A.3. Example JWE Using AES Key Wrap and AES_128_CBC_HMAC_SHA_256 - * - * This example encrypts the plaintext "Live long and prosper." to the - * recipient using AES Key Wrap for key encryption and - * AES_128_CBC_HMAC_SHA_256 for content encryption. - */ - -/* "Live long and prosper." */ -static uint8_t - -ex_a3_ptext[] = { - 76, 105, 118, 101, 32, 108, 111, 110, - 103, 32, 97, 110, 100, 32, 112, 114, - 111, 115, 112, 101, 114, 46 -}, - -*ex_a3_compact = (uint8_t *) - "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0" - "." - "6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ" - "." - "AxY8DCtDaGlsbGljb3RoZQ" - "." - "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY" - "." - "U0m_YmjN04DJvceFICbCVQ", - -*ex_a3_key = (uint8_t *) - "{\"kty\":\"oct\"," - "\"k\":\"GawgguFyGrWKav7AX4VKUg\"" - "}" -; - -static int -test_jwe_a3(struct lws_context *context) -{ - struct lws_jwe jwe; - char temp[2048]; - int n, ret = -1, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, (char *)ex_a3_key, - strlen((char *)ex_a3_key)) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - /* converts a compact serialization to jws b64 + decoded maps */ - if (lws_jws_compact_decode((const char *)ex_a3_compact, - strlen((char *)ex_a3_compact), - &jwe.jws.map, &jwe.jws.map_b64, temp, - &temp_len) != 5) { - lwsl_err("%s: lws_jws_compact_decode failed\n", __func__); - goto bail; - } - - n = lws_jwe_auth_and_decrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (jwe.jws.map.len[LJWE_CTXT] < sizeof(ex_a3_ptext) || - lws_timingsafe_bcmp(jwe.jws.map.buf[LJWE_CTXT], ex_a3_ptext, - sizeof(ex_a3_ptext))) { - lwsl_err("%s: plaintext AES decrypt wrong\n", __func__); - lwsl_hexdump_notice(ex_a3_ptext, sizeof(ex_a3_ptext)); - lwsl_hexdump_notice(jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - if (ret) - lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - -/* JWA B.2. Test Cases for AES_192_CBC_HMAC_SHA_384 - * - * Unfortunately JWA just gives this test case as hex literals, not - * inside a JWE. So we have to prepare the inputs "by hand". - */ - -static uint8_t - -jwa_b2_ptext[] = { - 0x41, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, - 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, - 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, - 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, - 0x62, 0x65, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, - 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, - 0x65, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, - 0x6f, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x20, 0x69, - 0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x68, 0x61, 0x6e, 0x64, 0x73, 0x20, 0x6f, 0x66, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x65, - 0x6d, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, - 0x75, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65 -}, - -jwa_b2_rawkey[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, -}, - -jwa_b2_iv[] = { - 0x1a, 0xf3, 0x8c, 0x2d, 0xc2, 0xb9, 0x6f, 0xfd, - 0xd8, 0x66, 0x94, 0x09, 0x23, 0x41, 0xbc, 0x04 -}, - -jwa_b2_e[] = { - 0xea, 0x65, 0xda, 0x6b, 0x59, 0xe6, 0x1e, 0xdb, - 0x41, 0x9b, 0xe6, 0x2d, 0x19, 0x71, 0x2a, 0xe5, - 0xd3, 0x03, 0xee, 0xb5, 0x00, 0x52, 0xd0, 0xdf, - 0xd6, 0x69, 0x7f, 0x77, 0x22, 0x4c, 0x8e, 0xdb, - 0x00, 0x0d, 0x27, 0x9b, 0xdc, 0x14, 0xc1, 0x07, - 0x26, 0x54, 0xbd, 0x30, 0x94, 0x42, 0x30, 0xc6, - 0x57, 0xbe, 0xd4, 0xca, 0x0c, 0x9f, 0x4a, 0x84, - 0x66, 0xf2, 0x2b, 0x22, 0x6d, 0x17, 0x46, 0x21, - 0x4b, 0xf8, 0xcf, 0xc2, 0x40, 0x0a, 0xdd, 0x9f, - 0x51, 0x26, 0xe4, 0x79, 0x66, 0x3f, 0xc9, 0x0b, - 0x3b, 0xed, 0x78, 0x7a, 0x2f, 0x0f, 0xfc, 0xbf, - 0x39, 0x04, 0xbe, 0x2a, 0x64, 0x1d, 0x5c, 0x21, - 0x05, 0xbf, 0xe5, 0x91, 0xba, 0xe2, 0x3b, 0x1d, - 0x74, 0x49, 0xe5, 0x32, 0xee, 0xf6, 0x0a, 0x9a, - 0xc8, 0xbb, 0x6c, 0x6b, 0x01, 0xd3, 0x5d, 0x49, - 0x78, 0x7b, 0xcd, 0x57, 0xef, 0x48, 0x49, 0x27, - 0xf2, 0x80, 0xad, 0xc9, 0x1a, 0xc0, 0xc4, 0xe7, - 0x9c, 0x7b, 0x11, 0xef, 0xc6, 0x00, 0x54, 0xe3 -}, - -jwa_b2_a[] = { /* "The second principle of Auguste Kerckhoffs" */ - 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63, - 0x69, 0x70, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, - 0x41, 0x75, 0x67, 0x75, 0x73, 0x74, 0x65, 0x20, - 0x4b, 0x65, 0x72, 0x63, 0x6b, 0x68, 0x6f, 0x66, - 0x66, 0x73 -}, - -jwa_b2_tag[] = { - 0x84, 0x90, 0xac, 0x0e, 0x58, 0x94, 0x9b, 0xfe, - 0x51, 0x87, 0x5d, 0x73, 0x3f, 0x93, 0xac, 0x20, - 0x75, 0x16, 0x80, 0x39, 0xcc, 0xc7, 0x33, 0xd7 - -} -; - -static int -test_jwa_b2(struct lws_context *context) -{ - struct lws_jwe jwe; - int n, ret = -1; - char buf[2048]; - - lws_jwe_init(&jwe, context); - - /* - * normally all this is interpreted from the JWE blob. But we don't - * have JWE test vectors for AES_256_CBC_HMAC_SHA_512, just a standalone - * one. So we have to create it all by hand. - * - * See test_jwe_a3 above for a more normal usage pattern. - */ - - lws_jwk_dup_oct(&jwe.jwk, jwa_b2_rawkey, sizeof(jwa_b2_rawkey)); - - memcpy(buf, jwa_b2_e, sizeof(jwa_b2_e)); - - jwe.jws.map.buf[LJWE_IV] = (char *)jwa_b2_iv; - jwe.jws.map.len[LJWE_IV] = sizeof(jwa_b2_iv); - - jwe.jws.map.buf[LJWE_CTXT] = buf; - jwe.jws.map.len[LJWE_CTXT] = sizeof(jwa_b2_e); - - jwe.jws.map.buf[LJWE_ATAG] = (char *)jwa_b2_tag; - jwe.jws.map.len[LJWE_ATAG] = sizeof(jwa_b2_tag); - - /* - * Normally this comes from the JOSE header. But this test vector - * doesn't have one... so... - */ - - if (lws_gencrypto_jwe_alg_to_definition("A128KW", &jwe.jose.alg)) - goto bail; - if (lws_gencrypto_jwe_enc_to_definition("A192CBC-HS384", - &jwe.jose.enc_alg)) - goto bail; - - n = lws_jwe_auth_and_decrypt_cbc_hs(&jwe, jwa_b2_rawkey, - jwa_b2_a, sizeof(jwa_b2_a)); - if (n < 0) { - lwsl_err("%s: lws_jwe_a_cbc_hs_decrypt failed\n", __func__); - - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (jwe.jws.map.len[LJWE_CTXT] < sizeof(jwa_b2_ptext) || - lws_timingsafe_bcmp(jwe.jws.map.buf[LJWE_CTXT],jwa_b2_ptext, - sizeof(jwa_b2_ptext))) { - lwsl_err("%s: plaintext AES decrypt wrong\n", __func__); - lwsl_hexdump_notice(jwa_b2_ptext, sizeof(jwa_b2_ptext)); - lwsl_hexdump_notice(jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - if (ret) - lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - - - -/* JWA B.3. Test Cases for AES_256_CBC_HMAC_SHA_512 - * - * Unfortunately JWA just gives this test case as hex literals, not - * inside a JWE. So we have to prepare the inputs "by hand". - */ - -static uint8_t - -jwa_b3_ptext[] = { - 0x41, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, - 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, - 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, - 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, - 0x62, 0x65, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, - 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, - 0x65, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, - 0x6f, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x20, 0x69, - 0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x68, 0x61, 0x6e, 0x64, 0x73, 0x20, 0x6f, 0x66, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x65, - 0x6d, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, - 0x75, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65 -}, - - -jwa_b3_rawkey[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f -}, - -jwa_b3_iv[] = { - 0x1a, 0xf3, 0x8c, 0x2d, 0xc2, 0xb9, 0x6f, 0xfd, - 0xd8, 0x66, 0x94, 0x09, 0x23, 0x41, 0xbc, 0x04 -}, - -jwa_b3_e[] = { - 0x4a, 0xff, 0xaa, 0xad, 0xb7, 0x8c, 0x31, 0xc5, - 0xda, 0x4b, 0x1b, 0x59, 0x0d, 0x10, 0xff, 0xbd, - 0x3d, 0xd8, 0xd5, 0xd3, 0x02, 0x42, 0x35, 0x26, - 0x91, 0x2d, 0xa0, 0x37, 0xec, 0xbc, 0xc7, 0xbd, - 0x82, 0x2c, 0x30, 0x1d, 0xd6, 0x7c, 0x37, 0x3b, - 0xcc, 0xb5, 0x84, 0xad, 0x3e, 0x92, 0x79, 0xc2, - 0xe6, 0xd1, 0x2a, 0x13, 0x74, 0xb7, 0x7f, 0x07, - 0x75, 0x53, 0xdf, 0x82, 0x94, 0x10, 0x44, 0x6b, - 0x36, 0xeb, 0xd9, 0x70, 0x66, 0x29, 0x6a, 0xe6, - 0x42, 0x7e, 0xa7, 0x5c, 0x2e, 0x08, 0x46, 0xa1, - 0x1a, 0x09, 0xcc, 0xf5, 0x37, 0x0d, 0xc8, 0x0b, - 0xfe, 0xcb, 0xad, 0x28, 0xc7, 0x3f, 0x09, 0xb3, - 0xa3, 0xb7, 0x5e, 0x66, 0x2a, 0x25, 0x94, 0x41, - 0x0a, 0xe4, 0x96, 0xb2, 0xe2, 0xe6, 0x60, 0x9e, - 0x31, 0xe6, 0xe0, 0x2c, 0xc8, 0x37, 0xf0, 0x53, - 0xd2, 0x1f, 0x37, 0xff, 0x4f, 0x51, 0x95, 0x0b, - 0xbe, 0x26, 0x38, 0xd0, 0x9d, 0xd7, 0xa4, 0x93, - 0x09, 0x30, 0x80, 0x6d, 0x07, 0x03, 0xb1, 0xf6, -}, - -jwa_b3_a[] = { /* "The second principle of Auguste Kerckhoffs" */ - 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63, - 0x69, 0x70, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, - 0x41, 0x75, 0x67, 0x75, 0x73, 0x74, 0x65, 0x20, - 0x4b, 0x65, 0x72, 0x63, 0x6b, 0x68, 0x6f, 0x66, - 0x66, 0x73 -}, - -jws_b3_tag[] = { - 0x4d, 0xd3, 0xb4, 0xc0, 0x88, 0xa7, 0xf4, 0x5c, - 0x21, 0x68, 0x39, 0x64, 0x5b, 0x20, 0x12, 0xbf, - 0x2e, 0x62, 0x69, 0xa8, 0xc5, 0x6a, 0x81, 0x6d, - 0xbc, 0x1b, 0x26, 0x77, 0x61, 0x95, 0x5b, 0xc5 -} -; - -static int -test_jwa_b3(struct lws_context *context) -{ - struct lws_jwe jwe; - char buf[2048]; - int n, ret = -1; - - lws_jwe_init(&jwe, context); - - /* - * normally all this is interpreted from the JWE blob. But we don't - * have JWE test vectors for AES_256_CBC_HMAC_SHA_512, just a standalone - * one. So we have to create it all by hand. - * - * See test_jwe_a3 above for a more normal usage pattern. - */ - - lws_jwk_dup_oct(&jwe.jwk, jwa_b3_rawkey, sizeof(jwa_b3_rawkey)); - - memcpy(buf, jwa_b3_e, sizeof(jwa_b3_e)); - - jwe.jws.map.buf[LJWE_IV] = (char *)jwa_b3_iv; - jwe.jws.map.len[LJWE_IV] = sizeof(jwa_b3_iv); - - jwe.jws.map.buf[LJWE_CTXT] = buf; - jwe.jws.map.len[LJWE_CTXT] = sizeof(jwa_b3_e); - - jwe.jws.map.buf[LJWE_ATAG] = (char *)jws_b3_tag; - jwe.jws.map.len[LJWE_ATAG] = sizeof(jws_b3_tag); - - /* - * Normally this comes from the JOSE header. But this test vector - * doesn't feature one... - */ - - if (lws_gencrypto_jwe_alg_to_definition("A128KW", &jwe.jose.alg)) - goto bail; - if (lws_gencrypto_jwe_enc_to_definition("A256CBC-HS512", - &jwe.jose.enc_alg)) - goto bail; - - n = lws_jwe_auth_and_decrypt_cbc_hs(&jwe, jwa_b3_rawkey, - jwa_b3_a, sizeof(jwa_b3_a)); - if (n < 0) { - lwsl_err("%s: lws_jwe_a_cbc_hs_decrypt failed\n", __func__); - - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (jwe.jws.map.len[LJWE_CTXT] < sizeof(jwa_b3_ptext) || - lws_timingsafe_bcmp(jwe.jws.map.buf[LJWE_CTXT],jwa_b3_ptext, - sizeof(jwa_b3_ptext))) { - lwsl_err("%s: plaintext AES decrypt wrong\n", __func__); - lwsl_hexdump_notice(jwa_b3_ptext, sizeof(jwa_b3_ptext)); - lwsl_hexdump_notice(jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - - if (ret) - lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - -/* JWA C. Example ECDH-ES Key Agreement Computation - * - * This example uses ECDH-ES Key Agreement and the Concat KDF to derive - * the CEK in the manner described in Section 4.6. In this example, the - * ECDH-ES Direct Key Agreement mode ("alg" value "ECDH-ES") is used to - * produce an agreed-upon key for AES GCM with a 128-bit key ("enc" - * value "A128GCM"). - * - * In this example, a producer Alice is encrypting content to a consumer - * Bob. The producer (Alice) generates an ephemeral key for the key - * agreement computation. - * - * JWA Appendix C where this comes from ONLY goes as far as to confirm the - * direct derived key, it doesn't do any AES128-GCM. - */ - -static const char - -*ex_jwa_c_jose = - "{\"alg\":\"ECDH-ES\"," - "\"enc\":\"A128GCM\"," - "\"apu\":\"QWxpY2U\"," /* b64u("Alice") */ - "\"apv\":\"Qm9i\"," /* b64u("Bob") */ - "\"epk\":" /* public part of A's ephemeral key */ - "{\"kty\":\"EC\"," - "\"crv\":\"P-256\"," - "\"x\":\"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0\"," - "\"y\":\"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps\"" - "}" - "}" -; - -static uint8_t -ex_jwa_c_z[] = { - 158, 86, 217, 29, 129, 113, 53, 211, - 114, 131, 66, 131, 191, 132, 38, 156, - 251, 49, 110, 163, 218, 128, 106, 72, - 246, 218, 167, 121, 140, 254, 144, 196 -}, -ex_jwa_c_derived_key[] = { - 86, 170, 141, 234, 248, 35, 109, 32, - 92, 34, 40, 205, 113, 167, 16, 26 -}; - - -static int -test_jwa_c(struct lws_context *context) -{ - struct lws_jwe jwe; - char temp[2048], *p; - int ret = -1, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - /* - * again the JWA Appendix C test vectors are not in the form of a - * complete JWE, but just the JWE JOSE header, so we must fake up the - * pieces and perform just the (normally internal) key agreement step - * for this test. - * - * See test_jwe_a3 above for a more normal usage pattern. - */ - - if (lws_jwe_parse_jose(&jwe.jose, ex_jwa_c_jose, strlen(ex_jwa_c_jose), - temp, &temp_len) < 0) { - lwsl_err("%s: JOSE parse failed\n", __func__); - - goto bail; - } - - /* - * The ephemeral key has been parsed into a jwk "jwe.jose.jwk_ephemeral" - * - * In this example, the ECDH-ES Direct Key Agreement mode ("alg" value - * "ECDH-ES") is used to produce an agreed-upon key for AES GCM with a - * 128-bit key ("enc" value "A128GCM"). - */ - - p = lws_concat_temp(temp, temp_len); - - if (lws_jwa_concat_kdf(&jwe, 1, (uint8_t *)p, - ex_jwa_c_z, sizeof(ex_jwa_c_z))) { - lwsl_err("%s: lws_jwa_concat_kdf failed\n", __func__); - - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (lws_timingsafe_bcmp(p, ex_jwa_c_derived_key, - sizeof(ex_jwa_c_derived_key))) { - lwsl_err("%s: ECDH-ES direct derived key wrong\n", __func__); - lwsl_hexdump_notice(ex_jwa_c_derived_key, - sizeof(ex_jwa_c_derived_key)); - lwsl_hexdump_notice(p, sizeof(ex_jwa_c_derived_key)); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - - if (ret) - lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - - -/* - * ECDH-ES Homebrew Encryption test - */ - -static const char - - /* peer key */ - -*ecdhes_t1_peer_p256_public_key = /* as below but with d removed */ - "{" - "\"crv\":\"P-256\"," - "\"kty\":\"EC\"," - "\"x\":\"ySlIGttmXG80WPjDO01QaXg7oAzW3NE-a-GF0NDGk_E\"," - "\"y\":\"i08k5z4ppqgtnLK8lh5qw4qp2FhxPdGjovgilajluuw\"" - "}", - -*ecdhes_t1_peer_p256_private_key = /* created by ./lws-crypto-jwk -t EC */ - "{" - "\"crv\":\"P-256\"," - "\"d\":\"ldszv0_cGFMkjxaPspGCP6X0NAaVCVeK48oH4RzT2T0\"," - "\"kty\":\"EC\"," - "\"x\":\"ySlIGttmXG80WPjDO01QaXg7oAzW3NE-a-GF0NDGk_E\"," - "\"y\":\"i08k5z4ppqgtnLK8lh5qw4qp2FhxPdGjovgilajluuw\"" - "}", - -*ecdhes_t1_peer_p384_public_key = /* as below but with d removed */ - "{\"crv\":\"P-384\"," - "\"kty\":\"EC\"," - "\"x\":\"injKcygDoG1AuP044ct88r_2DNinHr1CGqy4q2Sy5yo034Y" - "7yQ5_NT-lEUXrzlIW\"," - "\"y\":\"y52QaJLhVm-ts8xa1jL8GkmwGm_dX6xV1PSq4s3pbwx2Hu9" - "X29z5WYcTPFOCPtwJ\"}", - -*ecdhes_t1_peer_p384_private_key = /* created by ./lws-crypto-jwk -t EC -v "P-384" */ - "{\"crv\":\"P-384\"," - "\"d\":\"jYGze6ZwZxrflVx_I2lYWNf9GkfbeQNRwQCdtZhBlb85lk-" - "SAvaZuNiRUs_eWmPQ\"," - "\"kty\":\"EC\"," - "\"x\":\"injKcygDoG1AuP044ct88r_2DNinHr1CGqy4q2Sy5yo034Y" - "7yQ5_NT-lEUXrzlIW\"," - "\"y\":\"y52QaJLhVm-ts8xa1jL8GkmwGm_dX6xV1PSq4s3pbwx2Hu9" - "X29z5WYcTPFOCPtwJ\"}", - - *ecdhes_t1_peer_p521_public_key = /* as below but with d removed */ - "{\"crv\":\"P-521\"," - "\"kty\":\"EC\"," - "\"x\":\"AYe0gAkPzzjeQW5Ek9tVrWdfi0u6k7LVUru-b2x7V9EM3d" - "L4SbQiS1p2j2gmZ2a6aDoKDRU_2E4u9EQrlswlty-g\"," - "\"y\":\"AEAIIRkVL0WhtDlDSM7dciBtL1dOo5UPiW7ixIOv5K75Mo" - "uFNWO7cFmcxaCOn9459ex0giVyptmX_956C_DWabG6\"}", - -*ecdhes_t1_peer_p521_private_key = /* created by ./lws-crypto-jwk -t EC -v "P-521" */ - "{\"crv\":\"P-521\"," - "\"d\":\"AUer7_-qJtQtDWN6CMeGB20rzTa648kpsfidTOu3lnn6__" - "yOXkMj1yTYUBjVOnUjGHiTU1rCGsw4CyF-1nDRe7SM\"," - "\"kty\":\"EC\"," - "\"x\":\"AYe0gAkPzzjeQW5Ek9tVrWdfi0u6k7LVUru-b2x7V9EM3d" - "L4SbQiS1p2j2gmZ2a6aDoKDRU_2E4u9EQrlswlty-g\"," - "\"y\":\"AEAIIRkVL0WhtDlDSM7dciBtL1dOo5UPiW7ixIOv5K75Mo" - "uFNWO7cFmcxaCOn9459ex0giVyptmX_956C_DWabG6\"}", - -*ecdhes_t1_jose_hdr_es_128 = - "{\"alg\":\"ECDH-ES\",\"enc\":\"A128CBC-HS256\"}", - -*ecdhes_t1_jose_hdr_es_192 = - "{\"alg\":\"ECDH-ES\",\"enc\":\"A192CBC-HS384\"}", - -*ecdhes_t1_jose_hdr_es_256 = - "{\"alg\":\"ECDH-ES\",\"enc\":\"A256CBC-HS512\"}", - -*ecdhes_t1_jose_hdr_esakw128_128 = - "{\"alg\":\"ECDH-ES+A128KW\",\"enc\":\"A128CBC-HS256\"}", - -*ecdhes_t1_jose_hdr_esakw192_192 = - "{\"alg\":\"ECDH-ES+A192KW\",\"enc\":\"A192CBC-HS384\"}", - -*ecdhes_t1_jose_hdr_esakw256_256 = - "{\"alg\":\"ECDH-ES+A256KW\",\"enc\":\"A256CBC-HS512\"}", - -*ecdhes_t1_plaintext = - "This test plaintext is exactly 64 bytes long when unencrypted..." -; - -static int -test_ecdhes_t1(struct lws_context *context, const char *jose_hdr, - const char *peer_pubkey, const char *peer_privkey) -{ - char temp[3072], compact[2048]; - int n, ret = -1, temp_len = sizeof(temp); - struct lws_jwe jwe; - - lws_jwe_init(&jwe, context); - - /* read and interpret our canned JOSE header, setting the algorithm */ - - if (lws_jws_dup_element(&jwe.jws.map, LJWS_JOSE, - lws_concat_temp(temp, temp_len), &temp_len, - jose_hdr, strlen(jose_hdr), 0)) - goto bail; - - if (lws_jwe_parse_jose(&jwe.jose, jose_hdr, strlen(jose_hdr), - temp, &temp_len) < 0) { - lwsl_err("%s: JOSE parse failed\n", __func__); - - goto bail; - } - - /* for ecdh-es encryption, we need the peer's pubkey */ - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, (char *)peer_pubkey, - strlen((char *)peer_pubkey)) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - /* - * dup the plaintext into the ciphertext element, it will be - * encrypted in-place to a ciphertext of the same length + padding - */ - - if (lws_jws_dup_element(&jwe.jws.map, LJWE_CTXT, - lws_concat_temp(temp, temp_len), &temp_len, - ecdhes_t1_plaintext, - strlen(ecdhes_t1_plaintext), - lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN, - strlen(ecdhes_t1_plaintext)))) { - lwsl_notice("%s: Not enough temp space for ptext\n", __func__); - goto bail; - } - - /* - * perform the actual encryption - */ - - n = lws_jwe_encrypt(&jwe, lws_concat_temp(temp, temp_len), &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_encrypt failed\n", __func__); - goto bail; - } - - /* - * format for output - */ - - n = lws_jwe_render_flattened(&jwe, compact, sizeof(compact)); - if (n < 0) { - lwsl_err("%s: lws_jwe_render_compact failed: %d\n", - __func__, n); - goto bail; - } - - // puts(compact); - - n = lws_jwe_render_compact(&jwe, compact, sizeof(compact)); - if (n < 0) { - lwsl_err("%s: lws_jwe_render_compact failed: %d\n", - __func__, n); - goto bail; - } - - // puts(compact); - - /* okay, let's try to decrypt the whole thing, as the recipient - * getting the compact. jws->jwk needs to be our private key. */ - - lws_jwe_destroy(&jwe); - temp_len = sizeof(temp); - lws_jwe_init(&jwe, context); - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, (char *)peer_privkey, - strlen((char *)peer_privkey)) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - /* converts a compact serialization to jws b64 + decoded maps */ - if (lws_jws_compact_decode(compact, strlen(compact), &jwe.jws.map, - &jwe.jws.map_b64, temp, &temp_len) != 5) { - lwsl_err("%s: lws_jws_compact_decode failed\n", __func__); - goto bail; - } - - n = lws_jwe_auth_and_decrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - if (ret) - lwsl_err("%s: %s selftest failed +++++++++++++++++++\n", - __func__, jose_hdr); - else - lwsl_notice("%s: %s selftest OK\n", __func__, jose_hdr); - - return ret; -} - -/* AES Key Wrap and AES_XXX_CBC_HMAC_SHA_YYY variations - * - * These were created using the node-jose node.js package - */ -static const char - *akw_ptext = "plaintext0123456", - *akw_ct_128_128 = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Ii" - "wia2lkIjoiLTRXTEpQNWNrYUxBUFFFNXkwYXhLT0JUSTlFTngxUXBCa0toNkdOY2loOC" - "J9.h6oNSEgz3LwIMndEkPEa8H7_5zy0hh8TaU_1yWoNtu4Dh_WJpEgx9g.j7TYjj8wB0" - "RS6rclTWYmqw.zm3tPzuWhXoD7IsAWbA0xz-AJXvE9gydWPRBTaO40sQ.Okf7ttWDLPM" - "wIj1kUyUO_A", - *akw_ct_128_192 = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTkyQ0JDLUhTMzg0Ii" - "wia2lkIjoiLTRXTEpQNWNrYUxBUFFFNXkwYXhLT0JUSTlFTngxUXBCa0toNkdOY2loOC" - "J9.XkRTu4nP3b0KZxXjkjdHEnbf6AWZUmFvpsqZLuLxKcrONqDUsnYasnVuo6U0QKRUm" - "cyBRtSPGW4.MzNxxoOp8JR2AHoLNve-vw.rdxgo6InRAxk3afG02_75l58u5m6KYHd3h" - "LH16ksnZE.v7BLKaRZIwhUPhhBRTd8yPwH0xa1fOft", - *akw_ct_128_256 = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIi" - "wia2lkIjoiLTRXTEpQNWNrYUxBUFFFNXkwYXhLT0JUSTlFTngxUXBCa0toNkdOY2loOC" - "J9.mueR-8XzXs2RyvzzvghpIpGS1mGl7vkSjJDF5zqhH8-ektBpCXSd7R7MS5nh2-Xf_" - "8XDym1gn1QEQh5bDI3GPESnSN1TJR-h.g6plL_5L2BD8wcjZS7X79A.UTndfTFhGFaVZ" - "vWqPkV7dN00gckesd_7UylosVDqjwU.-rgi0jkYuCZDMwUVLxN6e6x8fXw2U0u4-vL8u" - "Kb__S8", - *akw_ct_192_128 = "eyJhbGciOiJBMTkyS1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Ii" - "wia2lkIjoiai10RWp2Q2JyNVlUZWtKUXlES3kyQXh5cjBWeUlUWXk4S3IycjB0cy1USS" - "J9.mEURnj2NvPa3TU0uR8mcm2cMd33Y6iYYZ_LFrYS_Gz49gcdxZpdk1Q.v3csq81X9o" - "mI-bcp6i-FTQ.EgroRqmqNfeH7XC9msLap1IGcqvc09SlnI4PO6RQqS0.hDi57mXD3vX" - "dx2r4Kwnv9w", - *akw_ct_192_192 = "eyJhbGciOiJBMTkyS1ciLCJlbmMiOiJBMTkyQ0JDLUhTMzg0Ii" - "wia2lkIjoiai10RWp2Q2JyNVlUZWtKUXlES3kyQXh5cjBWeUlUWXk4S3IycjB0cy1USS" - "J9.QHgtusQdP7Zvw9tsCZNkJyEmzg6KUaaIyTb2BXB0ng9mxSUIQ7y_6oqasYKBUJgBn" - "Koru-3CXOE.ZZXcGY35mmlAb4-IgA5XlQ.AuG2GRPeYJ80_4XoYAUgXbVY65ZQ689Grn" - "x8RCNQdfc.UjfgDr4z3PGQBdftWT2gqx1Egfd9PUR4", - *akw_ct_192_256 = "eyJhbGciOiJBMTkyS1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIi" - "wia2lkIjoiai10RWp2Q2JyNVlUZWtKUXlES3kyQXh5cjBWeUlUWXk4S3IycjB0cy1USS" - "J9.G6DziymYyU3-6unIa-Oz-0lksH05OJFDZKkFuShMuoazEMZ5ZH2S_65qD-pjpf8aN" - "2thOVOYT0mdtgFM0ARUfx8ZLhRFCcn1.yEKK4eARZIo9WtGVcQmgDQ.ovan2NXDmt_Ka" - "SsVJmhIMQqVz6meqz1oExfVcY8vdzA.R3T4lQIKX5cc2Ktv42e9u5PR--v_w2uK7F4Wp" - "Sr5SQ8", - *akw_ct_256_128 = "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Ii" - "wia2lkIjoiSDVwSzRRUU81U0tHbDA3UXhIdk9YMzVqS2FJbzA2NXVLdWRubVZFZVpJYy" - "J9.ZLWrz5CE7Iav2db37VL9ZABeaRVrV9af-7-46Loc9M2D0SPSNtsxpg.ktk-VU8-5b" - "XRvW_A6IqDjQ.xZVIglOhadDBHUYuxPx6Wr_YzOo0qCDH24xVe58qP9Q.pO_tME930wO" - "u5fNJ8ubGrw", - *akw_ct_256_192 = "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMTkyQ0JDLUhTMzg0Ii" - "wia2lkIjoiSDVwSzRRUU81U0tHbDA3UXhIdk9YMzVqS2FJbzA2NXVLdWRubVZFZVpJYy" - "J9.fcblAVZ7VOXtyhymqxDBr-zgvId18p3AURNbhH5FmAvKNuUVU37xPkz6BrFopLP0J" - "jqXaTyyg1s.fprTe2e0esH2w7EnLEgBZQ.g1BI0U1aKSM_JBEp9jC4BxBaFXVG5BW4nl" - "bhX1MDeLo.XOLanrIkitLLDRONnfM05avahl_lJ_UY", - *akw_ct_256_256 = "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIi" - "wia2lkIjoiSDVwSzRRUU81U0tHbDA3UXhIdk9YMzVqS2FJbzA2NXVLdWRubVZFZVpJYy" - "J9.SpizfgtzQLJCCnYnUmNfiMMTyL8iIDi8OyUDrO00KJtfwJdNAcs-NuYQkLKx6PlDJ" - "IGjucT4-IuA8k_Oc752kq1BzTHMZ-Mo.go-e8xpQoCmLD5RBQw7ruA.WqkEdM6T1_z5F" - "C-8eGQfGjos7cHPy1ecZk1Ep-TYgXo.bZVHhIpe2PbjguQlK_afkYDlVmEtRAe3LUJUX" - "4STOtU", - *akw_key_128 = "{\"k\":\"JjVJVh8JsXvKf9qgHHWWBA\",\"kty\":\"oct\"}", - *akw_key_192 = "{\"k\":\"BYF6urCMDRMKFXXRxXrDSVtW71AUZghj\",\"kty\":\"oct\"}", - *akw_key_256 = "{\"k\":\"cSHyZXGEfnlgKud21cM6tAxRyXnK6xbWRTsyLUegTMk\",\"kty\":\"oct\"}" -; - -static int -test_akw_decrypt(struct lws_context *context, const char *test_name, - const char *ciphertext, const char *key) -{ - struct lws_jwe jwe; - char temp[2048]; - int n, ret = -1, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, key, strlen(key)) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - /* converts a compact serialization to jws b64 + decoded maps */ - if (lws_jws_compact_decode(ciphertext, strlen(ciphertext), - &jwe.jws.map, &jwe.jws.map_b64, - temp, &temp_len) != 5) { - lwsl_err("%s: lws_jws_compact_decode failed\n", __func__); - goto bail; - } - - n = lws_jwe_auth_and_decrypt(&jwe, lws_concat_temp(temp, temp_len), &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail; - } - - /* allowing for trailing padding, confirm the plaintext */ - if (jwe.jws.map.len[LJWE_CTXT] < strlen(akw_ptext) || - lws_timingsafe_bcmp(jwe.jws.map.buf[LJWE_CTXT], akw_ptext, - strlen(akw_ptext))) { - lwsl_err("%s: plaintext AES decrypt wrong\n", __func__); - lwsl_hexdump_notice(akw_ptext, strlen(akw_ptext)); - lwsl_hexdump_notice(jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]); - goto bail; - } - - ret = 0; - -bail: - lws_jwe_destroy(&jwe); - if (ret) - lwsl_err("%s: selftest %s failed +++++++++++++++++++\n", - __func__, test_name); - else - lwsl_notice("%s: selftest %s OK\n", __func__, test_name); - - return ret; -} - -static int -test_akw_encrypt(struct lws_context *context, const char *test_name, - const char *alg, const char *enc, const char *ciphertext, - const char *key, char *compact, int compact_len) -{ - struct lws_jwe jwe; - char temp[4096]; - int ret = -1, n, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - if (lws_jwk_import(&jwe.jwk, NULL, NULL, key, strlen(key)) < 0) { - lwsl_notice("%s: Failed to decode JWK test key\n", __func__); - goto bail; - } - - if (lws_gencrypto_jwe_alg_to_definition(alg, &jwe.jose.alg)) { - lwsl_err("Unknown cipher alg %s\n", alg); - goto bail; - } - if (lws_gencrypto_jwe_enc_to_definition(enc, &jwe.jose.enc_alg)) { - lwsl_err("Unknown payload enc alg %s\n", enc); - goto bail; - } - - /* we require a JOSE-formatted header to do the encryption */ - - jwe.jws.map.buf[LJWS_JOSE] = temp; - jwe.jws.map.len[LJWS_JOSE] = lws_snprintf(temp, temp_len, - "{\"alg\":\"%s\", \"enc\":\"%s\"}", alg, enc); - temp_len -= jwe.jws.map.len[LJWS_JOSE]; - - /* - * dup the plaintext into the ciphertext element, it will be - * encrypted in-place to a ciphertext of the same length + padding - */ - - if (lws_jws_dup_element(&jwe.jws.map, LJWE_CTXT, - lws_concat_temp(temp, temp_len), &temp_len, - akw_ptext, strlen(akw_ptext), - lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN, - strlen(akw_ptext)))) { - lwsl_notice("%s: Not enough temp space for ptext\n", __func__); - goto bail; - } - - /* CEK size is determined by hash / hmac size */ - - n = lws_gencrypto_bits_to_bytes(jwe.jose.enc_alg->keybits_fixed); - if (lws_jws_randomize_element(context, &jwe.jws.map, LJWE_EKEY, - lws_concat_temp(temp, temp_len), - &temp_len, n, - LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) { - lwsl_err("Problem getting random\n"); - goto bail; - } - - n = lws_jwe_encrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_encrypt failed\n", __func__); - goto bail; - } - - n = lws_jwe_render_compact(&jwe, compact, compact_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_render_compact failed: %d\n", - __func__, n); - goto bail; - } - - ret = 0; -bail: - lws_jwe_destroy(&jwe); - if (ret) - lwsl_err("%s: selftest %s failed +++++++++++++++++++\n", - __func__, test_name); - else - lwsl_notice("%s: selftest %s OK\n", __func__, test_name); - - return ret; -} - -/* - * Check we can handle multi-recipient JWE - */ - -static char *complete = - "{" - "\"protected\":" - "\"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0\"," - "\"unprotected\":" - "{\"jku\":\"https://server.example.com/keys.jwks\"}," - "\"recipients\":[" - - "{\"header\":" - "{\"alg\":\"RSA1_5\",\"kid\":\"2011-04-29\"}," - "\"encrypted_key\":" - "\"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-" - "kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx" - "GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3" - "YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh" - "cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg" - "wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A\"}," - - "{\"header\":" - "{\"alg\":\"A128KW\",\"kid\":\"7\"}," - "\"encrypted_key\":" - "\"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ\"}]," - - "\"iv\":" - "\"AxY8DCtDaGlsbGljb3RoZQ\"," - "\"ciphertext\":" - "\"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY\"," - "\"tag\":" - "\"Mz-VPPyU4RlcuYv1IwIvzw\"" - "}\"" -; - -static int -test_jwe_json_complete(struct lws_context *context) -{ - struct lws_jwe jwe; - char temp[4096]; - int ret = -1, temp_len = sizeof(temp); - - lws_jwe_init(&jwe, context); - - if (lws_jwe_parse_jose(&jwe.jose, complete, strlen(complete), - temp, &temp_len) < 0) { - lwsl_err("%s: JOSE parse failed\n", __func__); - - goto bail; - } - - if (jwe.jose.recipients != 2) { - lwsl_err("%s: wrong recipients count %d\n", __func__, - jwe.jose.recipients); - goto bail; - } - - ret = 0; -bail: - lws_jwe_destroy(&jwe); - if (ret) - lwsl_err("%s: selftest failed +++++++++++++++++++\n", - __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - -int -test_jwe(struct lws_context *context) -{ - char compact[4096]; - int n = 0; - - n |= test_jwe_json_complete(context); - - n |= test_ecdhes_t1(context, ecdhes_t1_jose_hdr_es_128, - ecdhes_t1_peer_p256_public_key, - ecdhes_t1_peer_p256_private_key); - n |= test_ecdhes_t1(context, ecdhes_t1_jose_hdr_es_192, - ecdhes_t1_peer_p384_public_key, - ecdhes_t1_peer_p384_private_key); - n |= test_ecdhes_t1(context, ecdhes_t1_jose_hdr_es_256, - ecdhes_t1_peer_p521_public_key, - ecdhes_t1_peer_p521_private_key); - - n |= test_ecdhes_t1(context, ecdhes_t1_jose_hdr_esakw128_128, - ecdhes_t1_peer_p256_public_key, - ecdhes_t1_peer_p256_private_key); - n |= test_ecdhes_t1(context, ecdhes_t1_jose_hdr_esakw192_192, - ecdhes_t1_peer_p384_public_key, - ecdhes_t1_peer_p384_private_key); - n |= test_ecdhes_t1(context, ecdhes_t1_jose_hdr_esakw256_256, - ecdhes_t1_peer_p521_public_key, - ecdhes_t1_peer_p521_private_key); - - n |= test_jwe_a1(context); - - n |= test_jwe_a2(context); - - n |= test_jwe_ra_ptext_1024(context, (char *)lws_jwe_ex_a2_jwk_json, - strlen((char *)lws_jwe_ex_a2_jwk_json)); - n |= test_jwe_r256a192_ptext(context, (char *)lws_jwe_ex_a2_jwk_json, - strlen((char *)lws_jwe_ex_a2_jwk_json)); - n |= test_jwe_r256a256_ptext(context, (char *)lws_jwe_ex_a2_jwk_json, - strlen((char *)lws_jwe_ex_a2_jwk_json)); - n |= test_jwe_ra_ptext_1024(context, (char *)rsa_key_2048, - strlen((char *)rsa_key_2048)); - n |= test_jwe_r256a192_ptext(context, (char *)rsa_key_2048, - strlen((char *)rsa_key_2048)); - n |= test_jwe_r256a256_ptext(context, (char *)rsa_key_2048, - strlen((char *)rsa_key_2048)); - n |= test_jwe_ra_ptext_1024(context, (char *)rsa_key_4096, - strlen((char *)rsa_key_4096)); - n |= test_jwe_r256a192_ptext(context, (char *)rsa_key_4096, - strlen((char *)rsa_key_4096)); - n |= test_jwe_r256a256_ptext(context, (char *)rsa_key_4096, - strlen((char *)rsa_key_4096)); - n |= test_jwe_ra_ptext_1024(context, (char *)rsa_key_4096_no_optional, - strlen((char *)rsa_key_4096_no_optional)); - n |= test_jwe_r256a192_ptext(context, (char *)rsa_key_4096_no_optional, - strlen((char *)rsa_key_4096_no_optional)); - n |= test_jwe_r256a256_ptext(context, (char *)rsa_key_4096_no_optional, - strlen((char *)rsa_key_4096_no_optional)); - - /* AESKW decrypt all variations */ - - n |= test_akw_decrypt(context, "d-a128kw_128", akw_ct_128_128, akw_key_128); - n |= test_akw_decrypt(context, "d-a128kw_192", akw_ct_128_192, akw_key_128); - n |= test_akw_decrypt(context, "d-a128kw_256", akw_ct_128_256, akw_key_128); - n |= test_akw_decrypt(context, "d-a192kw_128", akw_ct_192_128, akw_key_192); - n |= test_akw_decrypt(context, "d-a192kw_192", akw_ct_192_192, akw_key_192); - n |= test_akw_decrypt(context, "d-a192kw_256", akw_ct_192_256, akw_key_192); - n |= test_akw_decrypt(context, "d-a256kw_128", akw_ct_256_128, akw_key_256); - n |= test_akw_decrypt(context, "d-a256kw_192", akw_ct_256_192, akw_key_256); - n |= test_akw_decrypt(context, "d-a256kw_256", akw_ct_256_256, akw_key_256); - - /* AESKW encrypt then confirm decrypt */ - - if (!test_akw_encrypt(context, "ed-128kw_128", "A128KW", "A128CBC-HS256", - akw_ptext, akw_key_128, compact, sizeof(compact))) - n |= test_akw_decrypt(context, "ed-128kw_128", compact, akw_key_128); - else - n = -1; - if (!test_akw_encrypt(context, "ed-128kw_192", "A128KW", "A192CBC-HS384", - akw_ptext, akw_key_128, compact, sizeof(compact))) - n |= test_akw_decrypt(context, "ed-128kw_192", compact, akw_key_128); - else - n = -1; - if (!test_akw_encrypt(context, "ed-128kw_256", "A128KW", "A256CBC-HS512", - akw_ptext, akw_key_128, compact, sizeof(compact))) - n |= test_akw_decrypt(context, "ed-128kw_256", compact, akw_key_128); - else - n = -1; - - if (!test_akw_encrypt(context, "ed-192kw_128", "A192KW", "A128CBC-HS256", - akw_ptext, akw_key_192, compact, sizeof(compact))) - n |= test_akw_decrypt(context, "ed-192kw_128", compact, akw_key_192); - else - n = -1; - if (!test_akw_encrypt(context, "ed-192kw_192", "A192KW", "A192CBC-HS384", - akw_ptext, akw_key_192, compact, sizeof(compact))) - n |= test_akw_decrypt(context, "ed-192kw_192", compact, akw_key_192); - else - n = -1; - if (!test_akw_encrypt(context, "ed-192kw_256", "A192KW", "A256CBC-HS512", - akw_ptext, akw_key_192, compact, sizeof(compact))) - n |= test_akw_decrypt(context, "ed-192kw_256", compact, akw_key_192); - else - n = -1; - - if (!test_akw_encrypt(context, "ed-256kw_128", "A256KW", "A128CBC-HS256", - akw_ptext, akw_key_256, compact, sizeof(compact))) - n |= test_akw_decrypt(context, "ed-256kw_128", compact, akw_key_256); - else - n = -1; - if (!test_akw_encrypt(context, "ed-256kw_192", "A256KW", "A192CBC-HS384", - akw_ptext, akw_key_256, compact, sizeof(compact))) - n |= test_akw_decrypt(context, "ed-256kw_192", compact, akw_key_256); - else - n = -1; - if (!test_akw_encrypt(context, "ed-256kw_256", "A256KW", "A256CBC-HS512", - akw_ptext, akw_key_256, compact, sizeof(compact))) - n |= test_akw_decrypt(context, "ed-256kw_256", compact, akw_key_256); - else - n = -1; - - n |= test_jwe_r256a128_jwe_openssl(context); - n |= test_jwe_r256a128_jwe_mbedtls(context); - n |= test_jwe_a3(context); - n |= test_jwa_b2(context); - n |= test_jwa_b3(context); - n |= test_jwa_c(context); - - return n; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/jwk.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/jwk.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/jwk.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/jwk.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,350 +0,0 @@ -/* - * lws-api-test-jose - RFC7517 jwk tests - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include - -static -uint8_t *lws_jwe_ex_a1_jwk_json = (uint8_t *) /* EC + RSA public keys */ - "{\"keys\":" - "[" - "{\"kty\":\"EC\"," - "\"crv\":\"P-256\"," - "\"x\":\"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\"," - "\"y\":\"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\"," - "\"use\":\"enc\"," - "\"kid\":\"1\"}," - - "{\"kty\":\"RSA\"," - "\"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx" - "4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs" - "tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2" - "QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI" - "SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb" - "w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\"," - "\"e\":\"AQAB\"," - "\"alg\":\"RS256\"," - "\"kid\":\"2011-04-29\"}" - "]" - "}", - -*lws_jwe_ex_a2_jwk_json = (uint8_t *) /* EC + RSA private keys */ - "{\"keys\":" - "[" - "{\"kty\":\"EC\"," - "\"crv\":\"P-256\"," - "\"x\":\"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\"," - "\"y\":\"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\"," - "\"d\":\"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE\"," - "\"use\":\"enc\"," - "\"kid\":\"1\"}," - - "{\"kty\":\"RSA\"," - "\"n\":\"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4" - "cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMst" - "n64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2Q" - "vzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbIS" - "D08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw" - "0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\"," - "\"e\":\"AQAB\"," - "\"d\":\"X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9" - "M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqij" - "wp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d" - "_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBz" - "nbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFz" - "me1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q\"," - "\"p\":\"83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPV" - "nwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqV" - "WlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs\"," - "\"q\":\"3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyum" - "qjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgx" - "kIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk\"," - "\"dp\":\"G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oim" - "YwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_Nmtu" - "YZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0\"," - "\"dq\":\"s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUU" - "vMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9" - "GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk\"," - "\"qi\":\"GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzg" - "UIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rx" - "yR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU\"," - "\"alg\":\"RS256\"," - "\"kid\":\"2011-04-29\"}" - "]" - "}", -*lws_jwe_ex_a3_jwk_json = (uint8_t *) /* oct symmetric keys */ - "{\"keys\":" - "[" - "{\"kty\":\"oct\"," - "\"alg\":\"A128KW\"," - "\"k\":\"GawgguFyGrWKav7AX4VKUg\"}," - - "{\"kty\":\"oct\"," - "\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75" - "aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"," - "\"kid\":\"HMAC key used in JWS spec Appendix A.1 example\"}" - "]" - "}", - -*lws_jwe_ex_b_jwk_json = (uint8_t *) /* x5c example (no parent JSON) */ - "{\"kty\":\"RSA\"," - "\"use\":\"sig\"," - "\"kid\":\"1b94c\"," - "\"n\":\"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08" - "PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Q" - "u2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4a" - "YWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwH" - "MTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMv" - "VfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ\"," - "\"e\":\"AQAB\"," - "\"x5c\":" - "[\"MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJB" - "gNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYD" - "VQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1" - "wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVaMGIxCzAJBg" - "NVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDV" - "QQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1w" - "YmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64zn8/QnH" - "YMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9if6amFtPDy2yvz3YlRij66" - "s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u3WG7K+IiZhtELto/A7Fck9Ws6" - "SQvzRvOE8uSirYbgmj6He4iO8NCyvaK0jIQRMMGQwsU1quGmFgHIXPLfnpn" - "fajr1rVTAwtgV5LEZ4Iel+W1GC8ugMhyr4/p1MtcIM42EA8BzE6ZQqC7VPq" - "PvEjZ2dbZkaBhPbiZAS3YeYBRDWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVk" - "aZdklLQp2Btgt9qr21m42f4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BA" - "QUFAAOCAQEAh8zGlfSlcI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL" - "+9gGlqCz5iWLOgWsnrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1" - "zFo+Owb1zxtp3PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL" - "2Bo3UPGrpsHzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo" - "4tpzd5rFXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTq" - "gawR+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA==\"]" - "}", -*lws_jwe_ex_c1_jwk_json = (uint8_t *) /* RSA enc private key (no parent JSON) */ - "{" - "\"kty\":\"RSA\"," - "\"kid\":\"juliet@capulet.lit\"," - "\"use\":\"enc\"," - "\"n\":\"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy" - "O125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP" - "8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0" - "Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0X" - "OC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1" - "_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q\"," - "\"e\":\"AQAB\"," - "\"d\":\"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfS" - "NkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9U" - "vqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnu" - "ToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsu" - "rY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2a" - "hecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ\"," - "\"p\":\"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHf" - "QP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8" - "UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws\"," - "\"q\":\"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6I" - "edis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYK" - "rYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s\"," - "\"dp\":\"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3" - "tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1w" - "Y52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c\"," - "\"dq\":\"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9" - "GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBy" - "mXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots\"," - "\"qi\":\"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqq" - "abu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0o" - "Yu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8\"" - "}" /*, -lws_jwe_ex_c1_plaintext[] = { - 123, 34, 107, 116, 121, 34, 58, 34, 82, 83, 65, 34, 44, 34, 107, - 105, 100, 34, 58, 34, 106, 117, 108, 105, 101, 116, 64, 99, 97, 112, - 117, 108, 101, 116, 46, 108, 105, 116, 34, 44, 34, 117, 115, 101, 34, - 58, 34, 101, 110, 99, 34, 44, 34, 110, 34, 58, 34, 116, 54, 81, 56, - 80, 87, 83, 105, 49, 100, 107, 74, 106, 57, 104, 84, 80, 56, 104, 78, - 89, 70, 108, 118, 97, 100, 77, 55, 68, 102, 108, 87, 57, 109, 87, - 101, 112, 79, 74, 104, 74, 54, 54, 119, 55, 110, 121, 111, 75, 49, - 103, 80, 78, 113, 70, 77, 83, 81, 82, 121, 79, 49, 50, 53, 71, 112, - 45, 84, 69, 107, 111, 100, 104, 87, 114, 48, 105, 117, 106, 106, 72, - 86, 120, 55, 66, 99, 86, 48, 108, 108, 83, 52, 119, 53, 65, 67, 71, - 103, 80, 114, 99, 65, 100, 54, 90, 99, 83, 82, 48, 45, 73, 113, 111, - 109, 45, 81, 70, 99, 78, 80, 56, 83, 106, 103, 48, 56, 54, 77, 119, - 111, 113, 81, 85, 95, 76, 89, 121, 119, 108, 65, 71, 90, 50, 49, 87, - 83, 100, 83, 95, 80, 69, 82, 121, 71, 70, 105, 78, 110, 106, 51, 81, - 81, 108, 79, 56, 89, 110, 115, 53, 106, 67, 116, 76, 67, 82, 119, 76, - 72, 76, 48, 80, 98, 49, 102, 69, 118, 52, 53, 65, 117, 82, 73, 117, - 85, 102, 86, 99, 80, 121, 83, 66, 87, 89, 110, 68, 121, 71, 120, 118, - 106, 89, 71, 68, 83, 77, 45, 65, 113, 87, 83, 57, 122, 73, 81, 50, - 90, 105, 108, 103, 84, 45, 71, 113, 85, 109, 105, 112, 103, 48, 88, - 79, 67, 48, 67, 99, 50, 48, 114, 103, 76, 101, 50, 121, 109, 76, 72, - 106, 112, 72, 99, 105, 67, 75, 86, 65, 98, 89, 53, 45, 76, 51, 50, - 45, 108, 83, 101, 90, 79, 45, 79, 115, 54, 85, 49, 53, 95, 97, 88, - 114, 107, 57, 71, 119, 56, 99, 80, 85, 97, 88, 49, 95, 73, 56, 115, - 76, 71, 117, 83, 105, 86, 100, 116, 51, 67, 95, 70, 110, 50, 80, 90, - 51, 90, 56, 105, 55, 52, 52, 70, 80, 70, 71, 71, 99, 71, 49, 113, - 115, 50, 87, 122, 45, 81, 34, 44, 34, 101, 34, 58, 34, 65, 81, 65, - 66, 34, 44, 34, 100, 34, 58, 34, 71, 82, 116, 98, 73, 81, 109, 104, - 79, 90, 116, 121, 115, 122, 102, 103, 75, 100, 103, 52, 117, 95, 78, - 45, 82, 95, 109, 90, 71, 85, 95, 57, 107, 55, 74, 81, 95, 106, 110, - 49, 68, 110, 102, 84, 117, 77, 100, 83, 78, 112, 114, 84, 101, 97, - 83, 84, 121, 87, 102, 83, 78, 107, 117, 97, 65, 119, 110, 79, 69, 98, - 73, 81, 86, 121, 49, 73, 81, 98, 87, 86, 86, 50, 53, 78, 89, 51, 121, - 98, 99, 95, 73, 104, 85, 74, 116, 102, 114, 105, 55, 98, 65, 88, 89, - 69, 82, 101, 87, 97, 67, 108, 51, 104, 100, 108, 80, 75, 88, 121, 57, - 85, 118, 113, 80, 89, 71, 82, 48, 107, 73, 88, 84, 81, 82, 113, 110, - 115, 45, 100, 86, 74, 55, 106, 97, 104, 108, 73, 55, 76, 121, 99, - 107, 114, 112, 84, 109, 114, 77, 56, 100, 87, 66, 111, 52, 95, 80, - 77, 97, 101, 110, 78, 110, 80, 105, 81, 103, 79, 48, 120, 110, 117, - 84, 111, 120, 117, 116, 82, 90, 74, 102, 74, 118, 71, 52, 79, 120, - 52, 107, 97, 51, 71, 79, 82, 81, 100, 57, 67, 115, 67, 90, 50, 118, - 115, 85, 68, 109, 115, 88, 79, 102, 85, 69, 78, 79, 121, 77, 113, 65, - 68, 67, 54, 112, 49, 77, 51, 104, 51, 51, 116, 115, 117, 114, 89, 49, - 53, 107, 57, 113, 77, 83, 112, 71, 57, 79, 88, 95, 73, 74, 65, 88, - 109, 120, 122, 65, 104, 95, 116, 87, 105, 90, 79, 119, 107, 50, 75, - 52, 121, 120, 72, 57, 116, 83, 51, 76, 113, 49, 121, 88, 56, 67, 49, - 69, 87, 109, 101, 82, 68, 107, 75, 50, 97, 104, 101, 99, 71, 56, 53, - 45, 111, 76, 75, 81, 116, 53, 86, 69, 112, 87, 72, 75, 109, 106, 79, - 105, 95, 103, 74, 83, 100, 83, 103, 113, 99, 78, 57, 54, 88, 53, 50, - 101, 115, 65, 81, 34, 44, 34, 112, 34, 58, 34, 50, 114, 110, 83, 79, - 86, 52, 104, 75, 83, 78, 56, 115, 83, 52, 67, 103, 99, 81, 72, 70, - 98, 115, 48, 56, 88, 98, 111, 70, 68, 113, 75, 117, 109, 51, 115, 99, - 52, 104, 51, 71, 82, 120, 114, 84, 109, 81, 100, 108, 49, 90, 75, 57, - 117, 119, 45, 80, 73, 72, 102, 81, 80, 48, 70, 107, 120, 88, 86, 114, - 120, 45, 87, 69, 45, 90, 69, 98, 114, 113, 105, 118, 72, 95, 50, 105, - 67, 76, 85, 83, 55, 119, 65, 108, 54, 88, 118, 65, 82, 116, 49, 75, - 107, 73, 97, 85, 120, 80, 80, 83, 89, 66, 57, 121, 107, 51, 49, 115, - 48, 81, 56, 85, 75, 57, 54, 69, 51, 95, 79, 114, 65, 68, 65, 89, 116, - 65, 74, 115, 45, 77, 51, 74, 120, 67, 76, 102, 78, 103, 113, 104, 53, - 54, 72, 68, 110, 69, 84, 84, 81, 104, 72, 51, 114, 67, 84, 53, 84, - 51, 121, 74, 119, 115, 34, 44, 34, 113, 34, 58, 34, 49, 117, 95, 82, - 105, 70, 68, 80, 55, 76, 66, 89, 104, 51, 78, 52, 71, 88, 76, 84, 57, - 79, 112, 83, 75, 89, 80, 48, 117, 81, 90, 121, 105, 97, 90, 119, 66, - 116, 79, 67, 66, 78, 74, 103, 81, 120, 97, 106, 49, 48, 82, 87, 106, - 115, 90, 117, 48, 99, 54, 73, 101, 100, 105, 115, 52, 83, 55, 66, 95, - 99, 111, 83, 75, 66, 48, 75, 106, 57, 80, 97, 80, 97, 66, 122, 103, - 45, 73, 121, 83, 82, 118, 118, 99, 81, 117, 80, 97, 109, 81, 117, 54, - 54, 114, 105, 77, 104, 106, 86, 116, 71, 54, 84, 108, 86, 56, 67, 76, - 67, 89, 75, 114, 89, 108, 53, 50, 122, 105, 113, 75, 48, 69, 95, 121, - 109, 50, 81, 110, 107, 119, 115, 85, 88, 55, 101, 89, 84, 66, 55, 76, - 98, 65, 72, 82, 75, 57, 71, 113, 111, 99, 68, 69, 53, 66, 48, 102, - 56, 48, 56, 73, 52, 115, 34, 44, 34, 100, 112, 34, 58, 34, 75, 107, - 77, 84, 87, 113, 66, 85, 101, 102, 86, 119, 90, 50, 95, 68, 98, 106, - 49, 112, 80, 81, 113, 121, 72, 83, 72, 106, 106, 57, 48, 76, 53, 120, - 95, 77, 79, 122, 113, 89, 65, 74, 77, 99, 76, 77, 90, 116, 98, 85, - 116, 119, 75, 113, 118, 86, 68, 113, 51, 116, 98, 69, 111, 51, 90, - 73, 99, 111, 104, 98, 68, 116, 116, 54, 83, 98, 102, 109, 87, 122, - 103, 103, 97, 98, 112, 81, 120, 78, 120, 117, 66, 112, 111, 79, 79, - 102, 95, 97, 95, 72, 103, 77, 88, 75, 95, 108, 104, 113, 105, 103, - 73, 52, 121, 95, 107, 113, 83, 49, 119, 89, 53, 50, 73, 119, 106, 85, - 110, 53, 114, 103, 82, 114, 74, 45, 121, 89, 111, 49, 104, 52, 49, - 75, 82, 45, 118, 122, 50, 112, 89, 104, 69, 65, 101, 89, 114, 104, - 116, 116, 87, 116, 120, 86, 113, 76, 67, 82, 86, 105, 68, 54, 99, 34, - 44, 34, 100, 113, 34, 58, 34, 65, 118, 102, 83, 48, 45, 103, 82, 120, - 118, 110, 48, 98, 119, 74, 111, 77, 83, 110, 70, 120, 89, 99, 75, 49, - 87, 110, 117, 69, 106, 81, 70, 108, 117, 77, 71, 102, 119, 71, 105, - 116, 81, 66, 87, 116, 102, 90, 49, 69, 114, 55, 116, 49, 120, 68, - 107, 98, 78, 57, 71, 81, 84, 66, 57, 121, 113, 112, 68, 111, 89, 97, - 78, 48, 54, 72, 55, 67, 70, 116, 114, 107, 120, 104, 74, 73, 66, 81, - 97, 106, 54, 110, 107, 70, 53, 75, 75, 83, 51, 84, 81, 116, 81, 53, - 113, 67, 122, 107, 79, 107, 109, 120, 73, 101, 51, 75, 82, 98, 66, - 121, 109, 88, 120, 107, 98, 53, 113, 119, 85, 112, 88, 53, 69, 76, - 68, 53, 120, 70, 99, 54, 70, 101, 105, 97, 102, 87, 89, 89, 54, 51, - 84, 109, 109, 69, 65, 117, 95, 108, 82, 70, 67, 79, 74, 51, 120, 68, - 101, 97, 45, 111, 116, 115, 34, 44, 34, 113, 105, 34, 58, 34, 108, - 83, 81, 105, 45, 119, 57, 67, 112, 121, 85, 82, 101, 77, 69, 114, 80, - 49, 82, 115, 66, 76, 107, 55, 119, 78, 116, 79, 118, 115, 53, 69, 81, - 112, 80, 113, 109, 117, 77, 118, 113, 87, 53, 55, 78, 66, 85, 99, - 122, 83, 99, 69, 111, 80, 119, 109, 85, 113, 113, 97, 98, 117, 57, - 86, 48, 45, 80, 121, 52, 100, 81, 53, 55, 95, 98, 97, 112, 111, 75, - 82, 117, 49, 82, 57, 48, 98, 118, 117, 70, 110, 85, 54, 51, 83, 72, - 87, 69, 70, 103, 108, 90, 81, 118, 74, 68, 77, 101, 65, 118, 109, - 106, 52, 115, 109, 45, 70, 112, 48, 111, 89, 117, 95, 110, 101, 111, - 116, 103, 81, 48, 104, 122, 98, 73, 53, 103, 114, 121, 55, 97, 106, - 100, 89, 121, 57, 45, 50, 108, 78, 120, 95, 55, 54, 97, 66, 90, 111, - 79, 85, 117, 57, 72, 67, 74, 45, 85, 115, 102, 83, 79, 73, 56, 34, - 125 } */ -; - -static int -key_import_callback(struct lws_jwk *s, void *user) -{ - lwsl_notice("%s: key type %d\n", __func__, s->kty); - - return 0; -} - - -int -test_jwk(struct lws_context *context) -{ - struct lws_jwk jwk; - - /* Test 1: A.1: Example public keys */ - - if (lws_jwk_import(&jwk, key_import_callback, NULL, - (char *)lws_jwe_ex_a1_jwk_json, - strlen((char *)lws_jwe_ex_a1_jwk_json)) < 0) { - lwsl_notice("Failed to decode JWK test key\n"); - goto bail1; - } - - lws_jwk_destroy(&jwk); - - /* Test 1: A.2: Example private keys */ - - if (lws_jwk_import(&jwk, key_import_callback, NULL, - (char *)lws_jwe_ex_a2_jwk_json, - strlen((char *)lws_jwe_ex_a2_jwk_json)) < 0) { - lwsl_notice("Failed at A.2\n"); - goto bail1; - } - - lws_jwk_destroy(&jwk); - - /* Test 1: A.3: Example symmetric keys */ - - if (lws_jwk_import(&jwk, key_import_callback, NULL, - (char *)lws_jwe_ex_a3_jwk_json, - strlen((char *)lws_jwe_ex_a3_jwk_json)) < 0) { - lwsl_notice("Failed at A.3\n"); - goto bail1; - } - - lws_jwk_destroy(&jwk); - - /* Test 1: B: Example x509 cert chain (no parent JSON) */ - - if (lws_jwk_import(&jwk, NULL, NULL, (char *)lws_jwe_ex_b_jwk_json, - strlen((char *)lws_jwe_ex_b_jwk_json)) < 0) { - lwsl_notice("Failed at B\n"); - goto bail1; - } - - lws_jwk_destroy(&jwk); - - /* Test 1: C.1: Example private key (no parent JSON) */ - - if (lws_jwk_import(&jwk, NULL, NULL, - (char *)lws_jwe_ex_c1_jwk_json, - strlen((char *)lws_jwe_ex_c1_jwk_json)) < 0) { - lwsl_notice("Failed at B\n"); - goto bail1; - } - - lws_jwk_destroy(&jwk); - - /* end */ - - lwsl_notice("%s: selftest OK\n", __func__); - - return 0; - -//bail: -// lws_jwk_destroy(&jwk); -bail1: - lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__); - - return 1; - -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/jws.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/jws.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/jws.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/jws.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,713 +0,0 @@ -/* - * lws-api-test-jose - RFC7515 jws tests - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include - -/* - * JSON Web Signature is defined in RFC7515 - * - * https://tools.ietf.org/html/rfc7515 - * - * It's basically a way to wrap some JSON with a JSON "header" describing the - * crypto, and a signature, all in a BASE64 wrapper with elided terminating '='. - * - * The signature stays with the content, it serves a different purpose than eg - * a TLS tunnel to transfer it. - * - */ - -/* for none, the compact serialization format is b64u(jose hdr).b64u(payload) */ - -static const char *none_cser = - "eyJhbGciOiJub25lIn0" - "." - "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt" - "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ", - *none_jose = "{\"alg\":\"none\"}", - *none_payload = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n" - " \"http://example.com/is_root\":true}"; - -int -test_jws_none(struct lws_context *context) -{ - struct lws_jws_map map; - struct lws_jose jose; - char temp[2048]; - int n, temp_len = sizeof(temp), ret = -1; - - lws_jose_init(&jose); - - /* A.5 Unsecured JSON "none" RFC7515 worked example */ - - /* decode the b64.b64[.b64] compact serialization blocks */ - n = lws_jws_compact_decode(none_cser, strlen(none_cser), &map, NULL, - temp, &temp_len); - if (n != 2) { - lwsl_err("%s: concat_map failed\n", __func__); - goto bail; - } - - /* confirm the decoded JOSE header is exactly what we expect */ - if (strncmp(none_jose, map.buf[LJWS_JOSE], map.len[LJWS_JOSE])) { - lwsl_err("%s: jose b64 decode wrong\n", __func__); - goto bail; - } - - /* parse the JOSE header */ - if (lws_jws_parse_jose(&jose, map.buf[LJWS_JOSE], - map.len[LJWS_JOSE], - (char *)lws_concat_temp(temp, temp_len), - &temp_len) < 0 || !jose.alg) { - lwsl_err("%s: JOSE parse failed\n", __func__); - goto bail; - } - - /* confirm we used the "none" alg as expected from JOSE hdr */ - if (strcmp(jose.alg->alg, "none")) { - lwsl_err("%s: JOSE header has wrong alg\n", __func__); - goto bail; - } - - /* confirm the payload is literally what we expect */ - if (strncmp(none_payload, map.buf[LJWS_PYLD], - map.len[LJWS_PYLD])) { - lwsl_err("%s: payload b64 decode wrong\n", __func__); - goto bail; - } - - /* end */ - - ret = 0; - -bail: - lws_jose_destroy(&jose); - - if (ret) - lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__); - else - lwsl_notice("%s: selftest OK\n", __func__); - - return ret; -} - - - -static const char - *test1 = "{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}", - *test1_enc = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9", - *test2 = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n" - " \"http://example.com/is_root\":true}", - *test2_enc = "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQ" - "ogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ", - *key_jwk = "{\"kty\":\"oct\",\r\n" - " \"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQ" - "Lr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"}", - *hash_enc = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" -; - -int -test_jws_HS256(struct lws_context *context) -{ - char buf[2048], temp[256], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr; - uint8_t digest[LWS_GENHASH_LARGEST]; - struct lws_jws_map map; - int temp_len = sizeof(temp); - struct lws_genhmac_ctx ctx; - struct lws_jose jose; - struct lws_jwk jwk; - struct lws_jws jws; - int n; - - lws_jose_init(&jose); - lws_jws_init(&jws, &jwk, context); - - /* Test 1: SHA256 on RFC7515 worked example */ - - /* parse the JOSE header */ - - if (lws_jws_parse_jose(&jose, test1, strlen(test1), temp, &temp_len) < 0 || - !jose.alg) { - lwsl_err("%s: JOSE parse failed\n", __func__); - goto bail; - } - - /* confirm we used the "none" alg as expected from JOSE hdr */ - if (strcmp(jose.alg->alg, "HS256")) { - lwsl_err("%s: JOSE header has wrong alg\n", __func__); - goto bail; - } - - /* 1.1: import the JWK oct key */ - - if (lws_jwk_import(&jwk, NULL, NULL, key_jwk, strlen(key_jwk)) < 0) { - lwsl_notice("Failed to decode JWK test key\n"); - return -1; - } - if (jwk.kty != LWS_GENCRYPTO_KTY_OCT) { - lwsl_err("%s: unexpected kty %d\n", __func__, jwk.kty); - - return -1; - } - - /* 1.2: create JWS known hdr + known payload */ - - n = lws_jws_encode_section(test1, strlen(test1), 1, &p, end); - if (n < 0) { - goto bail; - } - - if (strcmp(buf, test1_enc)) - goto bail; - - enc_ptr = p + 1; /* + 1 skips the . */ - n = lws_jws_encode_section(test2, strlen(test2), 0, &p, end); - if (n < 0) { - goto bail; - } - - if (strcmp(enc_ptr, test2_enc)) - goto bail; - - /* 1.3: use HMAC SHA-256 with known key on the hdr . payload */ - - if (lws_genhmac_init(&ctx, jose.alg->hmac_type, - jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, - jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].len)) - goto bail; - if (lws_genhmac_update(&ctx, (uint8_t *)buf, p - buf)) - goto bail_destroy_hmac; - lws_genhmac_destroy(&ctx, digest); - - /* 1.4: append a base64 encode of the computed HMAC digest */ - - enc_ptr = p + 1; /* + 1 skips the . */ - n = lws_jws_encode_section((const char *)digest, 32, 0, &p, end); - if (n < 0) - goto bail; - if (strcmp(enc_ptr, hash_enc)) { /* check against known B64URL hash */ - lwsl_err("%s: b64 enc of computed HMAC mismatches '%s' '%s'\n", - __func__, enc_ptr, hash_enc); - goto bail; - } - - /* 1.5: Check we can agree the signature matches the payload */ - - if (lws_jws_sig_confirm_compact_b64(buf, p - buf, &map, &jwk, context, - lws_concat_temp(temp, temp_len), &temp_len) < 0) { - lwsl_notice("%s: confirm sig failed\n", __func__); - goto bail; - } - - lws_jws_destroy(&jws); - lws_jwk_destroy(&jwk); - lws_jose_destroy(&jose); - - /* end */ - - lwsl_notice("%s: selftest OK\n", __func__); - - return 0; - -bail_destroy_hmac: - lws_genhmac_destroy(&ctx, NULL); - -bail: - lws_jws_destroy(&jws); - lws_jwk_destroy(&jwk); - lws_jose_destroy(&jose); - lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__); - - return 1; -} - - -static const char - /* the key from worked example in RFC7515 A-2, as a JWK */ - *rfc7515_rsa_key = - "{\"kty\":\"RSA\"," - " \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx" - "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs" - "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH" - "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV" - "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8" - "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\"," - "\"e\":\"AQAB\"," - "\"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I" - "jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0" - "BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn" - "439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT" - "CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh" - "BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\"," - "\"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi" - "YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG" - "BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\"," - "\"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa" - "ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA" - "-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\"," - "\"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q" - "CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb" - "34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\"," - "\"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa" - "7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky" - "NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\"," - "\"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o" - "y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU" - "W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U\"" - "}", - *rfc7515_rsa_a1 = /* the signed worked example in RFC7515 A-1 */ - "eyJhbGciOiJSUzI1NiJ9" - ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt" - "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ" - ".cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7" - "AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4" - "BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K" - "0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv" - "hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB" - "p0igcN_IoypGlUPQGe77Rw" -; - -int -test_jws_RS256(struct lws_context *context) -{ - struct lws_jws_map map; - struct lws_jose jose; - struct lws_jwk jwk; - struct lws_jws jws; - char temp[2048], *in; - int n, l, temp_len = sizeof(temp); - - lws_jose_init(&jose); - lws_jws_init(&jws, &jwk, context); - - /* Test 2: RS256 on RFC7515 worked example */ - - if (lws_gencrypto_jws_alg_to_definition("RS256", &jose.alg)) { - lwsl_err("%s: RS256 not supported\n", __func__); - goto bail; - } - - /* 2.1: import the jwk */ - - if (lws_jwk_import(&jwk, NULL, NULL, - rfc7515_rsa_key, strlen(rfc7515_rsa_key))) { - lwsl_notice("%s: 2.2: Failed to read JWK key\n", __func__); - goto bail2; - } - - if (jwk.kty != LWS_GENCRYPTO_KTY_RSA) { - lwsl_err("%s: 2.2: kty: %d instead of RSA\n", __func__, jwk.kty); - goto bail; - } - - /* 2.2: check the signature on the test packet from RFC7515 A-1 */ - - if (lws_jws_sig_confirm_compact_b64(rfc7515_rsa_a1, - strlen(rfc7515_rsa_a1), &map, - &jwk, context, temp, &temp_len) < 0) { - lwsl_notice("%s: 2.2: confirm rsa sig failed\n", __func__); - goto bail; - } - - if (lws_jws_b64_compact_map(rfc7515_rsa_a1, strlen(rfc7515_rsa_a1), - &jws.map_b64) != 3) { - lwsl_notice("%s: lws_jws_b64_compact_map failed\n", __func__); - goto bail; - } - - /* 2.3: generate our own signature for a copy of the test packet */ - - in = lws_concat_temp(temp, temp_len); - l = strlen(rfc7515_rsa_a1); - if (temp_len < l + 1) - goto bail; - memcpy(in, rfc7515_rsa_a1, l + 1); - temp_len -= l + 1; - - if (lws_jws_b64_compact_map(in, l, &jws.map_b64) != 3) { - lwsl_notice("%s: lws_jws_b64_compact_map failed\n", __func__); - goto bail; - } - - /* overwrite the copy of the known b64 sig (it's all placed inside temp) */ - n = lws_jws_sign_from_b64(&jose, &jws, - (char *)jws.map_b64.buf[LJWS_SIG], - jws.map_b64.len[LJWS_SIG] + 8); - if (n < 0) { - lwsl_err("%s: failed signing test packet\n", __func__); - goto bail; - } - jws.map_b64.len[LJWS_SIG] = n; - - /* 2.4: confirm our signature can be verified */ - - in[l] = '\0'; - if (lws_jws_sig_confirm_compact_b64(in, l, &map, &jwk, context, lws_concat_temp(temp, temp_len), &temp_len) < 0) { - lwsl_notice("%s: 2.2: confirm rsa sig failed\n", __func__); - goto bail; - } - - lws_jwk_destroy(&jwk); - - /* end */ - - lwsl_notice("%s: selftest OK\n", __func__); - - return 0; - -bail: - lws_jwk_destroy(&jwk); -bail2: - lws_jws_destroy(&jws); - lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__); - - return 1; -} - -static const char - *es256_jose = "{\"alg\":\"ES256\"}", - *es256_payload = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n" - " \"http://example.com/is_root\":true}", - *es256_cser = - "eyJhbGciOiJFUzI1NiJ9" - "." - "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt" - "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ" - "." - "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSA" - "pmWQxfKTUJqPP3-Kg6NU1Q", - *es256_jwk = - "{" - "\"kty\":\"EC\"," - "\"crv\":\"P-256\"," - "\"x\":\"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU\"," - "\"y\":\"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0\"," - "\"d\":\"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI\"" - "}" -#if 0 - , - rfc7515_ec_a3_R[] = { - 14, 209, 33, 83, 121, 99, 108, 72, 60, 47, 127, 21, 88, - 7, 212, 2, 163, 178, 40, 3, 58, 249, 124, 126, 23, 129, - 154, 195, 22, 158, 166, 101 - }, - rfc7515_ec_a3_S[] = { - 197, 10, 7, 211, 140, 60, 112, 229, 216, 241, 45, 175, - 8, 74, 84, 128, 166, 101, 144, 197, 242, 147, 80, 154, - 143, 63, 127, 138, 131, 163, 84, 213 - } -#endif -; - -int -test_jws_ES256(struct lws_context *context) -{ - uint8_t digest[LWS_GENHASH_LARGEST]; - struct lws_genhash_ctx hash_ctx; - struct lws_jws_map map; - struct lws_jose jose; - struct lws_jwk jwk; - struct lws_jws jws; - char temp[2048], *p; - int ret = -1, l, n, temp_len = sizeof(temp); - - /* A.3 "ES256" RFC7515 worked example - verify */ - - lws_jose_init(&jose); - - /* decode the b64.b64[.b64] compact serialization blocks */ - if (lws_jws_compact_decode(es256_cser, strlen(es256_cser), - &jws.map, &jws.map_b64, - temp, &temp_len) != 3) { - lwsl_err("%s: concat_map failed\n", __func__); - goto bail; - } - - /* confirm the decoded JOSE header is exactly what we expect */ - if (jws.map.len[LJWS_JOSE] != strlen(es256_jose) || - strncmp(es256_jose, jws.map.buf[LJWS_JOSE], - jws.map.len[LJWS_JOSE])) { - lwsl_err("%s: jose b64 decode wrong\n", __func__); - goto bail; - } - - /* confirm the decoded payload is exactly what we expect */ - if (jws.map.len[LJWS_PYLD] != strlen(es256_payload) || - strncmp(es256_payload, jws.map.buf[LJWS_PYLD], - jws.map.len[LJWS_PYLD])) { - lwsl_err("%s: payload b64 decode wrong\n", __func__); - goto bail; - } - - /* parse the JOSE header */ - if (lws_jws_parse_jose(&jose, jws.map.buf[LJWS_JOSE], - jws.map.len[LJWS_JOSE], - (char *)lws_concat_temp(temp, temp_len), &temp_len) < 0) { - lwsl_err("%s: JOSE parse failed\n", __func__); - goto bail; - } - - /* confirm we used "ES256" alg we expect from the JOSE hdr */ - if (strcmp(jose.alg->alg, "ES256")) { - lwsl_err("%s: JOSE header has wrong alg\n", __func__); - goto bail; - } - - jws.jwk = &jwk; - jws.context = context; - - /* import the ES256 jwk */ - if (lws_jwk_import(&jwk, NULL, NULL, es256_jwk, strlen(es256_jwk))) { - lwsl_notice("%s: Failed to read JWK key\n", __func__); - goto bail; - } - - /* sanity */ - if (jwk.kty != LWS_GENCRYPTO_KTY_EC) { - lwsl_err("%s: kty: %d instead of EC\n", - __func__, jwk.kty); - goto bail1; - } - - if (lws_jws_sig_confirm(&jws.map_b64, &jws.map, &jwk, context) < 0) { - lwsl_notice("%s: confirm EC sig failed\n", __func__); - goto bail1; - } - - /* A.3 "ES256" RFC7515 worked example - sign */ - - l = strlen(es256_cser); - if (temp_len < l + 1) - goto bail1; - p = lws_concat_temp(temp, temp_len); - memcpy(p, es256_cser, l + 1); - temp_len -= l + 1; - - /* scan the b64 compact serialization string to map the blocks */ - if (lws_jws_b64_compact_map(p, l, &jws.map_b64) != 3) - goto bail1; - - /* create the hash of the protected b64 part */ - if (lws_genhash_init(&hash_ctx, jose.alg->hash_type) || - lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_JOSE], - jws.map_b64.len[LJWS_JOSE]) || - lws_genhash_update(&hash_ctx, ".", 1) || - lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_PYLD], - jws.map_b64.len[LJWS_PYLD]) || - lws_genhash_destroy(&hash_ctx, digest)) { - lws_genhash_destroy(&hash_ctx, NULL); - - goto bail1; - } - - lwsl_hexdump(jws.map_b64.buf[LJWS_SIG], jws.map_b64.len[LJWS_SIG]); - - /* overwrite the copy of the known b64 sig (it's placed inside buf) */ - n = lws_jws_sign_from_b64(&jose, &jws, - (char *)jws.map_b64.buf[LJWS_SIG], - jws.map_b64.len[LJWS_SIG] + 8); - if (n < 0) { - lwsl_err("%s: failed signing test packet\n", __func__); - goto bail1; - } - jws.map_b64.len[LJWS_SIG] = n; - - lwsl_hexdump(jws.map_b64.buf[LJWS_SIG], jws.map_b64.len[LJWS_SIG]); - - /* 2.4: confirm our generated signature can be verified */ - -// lwsl_err("p %p, l %d\n", p, (int)l); - p[l] = '\0'; - if (lws_jws_sig_confirm_compact_b64(p, l, &map, &jwk, context, lws_concat_temp(temp, temp_len), &temp_len) < 0) { - lwsl_notice("%s: confirm our EC sig failed\n", __func__); - goto bail1; - } - - /* end */ - ret = 0; - -bail1: - lws_jwk_destroy(&jwk); - lws_jose_destroy(&jose); - -bail: - lwsl_notice("%s: selftest %s\n", __func__, ret ? "FAIL" : "OK"); - - return ret; -} - -static const char - *es512_jose = "{\"alg\":\"ES512\"}", - *es512_payload = "Payload", - *es512_cser = - "eyJhbGciOiJFUzUxMiJ9" - "." - "UGF5bG9hZA" - "." - "AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZq" - "wqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8Kp" - "EHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn", - *es512_jwk = - "{" - "\"kty\":\"EC\"," - "\"crv\":\"P-521\"," - "\"x\":\"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_" - "NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk\"," - "\"y\":\"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDl" - "y79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2\"," - "\"d\":\"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPA" - "xerEzgdRhajnu0ferB0d53vM9mE15j2C\"" - "}" -; - -int -test_jws_ES512(struct lws_context *context) -{ - uint8_t digest[LWS_GENHASH_LARGEST]; - struct lws_genhash_ctx hash_ctx; - struct lws_jws_map map; - struct lws_jose jose; - struct lws_jwk jwk; - struct lws_jws jws; - char temp[2048], *p; - int ret = -1, l, n, temp_len = sizeof(temp); - - /* A.4 "ES512" RFC7515 worked example - verify */ - - lws_jose_init(&jose); - - /* decode the b64.b64[.b64] compact serialization blocks */ - if (lws_jws_compact_decode(es512_cser, strlen(es512_cser), - &jws.map, &jws.map_b64, temp, - &temp_len) != 3) { - lwsl_err("%s: concat_map failed\n", __func__); - goto bail; - } - - /* confirm the decoded JOSE header is exactly what we expect */ - if (jws.map.len[LJWS_JOSE] != strlen(es512_jose) || - strncmp(es512_jose, jws.map.buf[LJWS_JOSE], - jws.map.len[LJWS_JOSE])) { - lwsl_err("%s: jose b64 decode wrong\n", __func__); - goto bail; - } - - /* confirm the decoded payload is exactly what we expect */ - if (jws.map.len[LJWS_PYLD] != strlen(es512_payload) || - strncmp(es512_payload, jws.map.buf[LJWS_PYLD], - jws.map.len[LJWS_PYLD])) { - lwsl_err("%s: payload b64 decode wrong\n", __func__); - goto bail; - } - - /* parse the JOSE header */ - if (lws_jws_parse_jose(&jose, jws.map.buf[LJWS_JOSE], - jws.map.len[LJWS_JOSE], - lws_concat_temp(temp, temp_len), &temp_len) < 0) { - lwsl_err("%s: JOSE parse failed\n", __func__); - goto bail; - } - - /* confirm we used "es512" alg we expect from the JOSE hdr */ - if (strcmp(jose.alg->alg, "ES512")) { - lwsl_err("%s: JOSE header has wrong alg\n", __func__); - goto bail; - } - - jws.jwk = &jwk; - jws.context = context; - - /* import the es512 jwk */ - if (lws_jwk_import(&jwk, NULL, NULL, es512_jwk, strlen(es512_jwk))) { - lwsl_notice("%s: Failed to read JWK key\n", __func__); - goto bail; - } - - /* sanity */ - if (jwk.kty != LWS_GENCRYPTO_KTY_EC) { - lwsl_err("%s: kty: %d instead of EC\n", - __func__, jwk.kty); - goto bail1; - } - - if (lws_jws_sig_confirm(&jws.map_b64, &jws.map, &jwk, context) < 0) { - lwsl_notice("%s: confirm EC sig failed\n", __func__); - goto bail1; - } - - /* A.3 "es512" RFC7515 worked example - sign */ - - l = strlen(es512_cser); - if (temp_len < l) - goto bail1; - p = lws_concat_temp(temp, temp_len); - memcpy(p, es512_cser, l + 1); - temp_len -= (l + 1); - - /* scan the b64 compact serialization string to map the blocks */ - if (lws_jws_b64_compact_map(p, l, &jws.map_b64) != 3) - goto bail1; - - /* create the hash of the protected b64 part */ - if (lws_genhash_init(&hash_ctx, jose.alg->hash_type) || - lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_JOSE], - jws.map_b64.len[LJWS_JOSE]) || - lws_genhash_update(&hash_ctx, ".", 1) || - lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_PYLD], - jws.map_b64.len[LJWS_PYLD]) || - lws_genhash_destroy(&hash_ctx, digest)) { - lws_genhash_destroy(&hash_ctx, NULL); - - goto bail1; - } - - /* overwrite the copy of the known b64 sig (it's placed inside buf) */ - n = lws_jws_sign_from_b64(&jose, &jws, - (char *)jws.map_b64.buf[LJWS_SIG], 1024); - if (n < 0) { - lwsl_err("%s: failed signing test packet\n", __func__); - goto bail1; - } - jws.map_b64.len[LJWS_SIG] = n; - - /* 2.4: confirm our generated signature can be verified */ - - p[l] = '\0'; - - if (lws_jws_sig_confirm_compact_b64(p, l, &map, &jwk, context, - lws_concat_temp(temp, temp_len), &temp_len) < 0) { - lwsl_notice("%s: confirm our ECDSA sig failed\n", __func__); - goto bail1; - } - - /* end */ - ret = 0; - -bail1: - lws_jwk_destroy(&jwk); - lws_jose_destroy(&jose); - -bail: - lwsl_notice("%s: selftest %s\n", __func__, ret ? "FAIL" : "OK"); - - return ret; -} - -int -test_jws(struct lws_context *context) -{ - int n = 0; - - n |= test_jws_none(context); - n |= test_jws_HS256(context); - n |= test_jws_RS256(context); - n |= test_jws_ES256(context); - n |= test_jws_ES512(context); - - return n; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -/* - * lws-api-test-jose - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include - -int -test_jwk(struct lws_context *context); -int -test_jws(struct lws_context *context); -int -test_jwe(struct lws_context *context); - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int result = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS JOSE api tests\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = 0; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - result |= test_jwk(context); - lwsl_notice("%d\n", result); - result |= test_jws(context); - lwsl_notice("%d\n", result); - result |= test_jwe(context); - lwsl_notice("%d\n", result); - - lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS"); - - lws_context_destroy(context); - - return result; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/README.md libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -# lws api test lwsac - -Demonstrates how to use and performs selftests for lwsac - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 - -``` - $ ./lws-api-test-lwsac -[2018/10/09 09:14:17:4834] USER: LWS API selftest: lwsac -[2018/10/09 09:14:17:4835] USER: Completed: PASS -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/selftest.sh libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/selftest.sh --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-jose/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-jose/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=1 - -dotest $1 $2 apiselftest -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lwsac/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lwsac/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lwsac/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lwsac/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -project(lws-api-test-lwsac) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-lwsac) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lwsac/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lwsac/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lwsac/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lwsac/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -/* - * lws-api-test-lwsac - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include - -struct mytest { - int payload; - /* notice doesn't have to be at start of struct */ - lws_list_ptr list_next; - /* a struct can appear on multiple lists too... */ -}; - -/* converts a ptr to struct mytest .list_next to a ptr to struct mytest */ -#define list_to_mytest(p) lws_list_ptr_container(p, struct mytest, list_next) - -int main(int argc, const char **argv) -{ - int n, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE, acc; - lws_list_ptr list_head = NULL, iter; - struct lwsac *lwsac = NULL; - struct mytest *m; - const char *p; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS API selftest: lwsac\n"); - - /* - * 1) allocate and create 1000 struct mytest in a linked-list - */ - - for (n = 0; n < 1000; n++) { - m = lwsac_use(&lwsac, sizeof(*m), 0); - if (!m) - return -1; - m->payload = n; - - lws_list_ptr_insert(&list_head, &m->list_next, NULL); - } - - /* - * 2) report some debug info about the lwsac state... those 1000 - * allocations actually only required 4 mallocs - */ - - lwsac_info(lwsac); - - /* 3) iterate the list, accumulating the payloads */ - - acc = 0; - iter = list_head; - while (iter) { - m = list_to_mytest(iter); - acc += m->payload; - - lws_list_ptr_advance(iter); - } - - if (acc != 499500) { - lwsl_err("%s: FAIL acc %d\n", __func__, acc); - - return 1; - } - - /* - * 4) deallocate everything (lwsac is also set to NULL). It just - * deallocates the 4 mallocs, everything in there is gone accordingly - */ - - lwsac_free(&lwsac); - - lwsl_user("Completed: PASS\n"); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lwsac/README.md libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lwsac/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lwsac/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lwsac/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -# lws api test lwsac - -Demonstrates how to use and performs selftests for lwsac - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 - -``` - $ ./lws-api-test-lwsac -[2018/10/09 09:14:17:4834] USER: LWS API selftest: lwsac -[2018/10/09 09:14:17:4835] USER: Completed: PASS -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lwsac/selftest.sh libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lwsac/selftest.sh --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lwsac/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lwsac/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=1 - -dotest $1 $2 apiselftest -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_dsh/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_dsh/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_dsh/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_dsh/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-api-test-lws_dsh) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-lws_dsh) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_NETWORK 1 requirements) -require_lws_config(LWS_WITH_LWS_DSH 1 requirements) - -if (requirements) - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_dsh/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_dsh/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_dsh/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_dsh/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,361 +0,0 @@ -/* - * lws-api-test-lws_dsh - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include - -int -test1(void) -{ - struct lws_dsh *dsh; - size_t size; - void *a1; - - /* - * test 1: single dsh, alloc 2 kinds and free everything back to a - * single free obj - */ - - dsh = lws_dsh_create(NULL, 16384, 2); - if (!dsh) { - lwsl_err("%s: Failed to create dsh\n", __func__); - - return 1; - } - - if (lws_dsh_alloc_tail(dsh, 0, "hello", 5, NULL, 0)) { - lwsl_err("%s: Failed to alloc 1\n", __func__); - - goto bail; - } - - if (lws_dsh_alloc_tail(dsh, 1, "some other string", 17, NULL, 0)) { - lwsl_err("%s: Failed to alloc 2\n", __func__); - - goto bail; - } - - if (lws_dsh_alloc_tail(dsh, 0, "hello again", 11, NULL, 0)) { - lwsl_err("%s: Failed to alloc 3\n", __func__); - - goto bail; - } - - if (lws_dsh_get_head(dsh, 1, &a1, &size)) { - lwsl_err("%s: no head 1\n", __func__); - - goto bail; - } - if (size != 17 || memcmp(a1, "some other string", 17)) { - lwsl_err("%s: test 1 mismatch\n", __func__); - - goto bail; - } - lws_dsh_free(&a1); - - if (lws_dsh_get_head(dsh, 0, &a1, &size)) { - lwsl_err("%s: no head 2\n", __func__); - - goto bail; - } - if (size != 5 || memcmp(a1, "hello", 5)) { - lwsl_err("%s: test 2 mismatch\n", __func__); - - goto bail; - } - lws_dsh_free(&a1); - - if (lws_dsh_get_head(dsh, 0, &a1, &size)) { - lwsl_err("%s: no head 3\n", __func__); - - goto bail; - } - if (size != 11 || memcmp(a1, "hello again", 11)) { - lwsl_err("%s: test 3 mismatch\n", __func__); - - goto bail; - } - lws_dsh_free(&a1); - - lws_dsh_destroy(&dsh); - - return 0; -bail: - lws_dsh_destroy(&dsh); - - return 1; -} - -int -test2(void) -{ - struct lws_dsh *dsh, *dsh2; - lws_dll2_owner_t owner; - uint8_t blob[4096]; - - memset(blob, 0, sizeof(blob)); - - /* - * test 2: multiple dsh, overflow allocation and dynamic destroy - */ - - lws_dll2_owner_clear(&owner); - - dsh = lws_dsh_create(&owner, 4096, 2); - if (!dsh) { - lwsl_err("%s: Failed to create dsh1\n", __func__); - - return 1; - } - - dsh2 = lws_dsh_create(&owner, 4096, 2); - if (!dsh2) { - lwsl_err("%s: Failed to create dsh2\n", __func__); - - goto bail; - } - - if (lws_dsh_alloc_tail(dsh, 0, blob, 4000, NULL, 0)) { - lwsl_err("%s: Failed to alloc 1\n", __func__); - - goto bail2; - } - - if (lws_dsh_alloc_tail(dsh2, 0, "hello", 5, NULL, 0)) { - lwsl_err("%s: Failed to alloc 2\n", __func__); - - goto bail2; - } - - /* - * We create this logically on dsh. But there's no room for the body. - * It should figure out it can use space on dsh2. - */ - - if (lws_dsh_alloc_tail(dsh, 0, blob, 2000, NULL, 0)) { - lwsl_err("%s: Failed to alloc 3\n", __func__); - - goto bail2; - } - - if (lws_dsh_alloc_tail(dsh2, 0, "hello again", 11, NULL, 0)) { - lwsl_err("%s: Failed to alloc 4\n", __func__); - - goto bail2; - } - - /* - * When we destroy dsh2 it will try to migrate out the 2000 allocation - * from there but find there is no space in dsh1. It should handle it - * by logicalling dropping the object. - */ - - lws_dsh_destroy(&dsh2); - lws_dsh_destroy(&dsh); - - return 0; - -bail2: - lws_dsh_destroy(&dsh2); - -bail: - lws_dsh_destroy(&dsh); - - return 1; - -} - -int -test3(void) -{ - struct lws_dsh *dsh, *dsh2; - lws_dll2_owner_t owner; - uint8_t blob[4096]; - - memset(blob, 0, sizeof(blob)); - - /* - * test 3: multiple dsh, umeetable allocation request - */ - - lws_dll2_owner_clear(&owner); - - dsh = lws_dsh_create(&owner, 4096, 2); - if (!dsh) { - lwsl_err("%s: Failed to create dsh1\n", __func__); - - return 1; - } - - dsh2 = lws_dsh_create(&owner, 4096, 2); - if (!dsh2) { - lwsl_err("%s: Failed to create dsh2\n", __func__); - - goto bail; - } - - if (lws_dsh_alloc_tail(dsh, 0, blob, 4000, NULL, 0)) { - lwsl_err("%s: Failed to alloc 1\n", __func__); - - goto bail2; - } - - if (lws_dsh_alloc_tail(dsh2, 0, "hello", 5, NULL, 0)) { - lwsl_err("%s: Failed to alloc 2\n", __func__); - - goto bail2; - } - - /* - * There's just no room for this, we expect it to fail - */ - - if (!lws_dsh_alloc_tail(dsh, 0, blob, 5000, NULL, 0)) { - lwsl_err("%s: Didn't fail to alloc as expected\n", __func__); - - goto bail2; - } - - if (lws_dsh_alloc_tail(dsh2, 0, "hello again", 11, NULL, 0)) { - lwsl_err("%s: Failed to alloc 4\n", __func__); - - goto bail2; - } - - lws_dsh_destroy(&dsh2); - lws_dsh_destroy(&dsh); - - return 0; - -bail2: - lws_dsh_destroy(&dsh2); - -bail: - lws_dsh_destroy(&dsh); - - return 1; -} - -int -test4(void) -{ - uint8_t blob[4096]; - struct lws_dsh *dsh; - size_t size; - void *a1; - - memset(blob, 0, sizeof(blob)); - - /* - * test 1: use up whole free list, then recover and alloc something - * else - */ - - dsh = lws_dsh_create(NULL, 4096, 2); - if (!dsh) { - lwsl_err("%s: Failed to create dsh\n", __func__); - - return 1; - } - - if (lws_dsh_alloc_tail(dsh, 0, blob, 4000, NULL, 0)) { - lwsl_err("%s: Failed to alloc 1\n", __func__); - - goto bail; - } - - if (lws_dsh_get_head(dsh, 0, &a1, &size)) { - lwsl_err("%s: no head 1\n", __func__); - - goto bail; - } - if (size != 4000) { - lwsl_err("%s: test 1 mismatch\n", __func__); - - goto bail; - } - lws_dsh_free(&a1); - - if (lws_dsh_alloc_tail(dsh, 0, "some other string", 17, NULL, 0)) { - lwsl_err("%s: Failed to alloc 2\n", __func__); - - goto bail; - } - - if (lws_dsh_alloc_tail(dsh, 0, "hello again", 11, NULL, 0)) { - lwsl_err("%s: Failed to alloc 3\n", __func__); - - goto bail; - } - - if (lws_dsh_get_head(dsh, 0, &a1, &size)) { - lwsl_err("%s: no head 1\n", __func__); - - goto bail; - } - if (size != 17 || memcmp(a1, "some other string", 17)) { - lwsl_err("%s: test 1 mismatch\n", __func__); - - goto bail; - } - lws_dsh_free(&a1); - - if (lws_dsh_get_head(dsh, 0, &a1, &size)) { - lwsl_err("%s: no head 2\n", __func__); - - goto bail; - } - if (size != 11 || memcmp(a1, "hello again", 11)) { - lwsl_err("%s: test 2 mismatch (%zu)\n", __func__, size); - - goto bail; - } - - lws_dsh_free(&a1); - - lws_dsh_destroy(&dsh); - - return 0; -bail: - lws_dsh_destroy(&dsh); - - return 1; -} - -int main(int argc, const char **argv) -{ - int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - int ret = 0, n; - const char *p; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS API selftest: lws_dsh\n"); - - n = test1(); - lwsl_user("%s: test1: %d\n", __func__, n); - ret |= n; - - n = test2(); - lwsl_user("%s: test2: %d\n", __func__, n); - ret |= n; - - n = test3(); - lwsl_user("%s: test3: %d\n", __func__, n); - ret |= n; - - n = test4(); - lwsl_user("%s: test4: %d\n", __func__, n); - ret |= n; - - lwsl_user("Completed: %s\n", ret ? "FAIL" : "PASS"); - - return ret; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_dsh/README.md libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_dsh/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_dsh/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_dsh/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -# lws api test lws_dsh - -Demonstrates how to use and performs selftests for lws_dsh - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 - -``` - $ ./lws-api-test-lws_dsh -[2018/10/09 09:14:17:4834] USER: LWS API selftest: lws_dsh -[2018/10/09 09:14:17:4835] USER: Completed: PASS -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_dsh/selftest.sh libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_dsh/selftest.sh --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_dsh/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_dsh/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=1 - -dotest $1 $2 apiselftest -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_sequencer/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_sequencer/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_sequencer/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_sequencer/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-api-test-lws_sequencer) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-lws_sequencer) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) -require_lws_config(LWS_WITH_SEQUENCER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_sequencer/libwebsockets.org.cer libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_sequencer/libwebsockets.org.cer --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_sequencer/libwebsockets.org.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_sequencer/libwebsockets.org.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFWjCCBEKgAwIBAgISA9x0/oj5PLdW46hsmR82/7ytMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5NDBaFw0x -OTEyMDYwNzA5NDBaMBwxGjAYBgNVBAMTEWxpYndlYnNvY2tldHMub3JnMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPinIkleLmvEcA/YuBss6ASXVi7g -yr6Sss7cB3vTy7Fp8OB2c1N25prHZxVpORAUo0UreiaY2Ws4NFvDaYp08ZffevuC -UhThsEJlbkD0uvt7dPapJt9PNJtlxjNFWyvHEy6PijzIaMYDROiStcCJQn7kAew/ -Za2+5kNVgKqT+7OXukJEFdSdVZI6QC/npeQlkIrFSq1WVthCGBNJehxxES0hSWzk -0gNVKlkD3/SbkupsfUpe73XiawMtrtsSE7cdnul7VZmiP8I/3sJr1+4/3xZ+DEYg -mVB82B0vd08VJYzU7Nf0pz0PWusAmzRoRn81IXkOfBg9ohlSSEoZhHYS7QIDAQAB -o4ICZjCCAmIwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr -BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRmKKyGjufWgp7pR2x0tWxG -D9G+WTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcB -AQRjMGEwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlw -dC5vcmcwLwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlw -dC5vcmcvMBwGA1UdEQQVMBOCEWxpYndlYnNvY2tldHMub3JnMEwGA1UdIARFMEMw -CAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9j -cHMubGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcAdH7a -gzGtMxCRIZzOJU9CcMK//V5CIAjGNzV55hB7zFYAAAFtCsWIfgAABAMASDBGAiEA -0H55VqSKV3otHK7uHNbcR0QwoUYtCmeObhsqxzCnmDwCIQD3mtuSKrxTD3oA+Yde -nmTgWfFyS4TNgLNEPCJYo2s75gB1ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM -9OVFR/R4AAABbQrFil4AAAQDAEYwRAIgNSpvz/1JA2aP6fh6ujGNuYfrAvWjlxXo -CJtVGe4XaDYCIGmK1/9tl1uQbVD46P5NswnULq06KQmuOrlI3HO4r86HMA0GCSqG -SIb3DQEBCwUAA4IBAQBiAlV7wkCsWE99VmZHBmcbZChWyWUHG3LM1hnaQRQjTSYk -CIlauCpWzlUd6weuvra85KqBbCYo+1hxbwITI796uAdgtHmBE8nj0VltHwKeSq2s -KKiGXBRT7Z7t0VHYSLOlGOVn1auuQFaWBArc0cQ/m1ZsoHvOiHTlKQvVsA4HnIxA -CjGY9OOQoh0c36ecbJZ44XKnU9J/OXtDx00aW6QodaZmgMp/OOCghFQUvufkgTUL -LZid873/8dJVWjAaj1VdadO1nSbdAfBbeWXy93+vg1aAoig80RoscrzYCaNlwmR7 -EO5zWxL3l+xUZogQSJuICgUgNzVB3wjn8HeHGsqt ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_sequencer/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_sequencer/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_sequencer/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_sequencer/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,399 +0,0 @@ -/* - * lws-api-test-lws_sequencer - * - * Written in 2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This api test uses the lws_sequencer api to make five http client requests - * to libwebsockets.org in sequence, from inside the event loop. The fourth - * fourth http client request is directed to port 22 where it stalls - * triggering the lws_sequencer timeout flow. The fifth is given a nonexistant - * dns name and is expected to fail. - */ - -#include - -#include - -static int interrupted, test_good = 0; - -enum { - SEQ1, - SEQ2, - SEQ3_404, - SEQ4_TIMEOUT, /* we expect to timeout */ - SEQ5_BAD_ADDRESS /* we expect the connection to fail */ -}; - -/* - * This is the user defined struct whose space is allocated along with the - * sequencer when that is created. - * - * You'd put everything your sequencer needs to do its job in here. - */ - -struct myseq { - struct lws_vhost *vhost; - struct lws *cwsi; /* client wsi for current step if any */ - - int state; /* which test we're on */ - int http_resp; -}; - -/* sequencer messages specific to this sequencer */ - -enum { - SEQ_MSG_CLIENT_FAILED = LWSSEQ_USER_BASE, - SEQ_MSG_CLIENT_DONE, -}; - -/* this is the sequence of GETs we will do */ - -static const char *url_paths[] = { - "https://libwebsockets.org/index.html", - "https://libwebsockets.org/lws.css", - "https://libwebsockets.org/404.html", - "https://libwebsockets.org:22", /* this causes us to time out */ - "https://doesntexist.invalid/" /* fail early in connect */ -}; - - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -/* - * This is the sequencer-aware http protocol handler. It monitors the client - * http action and queues messages for the sequencer when something definitive - * happens. - */ - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - struct myseq *s = (struct myseq *)user; - int seq_msg = SEQ_MSG_CLIENT_FAILED; - - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_notice("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - goto notify; - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - if (!s) - return 1; - s->http_resp = lws_http_client_http_response(wsi); - lwsl_info("Connected with server response: %d\n", s->http_resp); - break; - - /* chunks of chunked content, with header removed */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - lwsl_info("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); -#if 0 /* enable to dump the html */ - { - const char *p = in; - - while (len--) - if (*p < 0x7f) - putchar(*p++); - else - putchar('.'); - } -#endif - return 0; /* don't passthru */ - - /* uninterpreted http content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - { - char buffer[1024 + LWS_PRE]; - char *px = buffer + LWS_PRE; - int lenx = sizeof(buffer) - LWS_PRE; - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - return 0; /* don't passthru */ - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_notice("LWS_CALLBACK_COMPLETED_CLIENT_HTTP: wsi %p\n", - wsi); - if (!s) - return 1; - /* - * We got a definitive transaction completion - */ - seq_msg = SEQ_MSG_CLIENT_DONE; - goto notify; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - lwsl_info("LWS_CALLBACK_CLOSED_CLIENT_HTTP\n"); - if (!s) - return 1; - - lwsl_user("%s: wsi %p: seq failed at CLOSED_CLIENT_HTTP\n", - __func__, wsi); - goto notify; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); - -notify: - /* - * We only inform the sequencer of a definitive outcome for our step. - * - * So once we have informed it, we detach ourselves from the sequencer - * and the sequencer from ourselves. Wsi may want to live on but after - * we got our result and moved on to the next test or completed, the - * sequencer doesn't want to hear from it again. - */ - if (!s) - return 1; - - lws_set_wsi_user(wsi, NULL); - s->cwsi = NULL; - lws_seq_queue_event(lws_seq_from_user(s), seq_msg, - NULL, NULL); - - return 0; -} - -static const struct lws_protocols protocols[] = { - { "seq-test-http", callback_http, 0, 0, }, - { NULL, NULL, 0, 0 } -}; - - -static int -sequencer_start_client(struct myseq *s) -{ - struct lws_client_connect_info i; - const char *prot, *path1; - char uri[128], path[128]; - int n; - - lws_strncpy(uri, url_paths[s->state], sizeof(uri)); - - memset(&i, 0, sizeof i); - i.context = lws_seq_get_context(lws_seq_from_user(s)); - - if (lws_parse_uri(uri, &prot, &i.address, &i.port, &path1)) { - lwsl_err("%s: uri error %s\n", __func__, uri); - } - - if (!strcmp(prot, "https")) - i.ssl_connection = LCCSCF_USE_SSL; - - path[0] = '/'; - n = 1; - if (path1[0] == '/') - n = 0; - lws_strncpy(&path[n], path1, sizeof(path) - 1); - - i.path = path; - i.host = i.address; - i.origin = i.address; - i.method = "GET"; - i.vhost = s->vhost; - i.userdata = s; - - i.protocol = protocols[0].name; - i.local_protocol_name = protocols[0].name; - i.pwsi = &s->cwsi; - - if (!lws_client_connect_via_info(&i)) { - lwsl_notice("%s: connecting to %s://%s:%d%s failed\n", - __func__, prot, i.address, i.port, path); - - /* we couldn't even get started with the client connection */ - - lws_seq_queue_event(lws_seq_from_user(s), - SEQ_MSG_CLIENT_FAILED, NULL, NULL); - - return 1; - } - - lws_seq_timeout_us(lws_seq_from_user(s), 3 * LWS_US_PER_SEC); - - lwsl_notice("%s: wsi %p: connecting to %s://%s:%d%s\n", __func__, - s->cwsi, prot, i.address, i.port, path); - - return 0; -} - -/* - * The sequencer callback handles queued sequencer messages in the order they - * were queued. The messages are presented from the event loop thread context - * even if they were queued from a different thread. - */ - -static lws_seq_cb_return_t -sequencer_cb(struct lws_sequencer *seq, void *user, int event, - void *data, void *aux) -{ - struct myseq *s = (struct myseq *)user; - - switch ((int)event) { - case LWSSEQ_CREATED: /* our sequencer just got started */ - s->state = SEQ1; /* first thing we'll do is the first url */ - goto step; - - case LWSSEQ_DESTROYED: - /* - * This sequencer is about to be destroyed. If we have any - * other assets in play, detach them from us. - */ - if (s->cwsi) - lws_set_wsi_user(s->cwsi, NULL); - - interrupted = 1; - break; - - case LWSSEQ_TIMED_OUT: /* current step timed out */ - if (s->state == SEQ4_TIMEOUT) { - lwsl_user("%s: test %d got expected timeout\n", - __func__, s->state); - goto done; - } - lwsl_user("%s: seq timed out at step %d\n", __func__, s->state); - return LWSSEQ_RET_DESTROY; - - case SEQ_MSG_CLIENT_FAILED: - if (s->state == SEQ5_BAD_ADDRESS) { - /* - * in this specific case, we expect to fail - */ - lwsl_user("%s: test %d failed as expected\n", - __func__, s->state); - goto done; - } - - lwsl_user("%s: seq failed at step %d\n", __func__, s->state); - - return LWSSEQ_RET_DESTROY; - - case SEQ_MSG_CLIENT_DONE: - if (s->state >= SEQ4_TIMEOUT) { - /* - * In these specific cases, done would be a failure, - * we expected to timeout or fail - */ - lwsl_user("%s: seq failed at step %d\n", __func__, - s->state); - - return LWSSEQ_RET_DESTROY; - } - lwsl_user("%s: seq done step %d (resp %d)\n", __func__, - s->state, s->http_resp); - -done: - lws_seq_timeout_us(lws_seq_from_user(s), LWSSEQTO_NONE); - s->state++; - if (s->state == LWS_ARRAY_SIZE(url_paths)) { - /* the sequence has completed */ - lwsl_user("%s: sequence completed OK\n", __func__); - - test_good = 1; - - return LWSSEQ_RET_DESTROY; - } - -step: - sequencer_start_client(s); - break; - default: - break; - } - - return LWSSEQ_RET_CONTINUE; -} - -int -main(int argc, const char **argv) -{ - int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_context_creation_info info; - struct lws_context *context; - struct lws_sequencer *seq; - struct lws_vhost *vh; - lws_seq_info_t i; - struct myseq *s; - const char *p; - - /* the normal lws init */ - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS API selftest: lws_sequencer\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_EXPLICIT_VHOSTS; - info.protocols = protocols; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./libwebsockets.org.cer"; -#endif - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - vh = lws_create_vhost(context, &info); - if (!vh) { - lwsl_err("Failed to create first vhost\n"); - goto bail1; - } - - /* - * Create the sequencer... when the event loop starts, it will - * receive the LWSSEQ_CREATED callback - */ - - memset(&i, 0, sizeof(i)); - i.context = context; - i.user_size = sizeof(struct myseq); - i.puser = (void **)&s; - i.cb = sequencer_cb; - i.name = "seq"; - - seq = lws_seq_create(&i); - if (!seq) { - lwsl_err("%s: unable to create sequencer\n", __func__); - goto bail1; - } - s->vhost = vh; - - /* the usual lws event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail1: - lwsl_user("Completed: %s\n", !test_good ? "FAIL" : "PASS"); - - lws_context_destroy(context); - - return !test_good; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct-json/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct-json/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct-json/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct-json/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-api-test-lws_struct-json) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-lws_struct-json) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_STRUCT_JSON 1 requirements) - -if (requirements) - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct-json/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct-json/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct-json/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct-json/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,494 +0,0 @@ -/* - * lws-api-test-lws_struct-json - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * lws_struct apis are used to serialize and deserialize your C structs and - * linked-lists in a standardized way that's very modest on memory but - * convenient and easy to maintain. - * - * The API test shows how to serialize and deserialize a struct with a linked- - * list of child structs in JSON using lws_struct APIs. - */ - -#include - -/* - * in this example, the JSON is for one "builder" object, which may specify - * a child list "targets" of zero or more "target" objects. - */ - -static const char * const json_tests[] = { - "{" /* test 1 */ - "\"schema\":\"com-warmcat-sai-builder\"," - - "\"hostname\":\"learn\"," - "\"nspawn_timeout\":1800," - "\"targets\":[" - "{" - "\"name\":\"target1\"," - "\"someflag\":true" - "}," - "{" - "\"name\":\"target2\"," - "\"someflag\":false" - "}" - "]" - "}", - "{" /* test 2 */ - "\"schema\":\"com-warmcat-sai-builder\"," - - "\"hostname\":\"learn\"," - "\"targets\":[" - "{" - "\"name\":\"target1\"" - "}," - "{" - "\"name\":\"target2\"" - "}," - "{" - "\"name\":\"target3\"" - "}" - "]" - "}", "{" /* test 3 */ - "\"schema\":\"com-warmcat-sai-builder\"," - - "\"hostname\":\"learn\"," - "\"nspawn_timeout\":1800," - "\"targets\":[" - "{" - "\"name\":\"target1\"," - "\"unrecognized\":\"xyz\"," - "\"child\": {" - "\"somename\": \"abc\"," - "\"junk\": { \"x\": \"y\" }" - "}" - "}," - "{" - "\"name\":\"target2\"" - "}" - "]" - "}", - "{" /* test 4 */ - "\"schema\":\"com-warmcat-sai-builder\"," - - "\"hostname\":\"learn\"," - "\"nspawn_timeout\":1800" - "}", - "{" /* test 5 */ - "\"schema\":\"com-warmcat-sai-builder\"" - "}", - "{" /* test 6 ... check huge strings into smaller fixed char array */ - "\"schema\":\"com-warmcat-sai-builder\"," - "\"hostname\":\"" - "PYvtan6kqppjnS0KpYTCaiOLsJkc7XecAr1kcE0aCIciewYB+JcLG82mO1Vb1mJtjDwUjBxy2I6A" - "zefzoWUWmqZbsv4MXR55j9bKlyz1liiSX63iO0x6JAwACMtE2MkgcLwR86TSWAD9D1QKIWqg5RJ/" - "CRuVsW0DKAUMD52ql4JmPFuJpJgTq28z6PhYNzN3yI3bmQt6bzhA+A/xAsFzSBnb3MHYWzGMprr5" - "3FAP1ISo5Ec9i+2ehV40sG6Q470sH3PGQZ0YRPO7Sh/SyrSQ/scONmxRc3AcXl7X/CSs417ii+CV" - "8sq3ZgcxKNB7tNfN7idNx3upZ00G2BZy9jSy03cLKKLNaNUt0TQsxXbH55uDHzSEeZWvxJgT6zB1" - "NoMhdC02w+oXim94M6z6COCnqT3rgkGk8PHMry9Bkh4yVpRmzIRfMmln/lEhdZgxky2+g5hhlSIG" - "JYDCrdynD9kCfvfy6KGOpNIi1X+mhbbWn4lnL9ZKihL/RrfOV+oV4R26IDq+KqUiJBENeo8/GXkG" - "LUH/87iPyzXKEMavr6fkrK0vTGto8yEYxmOyaVz8phG5rwf4jJgmYNoMbGo8gWvhqO7UAGy2g7MW" - "v+B/t1eZZ+1euLsNrWAsFJiFbQKgdFfQT3RjB14iU8knlQ8usoy+pXssY2ddGJGVcGC21oZvstK9" - "eu1eRZftda/wP+N5unT1Hw7kCoVzqxHieiYt47EGIOaaQ7XjZDK6qPN6O/grHnvJZm2vBkxuXgsY" - "VkRQ7AuTWIecphqFsq7Wbc1YNbMW47SVU5zMD0WaCqbaaI0t4uIzRvPlD8cpiiTzFTrEHlIBTf8/" - "uZjjEGGLhJR1jPqA9D1Ej3ChV+ye6F9JTUMlozRMsGuF8U4btDzH5xdnmvRS4Ar6LKEtAXGkj2yu" - "yJln+v4RIWj2xOGPJovOqiXwi0FyM61f8U8gj0OiNA2/QlvrqQVDF7sMXgjvaE7iQt5vMETteZlx" - "+z3f+jTFM/aon511W4+ZkRD+6AHwucvM9BEC\"" - "}", - "{" /* test 7 ... check huge strings into char * */ - "\"schema\":\"com-warmcat-sai-builder\"," - "\"targets\":[" - "{" - "\"name\":\"" - "PYvtan6kqppjnS0KpYTCaiOLsJkc7XecAr1kcE0aCIciewYB+JcLG82mO1Vb1mJtjDwUjBxy2I6A" - "zefzoWUWmqZbsv4MXR55j9bKlyz1liiSX63iO0x6JAwACMtE2MkgcLwR86TSWAD9D1QKIWqg5RJ/" - "CRuVsW0DKAUMD52ql4JmPFuJpJgTq28z6PhYNzN3yI3bmQt6bzhA+A/xAsFzSBnb3MHYWzGMprr5" - "3FAP1ISo5Ec9i+2ehV40sG6Q470sH3PGQZ0YRPO7Sh/SyrSQ/scONmxRc3AcXl7X/CSs417ii+CV" - "8sq3ZgcxKNB7tNfN7idNx3upZ00G2BZy9jSy03cLKKLNaNUt0TQsxXbH55uDHzSEeZWvxJgT6zB1" - "NoMhdC02w+oXim94M6z6COCnqT3rgkGk8PHMry9Bkh4yVpRmzIRfMmln/lEhdZgxky2+g5hhlSIG" - "JYDCrdynD9kCfvfy6KGOpNIi1X+mhbbWn4lnL9ZKihL/RrfOV+oV4R26IDq+KqUiJBENeo8/GXkG" - "LUH/87iPyzXKEMavr6fkrK0vTGto8yEYxmOyaVz8phG5rwf4jJgmYNoMbGo8gWvhqO7UAGy2g7MW" - "v+B/t1eZZ+1euLsNrWAsFJiFbQKgdFfQT3RjB14iU8knlQ8usoy+pXssY2ddGJGVcGC21oZvstK9" - "eu1eRZftda/wP+N5unT1Hw7kCoVzqxHieiYt47EGIOaaQ7XjZDK6qPN6O/grHnvJZm2vBkxuXgsY" - "VkRQ7AuTWIecphqFsq7Wbc1YNbMW47SVU5zMD0WaCqbaaI0t4uIzRvPlD8cpiiTzFTrEHlIBTf8/" - "uZjjEGGLhJR1jPqA9D1Ej3ChV+ye6F9JTUMlozRMsGuF8U4btDzH5xdnmvRS4Ar6LKEtAXGkj2yu" - "yJln+v4RIWj2xOGPJovOqiXwi0FyM61f8U8gj0OiNA2/QlvrqQVDF7sMXgjvaE7iQt5vMETteZlx" - "+z3f+jTFM/aon511W4+ZkRD+6AHwucvM9BEC\"}]}" - "}", - "{" /* test 8 the "other" schema */ - "\"schema\":\"com-warmcat-sai-other\"," - "\"name\":\"somename\"" - "}", -}; - -/* - * These are the expected outputs for each test, without pretty formatting. - * - * There are some differences to do with missing elements being rendered with - * default values. - */ - -static const char * const json_expected[] = { - "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":\"learn\"," - "\"nspawn_timeout\":1800,\"targets\":[{\"name\":\"target1\",\"someflag\":true}," - "{\"name\":\"target2\",\"someflag\":false}]}", - - "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":\"learn\"," - "\"nspawn_timeout\":0,\"targets\":[{\"name\":\"target1\",\"someflag\":false}," - "{\"name\":\"target2\",\"someflag\":false},{\"name\":\"target3\",\"someflag\":false}]}", - - "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":\"learn\"," - "\"nspawn_timeout\":1800,\"targets\":[{\"name\":\"target1\",\"someflag\":false," - "\"child\":{\"somename\":\"abc\"}},{\"name\":\"target2\",\"someflag\":false}]}", - - "{\"schema\":\"com-warmcat-sai-builder\"," - "\"hostname\":\"learn\",\"nspawn_timeout\":1800}", - - "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":\"\"," - "\"nspawn_timeout\":0}", - - "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":" - "\"PYvtan6kqppjnS0KpYTCaiOLsJkc7Xe\"," - "\"nspawn_timeout\":0}", - - "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":\"\"," - "\"nspawn_timeout\":0,\"targets\":[{\"name\":\"PYvtan6kqppjnS0KpYTC" - "aiOLsJkc7XecAr1kcE0aCIciewYB+JcLG82mO1Vb1mJtjDwUjBxy2I6Azefz" - "oWUWmqZbsv4MXR55j9bKlyz1liiSX63iO0x6JAwACMtE2MkgcLwR86TSWAD9" - "D1QKIWqg5RJ/CRuVsW0DKAUMD52ql4JmPFuJpJgTq28z6PhYNzN3yI3bmQt6" - "bzhA+A/xAsFzSBnb3MHYWzGMprr53FAP1ISo5Ec9i+2ehV40sG6Q470sH3PG" - "QZ0YRPO7Sh/SyrSQ/scONmxRc3AcXl7X/CSs417ii+CV8sq3ZgcxKNB7tNfN" - "7idNx3upZ00G2BZy9jSy03cLKKLNaNUt0TQsxXbH55uDHzSEeZWvxJgT6zB1" - "NoMhdC02w+oXim94M6z6COCnqT3rgkGk8PHMry9Bkh4yVpRmzIRfMmln/lEh" - "dZgxky2+g5hhlSIGJYDCrdynD9kCfvfy6KGOpNIi1X+mhbbWn4lnL9ZKihL/" - "RrfOV+oV4R26IDq+KqUiJBENeo8/GXkGLUH/87iPyzXKEMavr6fkrK0vTGto" - "8yEYxmOyaVz8phG5rwf4jJgmYNoMbGo8gWvhqO7UAGy2g7MWv+B/t1eZZ+1e" - "uLsNrWAsFJiFbQKgdFfQT3RjB14iU8knlQ8usoy+pXssY2ddGJGVcGC21oZv" - "stK9eu1eRZftda/wP+N5unT1Hw7kCoVzqxHieiYt47EGIOaaQ7XjZDK6qPN6" - "O/grHnvJZm2vBkxuXgsYVkRQ7AuTWIecphqFsq7Wbc1YNbMW47SVU5zMD0Wa" - "CqbaaI0t4uIzRvPlD8cpiiTzFTrEHlIBTf8/uZjjEGGLhJR1jPqA9D1Ej3Ch" - "V+ye6F9JTUMlozRMsGuF8U4btDzH5xdnmvRS4Ar6LKEtAXGkj2yuyJln+v4R" - "IWj2xOGPJovOqiXwi0FyM61f8U8gj0OiNA2/QlvrqQVDF7sMXgjvaE7iQt5v" - "METteZlx+z3f+jTFM/aon511W4+ZkRD+6AHwucvM9BEC\"" - ",\"someflag\":false}]}", - "{\"schema\":\"com-warmcat-sai-other\",\"name\":\"somename\"}" -}; - -/* - * These annotate the members in the struct that will be serialized and - * deserialized with type and size information, as well as the name to use - * in the serialization format. - * - * Struct members that aren't annotated like this won't be serialized and - * when the struct is created during deserialiation, the will be set to 0 - * or NULL. - */ - -/* child object */ - -typedef struct sai_child { - const char * somename; -} sai_child_t; - -lws_struct_map_t lsm_child[] = { /* describes serializable members */ - LSM_STRING_PTR (sai_child_t, somename, "somename"), -}; - -/* target object */ - -typedef struct sai_target { - struct lws_dll2 target_list; - sai_child_t * child; - - const char * name; - char someflag; -} sai_target_t; - -static const lws_struct_map_t lsm_target[] = { - LSM_STRING_PTR (sai_target_t, name, "name"), - LSM_BOOLEAN (sai_target_t, someflag, "someflag"), - LSM_CHILD_PTR (sai_target_t, child, sai_child_t, - NULL, lsm_child, "child"), -}; - -/* the first kind of struct / schema we can receive */ - -/* builder object */ - -typedef struct sai_builder { - struct lws_dll2_owner targets; - - char hostname[32]; - unsigned int nspawn_timeout; -} sai_builder_t; - -static const lws_struct_map_t lsm_builder[] = { - LSM_CARRAY (sai_builder_t, hostname, "hostname"), - LSM_UNSIGNED (sai_builder_t, nspawn_timeout, "nspawn_timeout"), - LSM_LIST (sai_builder_t, targets, - sai_target_t, target_list, - NULL, lsm_target, "targets"), -}; - -/* - * the second kind of struct / schema we can receive - */ - -typedef struct sai_other { - char name[32]; -} sai_other_t; - -static const lws_struct_map_t lsm_other[] = { - LSM_CARRAY (sai_other_t, name, "name"), -}; - -/* - * meta composed pointers test - * - * We serialize a struct that consists of members that point to other objects, - * we expect this kind of thing - * - * { - * "schema": "meta", - * "t": { ... }, - * "e": { ...} - * } - */ - -typedef struct meta { - sai_target_t *t; - sai_builder_t *b; -} meta_t; - -static const lws_struct_map_t lsm_meta[] = { - LSM_CHILD_PTR (meta_t, t, sai_target_t, NULL, lsm_target, "t"), - LSM_CHILD_PTR (meta_t, b, sai_child_t, NULL, lsm_builder, "e"), -}; - -static const lws_struct_map_t lsm_schema_meta[] = { - LSM_SCHEMA (meta_t, NULL, lsm_meta, "meta.schema"), -}; - -/* - * Schema table - * - * Before we can understand the serialization top level format, we must read - * the schema, use the table below to create the right toplevel object for the - * schema name, and select the correct map tables to interpret the rest of the - * serialization. - * - * In this example there are two completely separate structs / schemas possible - * to receive, and we disambiguate and create the correct one using the schema - * JSON node. - * - * Therefore the schema table below is the starting point for the JSON - * deserialization. - */ - -static const lws_struct_map_t lsm_schema_map[] = { - LSM_SCHEMA (sai_builder_t, NULL, - lsm_builder, "com-warmcat-sai-builder"), - LSM_SCHEMA (sai_other_t, NULL, - lsm_other, "com-warmcat-sai-other"), -}; - -static int -show_target(struct lws_dll2 *d, void *user) -{ - sai_target_t *t = lws_container_of(d, sai_target_t, target_list); - - lwsl_notice(" target.name '%s' (target %p)\n", t->name, t); - - if (t->child) - lwsl_notice(" child %p, target.child.somename '%s'\n", - t->child, t->child->somename); - - return 0; -} - - -int main(int argc, const char **argv) -{ - int n, m, e = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; -#if 1 - lws_struct_serialize_t *ser; - uint8_t buf[4096]; - size_t written; -#endif - struct lejp_ctx ctx; - lws_struct_args_t a; - sai_builder_t *b, mb; - sai_target_t mt; - sai_other_t *o; - const char *p; - meta_t meta; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS API selftest: lws_struct JSON\n"); - - for (m = 0; m < (int)LWS_ARRAY_SIZE(json_tests); m++) { - - /* 1. deserialize the canned JSON into structs */ - - lwsl_notice("%s: ++++++++++++++++ test %d\n", __func__, m + 1); - - memset(&a, 0, sizeof(a)); - a.map_st[0] = lsm_schema_map; - a.map_entries_st[0] = LWS_ARRAY_SIZE(lsm_schema_map); - a.ac_block_size = 512; - - lws_struct_json_init_parse(&ctx, NULL, &a); - n = (int)(signed char)lejp_parse(&ctx, (uint8_t *)json_tests[m], - strlen(json_tests[m])); - if (n < 0) { - lwsl_err("%s: notification JSON decode failed '%s'\n", - __func__, lejp_error_to_string(n)); - e++; - goto done; - } - lwsac_info(a.ac); - - if (m + 1 != 8) { - b = a.dest; - if (!b) { - lwsl_err("%s: didn't produce any output\n", __func__); - e++; - goto done; - } - - if (a.top_schema_index) { - lwsl_err("%s: wrong top_schema_index\n", __func__); - e++; - goto done; - } - - lwsl_notice("builder.hostname = '%s', timeout = %d, targets (%d)\n", - b->hostname, b->nspawn_timeout, - b->targets.count); - - lws_dll2_foreach_safe(&b->targets, NULL, show_target); - } else { - o = a.dest; - if (!o) { - lwsl_err("%s: didn't produce any output\n", __func__); - e++; - goto done; - } - - if (a.top_schema_index != 1) { - lwsl_err("%s: wrong top_schema_index\n", __func__); - e++; - goto done; - } - - lwsl_notice("other.name = '%s'\n", o->name); - } - - /* 2. serialize the structs into JSON and confirm */ - - lwsl_notice("%s: .... strarting serialization of test %d\n", - __func__, m + 1); - - if (m + 1 != 8) { - ser = lws_struct_json_serialize_create(lsm_schema_map, - LWS_ARRAY_SIZE(lsm_schema_map), - 0//LSSERJ_FLAG_PRETTY - , b); - } else { - ser = lws_struct_json_serialize_create(&lsm_schema_map[1], - 1, - 0//LSSERJ_FLAG_PRETTY - , o); - } - if (!ser) { - lwsl_err("%s: unable to init serialization\n", __func__); - goto bail; - } - - do { - n = lws_struct_json_serialize(ser, buf, sizeof(buf), - &written); - switch (n) { - case LSJS_RESULT_FINISH: - puts((const char *)buf); - break; - case LSJS_RESULT_CONTINUE: - case LSJS_RESULT_ERROR: - goto bail; - } - } while(n == LSJS_RESULT_CONTINUE); - - if (strcmp(json_expected[m], (char *)buf)) { - lwsl_err("%s: test %d: expected %s\n", __func__, m + 1, - json_expected[m]); - e++; - goto done; - } - - lws_struct_json_serialize_destroy(&ser); - -done: - lwsac_free(&a.ac); - } - - if (e) - goto bail; - - /* ad-hoc tests */ - - memset(&meta, 0, sizeof(meta)); - memset(&mb, 0, sizeof(mb)); - memset(&mt, 0, sizeof(mt)); - - meta.t = &mt; - meta.b = &mb; - - meta.t->name = "mytargetname"; - lws_strncpy(meta.b->hostname, "myhostname", sizeof(meta.b->hostname)); - ser = lws_struct_json_serialize_create(lsm_schema_meta, 1, 0, - &meta); - if (!ser) { - lwsl_err("%s: failed to create json\n", __func__); - - - } - do { - n = lws_struct_json_serialize(ser, buf, sizeof(buf), &written); - switch (n) { - case LSJS_RESULT_CONTINUE: - case LSJS_RESULT_FINISH: - puts((const char *)buf); - if (strcmp((const char *)buf, - "{\"schema\":\"meta.schema\"," - "\"t\":{\"name\":\"mytargetname\"," - "\"someflag\":false}," - "\"e\":{\"hostname\":\"myhostname\"," - "\"nspawn_timeout\":0}}")) { - lwsl_err("%s: meta test fail\n", __func__); - goto bail; - } - break; - case LSJS_RESULT_ERROR: - goto bail; - } - } while(n == LSJS_RESULT_CONTINUE); - - lws_struct_json_serialize_destroy(&ser); - - - lwsl_user("Completed: PASS\n"); - - return 0; - -bail: - lwsl_user("Completed: FAIL\n"); - - return 1; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct-json/README.md libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct-json/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct-json/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct-json/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -# lws api test lws_struct JSON - -Demonstrates how to use and performs selftests for lws_struct -JSON serialization and deserialization - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 - -``` - $ ./lws-api-test-lws_struct-json -[2019/03/30 22:09:09:2529] USER: LWS API selftest: lws_struct JSON -[2019/03/30 22:09:09:2625] NOTICE: main: ++++++++++++++++ test 1 -[2019/03/30 22:09:09:2812] NOTICE: builder.hostname = 'learn', timeout = 1800, targets (2) -[2019/03/30 22:09:09:2822] NOTICE: target.name 'target1' (target 0x543a830) -[2019/03/30 22:09:09:2824] NOTICE: target.name 'target2' (target 0x543a860) -[2019/03/30 22:09:09:2826] NOTICE: main: .... strarting serialization of test 1 -[2019/03/30 22:09:09:2899] NOTICE: ser says 1 -{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":1800,"targets":[{"name":"target1"},{"name":"target2"}]} -[2019/03/30 22:09:09:2929] NOTICE: main: ++++++++++++++++ test 2 -[2019/03/30 22:09:09:2932] NOTICE: builder.hostname = 'learn', timeout = 0, targets (3) -[2019/03/30 22:09:09:2932] NOTICE: target.name 'target1' (target 0x543b060) -[2019/03/30 22:09:09:2933] NOTICE: target.name 'target2' (target 0x543b090) -[2019/03/30 22:09:09:2933] NOTICE: target.name 'target3' (target 0x543b0c0) -[2019/03/30 22:09:09:2934] NOTICE: main: .... strarting serialization of test 2 -[2019/03/30 22:09:09:2935] NOTICE: ser says 1 -{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":0,"targets":[{"name":"target1"},{"name":"target2"},{"name":"target3"}]} -[2019/03/30 22:09:09:2940] NOTICE: main: ++++++++++++++++ test 3 -[2019/03/30 22:09:09:2959] NOTICE: builder.hostname = 'learn', timeout = 1800, targets (2) -[2019/03/30 22:09:09:2960] NOTICE: target.name 'target1' (target 0x543b450) -[2019/03/30 22:09:09:2961] NOTICE: child 0x543b480, target.child.somename 'abc' -[2019/03/30 22:09:09:2961] NOTICE: target.name 'target2' (target 0x543b490) -[2019/03/30 22:09:09:2962] NOTICE: main: .... strarting serialization of test 3 -[2019/03/30 22:09:09:2969] NOTICE: ser says 1 -{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":1800,"targets":[{"name":"target1","child":{"somename":"abc"}},{"name":"target2"}]} -[2019/03/30 22:09:09:2970] NOTICE: main: ++++++++++++++++ test 4 -[2019/03/30 22:09:09:2971] NOTICE: builder.hostname = 'learn', timeout = 1800, targets (0) -[2019/03/30 22:09:09:2971] NOTICE: main: .... strarting serialization of test 4 -[2019/03/30 22:09:09:2973] NOTICE: ser says 1 -{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":1800} -[2019/03/30 22:09:09:2974] NOTICE: main: ++++++++++++++++ test 5 -[2019/03/30 22:09:09:2978] NOTICE: builder.hostname = '', timeout = 0, targets (0) -[2019/03/30 22:09:09:2979] NOTICE: main: .... strarting serialization of test 5 -[2019/03/30 22:09:09:2980] NOTICE: ser says 1 -{"schema":"com-warmcat-sai-builder","hostname":"","nspawn_timeout":0} -[2019/03/30 22:09:09:2982] USER: Completed: PASS -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct-json/selftest.sh libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct-json/selftest.sh --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct-json/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct-json/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=1 - -dotest $1 $2 apiselftest -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct_sqlite/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct_sqlite/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct_sqlite/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct_sqlite/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-api-test-lws_struct-sqlite) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-lws_struct-sqlite) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_STRUCT_SQLITE 1 requirements) - -if (requirements) - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared sqlite3) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets sqlite3) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct_sqlite/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct_sqlite/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct_sqlite/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct_sqlite/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,202 +0,0 @@ -/* - * lws-api-test-lws_struct-sqlite - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * lws_struct apis are used to serialize and deserialize your C structs and - * linked-lists in a standardized way that's very modest on memory but - * convenient and easy to maintain. - * - * The API test shows how to serialize and deserialize a struct with a linked- - * list of child structs in JSON using lws_struct APIs. - */ - -#include - -typedef struct teststruct { - lws_dll2_t list; /* not directly serialized */ - - char str1[32]; - const char *str2; - uint8_t u8; - uint16_t u16; - uint32_t u32; - uint64_t u64; - int32_t s32; -} teststruct_t; - -/* - * These are the members that we will serialize and deserialize, not every - * member in the struct (eg, the dll2 list member) - */ - -static const lws_struct_map_t lsm_teststruct[] = { - LSM_CARRAY (teststruct_t, str1, "str1"), - LSM_STRING_PTR (teststruct_t, str2, "str2"), - LSM_UNSIGNED (teststruct_t, u8, "u8"), - LSM_UNSIGNED (teststruct_t, u16, "u16"), - LSM_UNSIGNED (teststruct_t, u32, "u32"), - LSM_UNSIGNED (teststruct_t, u64, "u64"), - LSM_SIGNED (teststruct_t, s32, "s32"), -}; - -static const lws_struct_map_t lsm_schema_apitest[] = { - LSM_SCHEMA_DLL2 (teststruct_t, list, NULL, lsm_teststruct, "apitest") -}; - -static const char *test_string = - "No one would have believed in the last years of the nineteenth " - "century that this world was being watched keenly and closely by " - "intelligences greater than man's and yet as mortal as his own; that as " - "men busied themselves about their various concerns they were " - "scrutinised and studied, perhaps almost as narrowly as a man with a " - "microscope might scrutinise the transient creatures that swarm and " - "multiply in a drop of water. With infinite complacency men went to " - "and fro over this globe about their little affairs, serene in their " - "assurance of their empire over matter. It is possible that the " - "infusoria under the microscope do the same. No one gave a thought to " - "the older worlds of space as sources of human danger, or thought of " - "them only to dismiss the idea of life upon them as impossible or " - "improbable. It is curious to recall some of the mental habits of " - "those departed days. At most terrestrial men fancied there might be " - "other men upon Mars, perhaps inferior to themselves and ready to " - "welcome a missionary enterprise. Yet across the gulf of space, minds " - "that are to our minds as ours are to those of the beasts that perish, " - "intellects vast and cool and unsympathetic, regarded this earth with " - "envious eyes, and slowly and surely drew their plans against us. And " - "early in the twentieth century came the great disillusionment. "; - -int main(int argc, const char **argv) -{ - int e = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_context_creation_info info; - struct lws_context *context; - struct lwsac *ac = NULL; - lws_dll2_owner_t resown; - teststruct_t ts, *pts; - const char *p; - sqlite3 *db; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS API selftest: lws_struct SQLite\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - - unlink("_lws_apitest.sq3"); - - if (lws_struct_sq3_open(context, "_lws_apitest.sq3", &db)) { - lwsl_err("%s: failed to open table\n", __func__); - goto bail; - } - - /* 1. populate the struct */ - - memset(&ts, 0, sizeof(ts)); - - lws_strncpy(ts.str1, "hello", sizeof(ts.str1)); - ts.str2 = test_string; - ts.u8 = 1; - ts.u16 = 512, - ts.u32 = 0x55aa1234; /* 1437209140, */ - ts.u64 = 0x34abcdef01ull; - ts.s32 = -1; - - /* add our struct to the dll2 owner list */ - - lws_dll2_owner_clear(&resown); - lws_dll2_add_head(&ts.list, &resown); - - /* gratuitously create the table */ - - if (lws_struct_sq3_create_table(db, lsm_schema_apitest)) { - lwsl_err("%s: Create table failed\n", __func__); - e++; - goto done; - } - - /* serialize the items on the dll2 owner */ - - if (lws_struct_sq3_serialize(db, lsm_schema_apitest, &resown, 0)) { - lwsl_err("%s: Serialize failed\n", __func__); - e++; - goto done; - } - - /* resown should be cleared by deserialize, ac is already NULL */ - - lws_dll2_owner_clear(&resown); /* make sure old resown data is gone */ - - if (lws_struct_sq3_deserialize(db, NULL, NULL, lsm_schema_apitest, - &resown, &ac, 0, 1)) { - lwsl_err("%s: Deserialize failed\n", __func__); - e++; - goto done; - } - - /* we should have 1 entry in resown now (created into the ac) */ - - if (resown.count != 1) { - lwsl_err("%s: Expected 1 result got %d\n", __func__, - resown.count); - e++; - goto done; - } - - /* - * Convert the pointer to the embedded lws_dll2 into a pointer - * to the actual struct with the correct type - */ - - pts = lws_container_of(lws_dll2_get_head(&resown), - teststruct_t, list); - - if (strcmp(pts->str1, "hello") || - strcmp(pts->str2, test_string) || - pts->u8 != 1 || - pts->u16 != 512 || - pts->u32 != 0x55aa1234 || - pts->u64 != 0x34abcdef01ull || - pts->s32 != -1) { - lwsl_err("%s: unexpected deser values: %s\n", __func__, pts->str1); - lwsl_err("%s: %s\n", __func__, pts->str2); - lwsl_err("%s: %u %u %u 0x%llx %d\n", __func__, pts->u8, pts->u16, - pts->u32, (unsigned long long)pts->u64, pts->s32); - - e++; - goto done; - } - -done: - lwsac_free(&ac); - lws_struct_sq3_close(&db); - - - if (e) - goto bail; - - lws_context_destroy(context); - - lwsl_user("Completed: PASS\n"); - - return 0; - -bail: - lws_context_destroy(context); - - lwsl_user("Completed: FAIL\n"); - - return 1; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct_sqlite/README.md libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct_sqlite/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct_sqlite/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct_sqlite/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -# lws api test lws_struct SQLITE - -Demonstrates how to use and performs selftests for lws_struct -SQLITE serialization and deserialization - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 - -``` - $ ./lws-api-test-lws_struct-sqlite -[2020/02/22 09:55:05:4335] U: LWS API selftest: lws_struct SQLite -[2020/02/22 09:55:05:5579] N: lws_struct_sq3_open: created _lws_apitest.sq3 owned by 0:0 mode 0600 -[2020/02/22 09:55:05:9206] U: Completed: PASS - -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct_sqlite/selftest.sh libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct_sqlite/selftest.sh --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_struct_sqlite/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_struct_sqlite/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=1 - -dotest $1 $2 apiselftest -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_tokenize/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_tokenize/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_tokenize/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_tokenize/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -project(lws-api-test-lws_tokenize) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-api-test-lws_tokenize) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_tokenize/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_tokenize/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_tokenize/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_tokenize/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,526 +0,0 @@ -/* - * lws-api-test-lws_tokenize - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -struct expected { - lws_tokenize_elem e; - const char *value; - size_t len; -}; - -struct tests { - const char *string; - struct expected *exp; - int count; - int flags; -}; - -struct expected expected1[] = { - { LWS_TOKZE_TOKEN, "protocol-1", 10 }, - { LWS_TOKZE_DELIMITER, ",", 1}, - { LWS_TOKZE_TOKEN, "protocol_2", 10 }, - { LWS_TOKZE_DELIMITER, ",", 1}, - { LWS_TOKZE_TOKEN, "protocol3", 9 }, - { LWS_TOKZE_ENDED, NULL, 0 }, - }, - expected2[] = { - { LWS_TOKZE_TOKEN_NAME_COLON, "Accept-Language", 15 }, - { LWS_TOKZE_TOKEN, "fr-CH", 5 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_TOKEN, "fr", 2 }, - { LWS_TOKZE_DELIMITER, ";", 1}, - { LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 }, - { LWS_TOKZE_FLOAT, "0.9", 3 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_TOKEN, "en", 2 }, - { LWS_TOKZE_DELIMITER, ";", 1}, - { LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 }, - { LWS_TOKZE_FLOAT, "0.8", 3 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_TOKEN, "de", 2 }, - { LWS_TOKZE_DELIMITER, ";", 1}, - { LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 }, - { LWS_TOKZE_FLOAT, "0.7", 3 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_DELIMITER, "*", 1 }, - { LWS_TOKZE_DELIMITER, ";", 1 }, - { LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 }, - { LWS_TOKZE_FLOAT, "0.5", 3 }, - { LWS_TOKZE_ENDED, NULL, 0 }, - }, - expected3[] = { - { LWS_TOKZE_TOKEN_NAME_EQUALS, "quoted", 6 }, - { LWS_TOKZE_QUOTED_STRING, "things:", 7 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_INTEGER, "1234", 4 }, - { LWS_TOKZE_ENDED, NULL, 0 }, - }, - expected4[] = { - { LWS_TOKZE_ERR_COMMA_LIST, ",", 1 }, - }, - expected5[] = { - { LWS_TOKZE_TOKEN, "brokenlist2", 11 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_ERR_COMMA_LIST, ",", 1 }, - }, - expected6[] = { - { LWS_TOKZE_TOKEN, "brokenlist3", 11 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_ERR_COMMA_LIST, ",", 1 }, - - }, - expected7[] = { - { LWS_TOKZE_TOKEN, "fr", 2 }, - { LWS_TOKZE_DELIMITER, "-", 1 }, - { LWS_TOKZE_TOKEN, "CH", 2 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_TOKEN, "fr", 2 }, - { LWS_TOKZE_DELIMITER, ";", 1 }, - { LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 }, - { LWS_TOKZE_FLOAT, "0.9", 3 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_TOKEN, "en", 2 }, - { LWS_TOKZE_DELIMITER, ";", 1 }, - { LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 }, - { LWS_TOKZE_FLOAT, "0.8", 3 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_TOKEN, "de", 2 }, - { LWS_TOKZE_DELIMITER, ";", 1 }, - { LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 }, - { LWS_TOKZE_FLOAT, "0.7", 3 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_TOKEN, "*", 1 }, - { LWS_TOKZE_DELIMITER, ";", 1 }, - { LWS_TOKZE_TOKEN_NAME_EQUALS, "q", 1 }, - { LWS_TOKZE_FLOAT, "0.5", 3 }, - { LWS_TOKZE_ENDED, "", 0 }, - }, - expected8[] = { - { LWS_TOKZE_TOKEN, "Οὐχὶ", 10 }, - { LWS_TOKZE_TOKEN, "ταὐτὰ", 12 }, - { LWS_TOKZE_TOKEN, "παρίσταταί", 22 }, - { LWS_TOKZE_TOKEN, "μοι", 6 }, - { LWS_TOKZE_TOKEN, "γιγνώσκειν", 21 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_TOKEN, "ὦ", 3 }, - { LWS_TOKZE_TOKEN, "ἄνδρες", 13 }, - { LWS_TOKZE_TOKEN, "᾿Αθηναῖοι", 20 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_TOKEN, "greek", 5 }, - { LWS_TOKZE_ENDED, "", 0 }, - }, - expected9[] = { - /* - * because the tokenizer scans ahead for = aggregation, - * it finds the broken utf8 before reporting the token - */ - { LWS_TOKZE_ERR_BROKEN_UTF8, "", 0 }, - }, - expected10[] = { - { LWS_TOKZE_TOKEN, "badutf8-2", 9 }, - { LWS_TOKZE_TOKEN, "퟿", 3 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_ERR_BROKEN_UTF8, "", 0 }, - }, - expected11[] = { - { LWS_TOKZE_TOKEN, "1.myserver", 10 }, - { LWS_TOKZE_DELIMITER, ".", 1 }, - { LWS_TOKZE_TOKEN, "com", 3 }, - { LWS_TOKZE_ENDED, "", 0 }, - }, - expected12[] = { - { LWS_TOKZE_TOKEN, "1.myserver.com", 14 }, - { LWS_TOKZE_ENDED, "", 0 }, - }, - expected13[] = { - { LWS_TOKZE_TOKEN, "1.myserver.com", 14 }, - { LWS_TOKZE_ENDED, "", 0 }, - }, - expected14[] = { - { LWS_TOKZE_INTEGER, "1", 1 }, - { LWS_TOKZE_DELIMITER, ".", 1 }, - { LWS_TOKZE_TOKEN, "myserver", 8 }, - { LWS_TOKZE_DELIMITER, ".", 1 }, - { LWS_TOKZE_TOKEN, "com", 3 }, - { LWS_TOKZE_ENDED, "", 0 }, - }, - expected15[] = { - { LWS_TOKZE_TOKEN, "close", 5 }, - { LWS_TOKZE_DELIMITER, ",", 1 }, - { LWS_TOKZE_TOKEN, "Upgrade", 7 }, - { LWS_TOKZE_ENDED, "", 0 }, - }, - expected16[] = { - { LWS_TOKZE_TOKEN_NAME_EQUALS, "a", 1 }, - { LWS_TOKZE_TOKEN, "5", 1 }, - { LWS_TOKZE_ENDED, "", 0 }, - }, - expected17[] = { - { LWS_TOKZE_TOKEN, "hello", 5 }, - { LWS_TOKZE_ENDED, "", 0 }, - } -; - -struct tests tests[] = { - { - " protocol-1, protocol_2\t,\tprotocol3\n", - expected1, LWS_ARRAY_SIZE(expected1), - LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_AGG_COLON - }, { - "Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5", - expected2, LWS_ARRAY_SIZE(expected2), - LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_AGG_COLON - }, { - "quoted = \"things:\", 1234", - expected3, LWS_ARRAY_SIZE(expected3), - LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_AGG_COLON - }, { - ", brokenlist1", - expected4, LWS_ARRAY_SIZE(expected4), - LWS_TOKENIZE_F_COMMA_SEP_LIST - }, { - "brokenlist2,,", - expected5, LWS_ARRAY_SIZE(expected5), - LWS_TOKENIZE_F_COMMA_SEP_LIST - }, { - "brokenlist3,", - expected6, LWS_ARRAY_SIZE(expected6), - LWS_TOKENIZE_F_COMMA_SEP_LIST - }, { - "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5", - expected7, LWS_ARRAY_SIZE(expected7), - LWS_TOKENIZE_F_RFC7230_DELIMS - }, - { - " Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι, greek", - expected8, LWS_ARRAY_SIZE(expected8), - LWS_TOKENIZE_F_RFC7230_DELIMS - }, - { - "badutf8-1 \x80...", - expected9, LWS_ARRAY_SIZE(expected9), - LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_RFC7230_DELIMS - }, - { - "badutf8-2 \xed\x9f\xbf,\x80...", - expected10, LWS_ARRAY_SIZE(expected10), - LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_RFC7230_DELIMS - }, - { - "1.myserver.com", - expected11, LWS_ARRAY_SIZE(expected11), - 0 - }, - { - "1.myserver.com", - expected12, LWS_ARRAY_SIZE(expected12), - LWS_TOKENIZE_F_DOT_NONTERM - }, - { - "1.myserver.com", - expected13, LWS_ARRAY_SIZE(expected13), - LWS_TOKENIZE_F_DOT_NONTERM | LWS_TOKENIZE_F_NO_FLOATS - }, - { - "1.myserver.com", - expected14, LWS_ARRAY_SIZE(expected14), - LWS_TOKENIZE_F_NO_FLOATS - }, - { - "close, Upgrade", - expected15, LWS_ARRAY_SIZE(expected15), - LWS_TOKENIZE_F_COMMA_SEP_LIST - }, - { - "a=5", expected16, LWS_ARRAY_SIZE(expected16), - LWS_TOKENIZE_F_NO_INTEGERS - }, - { - "# comment1\r\nhello #comment2\r\n#comment3", expected17, - LWS_ARRAY_SIZE(expected17), LWS_TOKENIZE_F_HASH_COMMENT - } -}; - -/* - * add LWS_TOKZE_ERRS to the element index (which may be negative by that - * amount) to index this array - */ - -static const char *element_names[] = { - "LWS_TOKZE_ERR_BROKEN_UTF8", - "LWS_TOKZE_ERR_UNTERM_STRING", - "LWS_TOKZE_ERR_MALFORMED_FLOAT", - "LWS_TOKZE_ERR_NUM_ON_LHS", - "LWS_TOKZE_ERR_COMMA_LIST", - "LWS_TOKZE_ENDED", - "LWS_TOKZE_DELIMITER", - "LWS_TOKZE_TOKEN", - "LWS_TOKZE_INTEGER", - "LWS_TOKZE_FLOAT", - "LWS_TOKZE_TOKEN_NAME_EQUALS", - "LWS_TOKZE_TOKEN_NAME_COLON", - "LWS_TOKZE_QUOTED_STRING", -}; - - -int -exp_cb1(void *priv, const char *name, char *out, size_t *pos, size_t olen, - size_t *exp_ofs) -{ - const char *replace = NULL; - size_t total, budget; - - if (!strcmp(name, "test")) { - replace = "replacement_string"; - total = strlen(replace); - goto expand; - } - - return LSTRX_FATAL_NAME_UNKNOWN; - -expand: - budget = olen - *pos; - total -= *exp_ofs; - if (total < budget) - budget = total; - - memcpy(out + *pos, replace + (*exp_ofs), budget); - *exp_ofs += budget; - *pos += budget; - - if (budget == total) - return LSTRX_DONE; - - return LSTRX_FILLED_OUT; -} - -static const char *exp_inp1 = "this-is-a-${test}-for-strexp"; - -int main(int argc, const char **argv) -{ - struct lws_tokenize ts; - lws_tokenize_elem e; - const char *p; - int n, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - int fail = 0, ok = 0, flags = 0; - char dotstar[512]; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS API selftest: lws_tokenize\n"); - - if ((p = lws_cmdline_option(argc, argv, "-f"))) - flags = atoi(p); - - /* lws_strexp */ - - { - size_t in_len, used_in, used_out; - lws_strexp_t exp; - char obuf[128]; - const char *p; - - obuf[0] = '\0'; - lws_strexp_init(&exp, NULL, exp_cb1, obuf, sizeof(obuf)); - n = lws_strexp_expand(&exp, exp_inp1, 28, &used_in, &used_out); - if (n != LSTRX_DONE || used_in != 28 || - strcmp(obuf, "this-is-a-replacement_string-for-strexp")) { - lwsl_notice("%s: obuf %s\n", __func__, obuf); - lwsl_err("%s: lws_strexp test 1 failed: %d\n", __func__, n); - - return 1; - } - - p = exp_inp1; - in_len = strlen(p); - memset(obuf, 0, sizeof(obuf)); - lws_strexp_init(&exp, NULL, exp_cb1, obuf, 16); - n = lws_strexp_expand(&exp, p, in_len, &used_in, &used_out); - if (n != LSTRX_FILLED_OUT || used_in != 16 || used_out != 16) { - lwsl_err("a\n"); - return 1; - } - - p += used_in; - in_len -= used_in; - - memset(obuf, 0, sizeof(obuf)); - lws_strexp_reset_out(&exp, obuf, 16); - - n = lws_strexp_expand(&exp, p, in_len, &used_in, &used_out); - if (n != LSTRX_FILLED_OUT || used_in != 5 || used_out != 16) { - lwsl_err("b: n %d, used_in %d, used_out %d\n", n, - (int)used_in, (int)used_out); - return 2; - } - - p += used_in; - in_len -= used_in; - - memset(obuf, 0, sizeof(obuf)); - lws_strexp_reset_out(&exp, obuf, 16); - - n = lws_strexp_expand(&exp, p, in_len, &used_in, &used_out); - if (n != LSTRX_DONE || used_in != 7 || used_out != 7) { - lwsl_err("c: n %d, used_in %d, used_out %d\n", n, (int)used_in, (int)used_out); - return 2; - } - } - - /* sanity check lws_strnncpy() */ - - lws_strnncpy(dotstar, "12345678", 4, sizeof(dotstar)); - if (strcmp(dotstar, "1234")) { - lwsl_err("%s: lws_strnncpy check failed\n", __func__); - - return 1; - } - lws_strnncpy(dotstar, "12345678", 8, 6); - if (strcmp(dotstar, "12345")) { - lwsl_err("%s: lws_strnncpy check failed\n", __func__); - - return 1; - } - - p = lws_cmdline_option(argc, argv, "-s"); - - for (n = 0; n < (int)LWS_ARRAY_SIZE(tests); n++) { - int m = 0, in_fail = fail; - struct expected *exp = tests[n].exp; - - memset(&ts, 0, sizeof(ts)); - ts.start = tests[n].string; - ts.len = strlen(ts.start); - ts.flags = tests[n].flags; - - do { - e = lws_tokenize(&ts); - - lws_strnncpy(dotstar, ts.token, ts.token_len, - sizeof(dotstar)); - lwsl_info("{ %s, \"%s\", %d }\n", - element_names[e + LWS_TOKZE_ERRS], dotstar, - (int)ts.token_len); - - if (m == (int)tests[n].count) { - lwsl_notice("fail: expected end earlier\n"); - fail++; - break; - } - - if (e != exp->e) { - lwsl_notice("fail... tok %s vs expected %s\n", - element_names[e + LWS_TOKZE_ERRS], - element_names[exp->e + LWS_TOKZE_ERRS]); - fail++; - break; - } - - if (e > 0 && - (ts.token_len != exp->len || - memcmp(exp->value, ts.token, exp->len))) { - lws_strnncpy(dotstar, ts.token, ts.token_len, - sizeof(dotstar)); - lwsl_notice("fail token mismatch %d %d %s\n", - (int)ts.token_len, (int)exp->len, - dotstar); - fail++; - break; - } - - m++; - exp++; - - } while (e > 0); - - if (fail == in_fail) - ok++; - } - - if (p) { - ts.start = p; - ts.len = strlen(p); - ts.flags = flags; - - printf("\t{\n\t\t\"%s\",\n" - "\t\texpected%d, LWS_ARRAY_SIZE(expected%d),\n\t\t", - p, (int)LWS_ARRAY_SIZE(tests) + 1, - (int)LWS_ARRAY_SIZE(tests) + 1); - - if (!flags) - printf("0\n\t},\n"); - else { - if (flags & LWS_TOKENIZE_F_MINUS_NONTERM) - printf("LWS_TOKENIZE_F_MINUS_NONTERM"); - if (flags & LWS_TOKENIZE_F_AGG_COLON) { - if (flags & 1) - printf(" | "); - printf("LWS_TOKENIZE_F_AGG_COLON"); - } - if (flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) { - if (flags & 3) - printf(" | "); - printf("LWS_TOKENIZE_F_COMMA_SEP_LIST"); - } - if (flags & LWS_TOKENIZE_F_RFC7230_DELIMS) { - if (flags & 7) - printf(" | "); - printf("LWS_TOKENIZE_F_RFC7230_DELIMS"); - } - if (flags & LWS_TOKENIZE_F_DOT_NONTERM) { - if (flags & 15) - printf(" | "); - printf("LWS_TOKENIZE_F_DOT_NONTERM"); - } - if (flags & LWS_TOKENIZE_F_NO_FLOATS) { - if (flags & 31) - printf(" | "); - printf("LWS_TOKENIZE_F_NO_FLOATS"); - } - printf("\n\t},\n"); - } - - printf("\texpected%d[] = {\n", (int)LWS_ARRAY_SIZE(tests) + 1); - - do { - e = lws_tokenize(&ts); - - lws_strnncpy(dotstar, ts.token, ts.token_len, - sizeof(dotstar)); - - printf("\t\t{ %s, \"%s\", %d },\n", - element_names[e + LWS_TOKZE_ERRS], - dotstar, (int)ts.token_len); - - } while (e > 0); - - printf("\t}\n"); - } - - lwsl_user("Completed: PASS: %d, FAIL: %d\n", ok, fail); - - return !(ok && !fail); -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_tokenize/README.md libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_tokenize/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_tokenize/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_tokenize/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -# lws api test lws_tokenize - -Performs selftests for lws_tokenize - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --s "input string"|String to tokenize --f 15|LWS_TOKENIZE_F_ flag values to apply to processing of -s - -``` - $ ./lws-api-test-lws_tokenize -[2018/10/09 09:14:17:4834] USER: LWS API selftest: lws_tokenize -[2018/10/09 09:14:17:4835] USER: Completed: PASS: 6, FAIL: 0 -``` - -If the `-s string` option is given, the string is tokenized on stdout in -the format used to produce the tests in the sources - -``` - $ ./lws-api-test-lws_tokenize -s "hello: 1234,256" -[2018/10/09 09:14:17:4834] USER: LWS API selftest: lws_tokenize -{ LWS_TOKZE_TOKEN_NAME_COLON, "hello", 5 } -{ LWS_TOKZE_INTEGER, "1234", 4 } -{ LWS_TOKZE_DELIMITER, ",", 1 } -{ LWS_TOKZE_INTEGER, "256", 3 } -{ LWS_TOKZE_ENDED, "", 0 } -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_tokenize/selftest.sh libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_tokenize/selftest.sh --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-lws_tokenize/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-lws_tokenize/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=1 - -dotest $1 $2 apiselftest -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-secure-streams/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-secure-streams/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-secure-streams/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-secure-streams/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -project(lws-api-test-secure-streams) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) - -if (requirements) - - add_executable(${PROJECT_NAME} main.c) - - if (websockets_shared) - target_link_libraries(${PROJECT_NAME} websockets_shared) - add_dependencies(${PROJECT_NAME} websockets_shared) - else() - target_link_libraries(${PROJECT_NAME} websockets) - endif() - -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-secure-streams/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-secure-streams/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-secure-streams/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-secure-streams/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,384 +0,0 @@ -/* - * lws-api-test-secure-streams - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * Let's exercise some basic SS / h1 functionality against httpbin.org - */ - -#include -#include -#include - -static int interrupted, bad = 1; -static lws_state_notify_link_t nl; -static struct lws_context *context; - -static const char * const default_ss_policy = - "{" - "\"release\":" "\"01234567\"," - "\"product\":" "\"myproduct\"," - "\"schema-version\":" "1," -#if defined(VIA_LOCALHOST_SOCKS) - "\"via-socks5\":" "\"127.0.0.1:1080\"," -#endif - - "\"retry\": [" /* named backoff / retry strategies */ - "{\"default\": {" - "\"backoff\": [" "1000," - "2000," - "3000," - "5000," - "10000" - "]," - "\"conceal\":" "5," - "\"jitterpc\":" "20," - "\"svalidping\":" "30," - "\"svalidhup\":" "35" - "}}" - "]," - "\"certs\": [" /* named individual certificates in BASE64 DER */ - /* - * Let's Encrypt certs for warmcat.com / libwebsockets.org - * - * We fetch the real policy from there using SS and switch to - * using that. - */ - - "{\"amz_root_ca1\": \"" - "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF" - "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6" - "b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL" - "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv" - "b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj" - "ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM" - "9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw" - "IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6" - "VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L" - "93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm" - "jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC" - "AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA" - "A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI" - "U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs" - "N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv" - "o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU" - "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy" - "rqXRfboQnoZsG4q5WTP468SQvvG5" - "\"}" - "]," - "\"trust_stores\": [" /* named cert chains */ - "{" - "\"name\": \"amz\"," - "\"stack\": [" - "\"amz_root_ca1\"" - "]" - "}" - "]," - "\"s\": [" - /* - * "fetch_policy" decides from where the real policy - * will be fetched, if present. Otherwise the initial - * policy is treated as the whole, hardcoded, policy. - */ - "{\"httpbin_get\": {" - "\"endpoint\":" "\"httpbin.org\"," - "\"port\":" "443," - "\"protocol\":" "\"h1\"," - "\"http_method\":" "\"GET\"," - "\"http_url\":" "\"/get\"," - "\"tls\":" "true," - "\"opportunistic\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"amz\"" - "}}," - "{\"httpbin_get404\": {" - "\"endpoint\":" "\"httpbin.org\"," - "\"port\":" "443," - "\"protocol\":" "\"h1\"," - "\"http_method\":" "\"GET\"," - "\"http_url\":" "\"/status/404\"," - "\"tls\":" "true," - "\"opportunistic\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"amz\"" - "}}," - "{\"httpbin_post\": {" - "\"endpoint\":" "\"httpbin.org\"," - "\"port\":" "443," - "\"protocol\":" "\"h1\"," - "\"http_method\":" "\"POST\"," - "\"http_url\":" "\"/post\"," - "\"tls\":" "true," - "\"opportunistic\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"amz\"" - "}}" - "}" - "]}" -; - -typedef struct atss { - const lws_ss_info_t *ssi; - size_t send; - char expect_nack; -} atss_t; - -static const atss_t *next_test; - -typedef struct myss { - struct lws_ss_handle *ss; - void *opaque_data; - /* ... application specific state ... */ - lws_sorted_usec_list_t sul; - size_t payload; - size_t sent; - char seen_eom; - char ended_well; -} myss_t; - -/* secure streams payload interface */ - -static int -myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ - myss_t *m = (myss_t *)userobj; - - lwsl_hexdump_info(buf, len); - - m->payload += len; - - if (!(flags & LWSSS_FLAG_EOM)) - m->seen_eom = 1; - - return 0; -} - -static int -myss_tx_get(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, - int *flags) -{ - return 1; /* nothing to send */ -} - -static int -myss_tx_post(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, - int *flags) -{ - myss_t *m = (myss_t *)userobj; - size_t budget = (next_test->send - m->sent); - - if (!budget) - return 1; - - if (*len < budget) - budget = *len; - - if (!m->sent) - *flags |= LWSSS_FLAG_SOM; - - memset(buf, 0x55, budget); - *len = budget; - m->sent += budget; - if (m->sent != next_test->send) - lws_ss_request_tx(m->ss); - else - *flags |= LWSSS_FLAG_EOM; - - return 0; -} - -static int -myss_state(void *userobj, void *sh, lws_ss_constate_t state, - lws_ss_tx_ordinal_t ack) -{ - myss_t *m = (myss_t *)userobj; - - lwsl_notice("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - lws_ss_client_connect(m->ss); - if (next_test->send) - lws_ss_request_tx_len(m->ss, (unsigned long)next_test->send); - break; - case LWSSSCS_ALL_RETRIES_FAILED: - lwsl_notice("%s: Connection failed\n", __func__); - interrupted = 1; - break; - case LWSSSCS_QOS_NACK_REMOTE: - if (next_test->expect_nack) - goto happy; - lwsl_notice("%s: remote NACK\n", __func__); - interrupted = 1; - break; - case LWSSSCS_QOS_ACK_REMOTE: - /* - * To be satisfied, we want to see the ACK_REMOTE indicating - * that the transaction went through; that we had the payload - * EOM; and that we saw at least 200 + posted bytes response - */ - - if (!m->seen_eom || m->payload < 200 + next_test->send) { - lwsl_warn("%s: ACK_REMOTE but eom %d, payload %d\n", - __func__, m->seen_eom, (int)m->payload); - interrupted = 1; - return -1; - } - -happy: - /* when we disconnect, we can go happily */ - m->ended_well = 1; - - if (!(++next_test)->ssi) { - lwsl_notice("%s: completed all tests\n", __func__); - bad = 0; - interrupted = 1; - break; - } - if (lws_ss_create(context, 0, next_test->ssi, - NULL, NULL, NULL, NULL)) { - lwsl_err("%s: failed to create secure stream\n", - __func__); - return -1; - } - break; - - case LWSSSCS_DISCONNECTED: - if (!m->ended_well) { - lwsl_warn("%s: DISCONNECTED without good end\n", - __func__); - interrupted = 1; - } - break; - default: - break; - } - - return 0; -} - -static const lws_ss_info_t ssi_get = { - .handle_offset = offsetof(myss_t, ss), - .opaque_user_data_offset = offsetof(myss_t, opaque_data), - .rx = myss_rx, - .tx = myss_tx_get, - .state = myss_state, - .user_alloc = sizeof(myss_t), - .streamtype = "httpbin_get" -}, ssi_get404 = { - .handle_offset = offsetof(myss_t, ss), - .opaque_user_data_offset = offsetof(myss_t, opaque_data), - .rx = myss_rx, - .tx = myss_tx_get, - .state = myss_state, - .user_alloc = sizeof(myss_t), - .streamtype = "httpbin_get404" -}, ssi_post = { - .handle_offset = offsetof(myss_t, ss), - .opaque_user_data_offset = offsetof(myss_t, opaque_data), - .rx = myss_rx, - .tx = myss_tx_post, - .state = myss_state, - .user_alloc = sizeof(myss_t), - .streamtype = "httpbin_post" -}; - -static const atss_t test_list[] = { - { .ssi = &ssi_get }, - { .ssi = &ssi_get404, .expect_nack = 1 }, - { .ssi = &ssi_post, .send = 4096 }, - { .ssi = NULL } -}; - - -static int -app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = lws_system_context_from_system_mgr(mgr); - - /* - * For the things we care about, let's notice if we are trying to get - * past them when we haven't solved them yet, and make the system - * state wait while we trigger the dependent action. - */ - switch (target) { - - case LWS_SYSTATE_OPERATIONAL: - if (current == LWS_SYSTATE_OPERATIONAL) { - - next_test = &test_list[0]; - - if (lws_ss_create(context, 0, next_test->ssi, - NULL, NULL, NULL, NULL)) { - lwsl_err("%s: failed to create secure stream\n", - __func__); - return -1; - } - } - break; - } - - return 0; -} - -static lws_state_notify_link_t * const app_notifier_list[] = { - &nl, NULL -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - int n = 0; - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); - lws_cmdline_option_handle_builtin(argc, argv, &info); - - lwsl_user("LWS secure streams test client [-d]\n"); - - /* these options are mutually exclusive if given */ - - info.fd_limit_per_thread = 1 + 6 + 1; - info.port = CONTEXT_PORT_NO_LISTEN; - info.pss_policies_json = default_ss_policy; - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW; - - /* integrate us with lws system state management when context created */ - - nl.name = "app"; - nl.notify_cb = app_system_state_nf; - info.register_notifier_list = app_notifier_list; - - /* create the context */ - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* the event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-secure-streams/README.md libwebsockets-2.4.2/minimal-examples/api-tests/api-test-secure-streams/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-secure-streams/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-secure-streams/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -# lws api test lws_struct JSON - -Demonstrates how to use and performs selftests for lws_struct -JSON serialization and deserialization - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 - -``` - $ ./lws-api-test-lws_struct-json -[2019/03/30 22:09:09:2529] USER: LWS API selftest: lws_struct JSON -[2019/03/30 22:09:09:2625] NOTICE: main: ++++++++++++++++ test 1 -[2019/03/30 22:09:09:2812] NOTICE: builder.hostname = 'learn', timeout = 1800, targets (2) -[2019/03/30 22:09:09:2822] NOTICE: target.name 'target1' (target 0x543a830) -[2019/03/30 22:09:09:2824] NOTICE: target.name 'target2' (target 0x543a860) -[2019/03/30 22:09:09:2826] NOTICE: main: .... strarting serialization of test 1 -[2019/03/30 22:09:09:2899] NOTICE: ser says 1 -{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":1800,"targets":[{"name":"target1"},{"name":"target2"}]} -[2019/03/30 22:09:09:2929] NOTICE: main: ++++++++++++++++ test 2 -[2019/03/30 22:09:09:2932] NOTICE: builder.hostname = 'learn', timeout = 0, targets (3) -[2019/03/30 22:09:09:2932] NOTICE: target.name 'target1' (target 0x543b060) -[2019/03/30 22:09:09:2933] NOTICE: target.name 'target2' (target 0x543b090) -[2019/03/30 22:09:09:2933] NOTICE: target.name 'target3' (target 0x543b0c0) -[2019/03/30 22:09:09:2934] NOTICE: main: .... strarting serialization of test 2 -[2019/03/30 22:09:09:2935] NOTICE: ser says 1 -{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":0,"targets":[{"name":"target1"},{"name":"target2"},{"name":"target3"}]} -[2019/03/30 22:09:09:2940] NOTICE: main: ++++++++++++++++ test 3 -[2019/03/30 22:09:09:2959] NOTICE: builder.hostname = 'learn', timeout = 1800, targets (2) -[2019/03/30 22:09:09:2960] NOTICE: target.name 'target1' (target 0x543b450) -[2019/03/30 22:09:09:2961] NOTICE: child 0x543b480, target.child.somename 'abc' -[2019/03/30 22:09:09:2961] NOTICE: target.name 'target2' (target 0x543b490) -[2019/03/30 22:09:09:2962] NOTICE: main: .... strarting serialization of test 3 -[2019/03/30 22:09:09:2969] NOTICE: ser says 1 -{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":1800,"targets":[{"name":"target1","child":{"somename":"abc"}},{"name":"target2"}]} -[2019/03/30 22:09:09:2970] NOTICE: main: ++++++++++++++++ test 4 -[2019/03/30 22:09:09:2971] NOTICE: builder.hostname = 'learn', timeout = 1800, targets (0) -[2019/03/30 22:09:09:2971] NOTICE: main: .... strarting serialization of test 4 -[2019/03/30 22:09:09:2973] NOTICE: ser says 1 -{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":1800} -[2019/03/30 22:09:09:2974] NOTICE: main: ++++++++++++++++ test 5 -[2019/03/30 22:09:09:2978] NOTICE: builder.hostname = '', timeout = 0, targets (0) -[2019/03/30 22:09:09:2979] NOTICE: main: .... strarting serialization of test 5 -[2019/03/30 22:09:09:2980] NOTICE: ser says 1 -{"schema":"com-warmcat-sai-builder","hostname":"","nspawn_timeout":0} -[2019/03/30 22:09:09:2982] USER: Completed: PASS -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-smtp_client/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/api-tests/api-test-smtp_client/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-smtp_client/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-smtp_client/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -project(lws-unit-tests-smtp-client) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-unit-tests-smtp-client) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_SMTP 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-smtp_client/main.c libwebsockets-2.4.2/minimal-examples/api-tests/api-test-smtp_client/main.c --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-smtp_client/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-smtp_client/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,261 +0,0 @@ -/* - * lws-unit-tests-smtp-client - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This performs unit tests for the SMTP client abstract protocol - */ - -#include - -#include - -static int interrupted, results[10], count_tests, count_passes; - -static int -email_sent_or_failed(struct lws_smtp_email *email, void *buf, size_t len) -{ - free(email); - - return 0; -} - -/* - * The test helper calls this on the instance it created to prepare it for - * the test. In our case, we need to queue up a test email to send on the - * smtp client abstract protocol. - */ - -static int -smtp_test_instance_init(lws_abs_t *instance) -{ - lws_smtp_email_t *email = (lws_smtp_email_t *) - malloc(sizeof(*email) + 2048); - - if (!email) - return 1; - - /* attach an email to it */ - - memset(email, 0, sizeof(*email)); - email->data = NULL /* email specific user data */; - email->email_from = "noreply@warmcat.com"; - email->email_to = "andy@warmcat.com"; - email->payload = (void *)&email[1]; - - lws_snprintf((char *)email->payload, 2048, - "From: noreply@example.com\n" - "To: %s\n" - "Subject: Test email for lws smtp-client\n" - "\n" - "Hello this was an api test for lws smtp-client\n" - "\r\n.\r\n", "andy@warmcat.com"); - email->done = email_sent_or_failed; - - if (lws_smtpc_add_email(instance, email)) { - lwsl_err("%s: failed to add email\n", __func__); - return 1; - } - - return 0; -} - -/* - * from https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol - * - * test vector sent to protocol - * test vector received from protocol - */ - -static lws_unit_test_packet_t test_send1[] = { - { - "220 smtp.example.com ESMTP Postfix", - smtp_test_instance_init, 34, LWS_AUT_EXPECT_RX - }, { - "HELO lws-test-client\x0a", - NULL, 21, LWS_AUT_EXPECT_TX - }, { - "250 smtp.example.com, I am glad to meet you", - NULL, 43, LWS_AUT_EXPECT_RX - }, { - "MAIL FROM: \x0a", - NULL, 33, LWS_AUT_EXPECT_TX - }, { - "250 Ok", - NULL, 6, LWS_AUT_EXPECT_RX - }, { - "RCPT TO: \x0a", - NULL, 28, LWS_AUT_EXPECT_TX - }, { - "250 Ok", - NULL, 6, LWS_AUT_EXPECT_RX - }, { - "DATA\x0a", - NULL, 5, LWS_AUT_EXPECT_TX - }, { - "354 End data with .\x0a", - NULL, 35, LWS_AUT_EXPECT_RX - }, { - "From: noreply@example.com\n" - "To: andy@warmcat.com\n" - "Subject: Test email for lws smtp-client\n" - "\n" - "Hello this was an api test for lws smtp-client\n" - "\r\n.\r\n", - NULL, 27 + 21 + 39 + 1 + 47 + 5, LWS_AUT_EXPECT_TX - }, { - "250 Ok: queued as 12345\x0a", - NULL, 23, LWS_AUT_EXPECT_RX - }, { - "quit\x0a", - NULL, 5, LWS_AUT_EXPECT_TX - }, { - "221 Bye\x0a", - NULL, 7, LWS_AUT_EXPECT_RX | - LWS_AUT_EXPECT_LOCAL_CLOSE | - LWS_AUT_EXPECT_DO_REMOTE_CLOSE | - LWS_AUT_EXPECT_TEST_END - }, { /* sentinel */ - - } -}; - - -static lws_unit_test_packet_t test_send2[] = { - { - "220 smtp.example.com ESMTP Postfix", - smtp_test_instance_init, 34, LWS_AUT_EXPECT_RX - }, { - "HELO lws-test-client\x0a", - NULL, 21, LWS_AUT_EXPECT_TX - }, { - "250 smtp.example.com, I am glad to meet you", - NULL, 43, LWS_AUT_EXPECT_RX - }, { - "MAIL FROM: \x0a", - NULL, 33, LWS_AUT_EXPECT_TX - }, { - "500 Service Unavailable", - NULL, 23, LWS_AUT_EXPECT_RX | - LWS_AUT_EXPECT_DO_REMOTE_CLOSE | - LWS_AUT_EXPECT_TEST_END - }, { /* sentinel */ - - } -}; - -static lws_unit_test_t tests[] = { - { "sending", test_send1, 3 }, - { "rejected", test_send2, 3 }, - { } /* sentinel */ -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -/* - * set the HELO our SMTP client will use - */ - -static const lws_token_map_t smtp_ap_tokens[] = { - { - .u = { .value = "lws-test-client" }, - .name_index = LTMI_PSMTP_V_HELO, - }, { /* sentinel */ - } -}; - -void -tests_completion_cb(const void *cb_user) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_context_creation_info info; - lws_test_sequencer_args_t args; - struct lws_context *context; - lws_abs_t *abs = NULL; - struct lws_vhost *vh; - const char *p; - - /* the normal lws init */ - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS API selftest: SMTP client unit tests\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - vh = lws_create_vhost(context, &info); - if (!vh) { - lwsl_err("Failed to create first vhost\n"); - goto bail1; - } - - /* create the abs used to create connections */ - - abs = lws_abstract_alloc(vh, NULL, "smtp.unit_test", - &smtp_ap_tokens[0], NULL); - if (!abs) - goto bail1; - - /* configure the test sequencer */ - - args.abs = abs; - args.tests = tests; - args.results = results; - args.results_max = LWS_ARRAY_SIZE(results); - args.count_tests = &count_tests; - args.count_passes = &count_passes; - args.cb = tests_completion_cb; - args.cb_user = NULL; - - if (lws_abs_unit_test_sequencer(&args)) { - lwsl_err("%s: failed to create test sequencer\n", __func__); - goto bail1; - } - - /* the usual lws event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - /* describe the overall test results */ - - lwsl_user("%s: %d tests %d fail\n", __func__, count_tests, - count_tests - count_passes); - for (n = 0; n < count_tests; n++) - lwsl_user(" test %d: %s\n", n, - lws_unit_test_result_name(results[n])); - -bail1: - lwsl_user("Completed: %s\n", - !count_tests || count_passes != count_tests ? "FAIL" : "PASS"); - - lws_context_destroy(context); - - lws_abstract_free(&abs); - - return !count_tests || count_passes != count_tests; -} diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/api-test-smtp_client/README.md libwebsockets-2.4.2/minimal-examples/api-tests/api-test-smtp_client/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/api-test-smtp_client/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/api-test-smtp_client/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -# lws api test smtp client - -Performs unit tests on the lws SMTP client abstract protocol -implementation. - -The first test "sends mail to a server" (actually is prompted by -test vectors that look like a server) and the second test -confirm it can handle rejection by the "server" cleanly. - -## build - -Requires lws was built with `-DLWS_WITH_SMTP=1` at cmake. - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --r |Send the test email to this email address - - -``` - $ ./lws-api-test-smtp_client -[2019/06/28 21:56:41:0711] USER: LWS API selftest: SMTP client unit tests -[2019/06/28 21:56:41:1114] NOTICE: test_sequencer_cb: test-seq: created -[2019/06/28 21:56:41:1259] NOTICE: unit_test_sequencer_cb: unit-test-seq: created -[2019/06/28 21:56:41:1272] NOTICE: lws_atcut_client_conn: smtp: test 'sending': start -[2019/06/28 21:56:41:1441] NOTICE: unit_test_sequencer_cb: unit-test-seq: created -[2019/06/28 21:56:41:1442] NOTICE: lws_atcut_client_conn: smtp: test 'rejected': start -[2019/06/28 21:56:41:1453] NOTICE: lws_smtp_client_abs_rx: bad response from server: 500 (state 4) 500 Service Unavailable -[2019/06/28 21:56:41:1467] USER: test_sequencer_cb: sequence completed OK -[2019/06/28 21:56:41:1474] USER: main: 2 tests 0 fail -[2019/06/28 21:56:41:1476] USER: test 0: PASS -[2019/06/28 21:56:41:1478] USER: test 1: PASS -[2019/06/28 21:56:41:1480] USER: Completed: PASS -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/api-tests/README.md libwebsockets-2.4.2/minimal-examples/api-tests/README.md --- libwebsockets-4.0.20/minimal-examples/api-tests/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/api-tests/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -These are buildable test apps that run in CI to confirm correct api operation. - -|name|tests| ----|--- -api-test-lwsac|LWS Allocated Chunks api -api-test-lws_struct-json|Selftests for lws_struct JSON serialization and deserialization -api-test-lws_tokenize|Generic secure string tokenizer api -api-test-fts|LWS Full-text Search api -api-test-gencrypto|LWS Generic Crypto apis -api-test-jose|LWS JOSE apis -api-test-smtp_client|SMTP client for sending emails - diff -Nru libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-ws-proxy) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-proxy) -set(SRCS minimal-ws-proxy.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/minimal-ws-proxy.c libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/minimal-ws-proxy.c --- libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/minimal-ws-proxy.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/minimal-ws-proxy.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ -/* - * lws-minimal-ws-proxy - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws, - * with an added websocket proxy distributing what is received on a - * dumb-increment wss connection to https://libwebsockets.org to all - * browsers connected to this server. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal.c" - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - LWS_PLUGIN_PROTOCOL_MINIMAL, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws proxy | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -var head = 0, tail = 0, ring = new Array(); - -function get_appropriate_ws_url(extra_url) -{ - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; -} - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -document.addEventListener("DOMContentLoaded", function() { - - var ws = new_ws(get_appropriate_ws_url(""), "lws-minimal-proxy"); - try { - ws.onopen = function() { - document.getElementById("r").disabled = 0; - }; - - ws.onmessage =function got_packet(msg) { - var n, s = ""; - - ring[head] = msg.data + "\n"; - head = (head + 1) % 20; - if (tail === head) - tail = (tail + 1) % 20; - - n = tail; - do { - s = s + ring[n]; - n = (n + 1) % 20; - } while (n !== head); - - document.getElementById("r").value = s; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - ws.onclose = function(){ - document.getElementById("r").disabled = 1; - }; - } catch(exception) { - alert("

Error " + exception); - } -}, false); Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ - - - - - - - -
- - Minimal ws server proxy example.
- The server makes a dumb-increment-protocol wss connection
- to libwebsockets.org. It proxies what it was sent to
- all browsers open on this page.
- The textarea show the last 20 lines received. -
-
-
- - diff -Nru libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/protocol_lws_minimal.c libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/protocol_lws_minimal.c --- libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/protocol_lws_minimal.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/protocol_lws_minimal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,279 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal" - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This version uses an lws_ring ringbuffer to cache up to 8 messages at a time, - * so it's not so easy to lose messages. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include -#include - -/* one of these created for each message */ - -struct msg { - void *payload; /* is malloc'd */ - size_t len; -}; - -/* one of these is created for each client connecting to us */ - -struct per_session_data__minimal { - struct per_session_data__minimal *pss_list; - struct lws *wsi; - uint32_t tail; -}; - -/* one of these is created for each vhost our protocol is used with */ - -struct per_vhost_data__minimal { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - - struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ - - struct lws_ring *ring; /* ringbuffer holding unsent messages */ - struct lws_client_connect_info i; - struct lws *client_wsi; -}; - -/* destroys the message when everyone has had a copy of it */ - -static void -__minimal_destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} - -static int -connect_client(struct per_vhost_data__minimal *vhd) -{ - vhd->i.context = vhd->context; - vhd->i.port = 443; - vhd->i.address = "libwebsockets.org"; - vhd->i.path = "/"; - vhd->i.host = vhd->i.address; - vhd->i.origin = vhd->i.address; - vhd->i.ssl_connection = 1; - - vhd->i.protocol = "dumb-increment-protocol"; - vhd->i.local_protocol_name = "lws-minimal-proxy"; - vhd->i.pwsi = &vhd->client_wsi; - - return !lws_client_connect_via_info(&vhd->i); -} - -static int -callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal *pss = - (struct per_session_data__minimal *)user; - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - const struct msg *pmsg; - struct msg amsg; - int m; - - switch (reason) { - - /* --- protocol lifecycle callbacks --- */ - - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__minimal)); - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - - vhd->ring = lws_ring_create(sizeof(struct msg), 8, - __minimal_destroy_message); - if (!vhd->ring) - return 1; - - if (connect_client(vhd)) - lws_timed_callback_vh_protocol(vhd->vhost, - vhd->protocol, - LWS_CALLBACK_USER, 1); - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - lws_ring_destroy(vhd->ring); - break; - - /* --- serving callbacks --- */ - - case LWS_CALLBACK_ESTABLISHED: - /* add ourselves to the list of live pss held in the vhd */ - lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); - pss->tail = lws_ring_get_oldest_tail(vhd->ring); - pss->wsi = wsi; - break; - - case LWS_CALLBACK_CLOSED: - /* remove our closing pss from the list of live pss */ - lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, - pss, vhd->pss_list); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - pmsg = lws_ring_get_element(vhd->ring, &pss->tail); - if (!pmsg) - break; - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE, - pmsg->len, LWS_WRITE_TEXT); - if (m < (int)pmsg->len) { - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - lws_ring_consume_and_update_oldest_tail( - vhd->ring, /* lws_ring object */ - struct per_session_data__minimal, /* type of objects with tails */ - &pss->tail, /* tail of guy doing the consuming */ - 1, /* number of payload objects being consumed */ - vhd->pss_list, /* head of list of objects with tails */ - tail, /* member name of tail in objects with tails */ - pss_list /* member name of next object in objects with tails */ - ); - - /* more to do? */ - if (lws_ring_get_element(vhd->ring, &pss->tail)) - /* come back as soon as we can write more */ - lws_callback_on_writable(pss->wsi); - break; - - /* --- client callbacks --- */ - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - vhd->client_wsi = NULL; - lws_timed_callback_vh_protocol(vhd->vhost, vhd->protocol, - LWS_CALLBACK_USER, 1); - break; - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - lwsl_user("%s: established\n", __func__); - break; - - case LWS_CALLBACK_CLIENT_RECEIVE: - /* if no clients, just drop incoming */ - if (!vhd->pss_list) - break; - - if (!lws_ring_get_count_free_elements(vhd->ring)) { - lwsl_user("dropping!\n"); - break; - } - - amsg.len = len; - /* notice we over-allocate by LWS_PRE */ - amsg.payload = malloc(LWS_PRE + len); - if (!amsg.payload) { - lwsl_user("OOM: dropping\n"); - break; - } - - memcpy((char *)amsg.payload + LWS_PRE, in, len); - if (!lws_ring_insert(vhd->ring, &amsg, 1)) { - __minimal_destroy_message(&amsg); - lwsl_user("dropping!\n"); - break; - } - - /* - * let everybody know we want to write something on them - * as soon as they are ready - */ - lws_start_foreach_llp(struct per_session_data__minimal **, - ppss, vhd->pss_list) { - lws_callback_on_writable((*ppss)->wsi); - } lws_end_foreach_llp(ppss, pss_list); - break; - - case LWS_CALLBACK_CLIENT_CLOSED: - vhd->client_wsi = NULL; - lws_timed_callback_vh_protocol(vhd->vhost, vhd->protocol, - LWS_CALLBACK_USER, 1); - break; - - /* rate-limited client connect retries */ - - case LWS_CALLBACK_USER: - lwsl_notice("%s: LWS_CALLBACK_USER\n", __func__); - if (connect_client(vhd)) - lws_timed_callback_vh_protocol(vhd->vhost, - vhd->protocol, - LWS_CALLBACK_USER, 1); - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL \ - { \ - "lws-minimal-proxy", \ - callback_minimal, \ - sizeof(struct per_session_data__minimal), \ - 128, \ - 0, NULL, 0 \ - } - - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/README.md libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/README.md --- libwebsockets-4.0.20/minimal-examples/client-server/minimal-ws-proxy/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/client-server/minimal-ws-proxy/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -# lws minimal ws proxy - -## Build - -``` - $ cmake . && make -``` - -## Description - -This is the same as minimal-ws-server-ring, but with the -inclusion of a ws client connection to https://libwebsockets.org -using the dumb-increment protocol feeding the ringbuffer. - -Each client that connect to this server receives the content that -had arrived on the client connection feeding the ringbuffer proxied -to their browser window over a ws connection. - -## Usage - -``` - $ ./lws-minimal-ws-proxy -[2018/03/14 17:50:10:6938] USER: LWS minimal ws proxy | visit http://localhost:7681 -[2018/03/14 17:50:10:6955] NOTICE: Creating Vhost 'default' port 7681, 2 protocols, IPv6 off -[2018/03/14 17:50:10:6955] NOTICE: Using non-SSL mode -[2018/03/14 17:50:10:7035] NOTICE: created client ssl context for default -[2018/03/14 17:50:11:7047] NOTICE: binding to lws-minimal-proxy -[2018/03/14 17:50:11:7047] NOTICE: lws_client_connect_2: 0x872e60: address libwebsockets.org -[2018/03/14 17:50:12:3282] NOTICE: lws_client_connect_2: 0x872e60: address libwebsockets.org -[2018/03/14 17:50:13:8195] USER: callback_minimal: established -``` - -Visit http://localhost:7681 on multiple browser windows - -Data received on the remote wss connection is copied to all open browser windows. - -A ringbuffer holds up to 8 lines of text in the server, and the browser shows -the last 20 lines of received text. diff -Nru libwebsockets-4.0.20/minimal-examples/client-server/README.md libwebsockets-2.4.2/minimal-examples/client-server/README.md --- libwebsockets-4.0.20/minimal-examples/client-server/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/client-server/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -|name|demonstrates| ----|--- -minimal-ws-proxy|Serves an index.html over http that connects back to the ws server, and maintains a ws client connection of its own at the same time to https://libwebsockets.org dumb-increment-protocol to feed a ringbuffer that is sent to all connected browsers. diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwe/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwe/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwe/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwe/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-crypto-jwe) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-crypto-jwe) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_JOSE 1 requirements) - -if (requirements) - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.private libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.private --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.private 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.private 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -{"d":"XcSl3ulvs4OGomu9thRPVQGOstim0PY7CibP_bnCmzjvmGmzb8J4q5AUmJCnZT5TesOzXuXhyG95CxQWsakd9GWHSAinV1QQSLsahaezPULRG1qmo37JqKb9noKkvXguh5XU5np8HjeoeeEkF_XqtCdEo0wHijEjTL9RZar98jmyAmlizoHIY9NnECavs4DZB27onU61B61vGpw-y4xhC9jlZSIwRqIMDzeTcSv8fRKcVYR80ozm2_KwWMpue27rS2EfTQUtsMXuYmnvMAf_DHqA0tNWyD1gpUWYHvlyBh5xnYrWPuXxQBRNesImQdRQl5VMMsuvdtY-uZfIVUdN5CcsB0acronx4UsmVg-Qz-jd1NVW4koZQM9uA4oWiMZg4FEUTQ-UWelHCldg-PYLAazsItmaHPF9LcAPkLkI8jaVS33v-DhSeXHW3Pg3sibtnPhouiSvD84zMtzu1gjFT7vtapMynBeZouqeWYT-BFeu2wzppJcW1YxTQ_Ai80VJSFY__Huw-9r1MOHmDRcEW7x9W97UezWDjrh5Shhh4C6SMYbaf7ouACzFu1i_r8Q06JqKA7aY8i5izKlKA0We9tQKlTF8Fgsneu9gpxFglvZsd1ersiA-MkuP9qTBQpyAf3kJ6HS9GrQUju6r3DExdWDjdvM5Grt8QD7Zkv-qXeE","dp":"M-LFs3T2GI1JxD5LJt2GgV4cMDKbiPKBddLukfG0duUxNp0-6x2LZ0ptxrlHrhxBMMmvCg4GEaujrZdaYWCar6xCnlnkVlOELz4yZ3JBSpS86thJw03xuE7lyeR7usFY4CpSqUQGI_YveITuFeoh4YjwdKDuqPhOpDI-34ptgU93dlBRS9nnQFTiVoUdP4bhGTKOpULTiLgPXHQxQR5rfiGVD9AIwqHvMdBQ0hxQBKEt37PbRWK_eTzMslHZGWNfbg8ipwJxisvHyUn0c1X3Uelw8BRyvNVCNovNDeCj-R7kFkMvriMd_sqGVy1Go46WZ2wMkUJHkvmYk0gDlhnTGQ","dq":"qO89nQEJfdkaDtGGyD-sQE2Mm8p_PIPSpCmgMfpl8zgSOb4P9iqXBgpHyS7w10uY_UHt8KW6pY7ozy0y4Gu_f4Wk_rcXiYYdbuIhlFl0_nLI2mfFPGxr1xC64zfjjEaBr4zIJr_YzhvTpjZFtIdSAH5VG5Tv-2yUtCC2DnKnU2kzEkgUeSI6LHOEVhXqup7C0Kjiv9FJsLR0hiqwH4oLziqH7EVqVDvJI3yL1lhqoLKjAu1ogTDgH7hzSrqVhttnpwL8rDcgbtY6Q8C2csdN3Jt1ucgtGy-Yzgqf_QIULP3CRlqzDTvHrMe2A9cNAQ4dNsCbNAjW_MxxGKKWuWXAMQ","e":"AQAB","kid":"my kid","kty":"RSA","n":"2_YjG_D1sOWJxs6cohikupHf5WJfWSFfSCrnNZ7WR7AyTLnKZAF4VKyimMeJTLYYwCAXMDD5XmkF8VluI4O-hASUIJ7F9eDg3vO7nPwtkWa9lkqt-QyQZ_PjiOGpwetBLzrsaXsC9PvdVzrKXnjeNPsmmbC_Fx2cUn4H_9H_WfXi01VR75XFTBtxTrDY7hmpZHuFCFUOMCW9siTZRk9339Y6ORBznBs4jFbkGI1Pmc3op0o5f8S1gus9L81W5uyUrxfd-CkmJ6eWE8I36cfzI6irN2bhVhR_NXERUtS0QOEeJYlRJXqfYkxTMVlsXPl6zbYt__ZYLC6ZiUTCc6K2KmfGh7fihWbao4dyQW3Mq4kClhpIT0O01Y0r7sR1j4jTnFNqbmtPSl9lEMrfiUHfOLqRJo3qizQ-b6HLCDty1otFz8Q8gg0rD3copQ_zFrcTGwJGAv2Absraj7kp9EJXBqneCJ3dlRO8rzx7KB9Dsj-ygh3kZaubkPCeT1v4l_VUY2iGnK4vzIGKM7j56DQ97ZAi1Bb0y6GYSbrWB2_z0DKJu0fiU-NscbKplR68vgppUM6_iogrk48JEZg_kkTymniqbT3g7J_WeoZSx1Uu8ZHI3ysIFfUtFscOa2SJGlj1ds-lfk6Oqac_I8ahRqQeyVAEisZPmYIGSJajbJopJ4s","p":"_V4CwEjRd8Hv9-ncqGdB_vtReTIuHSWQzSx4al15J3VxvPFI2kxicNeQKyq3OAVT2kmCmUP3ETgCdwuKIgw_QbEc8qNxtS_KpM_KsuTe9a5jrQKpt8ctYhzELZfr_sy9UzUGJzr8glLjJ1IDX4x6_JAqYB_NhttP6bzgu5Dt-DKtRPNO1qZtfhrLIgmltpC2M6-AlAv-dyHSHck2VJIL84Hwk4FulozEYxop0dKuZdfM5Z1dZM8-ICo62O0zUKzoWxKmQcB9_gDZsxYaO6xZ9BLmaW6-WcPSEI6YDnPk8ptnk_Kbyc4kPW4Z3ASczxjaewBmfl2_lwkqkndFVptAeQ","q":"3j9DR6ZpKC3WrshSrxXFYAuT19Rlf6qQ_9uD_Fq7dIpTjCZdl01695Qx7UmujKoetutL3RMCpeRdZR-gCLVh8aMxpMuIc5fHC6HbhsdF-I7GoqO0DEJ6coS3n5Ey4EXL5uoLh4C3l67wBKfLmPW28bxxG2QAP59jncWXkrBQm_qbS5Qon8r7wj0tejG_tGdsPjhsFc9KdnkkBucT6MiEVpzpdwDlsn7bHpMsyPlNyc0fj5qYmRB-DN7rv5varaisBaVT0mLQdwKjBDVqNVnU2m5azPhY-2txvihHaI5_cLIsLLaqKMbB17UxGumuT_o8S03_h8-1syO3Ay87y9pPIw","qi":"JY2uUek6wPrp4fPcInX_5WdNlhyghcGVEvlqxs9iOEUeCtUc6d42n9tgiImMu605dQaigvNaH5y1pwDpLlmxUk0nOUVxqo9mv0Uw8WNXB88FyDb0fPbewLpn4Fskb8Umv6_OymJ1W814DRG-jq3sI5DsB7AjtqJQ22nP2Vs1bIrx5fUxuScwrMsWSrrjAx4Kr8-5eeSDqE-_c7DPZ_zSPYDoHaMeR2pZfNAq3mEbxp8jMukzh77rYZ3ffQEA6AyxFSCSCrxVozhP4ypQ0jAkXVWOlj4nuV6briIqlL3ZboydwsIolRwaPSgH6-bw03XS6Hb9DA0KHJKLun94N9n5kw","use":"enc"} diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.pub libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.pub --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.pub 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.pub 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -{"e":"AQAB","kid":"my kid","kty":"RSA","n":"2_YjG_D1sOWJxs6cohikupHf5WJfWSFfSCrnNZ7WR7AyTLnKZAF4VKyimMeJTLYYwCAXMDD5XmkF8VluI4O-hASUIJ7F9eDg3vO7nPwtkWa9lkqt-QyQZ_PjiOGpwetBLzrsaXsC9PvdVzrKXnjeNPsmmbC_Fx2cUn4H_9H_WfXi01VR75XFTBtxTrDY7hmpZHuFCFUOMCW9siTZRk9339Y6ORBznBs4jFbkGI1Pmc3op0o5f8S1gus9L81W5uyUrxfd-CkmJ6eWE8I36cfzI6irN2bhVhR_NXERUtS0QOEeJYlRJXqfYkxTMVlsXPl6zbYt__ZYLC6ZiUTCc6K2KmfGh7fihWbao4dyQW3Mq4kClhpIT0O01Y0r7sR1j4jTnFNqbmtPSl9lEMrfiUHfOLqRJo3qizQ-b6HLCDty1otFz8Q8gg0rD3copQ_zFrcTGwJGAv2Absraj7kp9EJXBqneCJ3dlRO8rzx7KB9Dsj-ygh3kZaubkPCeT1v4l_VUY2iGnK4vzIGKM7j56DQ97ZAi1Bb0y6GYSbrWB2_z0DKJu0fiU-NscbKplR68vgppUM6_iogrk48JEZg_kkTymniqbT3g7J_WeoZSx1Uu8ZHI3ysIFfUtFscOa2SJGlj1ds-lfk6Oqac_I8ahRqQeyVAEisZPmYIGSJajbJopJ4s"} diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwe/main.c libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwe/main.c --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwe/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwe/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,282 +0,0 @@ -/* - * lws-crypto-jwe - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include - -/* - * handles escapes and line wrapping suitable for use - * defining a C char array ( -c option ) - */ - -static void -format_c(const char *key) -{ - const char *k = key; - int seq = 0; - - while (*k) { - if (*k == '{') { - putchar('\"'); - putchar('{'); - putchar('\"'); - putchar('\n'); - putchar('\t'); - putchar('\"'); - k++; - seq = 0; - continue; - } - if (*k == '}') { - putchar('\"'); - putchar('\n'); - putchar('\"'); - putchar('}'); - putchar('\"'); - putchar('\n'); - k++; - seq = 0; - continue; - } - if (*k == '\"') { - putchar('\\'); - putchar('\"'); - seq += 2; - k++; - continue; - } - if (*k == ',') { - putchar(','); - putchar('\"'); - putchar('\n'); - putchar('\t'); - putchar('\"'); - k++; - seq = 0; - continue; - } - putchar(*k); - seq++; - if (seq >= 60) { - putchar('\"'); - putchar('\n'); - putchar('\t'); - putchar(' '); - putchar('\"'); - seq = 1; - } - k++; - } -} - -#define MAX_SIZE (4 * 1024 * 1024) - char temp[MAX_SIZE], compact[MAX_SIZE]; - -int main(int argc, const char **argv) -{ - int n, enc = 0, result = 0, - logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - char *in; - struct lws_context_creation_info info; - int temp_len = sizeof(temp); - struct lws_context *context; - struct lws_jwe jwe; - const char *p; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS JWE example tool\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = 0; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - lws_jwe_init(&jwe, context); - - /* if encrypting, set the ciphers */ - - if ((p = lws_cmdline_option(argc, argv, "-e"))) { - char *sp = strchr(p, ' '); - - if (!sp) { - lwsl_err("format: -e \" " - "\", eg, " - "-e \"RSA1_5 A128CBC-HS256\"\n"); - - return 1; - } - *sp = '\0'; - if (lws_gencrypto_jwe_alg_to_definition(p, &jwe.jose.alg)) { - lwsl_err("Unknown cipher alg %s\n", p); - return 1; - } - if (lws_gencrypto_jwe_enc_to_definition(sp + 1, &jwe.jose.enc_alg)) { - lwsl_err("Unknown payload enc alg %s\n", sp + 1); - return 1; - } - - /* create JOSE header, also needed for output */ - - if (lws_jws_alloc_element(&jwe.jws.map, LJWS_JOSE, - lws_concat_temp(temp, temp_len), - &temp_len, strlen(p) + - strlen(sp + 1) + 32, 0)) { - lwsl_err("%s: temp space too small\n", __func__); - return 1; - } - - jwe.jws.map.len[LJWS_JOSE] = lws_snprintf( - (char *)jwe.jws.map.buf[LJWS_JOSE], temp_len, - "{\"alg\":\"%s\",\"enc\":\"%s\"}", p, sp + 1); - - enc = 1; - } - - in = lws_concat_temp(temp, temp_len); - n = read(0, in, temp_len); - if (n < 0) { - lwsl_err("Problem reading from stdin\n"); - return 1; - } - - /* account for padding as well */ - - temp_len -= lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN, n); - - /* grab the key */ - - if ((p = lws_cmdline_option(argc, argv, "-k"))) { - if (lws_jwk_load(&jwe.jwk, p, NULL, NULL)) { - lwsl_err("%s: problem loading JWK %s\n", __func__, p); - - return 1; - } - } else { - lwsl_err("-k is required\n"); - - return 1; - } - - if (enc) { - - /* point CTXT to the plaintext we read from stdin */ - - jwe.jws.map.buf[LJWE_CTXT] = in; - jwe.jws.map.len[LJWE_CTXT] = n; - - /* - * Create a random CEK and set EKEY to it - * CEK size is determined by hash / hmac size - */ - - n = lws_gencrypto_bits_to_bytes(jwe.jose.enc_alg->keybits_fixed); - if (lws_jws_randomize_element(context, &jwe.jws.map, LJWE_EKEY, - lws_concat_temp(temp, temp_len), - &temp_len, n, - LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) { - lwsl_err("Problem getting random\n"); - goto bail1; - } - - /* perform the encryption of the CEK and the plaintext */ - - n = lws_jwe_encrypt(&jwe, lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_encrypt failed\n", __func__); - goto bail1; - } - if (lws_cmdline_option(argc, argv, "-f")) - /* output the JWE in flattened form */ - n = lws_jwe_render_flattened(&jwe, compact, - sizeof(compact)); - else - /* output the JWE in compact form */ - n = lws_jwe_render_compact(&jwe, compact, - sizeof(compact)); - - if (n < 0) { - lwsl_err("%s: lws_jwe_render failed: %d\n", - __func__, n); - goto bail1; - } - - if (lws_cmdline_option(argc, argv, "-c")) - format_c(compact); - else - if (write(1, compact, strlen(compact)) < 0) { - lwsl_err("Write stdout failed\n"); - goto bail1; - } - } else { - if (lws_cmdline_option(argc, argv, "-f")) { - if (lws_jwe_json_parse(&jwe, (uint8_t *)in, n, - lws_concat_temp(temp, temp_len), - &temp_len)) { - lwsl_err("%s: lws_jwe_json_parse failed\n", - __func__); - goto bail1; - } - } else - /* - * converts a compact serialization to b64 + decoded maps - * held in jws - */ - if (lws_jws_compact_decode(in, n, &jwe.jws.map, - &jwe.jws.map_b64, - lws_concat_temp(temp, temp_len), - &temp_len) != 5) { - lwsl_err("%s: lws_jws_compact_decode failed\n", - __func__); - goto bail1; - } - - /* - * Do the crypto according to what we parsed into the jose - * (information on the ciphers) and the jws (plaintext and - * signature info) - */ - - n = lws_jwe_auth_and_decrypt(&jwe, - lws_concat_temp(temp, temp_len), - &temp_len); - if (n < 0) { - lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", - __func__); - goto bail1; - } - - /* if it's valid, dump the plaintext and return 0 */ - - if (write(1, jwe.jws.map.buf[LJWE_CTXT], - jwe.jws.map.len[LJWE_CTXT]) < 0) { - lwsl_err("Write stdout failed\n"); - goto bail1; - } - } - - result = 0; - -bail1: - - lws_jwe_destroy(&jwe); - - lws_context_destroy(context); - - return result; -} diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwe/README.md libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwe/README.md --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwe/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwe/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -# lws minimal example for JWE - -Demonstrates how to encrypt and decrypt using JWE and JWK, providing a -commandline tool for creating encrypted JWE and decoding them. - -## build - -``` - $ cmake . && make -``` - -## usage - -Stdin is either the plaintext (if encrypting) or JWE (if decrypting). - -Stdout is either the JWE (if encrypting) or plaintext (if decrypting). - -You must pass a private or public key JWK file in the -k option if encrypting, -and must pass a private key JWK file in the -k option if decrypting. To be -clear, for asymmetric keys the public part of the key is required to encrypt, -and the private part required to decrypt. - -For convenience, a pair of public and private keys are provided, -`key-rsa-4096.private` and `key-rsa-4096.pub`, these were produced with just - -``` - $ lws-crypto-jwk -t RSA -b 4096 --public key-rsa-4096.pub >key-rsa-4096.private -``` - -Similar keys for EC modes may be produced with - -``` - $ lws-crypto-jwk -t EC -v P-256 --public key-ecdh-p-256.pub >key-ecdh-p-256.private -``` - -and for AES ("octet") symmetric keys - -``` - $ lws-crypto-jwk -t OCT -b 128 >key-aes-128.private -``` - -JWEs produced with openssl and mbedtls backends are completely interchangeable. - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --e " "|Encrypt (default is decrypt), eg, -e "RSA1_5 A128CBC-HS256". For decrypt, the cipher information comes from the input JWE. --k |JWK file to encrypt or decrypt with --c|Format the JWE as a linebroken C string --f|Output flattened representation (instead of compact by default) - -``` - $ echo -n "plaintext0123456" | ./lws-crypto-jwe -k key-rsa-4096.private -e "RSA1_5 A128CBC-HS256" -[2018/12/19 16:20:25:6519] USER: LWS JWE example tool -[2018/12/19 16:20:25:6749] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off -eyJhbGciOiJSU0ExXzUiLCAiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.ivFr7qzx-pQ4V_edbjpdvR9OwWL9KmojPE2rXQM52oLtW0BtnxZu2_ezqhsAelyIcaworgfobs3u4bslXHMFbeJJjPb5xD0fBDe64OYXZH1NpUGTMJh9Ka4CrJ2B3xhxe7EByGAuGqmluqE0Yezj7rhSw7vlr5JAwuOJ8FaGa8aZ8ldki5G5h_S2Furlbjdcw3Rrxk7mCoMHcLoqzfZtggMPwGAMFogCqcwUo7oSLbBeGaa6hpMbfSysugseWdr8TzObQKPM52k6iVAlGwRaOg_qdLMgZiYRhHA6nFKTQd7XBbNY6qAS8sPuj7Zz344tF3RSfJ0zX_telG71sOtVv5fMpeDU-eCdpOWlCBfu6J6FQfAFu6SJryM4ajGOif09CwFI5qUQ33SOfQfS_M3nqSyd6Vu5M4lsDrb5wK7_XX5gqUwvI9wicf_8WWR-CQomRF-JvEASnA2SIf8QqYfa8R2rP9q6Md4vwO4EZrtxIsMDPsH-4ZEFu7vDjyy09QfIWWsnEb8-UgpVXensgt2m_2bZ76r1VB8-0nZLMwMyEhaH2wra9vX2FWao5UkmNJ7ht300f4_V6QzMFoePpwCvsufWBW6jcQLB-frCWe6uitWaZHEB4LxmNPKzQSz4QwwTKhpF1jNn8Xh1-w1m-2h0gj-oe-S8QBwPveqhPI1p2fI.snuhUTXHNu5mJ6dEPQqg6g.yl36qC4o0GE4nrquQ2YyCg.Vf0MoT7_kUrZdCNWXhq1DQ -``` - -Notice the logging is on stderr, and the output alone on stdout. - -You can also pipe the output of the encrypt action directly into the decrypt -action, eg - -``` - $ echo -n "plaintext0123456" | \ - ./lws-crypto-jwe -k key-rsa-4096.pub -e "RSA1_5 A128CBC-HS256" | \ - ./lws-crypto-jwe -k key-rsa-4096.private -``` - -prints the plaintext on stdout. diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwk/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwk/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwk/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwk/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-crypto-jwk) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-crypto-jwk) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_JOSE 1 requirements) - -if (requirements) - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwk/main.c libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwk/main.c --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwk/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwk/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,190 +0,0 @@ -/* - * lws-crypto-jwk - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include - -/* - * handles escapes and line wrapping suitable for use - * defining a C char array ( -c option ) - */ - -static int -format_c(int fd, const char *key) -{ - const char *k = key; - int seq = 0; - - while (*k) { - if (*k == '{') { - if (write(fd, "\"{\"\n\t\"", 6) < 6) - return -1; - k++; - seq = 0; - continue; - } - if (*k == '}') { - if (write(fd, "\"\n\"}\"\n", 6) < 6) - return -1; - k++; - seq = 0; - continue; - } - if (*k == '\"') { - if (write(fd, "\\\"", 2) < 2) - return -1; - seq += 2; - k++; - continue; - } - if (*k == ',') { - if (write(fd, ",\"\n\t\"", 5) < 5) - return -1; - k++; - seq = 0; - continue; - } - if (write(fd, k, 1) < 1) - return -1; - seq++; - if (seq >= 60) { - if (write(fd, "\"\n\t \"", 5) < 5) - return -1; - seq = 1; - } - k++; - } - - return 0; -} - -int main(int argc, const char **argv) -{ - int result = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - enum lws_gencrypto_kty kty = LWS_GENCRYPTO_KTY_RSA; - struct lws_context_creation_info info; - const char *curve = "P-256", *p; - struct lws_context *context; - struct lws_jwk jwk; - int bits = 4096; - char key[32768]; - int vl = sizeof(key); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS JWK example\n"); - - if ((p = lws_cmdline_option(argc, argv, "-b"))) - bits = atoi(p); - - if ((p = lws_cmdline_option(argc, argv, "-t"))) { - if (!strcmp(p, "RSA")) - kty = LWS_GENCRYPTO_KTY_RSA; - else - if (!strcmp(p, "OCT")) - kty = LWS_GENCRYPTO_KTY_OCT; - else - if (!strcmp(p, "EC")) - kty = LWS_GENCRYPTO_KTY_EC; - else { - lwsl_err("Unknown key type (must be " - "OCT, RSA or EC)\n"); - - return 1; - } - } - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = 0; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - if ((p = lws_cmdline_option(argc, argv, "-v"))) - curve = p; - - if (lws_jwk_generate(context, &jwk, kty, bits, curve)) { - lwsl_err("lws_jwk_generate failed\n"); - - return 1; - } - - if ((p = lws_cmdline_option(argc, argv, "--kid"))) - lws_jwk_strdup_meta(&jwk, JWK_META_KID, p, strlen(p)); - - if ((p = lws_cmdline_option(argc, argv, "--use"))) - lws_jwk_strdup_meta(&jwk, JWK_META_USE, p, strlen(p)); - - if ((p = lws_cmdline_option(argc, argv, "--alg"))) - lws_jwk_strdup_meta(&jwk, JWK_META_ALG, p, strlen(p)); - - if ((p = lws_cmdline_option(argc, argv, "--key-ops"))) - lws_jwk_strdup_meta(&jwk, JWK_META_KEY_OPS, p, strlen(p)); - - if ((p = lws_cmdline_option(argc, argv, "--public")) && - kty != LWS_GENCRYPTO_KTY_OCT) { - - int fd; - - /* public version */ - - if (lws_jwk_export(&jwk, 0, key, &vl) < 0) { - lwsl_err("lws_jwk_export failed\n"); - - return 1; - } - - fd = open(p, LWS_O_CREAT | LWS_O_TRUNC | LWS_O_WRONLY, 0600); - if (fd < 0) { - lwsl_err("Can't open public key file %s\n", p); - return 1; - } - - if (lws_cmdline_option(argc, argv, "-c")) - format_c(fd, key); - else { - if (write(fd, key, strlen(key)) < 0) { - lwsl_err("Write public failed\n"); - return 1; - } - } - - close(fd); - } - - /* private version */ - - if (lws_jwk_export(&jwk, LWSJWKF_EXPORT_PRIVATE, key, &vl) < 0) { - lwsl_err("lws_jwk_export failed\n"); - - return 1; - } - - if (lws_cmdline_option(argc, argv, "-c")) { - if (format_c(1, key) < 0) - return 1; - } else - if (write(1, key, strlen(key)) < 0) { - lwsl_err("Write stdout failed\n"); - return 1; - } - - lws_jwk_destroy(&jwk); - - lws_context_destroy(context); - - return result; -} diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwk/README.md libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwk/README.md --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jwk/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jwk/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -# lws minimal example for JWK - -Demonstrates how to generate and format any kind of supported new random JWK keys. - -The full private key is output to stdout, a version of the key with the private -part removed and some metadata adapted can be saved to a file at the same time -using `--public `. In the public form, `key_ops` and `use` elements are -adjusted to remove activities that require a private key. - -Key elements are output in strict RFC7638 lexicographic order as required by -some applications. - -Keys produced with openssl and mbedtls backends are completely interchangeable. - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --t |RSA, OCT or EC --b |For RSA and OCT, key size in bits --v |For EC keys, the curve, eg, "P-384"... this implies the key bits ---kid "ID string"|Key identity string ---use "use[ use]"|Key use restriction (mutually exclusive with --key-ops): sig, enc ---alg |Specify the algorithm the key is designed for, eg "RSA1_5" ---key-ops "op[ op]"|Key valid operations (mutually exclusive with --use): sign, verify, encrypt, decrypt, wrapKey, unwrapKey, deriveKey, deriveBits --c|Format the jwk as a linebroken C string ---public |Only output the full, private key, not the public version first - -For legibility the example uses -c, however this - -``` - $ ./lws-crypto-jwk -t EC -v P-256 --key-ops "sign verify" --public mykey.pub -[2018/12/18 20:19:29:6972] USER: LWS JWK example -[2018/12/18 20:19:29:7200] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off -[2018/12/18 20:19:29:7251] NOTICE: lws_jwk_generate: generating ECDSA key on curve P-256 -{"crv":"P-256","d":"eMKM_S4BTL2aiebZLqvxglufV2YX4b3_32DesgEUOaM","key_ops":["sign","verify"],"kty":"EC","x":"OWauiGGtJ60ZegtqlwETQlmO1exTZdWbT2VbUs4a1hg","y":"g_eNOlqPecbguVQArL6Fd4T5xZthBgipNCBypXubPos"} -``` - -The output in `mykey.pub` is: - -``` -{"crv":"P-256","key_ops":["verify"],"kty":"EC","x":"OWauiGGtJ60ZegtqlwETQlmO1exTZdWbT2VbUs4a1hg","y":"g_eNOlqPecbguVQArL6Fd4T5xZthBgipNCBypXubPos"} -``` - -Notice the logging goes out on stderr, the key data goes on stdout. diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jws/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jws/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jws/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jws/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-crypto-jws) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-crypto-jws) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_JOSE 1 requirements) - -if (requirements) - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jws/main.c libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jws/main.c --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jws/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jws/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,209 +0,0 @@ -/* - * lws-crypto-jws - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include - -#define MAX_SIZE (4 * 1024 * 1024) -char temp[MAX_SIZE], compact[MAX_SIZE]; - -int main(int argc, const char **argv) -{ - int n, sign = 0, result = 0, - logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - char *in; - struct lws_context_creation_info info; - struct lws_jws_map map; - int temp_len = sizeof(temp); - struct lws_context *context; - struct lws_jose jose; - struct lws_jwk jwk; - struct lws_jws jws; - const char *p; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS JWS example tool\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = 0; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - lws_jose_init(&jose); - lws_jws_init(&jws, &jwk, context); - - /* if signing, set the ciphers */ - - if ((p = lws_cmdline_option(argc, argv, "-s"))) { - - if (lws_gencrypto_jws_alg_to_definition(p, &jose.alg)) { - lwsl_err("format: -s \"\", eg, " - "-e \"RS256\"\n"); - - return 1; - } - - /* create JOSE header, also needed for output */ - - if (lws_jws_alloc_element(&jws.map, LJWS_JOSE, - lws_concat_temp(temp, temp_len), - &temp_len, strlen(p) + 10, 0)) { - lwsl_err("%s: temp space too small\n", __func__); - return 1; - } - - jws.map.len[LJWS_JOSE] = - lws_snprintf((char *)jws.map.buf[LJWS_JOSE], - temp_len, "{\"alg\":\"%s\"}", p); - sign = 1; - } - - in = lws_concat_temp(temp, temp_len); - n = read(0, in, temp_len); - if (n < 0) { - lwsl_err("Problem reading from stdin\n"); - return 1; - } - temp_len -= n; - - /* grab the key */ - - if ((p = lws_cmdline_option(argc, argv, "-k"))) { - if (lws_jwk_load(&jwk, p, NULL, NULL)) { - lwsl_err("%s: problem loading JWK %s\n", __func__, p); - - return 1; - } - } else { - lwsl_err("-k is required\n"); - - return 1; - } - if (sign) { - - /* add the plaintext from stdin to the map and a b64 version */ - - jws.map.buf[LJWS_PYLD] = in; - jws.map.len[LJWS_PYLD] = n; - - if (lws_jws_encode_b64_element(&jws.map_b64, LJWS_PYLD, - lws_concat_temp(temp, temp_len), - &temp_len, jws.map.buf[LJWS_PYLD], - jws.map.len[LJWS_PYLD])) - goto bail1; - - /* add the b64 JOSE header to the b64 map */ - - if (lws_jws_encode_b64_element(&jws.map_b64, LJWS_JOSE, - lws_concat_temp(temp, temp_len), - &temp_len, jws.map.buf[LJWS_JOSE], - jws.map.len[LJWS_JOSE])) - goto bail1; - - /* prepare the space for the b64 signature in the map */ - - if (lws_jws_alloc_element(&jws.map_b64, LJWS_SIG, - lws_concat_temp(temp, temp_len), - &temp_len, lws_base64_size( - LWS_JWE_LIMIT_KEY_ELEMENT_BYTES), 0)) { - lwsl_err("%s: temp space too small\n", __func__); - goto bail1; - } - - - - /* sign the plaintext */ - - n = lws_jws_sign_from_b64(&jose, &jws, - (char *)jws.map_b64.buf[LJWS_SIG], - jws.map_b64.len[LJWS_SIG]); - if (n < 0) { - lwsl_err("%s: failed signing test packet\n", __func__); - goto bail1; - } - /* set the actual b64 signature size */ - jws.map_b64.len[LJWS_SIG] = n; - - if (lws_cmdline_option(argc, argv, "-f")) - /* create the flattened representation */ - n = lws_jws_write_flattened_json(&jws, compact, sizeof(compact)); - else - /* create the compact JWS representation */ - n = lws_jws_write_compact(&jws, compact, sizeof(compact)); - if (n < 0) { - lwsl_notice("%s: write_compact failed\n", __func__); - goto bail1; - } - - /* dump the compact JWS representation on stdout */ - - if (write(1, compact, strlen(compact)) < 0) { - lwsl_err("Write stdout failed\n"); - goto bail1; - } - - } else { - /* perform the verify directly on the compact representation */ - - if (lws_cmdline_option(argc, argv, "-f")) { - if (lws_jws_sig_confirm_json(in, n, &jws, &jwk, context, - lws_concat_temp(temp, temp_len), - &temp_len) < 0) { - lwsl_notice("%s: confirm rsa sig failed\n", - __func__); - lwsl_hexdump_notice(jws.map.buf[LJWS_JOSE], jws.map.len[LJWS_JOSE]); - lwsl_hexdump_notice(jws.map.buf[LJWS_PYLD], jws.map.len[LJWS_PYLD]); - lwsl_hexdump_notice(jws.map.buf[LJWS_SIG], jws.map.len[LJWS_SIG]); - - lwsl_hexdump_notice(jws.map_b64.buf[LJWS_JOSE], jws.map_b64.len[LJWS_JOSE]); - lwsl_hexdump_notice(jws.map_b64.buf[LJWS_PYLD], jws.map_b64.len[LJWS_PYLD]); - lwsl_hexdump_notice(jws.map_b64.buf[LJWS_SIG], jws.map_b64.len[LJWS_SIG]); - goto bail1; - } - } else { - if (lws_jws_sig_confirm_compact_b64(in, - lws_concat_used(temp, temp_len), - &map, &jwk, context, - lws_concat_temp(temp, temp_len), - &temp_len) < 0) { - lwsl_notice("%s: confirm rsa sig failed\n", - __func__); - goto bail1; - } - } - - lwsl_notice("VALID\n"); - - /* dump the verifed plaintext and return 0 */ - - if (write(1, jws.map.buf[LJWS_PYLD], jws.map.len[LJWS_PYLD]) < 0) { - lwsl_err("Write stdout failed\n"); - goto bail1; - } - } - - result = 0; - -bail1: - lws_jws_destroy(&jws); - lws_jwk_destroy(&jwk); - - lws_context_destroy(context); - - return result; -} diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jws/README.md libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jws/README.md --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-jws/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-jws/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -# lws minimal example for JWS - -Demonstrates how to sign and verify using compact JWS and JWK, providing a -commandline tool for signing and verifying stdin. - -## build - -``` - $ cmake . && make -``` - -## usage - -Stdin is either the plaintext (if signing) or compact JWS (if verifying). - -Stdout is either the JWE (if encrypting) or plaintext (if decrypting). - -You must pass a private or public key JWK file in the -k option if encrypting, -and must pass a private key JWK file in the -k option if decrypting. To be -clear, for asymmetric keys the public part of the key is required to encrypt, -and the private part required to decrypt. - -For convenience, a pair of public and private keys are provided, -`key-rsa-4096.private` and `key-rsa-4096.pub`, these were produced with just - -``` - $ lws-crypto-jwk -t RSA -b 4096 --public key-rsa-4096.pub >key-rsa-4096.private -``` - -Similar keys for EC modes may be produced with - -``` - $ lws-crypto-jwk -t EC -v P-256 --public key-ecdh-p-256.pub >key-ecdh-p-256.private -``` - -JWSs produced with openssl and mbedtls backends are completely interchangeable. - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --s ""|Sign (default is verify), eg, -e "ES256". For verify, the cipher information comes from the input JWS. --k |JWK file to sign or verify with... sign requires the key has its private part --c|Format the JWE as a linebroken C string --f|Output flattened representation (instead of compact by default) - -``` - $ echo -n "plaintext0123456" | ./lws-crypto-jws -s "ES256" -k ec-p256.private -[2018/12/19 16:20:25:6519] USER: LWS JWE example tool -[2018/12/19 16:20:25:6749] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off -eyJhbGciOiJSU0ExXzUiLCAiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.ivFr7qzx-pQ4V_edbjpdvR9OwWL9KmojPE2rXQM52oLtW0BtnxZu2_ezqhsAelyIcaworgfobs3u4bslXHMFbeJJjPb5xD0fBDe64OYXZH1NpUGTMJh9Ka4CrJ2B3xhxe7EByGAuGqmluqE0Yezj7rhSw7vlr5JAwuOJ8FaGa8aZ8ldki5G5h_S2Furlbjdcw3Rrxk7mCoMHcLoqzfZtggMPwGAMFogCqcwUo7oSLbBeGaa6hpMbfSysugseWdr8TzObQKPM52k6iVAlGwRaOg_qdLMgZiYRhHA6nFKTQd7XBbNY6qAS8sPuj7Zz344tF3RSfJ0zX_telG71sOtVv5fMpeDU-eCdpOWlCBfu6J6FQfAFu6SJryM4ajGOif09CwFI5qUQ33SOfQfS_M3nqSyd6Vu5M4lsDrb5wK7_XX5gqUwvI9wicf_8WWR-CQomRF-JvEASnA2SIf8QqYfa8R2rP9q6Md4vwO4EZrtxIsMDPsH-4ZEFu7vDjyy09QfIWWsnEb8-UgpVXensgt2m_2bZ76r1VB8-0nZLMwMyEhaH2wra9vX2FWao5UkmNJ7ht300f4_V6QzMFoePpwCvsufWBW6jcQLB-frCWe6uitWaZHEB4LxmNPKzQSz4QwwTKhpF1jNn8Xh1-w1m-2h0gj-oe-S8QBwPveqhPI1p2fI.snuhUTXHNu5mJ6dEPQqg6g.yl36qC4o0GE4nrquQ2YyCg.Vf0MoT7_kUrZdCNWXhq1DQ -``` - -Notice the logging is on stderr, and the output alone on stdout. - -When signing, the compact representation of the JWS is output on stdout. - -When verifying, if the signature is valid the plaintext is output on stdout -and the tool exits with a 0 exit code. Otherwise nothing is output on stdout -and it exits with a nonzero exit code. - diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-x509/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-x509/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-x509/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-x509/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-crypto-x509) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-crypto-x509) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_JOSE 1 requirements) - -if (requirements) - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-x509/main.c libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-x509/main.c --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-x509/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-x509/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,202 +0,0 @@ -/* - * lws-crypto-x509 - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include -#include - -static int -read_pem(const char *filename, char *pembuf, int pembuf_len) -{ - int n, fd = open(filename, LWS_O_RDONLY); - if (fd == -1) - return -1; - - n = read(fd, pembuf, pembuf_len - 1); - close(fd); - - pembuf[n++] = '\0'; - - return n; -} - -static int -read_pem_c509_cert(struct lws_x509_cert **x509, const char *filename, - char *pembuf, int pembuf_len) -{ - int n; - - n = read_pem(filename, pembuf, pembuf_len); - if (n < 0) - return -1; - - if (lws_x509_create(x509)) { - lwsl_err("%s: failed to create x509\n", __func__); - - return -1; - } - - if (lws_x509_parse_from_pem(*x509, pembuf, n) < 0) { - lwsl_err("%s: unable to parse PEM %s\n", __func__, filename); - lws_x509_destroy(x509); - - return -1; - } - - return 0; -} - -int main(int argc, const char **argv) -{ - int n, result = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_x509_cert *x509 = NULL, *x509_trusted = NULL; - struct lws_context_creation_info info; - struct lws_context *context; - struct lws_jwk jwk; - char pembuf[6144]; - const char *p; - - memset(&jwk, 0, sizeof(jwk)); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS X509 api example\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = 0; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - - p = lws_cmdline_option(argc, argv, "-c"); - if (!p) { - lwsl_err("%s: missing -c \n", __func__); - goto bail; - } - if (read_pem_c509_cert(&x509, p, pembuf, sizeof(pembuf))) { - lwsl_err("%s: unable to read \"%s\": errno %d\n", - __func__, p, errno); - goto bail; - } - - p = lws_cmdline_option(argc, argv, "-t"); - if (p) { - - if (read_pem_c509_cert(&x509_trusted, p, pembuf, - sizeof(pembuf))) { - lwsl_err("%s: unable to read \"%s\": errno %d\n", - __func__, p, errno); - goto bail1; - } - - lwsl_notice("%s: certs loaded OK\n", __func__); - - if (lws_x509_verify(x509, x509_trusted, NULL)) { - lwsl_err("%s: verify failed\n", __func__); - goto bail2; - } - - lwsl_notice("%s: verified OK\n", __func__); - } - - if (x509_trusted) { - - /* show the trusted cert public key as a JWK */ - - if (lws_x509_public_to_jwk(&jwk, x509_trusted, - "P-256,P-384,P-521", 4096)) { - lwsl_err("%s: unable to get trusted cert pubkey as JWK\n", - __func__); - - goto bail2; - } - - if ((p = lws_cmdline_option(argc, argv, "--alg"))) - lws_jwk_strdup_meta(&jwk, JWK_META_ALG, p, strlen(p)); - - lwsl_info("JWK version of trusted cert:\n"); - lws_jwk_dump(&jwk); - lws_jwk_destroy(&jwk); - } - - /* get the cert public key as a JWK */ - - if (lws_x509_public_to_jwk(&jwk, x509, "P-256,P-384,P-521", 4096)) { - lwsl_err("%s: unable to get cert pubkey as JWK\n", __func__); - - goto bail3; - } - lwsl_info("JWK version of cert:\n"); - - if ((p = lws_cmdline_option(argc, argv, "--alg"))) - lws_jwk_strdup_meta(&jwk, JWK_META_ALG, p, strlen(p)); - - lws_jwk_dump(&jwk); - /* only print public if he doesn't provide private */ - if (!lws_cmdline_option(argc, argv, "-p")) { - lwsl_notice("Issuing Cert Public JWK on stdout\n"); - n = sizeof(pembuf); - if (lws_jwk_export(&jwk, 0, pembuf, &n)) - puts(pembuf); - } - - /* if we know where the cert private key is, add that to the cert JWK */ - - p = lws_cmdline_option(argc, argv, "-p"); - if (p) { - n = read_pem(p, pembuf, sizeof(pembuf)); - if (n < 0) { - lwsl_err("%s: unable read privkey %s\n", __func__, p); - - goto bail3; - } - if (lws_x509_jwk_privkey_pem(&jwk, pembuf, n, NULL)) { - lwsl_err("%s: unable to parse privkey %s\n", - __func__, p); - - goto bail3; - } - - if ((p = lws_cmdline_option(argc, argv, "--alg"))) - lws_jwk_strdup_meta(&jwk, JWK_META_ALG, p, strlen(p)); - - lwsl_info("JWK version of cert + privkey:\n"); - lws_jwk_dump(&jwk); - lwsl_notice("Issuing Cert + Private JWK on stdout\n"); - n = sizeof(pembuf); - if (lws_jwk_export(&jwk, LWSJWKF_EXPORT_PRIVATE, pembuf, &n)) - puts(pembuf); - } - - result = 0; - -bail3: - lws_jwk_destroy(&jwk); -bail2: - lws_x509_destroy(&x509_trusted); -bail1: - lws_x509_destroy(&x509); -bail: - lws_context_destroy(context); - - if (result) - lwsl_err("%s: failed\n", __func__); - else - lwsl_notice("%s: OK\n", __func__); - - return result; -} diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-x509/README.md libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-x509/README.md --- libwebsockets-4.0.20/minimal-examples/crypto/minimal-crypto-x509/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/minimal-crypto-x509/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -# lws minimal example for X509 - -The example shows how to: - - - confirm one PEM cert or chain (-c) was signed by a trusted PEM cert (-t) - - convert a certificate public key to JWK - - convert a certificate public key and its private key PEM to a private JWK - -The examples work for EC and RSA certs and on mbedtls and OpenSSL the same. - -Notice the logging is on stderr, and only the JWK is output on stdout. - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --c |Required PEM Certificate(s) to operate on... may be multiple concatednated PEM --t |Single PEM trusted certificate --p |Optional private key matching certificate given in -c. If given, only the private JWK is printed to stdout - -Example for confirming trust relationship. Notice the PEM in -c must contain not only -the final certificate but also the certificates for any intermediate CAs. - -``` - $ ./lws-crypto-x509 -c ec-cert.pem -t ca-cert.pem -[2019/01/02 20:31:13:2031] USER: LWS X509 api example -[2019/01/02 20:31:13:2032] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off -[2019/01/02 20:31:13:2043] NOTICE: main: certs loaded OK -[2019/01/02 20:31:13:2043] NOTICE: main: verified OK <<<<====== -[2019/01/02 20:31:13:2045] NOTICE: Cert Public JWK -{"crv":"P-521","kty":"EC","x":"_uRNBbIbm0zhk8v6ujvQX9924264ZkqJhit0qamAoCegzuJbLf434kN7_aFEt6u-QWUu6-N1R8t6OlvrLo2jrNY","y":"AU-29XpNyB7e5e3s5t0ylzGEnF601A8A7Tx8m8xxngARZX_bn22itGJ3Y57BTcclPMoG80KjWAMnRVtrKqrD_aGD"} - -[2019/01/02 20:31:13:2045] NOTICE: main: OK -``` - -Example creating JWKs for public and public + private cert + PEM keys: - -``` - $ ./lws-crypto-x509 -c ec-cert.pem -p ec-key.pem -[2019/01/02 20:14:43:4966] USER: LWS X509 api example -[2019/01/02 20:14:43:5225] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off -[2019/01/02 20:14:43:5707] NOTICE: lws_x509_public_to_jwk: EC key -[2019/01/02 20:24:59:9514] USER: LWS X509 api example -[2019/01/02 20:24:59:9741] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off -[2019/01/02 20:25:00:1261] NOTICE: lws_x509_public_to_jwk: key type 408 "id-ecPublicKey" -[2019/01/02 20:25:00:1269] NOTICE: lws_x509_public_to_jwk: EC key -[2019/01/02 20:25:00:2097] NOTICE: Cert + Key Private JWK -{"crv":"P-521","d":"AU3iQSKfPskMTW4ZncrYLhipUYzLYty2XhemTQ_nSuUB1vB76jHmOYUTRXFBLkVCW8cQYyMa5dMa3Bvv-cdvH0IB","kty":"EC","x":"_uRNBbIbm0zhk8v6ujvQX9924264ZkqJhit0qamAoCegzuJbLf434kN7_aFEt6u-QWUu6-N1R8t6OlvrLo2jrNY","y":"AU-29XpNyB7e5e3s5t0ylzGEnF601A8A7Tx8m8xxngARZX_bn22itGJ3Y57BTcclPMoG80KjWAMnRVtrKqrD_aGD"} - -[2019/01/02 20:25:00:2207] NOTICE: main: OK -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/crypto/README.md libwebsockets-2.4.2/minimal-examples/crypto/README.md --- libwebsockets-4.0.20/minimal-examples/crypto/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/crypto/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -|name|tests| ----|--- -minimal-crypto-jwe|Examples for lws RFC7516 JWE apis -minimal-crypto-jwk|Examples for lws RFC7517 JWK apis -minimal-crypto-jws|Examples for lws RFC7515 JWS apis -minimal-crypto-x509|Examples for lws X.509 apis - diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-client/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-client/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-client/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-client/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,121 +0,0 @@ -project(lws-minimal-dbus-client) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) -include(CheckLibraryExists) - -set(SAMP lws-minimal-dbus-client) -set(SRCS minimal-dbus-client.c) - -if (NOT LWS_WITH_MINIMAL_EXAMPLES) - CHECK_LIBRARY_EXISTS(dbus-1 dbus_connection_set_watch_functions "" LWS_HAVE_LIBDBUS) - if (NOT LWS_HAVE_LIBDBUS) - message(FATAL_ERROR "Install dbus-devel, or libdbus-1-dev etc") - endif() - - if (NOT LWS_DBUS_LIB) - set(LWS_DBUS_LIB "dbus-1") - endif() - - if (NOT LWS_DBUS_INCLUDE1) - # look in fedora and debian / ubuntu place - if (EXISTS "/usr/include/dbus-1.0") - set(LWS_DBUS_INCLUDE1 "/usr/include/dbus-1.0") - else() - message(FATAL_ERROR "Set LWS_DBUS_INCLUDE1 to /usr/include/dbus-1.0 or wherever the main dbus includes are") - endif() - endif() - - if (NOT LWS_DBUS_INCLUDE2) - # look in fedora... debian / ubuntu has the ARCH in the path... - if (EXISTS "/usr/lib64/dbus-1.0/include") - set(LWS_DBUS_INCLUDE2 "/usr/lib64/dbus-1.0/include") - else() - message(FATAL_ERROR "Set LWS_DBUS_INCLUDE2 to /usr/lib/ARCH-linux-gnu/dbus-1.0/include or wherever dbus-arch-deps.h is on your system") - endif() - endif() - - set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES};${LWS_DBUS_INCLUDE1};${LWS_DBUS_INCLUDE2}) - - if (NOT LWS_DBUS_INCLUDE1 OR NOT LWS_DBUS_INCLUDE2) - message(FATAL_ERROR "To build with libdbus, LWS_DBUS_INCLUDE1/2 must be given. See lib/roles/dbus/README.md") - endif() - -endif() - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_DBUS 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - include_directories("${LWS_DBUS_INCLUDE1}") - include_directories("${LWS_DBUS_INCLUDE2}") - list(APPEND LIB_LIST dbus-1) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared ${LWS_DBUS_LIB}) - else() - target_link_libraries(${SAMP} websockets ${LWS_DBUS_LIB}) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-client/minimal-dbus-client.c libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-client/minimal-dbus-client.c --- libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-client/minimal-dbus-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-client/minimal-dbus-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,281 +0,0 @@ -/* - * lws-minimal-dbus-client - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal session dbus server that uses the lws event loop, - * making it possible to integrate it with other lws features. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -static struct lws_dbus_ctx *dbus_ctx; -static struct lws_context *context; -static int interrupted; - -#define THIS_INTERFACE "org.libwebsockets.test" -#define THIS_OBJECT "/org/libwebsockets/test" -#define THIS_BUSNAME "org.libwebsockets.test" - -#define THIS_LISTEN_PATH "unix:abstract=org.libwebsockets.test" - - -static DBusHandlerResult -client_message_handler(DBusConnection *conn, DBusMessage *message, void *data) -{ - const char *str; - - lwsl_info("%s: Got D-Bus request: %s.%s on %s\n", __func__, - dbus_message_get_interface(message), - dbus_message_get_member(message), - dbus_message_get_path(message)); - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &str, - DBUS_TYPE_INVALID)) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - lwsl_notice("%s: '%s'\n", __func__, str); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static void -destroy_dbus_client_conn(struct lws_dbus_ctx *ctx) -{ - if (!ctx || !ctx->conn) - return; - - lwsl_notice("%s\n", __func__); - - dbus_connection_remove_filter(ctx->conn, client_message_handler, ctx); - dbus_connection_close(ctx->conn); - dbus_connection_unref(ctx->conn); - - free(ctx); -} - -/* - * This callback is coming when lws has noticed the fd took a POLLHUP. The - * ctx has effectively gone out of scope before this, and the connection can - * be cleaned up and the ctx freed. - */ - -static void -cb_closing(struct lws_dbus_ctx *ctx) -{ - lwsl_err("%s: closing\n", __func__); - - if (ctx == dbus_ctx) - dbus_ctx = NULL; - - destroy_dbus_client_conn(ctx); -} - -static struct lws_dbus_ctx * -create_dbus_client_conn(struct lws_vhost *vh, int tsi, const char *ads) -{ - struct lws_dbus_ctx *ctx; - DBusError err; - - ctx = malloc(sizeof(*ctx)); - if (!ctx) - return NULL; - - memset(ctx, 0, sizeof(*ctx)); - - ctx->vh = vh; - ctx->tsi = tsi; - - dbus_error_init(&err); - - /* connect to the daemon bus */ - ctx->conn = dbus_connection_open_private(ads, &err); - if (!ctx->conn) { - lwsl_err("%s: Failed to connect: %s\n", - __func__, err.message); - goto fail; - } - - dbus_connection_set_exit_on_disconnect(ctx->conn, 0); - - if (!dbus_connection_add_filter(ctx->conn, client_message_handler, - ctx, NULL)) { - lwsl_err("%s: Failed to add filter\n", __func__); - goto fail; - } - - /* - * This is the part that binds the connection to lws watcher and - * timeout handling provided by lws - */ - - if (lws_dbus_connection_setup(ctx, ctx->conn, cb_closing)) { - lwsl_err("%s: connection bind to lws failed\n", __func__); - goto fail; - } - - lwsl_notice("%s: created OK\n", __func__); - - return ctx; - -fail: - dbus_error_free(&err); - - free(ctx); - - return NULL; -} - - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -/* - * This gets called if we timed out waiting for the server reply, or the - * reply arrived. - */ - -static void -pending_call_notify(DBusPendingCall *pending, void *data) -{ - // struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data; - const char *payload; - DBusMessage *msg; - - if (!dbus_pending_call_get_completed(pending)) { - lwsl_err("%s: timed out waiting for reply\n", __func__); - - goto bail; - } - - msg = dbus_pending_call_steal_reply(pending); - if (!msg) - goto bail; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &payload, - DBUS_TYPE_INVALID)) { - goto bail1; - } - - lwsl_user("%s: received '%s'\n", __func__, payload); - -bail1: - dbus_message_unref(msg); -bail: - dbus_pending_call_unref(pending); -} - -static int -remote_method_call(struct lws_dbus_ctx *ctx) -{ - DBusMessage *msg; - const char *payload = "Hello!"; - int ret = 1; - - msg = dbus_message_new_method_call( - /* dest */ THIS_BUSNAME, - /* object-path */ THIS_OBJECT, - /* interface */ THIS_INTERFACE, - /* method */ "Echo"); - if (!msg) - return 1; - - if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &payload, - DBUS_TYPE_INVALID)) - goto bail; - - if (!dbus_connection_send_with_reply(ctx->conn, msg, - &ctx->pc, - DBUS_TIMEOUT_USE_DEFAULT)) { - lwsl_err("%s: unable to send\n", __func__); - - goto bail; - } - - dbus_pending_call_set_notify(ctx->pc, pending_call_notify, ctx, NULL); - - ret = 0; - -bail: - dbus_message_unref(msg); - - return ret; -} - -int main(int argc, const char **argv) -{ - struct lws_vhost *vh; - struct lws_context_creation_info info; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */ /* | LLL_THREAD */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal DBUS client\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - vh = lws_create_vhost(context, &info); - if (!vh) - goto bail; - - dbus_ctx = create_dbus_client_conn(vh, 0, THIS_LISTEN_PATH); - if (!dbus_ctx) - goto bail1; - - if (remote_method_call(dbus_ctx)) - goto bail2; - - /* lws event loop (default poll one) */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail2: - destroy_dbus_client_conn(dbus_ctx); - -bail1: - /* this is required for valgrind-cleanliness */ - dbus_shutdown(); - lws_context_destroy(context); - - lwsl_notice("Exiting cleanly\n"); - - return 0; - -bail: - lwsl_err("%s: failed to start\n", __func__); - lws_context_destroy(context); - - return 1; -} diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-client/README.md libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-client/README.md --- libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-client/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-client/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -# lws minimal dbus client - -This demonstrates nonblocking, asynchronous dbus method calls as the client. - -## build - -Using libdbus requires additional non-default include paths setting, same as -is necessary for lws build described in ./lib/roles/dbus/README.md - -CMake can guess one path and the library name usually, see the README above -for details of how to override for custom libdbus and cross build. - -Fedora example: -``` -$ cmake .. -DLWS_DBUS_INCLUDE2="/usr/lib64/dbus-1.0/include" -$ make -``` - -Ubuntu example: -``` -$ cmake .. -DLWS_DBUS_INCLUDE2="/usr/lib/x86_64-linux-gnu/dbus-1.0/include" -$ make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 - -The minimal client connects to the minimal dbus server example, which is -expected to be listening on its default abstract unix domain socket path. - -It call the server Echo method with "Hello!" and returns to the event loop. -When the reply comes, it prints the returned message. - -Afterwards it just sits there receiving unsolicited messages from the server -example, until closed by the user. - -``` - $ ./lws-minimal-dbus-client -ctx -[2018/10/05 06:08:31:4901] NOTICE: pending_call_notify -[2018/10/05 06:08:31:4929] USER: pending_call_notify: received 'Hello!' -^C[2018/10/05 06:09:22:4409] NOTICE: destroy_dbus_client_conn -[2018/10/05 06:09:22:4691] NOTICE: Exiting cleanly -... -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,121 +0,0 @@ -project(lws-minimal-dbus-ws-proxy-testclient) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) -include(CheckLibraryExists) - -set(SAMP lws-minimal-dbus-ws-proxy-testclient) -set(SRCS minimal-dbus-ws-proxy-testclient.c) - -if (NOT LWS_WITH_MINIMAL_EXAMPLES) - CHECK_LIBRARY_EXISTS(dbus-1 dbus_connection_set_watch_functions "" LWS_HAVE_LIBDBUS) - if (NOT LWS_HAVE_LIBDBUS) - message(FATAL_ERROR "Install dbus-devel, or libdbus-1-dev etc") - endif() - - if (NOT LWS_DBUS_LIB) - set(LWS_DBUS_LIB "dbus-1") - endif() - - if (NOT LWS_DBUS_INCLUDE1) - # look in fedora and debian / ubuntu place - if (EXISTS "/usr/include/dbus-1.0") - set(LWS_DBUS_INCLUDE1 "/usr/include/dbus-1.0") - else() - message(FATAL_ERROR "Set LWS_DBUS_INCLUDE1 to /usr/include/dbus-1.0 or wherever the main dbus includes are") - endif() - endif() - - if (NOT LWS_DBUS_INCLUDE2) - # look in fedora... debian / ubuntu has the ARCH in the path... - if (EXISTS "/usr/lib64/dbus-1.0/include") - set(LWS_DBUS_INCLUDE2 "/usr/lib64/dbus-1.0/include") - else() - message(FATAL_ERROR "Set LWS_DBUS_INCLUDE2 to /usr/lib/ARCH-linux-gnu/dbus-1.0/include or wherever dbus-arch-deps.h is on your system") - endif() - endif() - - set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES};${LWS_DBUS_INCLUDE1};${LWS_DBUS_INCLUDE2}) - - if (NOT LWS_DBUS_INCLUDE1 OR NOT LWS_DBUS_INCLUDE2) - message(FATAL_ERROR "To build with libdbus, LWS_DBUS_INCLUDE1/2 must be given. See lib/roles/dbus/README.md") - endif() - -endif() - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_DBUS 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - include_directories("${LWS_DBUS_INCLUDE1}") - include_directories("${LWS_DBUS_INCLUDE2}") - list(APPEND LIB_LIST dbus-1) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared ${LWS_DBUS_LIB}) - else() - target_link_libraries(${SAMP} websockets ${LWS_DBUS_LIB}) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/minimal-dbus-ws-proxy-testclient.c libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/minimal-dbus-ws-proxy-testclient.c --- libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/minimal-dbus-ws-proxy-testclient.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/minimal-dbus-ws-proxy-testclient.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,459 +0,0 @@ -/* - * lws-minimal-dbus-ws-proxy-testclient - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This acts as a test client over DBUS, opening a session with - * minimal-dbus-ws-proxy and sending and receiving data on the libwebsockets - * mirror demo page. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -/* - * These are the various states our connection can be in, both with regards - * to the direct connection to the proxy, and the state of the onward ws - * connection the proxy opens at our request. - */ - -enum lws_dbus_client_state { - LDCS_NOTHING, /* no connection yet */ - LDCS_CONN, /* conn to proxy */ - LDCS_CONN_WAITING_ONWARD, /* conn to proxy, awaiting proxied conn */ - LDCS_CONN_ONWARD, /* conn to proxy and onward conn OK */ - LDCS_CONN_CLOSED, /* conn to proxy but onward conn closed */ - LDCS_CLOSED, /* connection to proxy is closed */ -}; - -/* - * our expanded dbus context - */ - -struct lws_dbus_ctx_wsproxy_client { - struct lws_dbus_ctx ctx; - - enum lws_dbus_client_state state; -}; - -static struct lws_dbus_ctx_wsproxy_client *dbus_ctx; -static struct lws_context *context; -static int interrupted, autoexit_budget = -1, count_rx, count_tx; - -#define THIS_INTERFACE "org.libwebsockets.wsclientproxy" -#define THIS_OBJECT "/org/libwebsockets/wsclientproxy" -#define THIS_BUSNAME "org.libwebsockets.wsclientproxy" - -#define THIS_LISTEN_PATH "unix:abstract=org.libwebsockets.wsclientproxy" - -static void -state_transition(struct lws_dbus_ctx_wsproxy_client *dcwc, - enum lws_dbus_client_state state) -{ - lwsl_notice("%s: %p: from state %d -> %d\n", __func__, - dcwc,dcwc->state, state); - dcwc->state = state; -} - -static DBusHandlerResult -filter(DBusConnection *conn, DBusMessage *message, void *data) -{ - struct lws_dbus_ctx_wsproxy_client *dcwc = - (struct lws_dbus_ctx_wsproxy_client *)data; - const char *str; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &str, - DBUS_TYPE_INVALID)) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - /* received ws data */ - - if (dbus_message_is_signal(message, THIS_INTERFACE, "Receive")) { - lwsl_user("%s: Received '%s'\n", __func__, str); - count_rx++; - } - - /* proxy ws connection failed */ - - if (dbus_message_is_signal(message, THIS_INTERFACE, "Status") && - !strcmp(str, "ws client connection error")) - state_transition(dcwc, LDCS_CONN_CLOSED); - - /* proxy ws connection succeeded */ - - if (dbus_message_is_signal(message, THIS_INTERFACE, "Status") && - !strcmp(str, "ws client connection established")) - state_transition(dcwc, LDCS_CONN_ONWARD); - - /* proxy ws connection has closed */ - - if (dbus_message_is_signal(message, THIS_INTERFACE, "Status") && - !strcmp(str, "ws client connection closed")) - state_transition(dcwc, LDCS_CONN_CLOSED); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static void -destroy_dbus_client_conn(struct lws_dbus_ctx_wsproxy_client **pdcwc) -{ - struct lws_dbus_ctx_wsproxy_client *dcwc = *pdcwc; - - if (!dcwc || !dcwc->ctx.conn) - return; - - lwsl_notice("%s\n", __func__); - - dbus_connection_remove_filter(dcwc->ctx.conn, filter, &dcwc->ctx); - dbus_connection_close(dcwc->ctx.conn); - dbus_connection_unref(dcwc->ctx.conn); - - free(dcwc); - - *pdcwc = NULL; -} - -/* - * This callback is coming when lws has noticed the fd took a POLLHUP. The - * ctx has effectively gone out of scope before this, and the connection can - * be cleaned up and the ctx freed. - */ - -static void -cb_closing(struct lws_dbus_ctx *ctx) -{ - struct lws_dbus_ctx_wsproxy_client *dcwc = - (struct lws_dbus_ctx_wsproxy_client *)ctx; - - lwsl_err("%s: closing\n", __func__); - - if (dcwc == dbus_ctx) - dbus_ctx = NULL; - - destroy_dbus_client_conn(&dcwc); - - interrupted = 1; -} - -static struct lws_dbus_ctx_wsproxy_client * -create_dbus_client_conn(struct lws_vhost *vh, int tsi, const char *ads) -{ - struct lws_dbus_ctx_wsproxy_client *dcwc; - DBusError e; - - dcwc = malloc(sizeof(*dcwc)); - if (!dcwc) - return NULL; - - memset(dcwc, 0, sizeof(*dcwc)); - - dcwc->state = LDCS_NOTHING; - dcwc->ctx.vh = vh; - dcwc->ctx.tsi = tsi; - - dbus_error_init(&e); - - lwsl_user("%s: connecting to '%s'\n", __func__, ads); -#if 1 - /* connect to our daemon bus */ - - dcwc->ctx.conn = dbus_connection_open_private(ads, &e); - if (!dcwc->ctx.conn) { - lwsl_err("%s: Failed to connect: %s\n", - __func__, e.message); - goto fail; - } -#else - /* connect to the SYSTEM bus */ - - dcwc->ctx.conn = dbus_bus_get(DBUS_BUS_SYSTEM, &e); - if (!dcwc->ctx.conn) { - lwsl_err("%s: Failed to get a session DBus connection: %s\n", - __func__, e.message); - goto fail; - } -#endif - dbus_connection_set_exit_on_disconnect(dcwc->ctx.conn, 0); - - if (!dbus_connection_add_filter(dcwc->ctx.conn, filter, - &dcwc->ctx, NULL)) { - lwsl_err("%s: Failed to add filter\n", __func__); - goto fail; - } - - /* - * This is the part that binds the connection to lws watcher and - * timeout handling provided by lws - */ - - if (lws_dbus_connection_setup(&dcwc->ctx, dcwc->ctx.conn, cb_closing)) { - lwsl_err("%s: connection bind to lws failed\n", __func__); - goto fail; - } - - state_transition(dcwc, LDCS_CONN); - - lwsl_notice("%s: created OK\n", __func__); - - return dcwc; - -fail: - dbus_error_free(&e); - - free(dcwc); - - return NULL; -} - - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -/* - * This gets called if we timed out waiting for the dbus server reply, or the - * reply arrived. - */ - -static void -pending_call_notify(DBusPendingCall *pending, void *data) -{ - const char *payload; - DBusMessage *msg; - - if (!dbus_pending_call_get_completed(pending)) { - lwsl_err("%s: timed out waiting for reply\n", __func__); - - goto bail; - } - - msg = dbus_pending_call_steal_reply(pending); - if (!msg) - goto bail; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &payload, - DBUS_TYPE_INVALID)) { - goto bail1; - } - - lwsl_user("%s: received '%s'\n", __func__, payload); - -bail1: - dbus_message_unref(msg); -bail: - dbus_pending_call_unref(pending); -} - -static int -remote_method_call(struct lws_dbus_ctx_wsproxy_client *dcwc) -{ - char _uri[96]; - const char *subprotocol = "lws-mirror-protocol", *uri = _uri; - DBusMessage *msg; - int ret = 1; - - /* - * make our own private mirror session... because others may run this - * at the same time against libwebsockets.org... as happened 2019-03-14 - * and broke travis tests :-) - */ - - lws_snprintf(_uri, sizeof(_uri), "wss://libwebsockets.org/?mirror=dbt-%d", - (int)getpid()); - - msg = dbus_message_new_method_call( - /* dest */ THIS_BUSNAME, - /* object-path */ THIS_OBJECT, - /* interface */ THIS_INTERFACE, - /* method */ "Connect"); - if (!msg) - return 1; - - if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &uri, - DBUS_TYPE_STRING, &subprotocol, - DBUS_TYPE_INVALID)) - goto bail; - - lwsl_user("%s: requesting proxy connection %s %s\n", __func__, - uri, subprotocol); - - if (!dbus_connection_send_with_reply(dcwc->ctx.conn, msg, &dcwc->ctx.pc, - DBUS_TIMEOUT_USE_DEFAULT)) { - lwsl_err("%s: unable to send\n", __func__); - - goto bail; - } - - dbus_pending_call_set_notify(dcwc->ctx.pc, pending_call_notify, - &dcwc->ctx, NULL); - - state_transition(dcwc, LDCS_CONN_WAITING_ONWARD); - - ret = 0; - -bail: - dbus_message_unref(msg); - - return ret; -} - -/* - * Stub lws protocol, just so we can get synchronous timers conveniently. - * - * Set up a 1Hz timer and if our connection state is suitable, use that - * to write mirror protocol drawing packets to the proxied ws connection - */ - -static int -callback_just_timer(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - char payload[64]; - const char *ws_pkt = payload; - DBusMessage *msg; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - case LWS_CALLBACK_USER: - lwsl_info("%s: LWS_CALLBACK_USER\n", __func__); - - if (!dbus_ctx || dbus_ctx->state != LDCS_CONN_ONWARD) - goto again; - - if (autoexit_budget > 0) { - if (!--autoexit_budget) { - lwsl_notice("reached autoexit budget\n"); - interrupted = 1; - break; - } - } - - msg = dbus_message_new_method_call(THIS_BUSNAME, THIS_OBJECT, - THIS_INTERFACE, "Send"); - if (!msg) - break; - - lws_snprintf(payload, sizeof(payload), "d #%06X %d %d %d %d;", - rand() & 0xffffff, rand() % 480, rand() % 300, - rand() % 480, rand() % 300); - - if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &ws_pkt, - DBUS_TYPE_INVALID)) { - dbus_message_unref(msg); - break; - } - - if (!dbus_connection_send_with_reply(dbus_ctx->ctx.conn, msg, - &dbus_ctx->ctx.pc, - DBUS_TIMEOUT_USE_DEFAULT)) { - lwsl_err("%s: unable to send\n", __func__); - dbus_message_unref(msg); - break; - } - - dbus_message_unref(msg); - dbus_pending_call_set_notify(dbus_ctx->ctx.pc, - pending_call_notify, - &dbus_ctx->ctx, NULL); - count_tx++; - -again: - lws_timed_callback_vh_protocol(lws_get_vhost(wsi), - lws_get_protocol(wsi), - LWS_CALLBACK_USER, 2); - break; - default: - break; - } - - return 0; -} - -static struct lws_protocols protocols[] = { - { "_just_timer", callback_just_timer, 0, 10, 0, NULL, 0 }, - { } -}; - - -int main(int argc, const char **argv) -{ - struct lws_vhost *vh; - struct lws_context_creation_info info; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */ /* | LLL_THREAD */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - if ((p = lws_cmdline_option(argc, argv, "-x"))) - autoexit_budget = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal DBUS ws proxy testclient\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; - info.protocols = protocols; - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - info.options |= - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - vh = lws_create_vhost(context, &info); - if (!vh) - goto bail; - - dbus_ctx = create_dbus_client_conn(vh, 0, THIS_LISTEN_PATH); - if (!dbus_ctx) - goto bail1; - - if (remote_method_call(dbus_ctx)) - goto bail2; - - /* lws event loop (default poll one) */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail2: - destroy_dbus_client_conn(&dbus_ctx); - -bail1: - /* this is required for valgrind-cleanliness */ - dbus_shutdown(); - lws_context_destroy(context); - - lwsl_notice("Exiting cleanly, rx: %d, tx: %d\n", count_rx, count_tx); - - return 0; - -bail: - lwsl_err("%s: failed to start\n", __func__); - lws_context_destroy(context); - - return 1; -} diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/README.md libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/README.md --- libwebsockets-4.0.20/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-client/minimal-dbus-ws-proxy-testclient/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -# lws minimal dbus ws proxy testclient - -This is a test client used to test `./minimal-examples/dbus-server/minimal-dbus-ws-proxy` - -It asks the minimal dbus ws proxy application to connect to libwebsockets.org -over the mirror protocol. And it proxies back the ASCII packets used to -communicate the mirror sample drawing vectors over dbus to this test client -if you draw on the [mirror example app](https://libwebsockets.org/testserver/) -in a browser. - -## build - -Using libdbus requires additional non-default include paths setting, same as -is necessary for lws build described in ./lib/roles/dbus/README.md - -CMake can guess one path and the library name usually, see the README above -for details of how to override for custom libdbus and cross build. - -Fedora example: -``` -$ cmake .. -DLWS_DBUS_INCLUDE2="/usr/lib64/dbus-1.0/include" -$ make -``` - -Ubuntu example: -``` -$ cmake .. -DLWS_DBUS_INCLUDE2="/usr/lib/x86_64-linux-gnu/dbus-1.0/include" -$ make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 - -This connects to the minimal-dbus-ws-proxy example running in another terminal. - -``` - $ ./lws-minimal-dbus-ws-proxy-testclient -[2018/10/05 14:17:16:6286] USER: LWS minimal DBUS ws proxy testclient -[2018/10/05 14:17:16:6538] NOTICE: Creating Vhost 'default' port 0, 1 protocols, IPv6 off -[2018/10/05 14:17:16:6617] USER: create_dbus_client_conn: connecting to 'unix:abstract=org.libwebsockets.wsclientproxy' -[2018/10/05 14:17:16:7189] NOTICE: create_dbus_client_conn: created OK -[2018/10/05 14:17:16:7429] USER: remote_method_call: requesting proxy connection wss://libwebsockets.org/ lws-mirror-protocol -[2018/10/05 14:17:17:0387] USER: pending_call_notify: received 'Connecting' -[2018/10/05 14:17:18:7475] NOTICE: client_message_handler: (type 7) 'ws client connection established' -[2018/10/05 14:17:21:2028] NOTICE: client_message_handler: (type 6) 'd #000000 323 63 323 67;' -[2018/10/05 14:17:21:2197] NOTICE: client_message_handler: (type 6) 'd #000000 323 67 327 73;' -... -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-client/README.md libwebsockets-2.4.2/minimal-examples/dbus-client/README.md --- libwebsockets-4.0.20/minimal-examples/dbus-client/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-client/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -|Example|Demonstrates| ----|--- -minimal-dbus-client|Shows how to connect to a DBusServer dbus server like minimal-dbus-server -minimal-dbus-ws-proxy-testclient|A test client for use with minimal-dbus-ws-proxy diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-server/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-server/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-server/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-server/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,121 +0,0 @@ -project(lws-minimal-dbus-server) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) -include(CheckLibraryExists) - -set(SAMP lws-minimal-dbus-server) -set(SRCS main.c) - -if (NOT LWS_WITH_MINIMAL_EXAMPLES) - CHECK_LIBRARY_EXISTS(dbus-1 dbus_connection_set_watch_functions "" LWS_HAVE_LIBDBUS) - if (NOT LWS_HAVE_LIBDBUS) - message(FATAL_ERROR "Install dbus-devel, or libdbus-1-dev etc") - endif() - - if (NOT LWS_DBUS_LIB) - set(LWS_DBUS_LIB "dbus-1") - endif() - - if (NOT LWS_DBUS_INCLUDE1) - # look in fedora and debian / ubuntu place - if (EXISTS "/usr/include/dbus-1.0") - set(LWS_DBUS_INCLUDE1 "/usr/include/dbus-1.0") - else() - message(FATAL_ERROR "Set LWS_DBUS_INCLUDE1 to /usr/include/dbus-1.0 or wherever the main dbus includes are") - endif() - endif() - - if (NOT LWS_DBUS_INCLUDE2) - # look in fedora... debian / ubuntu has the ARCH in the path... - if (EXISTS "/usr/lib64/dbus-1.0/include") - set(LWS_DBUS_INCLUDE2 "/usr/lib64/dbus-1.0/include") - else() - message(FATAL_ERROR "Set LWS_DBUS_INCLUDE2 to /usr/lib/ARCH-linux-gnu/dbus-1.0/include or wherever dbus-arch-deps.h is on your system") - endif() - endif() - - set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES};${LWS_DBUS_INCLUDE1};${LWS_DBUS_INCLUDE2}) - - if (NOT LWS_DBUS_INCLUDE1 OR NOT LWS_DBUS_INCLUDE2) - message(FATAL_ERROR "To build with libdbus, LWS_DBUS_INCLUDE1/2 must be given. See lib/roles/dbus/README.md") - endif() - -endif() - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_DBUS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - include_directories("${LWS_DBUS_INCLUDE1}") - include_directories("${LWS_DBUS_INCLUDE2}") - list(APPEND LIB_LIST dbus-1) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared ${LWS_DBUS_LIB}) - else() - target_link_libraries(${SAMP} websockets ${LWS_DBUS_LIB}) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-server/main.c libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-server/main.c --- libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-server/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-server/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,535 +0,0 @@ -/* - * lws-minimal-dbus-server - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal session dbus server that uses the lws event loop, - * making it possible to integrate it with other lws features. - * - * The dbus server parts are based on "Sample code illustrating basic use of - * D-BUS" (presumed Public Domain) here: - * - * https://github.com/fbuihuu/samples-dbus/blob/master/dbus-server.c - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -static struct lws_context *context; -static const char *version = "0.1"; -static int interrupted; -static struct lws_dbus_ctx dbus_ctx, ctx_listener; -static char session; - -#define THIS_INTERFACE "org.libwebsockets.test" -#define THIS_OBJECT "/org/libwebsockets/test" -#define THIS_BUSNAME "org.libwebsockets.test" - -#define THIS_LISTEN_PATH "unix:abstract=org.libwebsockets.test" - -static const char * -server_introspection_xml = - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " " - " \n" - - "\n"; - -static DBusHandlerResult -dmh_introspect(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - dbus_message_append_args(*reply, DBUS_TYPE_STRING, - &server_introspection_xml, DBUS_TYPE_INVALID); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static DBusHandlerResult -dmh_get(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - const char *interface, *property; - DBusError err; - - dbus_error_init(&err); - - if (!dbus_message_get_args(m, &err, DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - dbus_message_unref(*reply); - *reply = dbus_message_new_error(m, err.name, err.message); - dbus_error_free(&err); - - return DBUS_HANDLER_RESULT_HANDLED; - } - - if (strcmp(property, "Version")) /* Unknown property */ - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - dbus_message_append_args(*reply, DBUS_TYPE_STRING, &version, - DBUS_TYPE_INVALID); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static DBusHandlerResult -dmh_getall(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - DBusMessageIter arr, di, iter, va; - const char *property = "Version"; - - dbus_message_iter_init_append(*reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr); - - /* Append all properties name/value pairs */ - dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, NULL, &di); - dbus_message_iter_append_basic(&di, DBUS_TYPE_STRING, &property); - dbus_message_iter_open_container(&di, DBUS_TYPE_VARIANT, "s", &va); - dbus_message_iter_append_basic(&va, DBUS_TYPE_STRING, &version); - dbus_message_iter_close_container(&di, &va); - dbus_message_iter_close_container(&arr, &di); - - dbus_message_iter_close_container(&iter, &arr); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static DBusHandlerResult -dmh_ping(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - const char *pong = "Pong"; - - dbus_message_append_args(*reply, DBUS_TYPE_STRING, &pong, - DBUS_TYPE_INVALID); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static DBusHandlerResult -dmh_echo(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - const char *msg; - DBusError err; - - dbus_error_init(&err); - - if (!dbus_message_get_args(m, &err, DBUS_TYPE_STRING, - &msg, DBUS_TYPE_INVALID)) { - dbus_message_unref(*reply); - *reply = dbus_message_new_error(m, err.name, err.message); - dbus_error_free(&err); - - return DBUS_HANDLER_RESULT_HANDLED; - } - - dbus_message_append_args(*reply, DBUS_TYPE_STRING, &msg, - DBUS_TYPE_INVALID); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static DBusHandlerResult -dmh_emit_signal(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - DBusMessage *r = dbus_message_new_signal(THIS_OBJECT, THIS_INTERFACE, - "OnEmitSignal"); - - if (!r) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (!dbus_connection_send(c, r, NULL)) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - /* and send the original empty reply after */ - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static DBusHandlerResult -dmh_emit_quit(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - interrupted = 1; - - return DBUS_HANDLER_RESULT_HANDLED; -} - -struct lws_dbus_methods { - const char *inter; - const char *call; - lws_dbus_message_handler handler; -} meths[] = { - { DBUS_INTERFACE_INTROSPECTABLE, "Introspect", dmh_introspect }, - { DBUS_INTERFACE_PROPERTIES, "Get", dmh_get }, - { DBUS_INTERFACE_PROPERTIES, "GetAll", dmh_getall }, - { THIS_INTERFACE, "Ping", dmh_ping }, - { THIS_INTERFACE, "Echo", dmh_echo }, - { THIS_INTERFACE, "EmitSignal", dmh_emit_signal }, - { THIS_INTERFACE, "Quit", dmh_emit_quit }, -}; - -static DBusHandlerResult -server_message_handler(DBusConnection *conn, DBusMessage *message, void *data) -{ - struct lws_dbus_methods *mp = meths; - DBusHandlerResult result; - DBusMessage *reply = NULL; - size_t n; - - lwsl_info("%s: Got D-Bus request: %s.%s on %s\n", __func__, - dbus_message_get_interface(message), - dbus_message_get_member(message), - dbus_message_get_path(message)); - - for (n = 0; n < LWS_ARRAY_SIZE(meths); n++) { - if (dbus_message_is_method_call(message, mp->inter, mp->call)) { - reply = dbus_message_new_method_return(message); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - result = mp->handler(conn, message, &reply, data); - - if (result == DBUS_HANDLER_RESULT_HANDLED && - !dbus_connection_send(conn, reply, NULL)) - result = DBUS_HANDLER_RESULT_NEED_MEMORY; - - dbus_message_unref(reply); - - return result; - } - - mp++; - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static const DBusObjectPathVTable server_vtable = { - .message_function = server_message_handler -}; - -static void -destroy_dbus_server_conn(struct lws_dbus_ctx *ctx) -{ - if (!ctx->conn) - return; - - lwsl_notice("%s\n", __func__); - - dbus_connection_unregister_object_path(ctx->conn, THIS_OBJECT); - lws_dll2_remove(&ctx->next); - dbus_connection_unref(ctx->conn); -} - -static void -cb_closing(struct lws_dbus_ctx *ctx) -{ - lwsl_err("%s: closing\n", __func__); - destroy_dbus_server_conn(ctx); - - free(ctx); -} - - -static void -new_conn(DBusServer *server, DBusConnection *conn, void *data) -{ - struct lws_dbus_ctx *conn_ctx, *ctx = (struct lws_dbus_ctx *)data; - - lwsl_notice("%s: vh %s\n", __func__, lws_get_vhost_name(ctx->vh)); - - conn_ctx = malloc(sizeof(*conn_ctx)); - if (!conn_ctx) - return; - - memset(conn_ctx, 0, sizeof(*conn_ctx)); - - conn_ctx->tsi = ctx->tsi; - conn_ctx->vh = ctx->vh; - conn_ctx->conn = conn; - - if (lws_dbus_connection_setup(conn_ctx, conn, cb_closing)) { - lwsl_err("%s: connection bind to lws failed\n", __func__); - goto bail; - } - - if (!dbus_connection_register_object_path(conn, THIS_OBJECT, - &server_vtable, conn_ctx)) { - lwsl_err("%s: Failed to register object path\n", __func__); - goto bail; - } - - lws_dll2_add_head(&conn_ctx->next, &ctx->owner); - - /* we take on responsibility for explicit close / unref with this... */ - dbus_connection_ref(conn); - - return; - -bail: - free(conn_ctx); -} - -static int -create_dbus_listener(const char *ads) -{ - DBusError e; - - dbus_error_init(&e); - - if (!lws_dbus_server_listen(&ctx_listener, ads, &e, new_conn)) { - lwsl_err("%s: failed\n", __func__); - dbus_error_free(&e); - - return 1; - } - - return 0; -} - -static int -create_dbus_server_conn(struct lws_dbus_ctx *ctx, DBusBusType type) -{ - DBusError err; - int rv; - - dbus_error_init(&err); - - /* connect to the daemon bus */ - ctx->conn = dbus_bus_get(type, &err); - if (!ctx->conn) { - lwsl_err("%s: Failed to get a session DBus connection: %s\n", - __func__, err.message); - goto fail; - } - - /* - * by default dbus will call exit() when this connection closes... - * we have to shut down other things cleanly, so disable that - */ - dbus_connection_set_exit_on_disconnect(ctx->conn, 0); - - rv = dbus_bus_request_name(ctx->conn, THIS_BUSNAME, - DBUS_NAME_FLAG_REPLACE_EXISTING, &err); - if (rv != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - lwsl_err("%s: Failed to request name on bus: %s\n", - __func__, err.message); - goto fail; - } - - if (!dbus_connection_register_object_path(ctx->conn, THIS_OBJECT, - &server_vtable, NULL)) { - lwsl_err("%s: Failed to register object path for TestObject\n", - __func__); - dbus_bus_release_name(ctx->conn, THIS_BUSNAME, &err); - goto fail; - } - - /* - * This is the part that binds the connection to lws watcher and - * timeout handling provided by lws - */ - - if (lws_dbus_connection_setup(ctx, ctx->conn, cb_closing)) { - lwsl_err("%s: connection bind to lws failed\n", __func__); - goto fail; - } - - lwsl_notice("%s: created OK\n", __func__); - - return 0; - -fail: - dbus_error_free(&err); - - return 1; -} - -/* - * Cleanly release the connection - */ - -static void -destroy_dbus_server_listener(struct lws_dbus_ctx *ctx) -{ - dbus_server_disconnect(ctx->dbs); - - lws_start_foreach_dll_safe(struct lws_dll2 *, rdt, nx, - ctx->owner.head) { - struct lws_dbus_ctx *r = - lws_container_of(rdt, struct lws_dbus_ctx, next); - - dbus_connection_close(r->conn); - dbus_connection_unref(r->conn); - free(r); - } lws_end_foreach_dll_safe(rdt, nx); - - dbus_server_unref(ctx->dbs); -} - -/* - * DBUS can send messages outside the usual client-initiated RPC concept. - * - * You can receive them using a message filter. - */ - -static void -spam_connected_clients(struct lws_dbus_ctx *ctx) -{ - - /* send connected clients an unsolicited message */ - - lws_start_foreach_dll_safe(struct lws_dll2 *, rdt, nx, - ctx->owner.head) { - struct lws_dbus_ctx *r = - lws_container_of(rdt, struct lws_dbus_ctx, next); - - - DBusMessage *msg; - const char *payload = "Unsolicited message"; - - msg = dbus_message_new(DBUS_NUM_MESSAGE_TYPES + 1); - if (!msg) { - lwsl_err("%s: new message failed\n", __func__); - } - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &payload, - DBUS_TYPE_INVALID); - if (!dbus_connection_send(r->conn, msg, NULL)) { - lwsl_err("%s: unable to send\n", __func__); - } - - lwsl_notice("%s\n", __func__); - - dbus_message_unref(msg); - - } lws_end_foreach_dll_safe(rdt, nx); - -} - - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */ /* | LLL_THREAD */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal DBUS server\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - info.options |= - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - dbus_ctx.tsi = 0; - ctx_listener.tsi = 0; - ctx_listener.vh = dbus_ctx.vh = lws_create_vhost(context, &info); - if (!dbus_ctx.vh) - goto bail; - - session = !!lws_cmdline_option(argc, argv, "--session"); - - if (session) { - /* create the dbus connection, loosely bound to our lws vhost */ - - if (create_dbus_server_conn(&dbus_ctx, DBUS_BUS_SESSION)) - goto bail; - } else { - if (create_dbus_listener(THIS_LISTEN_PATH)) { - lwsl_err("%s: create_dbus_listener failed\n", __func__); - goto bail; - } - } - - /* lws event loop (default poll one) */ - - while (n >= 0 && !interrupted) { - if (!session) - spam_connected_clients(&ctx_listener); - n = lws_service(context, 0); - } - - if (session) - destroy_dbus_server_conn(&dbus_ctx); - else - destroy_dbus_server_listener(&ctx_listener); - - /* this is required for valgrind-cleanliness */ - dbus_shutdown(); - lws_context_destroy(context); - - lwsl_notice("Exiting cleanly\n"); - - return 0; - -bail: - lwsl_err("%s: failed to start\n", __func__); - - lws_context_destroy(context); - - return 1; -} diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-server/README.md libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-server/README.md --- libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-server/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-server/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,96 +0,0 @@ -# lws minimal dbus server - -## build - -Using libdbus requires additional non-default include paths setting, same as -is necessary for lws build described in ./lib/roles/dbus/README.md - -CMake can guess one path and the library name usually, see the README above -for details of how to override for custom libdbus and cross build. - -Fedora example: -``` -$ cmake .. -DLWS_DBUS_INCLUDE2="/usr/lib64/dbus-1.0/include" -$ make -``` - -Ubuntu example: -``` -$ cmake .. -DLWS_DBUS_INCLUDE2="/usr/lib/x86_64-linux-gnu/dbus-1.0/include" -$ make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 ---session | Bind to session bus instead of creating private abstract unix socket - -By default the minimal server listens using its own abstract unix socket -at `unix:abstract=org.libwebsockets.test`. - -You can also run it instead as a participant on the session bus, without its own -unix socket, by giving `--session`. - -### Examples using the default private abstract unix socket - -``` - $ ./lws-minimal-dbus-server -[2018/10/03 07:08:02:6448] USER: LWS minimal dbus server -[2018/10/03 07:08:02:6693] NOTICE: Creating Vhost 'default' port 0, 1 protocols, IPv6 off -... -``` - -You can communicate with the dbus server over its private abstract socket using, eg - -``` -$ gdbus introspect --address unix:abstract=org.libwebsockets.test --dest org.libwebsockets.test --object-path /org/libwebsockets/test -node /org/example/TestObject { - interface org.freedesktop.DBus.Introspectable { - methods: - Introspect(out s data); - signals: - properties: - }; - interface org.freedesktop.DBus.Properties { - methods: - Get(in s interface, -... -``` - -``` -$ gdbus call --address unix:abstract=org.libwebsockets.test --dest org.libwebsockets.test --object-path /org/libwebsockets/test --method org.libwebsockets.test.Echo HELLO -('HELLO',) -``` - -### Examples using the DBUS session bus - -``` - $ ./lws-minimal-dbus-server --session -[2018/10/03 07:08:02:6448] USER: LWS minimal dbus server -[2018/10/03 07:08:02:6693] NOTICE: Creating Vhost 'default' port 0, 1 protocols, IPv6 off -... -``` - -You can communicate with the dbus server over the session bus using, eg - -``` -$ gdbus introspect --session --dest org.libwebsockets.test --object-path /org/libwebsockets/test -node /org/example/TestObject { - interface org.freedesktop.DBus.Introspectable { - methods: - Introspect(out s data); - signals: - properties: - }; - interface org.freedesktop.DBus.Properties { - methods: - Get(in s interface, -... -``` - -``` -$ gdbus call --session --dest org.libwebsockets.test --object-path /org/libwebsockets/test --method org.libwebsockets.test.Echo HELLO -('HELLO',) -``` diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-ws-proxy/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-ws-proxy/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-ws-proxy/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-ws-proxy/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,123 +0,0 @@ -project(lws-minimal-dbus-ws-proxy) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) -include(CheckLibraryExists) - -set(SAMP lws-minimal-dbus-ws-proxy) -set(SRCS main.c) - -if (NOT LWS_WITH_MINIMAL_EXAMPLES) - CHECK_LIBRARY_EXISTS(dbus-1 dbus_connection_set_watch_functions "" LWS_HAVE_LIBDBUS) - if (NOT LWS_HAVE_LIBDBUS) - message(FATAL_ERROR "Install dbus-devel, or libdbus-1-dev etc") - endif() - - if (NOT LWS_DBUS_LIB) - set(LWS_DBUS_LIB "dbus-1") - endif() - - if (NOT LWS_DBUS_INCLUDE1) - # look in fedora and debian / ubuntu place - if (EXISTS "/usr/include/dbus-1.0") - set(LWS_DBUS_INCLUDE1 "/usr/include/dbus-1.0") - else() - message(FATAL_ERROR "Set LWS_DBUS_INCLUDE1 to /usr/include/dbus-1.0 or wherever the main dbus includes are") - endif() - endif() - - if (NOT LWS_DBUS_INCLUDE2) - # look in fedora... debian / ubuntu has the ARCH in the path... - if (EXISTS "/usr/lib64/dbus-1.0/include") - set(LWS_DBUS_INCLUDE2 "/usr/lib64/dbus-1.0/include") - else() - message(FATAL_ERROR "Set LWS_DBUS_INCLUDE2 to /usr/lib/ARCH-linux-gnu/dbus-1.0/include or wherever dbus-arch-deps.h is on your system") - endif() - endif() - - set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES};${LWS_DBUS_INCLUDE1};${LWS_DBUS_INCLUDE2}) - - if (NOT LWS_DBUS_INCLUDE1 OR NOT LWS_DBUS_INCLUDE2) - message(FATAL_ERROR "To build with libdbus, LWS_DBUS_INCLUDE1/2 must be given. See lib/roles/dbus/README.md") - endif() - -endif() - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_DBUS 1 requirements) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - include_directories("${LWS_DBUS_INCLUDE1}") - include_directories("${LWS_DBUS_INCLUDE2}") - list(APPEND LIB_LIST dbus-1) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared ${LWS_DBUS_LIB}) - else() - target_link_libraries(${SAMP} websockets ${LWS_DBUS_LIB}) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-ws-proxy/main.c libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-ws-proxy/main.c --- libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-ws-proxy/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-ws-proxy/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,102 +0,0 @@ -/* - * lws-minimal-dbus-ws-proxy - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal session dbus server that uses the lws event loop, - * and allows proxying ws client connections via DBUS. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal_dbus_ws_proxy.c" - -static int interrupted; -static struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_DBUS_WSPROXY, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -/* - * we pass the dbus address to connect to proxy with from outside the - * protocol plugin... eg if built as a plugin for lwsws, you would instead - * set this pvo in the lwsws JSON config. - */ - -static const struct lws_protocol_vhost_options pvo_ads = { - NULL, - NULL, - "ads", /* pvo name */ - (void *)"unix:abstract=org.libwebsockets.wsclientproxy" /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo_ads, /* "child" pvo linked-list */ - "lws-minimal-dbus-wsproxy", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - static struct lws_context *context; - struct lws_context_creation_info info; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */ /* | LLL_THREAD */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS DBUS ws client proxy\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - info.port = CONTEXT_PORT_NO_LISTEN; - info.ws_ping_pong_interval = 30; - info.protocols = protocols; - info.pvo = &pvo; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* lws event loop (default poll one) */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - lwsl_notice("Exiting cleanly\n"); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-ws-proxy/org.libwebsockets.wsclientproxy.conf libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-ws-proxy/org.libwebsockets.wsclientproxy.conf --- libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-ws-proxy/org.libwebsockets.wsclientproxy.conf 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-ws-proxy/org.libwebsockets.wsclientproxy.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-ws-proxy/protocol_lws_minimal_dbus_ws_proxy.c libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-ws-proxy/protocol_lws_minimal_dbus_ws_proxy.c --- libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-ws-proxy/protocol_lws_minimal_dbus_ws_proxy.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-ws-proxy/protocol_lws_minimal_dbus_ws_proxy.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,828 +0,0 @@ -/* - * ws protocol handler plugin for dbus ws proxy - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This proxies outgoing ws client connections on DBUS. So a DBUS client can - * reach out and get remote WS payloads in both directions. - * - * DEVELOPER NOTE - * - * Two worlds, dbus and ws, collide in this file. - * - * There main thing keeping it sane is both worlds are running in the same - * thread and on the same event loop. Although things may happen completely - * asynchronously in both worlds, the logical reaction to those events are - * serialized in a single event loop doing one thing at a time. - * - * So while you are servicing an event in the ws world, you can be certain the - * logical state of any related dbus thing cannot change underneath you, until - * you return back to the event loop, and vice versa. So other-world objects - * can't be freed, other-world handles can't close etc while you are servicing - * in your world. - * - * Since all bets are off what happens next, and in which world, after you - * return back to the event loop though, an additional rule is needed: worlds - * must not allocate in objects owned by the other world. They must generate - * their own objects in their world and use those for allocations and state. - * - * For example in the dbus-world there is a struct lws_dbus_ctx_wsproxy with - * various state, but he is subject to deletion by events in dbus-world. If - * the ws-world stored things there, they are subject to going out of scope - * at the whim of the dbus connection without the ws world hearing about it and - * cleanly deallocaing them. So the ws world must keep his own pss that remains - * in scope until the ws link closes for allocations from ws-world. - * - * In this application there's a point of contact between the worlds, a ring - * buffer allocated in ws world when the ws connection is established, and - * deallocated when the ws connection is closed. The DBUS world needs to put - * things in this ringbuffer. But the way lws_ring works, when the message - * allocated in DBUS world is queued on the ringbuffer, the ringbuffer itself - * takes responsibility for deallocation. So there is no problem. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#include -#endif - -#include -#include -#include - -/* - * dbus accepted connections create these larger context structs that start - * with the lws dbus context - */ - -struct vhd_dbus_proxy; - -struct msg { - void *payload; /* is malloc'd */ - size_t len; - char binary; - char first; - char final; -}; - -struct pss_dbus_proxy { - struct lws_ring *ring_out; - uint32_t ring_out_tail; -}; - -struct lws_dbus_ctx_wsproxy { - struct lws_dbus_ctx ctx; - - struct lws *cwsi; - struct vhd_dbus_proxy *vhd; - struct pss_dbus_proxy *pss; -}; - -struct vhd_dbus_proxy { - struct lws_context *context; - struct lws_vhost *vhost; - - /* - * Because the listener ctx is composed in the vhd, we can always get a - * pointer to the outer vhd from a pointer to ctx_listener inside. - */ - struct lws_dbus_ctx ctx_listener; - struct lws_dbus_ctx_wsproxy dctx; - - const char *dbus_listen_ads; -}; - -#define THIS_INTERFACE "org.libwebsockets.wsclientproxy" -#define THIS_OBJECT "/org/libwebsockets/wsclientproxy" -#define THIS_BUSNAME "org.libwebsockets.wsclientproxy" -static const char *version = "0.1"; - -static const char *server_introspection_xml = - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " " - " \n" - " " - " \n" - - "\n"; - -static void -destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} - -/* - * DBUS WORLD - */ - -static DBusHandlerResult -dmh_introspect(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - dbus_message_append_args(*reply, - DBUS_TYPE_STRING, &server_introspection_xml, - DBUS_TYPE_INVALID); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static DBusHandlerResult -dmh_get(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - const char *interface, *property; - DBusError err; - - dbus_error_init(&err); - - if (!dbus_message_get_args(m, &err, DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - dbus_message_unref(*reply); - *reply = dbus_message_new_error(m, err.name, err.message); - dbus_error_free(&err); - - return DBUS_HANDLER_RESULT_HANDLED; - } - - if (strcmp(property, "Version")) /* Unknown property */ - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - dbus_message_append_args(*reply, DBUS_TYPE_STRING, &version, - DBUS_TYPE_INVALID); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static DBusHandlerResult -dmh_getall(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - DBusMessageIter arr, di, iter, va; - const char *property = "Version"; - - dbus_message_iter_init_append(*reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr); - - /* Append all properties name/value pairs */ - dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, NULL, &di); - dbus_message_iter_append_basic(&di, DBUS_TYPE_STRING, &property); - dbus_message_iter_open_container(&di, DBUS_TYPE_VARIANT, "s", &va); - dbus_message_iter_append_basic(&va, DBUS_TYPE_STRING, &version); - dbus_message_iter_close_container(&di, &va); - dbus_message_iter_close_container(&arr, &di); - - dbus_message_iter_close_container(&iter, &arr); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static DBusHandlerResult -dmh_connect(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - struct lws_dbus_ctx_wsproxy *wspctx = (struct lws_dbus_ctx_wsproxy *)d; - const char *prot = "", *ads = "", *path = "", *baduri = "Bad Uri", - *connecting = "Connecting", *failed = "Failed", **pp; - struct lws_client_connect_info i; - char host[128], uri_copy[512]; - const char *uri, *subprotocol; - DBusError err; - int port = 0; - - dbus_error_init(&err); - - if (!dbus_message_get_args(m, &err, DBUS_TYPE_STRING, &uri, - DBUS_TYPE_STRING, &subprotocol, - DBUS_TYPE_INVALID)) { - dbus_message_unref(*reply); - *reply = dbus_message_new_error(m, err.name, err.message); - dbus_error_free(&err); - - return DBUS_HANDLER_RESULT_HANDLED; - } - - strncpy(uri_copy, uri, sizeof(uri_copy) - 1); - uri_copy[sizeof(uri_copy) - 1] = '\0'; - - if (lws_parse_uri(uri_copy, &prot, &ads, &port, &path)) { - pp = &baduri; - goto send_reply; - } - - lws_snprintf(host, sizeof(host), "%s:%u", ads, port); - - memset(&i, 0, sizeof(i)); - - assert(wspctx); - assert(wspctx->vhd); - - i.context = wspctx->vhd->context; - i.port = port; - i.address = ads; - i.path = path; - i.host = host; - i.origin = host; - i.ssl_connection = !strcmp(prot, "https") || !strcmp(prot, "wss"); - i.vhost = wspctx->ctx.vh; - i.protocol = subprotocol; - i.local_protocol_name = "lws-minimal-dbus-wsproxy"; - i.pwsi = &wspctx->cwsi; - - lwsl_user("%s: connecting to %s://%s:%d%s\n", __func__, prot, - i.address, i.port, i.path); - - if (!lws_client_connect_via_info(&i)) { - lwsl_notice("%s: client connect failed\n", __func__); - pp = &failed; - goto send_reply; - } - - lws_set_opaque_parent_data(wspctx->cwsi, wspctx); - lwsl_notice("%s: client connecting...\n", __func__); - pp = &connecting; - -send_reply: - dbus_message_append_args(*reply, DBUS_TYPE_STRING, pp, - DBUS_TYPE_INVALID); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static int -issue_dbus_signal(struct lws *wsi, const char *signame, const char *string) -{ - struct lws_dbus_ctx_wsproxy *wspctx = - lws_get_opaque_parent_data(wsi); - DBusMessage *m; - - if (!wspctx) - return 1; - - m = dbus_message_new_signal(THIS_OBJECT, THIS_INTERFACE, signame); - if (!m) { - lwsl_err("%s: new signal failed\n", __func__); - return 1; - } - - dbus_message_append_args(m, DBUS_TYPE_STRING, &string, - DBUS_TYPE_INVALID); - - if (!dbus_connection_send(wspctx->ctx.conn, m, NULL)) - lwsl_err("%s: unable to send\n", __func__); - - dbus_message_unref(m); - - return 0; -} - -static DBusHandlerResult -dmh_send(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d) -{ - struct lws_dbus_ctx_wsproxy *wspctx = (struct lws_dbus_ctx_wsproxy *)d; - const char *payload; - struct msg amsg; - DBusError err; - - dbus_error_init(&err); - - if (!wspctx->cwsi || !wspctx->pss) { - dbus_message_unref(*reply); - *reply = dbus_message_new_error(m, "Send Fail", "No ws conn"); - - return DBUS_HANDLER_RESULT_HANDLED; - } - - if (!dbus_message_get_args(m, &err, DBUS_TYPE_STRING, &payload, - DBUS_TYPE_INVALID)) { - dbus_message_unref(*reply); - *reply = dbus_message_new_error(m, err.name, err.message); - dbus_error_free(&err); - - return DBUS_HANDLER_RESULT_HANDLED; - } - - /* - * we allocate on the ringbuffer in ws world, but responsibility for - * freeing it is understood by lws_ring. - */ - - amsg.len = strlen(payload); - /* notice we over-allocate by LWS_PRE */ - amsg.payload = malloc(LWS_PRE + amsg.len); - if (!amsg.payload) { - lwsl_user("OOM: dropping\n"); - dbus_message_unref(*reply); - *reply = dbus_message_new_error(m, "Send Fail", "OOM"); - - return DBUS_HANDLER_RESULT_HANDLED; - } - amsg.binary = 0; - amsg.first = 1; - amsg.final = 1; - - memcpy((char *)amsg.payload + LWS_PRE, payload, amsg.len); - if (!lws_ring_insert(wspctx->pss->ring_out, &amsg, 1)) { - destroy_message(&amsg); - lwsl_user("Ring Full!\n"); - dbus_message_unref(*reply); - *reply = dbus_message_new_error(m, "Send Fail", "Ring full"); - - return DBUS_HANDLER_RESULT_HANDLED; - } - if (wspctx->cwsi) - lws_callback_on_writable(wspctx->cwsi); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -struct lws_dbus_methods { - const char *inter; - const char *call; - lws_dbus_message_handler handler; -} meths[] = { - { DBUS_INTERFACE_INTROSPECTABLE, "Introspect", dmh_introspect }, - { DBUS_INTERFACE_PROPERTIES, "Get", dmh_get }, - { DBUS_INTERFACE_PROPERTIES, "GetAll", dmh_getall }, - { THIS_INTERFACE, "Connect", dmh_connect }, - { THIS_INTERFACE, "Send", dmh_send }, -}; - -static DBusHandlerResult -server_message_handler(DBusConnection *conn, DBusMessage *message, void *data) -{ - struct lws_dbus_methods *mp = meths; - DBusMessage *reply = NULL; - DBusHandlerResult result; - size_t n; - - assert(data); - - lwsl_info("%s: Got D-Bus request: %s.%s on %s\n", __func__, - dbus_message_get_interface(message), - dbus_message_get_member(message), - dbus_message_get_path(message)); - - for (n = 0; n < LWS_ARRAY_SIZE(meths); n++) { - if (dbus_message_is_method_call(message, mp->inter, mp->call)) { - reply = dbus_message_new_method_return(message); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - result = mp->handler(conn, message, &reply, data); - - if (result == DBUS_HANDLER_RESULT_HANDLED && - !dbus_connection_send(conn, reply, NULL)) - result = DBUS_HANDLER_RESULT_NEED_MEMORY; - - dbus_message_unref(reply); - - return result; - } - - mp++; - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static const DBusObjectPathVTable vtable = { - .message_function = server_message_handler -}; - -static void -destroy_dbus_server_conn(struct lws_dbus_ctx_wsproxy *wsctx) -{ - if (!wsctx->ctx.conn) - return; - - lwsl_notice("%s\n", __func__); - - dbus_connection_unregister_object_path(wsctx->ctx.conn, THIS_OBJECT); - lws_dll2_remove(&wsctx->ctx.next); - dbus_connection_unref(wsctx->ctx.conn); -} - -/* - * This is the client dbus side going away. We need to stop the associated - * client ws part and make sure it can't dereference us now we are gone. - */ - -static void -cb_closing(struct lws_dbus_ctx *ctx) -{ - struct lws_dbus_ctx_wsproxy *wspctx = - (struct lws_dbus_ctx_wsproxy *)ctx; - lwsl_err("%s: closing\n", __func__); - - /* - * We have to take care that the associated proxy wsi knows our - * dbus ctx is going out of scope after we return from here. - * - * We do it by setting its pointer to our dbus ctx to NULL. - */ - - if (wspctx->cwsi) { - lws_set_opaque_parent_data(wspctx->cwsi, NULL); - lws_set_timeout(wspctx->cwsi, - PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE, - LWS_TO_KILL_ASYNC); - } - - destroy_dbus_server_conn(wspctx); - - free(wspctx); -} - -static void -new_conn(DBusServer *server, DBusConnection *conn, void *d) -{ - struct lws_dbus_ctx_wsproxy *conn_wspctx, /* the new conn context */ - /* the listener context */ - *wspctx = (struct lws_dbus_ctx_wsproxy *)d; - struct vhd_dbus_proxy *vhd = lws_container_of(d, - struct vhd_dbus_proxy, ctx_listener); - - assert(vhd->vhost == wspctx->ctx.vh); - - lwsl_notice("%s\n", __func__); - - conn_wspctx = malloc(sizeof(*conn_wspctx)); - if (!conn_wspctx) - return; - - memset(conn_wspctx, 0, sizeof(*conn_wspctx)); - - conn_wspctx->ctx.tsi = wspctx->ctx.tsi; - conn_wspctx->ctx.vh = wspctx->ctx.vh; - conn_wspctx->ctx.conn = conn; - conn_wspctx->vhd = vhd; /* let accepted connections also know the vhd */ - - assert(conn_wspctx->vhd); - - if (lws_dbus_connection_setup(&conn_wspctx->ctx, conn, cb_closing)) { - lwsl_err("%s: connection bind to lws failed\n", __func__); - goto bail; - } - - if (!dbus_connection_register_object_path(conn, THIS_OBJECT, &vtable, - conn_wspctx)) { - lwsl_err("%s: Failed to register object path\n", __func__); - goto bail; - } - - lws_dll2_add_head(&conn_wspctx->ctx.next, &wspctx->ctx.owner); - - /* we take on responsibility for explicit close / unref with this... */ - dbus_connection_ref(conn); - - return; - -bail: - free(conn_wspctx); -} - -static int -create_dbus_listener(struct vhd_dbus_proxy *vhd, int tsi) -{ - DBusError e; - - dbus_error_init(&e); -#if 0 - vhd->dctx.ctx.tsi = tsi; - vhd->dctx.ctx.vh = vhd->vhost; - vhd->dctx.ctx.next.prev = NULL; - vhd->dctx.ctx.next.next = NULL; - vhd->dctx.vhd = vhd; - vhd->dctx.cwsi = NULL; - - /* connect to the SYSTEM bus */ - - vhd->dctx.ctx.conn = dbus_bus_get(DBUS_BUS_SYSTEM, &e); - if (!vhd->dctx.ctx.conn) { - lwsl_notice("%s: Failed to get a session DBus connection: '%s'" - ", continuing with daemon listener only\n", - __func__, e.message); - dbus_error_free(&e); - dbus_error_init(&e); - goto daemon; - } - - /* - * by default dbus will call exit() when this connection closes... - * we have to shut down other things cleanly, so disable that - */ - dbus_connection_set_exit_on_disconnect(vhd->dctx.ctx.conn, 0); - - if (dbus_bus_request_name(vhd->dctx.ctx.conn, THIS_BUSNAME, - DBUS_NAME_FLAG_REPLACE_EXISTING, &e) != - DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - lwsl_notice("%s: Failed to request name on bus: '%s'," - " continuing with daemon listener only\n", - __func__, e.message); - dbus_connection_unref(vhd->dctx.ctx.conn); - vhd->dctx.ctx.conn = NULL; - dbus_error_free(&e); - dbus_error_init(&e); - goto daemon; - } - - if (!dbus_connection_register_object_path(vhd->dctx.ctx.conn, - THIS_OBJECT, &vtable, - &vhd->dctx)) { - lwsl_err("%s: Failed to register object path\n", __func__); - goto fail; - } - - /* - * This is the part that binds the connection to lws watcher and - * timeout handling provided by lws - */ - - if (lws_dbus_connection_setup(&vhd->dctx.ctx, vhd->dctx.ctx.conn, - cb_closing)) { - lwsl_err("%s: connection bind to lws failed\n", __func__); - goto fail; - } - -daemon: -#endif - vhd->ctx_listener.vh = vhd->vhost; - vhd->ctx_listener.tsi = tsi; - - if (!lws_dbus_server_listen(&vhd->ctx_listener, vhd->dbus_listen_ads, - &e, new_conn)) { - lwsl_err("%s: failed\n", __func__); - dbus_error_free(&e); - - return 1; - } - - lwsl_notice("%s: created DBUS listener on %s\n", __func__, - vhd->dbus_listen_ads); - - return 0; -#if 0 -fail: - dbus_error_free(&e); - - return 1; -#endif -} - -static void -destroy_dbus_server_listener(struct vhd_dbus_proxy *vhd) -{ - dbus_server_disconnect(vhd->ctx_listener.dbs); - - lws_start_foreach_dll_safe(struct lws_dll2 *, rdt, nx, - vhd->ctx_listener.owner.head) { - struct lws_dbus_ctx *r = lws_container_of(rdt, - struct lws_dbus_ctx, next); - - dbus_connection_close(r->conn); - dbus_connection_unref(r->conn); - free(r); - } lws_end_foreach_dll_safe(rdt, nx); - - if (vhd->dctx.ctx.conn) - dbus_connection_unref(vhd->dctx.ctx.conn); - dbus_server_unref(vhd->ctx_listener.dbs); -} - -/* - * WS WORLD - */ - -static int -callback_minimal_dbus_wsproxy(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct pss_dbus_proxy *pss = (struct pss_dbus_proxy *)user; - struct vhd_dbus_proxy *vhd = (struct vhd_dbus_proxy *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - struct lws_dbus_ctx_wsproxy *wspctx; - const struct msg *pmsg; - int flags, m; - - switch (reason) { - - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), sizeof(*vhd)); - if (!vhd) - return -1; - - vhd->context = lws_get_context(wsi); - vhd->vhost = lws_get_vhost(wsi); - - if (lws_pvo_get_str(in, "ads", &vhd->dbus_listen_ads)) { - lwsl_err("%s: pvo 'ads' must be set\n", __func__); - return -1; - } - - if (create_dbus_listener(vhd, 0)) { - lwsl_err("%s: create_dbus_listener failed\n", __func__); - return -1; - } - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - destroy_dbus_server_listener(vhd); - /* this is required for valgrind-cleanliness */ - dbus_shutdown(); - break; - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - lwsl_user("LWS_CALLBACK_CLIENT_ESTABLISHED\n"); - - /* - * create the send ringbuffer now the ws connection is - * established. - */ - - wspctx = lws_get_opaque_parent_data(wsi); - if (!wspctx) - break; - - wspctx->pss = pss; - pss->ring_out_tail = 0; - pss->ring_out = lws_ring_create(sizeof(struct msg), 8, - destroy_message); - if (!pss->ring_out) { - lwsl_err("OOM\n"); - return -1; - } - - issue_dbus_signal(wsi, "Status", - "ws client connection established"); - break; - - case LWS_CALLBACK_CLIENT_WRITEABLE: - lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE:\n"); - - pmsg = lws_ring_get_element(pss->ring_out, &pss->ring_out_tail); - if (!pmsg) { - lwsl_user(" (nothing in ring)\n"); - break; - } - - flags = lws_write_ws_flags( - pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT, - pmsg->first, pmsg->final); - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE, - pmsg->len, flags); - if (m < (int)pmsg->len) { - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - lwsl_user(" wrote %d: flags: 0x%x first: %d final %d\n", - m, flags, pmsg->first, pmsg->final); - - lws_ring_consume_single_tail(pss->ring_out, - &pss->ring_out_tail, 1); - - /* more to do for us? */ - if (lws_ring_get_element(pss->ring_out, &pss->ring_out_tail)) - /* come back as soon as we can write more */ - lws_callback_on_writable(wsi); - - break; - - case LWS_CALLBACK_CLIENT_RECEIVE: - - lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %4d " - "(rpp %5d, first %d, last %d, bin %d)\n", - (int)len, (int)lws_remaining_packet_payload(wsi), - lws_is_first_fragment(wsi), - lws_is_final_fragment(wsi), - lws_frame_is_binary(wsi)); - - { - char strbuf[256]; - int l = len; - - if (l > (int)sizeof(strbuf) - 1) - l = sizeof(strbuf) - 1; - - memcpy(strbuf, in, l); - strbuf[l] = '\0'; - - issue_dbus_signal(wsi, "Receive", strbuf); - } - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - issue_dbus_signal(wsi, "Status", "ws client connection error"); - break; - - case LWS_CALLBACK_CLIENT_CLOSED: - lwsl_err("LWS_CALLBACK_CLIENT_CLOSED ()\n"); - issue_dbus_signal(wsi, "Status", "ws client connection closed"); - - /* destroy any ringbuffer and pending messages */ - - lws_ring_destroy(pss->ring_out); - - wspctx = lws_get_opaque_parent_data(wsi); - if (!wspctx) - break; - - /* - * the wspctx cannot refer to its child wsi any longer, it is - * about to go out of scope. - */ - - wspctx->cwsi = NULL; - wspctx->pss = NULL; - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL_DBUS_WSPROXY \ - { \ - "lws-minimal-dbus-wsproxy", \ - callback_minimal_dbus_wsproxy, \ - sizeof(struct pss_dbus_proxy), \ - 1024, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_DBUS_WSPROXY -}; - -int -init_protocol_minimal_dbus_wsproxy(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal_dbus_wsproxy(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-ws-proxy/README.md libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-ws-proxy/README.md --- libwebsockets-4.0.20/minimal-examples/dbus-server/minimal-dbus-ws-proxy/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-server/minimal-dbus-ws-proxy/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,115 +0,0 @@ -# lws minimal dbus ws proxy - -This is an application which presents a DBUS server on one side, and a -websocket client proxy on the other. - -You connect to it over DBUS, send a Connect method on its interface giving -a URI and a ws subprotocol name. - -It replies with a string "Connecting" if all is well. - -Connection progress (including close) is then provided using type 7 messages -sent back to the dbus client. - -Payload from the ws connection is provided using type 6 messages sent back to -the dbus client. - -## build - -Using libdbus requires additional non-default include paths setting, same as -is necessary for lws build described in ./lib/roles/dbus/README.md - -CMake can guess one path and the library name usually, see the README above -for details of how to override for custom libdbus and cross build. - -Fedora example: -``` -$ cmake .. -DLWS_DBUS_INCLUDE2="/usr/lib64/dbus-1.0/include" -$ make -``` - -Ubuntu example: -``` -$ cmake .. -DLWS_DBUS_INCLUDE2="/usr/lib/x86_64-linux-gnu/dbus-1.0/include" -$ make -``` - -## Configuration - -The dbus-ws-proxy server tries to register its actual bus name with the SYSTEM -bus in DBUS. If it fails, eg because of insufficient permissions on the user, -then it continues without that and starts its own daemon normally. - -The main dbus daemon must be told how to accept these registrations if that's -what you want. A config file is provided that tells dbus to allow the -well-known busname for this daemon to be registered, but only by root. - -``` -$ sudo cp org.libwebsockets.wsclientproxy.conf /etc/dbus-1/system.d -$ sudo systemctl restart dbus -``` - -## usage - -Run the dbus-ws-proxy server, then start lws-minimal-dbus-ws-proxy-testclient in -another terminal. - -This test app sends a random line drawing message to the mirror example on -https://libwebsockets.org/testserver every couple of seconds, and displays -any received messages (such as its own sends mirrored back, or anything -drawn in the canvas in a browser). - -``` - $ sudo ./lws-minimal-dbus-ws-proxy-testclient -[2018/10/07 10:05:29:2084] USER: LWS minimal DBUS ws proxy testclient -[2018/10/07 10:05:29:2345] NOTICE: Creating Vhost 'default' port 0, 1 protocols, IPv6 off -[2018/10/07 10:05:29:2424] USER: create_dbus_client_conn: connecting to 'unix:abstract=org.libwebsockets.wsclientproxy' -[2018/10/07 10:05:29:2997] NOTICE: state_transition: 0x5679720: from state 0 -> 1 -[2018/10/07 10:05:29:2999] NOTICE: create_dbus_client_conn: created OK -[2018/10/07 10:05:29:3232] USER: remote_method_call: requesting proxy connection wss://libwebsockets.org/ lws-mirror-protocol -[2018/10/07 10:05:29:3450] NOTICE: state_transition: 0x5679720: from state 1 -> 2 -[2018/10/07 10:05:29:5972] USER: pending_call_notify: received 'Connecting' -[2018/10/07 10:05:31:3387] NOTICE: state_transition: 0x5679720: from state 2 -> 3 -[2018/10/07 10:05:33:6672] USER: filter: Received 'd #B0DC51 115 177 166 283;' -[2018/10/07 10:05:35:9723] USER: filter: Received 'd #E87CCD 9 192 106 235;' -[2018/10/07 10:05:38:2784] USER: filter: Received 'd #E2A9E3 379 290 427 62;' -[2018/10/07 10:05:39:5833] USER: filter: Received 'd #B127F8 52 126 60 226;' -[2018/10/07 10:05:41:8908] USER: filter: Received 'd #0E0F76 429 267 8 11;' -... -``` - -## ws proxy DBUS details - -### Fixed details - -Item|Value ----|--- -Address|unix:abstract=org.libwebsockets.wsclientproxy -Interface|org.libwebsockets.wsclientproxy -Bus Name|org.libwebsockets.wsclientproxy -Object path|/org/libwebsockets/wsclientproxy - -### Interface Methods - -Method|Arguments|Returns ----|---|--- -Connect|s: ws URI, s: ws subprotocol name|"Bad Uri", "Connecting" or "Failed" -Send|s: payload|Empty message if no problem, or error message - -When Connecting, the actual connection happens asynchronously if the initial -connection attempt doesn't fail immediately. If it's continuing in the -background, the reply will have been "Connecting". - -### Signals - -Signal Name|Argument|Meaning ----|---|--- -Receive|s: payload|Received data from the ws link -Status|s: status|See table below - -Status String|Meaning ----|--- -"ws client connection error"|The ws connection attempt ended with a fatal error -"ws client connection established"|The ws connection attempt succeeded -"ws client connection closed"|The ws connection has closed - diff -Nru libwebsockets-4.0.20/minimal-examples/dbus-server/README.md libwebsockets-2.4.2/minimal-examples/dbus-server/README.md --- libwebsockets-4.0.20/minimal-examples/dbus-server/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/dbus-server/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -|Example|Demonstrates| ----|--- -minimal-dbus-server|Shows how to run a DBUS session server using lws event loop -minimal-dbus-ws-proxy|Control ws client connections via DBUS diff -Nru libwebsockets-4.0.20/minimal-examples/gtk/minimal-gtk/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/gtk/minimal-gtk/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/gtk/minimal-gtk/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/gtk/minimal-gtk/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,102 +0,0 @@ -project(lws-minimal-gtk) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-gtk) -set(SRCS main.c) - - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITH_GLIB 1 requirements) -require_lws_config(LWS_WITH_GTK 1 requirements) - -if (requirements) - -# gtk pieces - - include (FindPkgConfig) - - set(LWS_GTK_INCLUDE_DIRS CACHE PATH "Path to the gtk include directory") - set(LWS_GTK_LIBRARIES CACHE PATH "Path to the gtk library") - PKG_SEARCH_MODULE(LWS_GTK2 gtk+-3.0) - if (LWS_GTK2_FOUND) - list(APPEND LWS_GTK_INCLUDE_DIRS "${LWS_GTK2_INCLUDE_DIRS}") - list(APPEND LWS_GTK_LIBRARIES "${LWS_GTK2_LIBRARIES}") - endif() - message("gtk include dir: ${LWS_GTK_INCLUDE_DIRS}") - message("gtk libraries: ${LWS_GTK_LIBRARIES}") - include_directories("${LWS_GTK_INCLUDE_DIRS}") - set(extralibs ${extralibs} ${LWS_GTK_LIBRARIES}) - - - - message("Extra libs: ${extralibs}") - - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared ${extralibs}) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets ${extralibs}) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/gtk/minimal-gtk/main.c libwebsockets-2.4.2/minimal-examples/gtk/minimal-gtk/main.c --- libwebsockets-4.0.20/minimal-examples/gtk/minimal-gtk/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/gtk/minimal-gtk/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,210 +0,0 @@ -#include -#include - -static int status = 0; - -static void -print_hello(GtkWidget *widget, gpointer data) -{ - g_print("Hello World\n"); -} - -static void -activate(GtkApplication *app, gpointer user_data) -{ - GtkWidget *window; - GtkWidget *button, *bbox; - - window = gtk_application_window_new(app); - gtk_window_set_title(GTK_WINDOW(window), "mywindow"); - gtk_window_set_default_size(GTK_WINDOW(window), 200, 200); - - bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); - gtk_container_add(GTK_CONTAINER(window), bbox); - - button = gtk_button_new_with_label("Hello World"); - g_signal_connect(button, "clicked", G_CALLBACK(print_hello), NULL); - g_signal_connect_swapped(button, "clicked", - G_CALLBACK(gtk_widget_destroy), window); - gtk_container_add(GTK_CONTAINER(bbox), button); - - gtk_widget_show_all(window); -} - -static int -system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = mgr->parent; - struct lws_client_connect_info i; - - if (current != LWS_SYSTATE_OPERATIONAL || - target != LWS_SYSTATE_OPERATIONAL) - return 0; - - lwsl_notice("%s: operational\n", __func__); - - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - i.context = context; - i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_H2_QUIRK_OVERFLOWS_TXCR | - LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM; - i.port = 443; - i.address = "warmcat.com"; - i.path = "/"; - i.host = i.address; - i.origin = i.address; - i.method = "GET"; - - i.protocol = "http"; - - return !lws_client_connect_via_info(&i); -} - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - break; - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - { - char buf[128]; - - lws_get_peer_simple(wsi, buf, sizeof(buf)); - status = lws_http_client_http_response(wsi); - - lwsl_user("Connected to %s, http response: %d\n", - buf, status); - } - break; - - /* chunks of chunked content, with header removed */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); - return 0; /* don't passthru */ - - /* uninterpreted http content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - { - char buffer[1024 + LWS_PRE]; - char *px = buffer + LWS_PRE; - int lenx = sizeof(buffer) - LWS_PRE; - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - return 0; /* don't passthru */ - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "http", - callback_http, - 0, - 0, - }, - { NULL, NULL, 0, 0 } -}; - -static gpointer -t1_main (gpointer user_data) -{ - lws_state_notify_link_t notifier = { {0}, system_notify_cb, "app" }; - lws_state_notify_link_t *na[] = { ¬ifier, NULL }; - GMainContext *t1_mc = (GMainContext *)user_data; - struct lws_context_creation_info info; - struct lws_context *context; - void *foreign_loops[1]; - GMainLoop *ml; - - g_print("%s: started\n", __func__); - - g_main_context_push_thread_default(t1_mc); - - ml = g_main_loop_new(t1_mc, FALSE); - - /* attach our lws activities to the main loop of this thread */ - - lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE, NULL); - memset(&info, 0, sizeof info); - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_GLIB; - info.protocols = protocols; - foreign_loops[0] = (void *)ml; - info.foreign_loops = foreign_loops; - info.register_notifier_list = na; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./warmcat.com.cer"; -#endif - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return NULL; - } - - /* - * We created the lws_context and bound it to this thread's main loop, - * let's run the thread's main loop now... - */ - - g_main_loop_run(ml); - g_main_loop_unref(ml); - - g_main_context_pop_thread_default(t1_mc); - g_main_context_unref(t1_mc); - - g_print("%s: ending\n", __func__); - - lws_context_destroy(context); - - return NULL; -} - -int -main(int argc, char **argv) -{ - GMainContext *t1_mc = g_main_context_new(); - GtkApplication *app; - GThread *t1; - int status; - - t1 = g_thread_new ("t1", t1_main, g_main_context_ref (t1_mc)); - (void)t1; - - app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE); - g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); - - status = g_application_run(G_APPLICATION(app), argc, argv); - g_object_unref(app); - - return status; -} - diff -Nru libwebsockets-4.0.20/minimal-examples/gtk/minimal-gtk/README.md libwebsockets-2.4.2/minimal-examples/gtk/minimal-gtk/README.md --- libwebsockets-4.0.20/minimal-examples/gtk/minimal-gtk/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/gtk/minimal-gtk/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -# lws minimal http client gtk - -The application goes to https://warmcat.com and receives the page data, -from inside a gtk app using gtk / glib main loop directly. - -## build - -``` - $ cmake . && make -``` - -## usage - - -``` -$ -t1_main: started -[2020/02/08 18:04:07:6647] N: Loading client CA for verification ./warmcat.com.cer -[2020/02/08 18:04:07:7744] U: Connected to 46.105.127.147, http response: 200 -[2020/02/08 18:04:07:7762] U: RECEIVE_CLIENT_HTTP_READ: read 4087 -[2020/02/08 18:04:07:7762] U: RECEIVE_CLIENT_HTTP_READ: read 4096 -[2020/02/08 18:04:07:7928] U: RECEIVE_CLIENT_HTTP_READ: read 4087 -[2020/02/08 18:04:07:7929] U: RECEIVE_CLIENT_HTTP_READ: read 4096 -[2020/02/08 18:04:07:7956] U: RECEIVE_CLIENT_HTTP_READ: read 4087 -[2020/02/08 18:04:07:7956] U: RECEIVE_CLIENT_HTTP_READ: read 4096 -[2020/02/08 18:04:07:7956] U: RECEIVE_CLIENT_HTTP_READ: read 1971 -[2020/02/08 18:04:07:7956] U: LWS_CALLBACK_COMPLETED_CLIENT_HTTP -Hello World -$ -``` - - diff -Nru libwebsockets-4.0.20/minimal-examples/gtk/minimal-gtk/warmcat.com.cer libwebsockets-2.4.2/minimal-examples/gtk/minimal-gtk/warmcat.com.cer --- libwebsockets-4.0.20/minimal-examples/gtk/minimal-gtk/warmcat.com.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/gtk/minimal-gtk/warmcat.com.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFUDCCBDigAwIBAgISA4mJfIm3iCGbU9+o8YQa+4nUMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5MjNaFw0x -OTEyMDYwNzA5MjNaMBYxFDASBgNVBAMTC3dhcm1jYXQuY29tMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnEoH9JW3GvpadpxHGZPb5wv1Q6KfAIMWtdq -YCOfotFxaYULuzHVxmrTTgmEqJr+eBqUBkXKmGuRR/9UipOmTu5j02qFyWHotFdF -ZGyp//8z+Rle9Qt1nL68oNIZLDtWkybh5x00b1uo4eyEszXUaa0aLqKP3lH7Q4jI -aSVARZ8snrJR640Gp3ByudvNTYkGz469bpWzRC/8wSNtzzY02DvHs1GxQx9tMXw+ -BbtUxeP7lpYFKEFBjgZaIKLv+4g8ItJIuO7gMSzG2JfpQHxdhrlhxpx7dsaMUcyM -nnYXysNL5JG3KEMhkxbtdpCaEQ8jLSPbl/rnF/+mgce+lSjMuQIDAQABo4ICYjCC -Al4wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD -AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSI9ai12zLFeNTEDHKI9Ghkqcpa2TAf -BgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEw -LgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcw -LwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv -MBYGA1UdEQQPMA2CC3dhcm1jYXQuY29tMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcG -CysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5 -cHQub3JnMIIBBgYKKwYBBAHWeQIEAgSB9wSB9ADyAHcAY/Lbzeg7zCzPC3KEJ1dr -M6SNYXePvXWmOLHHaFRL2I0AAAFtCsVHHAAABAMASDBGAiEAy0q1cR4VwPL3iviL -cBWN67kjJRXk+DwhodmeoM3kb3gCIQC2soAHFs0Umo+0RNdFrL41+hMuidh2cXbb -Ovc6nh5tOQB3AOJpS64m6OlACeiGG7Y7g9Q+5/50iPukjyiTAZ3d8dv+AAABbQrF -R48AAAQDAEgwRgIhANqKQm4t9by263CJ7/DLOaZCjtcK29KgJjPwhv08UMn1AiEA -h35nGTASR8/E7xz+56ZUleqD7U1ABFgWZRZskIzsFO8wDQYJKoZIhvcNAQELBQAD -ggEBADDJBVbKe2LPHmi8k2vxErB3Y0Ty+3gwgPEXKYtEvQ7tos89eE+QmOXAzH5J -GwRarFf7kzmKeJv04tMebiEtshpap47oJfxCxfrtpja8hP8Cdu/v/Ae6eEzu3yet -0N08GJdxQKfgCFaoGUptbaF2RCIZS12SVcX4TPpdP+xaiZdmIx4dGM6tReQ8+y8B -10b4Hi2+d/zW0W1z6+FAemU6yleWriJDUik5oas9XZF5LAAMDb/WgF5eIB6P9CUG -LuAO8lWlk9nBgXvMLTxZ74SJb17H4kFEIrIjvABNshz5gBW8xw9nfr5YIfANtwEj -BDsq06Df3UORYVs/j3T97gPAEZ4= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-http-client) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-client) -set(SRCS minimal-http-client.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client/minimal-http-client.c libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client/minimal-http-client.c --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client/minimal-http-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client/minimal-http-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,322 +0,0 @@ -/* - * lws-minimal-http-client - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the a minimal http client using lws. - * - * It visits https://warmcat.com/ and receives the html page there. You - * can dump the page data by changing the #if 0 below. - */ - -#include -#include -#include - -static int interrupted, bad = 1, status; -#if defined(LWS_WITH_HTTP2) -static int long_poll; -#endif -static struct lws *client_wsi; -static const char *ba_user, *ba_password; - -static const lws_retry_bo_t retry = { - .secs_since_valid_ping = 3, - .secs_since_valid_hangup = 10, -}; - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - interrupted = 1; - break; - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - { - char buf[128]; - - lws_get_peer_simple(wsi, buf, sizeof(buf)); - status = lws_http_client_http_response(wsi); - - lwsl_user("Connected to %s, http response: %d\n", - buf, status); - } -#if defined(LWS_WITH_HTTP2) - if (long_poll) { - lwsl_user("%s: Client entering long poll mode\n", __func__); - lws_h2_client_stream_long_poll_rxonly(wsi); - } -#endif - break; - -#if defined(LWS_WITH_HTTP_BASIC_AUTH) - - /* you only need this if you need to do Basic Auth */ - case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: - { - unsigned char **p = (unsigned char **)in, *end = (*p) + len; - char b[128]; - - if (!ba_user || !ba_password) - break; - - if (lws_http_basic_auth_gen(ba_user, ba_password, b, sizeof(b))) - break; - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_AUTHORIZATION, - (unsigned char *)b, (int)strlen(b), p, end)) - return -1; - - break; - } -#endif - - /* chunks of chunked content, with header removed */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); -#if defined(LWS_WITH_HTTP2) - if (long_poll) { - char dotstar[128]; - lws_strnncpy(dotstar, (const char *)in, len, - sizeof(dotstar)); - lwsl_notice("long poll rx: %d '%s'\n", (int)len, - dotstar); - } -#endif -#if 0 /* enable to dump the html */ - { - const char *p = in; - - while (len--) - if (*p < 0x7f) - putchar(*p++); - else - putchar('.'); - } -#endif - return 0; /* don't passthru */ - - /* uninterpreted http content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - { - char buffer[1024 + LWS_PRE]; - char *px = buffer + LWS_PRE; - int lenx = sizeof(buffer) - LWS_PRE; - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - return 0; /* don't passthru */ - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); - interrupted = 1; - bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - interrupted = 1; - bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "http", - callback_http, - 0, - 0, - }, - { NULL, NULL, 0, 0 } -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -struct args { - int argc; - const char **argv; -}; - -static int -system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = mgr->parent; - struct lws_client_connect_info i; - struct args *a = lws_context_user(context); - const char *p; - - if (current != LWS_SYSTATE_OPERATIONAL || target != LWS_SYSTATE_OPERATIONAL) - return 0; - - lwsl_info("%s: operational\n", __func__); - - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - i.context = context; - if (!lws_cmdline_option(a->argc, a->argv, "-n")) { - i.ssl_connection = LCCSCF_USE_SSL; -#if defined(LWS_WITH_HTTP2) - /* requires h2 */ - if (lws_cmdline_option(a->argc, a->argv, "--long-poll")) { - lwsl_user("%s: long poll mode\n", __func__); - long_poll = 1; - } -#endif - } - - if (lws_cmdline_option(a->argc, a->argv, "-l")) { - i.port = 7681; - i.address = "localhost"; - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - } else { - i.port = 443; - i.address = "warmcat.com"; - } - - if (lws_cmdline_option(a->argc, a->argv, "--nossl")) - i.ssl_connection = 0; - - i.ssl_connection |= LCCSCF_H2_QUIRK_OVERFLOWS_TXCR | - LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM; - - i.alpn = "h2"; - if (lws_cmdline_option(a->argc, a->argv, "--h1")) - i.alpn = "http/1.1"; - - if ((p = lws_cmdline_option(a->argc, a->argv, "-p"))) - i.port = atoi(p); - - if ((p = lws_cmdline_option(a->argc, a->argv, "--user"))) - ba_user = p; - if ((p = lws_cmdline_option(a->argc, a->argv, "--password"))) - ba_password = p; - - if (lws_cmdline_option(a->argc, a->argv, "-j")) - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - - if (lws_cmdline_option(a->argc, a->argv, "-k")) - i.ssl_connection |= LCCSCF_ALLOW_INSECURE; - - if (lws_cmdline_option(a->argc, a->argv, "-m")) - i.ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK; - - if (lws_cmdline_option(a->argc, a->argv, "-e")) - i.ssl_connection |= LCCSCF_ALLOW_EXPIRED; - - if ((p = lws_cmdline_option(a->argc, a->argv, "-f"))) { - i.ssl_connection |= LCCSCF_H2_MANUAL_RXFLOW; - i.manual_initial_tx_credit = atoi(p); - lwsl_notice("%s: manual peer tx credit %d\n", __func__, - i.manual_initial_tx_credit); - } - - /* the default validity check is 5m / 5m10s... -v = 3s / 10s */ - - if (lws_cmdline_option(a->argc, a->argv, "-v")) - i.retry_and_idle_policy = &retry; - - if ((p = lws_cmdline_option(a->argc, a->argv, "--server"))) - i.address = p; - - if ((p = lws_cmdline_option(a->argc, a->argv, "--path"))) - i.path = p; - else - i.path = "/"; - - i.host = i.address; - i.origin = i.address; - i.method = "GET"; - - i.protocol = protocols[0].name; - i.pwsi = &client_wsi; - - return !lws_client_connect_via_info(&i); -} - -int main(int argc, const char **argv) -{ - lws_state_notify_link_t notifier = { {0}, system_notify_cb, "app" }; - lws_state_notify_link_t *na[] = { ¬ifier, NULL }; - struct lws_context_creation_info info; - struct lws_context *context; - struct args args; - int n = 0; - // uint8_t memcert[4096]; - - args.argc = argc; - args.argv = argv; - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - lws_cmdline_option_handle_builtin(argc, argv, &info); - - lwsl_user("LWS minimal http client [-d] [-l] [--h1]\n"); - - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - info.user = &args; - info.register_notifier_list = na; - - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + 1 + 1; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./warmcat.com.cer"; -#endif -#if 0 - n = open("./warmcat.com.cer", O_RDONLY); - if (n >= 0) { - info.client_ssl_ca_mem_len = read(n, memcert, sizeof(memcert)); - info.client_ssl_ca_mem = memcert; - close(n); - n = 0; - memcert[info.client_ssl_ca_mem_len++] = '\0'; - } -#endif - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client/README.md libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client/README.md --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -# lws minimal http client - -The application goes to either https://warmcat.com or -https://localhost:7681 (with `-l` option) and receives the page data. - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --l| Connect to https://localhost:7681 and accept selfsigned cert ---h1|Specify http/1.1 only using ALPN, rejects h2 even if server supports it ---server |set server name to connect to --k|Apply tls option LCCSCF_ALLOW_INSECURE --j|Apply tls option LCCSCF_ALLOW_SELFSIGNED --m|Apply tls option LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK --e|Apply tls option LCCSCF_ALLOW_EXPIRED --v|Connection validity use 3s / 10s instead of default 5m / 5m10s ---nossl| disable ssl connection ---user | Set Basic Auth username ---password | Set Basic Auth password - -``` - $ ./lws-minimal-http-client -[2018/03/04 14:43:20:8562] USER: LWS minimal http client -[2018/03/04 14:43:20:8571] NOTICE: Creating Vhost 'default' port -1, 1 protocols, IPv6 on -[2018/03/04 14:43:20:8616] NOTICE: created client ssl context for default -[2018/03/04 14:43:20:8617] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com -[2018/03/04 14:43:21:1496] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com -[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: incoming content length 26520 -[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: client connection up -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 974 -[2018/03/04 14:43:22:3022] NOTICE: lws_http_client_read: transaction completed says -1 -[2018/03/04 14:43:23:3042] USER: Completed -``` - -You can also test the client Basic Auth support against the http-server/minimal-http-server-basicauth -example. In one console window run the server and in the other - -``` -$ lws-minimal-http-client -l --nossl --path /secret/index.html --user user --password password -``` - -The Basic Auth credentials for the test server are literally username "user" and password "password". - diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client/selftest.sh libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client/selftest.sh --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=4 - -dotest $1 $2 warmcat -dotest $1 $2 warmcat-h1 --h1 - -spawn "" $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost -l -spawn $SPID $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost-h1 -l --h1 - -kill $SPID 2>/dev/null -wait $SPID 2>/dev/null -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client/warmcat.com.cer libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client/warmcat.com.cer --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client/warmcat.com.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client/warmcat.com.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFUDCCBDigAwIBAgISA4mJfIm3iCGbU9+o8YQa+4nUMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5MjNaFw0x -OTEyMDYwNzA5MjNaMBYxFDASBgNVBAMTC3dhcm1jYXQuY29tMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnEoH9JW3GvpadpxHGZPb5wv1Q6KfAIMWtdq -YCOfotFxaYULuzHVxmrTTgmEqJr+eBqUBkXKmGuRR/9UipOmTu5j02qFyWHotFdF -ZGyp//8z+Rle9Qt1nL68oNIZLDtWkybh5x00b1uo4eyEszXUaa0aLqKP3lH7Q4jI -aSVARZ8snrJR640Gp3ByudvNTYkGz469bpWzRC/8wSNtzzY02DvHs1GxQx9tMXw+ -BbtUxeP7lpYFKEFBjgZaIKLv+4g8ItJIuO7gMSzG2JfpQHxdhrlhxpx7dsaMUcyM -nnYXysNL5JG3KEMhkxbtdpCaEQ8jLSPbl/rnF/+mgce+lSjMuQIDAQABo4ICYjCC -Al4wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD -AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSI9ai12zLFeNTEDHKI9Ghkqcpa2TAf -BgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEw -LgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcw -LwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv -MBYGA1UdEQQPMA2CC3dhcm1jYXQuY29tMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcG -CysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5 -cHQub3JnMIIBBgYKKwYBBAHWeQIEAgSB9wSB9ADyAHcAY/Lbzeg7zCzPC3KEJ1dr -M6SNYXePvXWmOLHHaFRL2I0AAAFtCsVHHAAABAMASDBGAiEAy0q1cR4VwPL3iviL -cBWN67kjJRXk+DwhodmeoM3kb3gCIQC2soAHFs0Umo+0RNdFrL41+hMuidh2cXbb -Ovc6nh5tOQB3AOJpS64m6OlACeiGG7Y7g9Q+5/50iPukjyiTAZ3d8dv+AAABbQrF -R48AAAQDAEgwRgIhANqKQm4t9by263CJ7/DLOaZCjtcK29KgJjPwhv08UMn1AiEA -h35nGTASR8/E7xz+56ZUleqD7U1ABFgWZRZskIzsFO8wDQYJKoZIhvcNAQELBQAD -ggEBADDJBVbKe2LPHmi8k2vxErB3Y0Ty+3gwgPEXKYtEvQ7tos89eE+QmOXAzH5J -GwRarFf7kzmKeJv04tMebiEtshpap47oJfxCxfrtpja8hP8Cdu/v/Ae6eEzu3yet -0N08GJdxQKfgCFaoGUptbaF2RCIZS12SVcX4TPpdP+xaiZdmIx4dGM6tReQ8+y8B -10b4Hi2+d/zW0W1z6+FAemU6yleWriJDUik5oas9XZF5LAAMDb/WgF5eIB6P9CUG -LuAO8lWlk9nBgXvMLTxZ74SJb17H4kFEIrIjvABNshz5gBW8xw9nfr5YIfANtwEj -BDsq06Df3UORYVs/j3T97gPAEZ4= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-attach/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-attach/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-attach/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-attach/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -project(lws-minimal-http-client-attach) -cmake_minimum_required(VERSION 2.8) -include(CheckIncludeFile) -include(CheckCSourceCompiles) - -Project(lws-minimal-http-client-attach) -set(SAMP lws-minimal-http-client-attach) -set(SRCS minimal-http-client-attach.c) - -MACRO(require_pthreads result) - CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) - if (NOT LWS_HAVE_PTHREAD_H) - if (LWS_WITH_MINIMAL_EXAMPLES) - set(result 0) - else() - message(FATAL_ERROR "threading support requires pthreads") - endif() - endif() -ENDMACRO() - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -if (WIN32) - set(requirements 0) -endif() -require_pthreads(requirements) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared pthread) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets pthread) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-attach/minimal-http-client-attach.c libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-attach/minimal-http-client-attach.c --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-attach/minimal-http-client-attach.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-attach/minimal-http-client-attach.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,275 +0,0 @@ -/* - * lws-minimal-http-client-attach - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates how to use the lws_system (*attach) api to allow a - * different thread to arrange to join an existing lws event loop safely. The - * attached stuff does an http client GET from the lws event loop, even though - * it was originally requested from a different thread than the lws event loop. - */ - -#include -#include -#include -#include - -static struct lws_context *context; -static pthread_t lws_thread; -static pthread_mutex_t lock; -static int interrupted, bad = 1, status; - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - interrupted = 1; - break; - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - { - char buf[128]; - - lws_get_peer_simple(wsi, buf, sizeof(buf)); - status = lws_http_client_http_response(wsi); - - lwsl_user("Connected to %s, http response: %d\n", - buf, status); - } - break; - - /* chunks of chunked content, with header removed */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); - -#if 0 /* enable to dump the html */ - { - const char *p = in; - - while (len--) - if (*p < 0x7f) - putchar(*p++); - else - putchar('.'); - } -#endif - return 0; /* don't passthru */ - - /* uninterpreted http content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - { - char buffer[1024 + LWS_PRE]; - char *px = buffer + LWS_PRE; - int lenx = sizeof(buffer) - LWS_PRE; - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - return 0; /* don't passthru */ - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); - interrupted = 1; - bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - interrupted = 1; - bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "http", - callback_http, - 0, - 0, - }, - { NULL, NULL, 0, 0 } -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -static void -attach_callback(struct lws_context *context, int tsi, void *opaque) -{ - struct lws_client_connect_info i; - - /* - * Even though it was asked for from a different thread, we are called - * back by lws from the lws event loop thread context - */ - lwsl_user("%s: called from tid %p\n", __func__, (void *)pthread_self()); - - /* - * We can set up our operations on the lws event loop and return so - * they can happen asynchronously - */ - - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - i.context = context; - i.ssl_connection = LCCSCF_USE_SSL; - i.ssl_connection |= LCCSCF_H2_QUIRK_OVERFLOWS_TXCR | - LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM; - i.port = 443; - i.address = "warmcat.com"; - i.path = "/"; - i.host = i.address; - i.origin = i.address; - i.method = "GET"; - - i.protocol = protocols[0].name; - - lws_client_connect_via_info(&i); -} - - -static int -lws_attach_with_pthreads_locking(struct lws_context *context, int tsi, - lws_attach_cb_t cb, lws_system_states_t state, - void *opaque, struct lws_attach_item **get) -{ - int n; - - pthread_mutex_lock(&lock); - /* - * We just provide system-specific locking around the lws non-threadsafe - * helper that adds and removes things from the pt list - */ - n = __lws_system_attach(context, tsi, cb, state, opaque, get); - pthread_mutex_unlock(&lock); - - return n; -} - - -lws_system_ops_t ops = { - .attach = lws_attach_with_pthreads_locking -}; - -/* - * We made this into a different thread to model it being run from completely - * different codebase that's all linked together - */ - -static void * -lws_create(void *d) -{ - struct lws_context_creation_info info; - - lwsl_user("%s: tid %p\n", __func__, (void *)pthread_self()); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.system_ops = &ops; - info.protocols = protocols; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - goto bail; - } - - /* start the event loop */ - - while (!interrupted) - if (lws_service(context, 0)) - interrupted = 1; - - lws_context_destroy(context); - -bail: - pthread_exit(NULL); - - return NULL; -} - -int main(int argc, const char **argv) -{ - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - const char *p; - void *retval; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("%s: main thread tid %p\n", __func__, (void *)pthread_self()); - lwsl_user("LWS minimal http client attach\n"); - - pthread_mutex_init(&lock, NULL); - - /* - * The idea of the example is we're going to split the lws context and - * event loop off to be created from its own thread... this is like it - * was actually started by some completely different code... - */ - - if (pthread_create(&lws_thread, NULL, lws_create, NULL)) { - lwsl_err("thread creation failed\n"); - goto bail1; - } - - /* - * Now on the original / different thread representing a different - * codebase that wants to join this existing event loop, we'll ask to - * get a callback from the event loop context when the event loop - * thread is operational. We have to wait around a bit because we - * may run before the lws context was created. - */ - - while (!context && n++ < 30) - usleep(10000); - - if (!context) { - lwsl_err("%s: context didn't start\n", __func__); - goto bail; - } - - /* - * From our different, non event loop thread, ask for our attach - * callback to get called when lws system state is OPERATIONAL - */ - - lws_system_get_ops(context)->attach(context, 0, attach_callback, - LWS_SYSTATE_OPERATIONAL, - NULL, NULL); - - /* - * That's all we wanted to do with our thread. Just wait for the lws - * thread to exit as well. - */ - -bail: - pthread_join(lws_thread, &retval); -bail1: - pthread_mutex_destroy(&lock); - - lwsl_user("%s: finished\n", __func__); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-attach/README.md libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-attach/README.md --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-attach/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-attach/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -# lws minimal http client attach - -This demonstrates how other threads can reach out to an existing lws_context -and join its event loop cleanly and safely. - -## build - -``` - $ cmake . && make -``` - -Pthreads is required on your system. - -## usage - -``` - $ ./lws-minimal-http-client-attach -[2019/12/31 18:30:49:3495] U: main: main thread tid 0x503e1c0 -[2019/12/31 18:30:50:3584] U: LWS minimal http client attach -[2019/12/31 18:30:50:4002] U: lws_create: tid 0x5c41700 -[2019/12/31 18:30:50:5727] E: callback_ntpc: set up system ops for set_clock -[2019/12/31 18:30:50:2110] N: callback_ntpc: Unix time: 1577817053 -[2019/12/31 18:30:50:2136] U: attach_callback: called from tid 0x5c41700 -[2019/12/31 18:30:51:8733] U: Connected to 46.105.127.147, http response: 200 -[2019/12/31 18:30:51:8818] U: RECEIVE_CLIENT_HTTP_READ: read 4087 -[2019/12/31 18:30:51:8823] U: RECEIVE_CLIENT_HTTP_READ: read 4096 -[2019/12/31 18:30:51:8846] U: RECEIVE_CLIENT_HTTP_READ: read 4087 -[2019/12/31 18:30:51:8847] U: RECEIVE_CLIENT_HTTP_READ: read 4096 -[2019/12/31 18:30:51:8855] U: RECEIVE_CLIENT_HTTP_READ: read 4087 -[2019/12/31 18:30:51:8856] U: RECEIVE_CLIENT_HTTP_READ: read 4096 -[2019/12/31 18:30:51:8860] U: RECEIVE_CLIENT_HTTP_READ: read 1971 -[2019/12/31 18:30:51:8873] U: LWS_CALLBACK_COMPLETED_CLIENT_HTTP -[2019/12/31 18:30:51:9629] U: main: finished -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-certinfo/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-certinfo/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-certinfo/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-certinfo/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -project(lws-minimal-http-client-certinfo) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-client-certinfo) -set(SRCS minimal-http-client-certinfo.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) -require_lws_config(LWS_OPENSSL_SUPPORT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-certinfo/minimal-http-client-certinfo.c libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-certinfo/minimal-http-client-certinfo.c --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-certinfo/minimal-http-client-certinfo.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-certinfo/minimal-http-client-certinfo.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,217 +0,0 @@ -/* - * lws-minimal-http-client - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the a minimal http client using lws. - * - * It visits https://warmcat.com/ and receives the html page there. You - * can dump the page data by changing the #if 0 below. - */ - -#include -#include -#include - -static int interrupted, bad = 1, status; -static struct lws *client_wsi; - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - uint8_t buf[1280]; - union lws_tls_cert_info_results *ci = - (union lws_tls_cert_info_results *)buf; - - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - client_wsi = NULL; - break; - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - status = lws_http_client_http_response(wsi); - lwsl_notice("lws_http_client_http_response %d\n", status); - - if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME, - ci, sizeof(buf) - sizeof(*ci))) - lwsl_notice(" Peer Cert CN : %s\n", ci->ns.name); - - if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_ISSUER_NAME, - ci, sizeof(ci->ns.name))) - lwsl_notice(" Peer Cert issuer : %s\n", ci->ns.name); - - if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_VALIDITY_FROM, - ci, 0)) - lwsl_notice(" Peer Cert Valid from: %s", ctime(&ci->time)); - - if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_VALIDITY_TO, - ci, 0)) - lwsl_notice(" Peer Cert Valid to : %s", ctime(&ci->time)); - if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_USAGE, - ci, 0)) - lwsl_notice(" Peer Cert usage bits: 0x%x\n", ci->usage); - if (!lws_tls_peer_cert_info(wsi, - LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY, - ci, sizeof(buf) - sizeof(*ci))) { - lwsl_notice(" Peer Cert public key:\n"); - lwsl_hexdump_notice(ci->ns.name, ci->ns.len); - } - break; - - /* chunks of chunked content, with header removed */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); -#if 0 /* enable to dump the html */ - { - const char *p = in; - - while (len--) - if (*p < 0x7f) - putchar(*p++); - else - putchar('.'); - } -#endif - return 0; /* don't passthru */ - - /* uninterpreted http content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - { - char buffer[1024 + LWS_PRE]; - char *px = buffer + LWS_PRE; - int lenx = sizeof(buffer) - LWS_PRE; - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - return 0; /* don't passthru */ - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); - client_wsi = NULL; - bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - client_wsi = NULL; - bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "http", - callback_http, - 0, - 0, - }, - { NULL, NULL, 0, 0 } -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_client_connect_info i; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* - * For LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE - * - * | LLL_INFO | LLL_PARSER | LLL_HEADER | LLL_EXT | - * LLL_CLIENT | LLL_LATENCY | LLL_DEBUG - */ ; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http client [<-d ] [-l] [--h1]\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + 1 + 1; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./warmcat.com.cer"; -#endif - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - i.context = context; - i.ssl_connection = LCCSCF_USE_SSL; - - if (lws_cmdline_option(argc, argv, "-l")) { - i.port = 7681; - i.address = "localhost"; - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - } else { - i.port = 443; - i.address = "warmcat.com"; - } - i.path = "/"; - i.host = i.address; - i.origin = i.address; - - /* force h1 even if h2 available */ - if (lws_cmdline_option(argc, argv, "--h1")) - i.alpn = "http/1.1"; - - i.method = "GET"; - - i.protocol = protocols[0].name; - i.pwsi = &client_wsi; - lws_client_connect_via_info(&i); - - while (n >= 0 && client_wsi && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-certinfo/README.md libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-certinfo/README.md --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-certinfo/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-certinfo/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -# lws minimal http client certinfo - -This demonstrates how to dump information from the peer -certificate largely independent of the tls backend. - -The application goes to https://warmcat.com and receives the page data. - -Before receiving the page it dumps information on the server's cert. - -This works independently of the tls backend being OpenSSL or mbedTLS. - -However the public keys cannot be compared between the two tls -backends, since they produce different representations. - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --l| Connect to https://localhost:7681 and accept selfsigned cert ---h1|Specify http/1.1 only using ALPN, rejects h2 even if server supports it - -``` - $ ./lws-minimal-http-client-certinfo -[2018/04/05 21:39:26:5882] USER: LWS minimal http client -[2018/04/05 21:39:26:5897] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 on -[2018/04/05 21:39:26:5955] NOTICE: created client ssl context for default -[2018/04/05 21:39:28:0824] NOTICE: lws_http_client_http_response 200 -[2018/04/05 21:39:28:0824] NOTICE: Peer Cert CN : warmcat.com -[2018/04/05 21:39:28:0824] NOTICE: Peer Cert issuer : /C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited -[2018/04/05 21:39:28:0825] NOTICE: Peer Cert Valid from: Mon Nov 3 00:00:00 2014 -[2018/04/05 21:39:28:0825] NOTICE: Peer Cert Valid to : Sat Nov 2 23:59:59 2019 -[2018/04/05 21:39:28:0825] NOTICE: Peer Cert usage bits: 0xa0 -[2018/04/05 21:39:28:0825] NOTICE: Peer Cert public key: -[2018/04/05 21:39:28:0825] NOTICE: -[2018/04/05 21:39:28:0825] NOTICE: 0000: 30 82 01 22 30 0D 06 09 2A 86 48 86 F7 0D 01 01 0.."0...*.H..... -[2018/04/05 21:39:28:0825] NOTICE: 0010: 01 05 00 03 82 01 0F 00 30 82 01 0A 02 82 01 01 ........0....... -[2018/04/05 21:39:28:0825] NOTICE: 0020: 00 EC 39 C1 98 25 A8 99 AC 01 9B D2 16 C0 CA A3 ..9..%.......... -[2018/04/05 21:39:28:0825] NOTICE: 0030: 0E 19 57 E5 3D 23 F3 79 7E 63 BF CD B8 88 D1 16 ..W.=#.y~c...... -[2018/04/05 21:39:28:0825] NOTICE: 0040: C6 F0 A6 ED 66 CB F3 C3 D6 7E A7 A3 AB 00 0A 3E ....f....~.....> -[2018/04/05 21:39:28:0825] NOTICE: 0050: AD EF 20 44 85 5A 61 F0 71 20 BD E3 D1 4B B6 53 .. D.Za.q ...K.S -[2018/04/05 21:39:28:0825] NOTICE: 0060: 57 AA 81 E6 ED 74 36 40 E7 FC 62 24 AD E8 82 1D W....t6@..b$.... -[2018/04/05 21:39:28:0826] NOTICE: 0070: 89 C4 3D 64 6C A8 34 4B DB FB DD 7D D2 2D FB 86 ..=dl.4K...}.-.. -[2018/04/05 21:39:28:0826] NOTICE: 0080: 97 EA 6B E2 C9 39 D6 19 DE A8 90 E7 86 8F CF 0A ..k..9.......... -[2018/04/05 21:39:28:0826] NOTICE: 0090: CD 09 3C AF FB 0A FF 85 E8 93 D1 4B A0 C5 21 AD ..<........K..!. -[2018/04/05 21:39:28:0826] NOTICE: 00A0: 58 52 30 0E 4B FE 4F C8 01 B9 BD 0F D4 E4 64 7B XR0.K.O.......d{ -[2018/04/05 21:39:28:0826] NOTICE: 00B0: 04 B4 D2 68 69 8F F1 D5 FD B0 1A CE 55 43 08 B7 ...hi.......UC.. -[2018/04/05 21:39:28:0826] NOTICE: 00C0: 9F 57 0D 4E E1 CA E8 5C B4 2A 6B AB 05 B5 57 67 .W.N...\.*k...Wg -[2018/04/05 21:39:28:0826] NOTICE: 00D0: B8 FD 20 F4 4F 6B 0E 47 7C AD EB B4 99 2C 9B 53 .. .Ok.G|....,.S -[2018/04/05 21:39:28:0826] NOTICE: 00E0: DF EA 67 8D 8A 9D A7 17 01 F9 4E BD 56 43 50 53 ..g.......N.VCPS -[2018/04/05 21:39:28:0826] NOTICE: 00F0: 08 4E FE 6A 85 4A 4D 45 03 DA 01 00 96 7A C0 A9 .N.j.JME.....z.. -[2018/04/05 21:39:28:0826] NOTICE: 0100: C2 32 5E 1A 9F 6F 7B E2 02 5E 70 12 D3 8E 76 6A .2^..o{..^p...vj -[2018/04/05 21:39:28:0826] NOTICE: 0110: 0B 59 A4 D7 31 9D C6 86 08 53 2E 02 8A 1E B1 FB .Y..1....S...... -[2018/04/05 21:39:28:0826] NOTICE: 0120: 7B 02 03 01 00 01 {..... -[2018/04/05 21:39:28:0826] NOTICE: -[2018/04/05 21:39:28:0829] USER: RECEIVE_CLIENT_HTTP_READ: read 503 -[2018/04/05 21:39:28:0829] USER: RECEIVE_CLIENT_HTTP_READ: read 512 -[2018/04/05 21:39:28:0829] USER: RECEIVE_CLIENT_HTTP_READ: read 512 -[2018/04/05 21:39:28:0829] USER: RECEIVE_CLIENT_HTTP_READ: read 512 -... -[2018/04/05 21:39:28:3777] USER: RECEIVE_CLIENT_HTTP_READ: read 512 -[2018/04/05 21:39:28:3777] USER: RECEIVE_CLIENT_HTTP_READ: read 512 -[2018/04/05 21:39:28:3778] USER: RECEIVE_CLIENT_HTTP_READ: read 503 -[2018/04/05 21:39:28:3778] USER: RECEIVE_CLIENT_HTTP_READ: read 512 -[2018/04/05 21:39:28:3778] USER: RECEIVE_CLIENT_HTTP_READ: read 512 -[2018/04/05 21:39:28:3778] USER: RECEIVE_CLIENT_HTTP_READ: read 471 -[2018/04/05 21:39:28:3778] USER: LWS_CALLBACK_COMPLETED_CLIENT_HTTP -[2018/04/05 21:39:28:3787] USER: Completed -``` - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-certinfo/warmcat.com.cer libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-certinfo/warmcat.com.cer --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-certinfo/warmcat.com.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-certinfo/warmcat.com.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFUDCCBDigAwIBAgISA4mJfIm3iCGbU9+o8YQa+4nUMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5MjNaFw0x -OTEyMDYwNzA5MjNaMBYxFDASBgNVBAMTC3dhcm1jYXQuY29tMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnEoH9JW3GvpadpxHGZPb5wv1Q6KfAIMWtdq -YCOfotFxaYULuzHVxmrTTgmEqJr+eBqUBkXKmGuRR/9UipOmTu5j02qFyWHotFdF -ZGyp//8z+Rle9Qt1nL68oNIZLDtWkybh5x00b1uo4eyEszXUaa0aLqKP3lH7Q4jI -aSVARZ8snrJR640Gp3ByudvNTYkGz469bpWzRC/8wSNtzzY02DvHs1GxQx9tMXw+ -BbtUxeP7lpYFKEFBjgZaIKLv+4g8ItJIuO7gMSzG2JfpQHxdhrlhxpx7dsaMUcyM -nnYXysNL5JG3KEMhkxbtdpCaEQ8jLSPbl/rnF/+mgce+lSjMuQIDAQABo4ICYjCC -Al4wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD -AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSI9ai12zLFeNTEDHKI9Ghkqcpa2TAf -BgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEw -LgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcw -LwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv -MBYGA1UdEQQPMA2CC3dhcm1jYXQuY29tMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcG -CysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5 -cHQub3JnMIIBBgYKKwYBBAHWeQIEAgSB9wSB9ADyAHcAY/Lbzeg7zCzPC3KEJ1dr -M6SNYXePvXWmOLHHaFRL2I0AAAFtCsVHHAAABAMASDBGAiEAy0q1cR4VwPL3iviL -cBWN67kjJRXk+DwhodmeoM3kb3gCIQC2soAHFs0Umo+0RNdFrL41+hMuidh2cXbb -Ovc6nh5tOQB3AOJpS64m6OlACeiGG7Y7g9Q+5/50iPukjyiTAZ3d8dv+AAABbQrF -R48AAAQDAEgwRgIhANqKQm4t9by263CJ7/DLOaZCjtcK29KgJjPwhv08UMn1AiEA -h35nGTASR8/E7xz+56ZUleqD7U1ABFgWZRZskIzsFO8wDQYJKoZIhvcNAQELBQAD -ggEBADDJBVbKe2LPHmi8k2vxErB3Y0Ty+3gwgPEXKYtEvQ7tos89eE+QmOXAzH5J -GwRarFf7kzmKeJv04tMebiEtshpap47oJfxCxfrtpja8hP8Cdu/v/Ae6eEzu3yet -0N08GJdxQKfgCFaoGUptbaF2RCIZS12SVcX4TPpdP+xaiZdmIx4dGM6tReQ8+y8B -10b4Hi2+d/zW0W1z6+FAemU6yleWriJDUik5oas9XZF5LAAMDb/WgF5eIB6P9CUG -LuAO8lWlk9nBgXvMLTxZ74SJb17H4kFEIrIjvABNshz5gBW8xw9nfr5YIfANtwEj -BDsq06Df3UORYVs/j3T97gPAEZ4= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-custom-headers/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-custom-headers/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-custom-headers/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-custom-headers/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-http-client-custom-headers) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-client-custom-headers) -set(SRCS minimal-http-client-custom-headers.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-custom-headers/minimal-http-client-custom-headers.c libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-custom-headers/minimal-http-client-custom-headers.c --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-custom-headers/minimal-http-client-custom-headers.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-custom-headers/minimal-http-client-custom-headers.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,228 +0,0 @@ -/* - * lws-minimal-http-client - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the a minimal http client using lws. - * - * It visits https://warmcat.com/ and receives the html page there. You - * can dump the page data by changing the #if 0 below. - */ - -#include -#include -#include - -static int interrupted, bad = 1, status; -static struct lws *client_wsi; - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - char val[32]; - int n; - - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - client_wsi = NULL; - break; - - case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: - { - unsigned char **p = (unsigned char **)in, *end = (*p) + len; - - /* - * How to send a custom header in the request to the server - */ - - if (lws_add_http_header_by_name(wsi, - (const unsigned char *)"dnt", - (const unsigned char *)"1", 1, p, end)) - return -1; - break; - } - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - status = lws_http_client_http_response(wsi); - lwsl_user("Connected with server response: %d\n", status); - - /* - * How to query custom headers (http 1.x only at the momemnt) - * - * warmcat.com sends a custom header "test-custom-header" for - * testing, it has the fixed value "hello". - */ - - n = lws_hdr_custom_length(wsi, "test-custom-header:", 19); - if (n < 0) - lwsl_notice("%s: Can't find test-custom-header\n", - __func__); - else { - if (lws_hdr_custom_copy(wsi, val, sizeof(val), - "test-custom-header:", 19) < 0) - lwsl_notice("%s: custom header too long\n", - __func__); - else - lwsl_notice("%s: custom header: '%s'\n", - __func__, val); - } - break; - - /* chunks of chunked content, with header removed */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); -#if 0 /* enable to dump the html */ - { - const char *p = in; - - while (len--) - if (*p < 0x7f) - putchar(*p++); - else - putchar('.'); - } -#endif - return 0; /* don't passthru */ - - /* uninterpreted http content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - { - char buffer[1024 + LWS_PRE]; - char *px = buffer + LWS_PRE; - int lenx = sizeof(buffer) - LWS_PRE; - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - return 0; /* don't passthru */ - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); - client_wsi = NULL; - bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - client_wsi = NULL; - bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "http", - callback_http, - 0, - 0, - }, - { NULL, NULL, 0, 0 } -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_client_connect_info i; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* - * For LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE - * - * | LLL_INFO | LLL_PARSER | LLL_HEADER | LLL_EXT | - * LLL_CLIENT | LLL_LATENCY | LLL_DEBUG - */ ; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http client Custom Headers [-d] [-l] [--h1]\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + 1 + 1; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./warmcat.com.cer"; -#endif - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - i.context = context; - - if (!lws_cmdline_option(argc, argv, "-n")) - i.ssl_connection = LCCSCF_USE_SSL; - - if (lws_cmdline_option(argc, argv, "-l")) { - i.port = 7681; - i.address = "localhost"; - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - } else { - i.port = 443; - i.address = "warmcat.com"; - } - - /* currently custom headers receive only works with h1 */ - i.alpn = "http/1.1"; - - i.path = "/"; - i.host = i.address; - i.origin = i.address; - i.method = "GET"; - - i.protocol = protocols[0].name; - i.pwsi = &client_wsi; - lws_client_connect_via_info(&i); - - while (n >= 0 && client_wsi && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-custom-headers/README.md libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-custom-headers/README.md --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-custom-headers/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-custom-headers/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -# lws minimal http client custom headers - -This http client application shows how to send and receive custom headers. - -This currently only works on http 1, so the app forces that even if h2 enables. - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --l| Connect to https://localhost:7681 and accept selfsigned cert --n|no TLS - -The app looks for a custom header "test-custom-header" sent by warmcat.com. - -``` - $ ./lws-minimal-http-client-custom-headers -[2019/03/11 05:46:45:7582] USER: LWS minimal http client Custom Headers [-d] [-l] [--h1] -[2019/03/11 05:46:45:7671] NOTICE: created client ssl context for default -[2019/03/11 05:46:46:7812] USER: Connected with server response: 200 -[2019/03/11 05:46:46:7812] NOTICE: callback_http: custom header: 'hello' -[2019/03/11 05:46:46:7814] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -... -``` -You can use the -n and -l to make this test app connect to localhost:7681 over http, -and confirm the "dnt:1" header was sent either by tcpdump or by running the test -server on :7681 with -d1151 - -``` -[2019/03/11 05:48:53:6806] PARSER: WSI_TOKEN_NAME_PART 'd' 0x64 (role=0x20000000) wsi->lextable_pos=0 -[2019/03/11 05:48:53:6807] PARSER: WSI_TOKEN_NAME_PART 'n' 0x6E (role=0x20000000) wsi->lextable_pos=567 -[2019/03/11 05:48:53:6807] PARSER: WSI_TOKEN_NAME_PART 't' 0x74 (role=0x20000000) wsi->lextable_pos=-1 -[2019/03/11 05:48:53:6807] PARSER: WSI_TOKEN_NAME_PART ' ' 0x20 (role=0x20000000) wsi->lextable_pos=-1 -[2019/03/11 05:48:53:6807] PARSER: WSI_TOKEN_NAME_PART '1' 0x31 (role=0x20000000) wsi->lextable_pos=-1 -' 0x0D (role=0x20000000) wsi->lextable_pos=-1NAME_PART ' -[2019/03/11 05:48:53:6807] PARSER: WSI_TOKEN_NAME_PART ' -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-custom-headers/warmcat.com.cer libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-custom-headers/warmcat.com.cer --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-custom-headers/warmcat.com.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-custom-headers/warmcat.com.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFUDCCBDigAwIBAgISA4mJfIm3iCGbU9+o8YQa+4nUMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5MjNaFw0x -OTEyMDYwNzA5MjNaMBYxFDASBgNVBAMTC3dhcm1jYXQuY29tMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnEoH9JW3GvpadpxHGZPb5wv1Q6KfAIMWtdq -YCOfotFxaYULuzHVxmrTTgmEqJr+eBqUBkXKmGuRR/9UipOmTu5j02qFyWHotFdF -ZGyp//8z+Rle9Qt1nL68oNIZLDtWkybh5x00b1uo4eyEszXUaa0aLqKP3lH7Q4jI -aSVARZ8snrJR640Gp3ByudvNTYkGz469bpWzRC/8wSNtzzY02DvHs1GxQx9tMXw+ -BbtUxeP7lpYFKEFBjgZaIKLv+4g8ItJIuO7gMSzG2JfpQHxdhrlhxpx7dsaMUcyM -nnYXysNL5JG3KEMhkxbtdpCaEQ8jLSPbl/rnF/+mgce+lSjMuQIDAQABo4ICYjCC -Al4wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD -AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSI9ai12zLFeNTEDHKI9Ghkqcpa2TAf -BgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEw -LgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcw -LwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv -MBYGA1UdEQQPMA2CC3dhcm1jYXQuY29tMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcG -CysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5 -cHQub3JnMIIBBgYKKwYBBAHWeQIEAgSB9wSB9ADyAHcAY/Lbzeg7zCzPC3KEJ1dr -M6SNYXePvXWmOLHHaFRL2I0AAAFtCsVHHAAABAMASDBGAiEAy0q1cR4VwPL3iviL -cBWN67kjJRXk+DwhodmeoM3kb3gCIQC2soAHFs0Umo+0RNdFrL41+hMuidh2cXbb -Ovc6nh5tOQB3AOJpS64m6OlACeiGG7Y7g9Q+5/50iPukjyiTAZ3d8dv+AAABbQrF -R48AAAQDAEgwRgIhANqKQm4t9by263CJ7/DLOaZCjtcK29KgJjPwhv08UMn1AiEA -h35nGTASR8/E7xz+56ZUleqD7U1ABFgWZRZskIzsFO8wDQYJKoZIhvcNAQELBQAD -ggEBADDJBVbKe2LPHmi8k2vxErB3Y0Ty+3gwgPEXKYtEvQ7tos89eE+QmOXAzH5J -GwRarFf7kzmKeJv04tMebiEtshpap47oJfxCxfrtpja8hP8Cdu/v/Ae6eEzu3yet -0N08GJdxQKfgCFaoGUptbaF2RCIZS12SVcX4TPpdP+xaiZdmIx4dGM6tReQ8+y8B -10b4Hi2+d/zW0W1z6+FAemU6yleWriJDUik5oas9XZF5LAAMDb/WgF5eIB6P9CUG -LuAO8lWlk9nBgXvMLTxZ74SJb17H4kFEIrIjvABNshz5gBW8xw9nfr5YIfANtwEj -BDsq06Df3UORYVs/j3T97gPAEZ4= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-h2-rxflow/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-h2-rxflow/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-h2-rxflow/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-h2-rxflow/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-http-client-h2-rxflow) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-client-h2-rxflow) -set(SRCS minimal-http-client.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H2 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-h2-rxflow/minimal-http-client.c libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-h2-rxflow/minimal-http-client.c --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-h2-rxflow/minimal-http-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-h2-rxflow/minimal-http-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,302 +0,0 @@ -/* - * lws-minimal-http-client-h2-rxflow - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the a minimal http client using lws. - * - * It visits https://warmcat.com/ and receives the html page there. You - * can dump the page data by changing the #if 0 below. - */ - -#include -#include -#include - -static int interrupted, bad = 1, status, each = 1024; -static struct lws *client_wsi; - -static const lws_retry_bo_t retry = { - .secs_since_valid_ping = 3, - .secs_since_valid_hangup = 10, -}; - -struct pss { - lws_sorted_usec_list_t sul; - struct lws *wsi; -}; - -/* - * Once we're established, we ask the server for another 1KB every 250ms - * until we have it all. - */ - -static void -drain_cb(lws_sorted_usec_list_t *sul) -{ - struct pss *pss = lws_container_of(sul, struct pss, sul); - - lws_wsi_tx_credit(pss->wsi, LWSTXCR_PEER_TO_US, each); - - lws_sul_schedule(lws_get_context(pss->wsi), 0, &pss->sul, drain_cb, - 250 * LWS_US_PER_MS); -} - - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct pss *pss = (struct pss *)user; - - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - interrupted = 1; - break; - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - { - char buf[128]; - - lws_get_peer_simple(wsi, buf, sizeof(buf)); - status = lws_http_client_http_response(wsi); - - lwsl_user("Connected to %s, http response: %d\n", - buf, status); - } - pss->wsi = wsi; - lws_sul_schedule(lws_get_context(wsi), 0, &pss->sul, drain_cb, - 250 * LWS_US_PER_MS); - break; - - /* chunks of chunked content, with header removed */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); - -#if 0 /* enable to dump the html */ - { - const char *p = in; - - while (len--) - if (*p < 0x7f) - putchar(*p++); - else - putchar('.'); - } -#endif - return 0; /* don't passthru */ - - /* uninterpreted http content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - { - char buffer[1024 + LWS_PRE]; - char *px = buffer + LWS_PRE; - int lenx = sizeof(buffer) - LWS_PRE; - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - return 0; /* don't passthru */ - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); - interrupted = 1; - bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - interrupted = 1; - bad = status != 200; - lws_sul_schedule(lws_get_context(wsi), 0, &pss->sul, NULL, - LWS_SET_TIMER_USEC_CANCEL); - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "http", - callback_http, - sizeof(struct pss), - 0, - }, - { NULL, NULL, 0, 0 } -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -struct args { - int argc; - const char **argv; -}; - -static int -system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = mgr->parent; - struct lws_client_connect_info i; - struct args *a = lws_context_user(context); - const char *p; - - if (current != LWS_SYSTATE_OPERATIONAL || target != LWS_SYSTATE_OPERATIONAL) - return 0; - - lwsl_info("%s: operational\n", __func__); - - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - i.context = context; - if (!lws_cmdline_option(a->argc, a->argv, "-n")) - i.ssl_connection = LCCSCF_USE_SSL; - - if (lws_cmdline_option(a->argc, a->argv, "-l")) { - i.port = 7681; - i.address = "localhost"; - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - } else { - i.port = 443; - i.address = "warmcat.com"; - } - - if (lws_cmdline_option(a->argc, a->argv, "--nossl")) - i.ssl_connection = 0; - - i.ssl_connection |= LCCSCF_H2_QUIRK_OVERFLOWS_TXCR | - LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM; - - i.alpn = "h2"; - if (lws_cmdline_option(a->argc, a->argv, "--h1")) - i.alpn = "http/1.1"; - - if ((p = lws_cmdline_option(a->argc, a->argv, "-p"))) - i.port = atoi(p); - - if (lws_cmdline_option(a->argc, a->argv, "-j")) - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - - if (lws_cmdline_option(a->argc, a->argv, "-k")) - i.ssl_connection |= LCCSCF_ALLOW_INSECURE; - - if (lws_cmdline_option(a->argc, a->argv, "-m")) - i.ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK; - - if (lws_cmdline_option(a->argc, a->argv, "-e")) - i.ssl_connection |= LCCSCF_ALLOW_EXPIRED; - - if ((p = lws_cmdline_option(a->argc, a->argv, "-f"))) { - i.ssl_connection |= LCCSCF_H2_MANUAL_RXFLOW; - i.manual_initial_tx_credit = atoi(p); - lwsl_notice("%s: manual peer tx credit %d\n", __func__, - i.manual_initial_tx_credit); - } - - if ((p = lws_cmdline_option(a->argc, a->argv, "--each"))) - each = atoi(p); - - /* the default validity check is 5m / 5m10s... -v = 3s / 10s */ - - if (lws_cmdline_option(a->argc, a->argv, "-v")) - i.retry_and_idle_policy = &retry; - - if ((p = lws_cmdline_option(a->argc, a->argv, "--server"))) - i.address = p; - - if ((p = lws_cmdline_option(a->argc, a->argv, "--path"))) - i.path = p; - else - i.path = "/"; - - i.host = i.address; - i.origin = i.address; - i.method = "GET"; - - i.protocol = protocols[0].name; - i.pwsi = &client_wsi; - - return !lws_client_connect_via_info(&i); -} - -int main(int argc, const char **argv) -{ - lws_state_notify_link_t notifier = { {0}, system_notify_cb, "app" }; - lws_state_notify_link_t *na[] = { ¬ifier, NULL }; - struct lws_context_creation_info info; - struct lws_context *context; - struct args args; - int n = 0; - // uint8_t memcert[4096]; - - args.argc = argc; - args.argv = argv; - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - lws_cmdline_option_handle_builtin(argc, argv, &info); - - lwsl_user("LWS minimal http client [-d] [-l] [--h1]\n"); - - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - info.user = &args; - info.register_notifier_list = na; - - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + 1 + 1; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./warmcat.com.cer"; -#endif -#if 0 - n = open("./warmcat.com.cer", O_RDONLY); - if (n >= 0) { - info.client_ssl_ca_mem_len = read(n, memcert, sizeof(memcert)); - info.client_ssl_ca_mem = memcert; - close(n); - n = 0; - memcert[info.client_ssl_ca_mem_len++] = '\0'; - } -#endif - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-h2-rxflow/README.md libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-h2-rxflow/README.md --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-h2-rxflow/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-h2-rxflow/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -# lws minimal http client-h2-rxflow - -The application reads from a server with tightly controlled and rate-limited -receive flow control using h2 tx credit. - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --l| Connect to https://localhost:7681 and accept selfsigned cert ---server |set server name to connect to ---path |URL path to access on server --k|Apply tls option LCCSCF_ALLOW_INSECURE --j|Apply tls option LCCSCF_ALLOW_SELFSIGNED --m|Apply tls option LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK --e|Apply tls option LCCSCF_ALLOW_EXPIRED --v|Connection validity use 3s / 10s instead of default 5m / 5m10s ---nossl| disable ssl connection --f |Indicate we will manually manage tx credit and set a new connection-specific initial tx credit - -RX is constrained to 1024 bytes every 250ms - -``` - $ ./lws-minimal-http-client-h2-rxflow --server phys.org --path "/" -f 1024 -[2019/12/26 13:32:59:6801] U: LWS minimal http client [-d] [-l] [--h1] -[2019/12/26 13:33:00:5087] N: system_notify_cb: manual peer tx credit 1024 -[2019/12/26 13:33:01:7390] U: Connected to 72.251.236.55, http response: 200 -[2019/12/26 13:33:01:7441] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2019/12/26 13:33:01:0855] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2019/12/26 13:33:02:3367] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2019/12/26 13:33:02:5858] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2019/12/26 13:33:02:8384] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2019/12/26 13:33:02:0886] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -... -[2019/12/26 13:33:46:1152] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2019/12/26 13:33:47:3650] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2019/12/26 13:33:47:6150] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2019/12/26 13:33:47:8666] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2019/12/26 13:33:47:1154] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2019/12/26 13:33:48:3656] U: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2019/12/26 13:33:48:6157] U: RECEIVE_CLIENT_HTTP_READ: read 380 -[2019/12/26 13:33:48:6219] U: LWS_CALLBACK_COMPLETED_CLIENT_HTTP -[2019/12/26 13:33:48:7050] U: Completed: OK - -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-h2-rxflow/selftest.sh libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-h2-rxflow/selftest.sh --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-h2-rxflow/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-h2-rxflow/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=4 - -dotest $1 $2 warmcat -dotest $1 $2 warmcat-h1 --h1 - -spawn "" $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost -l -spawn $SPID $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost-h1 -l --h1 - -kill $SPID 2>/dev/null -wait $SPID 2>/dev/null -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-h2-rxflow/warmcat.com.cer libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-h2-rxflow/warmcat.com.cer --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-h2-rxflow/warmcat.com.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-h2-rxflow/warmcat.com.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFUDCCBDigAwIBAgISA4mJfIm3iCGbU9+o8YQa+4nUMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5MjNaFw0x -OTEyMDYwNzA5MjNaMBYxFDASBgNVBAMTC3dhcm1jYXQuY29tMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnEoH9JW3GvpadpxHGZPb5wv1Q6KfAIMWtdq -YCOfotFxaYULuzHVxmrTTgmEqJr+eBqUBkXKmGuRR/9UipOmTu5j02qFyWHotFdF -ZGyp//8z+Rle9Qt1nL68oNIZLDtWkybh5x00b1uo4eyEszXUaa0aLqKP3lH7Q4jI -aSVARZ8snrJR640Gp3ByudvNTYkGz469bpWzRC/8wSNtzzY02DvHs1GxQx9tMXw+ -BbtUxeP7lpYFKEFBjgZaIKLv+4g8ItJIuO7gMSzG2JfpQHxdhrlhxpx7dsaMUcyM -nnYXysNL5JG3KEMhkxbtdpCaEQ8jLSPbl/rnF/+mgce+lSjMuQIDAQABo4ICYjCC -Al4wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD -AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSI9ai12zLFeNTEDHKI9Ghkqcpa2TAf -BgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEw -LgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcw -LwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv -MBYGA1UdEQQPMA2CC3dhcm1jYXQuY29tMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcG -CysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5 -cHQub3JnMIIBBgYKKwYBBAHWeQIEAgSB9wSB9ADyAHcAY/Lbzeg7zCzPC3KEJ1dr -M6SNYXePvXWmOLHHaFRL2I0AAAFtCsVHHAAABAMASDBGAiEAy0q1cR4VwPL3iviL -cBWN67kjJRXk+DwhodmeoM3kb3gCIQC2soAHFs0Umo+0RNdFrL41+hMuidh2cXbb -Ovc6nh5tOQB3AOJpS64m6OlACeiGG7Y7g9Q+5/50iPukjyiTAZ3d8dv+AAABbQrF -R48AAAQDAEgwRgIhANqKQm4t9by263CJ7/DLOaZCjtcK29KgJjPwhv08UMn1AiEA -h35nGTASR8/E7xz+56ZUleqD7U1ABFgWZRZskIzsFO8wDQYJKoZIhvcNAQELBQAD -ggEBADDJBVbKe2LPHmi8k2vxErB3Y0Ty+3gwgPEXKYtEvQ7tos89eE+QmOXAzH5J -GwRarFf7kzmKeJv04tMebiEtshpap47oJfxCxfrtpja8hP8Cdu/v/Ae6eEzu3yet -0N08GJdxQKfgCFaoGUptbaF2RCIZS12SVcX4TPpdP+xaiZdmIx4dGM6tReQ8+y8B -10b4Hi2+d/zW0W1z6+FAemU6yleWriJDUik5oas9XZF5LAAMDb/WgF5eIB6P9CUG -LuAO8lWlk9nBgXvMLTxZ74SJb17H4kFEIrIjvABNshz5gBW8xw9nfr5YIfANtwEj -BDsq06Df3UORYVs/j3T97gPAEZ4= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-hugeurl/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-hugeurl/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-hugeurl/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-hugeurl/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-http-client-hugeurl) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-client-hugeurl) -set(SRCS minimal-http-client-hugeurl.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-hugeurl/minimal-http-client-hugeurl.c libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-hugeurl/minimal-http-client-hugeurl.c --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-hugeurl/minimal-http-client-hugeurl.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-hugeurl/minimal-http-client-hugeurl.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,228 +0,0 @@ -/* - * lws-minimal-http-client hugeurl - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the a minimal http client using lws. - * - * It visits https://warmcat.com/?fakeparam=<2KB> and receives the html - * page there. You can dump the page data by changing the #if 0 below. - */ - -#include -#include -#include - -static int interrupted, bad = 1, status; -static struct lws *client_wsi; - -static const char * const uri = - "/?fakeparam=" - "00000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000" /* 500 */ - "11111111111111111111111111111111111111111111111111" - "11111111111111111111111111111111111111111111111111" - "11111111111111111111111111111111111111111111111111" - "11111111111111111111111111111111111111111111111111" - "11111111111111111111111111111111111111111111111111" - "11111111111111111111111111111111111111111111111111" - "11111111111111111111111111111111111111111111111111" - "11111111111111111111111111111111111111111111111111" - "11111111111111111111111111111111111111111111111111" - "11111111111111111111111111111111111111111111111111" /* 1000 */ - "22222222222222222222222222222222222222222222222222" - "22222222222222222222222222222222222222222222222222" - "22222222222222222222222222222222222222222222222222" - "22222222222222222222222222222222222222222222222222" - "22222222222222222222222222222222222222222222222222" - "22222222222222222222222222222222222222222222222222" - "22222222222222222222222222222222222222222222222222" - "22222222222222222222222222222222222222222222222222" - "22222222222222222222222222222222222222222222222222" - "22222222222222222222222222222222222222222222222222" /* 1500 */ - "33333333333333333333333333333333333333333333333333" - "33333333333333333333333333333333333333333333333333" - "33333333333333333333333333333333333333333333333333" - "33333333333333333333333333333333333333333333333333" - "33333333333333333333333333333333333333333333333333" - "33333333333333333333333333333333333333333333333333" - "33333333333333333333333333333333333333333333333333" - "33333333333333333333333333333333333333333333333333" - "33333333333333333333333333333333333333333333333333" - "33333333333333333333333333333333333333333333333333" /* 2000 */ -; - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - client_wsi = NULL; - break; - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - status = lws_http_client_http_response(wsi); - lwsl_user("Connected with server response: %d\n", status); - break; - - /* chunks of chunked content, with header removed */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); -#if 0 /* enable to dump the html */ - { - const char *p = in; - - while (len--) - if (*p < 0x7f) - putchar(*p++); - else - putchar('.'); - } -#endif - return 0; /* don't passthru */ - - /* uninterpreted http content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - { - char buffer[1024 + LWS_PRE]; - char *px = buffer + LWS_PRE; - int lenx = sizeof(buffer) - LWS_PRE; - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - return 0; /* don't passthru */ - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - client_wsi = NULL; - bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - client_wsi = NULL; - bad = status != 200; - lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "http", - callback_http, - 0, - 0, - }, - { NULL, NULL, 0, 0 } -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_client_connect_info i; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - signal(SIGINT, sigint_handler); - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http client hugeurl [-d ] [-l] [--h1]\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - info.pt_serv_buf_size = 8192; - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + 1 + 1; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./warmcat.com.cer"; -#endif - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - i.context = context; - i.ssl_connection = LCCSCF_USE_SSL; - - if (lws_cmdline_option(argc, argv, "-l")) { - i.port = 7681; - i.address = "localhost"; - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - } else { - i.port = 443; - i.address = "warmcat.com"; - } - - if (lws_cmdline_option(argc, argv, "--h1")) - i.alpn = "http/1.1"; - - i.path = uri; - i.host = i.address; - i.origin = i.address; - i.method = "GET"; - i.protocol = protocols[0].name; - i.pwsi = &client_wsi; - - lws_client_connect_via_info(&i); - - while (n >= 0 && client_wsi && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad? "failed": "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-hugeurl/README.md libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-hugeurl/README.md --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-hugeurl/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-hugeurl/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -# lws minimal http client hugeurl - -## build - -``` - $ cmake . && make -``` - -## usage - -The application goes to https://warmcat.com/?fakeparam=<2KB> and receives the page data. - -``` - $ ./lws-minimal-http-client -[2018/03/04 14:43:20:8562] USER: LWS minimal http client hugeurl -[2018/03/04 14:43:20:8571] NOTICE: Creating Vhost 'default' port -1, 1 protocols, IPv6 on -[2018/03/04 14:43:20:8616] NOTICE: created client ssl context for default -[2018/03/04 14:43:20:8617] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com -[2018/03/04 14:43:21:1496] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com -[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: incoming content length 26520 -[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: client connection up -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 974 -[2018/03/04 14:43:22:3022] NOTICE: lws_http_client_read: transaction completed says -1 -[2018/03/04 14:43:23:3042] USER: Completed -``` - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=6 - -dotest $1 $2 warmcat -dotest $1 $2 warmcat-h1 --h1 - -spawn "" $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost -l -spawn $SPID $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost-h1 -l --h1 -kill $SPID 2>/dev/null -wait $SPID 2>/dev/null - - -if [ -z "$TRAVIS_OS_NAME" ] ; then - SPID="" - spawn "" $5/http-server/minimal-http-server-eventlib $1/lws-minimal-http-server-eventlib --uv -s - dotest $1 $2 localhost-suv -l - spawn $SPID $5/http-server/minimal-http-server-eventlib $1/lws-minimal-http-server-eventlib --uv -s - dotest $1 $2 localhost-suv-h1 -l --h1 - - kill $SPID 2>/dev/null - wait $SPID 2>/dev/null -fi - -exit $FAILS - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-hugeurl/warmcat.com.cer libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-hugeurl/warmcat.com.cer --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-hugeurl/warmcat.com.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-hugeurl/warmcat.com.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFUDCCBDigAwIBAgISA4mJfIm3iCGbU9+o8YQa+4nUMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5MjNaFw0x -OTEyMDYwNzA5MjNaMBYxFDASBgNVBAMTC3dhcm1jYXQuY29tMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnEoH9JW3GvpadpxHGZPb5wv1Q6KfAIMWtdq -YCOfotFxaYULuzHVxmrTTgmEqJr+eBqUBkXKmGuRR/9UipOmTu5j02qFyWHotFdF -ZGyp//8z+Rle9Qt1nL68oNIZLDtWkybh5x00b1uo4eyEszXUaa0aLqKP3lH7Q4jI -aSVARZ8snrJR640Gp3ByudvNTYkGz469bpWzRC/8wSNtzzY02DvHs1GxQx9tMXw+ -BbtUxeP7lpYFKEFBjgZaIKLv+4g8ItJIuO7gMSzG2JfpQHxdhrlhxpx7dsaMUcyM -nnYXysNL5JG3KEMhkxbtdpCaEQ8jLSPbl/rnF/+mgce+lSjMuQIDAQABo4ICYjCC -Al4wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD -AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSI9ai12zLFeNTEDHKI9Ghkqcpa2TAf -BgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEw -LgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcw -LwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv -MBYGA1UdEQQPMA2CC3dhcm1jYXQuY29tMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcG -CysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5 -cHQub3JnMIIBBgYKKwYBBAHWeQIEAgSB9wSB9ADyAHcAY/Lbzeg7zCzPC3KEJ1dr -M6SNYXePvXWmOLHHaFRL2I0AAAFtCsVHHAAABAMASDBGAiEAy0q1cR4VwPL3iviL -cBWN67kjJRXk+DwhodmeoM3kb3gCIQC2soAHFs0Umo+0RNdFrL41+hMuidh2cXbb -Ovc6nh5tOQB3AOJpS64m6OlACeiGG7Y7g9Q+5/50iPukjyiTAZ3d8dv+AAABbQrF -R48AAAQDAEgwRgIhANqKQm4t9by263CJ7/DLOaZCjtcK29KgJjPwhv08UMn1AiEA -h35nGTASR8/E7xz+56ZUleqD7U1ABFgWZRZskIzsFO8wDQYJKoZIhvcNAQELBQAD -ggEBADDJBVbKe2LPHmi8k2vxErB3Y0Ty+3gwgPEXKYtEvQ7tos89eE+QmOXAzH5J -GwRarFf7kzmKeJv04tMebiEtshpap47oJfxCxfrtpja8hP8Cdu/v/Ae6eEzu3yet -0N08GJdxQKfgCFaoGUptbaF2RCIZS12SVcX4TPpdP+xaiZdmIx4dGM6tReQ8+y8B -10b4Hi2+d/zW0W1z6+FAemU6yleWriJDUik5oas9XZF5LAAMDb/WgF5eIB6P9CUG -LuAO8lWlk9nBgXvMLTxZ74SJb17H4kFEIrIjvABNshz5gBW8xw9nfr5YIfANtwEj -BDsq06Df3UORYVs/j3T97gPAEZ4= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-multi/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-multi/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-multi/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-multi/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-http-client-multi) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-client-multi) -set(SRCS minimal-http-client-multi.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,471 +0,0 @@ -/* - * lws-minimal-http-client-multi - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the a minimal http client using lws, which makes - * 8 downloads simultaneously from warmcat.com. - * - * Currently that takes the form of 8 individual simultaneous tcp and - * tls connections, which happen concurrently. Notice that the ordering - * of the returned payload may be intermingled for the various connections. - * - * By default the connections happen all together at the beginning and operate - * concurrently, which is fast. However this is resource-intenstive, there are - * 8 tcp connections, 8 tls tunnels on both the client and server. You can - * instead opt to have the connections happen one after the other inside a - * single tcp connection and tls tunnel, using HTTP/1.1 pipelining. To be - * eligible to be pipelined on another existing connection to the same server, - * the client connection must have the LCCSCF_PIPELINE flag on its - * info.ssl_connection member (this is independent of whether the connection - * is in ssl mode or not). - * - * HTTP/1.0: Pipelining only possible if Keep-Alive: yes sent by server - * HTTP/1.1: always possible... serializes requests - * HTTP/2: always possible... all requests sent as individual streams in parallel - */ - -#include -#include -#include -#include -#include - -#define COUNT 8 - -struct cliuser { - int index; -}; - -static int completed, failed, numbered, stagger_idx, posting, count = COUNT; -static lws_sorted_usec_list_t sul_stagger; -static struct lws_client_connect_info i; -static struct lws *client_wsi[COUNT]; -static char urlpath[64]; -static struct lws_context *context; - -/* we only need this for tracking POST emit state */ - -struct pss { - char body_part; -}; - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start, - *end = &buf[sizeof(buf) - LWS_PRE - 1]; - int n, idx = (int)(intptr_t)lws_get_opaque_user_data(wsi); - struct pss *pss = (struct pss *)user; - - switch (reason) { - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - lwsl_user("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: idx: %d, resp %u\n", - idx, lws_http_client_http_response(wsi)); - break; - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - client_wsi[idx] = NULL; - failed++; - goto finished; - - /* chunks of chunked content, with header removed */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - lwsl_user("RECEIVE_CLIENT_HTTP_READ: conn %d: read %d\n", idx, (int)len); - lwsl_hexdump_info(in, len); - return 0; /* don't passthru */ - - case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: - /* - * Tell lws we are going to send the body next... - */ - if (posting && !lws_http_is_redirected_to_get(wsi)) { - lwsl_user("%s: doing POST flow\n", __func__); - lws_client_http_body_pending(wsi, 1); - lws_callback_on_writable(wsi); - } else - lwsl_user("%s: doing GET flow\n", __func__); - break; - - /* uninterpreted http content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - { - char buffer[1024 + LWS_PRE]; - char *px = buffer + LWS_PRE; - int lenx = sizeof(buffer) - LWS_PRE; - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - return 0; /* don't passthru */ - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP %p: idx %d\n", - wsi, idx); - client_wsi[idx] = NULL; - goto finished; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - lwsl_info("%s: closed: %p\n", __func__, client_wsi[idx]); - if (client_wsi[idx]) { - /* - * If it completed normally, it will have been set to - * NULL then already. So we are dealing with an - * abnormal, failing, close - */ - client_wsi[idx] = NULL; - failed++; - goto finished; - } - break; - - case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: - if (!posting) - break; - if (lws_http_is_redirected_to_get(wsi)) - break; - lwsl_user("LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: %p, part %d\n", wsi, pss->body_part); - n = LWS_WRITE_HTTP; - - /* - * For a small body like this, we could prepare it in memory and - * send it all at once. But to show how to handle, eg, - * arbitrary-sized file payloads, or huge form-data fields, the - * sending is done in multiple passes through the event loop. - */ - - switch (pss->body_part++) { - case 0: - if (lws_client_http_multipart(wsi, "text", NULL, NULL, - &p, end)) - return -1; - /* notice every usage of the boundary starts with -- */ - p += lws_snprintf(p, end - p, "my text field\xd\xa"); - break; - case 1: - if (lws_client_http_multipart(wsi, "file", "myfile.txt", - "text/plain", &p, end)) - return -1; - p += lws_snprintf(p, end - p, - "This is the contents of the " - "uploaded file.\xd\xa" - "\xd\xa"); - break; - case 2: - if (lws_client_http_multipart(wsi, NULL, NULL, NULL, - &p, end)) - return -1; - lws_client_http_body_pending(wsi, 0); - /* necessary to support H2, it means we will write no - * more on this stream */ - n = LWS_WRITE_HTTP_FINAL; - break; - - default: - /* - * We can get extra callbacks here, if nothing to do, - * then do nothing. - */ - return 0; - } - - if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff(p, start), n) - != lws_ptr_diff(p, start)) - return 1; - - if (n != LWS_WRITE_HTTP_FINAL) - lws_callback_on_writable(wsi); - - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); - -finished: - if (++completed == count) { - if (!failed) - lwsl_user("Done: all OK\n"); - else - lwsl_err("Done: failed: %d\n", failed); - //interrupted = 1; - /* - * This is how we can exit the event loop even when it's an - * event library backing it... it will start and stage the - * destroy to happen after we exited this service for each pt - */ - lws_context_destroy(lws_get_context(wsi)); - } - - return 0; -} - -static const struct lws_protocols protocols[] = { - { "http", callback_http, sizeof(struct pss), 0, }, - { NULL, NULL, 0, 0 } -}; - -static void -signal_cb(void *handle, int signum) -{ - switch (signum) { - case SIGTERM: - case SIGINT: - break; - default: - lwsl_err("%s: signal %d\n", __func__, signum); - break; - } - lws_context_destroy(context); -} - -static void -sigint_handler(int sig) -{ - signal_cb(NULL, sig); -} - -#if defined(WIN32) -int gettimeofday(struct timeval * tp, struct timezone * tzp) -{ - // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's - // This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC) - // until 00:00:00 January 1, 1970 - static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); - - SYSTEMTIME system_time; - FILETIME file_time; - uint64_t time; - - GetSystemTime( &system_time ); - SystemTimeToFileTime( &system_time, &file_time ); - time = ((uint64_t)file_time.dwLowDateTime ) ; - time += ((uint64_t)file_time.dwHighDateTime) << 32; - - tp->tv_sec = (long) ((time - EPOCH) / 10000000L); - tp->tv_usec = (long) (system_time.wMilliseconds * 1000); - return 0; -} -#endif - -unsigned long long us(void) -{ - struct timeval t; - - gettimeofday(&t, NULL); - - return (t.tv_sec * 1000000ull) + t.tv_usec; -} - -static void -lws_try_client_connection(struct lws_client_connect_info *i, int m) -{ - char path[128]; - - if (numbered) { - lws_snprintf(path, sizeof(path), "/%d.png", m + 1); - i->path = path; - } else - i->path = urlpath; - - i->pwsi = &client_wsi[m]; - i->opaque_user_data = (void *)(intptr_t)m; - - if (!lws_client_connect_via_info(i)) { - failed++; - if (++completed == count) { - lwsl_user("Done: failed: %d\n", failed); - lws_context_destroy(context); - } - } else - lwsl_user("started connection %p: idx %d (%s)\n", - client_wsi[m], m, i->path); -} - -static void -stagger_cb(lws_sorted_usec_list_t *sul) -{ - lws_usec_t next; - - /* - * open the connections at 100ms intervals, with the - * last one being after 1s, testing both queuing, and - * direct H2 stream addition stability - */ - lws_try_client_connection(&i, stagger_idx++); - - if (stagger_idx == count) - return; - - next = 300 * LWS_US_PER_MS; - if (stagger_idx == count - 1) - next += 700 * LWS_US_PER_MS; - - lws_sul_schedule(context, 0, &sul_stagger, stagger_cb, next); -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - unsigned long long start; - const char *p; - int m, staggered = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - - info.signal_cb = signal_cb; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - - if (lws_cmdline_option(argc, argv, "--uv")) - info.options |= LWS_SERVER_OPTION_LIBUV; - else - if (lws_cmdline_option(argc, argv, "--event")) - info.options |= LWS_SERVER_OPTION_LIBEVENT; - else - if (lws_cmdline_option(argc, argv, "--ev")) - info.options |= LWS_SERVER_OPTION_LIBEV; - else - if (lws_cmdline_option(argc, argv, "--glib")) - info.options |= LWS_SERVER_OPTION_GLIB; - else - signal(SIGINT, sigint_handler); - - staggered = !!lws_cmdline_option(argc, argv, "-s"); - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http client [-s (staggered)] [-p (pipeline)]\n"); - lwsl_user(" [--h1 (http/1 only)] [-l (localhost)] [-d ]\n"); - lwsl_user(" [-n (numbered)] [--post]\n"); - - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - /* - * since we know this lws context is only ever going to be used with - * COUNT client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and COUNT + 1 (allowing for h2 - * network wsi) that we will use. - */ - info.fd_limit_per_thread = 1 + COUNT + 1; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./warmcat.com.cer"; -#endif - - if ((p = lws_cmdline_option(argc, argv, "--limit"))) - info.simultaneous_ssl_restriction = atoi(p); - -#if defined(LWS_WITH_DETAILED_LATENCY) - info.detailed_latency_cb = lws_det_lat_plot_cb; - info.detailed_latency_filepath = "/tmp/lws-latency-results"; -#endif - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - i.context = context; - i.ssl_connection = LCCSCF_USE_SSL | - LCCSCF_H2_QUIRK_OVERFLOWS_TXCR | - LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM; - - if (lws_cmdline_option(argc, argv, "--post")) { - posting = 1; - i.method = "POST"; - i.ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME; - } else - i.method = "GET"; - - /* enables h1 or h2 connection sharing */ - if (lws_cmdline_option(argc, argv, "-p")) - i.ssl_connection |= LCCSCF_PIPELINE; - - /* force h1 even if h2 available */ - if (lws_cmdline_option(argc, argv, "--h1")) - i.alpn = "http/1.1"; - - strcpy(urlpath, "/"); - - if (lws_cmdline_option(argc, argv, "-l")) { - i.port = 7681; - i.address = "localhost"; - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - if (posting) - strcpy(urlpath, "/formtest"); - } else { - i.port = 443; - i.address = "libwebsockets.org"; - if (posting) - strcpy(urlpath, "/testserver/formtest"); - } - - if (lws_cmdline_option(argc, argv, "-n")) - numbered = 1; - - if ((p = lws_cmdline_option(argc, argv, "--server"))) - i.address = p; - - if ((p = lws_cmdline_option(argc, argv, "--port"))) - i.port = atoi(p); - - if ((p = lws_cmdline_option(argc, argv, "--path"))) - lws_strncpy(urlpath, p, sizeof(urlpath)); - - if ((p = lws_cmdline_option(argc, argv, "-c"))) - if (atoi(p) <= COUNT && atoi(p)) - count = atoi(p); - - i.host = i.address; - i.origin = i.address; - i.protocol = protocols[0].name; - - if (!staggered) - /* - * just pile on all the connections at once, testing the - * pipeline queuing before the first is connected - */ - for (m = 0; m < count; m++) - lws_try_client_connection(&i, m); - else - /* - * delay the connections slightly - */ - lws_sul_schedule(context, 0, &sul_stagger, stagger_cb, - 100 * LWS_US_PER_MS); - - start = us(); - while (!lws_service(context, 0)) - ; - - lwsl_user("Duration: %lldms\n", (us() - start) / 1000); - lws_context_destroy(context); - - lwsl_user("Exiting with %d\n", failed || completed != count); - - return failed || completed != count; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-multi/README.md libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-multi/README.md --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-multi/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-multi/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -# lws minimal http client multi - -## build - -``` - $ cmake . && make -``` - -## usage - -The application goes to https://warmcat.com and receives the page data -same as minimal http client. - -However it does it for 8 client connections concurrently. - -## Commandline Options - -Option|Meaning ----|--- --s|Stagger the connections by 100ms, the last by 1s --p|Use http/1.1 pipelining or h2 simultaneous streams ---h1|Force http/1 only --l|Connect to server on https://localhost:7681 instead of https://warmcat.com:443 --n|Read numbered files like /1.png, /2.png etc. Default is just read / ---uv|Use libuv event loop if lws built for it ---event|Use libevent event loop if lws built for it ---ev|Use libev event loop if lws built for it ---post|POST to the server rather than GET --c|Create n connections (n can be 1 .. 8) ---path |Force the URL path (should start with /) \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-multi/selftest.sh libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-multi/selftest.sh --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-multi/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-multi/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=30 - -dotest $1 $2 warmcat -dotest $1 $2 warmcat-pipe -p -dotest $1 $2 warmcat-h1 --h1 -dotest $1 $2 warmcat-h1-pipe --h1 -p -dotest $1 $2 warmcat-stag -s -dotest $1 $2 warmcat-pipe-stag -p -s -dotest $1 $2 warmcat-h1-stag --h1 -s -dotest $1 $2 warmcat-h1-pipe-stag --h1 -p -s -dotest $1 $2 warmcat-post --post -dotest $1 $2 warmcat-post-pipe --post -p -dotest $1 $2 warmcat-post-pipe-stag --post -p -s -dotest $1 $2 warmcat-h1-post --post --h1 -dotest $1 $2 warmcat-h1-post-pipe --post --h1 -p -dotest $1 $2 warmcat-h1-post-pipe-stag --post --h1 -p -s -dotest $1 $2 warmcat-restrict-pipe --limit 1 -p -dotest $1 $2 warmcat-restrict-h1-pipe --limit 1 -p --h1 -dotest $1 $2 warmcat-restrict-pipe-stag --limit 1 -p -s -dotest $1 $2 warmcat-restrict-h1-pipe-stag --limit 1 -p --h1 -s -dofailtest $1 $2 fail-warmcat-restrict --limit 1 -dofailtest $1 $2 fail-warmcat-restrict-h1 --limit 1 --h1 -dofailtest $1 $2 fail-warmcat-restrict-stag --limit 1 -s -dofailtest $1 $2 fail-warmcat-restrict-h1-stag --limit 1 --h1 -s - -spawn "" $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost -l -spawn $SPID $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost-pipe -l -p -spawn $SPID $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost-h1 -l --h1 -spawn $SPID $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost-h1-pipe -l --h1 -p -spawn $SPID $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost-stag -l -s -spawn $SPID $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost-pipe-stag -l -p -s -spawn $SPID $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost-h1-stag -l --h1 -s -spawn $SPID $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls -dotest $1 $2 localhost-h1-pipe-stag -l --h1 -p -s - -kill $SPID 2>/dev/null -wait $SPID 2>/dev/null -exit $FAILS - diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-multi/warmcat.com.cer libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-multi/warmcat.com.cer --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-multi/warmcat.com.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-multi/warmcat.com.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFUDCCBDigAwIBAgISA4mJfIm3iCGbU9+o8YQa+4nUMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5MjNaFw0x -OTEyMDYwNzA5MjNaMBYxFDASBgNVBAMTC3dhcm1jYXQuY29tMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnEoH9JW3GvpadpxHGZPb5wv1Q6KfAIMWtdq -YCOfotFxaYULuzHVxmrTTgmEqJr+eBqUBkXKmGuRR/9UipOmTu5j02qFyWHotFdF -ZGyp//8z+Rle9Qt1nL68oNIZLDtWkybh5x00b1uo4eyEszXUaa0aLqKP3lH7Q4jI -aSVARZ8snrJR640Gp3ByudvNTYkGz469bpWzRC/8wSNtzzY02DvHs1GxQx9tMXw+ -BbtUxeP7lpYFKEFBjgZaIKLv+4g8ItJIuO7gMSzG2JfpQHxdhrlhxpx7dsaMUcyM -nnYXysNL5JG3KEMhkxbtdpCaEQ8jLSPbl/rnF/+mgce+lSjMuQIDAQABo4ICYjCC -Al4wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD -AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSI9ai12zLFeNTEDHKI9Ghkqcpa2TAf -BgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEw -LgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcw -LwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv -MBYGA1UdEQQPMA2CC3dhcm1jYXQuY29tMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcG -CysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5 -cHQub3JnMIIBBgYKKwYBBAHWeQIEAgSB9wSB9ADyAHcAY/Lbzeg7zCzPC3KEJ1dr -M6SNYXePvXWmOLHHaFRL2I0AAAFtCsVHHAAABAMASDBGAiEAy0q1cR4VwPL3iviL -cBWN67kjJRXk+DwhodmeoM3kb3gCIQC2soAHFs0Umo+0RNdFrL41+hMuidh2cXbb -Ovc6nh5tOQB3AOJpS64m6OlACeiGG7Y7g9Q+5/50iPukjyiTAZ3d8dv+AAABbQrF -R48AAAQDAEgwRgIhANqKQm4t9by263CJ7/DLOaZCjtcK29KgJjPwhv08UMn1AiEA -h35nGTASR8/E7xz+56ZUleqD7U1ABFgWZRZskIzsFO8wDQYJKoZIhvcNAQELBQAD -ggEBADDJBVbKe2LPHmi8k2vxErB3Y0Ty+3gwgPEXKYtEvQ7tos89eE+QmOXAzH5J -GwRarFf7kzmKeJv04tMebiEtshpap47oJfxCxfrtpja8hP8Cdu/v/Ae6eEzu3yet -0N08GJdxQKfgCFaoGUptbaF2RCIZS12SVcX4TPpdP+xaiZdmIx4dGM6tReQ8+y8B -10b4Hi2+d/zW0W1z6+FAemU6yleWriJDUik5oas9XZF5LAAMDb/WgF5eIB6P9CUG -LuAO8lWlk9nBgXvMLTxZ74SJb17H4kFEIrIjvABNshz5gBW8xw9nfr5YIfANtwEj -BDsq06Df3UORYVs/j3T97gPAEZ4= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-post/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-post/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-post/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-post/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-http-client-post) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-client-post) -set(SRCS minimal-http-client-post.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-post/libwebsockets.org.cer libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-post/libwebsockets.org.cer --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-post/libwebsockets.org.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-post/libwebsockets.org.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFWjCCBEKgAwIBAgISA9x0/oj5PLdW46hsmR82/7ytMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5NDBaFw0x -OTEyMDYwNzA5NDBaMBwxGjAYBgNVBAMTEWxpYndlYnNvY2tldHMub3JnMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPinIkleLmvEcA/YuBss6ASXVi7g -yr6Sss7cB3vTy7Fp8OB2c1N25prHZxVpORAUo0UreiaY2Ws4NFvDaYp08ZffevuC -UhThsEJlbkD0uvt7dPapJt9PNJtlxjNFWyvHEy6PijzIaMYDROiStcCJQn7kAew/ -Za2+5kNVgKqT+7OXukJEFdSdVZI6QC/npeQlkIrFSq1WVthCGBNJehxxES0hSWzk -0gNVKlkD3/SbkupsfUpe73XiawMtrtsSE7cdnul7VZmiP8I/3sJr1+4/3xZ+DEYg -mVB82B0vd08VJYzU7Nf0pz0PWusAmzRoRn81IXkOfBg9ohlSSEoZhHYS7QIDAQAB -o4ICZjCCAmIwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr -BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRmKKyGjufWgp7pR2x0tWxG -D9G+WTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcB -AQRjMGEwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlw -dC5vcmcwLwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlw -dC5vcmcvMBwGA1UdEQQVMBOCEWxpYndlYnNvY2tldHMub3JnMEwGA1UdIARFMEMw -CAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9j -cHMubGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcAdH7a -gzGtMxCRIZzOJU9CcMK//V5CIAjGNzV55hB7zFYAAAFtCsWIfgAABAMASDBGAiEA -0H55VqSKV3otHK7uHNbcR0QwoUYtCmeObhsqxzCnmDwCIQD3mtuSKrxTD3oA+Yde -nmTgWfFyS4TNgLNEPCJYo2s75gB1ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM -9OVFR/R4AAABbQrFil4AAAQDAEYwRAIgNSpvz/1JA2aP6fh6ujGNuYfrAvWjlxXo -CJtVGe4XaDYCIGmK1/9tl1uQbVD46P5NswnULq06KQmuOrlI3HO4r86HMA0GCSqG -SIb3DQEBCwUAA4IBAQBiAlV7wkCsWE99VmZHBmcbZChWyWUHG3LM1hnaQRQjTSYk -CIlauCpWzlUd6weuvra85KqBbCYo+1hxbwITI796uAdgtHmBE8nj0VltHwKeSq2s -KKiGXBRT7Z7t0VHYSLOlGOVn1auuQFaWBArc0cQ/m1ZsoHvOiHTlKQvVsA4HnIxA -CjGY9OOQoh0c36ecbJZ44XKnU9J/OXtDx00aW6QodaZmgMp/OOCghFQUvufkgTUL -LZid873/8dJVWjAaj1VdadO1nSbdAfBbeWXy93+vg1aAoig80RoscrzYCaNlwmR7 -EO5zWxL3l+xUZogQSJuICgUgNzVB3wjn8HeHGsqt ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-post/minimal-http-client-post.c libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-post/minimal-http-client-post.c --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-post/minimal-http-client-post.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-post/minimal-http-client-post.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,271 +0,0 @@ -/* - * lws-minimal-http-client-post - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the a minimal http client using lws and POST. - * - * It POSTs both form data and a file to the form at - * https://libwebsockets.org/testserver/formtest and dumps - * the html page received generated by the POST handler. - */ - -#include -#include -#include - -static int interrupted, bad = 0, status, count_clients = 1, completed; -static struct lws *client_wsi[4]; - -struct pss { - char body_part; -}; - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct pss *pss = (struct pss *)user; - char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start, - *end = &buf[sizeof(buf) - LWS_PRE - 1]; - int n; - - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - bad = 1; - if (++completed == count_clients) - lws_cancel_service(lws_get_context(wsi)); - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - for (n = 0; n < count_clients; n++) - if (client_wsi[n] == wsi) { - client_wsi[n] = NULL; - bad |= status != 200; - if (++completed == count_clients) - /* abort poll wait */ - lws_cancel_service(lws_get_context(wsi)); - } - break; - - /* ...callbacks related to receiving the result... */ - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - status = lws_http_client_http_response(wsi); - lwsl_user("Connected with server response: %d\n", status); - break; - - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); - lwsl_hexdump_notice(in, len); - return 0; /* don't passthru */ - - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - n = sizeof(buf) - LWS_PRE; - if (lws_http_client_read(wsi, &p, &n) < 0) - return -1; - - return 0; /* don't passthru */ - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); - bad |= status != 200; - /* - * Do this to mark us as having processed the completion - * so close doesn't duplicate (with pipelining, completion != - * connection close - */ - for (n = 0; n < count_clients; n++) - if (client_wsi[n] == wsi) - client_wsi[n] = NULL; - if (++completed == count_clients) - /* abort poll wait */ - lws_cancel_service(lws_get_context(wsi)); - break; - - /* ...callbacks related to generating the POST... */ - - case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: - /* - * Tell lws we are going to send the body next... - */ - if (!lws_http_is_redirected_to_get(wsi)) { - lwsl_user("%s: doing POST flow\n", __func__); - lws_client_http_body_pending(wsi, 1); - lws_callback_on_writable(wsi); - } else - lwsl_user("%s: doing GET flow\n", __func__); - break; - - case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: - if (lws_http_is_redirected_to_get(wsi)) - break; - lwsl_user("LWS_CALLBACK_CLIENT_HTTP_WRITEABLE\n"); - n = LWS_WRITE_HTTP; - - /* - * For a small body like this, we could prepare it in memory and - * send it all at once. But to show how to handle, eg, - * arbitrary-sized file payloads, or huge form-data fields, the - * sending is done in multiple passes through the event loop. - */ - - switch (pss->body_part++) { - case 0: - if (lws_client_http_multipart(wsi, "text", NULL, NULL, - &p, end)) - return -1; - /* notice every usage of the boundary starts with -- */ - p += lws_snprintf(p, end - p, "my text field\xd\xa"); - break; - case 1: - if (lws_client_http_multipart(wsi, "file", "myfile.txt", - "text/plain", &p, end)) - return -1; - p += lws_snprintf(p, end - p, - "This is the contents of the " - "uploaded file.\xd\xa" - "\xd\xa"); - break; - case 2: - if (lws_client_http_multipart(wsi, NULL, NULL, NULL, - &p, end)) - return -1; - lws_client_http_body_pending(wsi, 0); - /* necessary to support H2, it means we will write no - * more on this stream */ - n = LWS_WRITE_HTTP_FINAL; - break; - - default: - /* - * We can get extra callbacks here, if nothing to do, - * then do nothing. - */ - return 0; - } - - if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff(p, start), n) - != lws_ptr_diff(p, start)) - return 1; - - if (n != LWS_WRITE_HTTP_FINAL) - lws_callback_on_writable(wsi); - - return 0; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "http", - callback_http, - sizeof(struct pss), - 0, - }, - { NULL, NULL, 0, 0 } -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_client_connect_info i; - struct lws_context *context; - int n = 0; - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - lws_cmdline_option_handle_builtin(argc, argv, &info); - lwsl_user("LWS minimal http client - POST [-d] [-l] [--h1]\n"); - - if (lws_cmdline_option(argc, argv, "-m")) - count_clients = LWS_ARRAY_SIZE(client_wsi); - - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + count_clients + 1; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - if (!lws_cmdline_option(argc, argv, "-l")) - info.client_ssl_ca_filepath = "./libwebsockets.org.cer"; -#endif - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - i.context = context; - i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_HTTP_MULTIPART_MIME; - - if (lws_cmdline_option(argc, argv, "-l")) { - i.port = 7681; - i.address = "localhost"; - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - i.path = "/formtest"; - } else { - i.port = 443; - i.address = "libwebsockets.org"; - i.path = "/testserver/formtest"; - } - - if (lws_cmdline_option(argc, argv, "--form1")) - i.path = "/form1"; - - i.host = i.address; - i.origin = i.address; - i.method = "POST"; - - /* force h1 even if h2 available */ - if (lws_cmdline_option(argc, argv, "--h1")) - i.alpn = "http/1.1"; - - i.protocol = protocols[0].name; - - for (n = 0; n < count_clients; n++) { - i.pwsi = &client_wsi[n]; - if (!lws_client_connect_via_info(&i)) - completed++; - } - - while (n >= 0 && completed != count_clients && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-post/README.md libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-post/README.md --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-post/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-post/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -# lws minimal http client POST - -This example demonstrates a multipart POST to - -https://libwebsockets.org/testserver/formtest - -setting both a form variable and uploading a -short file. - -The result of the POST form processing is captured -and displayed in a hexdump. - -This is programmatically POSTing to the same -form you can access at - -https://libwebsockets.org/testserver - -in the "POST" tab with file upload. - -By default the client action occurs using http/2 if -your lws was built with `-DLWS_WITH_HTTP2=1`. - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-client-post -[2018/04/03 13:13:10:7891] USER: LWS minimal http client - POST -[2018/04/03 13:13:10:7905] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 on -[2018/04/03 13:13:10:7984] NOTICE: created client ssl context for default -[2018/04/03 13:13:12:8444] USER: LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER -[2018/04/03 13:13:12:8444] USER: LWS_CALLBACK_CLIENT_HTTP_WRITEABLE -[2018/04/03 13:13:12:8445] USER: LWS_CALLBACK_CLIENT_HTTP_WRITEABLE -[2018/04/03 13:13:12:8445] USER: LWS_CALLBACK_CLIENT_HTTP_WRITEABLE -[2018/04/03 13:13:13:1437] USER: LWS_CALLBACK_CLIENT_HTTP_WRITEABLE -[2018/04/03 13:13:13:1440] USER: LWS_CALLBACK_CLIENT_HTTP_WRITEABLE -[2018/04/03 13:13:13:1440] USER: RECEIVE_CLIENT_HTTP_READ: read 402 -[2018/04/03 13:13:13:1441] NOTICE: -[2018/04/03 13:13:13:1441] NOTICE: 0000: 3C 68 74 6D 6C 3E 3C 62 6F 64 79 3E 3C 68 31 3E

-[2018/04/03 13:13:13:1441] NOTICE: 0010: 46 6F 72 6D 20 72 65 73 75 6C 74 73 20 28 61 66 Form results (af -[2018/04/03 13:13:13:1441] NOTICE: 0020: 74 65 72 20 75 72 6C 64 65 63 6F 64 69 6E 67 29 ter urldecoding) -[2018/04/03 13:13:13:1441] NOTICE: 0030: 3C 2F 68 31 3E 3C 74 61 62 6C 65 3E 3C 74 72 3E

-[2018/04/03 13:13:13:1441] NOTICE: 0040: 3C 74 64 3E 4E 61 6D 65 3C 2F 74 64 3E 3C 74 64 < -[2018/04/03 13:13:13:1441] NOTICE: 0070: 74 72 3E 3C 74 64 3E 3C 62 3E 74 65 78 74 3C 2F tr>< -[2018/04/03 13:13:13:1442] NOTICE: 00D0: 74 64 3E 4E 55 4C 4C 3C 2F 74 64 3E 3C 2F 74 72 td>NULL -[2018/04/03 13:13:13:1442] NOTICE: 0110: 3C 2F 74 72 3E 3C 74 72 3E 3C 74 64 3E 3C 62 3E < -[2018/04/03 13:13:13:1442] NOTICE: 0130: 74 64 3E 30 3C 2F 74 64 3E 3C 74 64 3E 4E 55 4C td>0
filena -[2018/04/03 13:13:13:1442] NOTICE: 0160: 6D 65 3A 3C 2F 62 3E 20 6D 79 66 69 6C 65 2E 74 me: myfile.t -[2018/04/03 13:13:13:1442] NOTICE: 0170: 78 74 2C 20 3C 62 3E 6C 65 6E 67 74 68 3C 2F 62 xt, length 44 -[2018/04/03 13:13:13:1442] NOTICE: -[2018/04/03 13:13:13:1442] USER: LWS_CALLBACK_COMPLETED_CLIENT_HTTP -[2018/04/03 13:13:13:1455] USER: Completed -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-post/selftest.sh libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-post/selftest.sh --- libwebsockets-4.0.20/minimal-examples/http-client/minimal-http-client-post/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/minimal-http-client-post/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=8 - -dotest $1 $2 warmcat -dotest $1 $2 warmcat-h1 --h1 -dotest $1 $2 warmcat-m -m -dotest $1 $2 warmcat-m-h1 -m --h1 - -spawn "" $5 $1/libwebsockets-test-server -s -dotest $1 $2 localhost -l -d1151 -spawn $SPID $5 $1/libwebsockets-test-server -s -dotest $1 $2 localhost-h1 -l --h1 -spawn $SPID $5 $1/libwebsockets-test-server -s -dotest $1 $2 localhost-m -l -m -spawn $SPID $5 $1/libwebsockets-test-server -s -dotest $1 $2 localhost-m-h1 -l -m --h1 - -kill $SPID 2>/dev/null -wait $SPID 2>/dev/null -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/http-client/README.md libwebsockets-2.4.2/minimal-examples/http-client/README.md --- libwebsockets-4.0.20/minimal-examples/http-client/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-client/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -|name|demonstrates| ----|--- -minimal-http-client-certinfo|Shows how to gain detailed information on the peer certificate -minimal-http-client-custom-headers|Shows how to send and receive custom headers (h1 only) -minimal-http-client-hugeurl|Sends a > 2.5KB URL to warmcat.com -minimal-http-client-multi|Connects to and reads https://warmcat.com, 8 times concurrently -minimal-http-client-post|POSTs a form containing an uploaded file and a form variable, and captures the response -minimal-http-client|Connects to and reads https://warmcat.com diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-http-server) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server) -set(SRCS minimal-http-server.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/minimal-http-server.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/minimal-http-server.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/minimal-http-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/minimal-http-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -/* - * lws-minimal-http-server - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ - - - - - - -
- - Hello from the minimal http server example. -
- You can confirm the 404 page handler by going to this - nonexistant page. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -# lws minimal http server - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server -[2018/03/04 09:30:02:7986] USER: LWS minimal http server | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/ba-passwords libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/ba-passwords --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/ba-passwords 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/ba-passwords 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -user:password diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-http-server-basicauth) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-basicauth) -set(SRCS minimal-http-server-basicauth.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITH_HTTP_BASIC_AUTH 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/minimal-http-server-basicauth.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/minimal-http-server-basicauth.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/minimal-http-server-basicauth.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/minimal-http-server-basicauth.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -/* - * lws-minimal-http-server-basicauth - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http server with a second mount that - * is protected using a password file and basic auth. - * - * To keep it simple, it serves the static stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * - * You can change that by changing mount.origin below. - */ - -#include -#include -#include -#include - -static int interrupted; - -/* override the default mount for /secret in the URL space */ - -static const struct lws_http_mount mount_secret = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/secret", /* mountpoint URL */ - /* .origin */ "./mount-secret-origin", - /* .def */ "index.html", - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* dynamic */ - /* .mountpoint_len */ 7, /* char count */ - /* .basic_auth_login_file */ "./ba-passwords", -}; - -/* default mount serves the URL space from ./mount-origin */ - -static const struct lws_http_mount mount = { - /* .mount_next */ &mount_secret, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server basic auth | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ - - - - - - -
- - Hello from the minimal http server basic auth example. -

- This is a static page served from ./mount-origin/index.html. -

- Stuff down /secret in the URL space is protected by Basic Auth.
- Your browser will ask for a username / password combination, and
- lws will check it against ./ba-passwords, which contains a list of
- "username:password" one per line.
-
- The example content for ba-passwords is literally "user:password".
- Click on the link into the protected area of the URL space below
- and give your browser the credentials "user" and "password". -

- /secret - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-secret-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-secret-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-secret-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-secret-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ - - - - - -
- - This is the big secret protected by basic auth. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-secret-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-secret-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/mount-secret-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/mount-secret-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-basicauth/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-basicauth/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -# lws minimal http server basic auth - -This demonstrates how to protect a mount using a password -file outside of the mount itself. - -The demo has two mounts, a normal one at / and one protected -by basic auth at /secret. - -The file at ./ba-passwords contains valid user:password -combinations. - -## Discovering the authenticated user - -After a successful authentication, the `WSI_TOKEN_HTTP_AUTHORIZATION` token -contains the authenticated username. - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-basic-auth -[2018/04/19 08:40:05:1333] USER: LWS minimal http server basic auth | visit http://localhost:7681 -[2018/04/19 08:40:05:1333] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -``` - -Visit http://localhost:7681, and follow the link there to the secret area. - -Give your browser "user" and "password" as the credentials. - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-cgi/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-cgi/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-cgi/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-cgi/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -project(lws-minimal-http-server-cgi) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-cgi) -set(SRCS minimal-http-server.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_CGI 1 requirements) -require_lws_config(LWS_WITHOUT_SERVER 0 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-cgi/minimal-http-server.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-cgi/minimal-http-server.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-cgi/minimal-http-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-cgi/minimal-http-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -/* - * lws-minimal-http-server-cgi - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -static int interrupted; -static char cgi_script_fullpath[256]; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ cgi_script_fullpath, /* cgi script */ - /* .def */ "/", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_CGI, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server | visit http://localhost:7681\n"); - - { - char cwd[128]; - cwd[0] = '\0'; - getcwd(cwd, sizeof(cwd)); - - lws_snprintf(cgi_script_fullpath, sizeof(cgi_script_fullpath), - "%s/my-cgi-script.sh", cwd); - } - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - if (lws_cmdline_option(argc, argv, "-s")) { - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 1000); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-cgi/my-cgi-script.sh libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-cgi/my-cgi-script.sh --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-cgi/my-cgi-script.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-cgi/my-cgi-script.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -#!/bin/sh - -echo -e -n "content-type: text/html\x0d\x0a" -echo -e -n "transfer-encoding: chunked\x0d\x0a" -echo -e -n "\x0d\x0a" - -echo "" -echo "

lwstest script stdout

" ->&2 echo -n "lwstest script stderr: REQUEST_METHOD was $REQUEST_METHOD" - -echo "

REQUEST_METHOD=$REQUEST_METHOD

" - -if [ "$REQUEST_METHOD" = "POST" ] ; then - >&2 echo "lwstest script stderr: doing read" - echo "CONTENT_LENGTH=$CONTENT_LENGTH" - read -n $CONTENT_LENGTH line - >&2 echo "lwstest script stderr: done read" - - echo "read=\"$line\"" -else - echo "
NameLength -[2018/04/03 13:13:13:1441] NOTICE: 0060: 56 61 6C 75 65 3C 2F 74 64 3E 3C 2F 74 72 3E 3C Value
text13my text fi -[2018/04/03 13:13:13:1441] NOTICE: 00A0: 65 6C 64 3C 2F 74 64 3E 3C 2F 74 72 3E 3C 74 72 eld
send -[2018/04/03 13:13:13:1441] NOTICE: 00C0: 3C 2F 74 64 3E 3C 74 64 3E 30 3C 2F 74 64 3E 3C 0
file -[2018/04/03 13:13:13:1442] NOTICE: 00F0: 3C 2F 62 3E 3C 2F 74 64 3E 3C 74 64 3E 30 3C 2F 0NULL
-[2018/04/03 13:13:13:1442] NOTICE: 0120: 75 70 6C 6F 61 64 3C 2F 62 3E 3C 2F 74 64 3E 3C uploadNUL -[2018/04/03 13:13:13:1442] NOTICE: 0140: 4C 3C 2F 74 64 3E 3C 2F 74 72 3E 3C 2F 74 61 62 L
" - echo "" - cat /proc/meminfo | while read line ; do - A=`echo "$line" | cut -d: -f1` - B=`echo "$line" | tr -s ' ' | cut -d' ' -f2-` - echo -e "" - echo -e "" - done - echo "
/proc/meminfo
$A$B
" -fi - -echo "
done" -echo "" - -exit 0 - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-cgi/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-cgi/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-cgi/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-cgi/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -# lws minimal http server-cgi - -## build - -``` - $ cmake . && make -``` - -## usage - -This example runs a script ./my-cgi-script.sh when you vist / - -The script dumps some information from /proc on stdout, which -is proxied back to the browser, script output on stderr is -printed in the console. - -It's able to serve the script output over h1 using chunked encoding, -and over h2 having stripped the chunked encoding from the script -output. - -``` - $ ./lws-minimal-http-server-cgi -[2019/11/18 16:31:29:5481] U: LWS minimal http server | visit http://localhost:7681 -[2019/11/18 16:31:40:2176] N: CGI-stderr: lwstest script stderr: REQUEST_METHOD was GET -``` - -Visit http://localhost:7681 - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-http-server-custom-headers) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-custom-headers) -set(SRCS minimal-http-server-custom-headers.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_CUSTOM_HEADERS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/minimal-http-server-custom-headers.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/minimal-http-server-custom-headers.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/minimal-http-server-custom-headers.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/minimal-http-server-custom-headers.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,220 +0,0 @@ -/* - * lws-minimal-http-server-custom-headers - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http server that can produce dynamic http - * content as well as static content. - * - * To keep it simple, it serves the static stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * - * You can change that by changing mount.origin below. - */ - -#include -#include -#include -#include - -static int interrupted; - -struct pss { - char result[128 + LWS_PRE]; - int len; -}; - -/* - * This just lets us override LWS_CALLBACK_HTTP handling before passing it - * and everything else to the default handler. - */ - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - uint8_t buf[LWS_PRE + 2048], *start = &buf[LWS_PRE], *p = start, - *end = &buf[sizeof(buf) - LWS_PRE - 1]; - struct pss *pss = (struct pss *)user; - char value[32], *pr = &pss->result[LWS_PRE]; - int n, e = sizeof(pss->result) - LWS_PRE; - - switch (reason) { - case LWS_CALLBACK_HTTP: - /* - * LWS doesn't have the "DNT" header built-in. But we can - * query it using the "custom" versions of the header apis. - * - * You can set your modern browser to issue DNT, look in the - * privacy settings of your browser. - */ - - pss->len = 0; - n = lws_hdr_custom_length(wsi, "dnt:", 4); - if (n < 0) - pss->len = lws_snprintf(pr, e, - "%s: DNT header not found\n", __func__); - else { - - pss->len = lws_snprintf(pr, e, - "%s: DNT length %d
", __func__, n); - n = lws_hdr_custom_copy(wsi, value, sizeof(value), "dnt:", 4); - if (n < 0) - pss->len += lws_snprintf(pr + pss->len, e - pss->len, - "%s: unable to get DNT value\n", __func__); - else - - pss->len += lws_snprintf(pr + pss->len , e - pss->len, - "%s: DNT value '%s'\n", __func__, value); - } - - lwsl_user("%s\n", pr); - - if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, - "text/html", pss->len, &p, end)) - return 1; - - if (lws_finalize_write_http_header(wsi, start, &p, end)) - return 1; - - - /* write the body separately */ - lws_callback_on_writable(wsi); - return 0; - - case LWS_CALLBACK_HTTP_WRITEABLE: - - strcpy((char *)start, "hello"); - - if (lws_write(wsi, (uint8_t *)pr, pss->len, LWS_WRITE_HTTP_FINAL) != pss->len) - return 1; - - if (lws_http_transaction_completed(wsi)) - return -1; - return 0; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static struct lws_protocols protocols[] = { - { "http", callback_http, sizeof(struct pss), 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static const struct lws_http_mount mount_dyn = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/dyn", /* mountpoint URL */ - /* .origin */ NULL, /* protocol */ - /* .def */ NULL, - /* .protocol */ "http", - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_CALLBACK, /* dynamic */ - /* .mountpoint_len */ 4, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -/* default mount serves the URL space from ./mount-origin */ - -static const struct lws_http_mount mount = { - /* .mount_next */ &mount_dyn, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server custom headers | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - /* for testing ah queue, not useful in real world */ - if (lws_cmdline_option(argc, argv, "--ah1")) - info.max_http_header_pool = 1; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* http on 7681 */ - - info.port = 7681; - info.protocols = protocols; - info.mounts = &mount; - info.vhost_name = "http"; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("Failed to create tls vhost\n"); - goto bail; - } - - /* https on 7682 */ - - info.port = 7682; - info.error_document_404 = "/404.html"; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - info.vhost_name = "https"; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("Failed to create tls vhost\n"); - goto bail; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail: - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ - - - - - - -
- - Hello from the minimal http server custom headers example. -

- The idea is it will tell you what your browser sent for DNT, a header lws doesn't already know. -

- At the moment the custom header api only works on h1. -

- - Show DNT header using h1 over http
- Show DNT header using h1 (h2 if enabled) over https
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-custom-headers/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-custom-headers/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -# lws minimal http server dynamic content - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-dynamic -[2018/03/20 10:24:24:7099] USER: LWS minimal http server dynamic | visit http://localhost:7681 -[2018/03/20 10:24:24:7099] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -``` - -Visit http://localhost:7681, which is all static content. - -Click on the link to /dyn/anything, this opens a new tab with dynamicly-produced content. - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/ba-passwords libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/ba-passwords --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/ba-passwords 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/ba-passwords 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -user1:password -user2:password diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -project(lws-minimal-http-server-deaddrop) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-deaddrop) -set(SRCS minimal-http-server-deaddrop.c) - -# NOTE... if you are building this standalone, you must point LWS_PLUGINS_DIR -# to the lws plugins dir so it can pick up the plugin source. Eg, -# cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITH_HTTP_BASIC_AUTH 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (LWS_PLUGINS_DIR) - include_directories(${LWS_PLUGINS_DIR}) - endif() - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/minimal-http-server-deaddrop.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/minimal-http-server-deaddrop.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/minimal-http-server-deaddrop.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/minimal-http-server-deaddrop.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,171 +0,0 @@ -/* - * lws-minimal-http-server-deaddrop - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates how you can leverage the lws deaddrop plugin to make a - * secure, modern html5 file upload and sharing application. - * - * Because the guts are in a plugin, you can avoid all this setup by using the - * plugin from lwsws and do the config in JSON. - */ - -#include -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "../plugins/deaddrop/protocol_lws_deaddrop.c" - -static struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_DEADDROP, - { NULL, NULL, 0, 0 } /* terminator */ -}; - - -static int interrupted; - -/* - * teach the /get mount how to present various filetypes to the client... - * lws won't serve files it doesn't know the mimetype for as a security - * measure. - */ - -static struct lws_protocol_vhost_options em3 = { - NULL, NULL, ".zip", "application/zip" -}, em2 = { - &em3, NULL, ".pdf", "application/pdf" -}, extra_mimetypes = { - &em2, NULL, ".tar.gz", "application/x-gzip" -}; - -/* wire up /upload URLs to the plugin (protected by basic auth) */ - -static const struct lws_http_mount mount_upload = { - /* .mount_next */ NULL, - /* .mountpoint */ "/upload", /* mountpoint URL */ - /* .origin */ "lws-deaddrop", - /* .def */ "", - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_CALLBACK, - /* .mountpoint_len */ 7, /* char count */ - /* .basic_auth_login_file */ "./ba-passwords", -}; - -/* wire up /get URLs to the upload directory (protected by basic auth) */ - -static const struct lws_http_mount mount_get = { - /* .mount_next */ &mount_upload, /* linked-list "next" */ - /* .mountpoint */ "/get", /* mountpoint URL */ - /* .origin */ "./uploads", - /* .def */ "", - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ &extra_mimetypes, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* dynamic */ - /* .mountpoint_len */ 4, /* char count */ - /* .basic_auth_login_file */ "./ba-passwords", -}; - -/* wire up / to serve from ./mount-origin (protected by basic auth) */ - -static const struct lws_http_mount mount = { - /* .mount_next */ &mount_get, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ "./ba-passwords", -}; - -/* pass config options to the deaddrop plugin using pvos */ - -static struct lws_protocol_vhost_options pvo3 = { - /* make the wss also require to pass basic auth */ - NULL, NULL, "basic-auth", "./ba-passwords" -}, pvo2 = { - &pvo3, NULL, "max-size", "10000000" -}, pvo1 = { - &pvo2, NULL, "upload-dir", "./uploads" /* would be an absolute path */ -}, pvo = { - NULL, /* "next" pvo linked-list */ - &pvo1, /* "child" pvo linked-list */ - "lws-deaddrop", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server deaddrop | visit https://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.pvo = &pvo; - info.protocols = protocols; - info.error_document_404 = "/404.html"; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/deaddrop.css libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/deaddrop.css --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/deaddrop.css 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/deaddrop.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -.td { padding: 8px } -.h1 { } -.dd-fileinfo { font-size: 8pt; } -table td { - display: table-cell; - vertical-align: top; - background-color: rgba(247, 247, 232, 0.6); - text-align: center -} -table { - border: 2px solid #ccc; - padding: 4px; - border-radius: 12px; - transition: background-color 0.5s ease; -} -table.nb { border: 0px; border-radius: 0px; transition: opacity 0.5s; } -table.noconn { background-color: #ddd; } - -div { transition: opacity 0.5s; } -div.da { padding-left: 20px; padding-right:20px; } -div.trot { - animation: scale 0.5s linear infinite; -} -div.uplbox { padding-bottom: 8px; } -div.disa { opacity: 0.2; } - -td.ogn { text-align:left; font-size: 8pt; padding-left: 4px; padding-right: 4px;} -td.dow { text-align:left; font-size: 9pt; padding-left: 4px; padding-right: 4px;} -td.r { text-align: right; } -td.err { color: red; font-weight: bold; } -td.vm { display: table-cell; vertical-align: middle; padding-top: 12px; padding-bottom: 12px; } - -h3 { font-size: 12pt; margin-bottom: 6px; } -span { font-size: 9pt; } -a { font-size: 9pt; } - -input.ubtn { font-size: 16pt; margin-top: 4px; text-align: center } - -img.working { - display: inline-block; - float:left; - background: url("data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIKICAgICB3aWR0aD0iMjRweCIgaGVpZ2h0PSIzMHB4IiB2aWV3Qm94PSIwIDAgMjQgMzAiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUwIDUwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CiAgICA8cmVjdCB4PSIwIiB5PSIxMCIgd2lkdGg9IjQiIGhlaWdodD0iMTAiIGZpbGw9IiMzMzMiIG9wYWNpdHk9IjAuMiI+CiAgICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9Im9wYWNpdHkiIGF0dHJpYnV0ZVR5cGU9IlhNTCIgdmFsdWVzPSIwLjI7IDE7IC4yIiBiZWdpbj0iMHMiIGR1cj0iMC42cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIC8+CiAgICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImhlaWdodCIgYXR0cmlidXRlVHlwZT0iWE1MIiB2YWx1ZXM9IjEwOyAyMDsgMTAiIGJlZ2luPSIwcyIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICAgICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ieSIgYXR0cmlidXRlVHlwZT0iWE1MIiB2YWx1ZXM9IjEwOyA1OyAxMCIgYmVnaW49IjBzIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0iOCIgeT0iMTAiIHdpZHRoPSI0IiBoZWlnaHQ9IjEwIiBmaWxsPSIjMzMzIiAgb3BhY2l0eT0iMC4yIj4KICAgICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ib3BhY2l0eSIgYXR0cmlidXRlVHlwZT0iWE1MIiB2YWx1ZXM9IjAuMjsgMTsgLjIiIGJlZ2luPSIwLjE1cyIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICAgICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iaGVpZ2h0IiBhdHRyaWJ1dGVUeXBlPSJYTUwiIHZhbHVlcz0iMTA7IDIwOyAxMCIgYmVnaW49IjAuMTVzIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogICAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ5IiBhdHRyaWJ1dGVUeXBlPSJYTUwiIHZhbHVlcz0iMTA7IDU7IDEwIiBiZWdpbj0iMC4xNXMiIGR1cj0iMC42cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIC8+CiAgICA8L3JlY3Q+CiAgICA8cmVjdCB4PSIxNiIgeT0iMTAiIHdpZHRoPSI0IiBoZWlnaHQ9IjEwIiBmaWxsPSIjMzMzIiAgb3BhY2l0eT0iMC4yIj4KICAgICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ib3BhY2l0eSIgYXR0cmlidXRlVHlwZT0iWE1MIiB2YWx1ZXM9IjAuMjsgMTsgLjIiIGJlZ2luPSIwLjNzIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogICAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJoZWlnaHQiIGF0dHJpYnV0ZVR5cGU9IlhNTCIgdmFsdWVzPSIxMDsgMjA7IDEwIiBiZWdpbj0iMC4zcyIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICAgICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ieSIgYXR0cmlidXRlVHlwZT0iWE1MIiB2YWx1ZXM9IjEwOyA1OyAxMCIgYmVnaW49IjAuM3MiIGR1cj0iMC42cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIC8+CiAgICA8L3JlY3Q+Cjwvc3ZnPg=="); - width:0px; - height:0px; - cursor:pointer; - padding:0.6em 1em; - background-repeat: no-repeat; - vertical-align:middle; - color: rgba(0, 0, 0, 0); -} - -img.delbtn { - display: inline-block; - float:left; - background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAzNC4yMTcxMzMgMzQuMTQ0MjQ5IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KCTxkZWZzPgoJCTxyYWRpYWxHcmFkaWVudCBpZD0iYSIgY3g9IjEzNTguNSIgY3k9Ijk3Ny43MiIgcj0iNC4xMTYiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMS41MDM1IDAgMCAyLjQ5NDQgLTI1NzcuNCAtMTgyMy44KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCQk8c3RvcCBzdG9wLWNvbG9yPSIjZmZmIiBvZmZzZXQ9IjAiLz4KCQkJPHN0b3Agc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIwIiBvZmZzZXQ9IjEiLz4KCQk8L3JhZGlhbEdyYWRpZW50PgoJCTxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjEzNjYuMyIgeDI9IjEzNDQuOCIgeTE9Ijk3OC4xOSIgeTI9Ijk1OC43MyIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgtMjE3NCAtMTA3Ni4xKSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCQk8c3RvcCBzdG9wLWNvbG9yPSIjOWM5NzlkIiBvZmZzZXQ9IjAiLz4KCQkJPHN0b3Agc3RvcC1jb2xvcj0iI2Y0ZmVmMyIgc3RvcC1vcGFjaXR5PSIuNzQwMTYiIG9mZnNldD0iMSIvPgoJCTwvbGluZWFyR3JhZGllbnQ+Cgk8L2RlZnM+Cgk8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4MzQuMjkgMTIzLjc1KSI+CgkJPGcgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj4KCQkJPGNpcmNsZSBjeD0iLTgxNy4yMiIgY3k9Ii0xMDYuNjgiIHI9IjE2LjA5IiBmaWxsPSJ1cmwoI2IpIiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMS45NjUiLz4KCQkJPHBhdGggZD0ibS04MjUuNjctMTE0LjcxYzE3LjMzIDE3LjMzIDE3IDE2Ljk5OSAxNyAxNi45OTkiIGZpbGw9IiNmNTUiIHN0cm9rZT0iI2EwMCIgc3Ryb2tlLXdpZHRoPSIxLjY2NSIvPgoJCQk8cGF0aCBkPSJtLTgwOC45Ni0xMTQuNjFjLTE3LjMzIDE3LjMzLTE3IDE2Ljk5OS0xNyAxNi45OTkiIGZpbGw9IiNmNTUiIHN0cm9rZT0iI2EwMCIgc3Ryb2tlLXdpZHRoPSIxLjY2NSIvPgoJCTwvZz4KCQk8ZWxsaXBzZSB0cmFuc2Zvcm09InJvdGF0ZSg1NS44ODUpIiBjeD0iLTUzNC45NiIgY3k9IjYxNS4wNyIgcng9IjYuMTg4MyIgcnk9IjEwLjI2NyIgZmlsbD0idXJsKCNhKSIvPgoJPC9nPgo8L3N2Zz4="); - width:0px; - height:0px; - cursor:pointer; - padding:0.45em; - background-repeat: no-repeat; - vertical-align:middle; - color: rgba(0, 0, 0, 0); -} - -@keyframes scale { - 50% { - opacity: 0.5; - transform:scale(1.1) rotate(2deg); - } -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/deaddrop.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/deaddrop.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/deaddrop.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/deaddrop.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,296 +0,0 @@ -(function() { - - var server_max_size = 0, ws; - - function san(s) - { - if (!s) - return ""; - - return s.replace(/&/g, "&"). - replace(/\/g, ">"). - replace(/\"/g, """). - replace(/%/g, "%"); - } - - function lws_urlencode(s) - { - return encodeURI(s).replace(/@/g, "%40"); - } - - function trim(num) - { - var s = num.toString(); - - if (!s.indexOf(".")) - return s; - - while (s.length && s[s.length - 1] === "0") - s = s.substring(0, s.length - 1); - - if (s[s.length - 1] === ".") - s = s.substring(0, s.length - 1); - - return s; - } - - function humanize(n) - { - if (n < 1024) - return n + "B"; - - if (n < 1024 * 1024) - return trim((n / 1024).toFixed(2)) + "KiB"; - - if (n < 1024 * 1024 * 1024) - return trim((n / (1024 * 1024)).toFixed(2)) + "MiB"; - - return trim((n / (1024 * 1024 * 1024)).toFixed(2)) + "GiB"; - } - - function da_enter(e) - { - var da = document.getElementById("da"); - - e.preventDefault(); - da.classList.add("trot"); - } - - function da_leave(e) - { - var da = document.getElementById("da"); - - e.preventDefault(); - da.classList.remove("trot"); - } - - function da_over(e) - { - var da = document.getElementById("da"); - - e.preventDefault(); - da.classList.add("trot"); - } - - function clear_errors() { - var n, t = document.getElementById("ongoing"); - - for (n = 0; n < t.rows.length; n++) - if (t.rows[n].cells[0].classList.contains("err")) - t.deleteRow(n); - } - - function do_upload(file) { - var formData = new FormData(); - var t = document.getElementById("ongoing"); - - formData.append("file", file); - - var row = t.insertRow(0), c1 = row.insertCell(0), - c2 = row.insertCell(1), c3 = row.insertCell(2); - - c1.classList.add("ogn"); - c1.classList.add("r"); - - if (file.size > server_max_size) { - c1.innerHTML = "Too Large"; - c1.classList.add("err"); - } else - c1.innerHTML = ""; - - c2.classList.add("ogn"); - c2.classList.add("r"); - c2.innerHTML = humanize(file.size); - - c3.classList.add("ogn"); - c3.innerHTML = file.name; - - if (file.size > server_max_size) - return; - - fetch("upload/" + lws_urlencode(file.name), { - method: "POST", - body: formData - }) - .then((e) => { /* this just means we got a response code */ - var us = e.url.split("/"), ul = us[us.length - 1], n; - - for (n = 0; n < t.rows.length; n++) - if (ul === lws_urlencode( - t.rows[n].cells[2].textContent)) { - if (e.ok === true) { - t.deleteRow(n); - } else { - t.rows[n].cells[0].textContent = - "Failed " + san(e.status.toString()); - t.rows[n].cells[0]. - classList.add("err"); - } - break; - } - }) - .catch((e) => { - var us = e.url.split("/"), ul = us[us.length - 1], n; - - for (n = 0; n < t.rows.length; n++) - if (ul === lws_urlencode( - t.rows[n].cells[2].textContent)) { - t.rows[n].cells[0] = "FAIL"; - break; - } - }); - } - - function da_drop(e) { - var da = document.getElementById("da"); - - e.preventDefault(); - da.classList.remove("trot"); - - clear_errors(); - - ([...e.dataTransfer.files]).forEach(do_upload); - } - - function upl_button(e) { - var fi = document.getElementById("file"); - - clear_errors(); - e.preventDefault(); - - ([...fi.files]).forEach(do_upload); - } - - function body_drop(e) { - e.preventDefault(); - } - - function inp() { - var fi = document.getElementById("file"), - upl = document.getElementById("upl"); - console.log("inp"); - upl.disabled = !fi.files.length; - } - - function delfile(e) - { - e.stopPropagation(); - e.preventDefault(); - - ws.send("{\"del\":\"" + decodeURI(e.target.getAttribute("file")) + - "\"}"); - } - - function get_appropriate_ws_url(extra_url) - { - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; - } - - function new_ws(urlpath, protocol) - { - return new WebSocket(urlpath, protocol); - } - - document.addEventListener("DOMContentLoaded", function() { - var da = document.getElementById("da"), - fi = document.getElementById("file"), - upl = document.getElementById("upl"); - - da.addEventListener("dragenter", da_enter, false); - da.addEventListener("dragleave", da_leave, false); - da.addEventListener("dragover", da_over, false); - da.addEventListener("drop", da_drop, false); - - upl.addEventListener("click", upl_button, false); - fi.addEventListener("change", inp, false); - - window.addEventListener("dragover", body_drop, false); - window.addEventListener("drop", body_drop, false); - - ws = new_ws(get_appropriate_ws_url(""), "lws-deaddrop"); - try { - ws.onopen = function() { - var dd = document.getElementById("ddrop"), - da = document.getElementById("da"); - - dd.classList.remove("noconn"); - da.classList.remove("disa"); - }; - - ws.onmessage = function got_packet(msg) { - var j = JSON.parse(msg.data), s = "", n, - t = document.getElementById("dd-list"); - - server_max_size = j.max_size; - document.getElementById("size").innerHTML = - "Server maximum file size " + - humanize(j.max_size); - - s += ""; - for (n = 0; n < j.files.length; n++) { - var date = new Date(j.files[n].mtime * 1000); - s += ""; - } - s += "
" + - humanize(j.files[n].size) + - "" + - date.toDateString() + " " + - date.toLocaleTimeString() + - ""; - if (j.files[n].yours === 1) - s += ""; - else - s += " "; - - s += "" + - san(j.files[n].name) + "
"; - - t.innerHTML = s; - - for (n = 0; n < j.files.length; n++) { - var d = document.getElementById("d" + n); - if (d) - d.addEventListener("click", - delfile, false); - } - }; - - ws.onclose = function() { - var dd = document.getElementById("ddrop"), - da = document.getElementById("da"); - - dd.classList.add("noconn"); - da.classList.add("disa"); - }; - } catch(exception) { - alert("

Error " + exception); - } - - }); -}()); diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/drop.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/drop.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/drop.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/drop.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ - - - - - - - - -  
-

- - - -
-
-
-

...or select files to upload:

-
-
- - -
- -
-
-
-
- -
-
-
- - \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -# lws minimal http server deaddrop - -This demonstrates how you can leverage the lws deaddrop plugin to make a -secure, modern html5 file upload and sharing application. - -The demo is protected by basic auth credentials defined in the file at -./ba-passwords - by default the credentials are user: user1, password: password; -and user: user2, password: password again. - -You can upload files and have them appear on a shared, downloadable list that -is dynamically updated to all clients open on the page. Only the authenticated -uploader is able to delete the files he uploaded. - -Multiple simultaneous ongoing file uploads are supported. - -## build - -To build this standalone, you must tell cmake where the lws source tree -./plugins directory can be found, since it relies on including the source -of the raw-proxy plugin. - -``` - $ cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-deaddrop -[2018/12/01 10:31:09:7108] USER: LWS minimal http server deaddrop | visit https://localhost:7681 -[2018/12/01 10:31:09:8511] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/12/01 10:31:09:8522] NOTICE: Using SSL mode -[2018/12/01 10:31:10:0755] NOTICE: SSL ECDH curve 'prime256v1' -[2018/12/01 10:31:10:2562] NOTICE: lws_tls_client_create_vhost_context: doing cert filepath localhost-100y.cert -[2018/12/01 10:31:10:2581] NOTICE: Loaded client cert localhost-100y.cert -[2018/12/01 10:31:10:2583] NOTICE: lws_tls_client_create_vhost_context: doing private key filepath -[2018/12/01 10:31:10:2593] NOTICE: Loaded client cert private key localhost-100y.key -[2018/12/01 10:31:10:2596] NOTICE: created client ssl context for default -[2018/12/01 10:31:10:5290] NOTICE: deaddrop: vh default, upload dir ./uploads, max size 10000000 -[2018/12/01 10:31:10:5376] NOTICE: vhost default: cert expiry: 730203d -... -``` - -Visit https://localhost:7681, and follow the link there to the secret area. - -Give your browser "user1" and "password" as the credentials. For testing to -confirm what a different user sees, you can also log in as "user2" and -"password". - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/uploads/user1/placeholder.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/uploads/user1/placeholder.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-deaddrop/uploads/user1/placeholder.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-deaddrop/uploads/user1/placeholder.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -git doesn't support empty dirs... diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-http-server-dynamic) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-dynamic) -set(SRCS minimal-http-server-dynamic.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/minimal-http-server-dynamic.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/minimal-http-server-dynamic.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/minimal-http-server-dynamic.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/minimal-http-server-dynamic.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,303 +0,0 @@ -/* - * lws-minimal-http-server-dynamic - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http server that can produce dynamic http - * content as well as static content. - * - * To keep it simple, it serves the static stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * - * You can change that by changing mount.origin below. - */ - -#include -#include -#include -#include - -/* - * Unlike ws, http is a stateless protocol. This pss only exists for the - * duration of a single http transaction. With http/1.1 keep-alive and http/2, - * that is unrelated to (shorter than) the lifetime of the network connection. - */ -struct pss { - char path[128]; - - int times; - int budget; - - int content_lines; -}; - -static int interrupted; - -static int -callback_dynamic_http(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct pss *pss = (struct pss *)user; - uint8_t buf[LWS_PRE + 2048], *start = &buf[LWS_PRE], *p = start, - *end = &buf[sizeof(buf) - LWS_PRE - 1]; - time_t t; - int n; - - switch (reason) { - case LWS_CALLBACK_HTTP: - - /* in contains the url part after our mountpoint /dyn, if any */ - lws_snprintf(pss->path, sizeof(pss->path), "%s", (const char *)in); - - lws_get_peer_simple(wsi, (char *)buf, sizeof(buf)); - lwsl_notice("%s: HTTP: connection %s\n", __func__, (const char *)buf); - - /* - * prepare and write http headers... with regards to content- - * length, there are three approaches: - * - * - http/1.0 or connection:close: no need, but no pipelining - * - http/1.1 or connected:keep-alive - * (keep-alive is default for 1.1): content-length required - * - http/2: no need, LWS_WRITE_HTTP_FINAL closes the stream - * - * giving the api below LWS_ILLEGAL_HTTP_CONTENT_LEN instead of - * a content length forces the connection response headers to - * send back "connection: close", disabling keep-alive. - * - * If you know the final content-length, it's always OK to give - * it and keep-alive can work then if otherwise possible. But - * often you don't know it and avoiding having to compute it - * at header-time makes life easier at the server. - */ - if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, - "text/html", - LWS_ILLEGAL_HTTP_CONTENT_LEN, /* no content len */ - &p, end)) - return 1; - if (lws_finalize_write_http_header(wsi, start, &p, end)) - return 1; - - pss->times = 0; - pss->budget = atoi((char *)in + 1); - pss->content_lines = 0; - if (!pss->budget) - pss->budget = 10; - - /* write the body separately */ - lws_callback_on_writable(wsi); - - return 0; - - case LWS_CALLBACK_HTTP_WRITEABLE: - - if (!pss || pss->times > pss->budget) - break; - - /* - * We send a large reply in pieces of around 2KB each. - * - * For http/1, it's possible to send a large buffer at once, - * but lws will malloc() up a temp buffer to hold any data - * that the kernel didn't accept in one go. This is expensive - * in memory and cpu, so it's better to stage the creation of - * the data to be sent each time. - * - * For http/2, large data frames would block the whole - * connection, not just the stream and are not allowed. Lws - * will call back on writable when the stream both has transmit - * credit and the round-robin fair access for sibling streams - * allows it. - * - * For http/2, we must send the last part with - * LWS_WRITE_HTTP_FINAL to close the stream representing - * this transaction. - */ - n = LWS_WRITE_HTTP; - if (pss->times == pss->budget) - n = LWS_WRITE_HTTP_FINAL; - - if (!pss->times) { - /* - * the first time, we print some html title - */ - t = time(NULL); - /* - * to work with http/2, we must take care about LWS_PRE - * valid behind the buffer we will send. - */ - p += lws_snprintf((char *)p, end - p, "" - "" - "" - "
Dynamic content for '%s' from mountpoint." - "
Time: %s

" - "", pss->path, ctime(&t)); - } else { - /* - * after the first time, we create bulk content. - * - * Again we take care about LWS_PRE valid behind the - * buffer we will send. - */ - - while (lws_ptr_diff(end, p) > 80) - p += lws_snprintf((char *)p, end - p, - "%d.%d: this is some content... ", - pss->times, pss->content_lines++); - - p += lws_snprintf((char *)p, end - p, "

"); - } - - pss->times++; - if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff(p, start), n) != - lws_ptr_diff(p, start)) - return 1; - - /* - * HTTP/1.0 no keepalive: close network connection - * HTTP/1.1 or HTTP1.0 + KA: wait / process next transaction - * HTTP/2: stream ended, parent connection remains up - */ - if (n == LWS_WRITE_HTTP_FINAL) { - if (lws_http_transaction_completed(wsi)) - return -1; - } else - lws_callback_on_writable(wsi); - - return 0; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocol = - { "http", callback_dynamic_http, sizeof(struct pss), 0 }; - -static const struct lws_protocols *pprotocols[] = { &protocol, NULL }; - -/* override the default mount for /dyn in the URL space */ - -static const struct lws_http_mount mount_dyn = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/dyn", /* mountpoint URL */ - /* .origin */ NULL, /* protocol */ - /* .def */ NULL, - /* .protocol */ "http", - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_CALLBACK, /* dynamic */ - /* .mountpoint_len */ 4, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -/* default mount serves the URL space from ./mount-origin */ - -static const struct lws_http_mount mount = { - /* .mount_next */ &mount_dyn, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server dynamic | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - /* for testing ah queue, not useful in real world */ - if (lws_cmdline_option(argc, argv, "--ah1")) - info.max_http_header_pool = 1; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* http on 7681 */ - - info.port = 7681; - info.pprotocols = pprotocols; - info.mounts = &mount; - info.vhost_name = "http"; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("Failed to create tls vhost\n"); - goto bail; - } - - /* https on 7682 */ - - info.port = 7682; - info.error_document_404 = "/404.html"; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - info.vhost_name = "localhost"; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("Failed to create tls vhost\n"); - goto bail; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail: - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ - - - - - - -
- - Hello from the minimal http server dynamic content example. -

- This is a static page served from ./mount-origin/index.html. -

- Stuff down /dyn in the URL space is generated dynamically
- by the callback. For example, click on - /dyn/anything to - see dynamic content. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-dynamic/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-dynamic/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -# lws minimal http server dynamic content - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-dynamic -[2018/03/20 10:24:24:7099] USER: LWS minimal http server dynamic | visit http://localhost:7681 -[2018/03/20 10:24:24:7099] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -``` - -Visit http://localhost:7681, which is all static content. - -Click on the link to /dyn/anything, this opens a new tab with dynamicly-produced content. - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-http-server-eventlib) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-eventlib) -set(SRCS minimal-http-server-eventlib.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,122 +0,0 @@ -/* - * lws-minimal-http-server-eventlib - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http[s] server that can work with any of the - * supported event loop backends, or the default poll() one. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -static struct lws_context *context; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void signal_cb(void *handle, int signum) -{ - switch (signum) { - case SIGTERM: - case SIGINT: - break; - default: - lwsl_err("%s: signal %d\n", __func__, signum); - break; - } - lws_context_destroy(context); -} - -void sigint_handler(int sig) -{ - signal_cb(NULL, sig); -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - const char *p; - int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server eventlib | visit http://localhost:7681\n"); - lwsl_user(" [-s (ssl)] [--uv (libuv)] [--ev (libev)] [--event (libevent)]\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.pcontext = &context; - info.signal_cb = signal_cb; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - if (lws_cmdline_option(argc, argv, "-s")) { - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - if (lws_cmdline_option(argc, argv, "--uv")) - info.options |= LWS_SERVER_OPTION_LIBUV; - else - if (lws_cmdline_option(argc, argv, "--event")) - info.options |= LWS_SERVER_OPTION_LIBEVENT; - else - if (lws_cmdline_option(argc, argv, "--ev")) - info.options |= LWS_SERVER_OPTION_LIBEV; - else - if (lws_cmdline_option(argc, argv, "--glib")) - info.options |= LWS_SERVER_OPTION_GLIB; - else - signal(SIGINT, sigint_handler); - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (!lws_service(context, 0)) - ; - - lwsl_info("calling external context destroy\n"); - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ - - - - - - -
- - Hello from the minimal http server event loop example. -
- You can confirm the 404 page handler by going to this - nonexistant page. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -# lws minimal http server eventlib - -This demonstrates a minimal http server that can use any of the event libraries - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 ---uv|Use the libuv event library (lws must have been configured with `-DLWS_WITH_LIBUV=1`) ---event|Use the libevent library (lws must have been configured with `-DLWS_WITH_LIBEVENT=1`) ---ev|Use the libev event library (lws must have been configured with `-DLWS_WITH_LIBEV=1`) - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-eventlib -[2018/03/04 09:30:02:7986] USER: LWS minimal http server-eventlib | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-http-server-eventlib-demos) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-eventlib-demos) -set(SRCS minimal-http-server-eventlib-demos.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/minimal-http-server-eventlib-demos.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/minimal-http-server-eventlib-demos.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/minimal-http-server-eventlib-demos.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/minimal-http-server-eventlib-demos.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,191 +0,0 @@ -/* - * lws-minimal-http-server-eventlib - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http[s] server that can work with any of the - * supported event loop backends, or the default poll() one. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "../../../plugins/protocol_lws_mirror.c" -#include "../../../plugins/protocol_lws_status.c" -#include "../../../plugins/protocol_dumb_increment.c" -#include "../../../plugins/protocol_post_demo.c" - -static struct lws_context *context; - -static struct lws_protocols protocols[] = { - /* first protocol must always be HTTP handler */ - - { "http-only", lws_callback_http_dummy, 0, 0, }, - LWS_PLUGIN_PROTOCOL_DUMB_INCREMENT, - LWS_PLUGIN_PROTOCOL_MIRROR, - LWS_PLUGIN_PROTOCOL_LWS_STATUS, - LWS_PLUGIN_PROTOCOL_POST_DEMO, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -/* - * mount handlers for sections of the URL space - */ - -static const struct lws_http_mount mount_ziptest = { - NULL, /* linked-list pointer to next*/ - "/ziptest", /* mountpoint in URL namespace on this vhost */ - "candide.zip", /* handler */ - NULL, /* default filename if none given */ - NULL, - NULL, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - LWSMPRO_FILE, /* origin points to a callback */ - 8, /* strlen("/ziptest"), ie length of the mountpoint */ - NULL, - - { NULL, NULL } // sentinel -}; - -static const struct lws_http_mount mount_post = { - (struct lws_http_mount *)&mount_ziptest, /* linked-list pointer to next*/ - "/formtest", /* mountpoint in URL namespace on this vhost */ - "protocol-post-demo", /* handler */ - NULL, /* default filename if none given */ - NULL, - NULL, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - LWSMPRO_CALLBACK, /* origin points to a callback */ - 9, /* strlen("/formtest"), ie length of the mountpoint */ - NULL, - - { NULL, NULL } // sentinel -}; - - -static const struct lws_http_mount mount = { - /* .mount_next */ &mount_post, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "test.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void signal_cb(void *handle, int signum) -{ - lwsl_err("%s: signal %d\n", __func__, signum); - - switch (signum) { - case SIGTERM: - case SIGINT: - break; - default: - - break; - } - lws_context_destroy(context); -} - -void sigint_handler(int sig) -{ - signal_cb(NULL, sig); -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - const char *p; - int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server eventlib | visit http://localhost:7681\n"); - lwsl_user(" [-s (ssl)] [--uv (libuv)] [--ev (libev)] [--event (libevent)]\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.pcontext = &context; - info.protocols = protocols; - info.signal_cb = signal_cb; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - if (lws_cmdline_option(argc, argv, "-s")) { - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - if (lws_cmdline_option(argc, argv, "--uv")) - info.options |= LWS_SERVER_OPTION_LIBUV; - else - if (lws_cmdline_option(argc, argv, "--event")) - info.options |= LWS_SERVER_OPTION_LIBEVENT; - else - if (lws_cmdline_option(argc, argv, "--ev")) - info.options |= LWS_SERVER_OPTION_LIBEV; - else - if (lws_cmdline_option(argc, argv, "--glib")) - info.options |= LWS_SERVER_OPTION_GLIB; - else - signal(SIGINT, sigint_handler); - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (!lws_service(context, 0)) - ; - - lwsl_info("calling external context destroy\n"); - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/candide.zip and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/candide.zip differ Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/favicon.ico differ Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/http2.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/http2.png differ Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/leaf.jpg and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/leaf.jpg differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/lws-common.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/lws-common.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/lws-common.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/lws-common.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,125 +0,0 @@ -/* - * This section around grayOut came from here: - * http://www.codingforums.com/archive/index.php/t-151720.html - * Assumed public domain - * - * Init like this in your main html script, this also reapplies the gray - * - * lws_gray_out(true,{'zindex':'499'}); - * - * To remove the gray - * - * lws_gray_out(false); - * - */ - -function gsize(ptype) -{ - var h = document.compatMode === "CSS1Compat" && - !window.opera ? - document.documentElement.clientHeight : - document.body.clientHeight; - var w = document.compatMode === "CSS1Compat" && - !window.opera ? - document.documentElement.clientWidth : - document.body.clientWidth; - var pageWidth, pageHeight, t; - - if (document.body && - (document.body.scrollWidth || document.body.scrollHeight)) { - t = document.body.scrollWidth; - pageWidth = (w > t) ? ("" + w + "px") : ("" + (t) + "px"); - t = document.body.scrollHeight; - pageHeight = (h > t) ? ("" + h + "px") : ("" + (t) + "px"); - } else if (document.body.offsetWidth) { - t = document.body.offsetWidth; - pageWidth = (w > t) ? ("" + w + "px") : ("" + (t) + "px"); - t = document.body.offsetHeight; - pageHeight =(h > t) ? ("" + h + "px") : ("" + (t) + "px"); - } else { - pageWidth = "100%"; - pageHeight = "100%"; - } - return (ptype === 1) ? pageWidth : pageHeight; -} - -function addEvent( obj, type, fn ) { - if ( obj.attachEvent ) { - obj["e" + type + fn] = fn; - obj[type+fn] = function() { obj["e" + type + fn]( window.event );}; - obj.attachEvent("on" + type, obj[type + fn]); - } else - obj.addEventListener(type, fn, false); -} - -function removeEvent( obj, type, fn ) { - if ( obj.detachEvent ) { - obj.detachEvent("on" + type, obj[type + fn]); - obj[type + fn] = null; - } else - obj.removeEventListener(type, fn, false); -} - -function lws_gray_out(vis, _options) { - - var options = _options || {}; - var zindex = options.zindex || 50; - var opacity = options.opacity || 70; - var opaque = (opacity / 100); - var bgcolor = options.bgcolor || "#000000"; - var dark = document.getElementById("darkenScreenObject"); - - if (!dark) { - var tbody = document.getElementsByTagName("body")[0]; - var tnode = document.createElement("div"); - tnode.style.position = "absolute"; - tnode.style.top = "0px"; - tnode.style.left = "0px"; - tnode.style.overflow = "hidden"; - tnode.style.display ="none"; - tnode.id = "darkenScreenObject"; - tbody.appendChild(tnode); - dark = document.getElementById("darkenScreenObject"); - } - if (vis) { - dark.style.opacity = opaque; - dark.style.MozOpacity = opaque; - // dark.style.filter ='alpha(opacity='+opacity+')'; - dark.style.zIndex = zindex; - dark.style.backgroundColor = bgcolor; - dark.style.width = gsize(1); - dark.style.height = gsize(0); - dark.style.display = "block"; - addEvent(window, "resize", - function() { - dark.style.height = gsize(0); - dark.style.width = gsize(1); - } - ); - } else { - dark.style.display = "none"; - removeEvent(window, "resize", - function() { - dark.style.height = gsize(0); - dark.style.width = gsize(1); - } - ); - } -} - -/* - * end of grayOut related stuff - */ - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -function lws_san(s) -{ - if (s.search("<") !== -1) - return "invalid string"; - - return s; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.css libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.css --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.css 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,190 +0,0 @@ - -span.title { - font-size:18pt; - font-family: Arial; - font-weight:normal; - text-align:center; - color:#000000; -} -span.mount { - font-size:10pt; - font-family: Arial; - font-weight:normal; - text-align:center; - color:#000000; -} -span.mountname { - font-size:14pt; - font-family: Arial; - font-weight:bold; - text-align:center; - color:#404010; -} -span.n { - font-size:12pt; - font-family: Arial; - font-weight:normal; - text-align:center; - color:#808020; -} -span.v { - font-size:12pt; - font-family: Arial; - font-weight:bold; - text-align:center; - color:#202020; -} -span.m1 { - font-size:12pt; - font-family: Arial; - font-weight:bold; - text-align:center; - color:#202020; -} -span.m2 { - font-size:12pt; - font-family: Arial; - font-weight:normal; - text-align:center; - color:#202020; -} - -.browser { font-size:12pt; font-family: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; border-radius:10px;} -.group2 { vertical-align:middle; - text-align:center; - background:#f0f0e0; - padding:12px; - -webkit-border-radius:10px; - border-radius:10px; } -.explain { vertical-align:middle; - text-align:center; - background:#f0f0c0; padding:12px; - -webkit-border-radius:10px; - border-radius:10px; - color:#404000; - padding:3px; -} -td.wsstatus { vertical-align:middle; width:200px; height:50px; - text-align:center; - background:#f0f0c0; padding:6px; - -webkit-border-radius:8px; - border-radius:8px; - color:#404000; } -.tdform { vertical-align:middle; width:200px; height:50px; - text-align:center; - background:#f0f0d0; padding:6px; - -webkit-border-radius:8px; - margin:10px; - border-radius:8px; - border: 1px solid black; - border-collapse: collapse;font-size:18pt; font-family: Arial; font-weight:normal; text-align:center; color:#000000; - color:#404000; } - -td.l { vertical-align:middle; - text-align:center; - background:#d0d0b0; - padding:3px; - -webkit-border-radius:3px; - border-radius:3px; } - -td.bigger { font-size:120%; } - -div.bgw { background:white } -div.conninfo { - border: solid 2px #e0d040; - padding: 4px; - width: 500px; - height:350px; - overflow: auto; -} -span.f12 { font-size:12pt } - -.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; border-radius:10px; } -.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; border-radius:10px; } -.tabs { - position: relative; - min-height: 750px; /* This part sucks */ - clear: both; - margin: 25px 0; -} -.tab { - float: left; -} -.tab label { - background: #eee; - padding: 10px; - border: 1px solid #ccc; - margin-left: -1px; - position: relative; - left: 1px; -} -.tab [type=radio] { - display: none; -} -.content { - position: absolute; - top: 28px; - left: 0; - background: white; - right: 0; - bottom: 0; - padding: 20px; - border: 1px solid #ccc; -} -[type=radio]:checked ~ label { - background: white; - border-bottom: 1px solid white; - z-index: 2; -} -[type=radio]:checked ~ label ~ .content { - z-index: 1; -} - - td.wsstatus { vertical-align:middle; width:200px; height:50px; - text-align:center; - background:#f0f0c0; padding:6px; - -webkit-border-radius:8px; - border-radius:8px; - color:#404000; } - td.l { vertical-align:middle; - text-align:center; - background:#d0d0b0; - padding:3px; - -webkit-border-radius:3px; - border-radius:3px; } - td.dl { vertical-align:middle; - text-align:center; - background:#c0c0c0; - padding:3px; - -webkit-border-radius:3px; - border-radius:3px; } - td.c { vertical-align:middle; - text-align:center; - background:#c0c0a0; - padding:3px; - -webkit-border-radius:3px; - border-radius:3px; } - td.c0 { vertical-align:middle; - text-align:center; - background:#b0b090; - padding:3px; - -webkit-border-radius:3px; - border-radius:3px; } - td.dc0 { vertical-align:middle; - text-align:center; - background:#a0a0a0; - padding:3px; - -webkit-border-radius:3px; - border-radius:3px; } - td.c1 { vertical-align:middle; - text-align:center; - background:#c0c0c0; - padding:3px; - -webkit-border-radius:3px; - border-radius:3px; } - td.t { vertical-align:middle; - text-align:center; - background:#e0e0c0; - padding:3px; - -webkit-border-radius:3px; - border-radius:3px; } \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,261 +0,0 @@ - - - - - - - - Minimal Websocket test app - - - -
-
- - - -
- - - - - - - - - - -
- - - -
-
...
-
-
-Click Here to -have the test server send a big picture by http. -
-
- -
- - - -
-
- - - - - - - - - - - - -
- Websocket connection not initialized - dumb increment-protocol
-The incrementing number is coming from the server at 20Hz and is individual for -each connection to the server... try opening a second browser window. -

-The button sends a message over the websocket link to ask the server -to zero just this connection's number. -
- - -
-
-
-
- -
- - - -
-
- - - - - - - - - - - - - -
- Websocket connection not initialized - - lws-mirror-protocol -
-
-Use the mouse to draw on the canvas below -- all other browser windows open -on this page see your drawing in realtime and you can see any of theirs as -well. -

-The lws-mirror protocol doesn't interpret what is being sent to it, it just -re-sends it to every other websocket it has a connection with using that -protocol, including the guy who sent the packet. -

-libwebsockets-test-client joins in by spamming circles on to this -shared canvas when run. -
-
Drawing color: - -
-
-
-
-
-
- -
- - - -
-
- - - - - - - - - - - - - - -
- -
- Websocket connection not initialized - Open and close testing -
-To help with open and close testing, you can open and close a connection by -hand using the buttons.
- "Close" closes the connection from the browser with code 3000 - and reason 'Bye!".
- "Request Server Close" sends a message asking the server to -initiate the close, which it does with code 1001 and reason "Seeya". -
- - -
- -
-
-
- -
- - - -
-
- - - - - - - - - - - - -
-
Websocket connection not initialized
-
- Server Info - - -
-This information is sent by the server over a ws[s] link and updated live -whenever the information changes server-side. -
-
-
-
- -
- - - -
-
- - - - - - - - - - - - - - -
-POST Form testing -
-This tests POST handling in lws. -
- FORM 1: send with urlencoded POST body args
-
- Some text: -
- -
-
- FORM 2: send with multipart/form-data
- (can handle file upload, test limited to 100KB)
-
- Some text: - -
-   -
- -
-
-
-
-
- -
-
- -Looking for support? -https://libwebsockets.org, - - https://github.com/warmcat/libwebsockets
-Join the mailing list: - - https://libwebsockets.org/mailman/listinfo/libwebsockets - -
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/test.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,543 +0,0 @@ -(function () { -function check_file() -{ - var f = document.getElementById("file").files[0]; - var max_len = 100000; - var dis = 0; - - if (f) { - if (f.size >= max_len) { - dis = 1; - document.getElementById("file_info").innerHTML = - "File larger than " + - max_len+""; - } else - document.getElementById("file_info").innerHTML = - "File length "+f.size; - } else - dis = 1; - - document.getElementById("upload").disabled = dis; -} - -/* - * We display untrusted stuff in html context... reject anything - * that has HTML stuff in it - */ - -function san(s) -{ - if (s.search("<") !== -1) - return "invalid string"; - - return s; -} - -/* BrowserDetect came from http://www.quirksmode.org/js/detect.html */ - -var BrowserDetect = { - init: function () { - this.browser = this.searchString(this.dataBrowser) || - "An unknown browser"; - this.version = this.searchVersion(navigator.userAgent) - || this.searchVersion(navigator.appVersion) - || "an unknown version"; - this.OS = this.searchString(this.dataOS) || "an unknown OS"; - }, - searchString: function (data) { - for (var i=0;iwebsocket connection opened
" + - san(socket_di.extensions); - }; - - socket_di.onmessage =function got_packet(msg) { - document.getElementById("number").textContent = msg.data + "\n"; - }; - - socket_di.onclose = function(){ - document.getElementById("wsdi_statustd").style.backgroundColor = - "#ff4040"; - document.getElementById("wsdi_status").textContent = - " websocket connection CLOSED "; - }; - } catch(exception) { - alert("

Error" + exception); - } -} - - var socket_status, jso, s; - -function ws_open_status() -{ - - socket_status = new_ws(get_appropriate_ws_url(""), "lws-status"); - - try { - socket_status.onopen = function() { - document.getElementById("s_statustd").style.backgroundColor = - "#40ff40"; - document.getElementById("s_status").innerHTML = - " websocket connection opened
" + - san(socket_status.extensions); - }; - - socket_status.onmessage =function got_packet(msg) { - var s; - - console.log(msg.data); - - jso = JSON.parse(msg.data); - - if (jso.wss_over_h2 === "1") - document.getElementById("wstransport").innerHTML = - ""; - - document.getElementById("servinfo").innerHTML = - "" + - "" + - "
Build info"+ - san(jso.version) + "
Server info" + - san(jso.hostname) + "
"; - s=""; - var n; - for (n = 0; n < jso.conns.length; n++) { - var d = new Date(parseInt(jso.conns[n].time, 10) * 1000); - - s = s + ""; - } - s = s + "
client " + (n + 1) + - "" + san(jso.conns[n].peer) + - "
" + san(d.toString()) + - "
" + san(jso.conns[n].ua) + - "
"; - - document.getElementById("conninfo").innerHTML = s; - }; - - socket_status.onclose = function(){ - document.getElementById("s_statustd").style.backgroundColor = - "#ff4040"; - document.getElementById("s_status").textContent = - " websocket connection CLOSED "; - }; - } catch(exception) { - alert("

Error" + exception); - } -} - -function reset() { - socket_di.send("reset\n"); -} - - -function junk() { - for(var word = ""; word.length < 9000; word += "a"){} - socket_di.send(word); -} - -function on_pmd() { - socket_status.send("{ \"RequestType\":\"DDoS\", \"blob\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAJbElEQVR4Xu2af4xUVxXHv+e9O0OhIEibgrRlF8rOG6CWKiumrUlTo6byj6mmNFta2DdQsRJJrP5j+0cnjSVp1BoSU1yzO28LEeL6h7GJNhpJNa2CBQ1gd9k3QPlRBU2LLV0W2p173zFvZvbtzOybmbd7J9DEd/+befece97nfe+95/4gxEWLAGlZx8aIAWqKIAYYA9QkoGkeKzAGqElA0zxWYAxQk4CmeazAGKAmAU3zWIExQE0CmuaxAmOAmgQ0zWMFxgA1CWiaxwqMAWoS0DRvrQJ3ujeb17Hrx6SuzLwV32x7VzO+1pj3HJprJue8BcBQCSONRzr+2RrHaO2OtHDc1wDcUwqOj0s7nWpVoDp+hOMeA5Au+zggbesuHX+Vti1VoOh3j4BxRxEf8xmVSbe3KlAdP2bOPUmEpaXviiGZsVbq+IsBxgCnpp9YgVPjNan2RwagmXMfUpnUAEAc9k6Rx8AHB0zzy3euU5nUXk02RXMzl+9Ssw8PYN06FeYvOkAmM5dfpzLWL6LGFXkSMR13jIAEmKXMpBM6AIXjeih+BVLqTEcSWfJ/T71ks4bZ9vAYgU1/epC2ZegAFLnhAogEM0tV5x1r/UcGKBw3UJ1sTyVwH8laZ5EU2DO4WCTFmXFbOTbnemxZdHnq9AAMvDVTjF4ObKVS7di8IvA97jOSArNsiLZ8oGBpW5HYRKrkB1IFcGwkiS2dhWkB7B1qE6Z5uiUAew7NEsk5o4EveEtgLw98TwngwIApRlcFoogBVnzdSArUAWg6+T4QS3U69Xi98eiqK7A4vnX1+YsldWbPJmSzk8fJa6FAP67FXTv9sVLZqU1k9rvHibGsmKQzBlTGeih0gqgcA69CFxaO+zKA+4uxEF6W3dbaSXFdA4Bmzt1DhK5SLHycTMd9n4A5pT9oSNqp0GXO1VagcFx/cphZhnZF2tasjwJAkRs+CqJPFvEBIzHA8a8ScQyMAdbbTGgJwAY7FS3LA3edXCyUbJoHitzwKIhK3Zb5ssykr5/Uha9BHigc9whQ3nHyu7DIDe8A0TY/OI/xtJexngnP5ocLRCRAkLLbas1KhOGps6lE2MxvOO6TBvBsMS7Pe8rbtHz7pLhavRLpdwtgCDCkzIS/o9Gff8pg/n55EtlRSqT7h+8B0xhs62AYvHJlf524QZ3duzs0pfCT7aj7gQNsmqPHH1V2qr9+ewBePHYXIICNHfsb1TNzw7Y6e343svdNWh35dpHyQL9iKUV5VGVSu+qt94tx7DrZCa8wA93pP0deiTR80fLDyACjOGthncgAp9FmqwG+CsbnynHkpW1Z04ip5Sai3x0CY3kpU+P9sjt9d6saaSlA9JybJWaMXARDyRFjLrZ1fNiqQLX8DAwmxai4CCAhzblzsWFhsH7W8lvM8eOiRSAGqIUvVqAmvhhgDFCbgKaDqY+B2ayBBV2fQJJnwzQlSL6HjcsvhMaRHUwG/2dXjjWMtXdwPjBjPryCARKXIcQ7sJd8EOn9fFuTbkCBCDOMUZw69596SXWVv4HB2bicXAClBARdwqm95+stEurFER1gz6GESM45DGBFmLNJW+C5E7cKUmfH68pkoh3rl046r8CL7s3CQ+hdFQY2KNvaXS94o8/\" }"); - socket_status.send("{ \"RequestType\":\"SendImage\", \"RequestID\":\"283463389\", \"toType\":\"toUser\", \"toID\":\"1036\", \"fileType\":\"image/jpeg\", \"blob\":\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAGqAoADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArlviX43Pw78H3fipdHl1RreSGJLSKQRtK0kqxgBiDjlvSuprhvjNpmoav4INnpljPdznU9Ok8qCMu2xbuJmbA5wFBJPYA0AL4r+K2j+G/hxH8Rra0k1G3ureOezto3CPOXXcFBwcHbk9O1bmg+L9H12wW7juEimSygvrmAtlreOVCy7jjngN+VeOal4L8Vz3Wv8Ag99DvG0Hw5b317pEixMUupLpS0UUf94w5ZMD1FWdLfW/CFxqcF34S126l1vwxpttZfZdPllU3EUMitFIyqREcuOXIHXninYD0y8+KPgCwNkt34ntU/tCGO4tyA7BopPuOSBhQc8bsVYXxtpEc+tHULm2tbPRVheW5afIKyAkEjHy9OOTnNeEHQNX0bw/pTQaH4l0/W38NabbNCNFkvbS/kjgUeTMqrmF1bKncVx1rZ1Lwz4tuV1bVL3wpdSJDf8Ah+/urCGBmFzFAxaeOIf8tNv90ZzgDvRYD2fw34w8NeLoppvDurRXqwNslChlZD1GVYAj8q0rq9tLIRtd3CRCVxGhc4BY9Bn3rlfBl/Z61reqazp/g660q3kjhiW8u7SS0muyoOR5UiqwVc4DEc89q2fFQSTRprVtFk1VrjESWyjhmPQs38AHXdkYx60gNivPb74uwWXhPW/EX/CPzyXmjal/ZQ05Zh5k85ZFjCtjjcZFxxXXeGdO1LSdDtNP1bUPtt1CmJJvXngZ74HGcDOM4FeYXfhPXm+Nq28elXDeG7yaHXbi68s+SLmGORRGW6bi7RNjr8hoA6XU/i3p1pYeHr+w043a+INPk1NMziMQwIiNuY7T/fA/OtS8+J3gnSntLbWtftbO6u4IpxESzhA4BG5gMKPc4ryPSvBHipIvGdheaDei20HT7jRtB/ct/pMLzSSK0Yx8w8t4k47oaj8XaLqVglzJpmjeIbfV7rRLSAWw0WS/sdUdIiBFJsX9yQSVJZhgHPanYD1B/jD4PsfEWseHNdvo9Nm0q7itUeRiwn8yGOQN8q/IP3m3k9R1ra8ceLoPBfgzVfGTWpvYdMs3vPKSQL5qqucBsHGfXFebDwpq15YfE681LwvIuoaotuLdRbl/NK2EAKxHHzgSBxx3Bre+IukavqHwA1TRbPTbq41GXw75C2scTNM0vkAbAgGS2eMYzmgDt9c1xNF0CfXXiRlhjWQo8mwckDlsHHX0rMf4leCYNUg0O78QW0OoT+WogO4gO+Nql8bQTkYBIrjfFvjJfFvgjUfDeleE/Fq3s1qgQXHh68gQlXTI3PGBnr3rjdc0rWtM1K8Ph3QdeXVbq6tJW0250iS50/UGUIPN89V2wEYOSzfKVziiwHsPhL4k+GfGer6zoujXLvcaLcfZ5gyMA3yg7gSOmTj8K6qvPfh9aT6V418b2d3ot5bG81NL22uDZusE0Jt4l+WXGwkMrZUHI9K9CpAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUVma34m8OeGvsf/CQ67YaZ/aNyllafa7hIvtFw/3Yk3EbnPZRyaANOiisbQfGfhHxTc39l4a8T6XqtxpUohvorO7jme1kOcLIFJKHg8HHQ0AbNFZ2u+ItA8MWa6j4j1qx0u1eVIFmvLhYUMjnCoGYgbieg6mi08R+H7/VrnQbLW7G41KzjSa4s47hWmhjb7rOgOVB7EjmgDRoorO1/wAQ6D4V0uXXPE2tWOladAVEt3ezrDChZgq5diAMkgDnqRQBo0UgORkUtABRVbUtT07RrCfVNWvreys7VDJPcTyCOONB1ZmPAHuaNO1LT9XsYNT0q9gvLO5QSQzwSB45EPRlYcEe4oAs0VT1fWNJ8P6Zcazrup2un2FonmT3V1KsUUS+rMxAA9zU9tc297bRXlnPHPBOiyxSxsGV0YZDAjggggg0AS0UUmRnGeaAFooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooqrqGpWumQ+dcsQCcKBySaaTk7ITairstUVz3/AAmdh/z63H/jv+NH/CZ2H/Prcf8Ajv8AjW31ar/KZfWKfc6Giue/4TOw/wCfW4/8d/xo/wCEzsP+fW4/8d/xo+rVf5Q+sU+50NFc9/wmdh/z63H/AI7/AI0f8JnYf8+tx/47/jR9Wq/yh9Yp9zoaK57/AITOw/59bj/x3/GtDTNcstVLJBuWRRkow5xUyoVIK8kVGtCTsmaNFFFZGgUUUUAFFFFABXin7YfhS78TfAbxBeaUCup+HVj16ykUZaOS1cSkr7lVYfjXtdVtRsLXVLC50y+hWW3u4nhljboyMMEH8DQBxNz8UNOb4IH4tQyrHaz6AurREH7peEMg/wC+mAr52/Zu8OXnwh+KfhZtT3xn4seHLi9u1JO3+0Y5DcA/UxMwA9qxNG1PUX+COl/s0vcNJrFt46m8ITg9Xs7aYzE/TyvK9sGvaf2ptPTwl4J8LfEvS4WB+HGuWGolYx8xsg4imT6eU7E/SgBn7Qif8Jp8Ufhb8KU/eQTanJ4h1KP0gtgBG3/fbH8q6zw7rXw8j+M/jO3stIltPENlpdrNq2oSSnypbfLbAATgbcEk4rmPh40fj39pfx146jdZ7DwtYWnhrT5lOV8woJ7gA/70gH4Vm6DDpFx+0b8XLfX7mO30yXwzZpeTSSCNY4T5gdix4UAZ57UAbEH7S2r67YyeJfAvwY8TeIfDCFzHq0UkcX2iNTgyRRN8zrwcHIzXK/tQ+P8Aw78Sf2QNf8X+Grh3sriWzVllXZJDIt5EHjcdmB6irvg3wv8AtE/BnwxZ6B4BPhj4ieEbGEDSkkujZXv2Xqi7yDG+FxghuRiuE+Mvivwf4x/Y78djw34Tl8MXVtq8EOs6TKuJLe+a7hMhPJB3cEEcGgD2LU/2htUjt5tY8J/CHxL4j8O2u7fq9u0cccqL96SFG+aROCQeMjpXo/gHx34f+JPhSx8Y+GLhpbC/Qsu9drowOGRh2YHgitXTNNsdM0q10mxtkitLaBIIoVGFWNVwFA9McV4l+xt8vw28QQrwkPjDV0Reyr5q8D86AO2/aF/5Ij40/wCwRN/KpPgF/wAkX8G/9giD/wBBqP8AaF/5Ij40/wCwRN/KpPgF/wAkX8G/9giD/wBBoAn+Nt74T074UeJb7xzpcupaDDZFr+0ico8se5flBBBHOO9Y3iH4w+Gfh/4c8Jabo3h/UtV1HxDZxLoWh2QDTyRLCG5ZjhVVcZY1D+1X/wAm8eOv+wWf/Q1rktd+Hep+Lrb4aeLPh5440vRvHnhbQIns7W+Hmw3NrNbxrKskaneFPHzAH9aAOn8P/H+ZvF2neC/iJ8OtY8GX2tFk0uW6lSe3unUZKCRcbWx2IryTxZ8WPF1p+1VodxD8JvFM/wBj0TULaOxjeLddruT9+g3Y2jHfnmuq174jeNPDus+HbL9o/wCEumHSm1SKHT/EWk3v2m3t71uI2eMhZI89Bwa1vEP/ACeL4Q/7FbUv/Qo6APcrOeS6tIbmW3e3eWNXaKT70ZIztOO46VNRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXKeNSfNtRnjax/UV1dcp41/wBda/7rfzFdOD/jI58V/CZzVeear8Q9V0r4tWHg6e3g/sa8tlDT4PmR3D58vJ6YYgL9WFeh15/4t+HOo+IdY1fVrW/t4JLnTYobB23boLuKQSRyHA+6GVTxzxX0eF9lzNVdrf0/keFiPacqdPe5Qb4sXMPxG1fRbiGIeH9Js2Zp1UmWSdCBJjnGFZtv1U11Ws/ELw1oKeZqc8sajS5NX3bMj7OhQHv97Mi8Vx8fwf1MWcEMmq2rTto89reTYbMt7LIZXlAx90uSfXmn/wDCAePdSujfaxceHo3h8PzaNDAvmTxu7PEdz7lX5SI8EDkZ4zXVKGFk009Fo/Pz+ZzxniIp3Wr/AK/A2n+JNneQwG3t77S5jfQWzx39n8zrIpK7drYwcdcnGOlWrP4m+H77V49LtrXUmimuTaRX/wBm/wBFeYdUD5znIxnGM965DTvhN4lW4M00+n6fafbbW5XT4Lya4iTyg+91aRQQW3gbeny1veGPCXjXw+bTQFvdK/sKyvJLlZlLm6ljZy4jKFdo+ZuWDHipqU8Mk+V/j/V/QcJ4htcyO/rV8LkjWoAD1D5/75NZVavhf/kNwfR//QTXk1v4cvRnp0f4kfU7uiiivBPZCiiigAooooAKKKKAPEbP9mu3tf2kLv47HX1ayngLx6P5JxFetFHG9wGzjJEYzxnnrXqHjzwlZePfBWueC9RIW31uwnsJGIztEiFdw9xnNb1FAHl/7O/wZl+B/wAPh4Tv9eGt6ncXk99f6j5Rj+0SyOSCQSTwu1eT2p0HwVtp/iH428Wa3qEV7pfjPSItIn0/yirLGoYPl88hgxHAFenUUAeC+G/hh+0R8OdFh8D+CPiV4fvvD9kgttNm1nS3lvLK3UYSPcjqsm0YA3L0Apus/suyan8E/EXw0HjBpNc8VX8Wp6prdzb7vNuFmRziNSMLhNoGeM175RQAijaoX0GK8/8Agp8L7j4TeG9T0G51iPUm1DW73VhIkJjCLOwITBJyRjr3r0GigDm/iP4Tl8d+BNc8HQ3q2b6vZvarOybxGW7lQRn868h8L/CT9qDwj4e0/wAM6R8dPC62WmQLbwB/C+5gijjJMnNfQVFAHhmsfCL46eNfBviXwX8QPi1oWo2euaebSE2ugfZzBIWB3kh8sMDGOK0PGHwM128bwh4o8B+MF0Pxh4Q09NNivHtvNtr238tUkimiJ5VtoI5yp6GvY6KAPDL/AOEXxa+Juo6Snxj8ZaIdB0i9i1AaVolg8Qu54jmNpZJHZtoPO1cVr/FL4ReMvEPxB8PfFD4c+MLLRNa0O1uLGSO+sftMFzBNjIIDKQRtGCDXrlFAENmt0lpCl9KklwsaiV0XarPjkgdhntU1FFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFZ+r6RDq0IR2KOnKOO1aFFVGTg+aO4pRUlZnKf8IVN/wBBBP8Av2f8aP8AhCpv+ggn/fs/411dFb/XK3cw+q0uxyn/AAhU3/QQT/v2f8aP+EKm/wCggn/fs/411dFH1yt3D6rS7HKf8IVN/wBBBP8Av2f8aP8AhCpv+ggn/fs/411dFH1yt3D6rS7HKf8ACFTf9BBP+/Z/xrT0bw7FpUhuHl82XGAcYArYoqZ4mrNcrZUcPTg7pBRRRWBsFFFFABRRRQAVFcPcRqDbwLK2eQX24H5GpaKAKX2jVP8AoGx/+BH/ANjR9o1T/oGx/wDgR/8AY1dooApfaNU/6Bsf/gR/9jR9o1T/AKBsf/gR/wDY1dooApfaNU/6Bsf/AIEf/Y0faNU/6Bsf/gR/9jV2igCl9o1T/oGx/wDgR/8AY0faNU/6Bsf/AIEf/Y1dooApfaNU/wCgbH/4Ef8A2NH2jVP+gbH/AOBH/wBjV2igCl9o1T/oGx/+BH/2NH2jVP8AoGx/+BH/ANjV2igCl9o1T/oGx/8AgR/9jR9o1T/oGx/+BH/2NXaKAKX2jVP+gbH/AOBH/wBjR9o1T/oGx/8AgR/9jV2igCl9o1T/AKBsf/gR/wDY0faNU/6Bsf8A4Ef/AGNXaKAKX2jVP+gbH/4Ef/Y0faNU/wCgbH/4Ef8A2NXaKAKX2jVP+gbH/wCBH/2NH2jVP+gbH/4Ef/Y1dooApfaNU/6Bsf8A4Ef/AGNH2jVP+gbH/wCBH/2NXaKAKX2jVP8AoGx/+BH/ANjR9o1T/oGx/wDgR/8AY1dooApfaNU/6Bsf/gR/9jR9o1T/AKBsf/gR/wDY1dooApfaNU/6Bsf/AIEf/Y0faNU/6Bsf/gR/9jV2igCl9o1T/oGx/wDgR/8AY0faNU/6Bsf/AIEf/Y1dooApfaNU/wCgbH/4Ef8A2NH2jVP+gbH/AOBH/wBjV2igCl9o1T/oGx/+BH/2NH2jVP8AoGx/+BH/ANjV2igCl9o1T/oGx/8AgR/9jR9o1T/oGx/+BH/2NXaKAKX2jVP+gbH/AOBH/wBjR9o1T/oGx/8AgR/9jV2igCl9o1T/AKBsf/gR/wDY0faNU/6Bsf8A4Ef/AGNXaKAKX2jVP+gbH/4Ef/Y0faNU/wCgbH/4Ef8A2NXaKAKX2jVP+gbH/wCBH/2NH2jVP+gbH/4Ef/Y1dooApfaNU/6Bsf8A4Ef/AGNH2jVP+gbH/wCBH/2NXaKAKX2jVP8AoGx/+BH/ANjR9o1T/oGx/wDgR/8AY1dooApfaNU/6Bsf/gR/9jR9o1T/AKBsf/gR/wDY1dooApfaNU/6Bsf/AIEf/Y0faNU/6Bsf/gR/9jV2igCl9o1T/oGx/wDgR/8AY0faNU/6Bsf/AIEf/Y1dooApfaNU/wCgbH/4Ef8A2NH2jVP+gbH/AOBH/wBjV2igCl9o1T/oGx/+BH/2NH2jVP8AoGx/+BH/ANjV2igCl9o1T/oGx/8AgR/9jR9o1T/oGx/+BH/2NXaKAKX2jVP+gbH/AOBH/wBjR9o1T/oGx/8AgR/9jV2igCl9o1T/AKBsf/gR/wDY0faNU/6Bsf8A4Ef/AGNXaKAKX2jVP+gbH/4Ef/Y0faNU/wCgbH/4Ef8A2NXaKAKX2jVP+gbH/wCBH/2NH2jVP+gbH/4Ef/Y1dooApfaNU/6Bsf8A4Ef/AGNOSfUWdRJp8aqSMsJ84HrjFW6KACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACioZry0t/8AX3Mcf+8wFUZvEekxcfaN5/2VJosBqUVz03jC3GRBau3+8QKozeLb9/8AVRRxj86fKwOvorg5tc1Sb7124HoOKjtdUvba4WcXDtgjILZBFPlA9AoooqQCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACori5gtYzLcSqijualrkPFsztfpAWO1EBA9zTSuBqzeK9NjyIxJIfYYFUpvGMh/497NR/vtn+Vc3RV8qA1pvE+rS/dlWMf7Kj+tUZtRv7j/XXcrD0LnFV6KdkAUUUUAFFFFABQOtFA60AelDpS0g6UtZAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFcb4r/5Cv/bJf612Vcb4r/5Cv/bJf61UdwMaiiirAKKKVVZztVSSewFACUVch0fU5/8AV2UuD3K4H61eh8KanJ/rDFF/vNn+VF0Bi0V08Pg5BzPeE+yrV2HwxpUX3o3kP+01LmQHF0DrXb3ui6b9ilCWqIVQkEdQRXEDrQncD0odKWkHSlrMAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArjfFf8AyFf+2S/1rsq43xX/AMhX/tkv9aqO4FfSNGl1UuVkEaJwWIzzW7D4RsU5lmlkPpnApvg//jzn/wCuv9BW/Q27gZ8Og6TD92zRv9/5v51djhihG2KJUHooxT6KkAooooAKKKKAIbz/AI9Jv+ubfyrzoda9FvP+PSb/AK5t/KvOh1q4gelDpS0g6UtQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAVxviv8A5Cv/AGyX+tdlXG+K/wDkK/8AbJf61UdwNPwf/wAec3/XX+grfrA8H/8AHnN/11/oK36T3AKKKKQBRRRQAUUUUAQ3n/HpN/1zb+VedDrXot5/x6Tf9c2/lXnQ61cQPSh0paQdKWoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK43xX/yFf+2S/wBa7KuN8V/8hX/tkv8AWqjuBp+D/wDjzm/66/0Fb9YHg/8A485v+uv9BW/Se4BRRRSAKKKKACiiigCG8/49Jv8Arm38q86HWvRbz/j0m/65t/KvOh1q4gelDpS0g6UtQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAVxviv/AJCv/bJf612Vcb4r/wCQr/2yX+tVHcDT8H/8ec3/AF1/oK36wPB//HnN/wBdf6Ct+k9wCiiikAUUVDc3dtZp5lzMsY7ZPX6UATUViy+K9MQ4QSye4XH86j/4S+w/59p/yH+NOzA2Lw4tJs/882/lXnQ610GqeKBdW7W9pCyBxhmbGce1c+OtXFWA9KHSlpB0pazAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiioLy9t7GEz3MgVR09SfQUAT0Vy9z4vkJItLYAdi55/Kqh8Vaqe8Q+iVXKwOzri/FDq+qttOdqKp+vNNfxNqrqV81Fz3C81mO7yOXdizMcknvTirAdV4P/AOPOb/rr/QVv1geD/wDjzm/66/0Fb9S9wCiiikBXv7tLG1kuX/gHA9TXBXd3PeztPO5Zj09h6Cuq8WFhpqgdDIM/rXH1cUAUUUVQBQOtFS2sTT3MUKjJdwv5mgD0UdKWkpayAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACuH8QX73l+67j5cR2qP5mu4rzzUI2ivZ43HIc/zqo7gV6KKKsAooooA6zweP8AQpj/ANNf6Ct+snwzbNb6WjMMGUl/w7fpWtWb3AKKKKQFXU7IX9lJbHgsMqfQ1wU8EttK0MyFWU4INej1UvtLstQXFzECezDgiqTsB5/RXUyeDoScxXjKPQrmmr4OT+K+P4J/9eq5kBzFdH4Y0h/MGo3CFQv+rB7n1rSs/DWnWrB2VpWHQv0/KtUAAYAwBUuXYBaKKKkAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK5/xFocl03220XMgHzr/e966CimnYDzZ0eNtsiFSOxGKbXok9laXP8Ar7dH9yOar/2FpH/PjH+tVzAcGAScAZNbOjeH57yRZrpCkAOcHgt7V1EOm2Fucw2kan6Zq1ScgEVQoCqAABgAUtFFSAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUV4145vPGfw28Sv8A8InG2oW3jeZbK0jnmythqTD5ZOTnyioJIHdR60Aey0V4pot5490ix1zSvB+pWctl4LPk3UupK8s+qXIjE07bs/uwS5A68+grpPCfxLv/ABPLr08dvFFb2Oj2WoWyEfMJJUlLqx7gGMYp2A9HorzjVPEWp678BR4qlYR3+oaBDev5JKASvGrHb3AyaxtB8X/ELw7NpFh4ludIvYNT0CW9tgmYPIlhiVgskrHDKRnLHGDSA9gorwvw/wDFbxvqGrSaLDrOj61PdaLcajbS2dlIkEU0WCYlkJ2zAhsblP4Vrat8a7qy0hPENnZxT2lj4dl1fUI8c/aMhI4Qf4cvv59ENOwHr1FeA/8AC8fFNlb38S3mn6zP/Z5u4JYbCa3jt5lkRTE+8fMCH4bg8Hiuz8P+IfHL+K7fwp4tu9Omi1zRZdRtZLOFo2tmVo1ZGyfm4k4PHTpRYD0uivJNOtPG2hfFTw/4WTxTHcaRZ+H4xLHLAS8/l/Izls/fLDOfTitz4oeLdY8OzaXZaVruk6QL1pDJcXiGeVto4SKBSGkJ7kdAKQHf0V4x4c+KXjbxtpnhvStE+wWWs6oL57u7uLd/KjjtZjESsJw25yM7SRt71l6B4v8AH2malq3h1prKTxFrPij+zop3LNbQItuZXkCHnGxGwvHJ/GnYD3uivAL3x54w+HviHxjdeKLy0vbyKPTbW0liV1t2MgbEhi5K4AOQM5IqxafG7xKTceH7aSy1fU7uezttNv1s5bW3Ek8yxESK4z8m4NwTkA9KLAe70V5R4afxxa/GWXTPF2qWl6q6BHJDJaI0UbgzSZLRkkBgQRnJyAPpTvin4+8SeHNXaw0bX9H08Q2LXSwy27Xl1dOOiiGM7lX/AG8HrSA9Vorxaw+I/wAR/GCyT+Gm0nToYfDljrjG5haUl5oi5iGCOOOvb0Naum/Fy9tlj1TxNBBBpt14ck1iFkHPmwf69M98g7gPRTTsB6pRXC6v4n8W6D8JD4q1C2tR4gisIZ5odpESzuVDJjrgFiPwrmbfxD8Y7vxPH4STVPD8ct5pX9rrdm1ci3UMqmLZn5yTIvzZHAPFKwHsFFeY/Dn4ma14vv8AQre/treFdR8PR6lOsYJxP5jo20/3fl4FcrffGDxsdO0y/mu7HQ7C5F75uqz6dLcWwlimKRxPs4iBUElmOOKdgPeKK8XvPibfaZfT+IpYbG6n/wCER0+8X7LcmSCS4muJUARgdpTcQQwGSK7fwkfiZDqYj8Xy6Ve2Fxa+cs9mhia3myP3RUklxgn5h6dOaQHY0V5D4m8S+L/DfxV1e9l1iCXQ9P8ADceoiwEJ3N+9lUDdn7xZfvemB2q5pPjD4gaXqXhe68XSaXcab4skEEcVpEySWUjxNJGCxP7wELtJ4wTTsB6lRXI+J/Fl74Z8WaNb3nlDRNThuI5JSvzx3KAMoz6Mu7j/AGTXIaV48+IXi2+0/QdFfTdNury1uNVkubmAyLFarKI4kCAjczbgxORgUgPXaK8s8JfE7xBrHiPRvDWqWtrFdedq1lqZiyVaa0dEDxk9FbcTg+uO1ZsnxM+IGq67beHPD0GmLcXusahp6zXCNshighVw5xyTyeO5wOOtFgPZaK5Pxz4i1jwnpWl6rH5EsS39vBqRKHHkvlSy+nzlfzrhrD4weJNTn1TToLS1S6m1mytNGJUkSWssxjkkbnkoEkY49BRYD2WivBrn4i+KtFspTYPDpmnyeINcgutVlspryK3MV46xq6qcoGAPzEhRjirEGv8AjrxV408MTeHNf0dLi98OXslxcxMbmzIW4hAkRAQGJ4HJGNx9KdgPcaK4zwL4n1TxX8Pm1jV44Y9Qj+2WlwYMhDLBI8TMueQCUJH1rzPRfFvibS9J0a40XTpNX1NPCmrXkELO7PNLHdIFXGfm6+hPGBRYD3+ivALz4i+Mte8F+JU0zxto095YW8E4kjs3trq3LPh45LdzvXthz15GK9p8J/2ofDOmNrV3Hc3rWsbTTRpsV2IzkDtxilYDWooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACsPxJ4TsvE11ot3eXM8TaJqCajCI8Yd1UgK2QePm7YNblFAHCa58K4tU1PVb7TPFWraNBrwX+1bWz8ry7ohAhbLoWRigCkoVJAqG/+D2nyXckuieI9U0W1u9Oi0u9tbPy9lxbxghRllLI2GYblIPJ5r0Gii4HNxeBtOh8AwfD1bu5Nlb6dFpqzEr5pjRAoY8Y3YX0xWf4l+FeheKrS1sdSu7wQ2ulXGkBY2Ub4povLYk4zux0x3rtKKAOD0b4XXGneItL8T3/AI21fUrvSraSzjWaOBI2gfb8hVI1GcqDuHzH1xTtK+D3hTTNL8R6M/2i6tPE25blJnH7uMhv3cZABVQXcjuCa7qigDgH+FDahY3Nl4h8ba5qwmtltImnMKmGMOGyAiAMx2jLMCfeuh/4RCxHiTTPE32mf7Rpeny6dHHxsZHZCWbjOf3Y6HHJreooA57UvB0V94v07xjDq13aXNhA1q8MQjMdzESTtfcpI5OcqQfeqninwE+v6/YeJtN8SX+jajYW8toJbaOGTfDIVLKVlRgOVUggZ4rrKKAPNrD4KWekWNlDo/i3WLS90y7u7iyvx5TzRpcPvlibehEiljn5wT70sHwT0u3spwPE2sNqkuqLrMeqM0Znhugu3co27SCuQVIIwSMYr0iincDzmP4L6dM2s3eteJtX1PUNb+zvLeSmNHhkh/1bxBFCoR0wBj25NWbj4Tw6rp93B4h8Waxqd7cNBJBfu0cUlo8L743iWNVRWDAEnbzjnIrvaKVwOM8OfDh9G8UzeMtU8WaprWqT2a2LPdLEiCNWLAKkaKq8seg569aj174YprHiG+16z8U6ppn9rWkdlqEFsIitxEm7ADMheM4ZhlCp5rt6KAOH8IfCrTfCNlNaRazf3hm0mDRy84jBEMKFEI2qBu2nGfauf8UfDf8AtF/Bvgmz028l07RLtbq41GRlCmBQ2+FsY3eYG2kYxgk9q9YoouBleJ/D1r4q0G78P3k0sMF2qqzxY3ABg3GQR2qtb+D7G28SxeJ0uZzcQ6YdKEZxsMZdG3dM7sxjvjk1vUUAedW/wZtNLg0ZNA8Wavpk+kWTad9oiELNcW5YttcMhAOScMoBGetGn/B86BYWdn4Y8c65pr2scsLyfuZ/PSR953pKjLuB6MAD716LRQB55ZfBDwjZae2lJLePato0OjGNnH3I5XlWXIGQ+9yfTgcVr+GPAb6DqY1jU/E+p65dxWpsrd7wRqIISQSFWNVBJ2rljknHWusooA5TXvh7p+v+JD4huNRuoxNpp0q7tFCGK5t9zsA25SwIMjcqQaz9D+FUWl6npV7qXirVtYt9B3f2VaXfleXakqUDZRAzkKxUFyxANd3RQBwHxh0C/wDF+jWXhWw0q4ma8u45GvEYKlmqn5mY5zkqWUAepq/rXw5t7y70zVNA12+0HUdKtDYRXNmsbl7chcxukisjDKqRkZBHFdhRQB55/wAKc0+0tNK/sTxJqunanpc9zc/2mhjknuJLhg07SiRWVtzDPTjtirHhj4R6P4Y1Cy1ODWNSu57K7u70NcOrGSS4jCPuwo44yMfy4ru6KLgZfifw/Z+KtAvvD2oPIlvfRGJ2jIDLzkMM9wQCPpXM6V8IfDuk614c1uC7vGm8N2ktrAjsu2YuCDJIMcuNzYIx9413VFAHBn4VfY2a48PeMNY0m6a+vr1pYhFIr/apmldGjkRkIDMdpIyB3qfwl8K9E8IajZ6rY3t3LPa2dzaMZSuJTPKkjyMABhtyDgYGCeK7WigDC8N+EbHwzoM3h+0uZ5YZp7qcvJjcDPK8jDgAYBcgewFc6vwc0NbW1tU1fVI/sel3WlRSRSiOQJPKspkDKAQ6soxjj1Brv6KAPPf+FQwX76jdeJvFWp6xe39gNNW5ljgiaGAMWG0RIqk7jnLA10Wg+F73RZ7SWfxVqmoJa2IsvJn8tY5CGBEpVFA3gDbxgYzxmugooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Z\"}"); - socket_status.send("{ \"RequestType\":\"SendImage\", \"RequestID\":\"788346414\", \"toType\":\"toUser\", \"toID\":\"1036\", \"fileType\":\"image/jpeg\", \"blob\":\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAHgAoADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAopNy/wB4fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/AHh+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv8AeH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/wB4fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/AHh+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv8AeH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/wB4fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/AHh+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv8AeH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/wB4fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAxst6mky3940UmRQA7J9TRk+ppKKAAFh/EaCWP8RoooAXJ9TRk+ppKKADL/AN40Zf8AvGkwKMCgA3N/eNG5v7xoI70Ad6AF3Mf4iPxpMsP4j+dHSlxmgBCWH8R/OlBb+8fzpCM96OgoAVWb+8fzoLN6n86OBTSc0AOy3940Zb+8aTg0YFACkv8A3jRlv7xpNvvSYx3oAXLn+I0Ev/eNAwOc0YBNABubHU/nSbm/vH86VaMc5oATc394/nTt3+0aTb70baAAl8/eP50uW/vGk20baADc3940B27k0babQA/Lf3jRlv7xpMAdTRgHpQA4lj/EaTLf3jTSMUvFAC5b+8aMt/eNJjHNGAelAC5b+8aMt/eNJxQSD3oAXLf3jRlv7xpNvvRjHegBSW/vGgFv7xpuB60YHrQA7Lf3jRlv7xpAQO9IMDvQA7Lf3jRlv7xpuM96XbQAuW/vGjLf3jSfLR8tAC5b+8aAW/vGkAFJgetADst/eNGW/vGk2+9IAPWgB5LH+I0mW/vGmkYpeKAFy3940Zb+8aTGOaMA9KAFy3940Zb+8aTigkHvQAuW/vGjLf3jSbfejGO9ACkt/eNALf3jTcD1owPWgB2Wz940mWzjcaP4aMDFAC5b+8aPm/vGmjA70pGaAF+b+8aPm/vGm4HrRgetADst/eNALf3jTcD1pQPegBQX/vGjLf3jSAClOO9AAS2fvGgFs/eNJgYzRgYzQApLf3jQS3940hA9aQjHegB2W/vGjLf3jTcD1owPWgB1FFFABRRRQAUUUUAGc0UnzUtACZFGRRk+lGT6UALSZx2paMigApMr1paTJ9KAA57UDPelpPmoAMCjAoyKMigA+Wj5aMn0oyfSgBcYowKKKAEwKCAO1Lz6UhJoAMg0ZApfwo/CgBMCjApAT0FOoATAowKWigBMCjgUZPpRk+lAAeDmlpDycUtACfNSgGkzntRz9KADn1oOR3oyBQSD3oAXoKRTnqaXp1ozmgAooooAKKT5qPmoAXPGaQEmlBzScCgAwfWjB9aMijIoADntQM96BnvQc9qAFpP4qXPOKOM0AITilHNHtRQAnzUoBpM57Uc/SgA59aDkd6MgUEg96AF6CkU56ml6daM5oAKKKKACik+aj5qADqOKFpQc0ZFACEZpQMUgPrS0AIc9qBnvQc9qBnvQAtJto3UtACE54FKOKAc0ZxQAUUm72o3e1AAc9qTBFKCaDntQAtFITigHNACg5oJxQBigjNAATQPeikyKAFoozijOaACg57UUUAJgUv4Ugz3paAEwKMCl59KOfSgAooooATgUGl59KKADgUmRS0mT6UALRwKKTJ9KADg0vAoOR2pMn0oACfalyKTJ9KUnFABRjFFGRQAcdBzQeODwPSuN+IXxa8EfDLTW1DxZq8NrGASFJyx/Ac189aj/AMFFvhRaXTQ2K/aVU4D/ADCgD635AyRj3oPTI596+evh3+218HvHl8mlx6wlrdyHARwcZ+pr362vrW8tFvbW4SSJ13KynIIoAm45/hPal6ngY968W179q34XeHfGjeBtQ1YDUldY/L2Hqa9lgnS6hSdPuSorg+xGRQA89KWkPSloAQDFLRRQAc+lI1LnFITxQANSjgUUUAIc8sB16ilHp+leZ/Fz4++Bfgy1jH4xvxbPqG7yvlJzt+ldN8PfH+hfErw1beKfDtx59ldZ8t8Yzg4oA6XHt06UuOpHJpMkHPf0rB8ceM9H8A+HLnxNrkvlWVqAZG69aAN4nC5xk96Mc4HXtXlvwq/aJ8AfF2+ubHwjqQmltSBINpHX616iB6jPvQA7A60UUhGaAA57UtIc9qWgABzSYpQMUZoAQ9KWkPSloAQDFLRRQAc+lI1LnFITxQANSjiiigAooooAMYoPNIc9qWgAHSiijPOKACiiigBPmo+ag57Upz2oATrQDmjpQBigBaKKKAEBz2paQYzSk4oAQnFKeKQjNKeaAEY0ueM0YzQelAAcnvR+NJxnOaOtAC4zRSEZpaACiiigAooooAKKBzRQAUUUUAH40UHHeigAopMCjAoAX8aPxpDjvScetADufWjn1pDjvQMdqADB9aMH1oyKMigA59aXn1pDjvSkjvQAgyc4rmfiR40sfh94O1LxPqMgWO0iJBJ744/Wum6jBr5Z/wCChOuXen/Bi6s7aRkW5xvIPo1AHxBdah8R/wBrj4vnTo7u4aGe4ZQoY7Y0B/LpX2j4Q/4J4/CnTdESDX4ft96yAyS5K849K8x/4Ji+FbSX/hIvEVzErzr5XkuRyvXNfoGAoHAz70Afl5+1T+x3cfBy2HjPwJPM2nodzxxAgxc8c9TXuP7Anxn1/wAV+Hb3wT4j8+STTY8RSSggnIPrX2B4h8N6N4p02TStbtEubab78bDg1z/hv4V+BfBBnu/DWix2csincy9+KAPzF+M8m39rm4ZjhRdxdT06V+j+rfHf4X+BrCytPEfii3t5fs8YKr838I9K/MH9qI6j/wANIawulyFblp40jYDueK+h/CP7Auv+OvDCeJfG3jO4i1O6hDrEyltvHHf0oA+1vBnxW8B+P4xJ4X1+C6B7AgH8utdaCBkk5B6e1fjday+NP2avjpHoEGq3BksrqOMqWO11cgdM46Gv0i/aI+Nn/Cqvg5/wk0L41G6tIzAueSWUZP60Ad74w+MHw+8CKW8SeIbe229VDBiPwFZ3hX4+/CzxpdCz8PeKLeeU8YY7f51+dfwU+A/j39qnVr3xb4p8Tz2tk8m4SsxIcEnjGe1bHx6/ZA8SfAnSR448G+LJruCyIeXYSu054780Afp7G6yrvQh1PRgeKz9a8RaN4dtGvdX1CG0iQZLO4Gfzr5i/Yq/aIu/iN4GvNK8S3e/UdDiy0jHlxg/0FfLPx9+LPjv9oP4vt4A8MXs6WInMMMUTEdPvHIoA+/JP2pfgjHfGwk8YwifO3btOM/WvQ/D/AIr0DxVbC70LU4LqMjIKOCfyr4bsv+CbUkvhcXdx41mGpvF5m3YchsZxnNeS/BzWfiz+zv8AGhPC98uoXGmpOIrhSrMrg8Kc84oA9N/4KiFheeEDuwczYHr0r6E/YZOf2fNAYntJwe3zV86f8FN5zcDwRc7cNMkjn2yAa2vhh+0h4f8Agn+y3owa5SbWJ45RBbhuc7utAH2jrXxE8HeHbr7Hq+u20Ew6ozjNeTftU69pev8A7P2v32lXAmhZF+ZenWvjP4OfCf4lftUeO38ceKdQu4tIabe77yFK54AANfX/AO0f4N0rwN+zTq+g6OhjhtokVcsWJOeetAHy/wD8E6NWsNI8R+Ib7VLyO2hjZSWdgB39a+1Zf2nfgvFqR0mTxhCtyG2425GfrX5UfAnwN45+JnimTwV4TvJrW3vpMXUkf8IBr6s8Xf8ABOO7sfC8moaL4xmn1OCIyFdpBY4yec0Afd+i6/pHiG0F9o2oRXMDAEFGBq+8ixxtJKQEHcnFflj+yj8a/GPwg+K8fw78V3s7afLMYZklYnZjgdfU19O/tw/tEX/w08I2eieGLny9Q1qM7ZF6oMD+hoA9y8VfHn4XeDJWt9f8UW8MgOMKd38qd4T+Ovwy8bTeToHii3mc8BWIX+dfnz8AP2Q/F3x7sH8b+L/EN1Z2t0S0buWbfzzxnioPj/8AsneMv2ereLxr4T8Q3V5ZW7b5HQlAnPHGeaAP1Gmu7eC3N08gMaruLA8Yrmbb4p+BLu8TT4fEVs1ySVEe8A5/Ovn/APYt+Ok/xm8DXXhjxROZb+yjEUjE8sDx/KvmD9rj4NeKfgj8QI/G/hm5vP7Knm85cSMRHg5PfuaAP1Ha4hihNzJIqx7dxYngCuWh+KngWe7+wQ+ILd7gttCKwJJ/CvhLxj+3edT+BltoemztH4luofJlYdUxx+oq5+wr+z7rXiPUh8VfGU939n3mSzSSRsSEk7uM0AfoYjh1DL0IBFOPtTY0WNAi9FAApSaAFOT3opOBRkGgBaKKKAAmijIoJxQAmD60YPrRkUZFAC/jQOKQAUvAoATPGaM8ZoPSg9KAFooooAAc0E4oooAKKM5ooATgUvBpODS8CgAopMijIoAWiiigBM84paT+KjHOaAAUZ5xS0e9ABRRRQAUUUUAJ81HzUmB60YHrQAvzUfNSYHrRgetAClvSgZ70tJkUALRSA5oJxQAtI1GRQT2oAGoPShqD0oAAcmj+KgHn60fxUAB7YoPSgnmgnigAB4znrXzR+3v4TvPEHwS1C7so2lktApCqOeWr6XyNpBXkdKy/Evh+x8T6Jd6HqcKvFdxlCp5HSgD8+f8Agml4/sNH1vXPB+ozrHPfmNbdWOOVzmv0ZJ6gYr8mPjT8BviX+zf8Qz4u8JJcPYrMZra5hUngnJGB+VereEv+Ckuv6XoyWPiXwXNdXUKBfOZypcgemKAPsT9oX4sR/B/4dX3ilNjXUQHkxFgCxzivF/2Vf2pfFvx31nVLLUtNMNvZr97dkcg18ffFn41fFX9qjxFbaLY6Rdw2Uj7YrRVJVQfVsV94/slfs/j4LeAWfU4h/a9/FuuOORxxQB8MfGlFk/a4uFcAj7ZD1/Cv1f0dVTRbJEGB9mj/APQBX5UfGWyvH/a2uHWznK/a4fmEZI7d6/VjSfl0ezXv9mj/APQRQB+U/wC2QAP2oJ8Lg/bLbt7ivb/+Ch73q/DXwWsDN5JtB5uOn3VxXi/7Y1jezftNzSRWk7j7ZbfMsZI6r3r7e/aK+DUvxf8AgfDpmnW+7Uraxia3Hf7oJ/lQB8Ufs/eG/wBrDUvBFvP8JrnGjHOxVKZHPfPNdx4q+FX7c3ifRbjR/EtwJrCcASI7RqPzrhPgN+0J45/Zf1a78I+JvDt1PYb9vlEECPB7HHeu5+Nf7dPib4laGfCXgHw7dWcl5hWmj3E5zx2oA2P2afgB8T/hND4l1XxBbRwQ3FuxPlzq+fkPYGvKP2NTbP8AtMRPqpUuLqfbv79fWvrb9jb4Z+OrHwXfav8AEW5nkl1iLEcMpJ2jBH9a+Tfjp8LfHv7OvxjPjnw7p85sTOZraWJSQRnLdKAP1aBVUBBBGPyFeb618Qvg1ZeJX0jVbywGrKyqytEpbcenNfJw/wCCj8p8IG1bwfMdX8nys7jndjGeleU/s7/Df4h/H74zr478QxXkVgJ/NuJX3KMdVGKAPRf+CoLo7eDnh6MsxXHcYGK+VdI+C/xL8TfD6bx3a2E8mjaeAyHdkcnnAr6u/wCCmunXIbwXb20EswgWRPkQnoAO1e7/ALHPh201j9mrS9I1awGy5ilR1dMHknrQB5F/wT7+PdlNp/8Awq7W3igurc7bbIAL85NfQX7YRB+A2vt1BROfxr8+/jp8MvFP7N/xmXXvDkVwtq9x5ttJEhIIzlhxX1r45+KsPxf/AGTdR1eCGUXawRrPEUO4EHFAHj//AATVOlDxTr3nbBdl18vPXvnFfovPsMTBz8m07s9xivxa+Cni/wCIPwo8Uv448PaReTW9lJm4QIwyCcelfVnij/go7cXPhOW10nwjPDqs8Xl5ycqcYJxigDwj9oQRp+1tfroG3aLy32lOnUZrr/29/tv/AAmvhtrzd5XlQ7M9Purmsz9lb4Q+MfjV8YY/Hviayn/s+GYzXM0qkbs8r1r6l/bh/Z51H4leDrPXPDFoZNQ0OM7Y1HMg4/kBQB67+zCbD/hTegiwKeWYjkrjk1T/AGsf7OPwR13+0dhTyxgN65r4c/Z7/a68W/AnTj4H8WeHrq7sbViibwy+Wc89uai/aF/ay8X/AB/ij8FeEvD11a2M7BHCBm83J47cUAdD/wAE2zc/8J9riwqRbiQbj2xzivq79sXxF4E0j4TalB4yjhla4TFvG33ic9u9cN+x78G5PgT8Or3xj4xgMF5eRCaRCMlcAkfnXyf8cPHnjX9qD4vw+G9Ftbs6atx5dvFsYKFzhjQB876W9tZ67bajfWbyaeLoOFIIBTf6/Sv2n+Avinwh4q+G+kah4N8lbIQhfKTqpAAOfxr5/wDHv7E3h6b4EW+haPZr/bunW5lS4A+ZifmIr54/ZA+NXiT4JfECTwL4qhu00u4n8ly0bERkHA7dzQB+poPpSDOeaitLqG+tormE/LKgcfQjNS8YwaADvR3o70d6AAnFHQUEZpaAAc80HnikJxS4xQAU3JpQc0EZoAU8UgNKeaQCgA3UZFHy0fLQAZFGRR8tHy0AGQaMgUny0fLQAuRRn3pDtxSjaaADvR3o70d6ADn1oOR3oIHrRgHpQAEkDrQARzRjPNAAoACcUtITiloAQDvS0UUAJ3o70d6O9AC0UUGgBAeaCOc0mBig4xQDVg5zkUvPrQMdKBjPFJK/UNWHA4ozQwxQRTt5hdCE0ueKRqXHFHzC6BaAecYoFIetO6DQUkjvQcgdaQ896OtK6DQXIHSjIoA4pB1o1DUM0ZoHWlajUNRaMUh6YoWlqBS1PRdM1u2az1OyhuYmGCsiA/zrzLUv2V/gjrF013f+DIWlY5JDkD9K9ZODgmg4DYLZFPUZyHhL4SfD7wOAPDvh62tj2YoGI/E115A/1eARjFKeRjHSj5cZzk+lOzE7HJXnwr8DX+sHXbzQoJL1iG84gZyK6tFVVWNQAEGAPQU8YIwBSdyCdxPalqLc5TWvhd4I17UjrOraFBPeFg3mMBnI6V1EUccUaQxgeWi7QuO1PHXnj0o3EfL2FFhnGeLPg98PPG0vmeI/DltPJ/eChT+grO8O/AL4U+FrsX2jeFreKVTkFhu/nXooOBn0oJzyOlFgGRxRQxpHDGqIvACjAFZ+u+GtE8SWr2OuWEN1E4xh0BxWkOufXtRz0zg0rtB7zPK3/Zf+Cz3f2tvBkPmk7t27jP0r0LQvDei+GrNNP0TT4bWKMYARAP1rSIz06+tKD2LZI600nILM57xR4C8LeMjC3iPS4rtrfOwuM4zWlouh6X4c0+PStHtVt7SP7iKOBV7nlg2c0vG0ZGCKAOf8TeBPCvjARf8ACQaPDeeVnbvUZGarWXw08GaZpU+i2mjRR2VxjzIh0P4V1HGN4GCetB9hgetAHJad8KvAWmW1xaWfh21SO5H71SgOf0rmpv2Zvgxc3IvJfCEAk3bg2e/0r1IEgbs5IoXptxwaAMzQfDWheGLJdP0OwhtYIhgBEAzWk6LIrCVAynjBGQaXg8E5pM8H+8egoA4PxN8C/hj4vuTda74Xt5ZW5LKoX+VL4X+B/wAMfB9yLnQfC9vFKvIZlDY/MV3mcDBPTtS8AdeT2p2sw0XQpalpOn6rZNpl9CJLZxhkAwMVzuifCjwJ4d1AappPh+3guUJKOFBPPWuu7YB4WjqcjqaSTb0YIR1V1MTKCMYxXGXvwe+Heo6k2qXXh23a5dg28KAcjvXaZ6ZOcUDjhjgetF0O5Fb20NpAlvAMKgCgegFSnOQc0ZA+ajjv3ouguLRSAYpScUm3HYQhOKWjIFFHqAhGaWk3Uuad0AUUgOaDzTugFHFNPWnZpo60XQCnpQOlLRRdAIM96Dk0ZFGRS0QW1uGD60YPrRkUZFK6YO973A9KAMUtJupgAOaCcUZA4oPSgBc0mD60ZAoyKADJ9KMn0oyKMigANABpcZpuB60ALg+tGD60ZFGRQAvPrRRkUUAJg+tGD60YFGBQAHPagjNGBQCACc9KG1b3gbSXvBjvnHvRjuR+NeX/ABB/aE8BfD+8bTr7UUe7X70YycfiK4cftneAedqcDqea46mMoU92cVTMMNSdnI+iM/7X6UZ/2v0r54/4bO8Bdk/nR/w2d4B/ufzqf7QofzELNMK/tH0OcHvQMDvXzv8A8NneAv7n86P+GzvAX9z+dH9oYf8AmD+1ML/MfRHPrRz6187/APDZ3gP+5/Oj/hs7wH/c/nR/aFD+YP7Uwv8AMfRB3HvSYP8AEa+eD+2d4C7J/Omy/tpeAIo8uvT61rTxtGbsmH9qYX+Y+icn1oHHevmj/huj4c/3P50f8Nz/AA6/ufzr2FleKkrqI/7Uw38x9L4PpQQT2r5nP7c/w4/55/zo/wCG6Phx3jP60/7KxX8of2phv5j6ZNA4/wD118zf8N0fDf8A55n9aP8Ahuj4b/8APM/rR/ZWK/lF/aWH/mPpgcUdTXzP/wAN0fDf/nmf1o/4bn+HHaM/rT/srFfyh/aeG/mPpnnOB0pAMn6V8zn9uf4c8/uzx9asWf7bnw6vrlLaKP5n+tH9lYr+Ul5thIK8pH0jn/apPoa8NH7WHgkgEJ/Oj/hq/wAFdk/nT/snFP7Bxf6yZbF2lUPc8H1owfWvDP8Ahq/wX/d/nR/w1f4L/u/zo/sjF/yh/rLlv/Pw9zz/ALX6UZ9/0rwz/hrDwV/c/nR/w1f4K/ufzpf2Ti/5Q/1ly3/n4e5k+9A69a8M/wCGsPBX9z+dH/DV/gofwfzprKMU/sjXEuXJfGe5kkKTnk0AgMQK8MP7V/gv72z+dXNL/ae8Iatex2FvH+8f61FTLMVBX5S6Gf4CtKymez5HrRn/AGv0rhB8VtNIBFu2D9aX/haunf8APu361wNW0Z7SaaujusH1owfWuE/4Wrp3/Pu360f8LV07/n3b9aBnd5/2v0oz/tfpXCf8LV07/n3b9aP+Fq6d/wA+7frQB3fXvSH1zXC/8LV07/n3b9aP+Fq6d/z7t+tAHdHjk85pQDjrxXC/8LV0wHi3bH41R1T41aJpVv8AabqEhR9axxFanhoe2qOyNKVGVeXLBHpBDY4NAz3NePf8NI+Ev7v86P8AhpHwlj7v868KXFWWN6VLHpvIsdNXjA9hyfSjn0rx7/hpHwn/AHf50n/DSXhP+7/Oj/WrK/8An4P+wcd/IexfhRz6V49/w0n4U/u/zo/4aT8J+n86P9asr/5+B/YWO/kPYefSgZHavHv+GkvCfp/Oj/hpLwp6fzprivLLXdQP7Ax/8h7CB37mgnHJPSvHz+0l4U/u9PrWN4j/AGtvBHhu3FxdD5W+taUeJMtxMuWNQqPD+Pe0D3nPtRn2/WvmA/t2/Dj+7/Ok/wCG7fhx/cP616Sx9D+Y2/1WzP8A59M+oM+360Z9v1r5f/4bu+HH9w/rR/w3d8OP7h/Wj6/Q/mD/AFWzT/n0z6gz7frRn2/Wvl//AIbu+HB/gP60f8N3fDj+4f1p/XqH8wf6rZp/z6Z9QZpMj0r5g/4bt+HH9w/rSj9u34b/ANw/rS+v0P5h/wCq2af8+mfTxzwc8GlIGeelfMcP7dfw2aRUYbVY8nnivZfhx8X/AAZ8TrP7V4Z1RLgoBuToR+dXTxdGo7JnLisix+DhzVoWR3GBRnFIDmlxmult9DyNluIRmgDFAIoJxQAfNR81Hy0fLQApz2oGe9J8tHy0AAOe1BOO1G6jdQAZ5xR/FS55xSfxUALRz6UnzUc+tAC0UUUAFFFFACcHLetY3i6+k07w7f3kJ+ZIWI/I1skk4OOBXPeP9p8I6mB3gb+RqKvwMyry9x+h+ZHiPUr7Xtcu9Sv5WkkkmcfMc9CapfZMZIA5q3sBvLknn9/Jj/vo1YWMAZ71+e1qj52flderLnkjM+yn+7R9kI6CtTyh6UeUPSsXOSdkY+0ktDK+yn+7S/ZT6VqeUPSjyhjOKfPJD9pK9rmX9k9qPsp9K1PKHpR5Q9KFO60H7WWzMo2xGeKo6pBttJDjtXRGNcEkVl62irYyYHQV6GVyviYJ9wjVu7HExQbh071L9m5HFS2Y+Q8d6thAa/pXDQjKlGy6IcqjUtWUPsgoFoK0PKo8ut/ZQWpPtJt6Gf8AZBR9kHpWh5dHl0eyXQftGt2Z5tKPsnPStDy6PLpuiJVuhnG268Ve0CDbq0P1pzR8ZqxoqY1WH60vZR7GGJqP2TPSoLYsi8dqn+xHd0q1Yx5iX6VdEIHOKtU0fn9WvJS3Mj7H7Uv2L2rW8kelHkj0o9mk0kiPrGm5k/YvQUfYj6VriIelHlewodKKshfWJRd4syPsXtR9i9q1vJHpR5I9KPZK6G8RJK9zHNlz0rc8CwbPFFnx/FUTQgdK0vByAeKLPA/jrz8ypWw0rHs5BXlLHU031Pqe00xXhjIQcqO3tVj+x1I/1Y/KtbS4Fa2iyP4F/lWkLZcdK/LJ/Ez+jKXwL0OY/sdP+eQ/Kj+x0/55D8q6j7MPQUfZl9BUlnL/ANjp/wA8h+VH9jp/zyH5V1H2ZfQUfZh6CgDl/wCx0/55D8qP7HT/AJ5D8q6j7MPQUfZh6CgDljpA/wCeYx9K4H4tWSw+HnOwD8PevZZLdQM4ry741RKvhtyPUfzr5/idtZbUPWyTXGwR4BHaZHAqUWRx0rQtIlKjirawJ6V/M9SvKMnqftlOlHkWhifYj6UGxz2rb8lPSgQIe1R9Zk1uV7GOljE+xcfdo+w/7Nbfkp6UeSnpU/Wp7Jh7GLaujE+xe1H2I+lbnkL6UeQvpVfWJ8urGqMXJJGEbJvSvNfjNBs0mH8a9kkhXB4ryj42pjSocep/nXs5DXlLGQVzowlH98rnhywbuop32fBq1EgqXy81+tqbaPs44eLitCh9nGelH2cDtWh5Q9KPL9qfPbqX9VjfYz/s6+lH2celaHlD+7R5Y9KTqX0QfVoJNWM/7MB2pfs6+lX/ACx/dpPLX0oc7ISw0exnmADOT16V7T+yX4n1TQ/i3pem21w/kXjMJV3ccDjivJWjHOF6da9F/ZsG340+Hzjje+fyrrwVR+1jqeDxHhYf2fUuuh+qcZLRo3qoNPOccVFBnyY/9wfyqT5q+6Xwn8vTilKXqLQBimg4pQc1RAfxUNRjnNH8VABkUuRRz6Uc+lACZFGRRk+lHPpQAHpQOlLRQAUUUUAFFFFABRRRQAh6Vzvj/wD5FLUv+uDfyNdEOc1zvxA/5FLUv+uDfyNZVvgZlX/hy9D8yf8Al5uP+u8n/oRqyOlVx/x9XH/XeT/0I1aAr86qfGz8nr/HL1A9RluvQ0AHgbcepz1o7EYz6V6P8Lvgn4i+JQlns42htYlyZcZyadKlOtLlp7ioYedafLT1Z5zgnIxmun+H/wAP9b+IGsw6VpVs5RmHmNjhRW7pfwR8YXvjM+E202RXjkAd8cBfX8q+0/AHw/8ACvwd8K+dKYklSPdNO3UnFelg8ulXqc1TRI9fL8oliJ81TRLc+Zvi5+zTN4H8PRa3pdyJljXNxkYwa8AKkEgr0OGr3r9oH493XjO7m0DRJCumxkqWH8deDHnG7qefrXPmHsvaWo7HLmaoKrbDidjWRrufsMn0rXI4NZGu/wDHjJ9KrKrPFwXmcCepylkBtH1NXF6VTs/uD8auL0r+n8M7YeK8kKau7sXoQSeOwpOnbHtS4J4ByB0969Z+Fn7PHiz4oabcarZwNDbwrujcj79KtXpYWHNVZpRoVMTK1M8mABbIruvhP8J/EHxR12HTdMt38ncPOl28KK2vB37PvjXxH43bwpNp0sIgkAmkI4Uf/qr738G+DPBfwI8Fb5DDB5EW6aZgMsa8TM86jh48lDWTPZy7KJV3zV9Io+Pfj/8Asv3Pwy0iLxDpFwJrJFHng8bD/XmvnVjghvzFe9ftG/tD6h8TtSl0XSnMejwsVAB4krwckkliceld+VLEOhzYndnDmUqCq8uH2Qh6VNo//ITi+tQnpU+jf8hOH616LR5GJ/gs9Z08fukq+DVHT/8AUp9KvDvVxR+d1viF6YK9uvtR1BP3g3X2o5+WvRPBnwW8ReMdGn1eCMxxqu6IY+/61jiMRTw0eao7GuDwNbHVHToRuzzvAHAGAK7T4cfDXVfH2pi3ghZLYEeZLjgCr3gr4P8AiDxJ4jOk3No8Mdu+JnI7V9X6bp/hn4VeGOTFBHAmST1Jrw81zqNL93h9ZM+u4f4XliZe3xq5acT5l+LnwYn8CRrqFk4ltSBu7bT/AFrycAZGOlel/F74sX3j3UmtYnKWEbEKg715qMngngdK9PK/b/V08R8R4GffVPrklgvgQ1hxitHweP8Aip7PP9+s9q0fB/Piiz/36WaL/ZpJmnDzisfTXW59jaT/AMe0X+4v8q1FrL0n/j2i/wBxf5VqDpX5RP4mf0lS+Begc8DGcdqD97nr29qGIXndhQCSfSvCfH37W3gHwJ42tPB9xcpIXcpPID/qj2qSz3bv0+prxn9oL9ozw58F9Ckle4S41KUfuIA3JP8ASs745ftQ+Efh14OXUtNv4ry+vYibaNG7kf8A16/O1U+IX7SHxCABnu7i8l99qLn8hxQB+hP7MX7TVl8arB7G/iaDU7cncuCQwJ45+lfQGSTnHTrXjf7O37PehfBfw6kcSh9WuEU3E2OSa9lOeMNn8KAGS/dryz43f8i5J+H869Tl+7Xlnxt/5FyT6j+dfP8AE7Tyyoevkf8AvsDxW05UfSrvQVStBhV+lXB0r+X63xP1P3Ck7U0GB/hS8HqKFUllK9+MV3WmfCTxBqXh+TWkjYEDdHFjlh3roweW4jMZNYdXsYYnG0MIlKu7XOF2jcAOQf1rufh18M7/AMY3PmyQtHZp1cjrVj4d/C3VPE2pbr+FobWFvm3DrX0BqGpeH/h1oG3EcSxJgKOpNfa8OcJqSeLzBWprv1Pl864hcGsNgdZv8D5x+I3w9n8GXow++3f7prjM/wAIHPb2rqPHnji/8Y6m9zKSLdSfLT0Fct83ds4r5LOvqzxklgvgPosqWIeGj9Z+LqNkHymvJfjeP+JVDj3/AJ1605yDXk3xw50mH6n+dbcO6Y6ET2cIl7eJ4zF0qeoYugqev2B7H29NqMUg4+8TgUnQYIx71JDDLcTLDCpZ2IUL6k17Rbfss+ObnwL/AMJgsDl9m8W+3kitaeHlVWhw47M8Nl0l9Zmo32PFApY/J1PQV7v+z9+zVrHxRn/tPVIXt9LQH5mX73HH61e/Z5/Zm1jx7rSal4jspLfTLaTL7xjfg/8A1q+zvHPjnwT8B/BXlRiCHyItsUS4yxxXr4HLo8nta2x8FxPxhUVRYDKveqy6rofn18dPgxqHwi8QiwkkElpOT5MnHzAdeO1eY46DORXc/Fn4qa38U/EUmrapKRAHPkx/3BXC8keory8T7P2j9nsfdZOsX9Sh9dd6ltRH6V6H+zf/AMlq0D/ff+VeeP0r0P8AZv8A+S16B/vP/KrwS/fROTiPXL6nofqfbHMMf+4KkJxUdt/qI/8AdH8qlr71fCfyrW+J+oUgHNLRVGYhGe9KRmiigApPmpaT5qAFPtSDPelPtSDPegBaMiijAoATrR0paKACiiigAooooAMVzvxAA/4RLUuf+WDfyNdDj3rnviAMeEtSP/TBv5Gsq2kGY19KTPzJH/H1cf8AXeT/ANCNWR1FV1/4+rn/AK7yf+hGrA7V+d1fjZ+UV9arFP6Gvoz9mr462/hMxeEteVFs3O2ObAG3/GvnPHG3OPUUqM8Tb0Yqy8hga0wteeHmpwNcJip4OoqkT9RZ9S8M2FhJ4ldrdUZN5m4yRj1r4z+Pvx+v/Gd5LoegzNHpsRKsVb79eb3XxX8Y3vhmPwrPqsn2KIYxnkj61xuSSWLEnv716eLzV4iHJT0PYx+ePE0/Z0lbuKxLEsxJJ9aKQUA5rxNz53fcQ96ydd/48ZPpWse9ZOu/8eMn0r0Mq/3uHqVHc5Sz+4KuLVSy+4PqatrX9PYbWhG/ZClJ82gobDAgYAORX1v+yl+0ZZ6EsPgfxN5cUBIWGUgDFfJBA4+bOf0p0c8tvIkkLFGQ5BBwc1jj8DTzCl7OR04HFTwVX2iP1y1bW/CfhnTJvFVw9rFGyeY0wAy3HFfAn7Rf7ReqfEnVZdH0e4eLSIWKqqnHmV57rfxi8ca94atvCmoavLJZW4KhemR9a4jcxADHNeNluQrCy9rX1fQ9bMM6liV7Ojog5PJPWgZ70c0gz3r6bW10fP2V7MG61Po//ITh+tQN1qfR/wDkJw/WkzDE/wAFnrdgcxJ9BV/PSqFgMRJ9BV/0qon53W+MVSVYSDqpyPevpH4BfGWyihh8K63sjI+WKQ8Bq+bQOPX29KfBPJbyLNBKUKHII4INcOY4Cnj6bpz3PTyXN6mT4lVobdT9BNa1vw74Z06bW53hhTbuLgD5vSvkL4s/FvUfHWoyW9vM0dhGxEag9a5zXPiJ4m1/TLfS9Qv3eCEFQOma5gf7TV5eVZEsHP2lfWXQ+gz/AIulmUVRw/ux6hnNFFFfSHw71dxDWh4OP/FT2n+/Wea0PB3/ACM9p/v15uaf7tI93h7/AJGEPU+x9J/49ov9xf5VqLWXpP8Ax7Rf7i/yrUWvyifxM/pKl8C9Bs8azRPC4yJFKt9CK/PX9sD9ljV9H1G5+IPhKOW4tpG8yaMEkx/jX6Gjhsk9aq6lplpq1nLY6hAs8MylWVhkEVJZ+MngnwV43+KviK28MWSXVy6vsO8kiMd+tfp9+z1+zz4d+Dfh6FRbRy6pMgM9wV5zXVeBfgt4E+Ht/d6j4d0aK3mum3O/U59s13g65z06UAAAPNAwe1LSAY70ANk/1Zryv42f8i3J+H869Uk/1Zryv42f8i3J+H86+f4n/wCRZUPXyP8A32B4ta/dH0q6eoqla/dH0q6eor+Xq/xM/caX8NDomMUqP3Qgivo74RfFCx1i0j0PVCkdyg2qT0YV83H72c/Sp7O9ubC4jubSUxOhySK9zh7PquR1lUirxe6PJznKaebUeSWjWzPsbxJ4k0Pwfpcl6/lJxkBcDJr5b8deOdQ8X6k8s07CIE+XGOgFUfEPi7WfEpRdSu2kSMYVegFYoO3kHr39K9niXi6ebr2ND3YHnZJw5HLf3tb3ph0FA5FIOuKOp4r4Y+pW42Toa8m+N/8AyCYfqa9ak6V5L8cP+QVF9TXucPf79A6MJ/GR41D92phyKhh+7Uw4r9etdWZ9vT1SZa02/m028hv7fG+FwwyM5wa/Q39mv9obQfH+jw+GtcaGHUIkCbWxh/6V+dJycnOCa0dB1/VPDmpRappFy8E8JyCprvwWM+pvyPm+JuHKfENDlbtNbM/Ur4n/ABP8H/CDw3Les0ETlSYokABY/hX5vfFf4seIfijr8+p6lcP5Ic+VDu4UVn+OviX4q+IN0s/iHUZJ/KUCNScDp6VynGPQ1vjsyeI92HwnmcK8H08kXtsR71TuLRRRXk+h92rX0GSdK9D/AGbv+S16B/vP/KvPJfu16H+zd/yWrQP99/5V2YP+NE8LiPTL6iXY/VG34gj/ANwfyqSo4P8AUx/7g/lUlfexfuo/lOprNt92FAGKCcUVRmJjnNB6UtFACHpS0h6UtABRRRQAUUUUAFFFFABRRRQAUUUUAGMsR61zvxAwPCGp5/54N/I10OTuA9Ky/E2ntquh3lggy0sLAfXBqKq5oMyrq9Nn5eJzdXBzx50n/oRqyvI54Bq/4q8N6l4U8RXuk6payRPHMxUlTggknrWasqbfvZPpX53WpyjN6H5Xiaco1XoS0UzzV9aTzU9RWXJNdDD2c+iJKTB9ab5qetIZU7mhwl0QOnN7okoqMyoO4pfNX1/Wjkl2Dkl2HHpWVrv/AB5S/StEypjiszXHU2UmPSvRyqLWLhddSowl2OUs87B9aueuTVKzYBOfWrXmJzzX9M4acPZRbfRBKEnokSYPrRg+tM80daXzV9a256b1TJ5Ki0aHUUzzEHejzEPWhVV3B02ug/B9aMH1pvmrSeatNVIN2uHsprWw5j2qxo3Opw896qmRc1Y0ZgdVi+tJzj3McTCXsXoeuaef3S89hV8YwOazrB1ES5PYVd81OOaqM49z88rUanN8LJKKZ5y+opfMT+8Kp1ILVsyWHqb8rHUUzzk9R+dHmp6inzR6MPYztqmPopnnJ6j86POT1H50c8e4OjU/lY5ulaPg8Z8T2f8Av1lGZDxkVp+D2U+J7PB/jrzcznF4eVme5w/TmsfBtM+yNJ/49ov9xf5VqD2NYulX1oLeMGZPuD+IelaI1Cz7zx/99CvyqfxM/o+l8CLLUo+tVf7Qs/8AnvH/AN9Cg6hZ9p4/++hUllnB9aMH1qr/AGhZ/wDPeP8A76o/tCz/AOe8f/fVAFoYoOKrf2hZf890/wC+hR/aFl/z3T/voUATy/dxmvK/jdn/AIRyTnuP516W+oWewkTx/wDfQrzD403EEvh6QJIrHjgHPevA4mTeW1Ej1skaWNg2eN2h4H0q4elUbWRNo57Va82PH3hX8w1qNTmfus/b6dSHs1qSUH61H5sfrR5sfZh+dZOjWbvys09pB63Hk89aUnio/MT+8KPNTGMj86FRqr7LEqkU7XHnpQtM8yP1A/GgSx+o/On7Gp/KxKpC+4rk7SDXk3xw/wCQVF9T/OvV3ljKk5FeTfG8g6XDj1P869zh6lNY6LaZ0YWpD2y1PHIcham5x1qCJuKl3DGK/XeSUldI+2hXpctnJfePopu8elIXHalyN6NF+2pS2kvvHUtM3il3ijkklZIft6ezkvvHUU3eKTeKfI3okL29GWikr+oPggdh6V6J+zfx8a9Ay3LO/wCHFedFwScjPpXt/wCyR4B1rX/ihY67HZyCz09iZJGXA5HFdmChL2sdD5zijF06WXT95bH6S25P2eL/AHR/KpabEu2NU/ugCnGvu0vdR/LtWXNNpAelIBigHNLTICiiigAooooAKRaWigAopcH0NGD6GgBo6UtAVsdDS4PoaAEowKXB9DRg+hoASilwfQ0bT6GgBoPFID+tP2N/dP5Umw56Gl7z0YWvuch4u+Ffgfxud3iLRI7lu7A7T+YrlG/Zb+C+Qf8AhFj/AN/mr1raR2NG3sQR+FYywtOW6OWeEoTeqPJf+GW/gvjJ8Lf+R2o/4Zb+C2M/8It/5HavWtr47/lRtOO/5VH1Sl/KT9Rw/Y8lH7LnwWH/ADK//kZqP+GW/gt/0K//AJGavW9rf3T+VG1v7p/Kj6pS/lD6jh/5TyQ/sufBU/8AMrn/AL/NR/wy78F/+hW/8jtXrWxvQ/lRhvf8qHhKP8ofUcP/ACnkv/DLnwXzz4WP/f8Aakk/ZX+Ckow3hXI/67NXrZR+pB/KlKsRwD+VXDD0ou6QfUcP/KeM/wDDI3wJ/wChR/8AI7Uf8Mi/Ar/oUv8AyO1eyhWx0P5UYbP3TXorG10rKTH9Rw/8p4z/AMMi/Av/AKFL/wAjtS/8Mi/Ar/oUv/I7V7LsPoaNh9DT+u4j+Zh9Rw/8p41/wyL8Cv8AoUv/ACO1H/DI3wK/6FL/AMjtXsuG9D+VGG9DR9exH8zD6jh/5Txr/hkb4Ff9Cj/5Hag/si/Ar/oUf/I7V7LsPoaNh9DR9dxH8zD6jh/5Txo/sjfAwAf8UlwP+m7VLbfsn/A+1mE9t4Sww/6btXsAQg5IODQVPoQPpR9er/zMmWX0JKzieZ/8M6fCYdPD2P8Atq1H/DO3wn/6F8/9/Wr0zBHY0YJ7H8qP7Qr/AMzOZ5LgnvBHmZ/Z1+FA/wCZfP8A39agfs6/Cg/8y+f+/rV6YVbPQ/lQA3ofyp/X6/8AMw/sTBfyI8z/AOGdvhP/ANC8f+/rUf8ADO3wn/6F4/8Af1q9M5/umjn+6aX16v8AzMP7EwX/AD7R5of2dPhT/wBC8f8Av81N/wCGdPhV/wBC8f8Av61emkE/wGk2n+4aax9f+Zg8jwT+wjzT/hnP4UFiP+EePP8A01arFj8Avhhp1yt3Z6BslTofNNeiEEjaSQfpS7Tx8pI7molja8tHJmlPKMHSd1BXOVHwz8I9rBx/21NH/CtPCX/PjJ/39aup2n+6aNp/umuZu56KVlY5b/hWnhH/AJ8ZP+/rUf8ACtPCP/PjJ/39aup2n+6aNh7KaBnLf8K08Jf8+En/AH9aj/hWnhL/AJ8JP+/rV1JDd0NADdkNAHLH4a+Ev+fCQ/8AbVqT/hWnhP8A58JP+/zV1O0/3DRtP9w0Acv/AMK08In5Rp8mP+uzVXvfhP4Hvo/Ju9LZl95WNdkEI4OfypMMOCpIrKrRjiI8s1oVCq6UrxPPj8Cvhz/DouP+2ho/4UV8Of8AoDH/AL+GvQQpH8JoCE/wGvLWQZfJ3nTR3vM8Sl7s39558fgV8Ou2jH/v6aT/AIUV8O/+gN/5FNehFTn7po2kdFNP+wMv/wCfaJ/tTF/zv7zz7/hRfw7/AOgMf+/rUn/Civh3/wBAb/yK1eh4/wBk0Y/2TR/YGX/8+0P+1MX/ADv7zz0/Ar4ddtGP/f00n/Ci/h0P+YL/AORGr0LaT2NJtP8AdNC4fwFv4aB5pi/53955/wD8KL+HWMHRj/38NZ2sfs2/CfXIfJ1PQPNT08xq9SKnGMGja4GApAqqWTYKjK6poazXFr7b+88U/wCGP/gX28J/+Rmo/wCGP/gZ/wBCn/5Hava9r/5FG1/Su76rSX2TX+2sb/z8f3s8UP7IHwK7+Ff/ACM1A/ZA+BX/AEKn/kZq9rCnupoKHspo+q0v5Q/trHf8/X97PFP+GPvgX/0Kf/kZqP8Ahj/4Gf8AQp/+R2r2sq3YGja/p+lH1Wl/KH9tY7/n6/vZ4mf2QPgX28K/+RmpR+x/8CyP+RU/8jNXtW1v7hpdpHRTR9Vpdg/trHf8/H97PF4v2QvgdG4b/hE+VOR++avSvCvgnw54LshYeH9MjtYlGPlHP51vBTkkgk+lCq3J2EVUcPGHQwrZlisQuWrNteoCil2n+6fyo2n0NbX6HBs7oTpRS7T6GjafQ0AJSYFO2n0NGD6GgBM4oo2k9jS7T6GgBKM5pdp9DSbSOxoA2tq/3R+VG1f7o/KlooATav8AdH5UbV/uj8qWigBNq/3R+VG1f7o/KlooATav90flRtX+6PypaKAGlR2UflSbB6CnfjSHPehXFZMNqjsPypML/dFKtLkdKA2EwvoKMJ6D8qNvvRt96Bi/L6CjC+gpNvvRt96A0FwPQUbV/uj8qTHvRt96QC4X0FJhfQUbfejb70wDC/3R+VGF/uj8qXHSjHSgBML/AHR+VGF/uj8qXHSjHSkGgmF/uj8qML/dH5UuOlGOlAaCYX+6Pyowv90flS46UY96YaBtX+6PypNo/uj8qdSZFAtRNq/3RRgDHyilyKTbS1GLtX+6Pyo2r/dH5UYo20C0Dav90flRtX+6Pyo20baA0Dav90flRtX+6PypNvvS7aY9g2r/AHR+VN2+wp9FK7Fa4m1f7o/Kjav90flS0UxibV/uj8qNq/3R+VLRQAm1f7o/Kjav90flS0UAJtX+6Pyo2r/dH5UtFACbV7gflSYT0H5U6kyKV2hbjSF7AUYHoKfRRa4xuxfQUbF9BTqKYDdi+go2L6D8qdRQAzavt+VKFX0FKDmlpagN2p3Ao2r/AHRTqTFMWobV/uj8qNq/3R+VGKNtINA2r/dH5UbV/uj8qNtG2gNA2r/dH5UbV/uj8qNtG2gNA2r/AHR+VG1f7o/KjbRtoDQQKOuBS7V9BR+NLRqwGkKP4RS7V/uj8qWm7fejYYu1f7o/Kjav90flS0UwE2r/AHR+VG1f7o/KlooATav90flRtX+6PypaKAE2r/dH5UbV/uj8qWigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9k=\"}"); -} - -var socket_ot; - -function ot_open() { - socket_ot = new_ws(get_appropriate_ws_url(""), "dumb-increment-protocol"); - - console.log("ot_open"); - - try { - socket_ot.onopen = function() { - document.getElementById("ot_statustd").style.backgroundColor = - "#40ff40"; - document.getElementById("ot_status").innerHTML = - " websocket connection opened
" + - san(socket_di.extensions); - document.getElementById("ot_open_btn").disabled = true; - document.getElementById("ot_close_btn").disabled = false; - document.getElementById("ot_req_close_btn").disabled = false; - console.log("ot_open.onopen"); - }; - - socket_ot.onclose = function(e){ - document.getElementById("ot_statustd").style.backgroundColor = - "#ff4040"; - document.getElementById("ot_status").textContent = - " websocket connection CLOSED, code: " + e.code + - ", reason: " + e.reason; - document.getElementById("ot_open_btn").disabled = false; - document.getElementById("ot_close_btn").disabled = true; - document.getElementById("ot_req_close_btn").disabled = true; - }; - } catch(exception) { - alert("

Error" + exception); - } -} - -/* browser will close the ws in a controlled way */ -function ot_close() { - socket_ot.close(3000, "Bye!"); -} - -/* we ask the server to close the ws in a controlled way */ -function ot_req_close() { - socket_ot.send("closeme\n"); -} - -var socket_lm; -var pending = ""; - -function lm_timer_handler(ev) { - socket_lm.send(pending); - pending=""; -} - -/* lws-mirror protocol */ - -var down = 0; -var no_last = 1; -var last_x = 0, last_y = 0; -var ctx; -var color = "#000000"; -var lm_timer; - -function ev_mousemove (ev) { - var x, y; - - if (ev.offsetX) { - x = ev.offsetX; - y = ev.offsetY; - } else { - x = ev.layerX - offsetX; - y = ev.layerY - offsetY; - - } - - if (!down) - return; - if (no_last) { - no_last = 0; - last_x = x; - last_y = y; - return; - } - pending = pending + "d " + color + " " + last_x + " " + last_y + - " " + x + " " + y + ";"; - - if (pending.length > 400) { - socket_lm.send(pending); - clearTimeout(lm_timer); - pending = ""; - } else - lm_timer = setTimeout(lm_timer_handler, 1); - - last_x = x; - last_y = y; -} - -function ev_mousedown (ev) { - down = 1; -} - -function ev_mouseup(ev) { - down = 0; - no_last = 1; -} - - -function ws_open_mirror() -{ - socket_lm = new_ws(get_appropriate_ws_url("?mirror=" + mirror_name), - "lws-mirror-protocol"); - try { - socket_lm.onopen = function() { - document.getElementById("wslm_statustd").style.backgroundColor = - "#40ff40"; - document.getElementById("wslm_status").innerHTML = - " websocket connection opened
" + - san(socket_lm.extensions); - lws_gray_out(false); - }; - - socket_lm.onmessage =function got_packet(msg) { - j = msg.data.split(";"); - var f = 0; - while (f < j.length - 1) { - i = j[f].split(" "); - if (i[0] === "d") { - ctx.strokeStyle = i[1]; - ctx.beginPath(); - ctx.moveTo(+(i[2]), +(i[3])); - ctx.lineTo(+(i[4]), +(i[5])); - ctx.stroke(); - } - if (i[0] === "c") { - ctx.strokeStyle = i[1]; - ctx.beginPath(); - ctx.arc(+(i[2]), +(i[3]), +(i[4]), 0, Math.PI*2, true); - ctx.stroke(); - } - - f++; - } - }; - - socket_lm.onclose = function(){ - document.getElementById("wslm_statustd").style.backgroundColor = - "#ff4040"; - document.getElementById("wslm_status").textContent = - " websocket connection CLOSED "; - lws_gray_out(true,{"zindex":"499"}); - }; - } catch(exception) { - alert("

Error" + exception); - } - - var canvas = document.createElement("canvas"); - canvas.height = 300; - canvas.width = 480; - ctx = canvas.getContext("2d"); - - document.getElementById("wslm_drawing").appendChild(canvas); - - canvas.addEventListener("mousemove", ev_mousemove, false); - canvas.addEventListener("mousedown", ev_mousedown, false); - canvas.addEventListener("mouseup", ev_mouseup, false); - - offsetX = offsetY = 0; - element = canvas; - if (element.offsetParent) { - do { - offsetX += element.offsetLeft; - offsetY += element.offsetTop; - element = element.offsetParent; - } while (element); - } -} - -function update_color() { - color = document.getElementById("color").value; -} - -/* stuff that has to be delayed until all the page assets are loaded */ - -window.addEventListener("load", function() { - - lws_gray_out(true,{"zindex":"499"}); - - document.getElementById("file").onchange = check_file; - document.getElementById("offset").onclick = reset; - document.getElementById("junk").onclick = junk; - document.getElementById("color").onclick = update_color; - document.getElementById("ot_open_btn").onclick = ot_open; - document.getElementById("ot_close_btn").onclick = ot_close; - document.getElementById("ot_req_close_btn").onclick = ot_req_close; - document.getElementById("pmd").onclick = on_pmd; - - var transport_protocol = ""; - - if ( performance && performance.timing.nextHopProtocol ) { - transport_protocol = performance.timing.nextHopProtocol; - } else if ( window.chrome && window.chrome.loadTimes ) { - transport_protocol = window.chrome.loadTimes().connectionInfo; - } else { - - var p = performance.getEntriesByType("resource"); - for (var i=0; i < p.length; i++) { - var value = "nextHopProtocol" in p[i]; - if (value) - transport_protocol = p[i].nextHopProtocol; - } - } - - console.log("transport protocol " + transport_protocol); - - if (transport_protocol === "h2") - document.getElementById("transport").innerHTML = - ""; - - BrowserDetect.init(); - - document.getElementById("brow").textContent = " " + - BrowserDetect.browser + " " + BrowserDetect.version + " " + - BrowserDetect.OS +" "; - - document.getElementById("number").textContent = - get_appropriate_ws_url(mirror_name); - - /* create the ws connections back to the server */ - - ws_open_dumb_increment(); - ws_open_status(); - ws_open_mirror(); - -}, false); - -}()); Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/wss-over-h2.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/mount-origin/wss-over-h2.png differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-demos/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-demos/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -# lws minimal http server eventlib demos - -This demonstrates a slightly more complex demo that can use -any of the event loops (it defaults to poll) - -It uses statically included plugins to provide the lws test server functions - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 ---uv|Use the libuv event library (lws must have been configured with `-DLWS_WITH_LIBUV=1`) ---event|Use the libevent library (lws must have been configured with `-DLWS_WITH_LIBEVENT=1`) ---ev|Use the libev event library (lws must have been configured with `-DLWS_WITH_LIBEV=1`) - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-eventlib-demos -[2018/03/04 09:30:02:7986] USER: LWS minimal http server-eventlib-demos | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -project(lws-minimal-http-server-eventlib-foreign) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-eventlib-foreign) -set(SRCS minimal-http-server-eventlib-foreign.c) - - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - - - -CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_LIBUV)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBUV) -CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_LIBEVENT)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBEVENT) -CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_LIBEV)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBEV) -CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_GLIB)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_GLIB) - - -if (LWS_WITH_LIBUV) - set(extralibs ${extralibs} uv) -endif() -if (LWS_WITH_LIBEVENT) - set(extralibs ${extralibs} event) -endif() -if (LWS_WITH_LIBEV) - set(extralibs ${extralibs} ev) -endif() -if (LWS_WITH_GLIB) - set(LWS_GLIB_INCLUDE_DIRS CACHE PATH "Path to the glib include directory") - set(LWS_GLIB_LIBRARIES CACHE PATH "Path to the glib library") - include (FindPkgConfig) - if (NOT GLIB_FOUND) - find_path(GLIB_INCLUDE_DIRS NAMES glib-2.0/glib.h) - find_library(GLIB_LIBRARIES NAMES glib-2.0) - if(GLIB_INCLUDE_DIRS AND GLIB_LIBRARIES) - set(GLIB_FOUND 1) - endif() - if (GLIB_INCLUDE_DIRS) - set(GLIB_INCLUDE_DIRS "${GLIB_INCLUDE_DIRS}/glib-2.0") - endif() - endif() - PKG_SEARCH_MODULE(LWS_GLIB2 glib-2.0) - if (LWS_GLIB2_FOUND) - list(APPEND GLIB_INCLUDE_DIRS "${LWS_GLIB2_INCLUDE_DIRS}") - endif() - message("glib include dir: ${GLIB_INCLUDE_DIRS}") - message("glib libraries: ${GLIB_LIBRARIES}") - include_directories("${GLIB_INCLUDE_DIRS}") - set(extralibs ${extralibs} ${GLIB_LIBRARIES}) -endif() - -message("Extra libs: ${extralibs}") - -if (NOT LWS_WITH_LIBUV AND NOT LWS_WITH_LIBEVENT AND NOT LWS_WITH_LIBEV AND NOT LWS_WITH_GLIB) - set(requirements 0) -endif() - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared ${extralibs}) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets ${extralibs}) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,493 +0,0 @@ -/* - * lws-minimal-http-server-eventlib-foreign - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws that - * uses a libuv event loop created outside lws. It shows how lws can - * participate in someone else's event loop and clean up after itself. - * - * You choose the event loop to work with at runtime, by giving the - * --uv, --event or --ev switch. Lws has to have been configured to build the - * selected event lib support. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -struct lws_context_creation_info info; -static struct lws_context *context; -static int lifetime = 5, reported; - -static void foreign_timer_service(void *foreign_loop); - -enum { - TEST_STATE_CREATE_LWS_CONTEXT, - TEST_STATE_DESTROY_LWS_CONTEXT, - TEST_STATE_EXIT -}; - -static int sequence = TEST_STATE_CREATE_LWS_CONTEXT; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -static void -signal_cb(int signum) -{ - lwsl_notice("Signal %d caught, exiting...\n", signum); - - switch (signum) { - case SIGTERM: - case SIGINT: - break; - default: - break; - } - - lws_context_destroy(context); -} - -/* - * The event-loop specific foreign loop code, one set for each event loop lib - * - * Only the code in this section is specific to the event library used. - */ - -#if defined(LWS_WITH_LIBUV) - -static uv_loop_t loop_uv; -static uv_timer_t timer_outer_uv; -static uv_signal_t sighandler_uv; - -static void -timer_cb_uv(uv_timer_t *t) -{ - foreign_timer_service(&loop_uv); -} - -static void -signal_cb_uv(uv_signal_t *watcher, int signum) -{ - signal_cb(signum); -} - -static void -foreign_event_loop_init_and_run_libuv(void) -{ - /* we create and start our "foreign loop" */ - -#if (UV_VERSION_MAJOR > 0) // Travis... - uv_loop_init(&loop_uv); -#endif - uv_signal_init(&loop_uv, &sighandler_uv); - uv_signal_start(&sighandler_uv, signal_cb_uv, SIGINT); - - uv_timer_init(&loop_uv, &timer_outer_uv); -#if (UV_VERSION_MAJOR > 0) // Travis... - uv_timer_start(&timer_outer_uv, timer_cb_uv, 0, 1000); -#else - (void)timer_cb_uv; -#endif - - uv_run(&loop_uv, UV_RUN_DEFAULT); -} - -static void -foreign_event_loop_stop_libuv(void) -{ - uv_stop(&loop_uv); -} - -static void -foreign_event_loop_cleanup_libuv(void) -{ - /* cleanup the foreign loop assets */ - - uv_timer_stop(&timer_outer_uv); - uv_close((uv_handle_t*)&timer_outer_uv, NULL); - uv_signal_stop(&sighandler_uv); - uv_close((uv_handle_t *)&sighandler_uv, NULL); - - uv_run(&loop_uv, UV_RUN_DEFAULT); -#if (UV_VERSION_MAJOR > 0) // Travis... - uv_loop_close(&loop_uv); -#endif -} - -#endif - -#if defined(LWS_WITH_LIBEVENT) - -static struct event_base *loop_event; -static struct event *timer_outer_event; -static struct event *sighandler_event; - -static void -timer_cb_event(int fd, short event, void *arg) -{ - foreign_timer_service(loop_event); -} - -static void -signal_cb_event(int fd, short event, void *arg) -{ - signal_cb((int)(lws_intptr_t)arg); -} - -static void -foreign_event_loop_init_and_run_libevent(void) -{ - struct timeval tv; - - /* we create and start our "foreign loop" */ - - tv.tv_sec = 1; - tv.tv_usec = 0; - - loop_event = event_base_new(); - - sighandler_event = evsignal_new(loop_event, SIGINT, signal_cb_event, - (void*)SIGINT); - - timer_outer_event = event_new(loop_event, -1, EV_PERSIST, - timer_cb_event, NULL); - //evtimer_new(loop_event, timer_cb_event, NULL); - evtimer_add(timer_outer_event, &tv); - - event_base_loop(loop_event, 0); -} - -static void -foreign_event_loop_stop_libevent(void) -{ - event_base_loopexit(loop_event, NULL); -} - -static void -foreign_event_loop_cleanup_libevent(void) -{ - /* cleanup the foreign loop assets */ - - evtimer_del(timer_outer_event); - event_free(timer_outer_event); - evsignal_del(sighandler_event); - event_free(sighandler_event); - - event_base_loop(loop_event, 0); - event_base_free(loop_event); -} - -#endif - -#if defined(LWS_WITH_GLIB) - -#include -#include - -static GMainLoop *loop_glib; -static guint timer_outer_glib; -static guint sighandler_glib; - -static int -timer_cb_glib(void *p) -{ - foreign_timer_service(loop_glib); - return 1; -} - -static void -signal_cb_glib(void *p) -{ - signal_cb(SIGINT); -} - -static void -foreign_event_loop_init_and_run_glib(void) -{ - /* we create and start our "foreign loop" */ - - loop_glib = g_main_loop_new(NULL, 0); - - sighandler_glib = g_unix_signal_add(SIGINT, - G_SOURCE_FUNC(signal_cb_glib), NULL); - - timer_outer_glib = g_timeout_add(1000, timer_cb_glib, NULL); - - g_main_loop_run(loop_glib); -} - -static void -foreign_event_loop_stop_glib(void) -{ - g_main_loop_quit(loop_glib); -} - -static void -foreign_event_loop_cleanup_glib(void) -{ - /* cleanup the foreign loop assets */ - g_source_remove(sighandler_glib); - g_main_loop_unref(loop_glib); -} - -#endif - -#if defined(LWS_WITH_LIBEV) - -static struct ev_loop *loop_ev; -static struct ev_timer timer_outer_ev; -static struct ev_signal sighandler_ev; - -static void -timer_cb_ev(struct ev_loop *loop, struct ev_timer *watcher, int revents) -{ - foreign_timer_service(loop_ev); -} - -static void -signal_cb_ev(struct ev_loop *loop, struct ev_signal *watcher, int revents) -{ - signal_cb(watcher->signum); -} - -static void -foreign_event_loop_init_and_run_libev(void) -{ - /* we create and start our "foreign loop" */ - - loop_ev = ev_loop_new(0); - - ev_signal_init(&sighandler_ev, signal_cb_ev, SIGINT); - ev_signal_start(loop_ev, &sighandler_ev); - - ev_timer_init(&timer_outer_ev, timer_cb_ev, 0, 1); - ev_timer_start(loop_ev, &timer_outer_ev); - - ev_run(loop_ev, 0); -} - -static void -foreign_event_loop_stop_libev(void) -{ - ev_break(loop_ev, EVBREAK_ALL); -} - -static void -foreign_event_loop_cleanup_libev(void) -{ - /* cleanup the foreign loop assets */ - - ev_timer_stop(loop_ev, &timer_outer_ev); - ev_signal_stop(loop_ev, &sighandler_ev); - - ev_run(loop_ev, 0); - ev_loop_destroy(loop_ev); -} - -#endif - -/* this is called at 1Hz using a foreign loop timer */ - -static void -foreign_timer_service(void *foreign_loop) -{ - void *foreign_loops[1]; - - lwsl_user("Foreign 1Hz timer\n"); - - if (sequence == TEST_STATE_EXIT && !context && !reported) { - /* - * at this point the lws_context_destroy() we did earlier - * has completed and the entire context is wholly destroyed - */ - lwsl_user("lws_destroy_context() done, continuing for 5s\n"); - reported = 1; - } - - if (--lifetime) - return; - - switch (sequence++) { - case TEST_STATE_CREATE_LWS_CONTEXT: - /* this only has to exist for the duration of create context */ - foreign_loops[0] = foreign_loop; - info.foreign_loops = foreign_loops; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return; - } - lwsl_user("LWS Context created and will be active for 10s\n"); - lifetime = 11; - break; - - case TEST_STATE_DESTROY_LWS_CONTEXT: - /* cleanup the lws part */ - lwsl_user("Destroying lws context and continuing loop for 5s\n"); - lws_context_destroy(context); - lifetime = 6; - break; - - case TEST_STATE_EXIT: - lwsl_user("Deciding to exit foreign loop too\n"); -#if defined(LWS_WITH_LIBUV) - if (info.options & LWS_SERVER_OPTION_LIBUV) - foreign_event_loop_stop_libuv(); -#endif -#if defined(LWS_WITH_LIBEVENT) - if (info.options & LWS_SERVER_OPTION_LIBEVENT) - foreign_event_loop_stop_libevent(); -#endif -#if defined(LWS_WITH_LIBEV) - if (info.options & LWS_SERVER_OPTION_LIBEV) - foreign_event_loop_stop_libev(); -#endif -#if defined(LWS_WITH_GLIB) - if (info.options & LWS_SERVER_OPTION_GLIB) - foreign_event_loop_stop_glib(); -#endif - break; - default: - break; - } -} - -int main(int argc, const char **argv) -{ - const char *p; - int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server eventlib + foreign loop |" - " visit http://localhost:7681\n"); - - /* - * We prepare the info here, but don't use it until later in the - * timer callback, to demonstrate the independence of the foreign loop - * and lws. - */ - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.pcontext = &context; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - if (lws_cmdline_option(argc, argv, "-s")) { - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - if (lws_cmdline_option(argc, argv, "--uv")) - info.options |= LWS_SERVER_OPTION_LIBUV; - else - if (lws_cmdline_option(argc, argv, "--event")) - info.options |= LWS_SERVER_OPTION_LIBEVENT; - else - if (lws_cmdline_option(argc, argv, "--ev")) - info.options |= LWS_SERVER_OPTION_LIBEV; - else - if (lws_cmdline_option(argc, argv, "--glib")) - info.options |= LWS_SERVER_OPTION_GLIB; - else { - lwsl_err("This app only makes sense when used\n"); - lwsl_err(" with a foreign loop, --uv, --event, --glib, or --ev\n"); - - return 1; - } - - lwsl_user(" This app creates a foreign event loop with a timer +\n"); - lwsl_user(" signalhandler, and performs a test in three phases:\n"); - lwsl_user("\n"); - lwsl_user(" 1) 5s: Runs the loop with just the timer\n"); - lwsl_user(" 2) 10s: create an lws context serving on localhost:7681\n"); - lwsl_user(" using the same foreign loop. Destroy it after 10s.\n"); - lwsl_user(" 3) 5s: Run the loop again with just the timer\n"); - lwsl_user("\n"); - lwsl_user(" Finally close only the timer and signalhandler and\n"); - lwsl_user(" exit the loop cleanly\n"); - lwsl_user("\n"); - - /* foreign loop specific startup and run */ - -#if defined(LWS_WITH_LIBUV) - if (info.options & LWS_SERVER_OPTION_LIBUV) - foreign_event_loop_init_and_run_libuv(); -#endif -#if defined(LWS_WITH_LIBEVENT) - if (info.options & LWS_SERVER_OPTION_LIBEVENT) - foreign_event_loop_init_and_run_libevent(); -#endif -#if defined(LWS_WITH_LIBEV) - if (info.options & LWS_SERVER_OPTION_LIBEV) - foreign_event_loop_init_and_run_libev(); -#endif -#if defined(LWS_WITH_GLIB) - if (info.options & LWS_SERVER_OPTION_GLIB) - foreign_event_loop_init_and_run_glib(); -#endif - - lws_context_destroy(context); - - /* foreign loop specific cleanup and exit */ - -#if defined(LWS_WITH_LIBUV) - if (info.options & LWS_SERVER_OPTION_LIBUV) - foreign_event_loop_cleanup_libuv(); -#endif -#if defined(LWS_WITH_LIBEVENT) - if (info.options & LWS_SERVER_OPTION_LIBEVENT) - foreign_event_loop_cleanup_libevent(); -#endif -#if defined(LWS_WITH_LIBEV) - if (info.options & LWS_SERVER_OPTION_LIBEV) - foreign_event_loop_cleanup_libev(); -#endif -#if defined(LWS_WITH_GLIB) - if (info.options & LWS_SERVER_OPTION_GLIB) - foreign_event_loop_cleanup_glib(); -#endif - - lwsl_user("%s: exiting...\n", __func__); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ - - - - - - -
- - Hello from the minimal http server eventlib foreign loop example. -
- The timer messages in the console are coming from
- a timer on the event library lib loop set up before the lws context
- started using it. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -# lws minimal http server eventlib foreign - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 ---uv|Use the libuv event library (lws must have been configured with `-DLWS_WITH_LIBUV=1`) ---event|Use the libevent library (lws must have been configured with `-DLWS_WITH_LIBEVENT=1`) ---ev|Use the libev event library (lws must have been configured with `-DLWS_WITH_LIBEV=1`) - -Notice libevent and libev cannot coexist in the one library. But all the other combinations are OK. - -x|libuv|libevent|libev ----|---|---|--- -libuv|-|OK|OK -libevent|OK|-|no -libev|OK|no|- - -This demonstrates having lws take part in a libuv loop owned by -something else, with its own objects running in the loop. - -Lws can join the loop, and clean up perfectly after itself without -leaving anything behind or making trouble in the larger loop, which -does not need to stop during lws creation or destruction. - -First the foreign loop is created with a 1s timer, and runs alone for 5s. - -Then the lws context is created inside the timer callback and runs for 10s... -during this period you can visit http://localhost:7681 for normal lws -service using the foreign loop. - -After the 10s are up, the lws context is destroyed inside the foreign loop -timer. The foreign loop runs alone again for a further 5s and then -exits itself. - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-eventlib-foreign -[2018/03/29 12:19:31:3480] USER: LWS minimal http server eventlib + foreign loop | visit http://localhost:7681 -[2018/03/29 12:19:31:3724] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/03/29 12:19:31:3804] NOTICE: Using foreign event loop... -[2018/03/29 12:19:31:3938] USER: Foreign 1Hz timer -[2018/03/29 12:19:32:4011] USER: Foreign 1Hz timer -[2018/03/29 12:19:33:4024] USER: Foreign 1Hz timer -^C[2018/03/29 12:19:33:8868] NOTICE: Signal 2 caught, exiting... -[2018/03/29 12:19:33:8963] USER: main: starting exit cleanup... -[2018/03/29 12:19:33:9064] USER: main: lws context destroyed: cleaning the foreign loop -[2018/03/29 12:19:33:9108] USER: main: exiting... -``` - -Visit http://localhost:7681 - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -project(lws-minimal-http-server-eventlib-smp) -cmake_minimum_required(VERSION 2.8) -include(CheckIncludeFile) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-eventlib-smp) -set(SRCS minimal-http-server-eventlib-smp.c) - -MACRO(require_pthreads result) - CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) - if (NOT LWS_HAVE_PTHREAD_H) - if (LWS_WITH_MINIMAL_EXAMPLES) - set(result 0) - else() - message(FATAL_ERROR "threading support requires pthreads") - endif() - endif() -ENDMACRO() - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_pthreads(requirements) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared pthread) - add_dependencies(${SAMP} websockets_shared pthread) - else() - target_link_libraries(${SAMP} websockets pthread) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/minimal-http-server-eventlib-smp.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/minimal-http-server-eventlib-smp.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/minimal-http-server-eventlib-smp.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/minimal-http-server-eventlib-smp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,162 +0,0 @@ -/* - * lws-minimal-http-server-eventlib-smp - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http[s] server that can work with any of the - * supported event loop backends, or the default poll() one. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -#include - -#define COUNT_THREADS 8 - -static struct lws_context *context; -static volatile int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void *thread_service(void *threadid) -{ - while (lws_service_tsi(context, 10000, - (int)(lws_intptr_t)threadid) >= 0 && - !interrupted) - ; - - pthread_exit(NULL); - - return NULL; -} - -void signal_cb(void *handle, int signum) -{ - interrupted = 1; - - switch (signum) { - case SIGTERM: - case SIGINT: - break; - default: - lwsl_err("%s: signal %d\n", __func__, signum); - break; - } - lws_context_destroy(context); -} - -void sigint_handler(int sig) -{ - signal_cb(NULL, sig); -} - -int main(int argc, const char **argv) -{ - pthread_t pthread_service[COUNT_THREADS]; - struct lws_context_creation_info info; - const char *p; - void *retval; - int n, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server eventlib SMP | visit http://localhost:7681\n"); - lwsl_user(" [-s (ssl)] [--uv (libuv)] [--ev (libev)] [--event (libevent)]\n"); - lwsl_user("WARNING: Not stable, under development!\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.pcontext = &context; - info.signal_cb = signal_cb; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - if ((p = lws_cmdline_option(argc, argv, "-t"))) { - info.count_threads = atoi(p); - if (info.count_threads < 1 || info.count_threads > LWS_MAX_SMP) - return 1; - } else - info.count_threads = COUNT_THREADS; - - if (lws_cmdline_option(argc, argv, "-s")) { - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - if (lws_cmdline_option(argc, argv, "--uv")) - info.options |= LWS_SERVER_OPTION_LIBUV; - else - if (lws_cmdline_option(argc, argv, "--event")) - info.options |= LWS_SERVER_OPTION_LIBEVENT; - else - if (lws_cmdline_option(argc, argv, "--ev")) - info.options |= LWS_SERVER_OPTION_LIBEV; - else - if (lws_cmdline_option(argc, argv, "--glib")) - info.options |= LWS_SERVER_OPTION_GLIB; - else - signal(SIGINT, sigint_handler); - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - lwsl_notice(" Service threads: %d\n", lws_get_count_threads(context)); - - /* start all the service threads */ - - for (n = 0; n < lws_get_count_threads(context); n++) - if (pthread_create(&pthread_service[n], NULL, thread_service, - (void *)(lws_intptr_t)n)) - lwsl_err("Failed to start service thread\n"); - - /* wait for all the service threads to exit */ - - while ((--n) >= 0) - pthread_join(pthread_service[n], &retval); - - lwsl_notice("%s: calling external context destroy\n", __func__); - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ - - - - - - -
- - Hello from the minimal http server event loop example. -
- You can confirm the 404 page handler by going to this - nonexistant page. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-eventlib-smp/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-eventlib-smp/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -# lws minimal http server eventlib - -WARNING: this is under development, it's not stable. - -This demonstrates a minimal http server that can use any of the event libraries - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --t |Number of threads to use. ---uv|Use the libuv event library (lws must have been configured with `-DLWS_WITH_LIBUV=1`) ---event|Use the libevent library (lws must have been configured with `-DLWS_WITH_LIBEVENT=1`) ---ev|Use the libev event library (lws must have been configured with `-DLWS_WITH_LIBEV=1`) - -## build - -lilbwebsockets must have been built with `LWS_MAX_SMP` greater than 1 to use -multiple threads. - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-eventlib-smp -[2018/03/04 09:30:02:7986] USER: LWS minimal http server-eventlib | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-minimal-http-server-form-get) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-form-get) -set(SRCS minimal-http-server-form-get.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/minimal-http-server-form-get.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/minimal-http-server-form-get.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/minimal-http-server-form-get.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/minimal-http-server-form-get.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,148 +0,0 @@ -/* - * lws-minimal-http-server-form-get - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http server that performs a form GET with a couple - * of parameters. It dumps the parameters to the console log and redirects - * to another page. - */ - -#include -#include -#include - -static int interrupted; - -static const char * param_names[] = { - "text1", - "send" -}; - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE], - *start = &buf[LWS_PRE], *p = start, - *end = &buf[sizeof(buf) - 1]; - const char *val; - int n; - - switch (reason) { - case LWS_CALLBACK_HTTP: - - if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) - /* not a GET */ - break; - lwsl_err("%s: %s\n", __func__, (const char *)in); - if (strcmp((const char *)in, "/form1")) - /* not our form URL */ - break; - - /* we just dump the decoded things to the log */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(param_names); n++) { - val = lws_get_urlarg_by_name(wsi, param_names[n], - (char *)buf, sizeof(buf)); - if (!val) - lwsl_user("%s: undefined\n", param_names[n]); - else - lwsl_user("%s: (len %d) '%s'\n", param_names[n], - (int)strlen((const char *)buf),buf); - } - - /* - * Our response is to redirect to a static page. We could - * have generated a dynamic html page here instead. - */ - - if (lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, - (unsigned char *)"after-form1.html", - 16, &p, end) < 0) - return -1; - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static struct lws_protocols protocols[] = { - { "http", callback_http, 0, 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -/* default mount serves the URL space from ./mount-origin */ - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server GET | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.protocols = protocols; - info.mounts = &mount; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/after-form1.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/after-form1.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/after-form1.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/after-form1.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ - - - - - - -
- - Thanks for posting the form. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ - - - - - - -
- - Hello from the minimal http form GET example. -

- This is a static page served from ./mount-origin/index.html. -

- When you submit the form below, you will see the values of the
- form parameters reported on the console log. -

-

- Type some text:
-
- -
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-get/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-get/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -# lws minimal http server form GET - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-form-get -[2018/03/29 08:29:41:7044] USER: LWS minimal http server form GET | visit http://localhost:7681 -[2018/03/29 08:29:41:7044] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/03/29 08:29:49:8601] USER: text1: (len 4) 'xxxx' -[2018/03/29 08:29:49:8601] USER: send: (len 6) 'Submit' -``` - -Visit http://localhost:7681, submit the form. - -The form parameters are dumped to the log and you are redirected to a different page. diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-minimal-http-server-form-post) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-form-post) -set(SRCS minimal-http-server-form-post.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/minimal-http-server-form-post.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/minimal-http-server-form-post.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/minimal-http-server-form-post.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/minimal-http-server-form-post.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,222 +0,0 @@ -/* - * lws-minimal-http-server-form-post - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http server that performs POST with a couple - * of parameters. It dumps the parameters to the console log and redirects - * to another page. - */ - -#include -#include -#include - -/* - * Unlike ws, http is a stateless protocol. This pss only exists for the - * duration of a single http transaction. With http/1.1 keep-alive and http/2, - * that is unrelated to (shorter than) the lifetime of the network connection. - */ -struct pss { - struct lws_spa *spa; -}; - -static int interrupted, use303; - -static const char * const param_names[] = { - "text1", - "send", -}; - -enum enum_param_names { - EPN_TEXT1, - EPN_SEND, -}; - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - struct pss *pss = (struct pss *)user; - uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE], *start = &buf[LWS_PRE], - *p = start, *end = &buf[sizeof(buf) - 1]; - int n; - - switch (reason) { - case LWS_CALLBACK_HTTP: - - /* - * Manually report that our form target URL exists - * - * you can also do this by adding a mount for the form URL - * to the protocol with type LWSMPRO_CALLBACK, then no need - * to trap LWS_CALLBACK_HTTP. - */ - - if (!strcmp((const char *)in, "/form1")) - /* assertively allow it to exist in the URL space */ - return 0; - - /* default to 404-ing the URL if not mounted */ - break; - - case LWS_CALLBACK_HTTP_BODY: - - /* create the POST argument parser if not already existing */ - - if (!pss->spa) { - pss->spa = lws_spa_create(wsi, param_names, - LWS_ARRAY_SIZE(param_names), 1024, - NULL, NULL); /* no file upload */ - if (!pss->spa) - return -1; - } - - /* let it parse the POST data */ - - if (lws_spa_process(pss->spa, in, (int)len)) - return -1; - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - if (pss->spa && lws_spa_destroy(pss->spa)) - return -1; - break; - - case LWS_CALLBACK_HTTP_BODY_COMPLETION: - - /* inform the spa no more payload data coming */ - - lwsl_user("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); - lws_spa_finalize(pss->spa); - - /* we just dump the decoded things to the log */ - - if (pss->spa) - for (n = 0; n < (int)LWS_ARRAY_SIZE(param_names); n++) { - if (!lws_spa_get_string(pss->spa, n)) - lwsl_user("%s: undefined\n", param_names[n]); - else - lwsl_user("%s: (len %d) '%s'\n", - param_names[n], - lws_spa_get_length(pss->spa, n), - lws_spa_get_string(pss->spa, n)); - } - - if (pss->spa && lws_spa_destroy(pss->spa)) - return -1; - - /* - * Our response is to redirect to a static page. We could - * have generated a dynamic html page here instead. - */ - - if (lws_http_redirect(wsi, use303 ? HTTP_STATUS_SEE_OTHER : - HTTP_STATUS_MOVED_PERMANENTLY, - (unsigned char *)"after-form1.html", - 16, &p, end) < 0) - return -1; - break; - - case LWS_CALLBACK_HTTP_DROP_PROTOCOL: - /* called when our wsi user_space is going to be destroyed */ - if (pss->spa) { - lws_spa_destroy(pss->spa); - pss->spa = NULL; - } - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static struct lws_protocols protocols[] = { - { "http", callback_http, sizeof(struct pss), 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -/* default mount serves the URL space from ./mount-origin */ - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server POST | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.protocols = protocols; - info.mounts = &mount; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - if (lws_cmdline_option(argc, argv, "-s")) { - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - if (lws_cmdline_option(argc, argv, "--303")) { - lwsl_user("%s: using 303 redirect\n", __func__); - use303 = 1; - } - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/after-form1.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/after-form1.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/after-form1.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/after-form1.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ - - - - - - -
- - Thanks for posting the form. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ - - - - - - -
- - Hello from the minimal http POST example. -

- This is a static page served from ./mount-origin/index.html. -

- When you POST the form below, you will see the values of the
- form parameters reported on the console log. -

-

- Type some text:
-
- -
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -# lws minimal http server form POST - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-form-post -[2018/03/29 08:29:41:7044] USER: LWS minimal http server form POST | visit http://localhost:7681 -[2018/03/29 08:29:41:7044] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/03/29 08:29:49:8601] USER: text1: (len 4) 'xxxx' -[2018/03/29 08:29:49:8601] USER: send: (len 6) 'Submit' -``` - -Visit http://localhost:7681, submit the form. - -The form parameters are dumped to the log and you are redirected to a different page. diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-minimal-http-server-form-post-file) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-form-post-file) -set(SRCS minimal-http-server-form-post-file.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/minimal-http-server-form-post-file.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/minimal-http-server-form-post-file.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/minimal-http-server-form-post-file.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/minimal-http-server-form-post-file.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,260 +0,0 @@ -/* - * lws-minimal-http-server-form-post-file - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http server that performs POST with a couple - * of parameters and a file upload, all in multipart (mime) form mode. - * It saves the uploaded file in the current directory, dumps the parameters to - * the console log and redirects to another page. - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * Unlike ws, http is a stateless protocol. This pss only exists for the - * duration of a single http transaction. With http/1.1 keep-alive and http/2, - * that is unrelated to (shorter than) the lifetime of the network connection. - */ -struct pss { - struct lws_spa *spa; /* lws helper decodes multipart form */ - char filename[128]; /* the filename of the uploaded file */ - unsigned long long file_length; /* the amount of bytes uploaded */ - int fd; /* fd on file being saved */ -}; - -static int interrupted; - -static const char * const param_names[] = { - "text1", - "send", -}; - -enum enum_param_names { - EPN_TEXT1, - EPN_SEND, -}; - -static int -file_upload_cb(void *data, const char *name, const char *filename, - char *buf, int len, enum lws_spa_fileupload_states state) -{ - struct pss *pss = (struct pss *)data; - - switch (state) { - case LWS_UFS_OPEN: - /* take a copy of the provided filename */ - lws_strncpy(pss->filename, filename, sizeof(pss->filename) - 1); - /* remove any scary things like .. */ - lws_filename_purify_inplace(pss->filename); - /* open a file of that name for write in the cwd */ - pss->fd = lws_open(pss->filename, O_CREAT | O_TRUNC | O_RDWR, 0600); - if (pss->fd == -1) { - lwsl_notice("Failed to open output file %s\n", - pss->filename); - return 1; - } - break; - case LWS_UFS_FINAL_CONTENT: - case LWS_UFS_CONTENT: - if (len) { - int n; - - pss->file_length += len; - - n = write(pss->fd, buf, len); - if (n < len) { - lwsl_notice("Problem writing file %d\n", errno); - } - } - if (state == LWS_UFS_CONTENT) - /* wasn't the last part of the file */ - break; - - /* the file upload is completed */ - - lwsl_user("%s: upload done, written %lld to %s\n", __func__, - pss->file_length, pss->filename); - - close(pss->fd); - pss->fd = -1; - break; - case LWS_UFS_CLOSE: - break; - } - - return 0; -} - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE], *start = &buf[LWS_PRE], - *p = start, *end = &buf[sizeof(buf) - 1]; - struct pss *pss = (struct pss *)user; - int n; - - switch (reason) { - case LWS_CALLBACK_HTTP: - - /* - * Manually report that our form target URL exists - * - * you can also do this by adding a mount for the form URL - * to the protocol with type LWSMPRO_CALLBACK, then no need - * to trap LWS_CALLBACK_HTTP. - */ - - if (!strcmp((const char *)in, "/form1")) - /* assertively allow it to exist in the URL space */ - return 0; - - /* default to 404-ing the URL if not mounted */ - break; - - case LWS_CALLBACK_HTTP_BODY: - - /* create the POST argument parser if not already existing */ - - if (!pss->spa) { - pss->spa = lws_spa_create(wsi, param_names, - LWS_ARRAY_SIZE(param_names), 1024, - file_upload_cb, pss); - if (!pss->spa) - return -1; - } - - /* let it parse the POST data */ - - if (lws_spa_process(pss->spa, in, (int)len)) - return -1; - break; - - case LWS_CALLBACK_HTTP_BODY_COMPLETION: - - /* inform the spa no more payload data coming */ - - lws_spa_finalize(pss->spa); - - /* we just dump the decoded things to the log */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(param_names); n++) { - if (!lws_spa_get_string(pss->spa, n)) - lwsl_user("%s: undefined\n", param_names[n]); - else - lwsl_user("%s: (len %d) '%s'\n", - param_names[n], - lws_spa_get_length(pss->spa, n), - lws_spa_get_string(pss->spa, n)); - } - - /* - * Our response is to redirect to a static page. We could - * have generated a dynamic html page here instead. - */ - - if (lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, - (unsigned char *)"after-form1.html", - 16, &p, end) < 0) - return -1; - - break; - - case LWS_CALLBACK_HTTP_DROP_PROTOCOL: - /* called when our wsi user_space is going to be destroyed */ - if (pss->spa) { - lws_spa_destroy(pss->spa); - pss->spa = NULL; - } - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static struct lws_protocols protocols[] = { - { "http", callback_http, sizeof(struct pss), 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -/* default mount serves the URL space from ./mount-origin */ - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server POST file | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.protocols = protocols; - info.mounts = &mount; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/after-form1.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/after-form1.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/after-form1.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/after-form1.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ - - - - - - -
- - Thanks for posting the form.
-
- The file you uploaded should have been saved in the current directory. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ - - - - - - -
- - Hello from the minimal http form POST file example. -

- This is a static page served from ./mount-origin/index.html. -

- When you POST the form below, you will see the values of the
- form parameters reported on the console log, and the file will - be uploaded and saved in the current working directory. -

-

- Type some text:
-
-
- Select a file to upload: -   -
-
- -
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-file/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-file/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -# lws minimal http server form POST file - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-form-post-file -[2018/03/29 09:58:30:8800] USER: LWS minimal http server POST file | visit http://localhost:7681 -[2018/03/29 09:58:30:8800] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/03/29 09:58:45:3284] USER: file_upload_cb: upload done, written 2729 to wss-over-h2.png -[2018/03/29 09:58:45:3284] USER: text1: (len 3) 'xxx' -[2018/03/29 09:58:45:3284] USER: send: (len 6) 'Submit' -``` - -Visit http://localhost:7681, select a file to upload and submit the form. - -The file is uploaded and saved in the cwd, the form parameters are dumped to the log and -you are redirected to a different page. diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-lwsac/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-lwsac/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-lwsac/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-lwsac/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-minimal-http-server-form-post-lwsac) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-form-post-lwsac) -set(SRCS minimal-http-server-form-post.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-lwsac/minimal-http-server-form-post.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-lwsac/minimal-http-server-form-post.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-lwsac/minimal-http-server-form-post.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-lwsac/minimal-http-server-form-post.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,217 +0,0 @@ -/* - * lws-minimal-http-server-form-post-lwsac - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http server that performs POST with a couple - * of parameters. It dumps the parameters to the console log and redirects - * to another page. - */ - -#include -#include -#include - -/* - * Unlike ws, http is a stateless protocol. This pss only exists for the - * duration of a single http transaction. With http/1.1 keep-alive and http/2, - * that is unrelated to (shorter than) the lifetime of the network connection. - */ -struct pss { - struct lws_spa *spa; - struct lwsac *ac; -}; - -static int interrupted; - -static const char * const param_names[] = { - "text1", - "send", -}; - -enum enum_param_names { - EPN_TEXT1, - EPN_SEND, -}; - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - struct pss *pss = (struct pss *)user; - uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE], *start = &buf[LWS_PRE], - *p = start, *end = &buf[sizeof(buf) - 1]; - int n; - - switch (reason) { - case LWS_CALLBACK_HTTP: - - /* - * Manually report that our form target URL exists - * - * you can also do this by adding a mount for the form URL - * to the protocol with type LWSMPRO_CALLBACK, then no need - * to trap LWS_CALLBACK_HTTP. - */ - - if (!strcmp((const char *)in, "/form1")) - /* assertively allow it to exist in the URL space */ - return 0; - - /* default to 404-ing the URL if not mounted */ - break; - - case LWS_CALLBACK_HTTP_BODY: - - /* create the POST argument parser if not already existing */ - - if (!pss->spa) { - lws_spa_create_info_t i; - - memset(&i, 0, sizeof(i)); - i.param_names = param_names; - i.count_params = LWS_ARRAY_SIZE(param_names); - i.ac = &pss->ac; - i.ac_chunk_size = 512; - - pss->spa = lws_spa_create_via_info(wsi, &i); /* no file upload */ - if (!pss->spa) - return -1; - } - - /* let it parse the POST data */ - - if (lws_spa_process(pss->spa, in, (int)len)) - return -1; - break; - - case LWS_CALLBACK_HTTP_BODY_COMPLETION: - - /* inform the spa no more payload data coming */ - - lwsl_user("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); - lws_spa_finalize(pss->spa); - - /* we just dump the decoded things to the log */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(param_names); n++) { - if (!lws_spa_get_string(pss->spa, n)) - lwsl_user("%s: undefined\n", param_names[n]); - else - lwsl_user("%s: (len %d) '%s'\n", - param_names[n], - lws_spa_get_length(pss->spa, n), - lws_spa_get_string(pss->spa, n)); - } - - lwsac_free(&pss->ac); - - /* - * Our response is to redirect to a static page. We could - * have generated a dynamic html page here instead. - */ - - if (lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, - (unsigned char *)"after-form1.html", - 16, &p, end) < 0) - return -1; - break; - - case LWS_CALLBACK_HTTP_DROP_PROTOCOL: - /* called when our wsi user_space is going to be destroyed */ - if (pss->spa) { - lws_spa_destroy(pss->spa); - pss->spa = NULL; - } - lwsac_free(&pss->ac); - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static struct lws_protocols protocols[] = { - { "http", callback_http, sizeof(struct pss), 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -/* default mount serves the URL space from ./mount-origin */ - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server POST | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.protocols = protocols; - info.mounts = &mount; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - if (lws_cmdline_option(argc, argv, "-s")) { - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-lwsac/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-lwsac/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-form-post-lwsac/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-form-post-lwsac/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -# lws minimal http server form POST lwsac - -Shows how to parse the form using an lwsac to hold the form data - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-form-post-lwsac -[2018/03/29 08:29:41:7044] USER: LWS minimal http server form POST | visit http://localhost:7681 -[2018/03/29 08:29:41:7044] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/03/29 08:29:49:8601] USER: text1: (len 4) 'xxxx' -[2018/03/29 08:29:49:8601] USER: send: (len 6) 'Submit' -``` - -Visit http://localhost:7681, submit the form. - -The form parameters are dumped to the log and you are redirected to a different page. diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -project(lws-minimal-http-server-fulltext-search) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-fulltext-search) -set(SRCS minimal-http-server.c) - -include_directories(../../../plugins) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_FTS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/lws-fts.index and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/lws-fts.index differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/minimal-http-server.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/minimal-http-server.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/minimal-http-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/minimal-http-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -/* - * lws-minimal-http-server-fts - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates how to use lws full-text search - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include - -const char *index_filepath = "./lws-fts.index"; -static int interrupted; - -static struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_FULLTEXT_DEMO, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static struct lws_protocol_vhost_options pvo_idx = { - NULL, - NULL, - "indexpath", /* pvo name */ - NULL /* filled in at runtime */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo_idx, /* "child" pvo linked-list */ - "lws-test-fts", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; - -/* override the default mount for /fts in the URL space */ - -static const struct lws_http_mount mount_fts = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/fts", /* mountpoint URL */ - /* .origin */ NULL, /* protocol */ - /* .def */ NULL, - /* .protocol */ "lws-test-fts", - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_CALLBACK, /* dynamic */ - /* .mountpoint_len */ 4, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -static const struct lws_http_mount mount = { - /* .mount_next */ &mount_fts, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server fulltext search | " - "visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - info.pvo = &pvo; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - pvo_idx.value = index_filepath; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/dorian-gray-wikipedia.jpg and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/dorian-gray-wikipedia.jpg differ Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ - - - - - - - - - - - -
-
- -The Picture of Dorian Gray
- - - - -
Fulltext search
- -
-
- -
-
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/lws-fts.css libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/lws-fts.css --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/lws-fts.css 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/lws-fts.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,154 +0,0 @@ -span.title { - font-size: 24pt; - text-align: center; -} - -img.eyeglass { - display: inline-block; - background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAyMjQuMDUyMjIgMjU2LjQ4MjE1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KCTxkZWZzPgoJCTxsaW5lYXJHcmFkaWVudCBpZD0iYSIgeDE9IjkwOS4xMSIgeDI9Ijk5Ni42OSIgeTE9Ii03MS4wMzUiIHkyPSItMzEuNjEzIiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC05MTYuMDYgMTA1LjU4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCQk8c3RvcCBzdG9wLWNvbG9yPSIjNGQ0ZDRkIiBvZmZzZXQ9IjAiLz4KCQkJPHN0b3Agc3RvcC1jb2xvcj0iIzgwODA4MCIgc3RvcC1vcGFjaXR5PSIuMDgyNjQ1IiBvZmZzZXQ9IjEiLz4KCQk8L2xpbmVhckdyYWRpZW50PgoJCTxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjEwMjAuMyIgeDI9IjEwMTcuNiIgeTE9Ii0zNy44NjMiIHkyPSItMzUuMzgzIiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC05MTYuMDYgMTA1LjU4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCQk8c3RvcCBzdG9wLWNvbG9yPSIjOTk5IiBvZmZzZXQ9IjAiLz4KCQkJPHN0b3Agc3RvcC1jb2xvcj0iIzk5OSIgc3RvcC1vcGFjaXR5PSIwIiBvZmZzZXQ9IjEiLz4KCQk8L2xpbmVhckdyYWRpZW50PgoJCTxmaWx0ZXIgaWQ9ImMiIHg9Ii0uMDg1NTgiIHk9Ii0uMTIyNiIgd2lkdGg9IjEuMTcxMiIgaGVpZ2h0PSIxLjI0NTIiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjAuNDc2NDQ5MzkiLz4KCQk8L2ZpbHRlcj4KCQk8ZmlsdGVyIGlkPSJkIiB4PSItLjIxNjYxIiB5PSItLjI5NDQ1IiB3aWR0aD0iMS40MzMyIiBoZWlnaHQ9IjEuNTg4OSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KCQkJPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMS40NjkwMTk0Ii8+CgkJPC9maWx0ZXI+CgkJPGZpbHRlciBpZD0iZSIgeD0iLS4wNTkyMjgiIHk9Ii0uMDc3NjUyIiB3aWR0aD0iMS4xMTg1IiBoZWlnaHQ9IjEuMTU1MyIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KCQkJPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMy40NzIzODc4Ii8+CgkJPC9maWx0ZXI+CgkJPGZpbHRlciBpZD0iZiIgeD0iLS4wNzUwNTMiIHk9Ii0uMDY3MDAzIiB3aWR0aD0iMS4xNTAxIiBoZWlnaHQ9IjEuMTM0IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPgoJCQk8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIyLjMwNjQ1MzgiLz4KCQk8L2ZpbHRlcj4KCQk8ZmlsdGVyIGlkPSJqIiB4PSItLjcwMzc4IiB5PSItLjY5MDc0IiB3aWR0aD0iMi40MDc2IiBoZWlnaHQ9IjIuMzgxNSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KCQkJPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iNC4xMTIwODgiLz4KCQk8L2ZpbHRlcj4KCQk8ZmlsdGVyIGlkPSJpIiB4PSItLjA2MSIgeT0iLS4wOTUzNDUiIHdpZHRoPSIxLjEyMiIgaGVpZ2h0PSIxLjE5MDciIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjAuNzU4ODkwNDIiLz4KCQk8L2ZpbHRlcj4KCQk8ZmlsdGVyIGlkPSJoIiB4PSItLjA3OTU4NyIgeT0iLS4xNjc5NyIgd2lkdGg9IjEuMTU5MiIgaGVpZ2h0PSIxLjMzNTkiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjAuNTgwMDg2MTUiLz4KCQk8L2ZpbHRlcj4KCQk8ZmlsdGVyIGlkPSJnIiB4PSItLjAzMjI5NSIgeT0iLS4wMjgwMDkiIHdpZHRoPSIxLjA2NDYiIGhlaWdodD0iMS4wNTYiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjEuMjM0MzQ2OCIvPgoJCTwvZmlsdGVyPgoJCTxmaWx0ZXIgaWQ9ImsiIHg9Ii0uMTY2NDgiIHk9Ii0uMzQ5ODEiIHdpZHRoPSIxLjMzMyIgaGVpZ2h0PSIxLjY5OTYiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjMuODg4NzYzNiIvPgoJCTwvZmlsdGVyPgoJCTxmaWx0ZXIgaWQ9Im0iIHg9Ii0uMDM1OTIyIiB5PSItLjA0NzIxMSIgd2lkdGg9IjEuMDcxOCIgaGVpZ2h0PSIxLjA5NDQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIuMDUzNDk2NiIvPgoJCTwvZmlsdGVyPgoJCTxmaWx0ZXIgaWQ9ImwiIHg9Ii0uMDM1OTIyIiB5PSItLjA0NzIxMSIgd2lkdGg9IjEuMDcxOCIgaGVpZ2h0PSIxLjA5NDQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIuMTg1OTg3NSIvPgoJCTwvZmlsdGVyPgoJPC9kZWZzPgoJPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTkuMDQ0IDUzLjQ5MSkiPgoJCTxwYXRoIGQ9Im0tOC44MDY4IDI2LjA4MmMtMy4wMzI4LTM1LjM1MyAyOC43MTItNjIuMTY3IDY0LjY1My02NC4wMTcgMjIuOTg5LTIuNTkzMiA2Ni44MTkgOS4wNDY5IDgyLjk1NSA0Ni4yOTMtMS4yODAxLTIxLjkyMy0yNS45NjctNjguMjkxLTEwOC43OC00OS4yOTMtMjUuMzc2IDguMjg5Mi01MS4zMzYgMzUuMzQ3LTQxLjQxMSA2My41NTZ6IiBmaWxsPSIjOTk5Ii8+CgkJPGVsbGlwc2UgY3g9IjYzLjgzMiIgY3k9IjEzLjY5NyIgcng9Ijc1LjgwNyIgcnk9IjU3LjY3OSIgZmlsbD0iI2IzYjNiMyIgZmlsbC1vcGFjaXR5PSIuNDQyMTUiLz4KCQk8cGF0aCBkPSJtNjIuNzU1LTQ3LjQ3MmE3Ny43ODQgNTkuMTgzIDAgMCAwIC03Ny43ODMgNTkuMTgzIDc3Ljc4NCA1OS4xODMgMCAwIDAgNzcuNzgzIDU5LjE4MyA3Ny43ODQgNTkuMTgzIDAgMCAwIDc3Ljc4NCAtNTkuMTgzIDc3Ljc4NCA1OS4xODMgMCAwIDAgLTc3Ljc4NCAtNTkuMTgzem0wLjEyNjEgMi4xMDI3YTc1LjgxNyA1Ny42ODcgMCAwIDEgNzUuODE3IDU3LjY4NiA3NS44MTcgNTcuNjg3IDAgMCAxIC03NS44MTcgNTcuNjg3IDc1LjgxNyA1Ny42ODcgMCAwIDEgLTc1LjgxNyAtNTcuNjg3IDc1LjgxNyA1Ny42ODcgMCAwIDEgNzUuODE3IC01Ny42ODZ6IiBmaWxsPSIjODA4MDgwIi8+CgkJPGVsbGlwc2UgY3g9IjYzLjYxMSIgY3k9IjE3LjE4OCIgcng9IjczLjAyNSIgcnk9IjU1LjU2MiIgZmlsbD0iI2IzYjNiMyIgZmlsbC1vcGFjaXR5PSIuNTUzNzIiIGZpbHRlcj0idXJsKCNsKSIvPgoJCTxwYXRoIGQ9Im0xMzkuNDMgMTguMjI0Yy0xLjEyMjUgMzYuMDg2LTMzLjMxMyA2My4xMjUtNzUuMzMxIDYzLjEyNS00Mi4wMTcgMC03Ny45NS0zNC43MS03Ny45NS02Ni42NzkgNy4yOTY1IDIxLjUzOCAyNS4xMTEgNTIuMTA4IDc4LjY5OCA1Mi4zMDkgNTMuOTYyLTIuNjA1NCA2OS45MDUtMzQuODg3IDc0LjU4Mi00OC43NTR6IiBmaWxsPSIjODA4MDgwIi8+CgkJPGVsbGlwc2UgY3g9IjYwLjQ5NiIgY3k9IjEyLjEyOSIgcng9IjY4LjU5OSIgcnk9IjUyLjE5NSIgZmlsbD0iI2VjZWNlYyIgZmlsbC1vcGFjaXR5PSIuNTQ1NDUiIGZpbHRlcj0idXJsKCNtKSIvPgoJCTxlbGxpcHNlIHRyYW5zZm9ybT0ibWF0cml4KC45MTQ2MSAtLjUzNjUyIC41MzY1MiAuOTE0NjEgLTg5NC4zNCA0NDguMjkpIiBjeD0iOTY5Ljk1IiBjeT0iNjMuNzI0IiByeD0iMjguNDQzIiByeT0iMTIuNDM1IiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44MDE2NSIgZmlsdGVyPSJ1cmwoI2spIi8+CgkJPHBhdGggZD0ibTg2LjU0NSA3NS4wODYgMy42NjU0IDUuMjU0OCAyLjAwNDggMS42ODM3IDIuMDkxMSAwLjc2Mzg1IDAuOTQ2NiAwLjQ0MTc0IDkuMjMyMyAxMy43MzQgMS4xMDUyIDUuNDI2NSA2Mi43MTIgODkuOTA3YzguMTA1IDkuNjI1MiAyOC45OTUgMTIuMDIgMzIuMzM3LTMuMDkwNiAzLjE1MzItMTMuMTg1LTkuNDY5Mi0yMS42NTItMTYuNDY3LTMwLjc0OC0xOS41NDgtMjIuMzU4LTQ3LjU3LTU1LjE3OS01OC4yODgtNjcuNDQ1LTMuNTc0My0yLjIxMjQtMy4wOTY5LTAuOTY3NTUtNS45NDg3LTMuMjQ2Ni0yLjg1MTktMi4yNzkxLTcuNDI5Mi04LjAxODgtMTAuMjAyLTExLjEyOC0wLjgzNjMtMC45Mzc4Ny0wLjE5ODQtNC4xMDIyLTAuOTg4NS00Ljk4NTktMi41OTg4LTIuOTA2Ny00LjkwMy01LjQ3MTEtNi42MTkzLTcuMzY4NS02Ljc2NjctNC4xOTItMTguMTk3IDIuNDc2MS0xNS41ODIgMTAuODAyeiIgZmlsbD0iIzY2NiIvPgoJCTxnIGZpbGw9IiM0ZDRkNGQiPgoJCQk8cGF0aCBkPSJtMTEzLjQ5IDk0LjI2MmMzLjUwNzEgOS4xNTc4IDYuMDYwNiAxOC44NjUgMTAuODM0IDI3LjQzMiA2Ljg4NTggMTAuMTY2IDEyLjYyNCAyMS4yMzMgMjEuNDE2IDI5Ljk0NyA1LjUyNDggNS44Mzk1IDEwLjIwOSAxMi42NjggMTcuMzU4IDE2LjY2NCA1Ljk5MyA2LjI1NCAxNC45MjUgNS41MDU2IDIxLjk5MSA5LjQ3MTIgNy4xMjEzIDIuNDAzOCAxNi4yMTUgMTIuMjIgMTAuNDQgMTkuMTk3LTguMjk5MiA1Ljk1NjItMTkuOTU3IDIuNDY3Mi0yNi44NDMtNC4xNDItNi41OTU5LTguMjQ1Mi0xMi4xODUtMTcuNDA5LTE4LjM5LTI2LjAxLTE0Ljg4OC0yMS41MDUtMjkuNzEtNDMuMDc2LTQ0LjU5OC02NC41OCAxLjMyMjQtMy44ODcgMy40MjM2LTYuNjg0NiA3Ljc5MTYtNy45Nzg3eiIgZmlsdGVyPSJ1cmwoI2cpIi8+CgkJCTxwYXRoIGQ9Im0xMDguNDMgNzIuMTg2Yy0yLjA0NzctMC40OTI2NS00LjEyNDItMS4yMTE1LTYuMjQyNS0wLjY0OTA3LTIuODIxNiAwLjI1ODIzLTUuMjYxOSAxLjkyMjMtNy4xMjgyIDMuOTY2Ni0xLjA5NDggMC45MjI5MyAxLjU3NS0wLjM3OTc2IDIuMTg4NC0wLjQxOCAyLjA4ODItMC41MTk4NSA0LjI5MDItMC40OTI3MiA2LjQyNDctMC4zMTQ5MyAzLjAyNTUgMC4yMTIyMSA1LjMzMDUgMi4zMTc3IDcuNTYzNiA0LjEzNTIgMC42MTI0IDAuNDkyMzYgMS42OTg4IDEuMjIzMiAwLjU1MiAwLjE5MjkyLTAuODQ4NS0xLjE4MjYtMi41OTM1LTEuOTIwNC0yLjg3NjctMy4zODM0LTAuMTYwNC0xLjE3NjQtMC4zMjA5LTIuMzUyOC0wLjQ4MTMtMy41MjkyeiIgZmlsdGVyPSJ1cmwoI2gpIi8+CgkJCTxwYXRoIGQ9Im0xMTguMzUgODYuMjE3Yy0yLjQzODggMC4wMjQtNC45NzU3LTAuMjc1MjYtNy4yNTQ3IDAuODEzNzYtMi43MDYzIDAuODYzNzEtNC44OTc1IDIuMDc2MS04LjI3MzYgMS45OTI2LTEuMDk4NS0wLjE4MjktMi4zNTk0LTEuMTY2Mi0zLjgzMjUtMi4zNTM0LTEuNTU4My0xLjI1NTgtMy4yMjE1LTMuMjE1My0zLjY5NS0zLjMyMjIgMi45MTQ1IDQuNDE3NSA2LjEwOTYgOS4wMjIyIDkuMDI0MiAxMy40NCAwLjM1NSAxLjU2MTUgMC43MTQ4IDQuOTY2OCAxLjMwMjEgNS42NjI4IDIuNTQzOS0zLjI1NDcgNS4wMTgzLTYuNzQ4IDguNzIzMy04Ljc3OTggMi42ODQ4LTEuNDkyNiA1LjUwOTgtMy40MDAyIDguNzQ1MS0yLjk1NDQgMC45MDg5IDAuMTMyOTUgMy4zOTA3IDAuMjE4NDEgMS4xNTQ5LTAuNTU4MS0yLjEwODctMS4wNjU2LTQuMzA2NS0yLjEzNTUtNS44OTM4LTMuOTQxeiIgZmlsdGVyPSJ1cmwoI2kpIi8+CgkJPC9nPgoJCTxlbGxpcHNlIGN4PSIxODguMTMiIGN5PSIxODUuMTciIHJ4PSI3LjAxMTUiIHJ5PSI3LjE0MzciIGZpbGw9IiM5OTkiIGZpbHRlcj0idXJsKCNqKSIvPgoJCTxnIGZpbGw9IiNiM2IzYjMiPgoJCQk8cGF0aCBkPSJtMTI1LjIgOTMuNTUxYy0xLjkxLTAuMjc5MjItMy43NTc3LTAuMDIwNi01LjU1NjMgMC42NjE0Ni0wLjg2MTcgMS4zMzE2LTAuODQ5MyAyLjUzNzkgMC40NDI5IDMuNTYwOSAxOS45NjEgMjUuNDQ4IDM5LjkyMSA1MC44OTYgNTkuODgyIDc2LjM0MyAyLjgwNzIgMC42MTM3IDUuNjEwMiAxLjQ4ODIgOC40MiAxLjkzOSAxLjU3MDQtMC4zNDM4MyAzLjQwMi0wLjQ4OTAzIDQuMzg1MS0xLjY3NzkgMC4zMDg3LTIuMTczOS0xLjkzMzctMy4zNDY5LTIuOTkxNS00Ljk3NDEtMjEuNTI4LTI1LjI4NC00My4wNTUtNTAuNTY4LTY0LjU4Mi03NS44NTN6IiBmaWx0ZXI9InVybCgjZikiLz4KCQkJPHBhdGggZD0ibTEwMy45MS0zMi4zOTFjNy4yMzI3IDEzLjM0OCAxMS44MiAzMC4wNjkgNi4wMTA0IDQ0LjY1LTguMjg4MiAxNi4yNTktMjQuNDQ0IDI3LjQ4NC00Mi42MjEgMjkuNTc1LTIwLjU0MSA1LjMzMTktNDMuNjc4IDUuNjI3OS02Mi4zMzUtNS41NDYzLTE3LjgyOC01LjEyNTQgNC40MzQ4IDE0LjgzNiAxMC4xNTkgMTcuNjQ0IDMxLjMyOSAxOS4yMjUgNzUuMDUyIDE2LjY0NyAxMDQuMDUtNS45NDA5IDEyLjE3Ni0xMC44MjggMjIuOTY0LTI2LjMyNSAxOC43OTQtNDMuNDQ4LTMuOTA5NS0yMi4yNTYtMjMuNzAzLTM4LjEwNC00NC4xMzQtNDUuMDYgMy4xMTc0IDMuMDA1NCA2Ljg3NzEgNS4yMTggMTAuMDc2IDguMTI3MXoiIGZpbGwtb3BhY2l0eT0iLjQwOTA5IiBmaWx0ZXI9InVybCgjZSkiLz4KCQkJPHBhdGggZD0ibTEwMi4wNyA3NC45OTJoNC40OTAybDIuOTkzNCAxLjY4MzggOC43OTMxIDkuOTE1Ny00LjExNTktMC45MzU0NC0yLjYxOTIgMS4zMDk2eiIgZmlsbC1vcGFjaXR5PSIuNDA5MDkiIGZpbHRlcj0idXJsKCNkKSIvPgoJCTwvZz4KCQk8Zz4KCQkJPHBhdGggZD0ibTk1Ljc3IDYyLjk5MiA2LjQxNjEgOC40MDA1IDIuOTEwNS0wLjA2NjEgMi4wNTA1IDAuMzMwNzMgMS45ODQ0IDAuNjYxNDYtMC4zOTY5LTAuNzkzNzUtNi40MTYyLTcuMjA5OS0xLjM4OS0wLjcyNzYxLTIuMjQ5LTAuNTI5MTYtMS4wNTgzLTAuMDY2MnoiIGZpbGw9InVybCgjYikiIGZpbHRlcj0idXJsKCNjKSIvPgoJCQk8cGF0aCBkPSJtLTE0LjM2MyAxNC4xNzYgMC43OTM3NSAxMC4wNTQgNS4wMjcxIDExLjY0MiA1LjAyNzEgMTAuMDU0IDkuNzg5NiAxMC44NDggMTIuNDM1IDEwLjg0OCAxNC44MTcgNy42NzI5IDEyLjk2NSAzLjcwNDIgMTEuOTA2IDIuMTE2N2g4LjczMTJsMjAuNjM4LTIuNjQ1OCAxLjA1ODMtMS4wNTgzLTIuMzgxMy0zLjQzOTYgMC41MjkyLTUuODIwOCAzLjQzOTYtMi4zODEyIDMuNzA0Mi0yLjExNjcgNy45Mzc1LTQuNDk3OS0xNS4wODEgNC43NjI1LTE3LjcyNyAzLjE3NS0yMC42MzgtMS4wNTgzLTE2LjY2OS0zLjcwNDItMTYuOTMzLTguMjAyMS0xMS4xMTItOC40NjY3LTExLjkwNi0xNi40MDR6IiBmaWxsPSJ1cmwoI2EpIi8+CgkJCTx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDUyLjY0MikiIHg9IjIyMS4yMzgzNCIgeT0iLTM0LjUzODExMyIgZG9taW5hbnQtYmFzZWxpbmU9ImF1dG8iIGZpbGw9IiM2NjY2NjYiIGZvbnQtZmFtaWx5PSInT3BlbiBTYW5zJyIgZm9udC1zaXplPSI4LjkxMDNweCIgbGV0dGVyLXNwYWNpbmc9IjBweCIgc3Ryb2tlLXdpZHRoPSIuMDQ2NDA4IiB0ZXh0LWFsaWduPSJjZW50ZXIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHdvcmQtc3BhY2luZz0iMHB4IiBzdHlsZT0iZm9udC1mZWF0dXJlLXNldHRpbmdzOm5vcm1hbDtmb250LXZhcmlhbnQtYWx0ZXJuYXRlczpub3JtYWw7Zm9udC12YXJpYW50LWNhcHM6bm9ybWFsO2ZvbnQtdmFyaWFudC1saWdhdHVyZXM6bm9ybWFsO2ZvbnQtdmFyaWFudC1udW1lcmljOm5vcm1hbDtmb250LXZhcmlhbnQtcG9zaXRpb246bm9ybWFsO2xpbmUtaGVpZ2h0OjEuMjU7c2hhcGUtcGFkZGluZzowO3RleHQtZGVjb3JhdGlvbi1jb2xvcjojMDAwMDAwO3RleHQtZGVjb3JhdGlvbi1saW5lOm5vbmU7dGV4dC1kZWNvcmF0aW9uLXN0eWxlOnNvbGlkO3RleHQtaW5kZW50OjA7dGV4dC1vcmllbnRhdGlvbjptaXhlZDt0ZXh0LXRyYW5zZm9ybTpub25lO3doaXRlLXNwYWNlOm5vcm1hbCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjIyMS4yMzgzNCIgeT0iLTM0LjUzODExMyIgZmlsbD0iIzY2NjY2NiIgc3Ryb2tlLXdpZHRoPSIuMDQ2NDA4Ij5naW90b2hhc2hpPC90c3Bhbj48L3RleHQ+CgkJPC9nPgoJPC9nPgo8L3N2Zz4="); - width:0px; - height:0px; - padding:2em 1.6em; - vertical-align:middle; - margin-right: 0.1em; - background-repeat: no-repeat; - color: rgba(0, 0, 0, 0); -} - -img.spinner { - display: inline-block; - background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiCiAgaWQ9InN2Zy1zcGlubmVyIiAKICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgeD0iMHB4IiAKICB5PSIwcHgiIAogIHZpZXdCb3g9Ii0xMTIgLTEyOCA0NDggNTEyIgogIHhtbDpzcGFjZT0icHJlc2VydmUiPgoKCTxkZWZzPgoJCTxsaW5lYXJHcmFkaWVudCBpZD0iYSIgeDE9IjkwOS4xMSIgeDI9Ijk5Ni42OSIgeTE9Ii03MS4wMzUiIHkyPSItMzEuNjEzIiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC05MTYuMDYgMTA1LjU4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCQk8c3RvcCBzdG9wLWNvbG9yPSIjNGQ0ZDRkIiBvZmZzZXQ9IjAiLz4KCQkJPHN0b3Agc3RvcC1jb2xvcj0iIzgwODA4MCIgc3RvcC1vcGFjaXR5PSIuMDgyNjQ1IiBvZmZzZXQ9IjEiLz4KCQk8L2xpbmVhckdyYWRpZW50PgoJCTxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjEwMjAuMyIgeDI9IjEwMTcuNiIgeTE9Ii0zNy44NjMiIHkyPSItMzUuMzgzIiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC05MTYuMDYgMTA1LjU4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCQk8c3RvcCBzdG9wLWNvbG9yPSIjOTk5IiBvZmZzZXQ9IjAiLz4KCQkJPHN0b3Agc3RvcC1jb2xvcj0iIzk5OSIgc3RvcC1vcGFjaXR5PSIwIiBvZmZzZXQ9IjEiLz4KCQk8L2xpbmVhckdyYWRpZW50PgoJCTxmaWx0ZXIgaWQ9ImMiIHg9Ii0uMDg1NTgiIHk9Ii0uMTIyNiIgd2lkdGg9IjEuMTcxMiIgaGVpZ2h0PSIxLjI0NTIiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjAuNDc2NDQ5MzkiLz4KCQk8L2ZpbHRlcj4KCQk8ZmlsdGVyIGlkPSJkIiB4PSItLjIxNjYxIiB5PSItLjI5NDQ1IiB3aWR0aD0iMS40MzMyIiBoZWlnaHQ9IjEuNTg4OSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KCQkJPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMS40NjkwMTk0Ii8+CgkJPC9maWx0ZXI+CgkJPGZpbHRlciBpZD0iZSIgeD0iLS4wNTkyMjgiIHk9Ii0uMDc3NjUyIiB3aWR0aD0iMS4xMTg1IiBoZWlnaHQ9IjEuMTU1MyIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KCQkJPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMy40NzIzODc4Ii8+CgkJPC9maWx0ZXI+CgkJPGZpbHRlciBpZD0iZiIgeD0iLS4wNzUwNTMiIHk9Ii0uMDY3MDAzIiB3aWR0aD0iMS4xNTAxIiBoZWlnaHQ9IjEuMTM0IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPgoJCQk8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIyLjMwNjQ1MzgiLz4KCQk8L2ZpbHRlcj4KCQk8ZmlsdGVyIGlkPSJqIiB4PSItLjcwMzc4IiB5PSItLjY5MDc0IiB3aWR0aD0iMi40MDc2IiBoZWlnaHQ9IjIuMzgxNSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KCQkJPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iNC4xMTIwODgiLz4KCQk8L2ZpbHRlcj4KCQk8ZmlsdGVyIGlkPSJpIiB4PSItLjA2MSIgeT0iLS4wOTUzNDUiIHdpZHRoPSIxLjEyMiIgaGVpZ2h0PSIxLjE5MDciIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjAuNzU4ODkwNDIiLz4KCQk8L2ZpbHRlcj4KCQk8ZmlsdGVyIGlkPSJoIiB4PSItLjA3OTU4NyIgeT0iLS4xNjc5NyIgd2lkdGg9IjEuMTU5MiIgaGVpZ2h0PSIxLjMzNTkiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjAuNTgwMDg2MTUiLz4KCQk8L2ZpbHRlcj4KCQk8ZmlsdGVyIGlkPSJnIiB4PSItLjAzMjI5NSIgeT0iLS4wMjgwMDkiIHdpZHRoPSIxLjA2NDYiIGhlaWdodD0iMS4wNTYiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjEuMjM0MzQ2OCIvPgoJCTwvZmlsdGVyPgoJCTxmaWx0ZXIgaWQ9ImsiIHg9Ii0uMTY2NDgiIHk9Ii0uMzQ5ODEiIHdpZHRoPSIxLjMzMyIgaGVpZ2h0PSIxLjY5OTYiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjMuODg4NzYzNiIvPgoJCTwvZmlsdGVyPgoJCTxmaWx0ZXIgaWQ9Im0iIHg9Ii0uMDM1OTIyIiB5PSItLjA0NzIxMSIgd2lkdGg9IjEuMDcxOCIgaGVpZ2h0PSIxLjA5NDQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIuMDUzNDk2NiIvPgoJCTwvZmlsdGVyPgoJCTxmaWx0ZXIgaWQ9ImwiIHg9Ii0uMDM1OTIyIiB5PSItLjA0NzIxMSIgd2lkdGg9IjEuMDcxOCIgaGVpZ2h0PSIxLjA5NDQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CgkJCTxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIuMTg1OTg3NSIvPgoJCTwvZmlsdGVyPgoJPC9kZWZzPgoJPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTkuMDQ0IDUzLjQ5MSkiPgoJCTxwYXRoIGQ9Im0tOC44MDY4IDI2LjA4MmMtMy4wMzI4LTM1LjM1MyAyOC43MTItNjIuMTY3IDY0LjY1My02NC4wMTcgMjIuOTg5LTIuNTkzMiA2Ni44MTkgOS4wNDY5IDgyLjk1NSA0Ni4yOTMtMS4yODAxLTIxLjkyMy0yNS45NjctNjguMjkxLTEwOC43OC00OS4yOTMtMjUuMzc2IDguMjg5Mi01MS4zMzYgMzUuMzQ3LTQxLjQxMSA2My41NTZ6IiBmaWxsPSIjOTk5Ii8+CgkJPGVsbGlwc2UgY3g9IjYzLjgzMiIgY3k9IjEzLjY5NyIgcng9Ijc1LjgwNyIgcnk9IjU3LjY3OSIgZmlsbD0iI2IzYjNiMyIgZmlsbC1vcGFjaXR5PSIuNDQyMTUiLz4KCQk8cGF0aCBkPSJtNjIuNzU1LTQ3LjQ3MmE3Ny43ODQgNTkuMTgzIDAgMCAwIC03Ny43ODMgNTkuMTgzIDc3Ljc4NCA1OS4xODMgMCAwIDAgNzcuNzgzIDU5LjE4MyA3Ny43ODQgNTkuMTgzIDAgMCAwIDc3Ljc4NCAtNTkuMTgzIDc3Ljc4NCA1OS4xODMgMCAwIDAgLTc3Ljc4NCAtNTkuMTgzem0wLjEyNjEgMi4xMDI3YTc1LjgxNyA1Ny42ODcgMCAwIDEgNzUuODE3IDU3LjY4NiA3NS44MTcgNTcuNjg3IDAgMCAxIC03NS44MTcgNTcuNjg3IDc1LjgxNyA1Ny42ODcgMCAwIDEgLTc1LjgxNyAtNTcuNjg3IDc1LjgxNyA1Ny42ODcgMCAwIDEgNzUuODE3IC01Ny42ODZ6IiBmaWxsPSIjODA4MDgwIi8+CgkJPGVsbGlwc2UgY3g9IjYzLjYxMSIgY3k9IjE3LjE4OCIgcng9IjczLjAyNSIgcnk9IjU1LjU2MiIgZmlsbD0iI2IzYjNiMyIgZmlsbC1vcGFjaXR5PSIuNTUzNzIiIGZpbHRlcj0idXJsKCNsKSIvPgoJCTxwYXRoIGQ9Im0xMzkuNDMgMTguMjI0Yy0xLjEyMjUgMzYuMDg2LTMzLjMxMyA2My4xMjUtNzUuMzMxIDYzLjEyNS00Mi4wMTcgMC03Ny45NS0zNC43MS03Ny45NS02Ni42NzkgNy4yOTY1IDIxLjUzOCAyNS4xMTEgNTIuMTA4IDc4LjY5OCA1Mi4zMDkgNTMuOTYyLTIuNjA1NCA2OS45MDUtMzQuODg3IDc0LjU4Mi00OC43NTR6IiBmaWxsPSIjODA4MDgwIi8+CgkJPGVsbGlwc2UgY3g9IjYwLjQ5NiIgY3k9IjEyLjEyOSIgcng9IjY4LjU5OSIgcnk9IjUyLjE5NSIgZmlsbD0iI2VjZWNlYyIgZmlsbC1vcGFjaXR5PSIuNTQ1NDUiIGZpbHRlcj0idXJsKCNtKSIvPgoJCTxlbGxpcHNlIHRyYW5zZm9ybT0ibWF0cml4KC45MTQ2MSAtLjUzNjUyIC41MzY1MiAuOTE0NjEgLTg5NC4zNCA0NDguMjkpIiBjeD0iOTY5Ljk1IiBjeT0iNjMuNzI0IiByeD0iMjguNDQzIiByeT0iMTIuNDM1IiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44MDE2NSIgZmlsdGVyPSJ1cmwoI2spIi8+CgkJPHBhdGggZD0ibTg2LjU0NSA3NS4wODYgMy42NjU0IDUuMjU0OCAyLjAwNDggMS42ODM3IDIuMDkxMSAwLjc2Mzg1IDAuOTQ2NiAwLjQ0MTc0IDkuMjMyMyAxMy43MzQgMS4xMDUyIDUuNDI2NSA2Mi43MTIgODkuOTA3YzguMTA1IDkuNjI1MiAyOC45OTUgMTIuMDIgMzIuMzM3LTMuMDkwNiAzLjE1MzItMTMuMTg1LTkuNDY5Mi0yMS42NTItMTYuNDY3LTMwLjc0OC0xOS41NDgtMjIuMzU4LTQ3LjU3LTU1LjE3OS01OC4yODgtNjcuNDQ1LTMuNTc0My0yLjIxMjQtMy4wOTY5LTAuOTY3NTUtNS45NDg3LTMuMjQ2Ni0yLjg1MTktMi4yNzkxLTcuNDI5Mi04LjAxODgtMTAuMjAyLTExLjEyOC0wLjgzNjMtMC45Mzc4Ny0wLjE5ODQtNC4xMDIyLTAuOTg4NS00Ljk4NTktMi41OTg4LTIuOTA2Ny00LjkwMy01LjQ3MTEtNi42MTkzLTcuMzY4NS02Ljc2NjctNC4xOTItMTguMTk3IDIuNDc2MS0xNS41ODIgMTAuODAyeiIgZmlsbD0iIzY2NiIvPgoJCTxnIGZpbGw9IiM0ZDRkNGQiPgoJCQk8cGF0aCBkPSJtMTEzLjQ5IDk0LjI2MmMzLjUwNzEgOS4xNTc4IDYuMDYwNiAxOC44NjUgMTAuODM0IDI3LjQzMiA2Ljg4NTggMTAuMTY2IDEyLjYyNCAyMS4yMzMgMjEuNDE2IDI5Ljk0NyA1LjUyNDggNS44Mzk1IDEwLjIwOSAxMi42NjggMTcuMzU4IDE2LjY2NCA1Ljk5MyA2LjI1NCAxNC45MjUgNS41MDU2IDIxLjk5MSA5LjQ3MTIgNy4xMjEzIDIuNDAzOCAxNi4yMTUgMTIuMjIgMTAuNDQgMTkuMTk3LTguMjk5MiA1Ljk1NjItMTkuOTU3IDIuNDY3Mi0yNi44NDMtNC4xNDItNi41OTU5LTguMjQ1Mi0xMi4xODUtMTcuNDA5LTE4LjM5LTI2LjAxLTE0Ljg4OC0yMS41MDUtMjkuNzEtNDMuMDc2LTQ0LjU5OC02NC41OCAxLjMyMjQtMy44ODcgMy40MjM2LTYuNjg0NiA3Ljc5MTYtNy45Nzg3eiIgZmlsdGVyPSJ1cmwoI2cpIi8+CgkJCTxwYXRoIGQ9Im0xMDguNDMgNzIuMTg2Yy0yLjA0NzctMC40OTI2NS00LjEyNDItMS4yMTE1LTYuMjQyNS0wLjY0OTA3LTIuODIxNiAwLjI1ODIzLTUuMjYxOSAxLjkyMjMtNy4xMjgyIDMuOTY2Ni0xLjA5NDggMC45MjI5MyAxLjU3NS0wLjM3OTc2IDIuMTg4NC0wLjQxOCAyLjA4ODItMC41MTk4NSA0LjI5MDItMC40OTI3MiA2LjQyNDctMC4zMTQ5MyAzLjAyNTUgMC4yMTIyMSA1LjMzMDUgMi4zMTc3IDcuNTYzNiA0LjEzNTIgMC42MTI0IDAuNDkyMzYgMS42OTg4IDEuMjIzMiAwLjU1MiAwLjE5MjkyLTAuODQ4NS0xLjE4MjYtMi41OTM1LTEuOTIwNC0yLjg3NjctMy4zODM0LTAuMTYwNC0xLjE3NjQtMC4zMjA5LTIuMzUyOC0wLjQ4MTMtMy41MjkyeiIgZmlsdGVyPSJ1cmwoI2gpIi8+CgkJCTxwYXRoIGQ9Im0xMTguMzUgODYuMjE3Yy0yLjQzODggMC4wMjQtNC45NzU3LTAuMjc1MjYtNy4yNTQ3IDAuODEzNzYtMi43MDYzIDAuODYzNzEtNC44OTc1IDIuMDc2MS04LjI3MzYgMS45OTI2LTEuMDk4NS0wLjE4MjktMi4zNTk0LTEuMTY2Mi0zLjgzMjUtMi4zNTM0LTEuNTU4My0xLjI1NTgtMy4yMjE1LTMuMjE1My0zLjY5NS0zLjMyMjIgMi45MTQ1IDQuNDE3NSA2LjEwOTYgOS4wMjIyIDkuMDI0MiAxMy40NCAwLjM1NSAxLjU2MTUgMC43MTQ4IDQuOTY2OCAxLjMwMjEgNS42NjI4IDIuNTQzOS0zLjI1NDcgNS4wMTgzLTYuNzQ4IDguNzIzMy04Ljc3OTggMi42ODQ4LTEuNDkyNiA1LjUwOTgtMy40MDAyIDguNzQ1MS0yLjk1NDQgMC45MDg5IDAuMTMyOTUgMy4zOTA3IDAuMjE4NDEgMS4xNTQ5LTAuNTU4MS0yLjEwODctMS4wNjU2LTQuMzA2NS0yLjEzNTUtNS44OTM4LTMuOTQxeiIgZmlsdGVyPSJ1cmwoI2kpIi8+CgkJPC9nPgoJCTxlbGxpcHNlIGN4PSIxODguMTMiIGN5PSIxODUuMTciIHJ4PSI3LjAxMTUiIHJ5PSI3LjE0MzciIGZpbGw9IiM5OTkiIGZpbHRlcj0idXJsKCNqKSIvPgoJCTxnIGZpbGw9IiNiM2IzYjMiPgoJCQk8cGF0aCBkPSJtMTI1LjIgOTMuNTUxYy0xLjkxLTAuMjc5MjItMy43NTc3LTAuMDIwNi01LjU1NjMgMC42NjE0Ni0wLjg2MTcgMS4zMzE2LTAuODQ5MyAyLjUzNzkgMC40NDI5IDMuNTYwOSAxOS45NjEgMjUuNDQ4IDM5LjkyMSA1MC44OTYgNTkuODgyIDc2LjM0MyAyLjgwNzIgMC42MTM3IDUuNjEwMiAxLjQ4ODIgOC40MiAxLjkzOSAxLjU3MDQtMC4zNDM4MyAzLjQwMi0wLjQ4OTAzIDQuMzg1MS0xLjY3NzkgMC4zMDg3LTIuMTczOS0xLjkzMzctMy4zNDY5LTIuOTkxNS00Ljk3NDEtMjEuNTI4LTI1LjI4NC00My4wNTUtNTAuNTY4LTY0LjU4Mi03NS44NTN6IiBmaWx0ZXI9InVybCgjZikiLz4KCQkJPHBhdGggZD0ibTEwMy45MS0zMi4zOTFjNy4yMzI3IDEzLjM0OCAxMS44MiAzMC4wNjkgNi4wMTA0IDQ0LjY1LTguMjg4MiAxNi4yNTktMjQuNDQ0IDI3LjQ4NC00Mi42MjEgMjkuNTc1LTIwLjU0MSA1LjMzMTktNDMuNjc4IDUuNjI3OS02Mi4zMzUtNS41NDYzLTE3LjgyOC01LjEyNTQgNC40MzQ4IDE0LjgzNiAxMC4xNTkgMTcuNjQ0IDMxLjMyOSAxOS4yMjUgNzUuMDUyIDE2LjY0NyAxMDQuMDUtNS45NDA5IDEyLjE3Ni0xMC44MjggMjIuOTY0LTI2LjMyNSAxOC43OTQtNDMuNDQ4LTMuOTA5NS0yMi4yNTYtMjMuNzAzLTM4LjEwNC00NC4xMzQtNDUuMDYgMy4xMTc0IDMuMDA1NCA2Ljg3NzEgNS4yMTggMTAuMDc2IDguMTI3MXoiIGZpbGwtb3BhY2l0eT0iLjQwOTA5IiBmaWx0ZXI9InVybCgjZSkiLz4KCQkJPHBhdGggZD0ibTEwMi4wNyA3NC45OTJoNC40OTAybDIuOTkzNCAxLjY4MzggOC43OTMxIDkuOTE1Ny00LjExNTktMC45MzU0NC0yLjYxOTIgMS4zMDk2eiIgZmlsbC1vcGFjaXR5PSIuNDA5MDkiIGZpbHRlcj0idXJsKCNkKSIvPgoJCTwvZz4KCQk8Zz4KCQkJPHBhdGggZD0ibTk1Ljc3IDYyLjk5MiA2LjQxNjEgOC40MDA1IDIuOTEwNS0wLjA2NjEgMi4wNTA1IDAuMzMwNzMgMS45ODQ0IDAuNjYxNDYtMC4zOTY5LTAuNzkzNzUtNi40MTYyLTcuMjA5OS0xLjM4OS0wLjcyNzYxLTIuMjQ5LTAuNTI5MTYtMS4wNTgzLTAuMDY2MnoiIGZpbGw9InVybCgjYikiIGZpbHRlcj0idXJsKCNjKSIvPgoJCQk8cGF0aCBkPSJtLTE0LjM2MyAxNC4xNzYgMC43OTM3NSAxMC4wNTQgNS4wMjcxIDExLjY0MiA1LjAyNzEgMTAuMDU0IDkuNzg5NiAxMC44NDggMTIuNDM1IDEwLjg0OCAxNC44MTcgNy42NzI5IDEyLjk2NSAzLjcwNDIgMTEuOTA2IDIuMTE2N2g4LjczMTJsMjAuNjM4LTIuNjQ1OCAxLjA1ODMtMS4wNTgzLTIuMzgxMy0zLjQzOTYgMC41MjkyLTUuODIwOCAzLjQzOTYtMi4zODEyIDMuNzA0Mi0yLjExNjcgNy45Mzc1LTQuNDk3OS0xNS4wODEgNC43NjI1LTE3LjcyNyAzLjE3NS0yMC42MzgtMS4wNTgzLTE2LjY2OS0zLjcwNDItMTYuOTMzLTguMjAyMS0xMS4xMTItOC40NjY3LTExLjkwNi0xNi40MDR6IiBmaWxsPSJ1cmwoI2EpIi8+CgkJCTx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDUyLjY0MikiIHg9IjIyMS4yMzgzNCIgeT0iLTM0LjUzODExMyIgZG9taW5hbnQtYmFzZWxpbmU9ImF1dG8iIGZpbGw9IiM2NjY2NjYiIGZvbnQtZmFtaWx5PSInT3BlbiBTYW5zJyIgZm9udC1zaXplPSI4LjkxMDNweCIgbGV0dGVyLXNwYWNpbmc9IjBweCIgc3Ryb2tlLXdpZHRoPSIuMDQ2NDA4IiB0ZXh0LWFsaWduPSJjZW50ZXIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHdvcmQtc3BhY2luZz0iMHB4IiBzdHlsZT0iZm9udC1mZWF0dXJlLXNldHRpbmdzOm5vcm1hbDtmb250LXZhcmlhbnQtYWx0ZXJuYXRlczpub3JtYWw7Zm9udC12YXJpYW50LWNhcHM6bm9ybWFsO2ZvbnQtdmFyaWFudC1saWdhdHVyZXM6bm9ybWFsO2ZvbnQtdmFyaWFudC1udW1lcmljOm5vcm1hbDtmb250LXZhcmlhbnQtcG9zaXRpb246bm9ybWFsO2xpbmUtaGVpZ2h0OjEuMjU7c2hhcGUtcGFkZGluZzowO3RleHQtZGVjb3JhdGlvbi1jb2xvcjojMDAwMDAwO3RleHQtZGVjb3JhdGlvbi1saW5lOm5vbmU7dGV4dC1kZWNvcmF0aW9uLXN0eWxlOnNvbGlkO3RleHQtaW5kZW50OjA7dGV4dC1vcmllbnRhdGlvbjptaXhlZDt0ZXh0LXRyYW5zZm9ybTpub25lO3doaXRlLXNwYWNlOm5vcm1hbCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjIyMS4yMzgzNCIgeT0iLTM0LjUzODExMyIgZmlsbD0iIzY2NjY2NiIgc3Ryb2tlLXdpZHRoPSIuMDQ2NDA4Ij5naW90b2hhc2hpPC90c3Bhbj48L3RleHQ+CgkJPC9nPgoJPC9nPgogIAogIDxhbmltYXRlVHJhbnNmb3JtIAogICAgYXR0cmlidXRlVHlwZT0ieG1sIgogICAgYXR0cmlidXRlTmFtZT0idHJhbnNmb3JtIgogICAgdHlwZT0icm90YXRlIiAKICAgIGZyb209IjAgMCAwIgogICAgdG89IjM2MCAwIDAiIAogICAgZHVyPSIyMi44cyIKICAgIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIgogIC8+IAo8L3N2Zz4="); - width:0px; - height:0px; - padding:2.25em 2em 2.25em 2em; - margin: 0 0.8em; - background-repeat: no-repeat; - line-height:100%; - vertical-align:middle; - color: rgba(0, 0, 0, 0); -} - -img.noentry { - display: inline-block; - background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAzMy4wNTQyMDcgMzMuMDQ0NzI0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgoJPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTY5LjY1MSAtNjguODExKSI+CgkJPGNpcmNsZSBjeD0iODYuMzk1IiBjeT0iODUuMzQiIHI9IjE1Ljk0OSIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iLjc0MiIvPgoJCTxwYXRoIGQ9Im04My4zMDYgMTAxLjY4Yy0yLjI5MDItMC40MDg2NC00Ljg2NTItMS40OTg5LTYuNzktMi44NzQ5LTMuNTAwOC0yLjUwMjYtNS45NDE1LTYuNDIyNS02LjY5NzItMTAuNzU2LTAuMjIzMDEtMS4yNzg5LTAuMjIzMDEtNC4xNDUzIDAtNS40MjQyIDAuNDIzNTYtMi40Mjg5IDEuNDc1NC00Ljk1OTIgMi44ODE2LTYuOTMxOSAyLjQ5MTMtMy40OTQ5IDYuNDQ3LTUuOTYyNCAxMC43NjYtNi43MTU1IDEuMjc4OS0wLjIyMzAyIDQuMTQ1NC0wLjIyMzAyIDUuNDI0MiAwIDIuNDM2MiAwLjQyNDg0IDQuOTU3NyAxLjQ3NDggNi45NTA0IDIuODk0MiAzLjQ2ODQgMi40NzA2IDUuOTYxOSA2LjQ1NzQgNi42ODk5IDEwLjY5NiAwLjIzMDA0IDEuMzM5NiAwLjIzMzc4IDQuMTgxOSA4ZS0zIDUuNDgxMi0wLjM4NzA1IDIuMjE5NS0xLjM2ODcgNC43MjA2LTIuNTMxMiA2LjQ0OTItMi41ODEyIDMuODM4Mi02LjUzNyA2LjM5OTctMTEuMTE2IDcuMTk4My0xLjI1MjIgMC4yMTgzNi00LjMxODQgMC4yMDkxNC01LjU4NDYtMC4wMTczem0tMy45Mjc2LTUuODQ5NmMwLTAuNDY0NzEgMC4wNjQ1LTAuNDQxODMtMS4yODk3LTAuNDU3NDctMC41NDgxMi02ZS0zIC0xLjA2MjUtMC4wNTQzLTEuMTQzMS0wLjEwNjQxLTAuMTg3OTktMC4xMjE2LTAuMTkyNTYtMS4xNjA1LTZlLTMgLTEuMzQ3MiAwLjA3NzMtMC4wNzczIDAuNDQ2NjktMC4xNDA2NSAwLjgyMDY5LTAuMTQwNjVoMC42ODAwMXYtMC44MjA3aC0wLjY4OTA5Yy0wLjgyNjc2IDAtMC45ODA2OC0wLjEzOTMtMC45MzAxNi0wLjg0MTM0bDAuMDM2Ni0wLjUwNjk0IDAuOTM3OTUtMC4wMjk0YzEuMzg3NS0wLjA0MzcgMS40ODctMC4wODAyIDEuNDQzNC0wLjUzMTRsLTAuMDM2NC0wLjM3NzAyaC0zLjE2NTZsLTAuMDMxNCAyLjc4NDUtMC4wMzE0IDIuNzg0NWgzLjQwNDN6bTEuNzU4Ni0xLjI5OTRjMC0xLjI2NTMgMC4wMzYyLTEuNjk3OCAwLjEzODkyLTEuNjYzNSAwLjA3NjQgMC4wMjU0IDAuNTYxNTMgMC43OTQ4NiAxLjA3ODEgMS43MDk4IDAuOTEzMzMgMS42MTc1IDAuOTUwMDQgMS42NjM1IDEuMzI4OCAxLjY2MzVoMC4zODk0OWwtMC4wMzE0LTIuNzg0NS0wLjAzMTQtMi43ODQ1aC0wLjcwMzQ2bC0wLjA1ODcgMS41NTQxYy0wLjAzMjMgMC44NTQ3NS0wLjEwODUxIDEuNTU5Ni0wLjE2OTMyIDEuNTY2NC0wLjA2MSA4ZS0zIC0wLjUxMDY5LTAuNjkyNTctMC45OTk1LTEuNTU0MS0wLjg3MzQtMS41Mzk0LTAuODk1NzgtMS41NjY0LTEuMjk2Mi0xLjU2NjRoLTAuNDA3NGwtMC4wMzE0IDIuNzg0NS0wLjAzMTQgMi43ODQ1aDAuODI0OTN6bTUuOTc5NC0wLjY5MzY5IDAuMDU4Ny0yLjM0NDkgMC41ODYyMy0wLjA1ODdjMC41NjAxNi0wLjA1NiAwLjU4NjIxLTAuMDc0MyAwLjU4NjIxLTAuNDEwMzV2LTAuMzUxNzJsLTEuNjYxOC0wLjAzMjVjLTEuNTkzMS0wLjAzMTItMS42NjUtMC4wMjI1LTEuNzM5IDAuMjEwOC0wLjExODMzIDAuMzcyOTkgMC4xNDEwNCAwLjU4Mzc1IDAuNzE4NTkgMC41ODM3NWgwLjUxMzIzdjIuMzI1M2MwIDEuMjc4OSAwLjAzNzcgMi4zNjMgMC4wODM3IDIuNDA5IDAuMDQ2IDAuMDQ2IDAuMjQzODkgMC4wNjgxIDAuNDM5NjcgMC4wNDg5bDAuMzU1OTQtMC4wMzQ4em0zLjA0ODMgMS4xNzI0IDAuMDU4Ny0xLjE3MjQgMC40MjAzNy0wLjAzNTJjMC4yNDM0LTAuMDIwNCAwLjQ3NDAyIDAuMDI5NCAwLjU0Nzc1IDAuMTE4MzQgMC4wNyAwLjA4NDUgMC4zMDQxOCAwLjYyNzg0IDAuNTIwMjcgMS4yMDc2IDAuMzkxNzMgMS4wNTExIDAuMzk0MTkgMS4wNTQzIDAuODI5ODQgMS4wOTA2IDAuNTM2MzIgMC4wNDQ2IDAuNTQwNzEtNmUtMyAwLjEwMTAxLTEuMTUyOS0wLjUyODg5LTEuMzc5OC0wLjUyNDA4LTEuMzE3OS0wLjEzNTQ1LTEuNzUzIDAuNTUwMjUtMC42MTYzNCAwLjYwNTQzLTEuMzg2NiAwLjE0OTExLTIuMDgyOS0wLjMwNDEtMC40NjQwOS0wLjgyOTM1LTAuNjE2NjMtMi4xMjM0LTAuNjE2NjMtMC45NTI5NSAwLTEuMTc2NSAwLjAzMzctMS4yMzQ4IDAuMTg1NDgtMC4xMDA2MyAwLjI2MjI0LTAuMDg4OSA1LjI2ODEgMC4wMTM1IDUuMzY5NiAwLjA0NiAwLjA0NiAwLjI0Mzg4IDAuMDY3OSAwLjQzOTY2IDAuMDQ4OWwwLjM1NTkzLTAuMDM0OHptMC4wNDc1LTIuMjE0MmMtMC4wMzQzLTAuMDg5My0wLjA0NTgtMC4zOTI3MS0wLjAyNTYtMC42NzQxNGwwLjAzNjctMC41MTE2OSAwLjcxOTE0LTAuMDM0NmMwLjU0MDI1LTAuMDI2IDAuNzY2MjkgMC4wMTM1IDAuOTA4NjMgMC4xNTQ4OSAwLjI3ODA1IDAuMjc4MDcgMC4yNDA3MiAwLjgyNzAxLTAuMDcxIDEuMDQ1NC0wLjMzNjk0IDAuMjM2MDEtMS40NzkzIDAuMjUwNjItMS41Njc4IDAuMDJ6bTYuMzc1NiAyLjE4MTEgMC4wMzM1LTEuMjA1NiAwLjg3NTE3LTEuNDM4MmMwLjQ4MTM1LTAuNzkxMDMgMC44NzUxOS0xLjUwNDcgMC44NzUxOS0xLjU4NiAwLTAuMDk5MS0wLjE0NDY5LTAuMTM1ODQtMC40Mzg5NC0wLjExMTQtMC4zOTgxOSAwLjAzMzEtMC40ODQzNyAwLjEwNzE3LTAuOTI4MjcgMC43OTg0Ni0wLjI2OTE1IDAuNDE5MTYtMC41NzI4MiAwLjg2NzYtMC42NzQ4OCAwLjk5NjU4bC0wLjE4NTQ4IDAuMjM0NDYtMC4xODU0OC0wLjIzNDQ2Yy0wLjEwMTk4LTAuMTI4OTItMC40MDU3My0wLjU3NzQyLTAuNjc0ODctMC45OTY1OC0wLjQ0MzkxLTAuNjkxMzMtMC41MzAwOS0wLjc2NTQ0LTAuOTI4MjgtMC43OTg0Ni0wLjMxMzI2LTAuMDI2LTAuNDM4OTQgMC4wMS0wLjQzODk0IDAuMTI2MDMgMCAwLjA4OTMgMC4zOTU3MSAwLjgwMDIgMC44NzkzMyAxLjU3OThsMC44NzkzMiAxLjQxNzV2MS4xNjI5YzAgMC42Mzk2MSAwLjAzNzcgMS4yMDA2IDAuMDgzNyAxLjI0NjYgMC4wNDYgMC4wNDYgMC4yNDM4OCAwLjA2ODEgMC40Mzk2NSAwLjA0ODlsMC4zNTU5Ni0wLjAzNDh6bTMuNjM2Mi02LjczMThjMC4xNDg3NC0wLjE0ODczIDAuMjExMzgtNC45MzE4IDAuMDY5NS01LjMwMTUtMC4wNjQzLTAuMTY3NTktMS40MTctMC4xODU0OC0xNC4xMTUtMC4xODU0OC0xMi42OTggMC0xNC4wNTEgMC4wMTczLTE0LjExNSAwLjE4NTQ4LTAuMTI0MyAwLjMyMzktMC4wNzk1IDQuOTczIDAuMDUwMiA1LjIxNTQgMC4xMTg5MSAwLjIyMjI1IDAuMzk5MTIgMC4yMjY3OSAxNC4wNDYgMC4yMjY3OSAxMC42NzQgMCAxMy45NTctMC4wMzI5IDE0LjA2NS0wLjE0MDY1em0tMTAuNDQtOC40OTYyYzAuNzAwNzctMC41MjMyIDEuMDYyNC0xLjkyOTYgMC44NDUzNy0zLjI4OC0wLjIxNjIxLTEuMzUzMy0wLjg0MTIxLTIuMDI1Ny0xLjg4MzktMi4wMjY1LTEuMDcxLTllLTQgLTEuODAyMyAwLjg4Njc1LTEuOTM5MyAyLjM1MzYtMC4xMzQzIDEuNDM4OSAwLjMxMTM4IDIuNjQ4NyAxLjEzMjkgMy4wNzQ5IDAuNTExMjUgMC4yNjUyNCAxLjQxMTUgMC4yMDk1OSAxLjg0NDktMC4xMTM5em0tMS42NDUxLTAuODMwNjVjLTAuMzExNDktMC4zNDQxOC0wLjQzNjY3LTAuODI2MzctMC40MzY2Ny0xLjY4MTkgMC0wLjk0OTY1IDAuMjA1MzUtMS41Mjg3IDAuNjQ0MTItMS44MTYyIDAuNjY0MjctMC40MzUyNCAxLjMyODIgMC4xODA0OCAxLjQzMjMgMS4zMjgzIDAuMTM5MTEgMS41MzMyLTAuMjM1NDEgMi4zNzUyLTEuMDU2MyAyLjM3NTItMC4yMjk4MyAwLTAuNDc1OS0wLjA4NjYtMC41ODMzOC0wLjIwNTR6bS01LjY2NjUgMC44NDA2NGMwLjAzOTEtMC4xMDE5OCAwLjA3MTItMC44NDA2MiAwLjA3MTItMS42NDE0IDAtMC44MDA3NyAwLjAzOTYtMS40NTU5IDAuMDg3OS0xLjQ1NTcgMC4wNDgzIDllLTUgMC40OTEzOSAwLjcyNTUzIDAuOTg0NSAxLjYxMjEgMC44MjcwOSAxLjQ4NyAwLjkyNDc1IDEuNjE0OCAxLjI2MDQgMS42NDg2IDAuMjAwMzggMC4wMjAyIDAuMzY1NDgtMC4wMTkyIDAuMzY3NTQtMC4wODc5IDJlLTMgLTAuMDY4NSA0ZS0zIC0xLjIzMjYgNGUtMyAtMi41ODY4IDAtMS4zNTQyLTJlLTMgLTIuNTE4Mi00ZS0zIC0yLjU4NjgtMmUtMyAtMC4wNjk1LTAuMTcyMjEtMC4xMDg1Mi0wLjM4NDc4LTAuMDg3OWwtMC4zODEwMiAwLjAzNjctMC4wMzI3IDEuNDk0OGMtMC4wMTczIDAuODIyMTYtMC4wNzM5IDEuNDk0OC0wLjEyMzkyIDEuNDk0OC0wLjA1MDIgMC0wLjQ2NTEyLTAuNjcyNjgtMC45MjIxOS0xLjQ5NDgtMC43NzY1OC0xLjM5Ny0wLjg1NTk3LTEuNDk3NC0xLjIxMi0xLjUzMjktMC4yMjU0Ni0wLjAyMjUtMC40MjExNiAwLjAyNTQtMC40Nzk0MiAwLjExNzE3LTAuMTExNzkgMC4xNzY0NC0wLjEzOTg4IDQuNzg1Ny0wLjAzMDggNS4wNjk4IDAuMDQxNCAwLjEwNzc1IDAuMjA4MTEgMC4xODU0OCAwLjM5NzgxIDAuMTg1NDggMC4xODk3MSAwIDAuMzU2NDktMC4wNzc3IDAuMzk3ODItMC4xODU0OHoiIGZpbGw9IiNlYzIyMjkiIHN0cm9rZS13aWR0aD0iLjExNzI0Ii8+Cgk8L2c+Cjwvc3ZnPgo="); - width:0px; - height:0px; - padding:2.25em 2em 2.25em 2em; - margin: 0 0.8em; - background-repeat: no-repeat; - line-height:100%; - vertical-align:middle; - font-size: 20pt; - color: rgba(0, 0, 0, 0); -} - -div.searchbg { - background-repeat: no-repeat; - background-image: url("dorian-gray-wikipedia.jpg"); - background-position: left top; - width: 561px; - height: 844px; - padding: 10px; - padding-top: 20px; - text-align:center; -} - -table.searchtable { - position:relative; - display:inline-table; - padding-top: 6px; -} - -div.acomplete { - position:absolute; - display:block; - float:right; - text-align:left; - background-color: #aaa; - font-size: 12pt; - max-height: 50vh; - right:0px; - margin: 0px; - padding: 0px 1px; - overflow:auto; - opacity: 0; - z-index: 4; - border: 1px solid gray; - border-radius: 3px; - background-color: white; - white-space: nowrap; - box-shadow: 0px 5px 15px gray; - transition: opacity 0.3s; - font-weight:normal -} - -div.acomplete ul { - list-style-type: none; - padding: 0px 2px; - cursor: pointer; -} - -div.acomplete ul li { - margin: 2px; - padding: 1px; - font-size: 14px; - left: 0px; -} - -div.acomplete ul li:hover { - background-color: lightblue; -} - -div.acomplete ul li:active { - background-color: blue; - color: white; -} - -div.searchresults { - position:absolute; - display:block; - float:right; - text-align:left; - font-size: 9pt; - width: 100%; - max-height: 600px; - left:0px; - margin: 4px 20px; - margin-top: 24px; - padding: 0px 20px; - overflow: scroll; - opacity: 0; - z-index: 3; - border: 1px solid gray; - border-radius: 3px; - background-color: rgba(255,255,255,0.7); - white-space: nowrap; - box-shadow: 0px 5px 15px gray; - transition: opacity 0.3s; - font-weight:normal; -} - -div.filepath { - font-size: 14pt; - padding: 12px 0px; -} - -input.viable { - color: #000 -} - -input.nonviable { - color: #aaa -} - -td.searchboxtitle { - text-align:right; - font-size: 15pt; -} - -td.r { - text-align:right; - color: #aaa; - width:99%; -} - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/lws-fts.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/lws-fts.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/lws-fts.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/lws-fts.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,220 +0,0 @@ -/* lws-fts.js - JS supporting lws fulltext search - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - */ - - -(function() { - - var last_ac = ""; - - function san(s) - { - s.replace(/"; - - s += ""; - for (m = 0; m < jj.fp[n].hits.length; m++) - s += ""; - - s += "
" + jj.fp[n].hits[m].l + - "" + jj.fp[n].hits[m].s + - "
"; - - } - } - - sr.innerHTML = s; - }; - - inp.blur(); - ac.style.opacity = "0"; - sr.style.innerHTML = ""; - xhr.open("GET", "../fts/r/" + document.getElementById("lws_fts").value); - xhr.send(); - } - - function lws_fts_ac_select(e) - { - var t = e.target; - - while (t) { - if (t.getAttribute && t.getAttribute("string")) { - document.getElementById("lws_fts").value = - t.getAttribute("string"); - - lws_fts_choose(); - } - - t = t.parentNode; - } - } - - function lws_fts_search_input() - { - var ac = document.getElementById("acomplete"), - sb = document.getElementById("lws_fts"); - - if (last_ac === sb.value) - return; - - last_ac = sb.value; - - ac.style.width = (parseInt(sb.offsetWidth, 10) - 2) + "px"; - ac.style.opacity = "1"; - - /* detect loss of focus for popup menu */ - sb.addEventListener("focusout", function(e) { - ac.style.opacity = "0"; - }); - - - var xhr = new XMLHttpRequest(); - - xhr.onopen = function(e) { - xhr.setRequestHeader("cache-control", "max-age=0"); - }; - xhr.onload = function(e) { - var jj, n, s = "", lic = 0; - var inp = document.getElementById("lws_fts"); - var ac = document.getElementById("acomplete"); - - // console.log(xhr.responseText); - jj = JSON.parse(xhr.responseText); - - switch(parseInt(jj.indexed, 10)) { - case 0: /* there is no index */ - break; - - case 1: /* yay there is an index */ - - if (jj.ac) { - lic = jj.ac.length; - s += ""; - - if (!lic) { - //s = ""; - inp.className = "nonviable"; - ac.style.opacity = "0"; - } else { - inp.className = "viable"; - ac.style.opacity = "1"; - } - } - - break; - - default: - - /* an index is being built... */ - - s = "
" + - "
Indexing
" + - "
" + - "
" + - jj.index_done + " / " + jj.index_files + - "
" + - "
"; - - setTimeout(lws_fts_search_input, 300); - - break; - } - - ac.innerHTML = s; - - for (n = 0; n < lic; n++) - if (document.getElementById("mi_ac" + n)) - document.getElementById("mi_ac" + n). - addEventListener("click", lws_fts_ac_select); - if (jj.index_files) { - document.getElementById("bar2").style.width = - ((150 * jj.index_done) / (jj.index_files + 1)) + "px"; - } - }; - - xhr.open("GET", "../fts/a/" + document.getElementById("lws_fts").value); - xhr.send(); - } - - document.addEventListener("DOMContentLoaded", function() { - var inp = document.getElementById("lws_fts"); - - inp.addEventListener("input", lws_fts_search_input, false); - - inp.addEventListener("keydown", - function(e) { - var inp = document.getElementById("lws_fts"); - var sr = document.getElementById("searchresults"); - var ac = document.getElementById("acomplete"); - if (e.key === "Enter" && inp.className === "viable") { - lws_fts_choose(); - sr.focus(); - ac.style.opacity = "0"; - } - }, false); - - }, false); - -}()); diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -# lws minimal http server - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server -[2018/03/04 09:30:02:7986] USER: LWS minimal http server | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/the-picture-of-dorian-gray.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/the-picture-of-dorian-gray.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-fulltext-search/the-picture-of-dorian-gray.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-fulltext-search/the-picture-of-dorian-gray.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,8904 +0,0 @@ -The Project Gutenberg EBook of The Picture of Dorian Gray, by Oscar Wilde - -This eBook is for the use of anyone anywhere at no cost and with -almost no restrictions whatsoever. You may copy it, give it away or -re-use it under the terms of the Project Gutenberg License included -with this eBook or online at www.gutenberg.net - - -Title: The Picture of Dorian Gray - -Author: Oscar Wilde - -Release Date: June 9, 2008 [EBook #174] -[This file last updated on July 2, 2011] -[This file last updated on July 23, 2014] - - -Language: English - - -*** START OF THIS PROJECT GUTENBERG EBOOK THE PICTURE OF DORIAN GRAY *** - - - - -Produced by Judith Boss. HTML version by Al Haines. - - - - - - - - - - -The Picture of Dorian Gray - -by - -Oscar Wilde - - - - -THE PREFACE - -The artist is the creator of beautiful things. To reveal art and -conceal the artist is art's aim. The critic is he who can translate -into another manner or a new material his impression of beautiful -things. - -The highest as the lowest form of criticism is a mode of autobiography. -Those who find ugly meanings in beautiful things are corrupt without -being charming. This is a fault. - -Those who find beautiful meanings in beautiful things are the -cultivated. For these there is hope. They are the elect to whom -beautiful things mean only beauty. - -There is no such thing as a moral or an immoral book. Books are well -written, or badly written. That is all. - -The nineteenth century dislike of realism is the rage of Caliban seeing -his own face in a glass. - -The nineteenth century dislike of romanticism is the rage of Caliban -not seeing his own face in a glass. The moral life of man forms part -of the subject-matter of the artist, but the morality of art consists -in the perfect use of an imperfect medium. No artist desires to prove -anything. Even things that are true can be proved. No artist has -ethical sympathies. An ethical sympathy in an artist is an -unpardonable mannerism of style. No artist is ever morbid. The artist -can express everything. Thought and language are to the artist -instruments of an art. Vice and virtue are to the artist materials for -an art. From the point of view of form, the type of all the arts is -the art of the musician. From the point of view of feeling, the -actor's craft is the type. All art is at once surface and symbol. -Those who go beneath the surface do so at their peril. Those who read -the symbol do so at their peril. It is the spectator, and not life, -that art really mirrors. Diversity of opinion about a work of art -shows that the work is new, complex, and vital. When critics disagree, -the artist is in accord with himself. We can forgive a man for making -a useful thing as long as he does not admire it. The only excuse for -making a useless thing is that one admires it intensely. - - All art is quite useless. - - OSCAR WILDE - - - - -CHAPTER 1 - -The studio was filled with the rich odour of roses, and when the light -summer wind stirred amidst the trees of the garden, there came through -the open door the heavy scent of the lilac, or the more delicate -perfume of the pink-flowering thorn. - -From the corner of the divan of Persian saddle-bags on which he was -lying, smoking, as was his custom, innumerable cigarettes, Lord Henry -Wotton could just catch the gleam of the honey-sweet and honey-coloured -blossoms of a laburnum, whose tremulous branches seemed hardly able to -bear the burden of a beauty so flamelike as theirs; and now and then -the fantastic shadows of birds in flight flitted across the long -tussore-silk curtains that were stretched in front of the huge window, -producing a kind of momentary Japanese effect, and making him think of -those pallid, jade-faced painters of Tokyo who, through the medium of -an art that is necessarily immobile, seek to convey the sense of -swiftness and motion. The sullen murmur of the bees shouldering their -way through the long unmown grass, or circling with monotonous -insistence round the dusty gilt horns of the straggling woodbine, -seemed to make the stillness more oppressive. The dim roar of London -was like the bourdon note of a distant organ. - -In the centre of the room, clamped to an upright easel, stood the -full-length portrait of a young man of extraordinary personal beauty, -and in front of it, some little distance away, was sitting the artist -himself, Basil Hallward, whose sudden disappearance some years ago -caused, at the time, such public excitement and gave rise to so many -strange conjectures. - -As the painter looked at the gracious and comely form he had so -skilfully mirrored in his art, a smile of pleasure passed across his -face, and seemed about to linger there. But he suddenly started up, -and closing his eyes, placed his fingers upon the lids, as though he -sought to imprison within his brain some curious dream from which he -feared he might awake. - -"It is your best work, Basil, the best thing you have ever done," said -Lord Henry languidly. "You must certainly send it next year to the -Grosvenor. The Academy is too large and too vulgar. Whenever I have -gone there, there have been either so many people that I have not been -able to see the pictures, which was dreadful, or so many pictures that -I have not been able to see the people, which was worse. The Grosvenor -is really the only place." - -"I don't think I shall send it anywhere," he answered, tossing his head -back in that odd way that used to make his friends laugh at him at -Oxford. "No, I won't send it anywhere." - -Lord Henry elevated his eyebrows and looked at him in amazement through -the thin blue wreaths of smoke that curled up in such fanciful whorls -from his heavy, opium-tainted cigarette. "Not send it anywhere? My -dear fellow, why? Have you any reason? What odd chaps you painters -are! You do anything in the world to gain a reputation. As soon as -you have one, you seem to want to throw it away. It is silly of you, -for there is only one thing in the world worse than being talked about, -and that is not being talked about. A portrait like this would set you -far above all the young men in England, and make the old men quite -jealous, if old men are ever capable of any emotion." - -"I know you will laugh at me," he replied, "but I really can't exhibit -it. I have put too much of myself into it." - -Lord Henry stretched himself out on the divan and laughed. - -"Yes, I knew you would; but it is quite true, all the same." - -"Too much of yourself in it! Upon my word, Basil, I didn't know you -were so vain; and I really can't see any resemblance between you, with -your rugged strong face and your coal-black hair, and this young -Adonis, who looks as if he was made out of ivory and rose-leaves. Why, -my dear Basil, he is a Narcissus, and you--well, of course you have an -intellectual expression and all that. But beauty, real beauty, ends -where an intellectual expression begins. Intellect is in itself a mode -of exaggeration, and destroys the harmony of any face. The moment one -sits down to think, one becomes all nose, or all forehead, or something -horrid. Look at the successful men in any of the learned professions. -How perfectly hideous they are! Except, of course, in the Church. But -then in the Church they don't think. A bishop keeps on saying at the -age of eighty what he was told to say when he was a boy of eighteen, -and as a natural consequence he always looks absolutely delightful. -Your mysterious young friend, whose name you have never told me, but -whose picture really fascinates me, never thinks. I feel quite sure of -that. He is some brainless beautiful creature who should be always -here in winter when we have no flowers to look at, and always here in -summer when we want something to chill our intelligence. Don't flatter -yourself, Basil: you are not in the least like him." - -"You don't understand me, Harry," answered the artist. "Of course I am -not like him. I know that perfectly well. Indeed, I should be sorry -to look like him. You shrug your shoulders? I am telling you the -truth. There is a fatality about all physical and intellectual -distinction, the sort of fatality that seems to dog through history the -faltering steps of kings. It is better not to be different from one's -fellows. The ugly and the stupid have the best of it in this world. -They can sit at their ease and gape at the play. If they know nothing -of victory, they are at least spared the knowledge of defeat. They -live as we all should live--undisturbed, indifferent, and without -disquiet. They neither bring ruin upon others, nor ever receive it -from alien hands. Your rank and wealth, Harry; my brains, such as they -are--my art, whatever it may be worth; Dorian Gray's good looks--we -shall all suffer for what the gods have given us, suffer terribly." - -"Dorian Gray? Is that his name?" asked Lord Henry, walking across the -studio towards Basil Hallward. - -"Yes, that is his name. I didn't intend to tell it to you." - -"But why not?" - -"Oh, I can't explain. When I like people immensely, I never tell their -names to any one. It is like surrendering a part of them. I have -grown to love secrecy. It seems to be the one thing that can make -modern life mysterious or marvellous to us. The commonest thing is -delightful if one only hides it. When I leave town now I never tell my -people where I am going. If I did, I would lose all my pleasure. It -is a silly habit, I dare say, but somehow it seems to bring a great -deal of romance into one's life. I suppose you think me awfully -foolish about it?" - -"Not at all," answered Lord Henry, "not at all, my dear Basil. You -seem to forget that I am married, and the one charm of marriage is that -it makes a life of deception absolutely necessary for both parties. I -never know where my wife is, and my wife never knows what I am doing. -When we meet--we do meet occasionally, when we dine out together, or go -down to the Duke's--we tell each other the most absurd stories with the -most serious faces. My wife is very good at it--much better, in fact, -than I am. She never gets confused over her dates, and I always do. -But when she does find me out, she makes no row at all. I sometimes -wish she would; but she merely laughs at me." - -"I hate the way you talk about your married life, Harry," said Basil -Hallward, strolling towards the door that led into the garden. "I -believe that you are really a very good husband, but that you are -thoroughly ashamed of your own virtues. You are an extraordinary -fellow. You never say a moral thing, and you never do a wrong thing. -Your cynicism is simply a pose." - -"Being natural is simply a pose, and the most irritating pose I know," -cried Lord Henry, laughing; and the two young men went out into the -garden together and ensconced themselves on a long bamboo seat that -stood in the shade of a tall laurel bush. The sunlight slipped over -the polished leaves. In the grass, white daisies were tremulous. - -After a pause, Lord Henry pulled out his watch. "I am afraid I must be -going, Basil," he murmured, "and before I go, I insist on your -answering a question I put to you some time ago." - -"What is that?" said the painter, keeping his eyes fixed on the ground. - -"You know quite well." - -"I do not, Harry." - -"Well, I will tell you what it is. I want you to explain to me why you -won't exhibit Dorian Gray's picture. I want the real reason." - -"I told you the real reason." - -"No, you did not. You said it was because there was too much of -yourself in it. Now, that is childish." - -"Harry," said Basil Hallward, looking him straight in the face, "every -portrait that is painted with feeling is a portrait of the artist, not -of the sitter. The sitter is merely the accident, the occasion. It is -not he who is revealed by the painter; it is rather the painter who, on -the coloured canvas, reveals himself. The reason I will not exhibit -this picture is that I am afraid that I have shown in it the secret of -my own soul." - -Lord Henry laughed. "And what is that?" he asked. - -"I will tell you," said Hallward; but an expression of perplexity came -over his face. - -"I am all expectation, Basil," continued his companion, glancing at him. - -"Oh, there is really very little to tell, Harry," answered the painter; -"and I am afraid you will hardly understand it. Perhaps you will -hardly believe it." - -Lord Henry smiled, and leaning down, plucked a pink-petalled daisy from -the grass and examined it. "I am quite sure I shall understand it," he -replied, gazing intently at the little golden, white-feathered disk, -"and as for believing things, I can believe anything, provided that it -is quite incredible." - -The wind shook some blossoms from the trees, and the heavy -lilac-blooms, with their clustering stars, moved to and fro in the -languid air. A grasshopper began to chirrup by the wall, and like a -blue thread a long thin dragon-fly floated past on its brown gauze -wings. Lord Henry felt as if he could hear Basil Hallward's heart -beating, and wondered what was coming. - -"The story is simply this," said the painter after some time. "Two -months ago I went to a crush at Lady Brandon's. You know we poor -artists have to show ourselves in society from time to time, just to -remind the public that we are not savages. With an evening coat and a -white tie, as you told me once, anybody, even a stock-broker, can gain -a reputation for being civilized. Well, after I had been in the room -about ten minutes, talking to huge overdressed dowagers and tedious -academicians, I suddenly became conscious that some one was looking at -me. I turned half-way round and saw Dorian Gray for the first time. -When our eyes met, I felt that I was growing pale. A curious sensation -of terror came over me. I knew that I had come face to face with some -one whose mere personality was so fascinating that, if I allowed it to -do so, it would absorb my whole nature, my whole soul, my very art -itself. I did not want any external influence in my life. You know -yourself, Harry, how independent I am by nature. I have always been my -own master; had at least always been so, till I met Dorian Gray. -Then--but I don't know how to explain it to you. Something seemed to -tell me that I was on the verge of a terrible crisis in my life. I had -a strange feeling that fate had in store for me exquisite joys and -exquisite sorrows. I grew afraid and turned to quit the room. It was -not conscience that made me do so: it was a sort of cowardice. I take -no credit to myself for trying to escape." - -"Conscience and cowardice are really the same things, Basil. -Conscience is the trade-name of the firm. That is all." - -"I don't believe that, Harry, and I don't believe you do either. -However, whatever was my motive--and it may have been pride, for I used -to be very proud--I certainly struggled to the door. There, of course, -I stumbled against Lady Brandon. 'You are not going to run away so -soon, Mr. Hallward?' she screamed out. You know her curiously shrill -voice?" - -"Yes; she is a peacock in everything but beauty," said Lord Henry, -pulling the daisy to bits with his long nervous fingers. - -"I could not get rid of her. She brought me up to royalties, and -people with stars and garters, and elderly ladies with gigantic tiaras -and parrot noses. She spoke of me as her dearest friend. I had only -met her once before, but she took it into her head to lionize me. I -believe some picture of mine had made a great success at the time, at -least had been chattered about in the penny newspapers, which is the -nineteenth-century standard of immortality. Suddenly I found myself -face to face with the young man whose personality had so strangely -stirred me. We were quite close, almost touching. Our eyes met again. -It was reckless of me, but I asked Lady Brandon to introduce me to him. -Perhaps it was not so reckless, after all. It was simply inevitable. -We would have spoken to each other without any introduction. I am sure -of that. Dorian told me so afterwards. He, too, felt that we were -destined to know each other." - -"And how did Lady Brandon describe this wonderful young man?" asked his -companion. "I know she goes in for giving a rapid _precis_ of all her -guests. I remember her bringing me up to a truculent and red-faced old -gentleman covered all over with orders and ribbons, and hissing into my -ear, in a tragic whisper which must have been perfectly audible to -everybody in the room, the most astounding details. I simply fled. I -like to find out people for myself. But Lady Brandon treats her guests -exactly as an auctioneer treats his goods. She either explains them -entirely away, or tells one everything about them except what one wants -to know." - -"Poor Lady Brandon! You are hard on her, Harry!" said Hallward -listlessly. - -"My dear fellow, she tried to found a _salon_, and only succeeded in -opening a restaurant. How could I admire her? But tell me, what did -she say about Mr. Dorian Gray?" - -"Oh, something like, 'Charming boy--poor dear mother and I absolutely -inseparable. Quite forget what he does--afraid he--doesn't do -anything--oh, yes, plays the piano--or is it the violin, dear Mr. -Gray?' Neither of us could help laughing, and we became friends at -once." - -"Laughter is not at all a bad beginning for a friendship, and it is far -the best ending for one," said the young lord, plucking another daisy. - -Hallward shook his head. "You don't understand what friendship is, -Harry," he murmured--"or what enmity is, for that matter. You like -every one; that is to say, you are indifferent to every one." - -"How horribly unjust of you!" cried Lord Henry, tilting his hat back -and looking up at the little clouds that, like ravelled skeins of -glossy white silk, were drifting across the hollowed turquoise of the -summer sky. "Yes; horribly unjust of you. I make a great difference -between people. I choose my friends for their good looks, my -acquaintances for their good characters, and my enemies for their good -intellects. A man cannot be too careful in the choice of his enemies. -I have not got one who is a fool. They are all men of some -intellectual power, and consequently they all appreciate me. Is that -very vain of me? I think it is rather vain." - -"I should think it was, Harry. But according to your category I must -be merely an acquaintance." - -"My dear old Basil, you are much more than an acquaintance." - -"And much less than a friend. A sort of brother, I suppose?" - -"Oh, brothers! I don't care for brothers. My elder brother won't die, -and my younger brothers seem never to do anything else." - -"Harry!" exclaimed Hallward, frowning. - -"My dear fellow, I am not quite serious. But I can't help detesting my -relations. I suppose it comes from the fact that none of us can stand -other people having the same faults as ourselves. I quite sympathize -with the rage of the English democracy against what they call the vices -of the upper orders. The masses feel that drunkenness, stupidity, and -immorality should be their own special property, and that if any one of -us makes an ass of himself, he is poaching on their preserves. When -poor Southwark got into the divorce court, their indignation was quite -magnificent. And yet I don't suppose that ten per cent of the -proletariat live correctly." - -"I don't agree with a single word that you have said, and, what is -more, Harry, I feel sure you don't either." - -Lord Henry stroked his pointed brown beard and tapped the toe of his -patent-leather boot with a tasselled ebony cane. "How English you are -Basil! That is the second time you have made that observation. If one -puts forward an idea to a true Englishman--always a rash thing to -do--he never dreams of considering whether the idea is right or wrong. -The only thing he considers of any importance is whether one believes -it oneself. Now, the value of an idea has nothing whatsoever to do -with the sincerity of the man who expresses it. Indeed, the -probabilities are that the more insincere the man is, the more purely -intellectual will the idea be, as in that case it will not be coloured -by either his wants, his desires, or his prejudices. However, I don't -propose to discuss politics, sociology, or metaphysics with you. I -like persons better than principles, and I like persons with no -principles better than anything else in the world. Tell me more about -Mr. Dorian Gray. How often do you see him?" - -"Every day. I couldn't be happy if I didn't see him every day. He is -absolutely necessary to me." - -"How extraordinary! I thought you would never care for anything but -your art." - -"He is all my art to me now," said the painter gravely. "I sometimes -think, Harry, that there are only two eras of any importance in the -world's history. The first is the appearance of a new medium for art, -and the second is the appearance of a new personality for art also. -What the invention of oil-painting was to the Venetians, the face of -Antinous was to late Greek sculpture, and the face of Dorian Gray will -some day be to me. It is not merely that I paint from him, draw from -him, sketch from him. Of course, I have done all that. But he is much -more to me than a model or a sitter. I won't tell you that I am -dissatisfied with what I have done of him, or that his beauty is such -that art cannot express it. There is nothing that art cannot express, -and I know that the work I have done, since I met Dorian Gray, is good -work, is the best work of my life. But in some curious way--I wonder -will you understand me?--his personality has suggested to me an -entirely new manner in art, an entirely new mode of style. I see -things differently, I think of them differently. I can now recreate -life in a way that was hidden from me before. 'A dream of form in days -of thought'--who is it who says that? I forget; but it is what Dorian -Gray has been to me. The merely visible presence of this lad--for he -seems to me little more than a lad, though he is really over -twenty--his merely visible presence--ah! I wonder can you realize all -that that means? Unconsciously he defines for me the lines of a fresh -school, a school that is to have in it all the passion of the romantic -spirit, all the perfection of the spirit that is Greek. The harmony of -soul and body--how much that is! We in our madness have separated the -two, and have invented a realism that is vulgar, an ideality that is -void. Harry! if you only knew what Dorian Gray is to me! You remember -that landscape of mine, for which Agnew offered me such a huge price -but which I would not part with? It is one of the best things I have -ever done. And why is it so? Because, while I was painting it, Dorian -Gray sat beside me. Some subtle influence passed from him to me, and -for the first time in my life I saw in the plain woodland the wonder I -had always looked for and always missed." - -"Basil, this is extraordinary! I must see Dorian Gray." - -Hallward got up from the seat and walked up and down the garden. After -some time he came back. "Harry," he said, "Dorian Gray is to me simply -a motive in art. You might see nothing in him. I see everything in -him. He is never more present in my work than when no image of him is -there. He is a suggestion, as I have said, of a new manner. I find -him in the curves of certain lines, in the loveliness and subtleties of -certain colours. That is all." - -"Then why won't you exhibit his portrait?" asked Lord Henry. - -"Because, without intending it, I have put into it some expression of -all this curious artistic idolatry, of which, of course, I have never -cared to speak to him. He knows nothing about it. He shall never know -anything about it. But the world might guess it, and I will not bare -my soul to their shallow prying eyes. My heart shall never be put -under their microscope. There is too much of myself in the thing, -Harry--too much of myself!" - -"Poets are not so scrupulous as you are. They know how useful passion -is for publication. Nowadays a broken heart will run to many editions." - -"I hate them for it," cried Hallward. "An artist should create -beautiful things, but should put nothing of his own life into them. We -live in an age when men treat art as if it were meant to be a form of -autobiography. We have lost the abstract sense of beauty. Some day I -will show the world what it is; and for that reason the world shall -never see my portrait of Dorian Gray." - -"I think you are wrong, Basil, but I won't argue with you. It is only -the intellectually lost who ever argue. Tell me, is Dorian Gray very -fond of you?" - -The painter considered for a few moments. "He likes me," he answered -after a pause; "I know he likes me. Of course I flatter him -dreadfully. I find a strange pleasure in saying things to him that I -know I shall be sorry for having said. As a rule, he is charming to -me, and we sit in the studio and talk of a thousand things. Now and -then, however, he is horribly thoughtless, and seems to take a real -delight in giving me pain. Then I feel, Harry, that I have given away -my whole soul to some one who treats it as if it were a flower to put -in his coat, a bit of decoration to charm his vanity, an ornament for a -summer's day." - -"Days in summer, Basil, are apt to linger," murmured Lord Henry. -"Perhaps you will tire sooner than he will. It is a sad thing to think -of, but there is no doubt that genius lasts longer than beauty. That -accounts for the fact that we all take such pains to over-educate -ourselves. In the wild struggle for existence, we want to have -something that endures, and so we fill our minds with rubbish and -facts, in the silly hope of keeping our place. The thoroughly -well-informed man--that is the modern ideal. And the mind of the -thoroughly well-informed man is a dreadful thing. It is like a -_bric-a-brac_ shop, all monsters and dust, with everything priced above -its proper value. I think you will tire first, all the same. Some day -you will look at your friend, and he will seem to you to be a little -out of drawing, or you won't like his tone of colour, or something. -You will bitterly reproach him in your own heart, and seriously think -that he has behaved very badly to you. The next time he calls, you -will be perfectly cold and indifferent. It will be a great pity, for -it will alter you. What you have told me is quite a romance, a romance -of art one might call it, and the worst of having a romance of any kind -is that it leaves one so unromantic." - -"Harry, don't talk like that. As long as I live, the personality of -Dorian Gray will dominate me. You can't feel what I feel. You change -too often." - -"Ah, my dear Basil, that is exactly why I can feel it. Those who are -faithful know only the trivial side of love: it is the faithless who -know love's tragedies." And Lord Henry struck a light on a dainty -silver case and began to smoke a cigarette with a self-conscious and -satisfied air, as if he had summed up the world in a phrase. There was -a rustle of chirruping sparrows in the green lacquer leaves of the ivy, -and the blue cloud-shadows chased themselves across the grass like -swallows. How pleasant it was in the garden! And how delightful other -people's emotions were!--much more delightful than their ideas, it -seemed to him. One's own soul, and the passions of one's -friends--those were the fascinating things in life. He pictured to -himself with silent amusement the tedious luncheon that he had missed -by staying so long with Basil Hallward. Had he gone to his aunt's, he -would have been sure to have met Lord Goodbody there, and the whole -conversation would have been about the feeding of the poor and the -necessity for model lodging-houses. Each class would have preached the -importance of those virtues, for whose exercise there was no necessity -in their own lives. The rich would have spoken on the value of thrift, -and the idle grown eloquent over the dignity of labour. It was -charming to have escaped all that! As he thought of his aunt, an idea -seemed to strike him. He turned to Hallward and said, "My dear fellow, -I have just remembered." - -"Remembered what, Harry?" - -"Where I heard the name of Dorian Gray." - -"Where was it?" asked Hallward, with a slight frown. - -"Don't look so angry, Basil. It was at my aunt, Lady Agatha's. She -told me she had discovered a wonderful young man who was going to help -her in the East End, and that his name was Dorian Gray. I am bound to -state that she never told me he was good-looking. Women have no -appreciation of good looks; at least, good women have not. She said -that he was very earnest and had a beautiful nature. I at once -pictured to myself a creature with spectacles and lank hair, horribly -freckled, and tramping about on huge feet. I wish I had known it was -your friend." - -"I am very glad you didn't, Harry." - -"Why?" - -"I don't want you to meet him." - -"You don't want me to meet him?" - -"No." - -"Mr. Dorian Gray is in the studio, sir," said the butler, coming into -the garden. - -"You must introduce me now," cried Lord Henry, laughing. - -The painter turned to his servant, who stood blinking in the sunlight. -"Ask Mr. Gray to wait, Parker: I shall be in in a few moments." The -man bowed and went up the walk. - -Then he looked at Lord Henry. "Dorian Gray is my dearest friend," he -said. "He has a simple and a beautiful nature. Your aunt was quite -right in what she said of him. Don't spoil him. Don't try to -influence him. Your influence would be bad. The world is wide, and -has many marvellous people in it. Don't take away from me the one -person who gives to my art whatever charm it possesses: my life as an -artist depends on him. Mind, Harry, I trust you." He spoke very -slowly, and the words seemed wrung out of him almost against his will. - -"What nonsense you talk!" said Lord Henry, smiling, and taking Hallward -by the arm, he almost led him into the house. - - - -CHAPTER 2 - -As they entered they saw Dorian Gray. He was seated at the piano, with -his back to them, turning over the pages of a volume of Schumann's -"Forest Scenes." "You must lend me these, Basil," he cried. "I want -to learn them. They are perfectly charming." - -"That entirely depends on how you sit to-day, Dorian." - -"Oh, I am tired of sitting, and I don't want a life-sized portrait of -myself," answered the lad, swinging round on the music-stool in a -wilful, petulant manner. When he caught sight of Lord Henry, a faint -blush coloured his cheeks for a moment, and he started up. "I beg your -pardon, Basil, but I didn't know you had any one with you." - -"This is Lord Henry Wotton, Dorian, an old Oxford friend of mine. I -have just been telling him what a capital sitter you were, and now you -have spoiled everything." - -"You have not spoiled my pleasure in meeting you, Mr. Gray," said Lord -Henry, stepping forward and extending his hand. "My aunt has often -spoken to me about you. You are one of her favourites, and, I am -afraid, one of her victims also." - -"I am in Lady Agatha's black books at present," answered Dorian with a -funny look of penitence. "I promised to go to a club in Whitechapel -with her last Tuesday, and I really forgot all about it. We were to -have played a duet together--three duets, I believe. I don't know what -she will say to me. I am far too frightened to call." - -"Oh, I will make your peace with my aunt. She is quite devoted to you. -And I don't think it really matters about your not being there. The -audience probably thought it was a duet. When Aunt Agatha sits down to -the piano, she makes quite enough noise for two people." - -"That is very horrid to her, and not very nice to me," answered Dorian, -laughing. - -Lord Henry looked at him. Yes, he was certainly wonderfully handsome, -with his finely curved scarlet lips, his frank blue eyes, his crisp -gold hair. There was something in his face that made one trust him at -once. All the candour of youth was there, as well as all youth's -passionate purity. One felt that he had kept himself unspotted from -the world. No wonder Basil Hallward worshipped him. - -"You are too charming to go in for philanthropy, Mr. Gray--far too -charming." And Lord Henry flung himself down on the divan and opened -his cigarette-case. - -The painter had been busy mixing his colours and getting his brushes -ready. He was looking worried, and when he heard Lord Henry's last -remark, he glanced at him, hesitated for a moment, and then said, -"Harry, I want to finish this picture to-day. Would you think it -awfully rude of me if I asked you to go away?" - -Lord Henry smiled and looked at Dorian Gray. "Am I to go, Mr. Gray?" -he asked. - -"Oh, please don't, Lord Henry. I see that Basil is in one of his sulky -moods, and I can't bear him when he sulks. Besides, I want you to tell -me why I should not go in for philanthropy." - -"I don't know that I shall tell you that, Mr. Gray. It is so tedious a -subject that one would have to talk seriously about it. But I -certainly shall not run away, now that you have asked me to stop. You -don't really mind, Basil, do you? You have often told me that you -liked your sitters to have some one to chat to." - -Hallward bit his lip. "If Dorian wishes it, of course you must stay. -Dorian's whims are laws to everybody, except himself." - -Lord Henry took up his hat and gloves. "You are very pressing, Basil, -but I am afraid I must go. I have promised to meet a man at the -Orleans. Good-bye, Mr. Gray. Come and see me some afternoon in Curzon -Street. I am nearly always at home at five o'clock. Write to me when -you are coming. I should be sorry to miss you." - -"Basil," cried Dorian Gray, "if Lord Henry Wotton goes, I shall go, -too. You never open your lips while you are painting, and it is -horribly dull standing on a platform and trying to look pleasant. Ask -him to stay. I insist upon it." - -"Stay, Harry, to oblige Dorian, and to oblige me," said Hallward, -gazing intently at his picture. "It is quite true, I never talk when I -am working, and never listen either, and it must be dreadfully tedious -for my unfortunate sitters. I beg you to stay." - -"But what about my man at the Orleans?" - -The painter laughed. "I don't think there will be any difficulty about -that. Sit down again, Harry. And now, Dorian, get up on the platform, -and don't move about too much, or pay any attention to what Lord Henry -says. He has a very bad influence over all his friends, with the -single exception of myself." - -Dorian Gray stepped up on the dais with the air of a young Greek -martyr, and made a little _moue_ of discontent to Lord Henry, to whom he -had rather taken a fancy. He was so unlike Basil. They made a -delightful contrast. And he had such a beautiful voice. After a few -moments he said to him, "Have you really a very bad influence, Lord -Henry? As bad as Basil says?" - -"There is no such thing as a good influence, Mr. Gray. All influence -is immoral--immoral from the scientific point of view." - -"Why?" - -"Because to influence a person is to give him one's own soul. He does -not think his natural thoughts, or burn with his natural passions. His -virtues are not real to him. His sins, if there are such things as -sins, are borrowed. He becomes an echo of some one else's music, an -actor of a part that has not been written for him. The aim of life is -self-development. To realize one's nature perfectly--that is what each -of us is here for. People are afraid of themselves, nowadays. They -have forgotten the highest of all duties, the duty that one owes to -one's self. Of course, they are charitable. They feed the hungry and -clothe the beggar. But their own souls starve, and are naked. Courage -has gone out of our race. Perhaps we never really had it. The terror -of society, which is the basis of morals, the terror of God, which is -the secret of religion--these are the two things that govern us. And -yet--" - -"Just turn your head a little more to the right, Dorian, like a good -boy," said the painter, deep in his work and conscious only that a look -had come into the lad's face that he had never seen there before. - -"And yet," continued Lord Henry, in his low, musical voice, and with -that graceful wave of the hand that was always so characteristic of -him, and that he had even in his Eton days, "I believe that if one man -were to live out his life fully and completely, were to give form to -every feeling, expression to every thought, reality to every dream--I -believe that the world would gain such a fresh impulse of joy that we -would forget all the maladies of mediaevalism, and return to the -Hellenic ideal--to something finer, richer than the Hellenic ideal, it -may be. But the bravest man amongst us is afraid of himself. The -mutilation of the savage has its tragic survival in the self-denial -that mars our lives. We are punished for our refusals. Every impulse -that we strive to strangle broods in the mind and poisons us. The body -sins once, and has done with its sin, for action is a mode of -purification. Nothing remains then but the recollection of a pleasure, -or the luxury of a regret. The only way to get rid of a temptation is -to yield to it. Resist it, and your soul grows sick with longing for -the things it has forbidden to itself, with desire for what its -monstrous laws have made monstrous and unlawful. It has been said that -the great events of the world take place in the brain. It is in the -brain, and the brain only, that the great sins of the world take place -also. You, Mr. Gray, you yourself, with your rose-red youth and your -rose-white boyhood, you have had passions that have made you afraid, -thoughts that have filled you with terror, day-dreams and sleeping -dreams whose mere memory might stain your cheek with shame--" - -"Stop!" faltered Dorian Gray, "stop! you bewilder me. I don't know -what to say. There is some answer to you, but I cannot find it. Don't -speak. Let me think. Or, rather, let me try not to think." - -For nearly ten minutes he stood there, motionless, with parted lips and -eyes strangely bright. He was dimly conscious that entirely fresh -influences were at work within him. Yet they seemed to him to have -come really from himself. The few words that Basil's friend had said -to him--words spoken by chance, no doubt, and with wilful paradox in -them--had touched some secret chord that had never been touched before, -but that he felt was now vibrating and throbbing to curious pulses. - -Music had stirred him like that. Music had troubled him many times. -But music was not articulate. It was not a new world, but rather -another chaos, that it created in us. Words! Mere words! How -terrible they were! How clear, and vivid, and cruel! One could not -escape from them. And yet what a subtle magic there was in them! They -seemed to be able to give a plastic form to formless things, and to -have a music of their own as sweet as that of viol or of lute. Mere -words! Was there anything so real as words? - -Yes; there had been things in his boyhood that he had not understood. -He understood them now. Life suddenly became fiery-coloured to him. -It seemed to him that he had been walking in fire. Why had he not -known it? - -With his subtle smile, Lord Henry watched him. He knew the precise -psychological moment when to say nothing. He felt intensely -interested. He was amazed at the sudden impression that his words had -produced, and, remembering a book that he had read when he was sixteen, -a book which had revealed to him much that he had not known before, he -wondered whether Dorian Gray was passing through a similar experience. -He had merely shot an arrow into the air. Had it hit the mark? How -fascinating the lad was! - -Hallward painted away with that marvellous bold touch of his, that had -the true refinement and perfect delicacy that in art, at any rate comes -only from strength. He was unconscious of the silence. - -"Basil, I am tired of standing," cried Dorian Gray suddenly. "I must -go out and sit in the garden. The air is stifling here." - -"My dear fellow, I am so sorry. When I am painting, I can't think of -anything else. But you never sat better. You were perfectly still. -And I have caught the effect I wanted--the half-parted lips and the -bright look in the eyes. I don't know what Harry has been saying to -you, but he has certainly made you have the most wonderful expression. -I suppose he has been paying you compliments. You mustn't believe a -word that he says." - -"He has certainly not been paying me compliments. Perhaps that is the -reason that I don't believe anything he has told me." - -"You know you believe it all," said Lord Henry, looking at him with his -dreamy languorous eyes. "I will go out to the garden with you. It is -horribly hot in the studio. Basil, let us have something iced to -drink, something with strawberries in it." - -"Certainly, Harry. Just touch the bell, and when Parker comes I will -tell him what you want. I have got to work up this background, so I -will join you later on. Don't keep Dorian too long. I have never been -in better form for painting than I am to-day. This is going to be my -masterpiece. It is my masterpiece as it stands." - -Lord Henry went out to the garden and found Dorian Gray burying his -face in the great cool lilac-blossoms, feverishly drinking in their -perfume as if it had been wine. He came close to him and put his hand -upon his shoulder. "You are quite right to do that," he murmured. -"Nothing can cure the soul but the senses, just as nothing can cure the -senses but the soul." - -The lad started and drew back. He was bareheaded, and the leaves had -tossed his rebellious curls and tangled all their gilded threads. -There was a look of fear in his eyes, such as people have when they are -suddenly awakened. His finely chiselled nostrils quivered, and some -hidden nerve shook the scarlet of his lips and left them trembling. - -"Yes," continued Lord Henry, "that is one of the great secrets of -life--to cure the soul by means of the senses, and the senses by means -of the soul. You are a wonderful creation. You know more than you -think you know, just as you know less than you want to know." - -Dorian Gray frowned and turned his head away. He could not help liking -the tall, graceful young man who was standing by him. His romantic, -olive-coloured face and worn expression interested him. There was -something in his low languid voice that was absolutely fascinating. -His cool, white, flowerlike hands, even, had a curious charm. They -moved, as he spoke, like music, and seemed to have a language of their -own. But he felt afraid of him, and ashamed of being afraid. Why had -it been left for a stranger to reveal him to himself? He had known -Basil Hallward for months, but the friendship between them had never -altered him. Suddenly there had come some one across his life who -seemed to have disclosed to him life's mystery. And, yet, what was -there to be afraid of? He was not a schoolboy or a girl. It was -absurd to be frightened. - -"Let us go and sit in the shade," said Lord Henry. "Parker has brought -out the drinks, and if you stay any longer in this glare, you will be -quite spoiled, and Basil will never paint you again. You really must -not allow yourself to become sunburnt. It would be unbecoming." - -"What can it matter?" cried Dorian Gray, laughing, as he sat down on -the seat at the end of the garden. - -"It should matter everything to you, Mr. Gray." - -"Why?" - -"Because you have the most marvellous youth, and youth is the one thing -worth having." - -"I don't feel that, Lord Henry." - -"No, you don't feel it now. Some day, when you are old and wrinkled -and ugly, when thought has seared your forehead with its lines, and -passion branded your lips with its hideous fires, you will feel it, you -will feel it terribly. Now, wherever you go, you charm the world. -Will it always be so? ... You have a wonderfully beautiful face, Mr. -Gray. Don't frown. You have. And beauty is a form of genius--is -higher, indeed, than genius, as it needs no explanation. It is of the -great facts of the world, like sunlight, or spring-time, or the -reflection in dark waters of that silver shell we call the moon. It -cannot be questioned. It has its divine right of sovereignty. It -makes princes of those who have it. You smile? Ah! when you have lost -it you won't smile.... People say sometimes that beauty is only -superficial. That may be so, but at least it is not so superficial as -thought is. To me, beauty is the wonder of wonders. It is only -shallow people who do not judge by appearances. The true mystery of -the world is the visible, not the invisible.... Yes, Mr. Gray, the -gods have been good to you. But what the gods give they quickly take -away. You have only a few years in which to live really, perfectly, -and fully. When your youth goes, your beauty will go with it, and then -you will suddenly discover that there are no triumphs left for you, or -have to content yourself with those mean triumphs that the memory of -your past will make more bitter than defeats. Every month as it wanes -brings you nearer to something dreadful. Time is jealous of you, and -wars against your lilies and your roses. You will become sallow, and -hollow-cheeked, and dull-eyed. You will suffer horribly.... Ah! -realize your youth while you have it. Don't squander the gold of your -days, listening to the tedious, trying to improve the hopeless failure, -or giving away your life to the ignorant, the common, and the vulgar. -These are the sickly aims, the false ideals, of our age. Live! Live -the wonderful life that is in you! Let nothing be lost upon you. Be -always searching for new sensations. Be afraid of nothing.... A new -Hedonism--that is what our century wants. You might be its visible -symbol. With your personality there is nothing you could not do. The -world belongs to you for a season.... The moment I met you I saw that -you were quite unconscious of what you really are, of what you really -might be. There was so much in you that charmed me that I felt I must -tell you something about yourself. I thought how tragic it would be if -you were wasted. For there is such a little time that your youth will -last--such a little time. The common hill-flowers wither, but they -blossom again. The laburnum will be as yellow next June as it is now. -In a month there will be purple stars on the clematis, and year after -year the green night of its leaves will hold its purple stars. But we -never get back our youth. The pulse of joy that beats in us at twenty -becomes sluggish. Our limbs fail, our senses rot. We degenerate into -hideous puppets, haunted by the memory of the passions of which we were -too much afraid, and the exquisite temptations that we had not the -courage to yield to. Youth! Youth! There is absolutely nothing in -the world but youth!" - -Dorian Gray listened, open-eyed and wondering. The spray of lilac fell -from his hand upon the gravel. A furry bee came and buzzed round it -for a moment. Then it began to scramble all over the oval stellated -globe of the tiny blossoms. He watched it with that strange interest -in trivial things that we try to develop when things of high import -make us afraid, or when we are stirred by some new emotion for which we -cannot find expression, or when some thought that terrifies us lays -sudden siege to the brain and calls on us to yield. After a time the -bee flew away. He saw it creeping into the stained trumpet of a Tyrian -convolvulus. The flower seemed to quiver, and then swayed gently to -and fro. - -Suddenly the painter appeared at the door of the studio and made -staccato signs for them to come in. They turned to each other and -smiled. - -"I am waiting," he cried. "Do come in. The light is quite perfect, -and you can bring your drinks." - -They rose up and sauntered down the walk together. Two green-and-white -butterflies fluttered past them, and in the pear-tree at the corner of -the garden a thrush began to sing. - -"You are glad you have met me, Mr. Gray," said Lord Henry, looking at -him. - -"Yes, I am glad now. I wonder shall I always be glad?" - -"Always! That is a dreadful word. It makes me shudder when I hear it. -Women are so fond of using it. They spoil every romance by trying to -make it last for ever. It is a meaningless word, too. The only -difference between a caprice and a lifelong passion is that the caprice -lasts a little longer." - -As they entered the studio, Dorian Gray put his hand upon Lord Henry's -arm. "In that case, let our friendship be a caprice," he murmured, -flushing at his own boldness, then stepped up on the platform and -resumed his pose. - -Lord Henry flung himself into a large wicker arm-chair and watched him. -The sweep and dash of the brush on the canvas made the only sound that -broke the stillness, except when, now and then, Hallward stepped back -to look at his work from a distance. In the slanting beams that -streamed through the open doorway the dust danced and was golden. The -heavy scent of the roses seemed to brood over everything. - -After about a quarter of an hour Hallward stopped painting, looked for -a long time at Dorian Gray, and then for a long time at the picture, -biting the end of one of his huge brushes and frowning. "It is quite -finished," he cried at last, and stooping down he wrote his name in -long vermilion letters on the left-hand corner of the canvas. - -Lord Henry came over and examined the picture. It was certainly a -wonderful work of art, and a wonderful likeness as well. - -"My dear fellow, I congratulate you most warmly," he said. "It is the -finest portrait of modern times. Mr. Gray, come over and look at -yourself." - -The lad started, as if awakened from some dream. - -"Is it really finished?" he murmured, stepping down from the platform. - -"Quite finished," said the painter. "And you have sat splendidly -to-day. I am awfully obliged to you." - -"That is entirely due to me," broke in Lord Henry. "Isn't it, Mr. -Gray?" - -Dorian made no answer, but passed listlessly in front of his picture -and turned towards it. When he saw it he drew back, and his cheeks -flushed for a moment with pleasure. A look of joy came into his eyes, -as if he had recognized himself for the first time. He stood there -motionless and in wonder, dimly conscious that Hallward was speaking to -him, but not catching the meaning of his words. The sense of his own -beauty came on him like a revelation. He had never felt it before. -Basil Hallward's compliments had seemed to him to be merely the -charming exaggeration of friendship. He had listened to them, laughed -at them, forgotten them. They had not influenced his nature. Then had -come Lord Henry Wotton with his strange panegyric on youth, his -terrible warning of its brevity. That had stirred him at the time, and -now, as he stood gazing at the shadow of his own loveliness, the full -reality of the description flashed across him. Yes, there would be a -day when his face would be wrinkled and wizen, his eyes dim and -colourless, the grace of his figure broken and deformed. The scarlet -would pass away from his lips and the gold steal from his hair. The -life that was to make his soul would mar his body. He would become -dreadful, hideous, and uncouth. - -As he thought of it, a sharp pang of pain struck through him like a -knife and made each delicate fibre of his nature quiver. His eyes -deepened into amethyst, and across them came a mist of tears. He felt -as if a hand of ice had been laid upon his heart. - -"Don't you like it?" cried Hallward at last, stung a little by the -lad's silence, not understanding what it meant. - -"Of course he likes it," said Lord Henry. "Who wouldn't like it? It -is one of the greatest things in modern art. I will give you anything -you like to ask for it. I must have it." - -"It is not my property, Harry." - -"Whose property is it?" - -"Dorian's, of course," answered the painter. - -"He is a very lucky fellow." - -"How sad it is!" murmured Dorian Gray with his eyes still fixed upon -his own portrait. "How sad it is! I shall grow old, and horrible, and -dreadful. But this picture will remain always young. It will never be -older than this particular day of June.... If it were only the other -way! If it were I who was to be always young, and the picture that was -to grow old! For that--for that--I would give everything! Yes, there -is nothing in the whole world I would not give! I would give my soul -for that!" - -"You would hardly care for such an arrangement, Basil," cried Lord -Henry, laughing. "It would be rather hard lines on your work." - -"I should object very strongly, Harry," said Hallward. - -Dorian Gray turned and looked at him. "I believe you would, Basil. -You like your art better than your friends. I am no more to you than a -green bronze figure. Hardly as much, I dare say." - -The painter stared in amazement. It was so unlike Dorian to speak like -that. What had happened? He seemed quite angry. His face was flushed -and his cheeks burning. - -"Yes," he continued, "I am less to you than your ivory Hermes or your -silver Faun. You will like them always. How long will you like me? -Till I have my first wrinkle, I suppose. I know, now, that when one -loses one's good looks, whatever they may be, one loses everything. -Your picture has taught me that. Lord Henry Wotton is perfectly right. -Youth is the only thing worth having. When I find that I am growing -old, I shall kill myself." - -Hallward turned pale and caught his hand. "Dorian! Dorian!" he cried, -"don't talk like that. I have never had such a friend as you, and I -shall never have such another. You are not jealous of material things, -are you?--you who are finer than any of them!" - -"I am jealous of everything whose beauty does not die. I am jealous of -the portrait you have painted of me. Why should it keep what I must -lose? Every moment that passes takes something from me and gives -something to it. Oh, if it were only the other way! If the picture -could change, and I could be always what I am now! Why did you paint -it? It will mock me some day--mock me horribly!" The hot tears welled -into his eyes; he tore his hand away and, flinging himself on the -divan, he buried his face in the cushions, as though he was praying. - -"This is your doing, Harry," said the painter bitterly. - -Lord Henry shrugged his shoulders. "It is the real Dorian Gray--that -is all." - -"It is not." - -"If it is not, what have I to do with it?" - -"You should have gone away when I asked you," he muttered. - -"I stayed when you asked me," was Lord Henry's answer. - -"Harry, I can't quarrel with my two best friends at once, but between -you both you have made me hate the finest piece of work I have ever -done, and I will destroy it. What is it but canvas and colour? I will -not let it come across our three lives and mar them." - -Dorian Gray lifted his golden head from the pillow, and with pallid -face and tear-stained eyes, looked at him as he walked over to the deal -painting-table that was set beneath the high curtained window. What -was he doing there? His fingers were straying about among the litter -of tin tubes and dry brushes, seeking for something. Yes, it was for -the long palette-knife, with its thin blade of lithe steel. He had -found it at last. He was going to rip up the canvas. - -With a stifled sob the lad leaped from the couch, and, rushing over to -Hallward, tore the knife out of his hand, and flung it to the end of -the studio. "Don't, Basil, don't!" he cried. "It would be murder!" - -"I am glad you appreciate my work at last, Dorian," said the painter -coldly when he had recovered from his surprise. "I never thought you -would." - -"Appreciate it? I am in love with it, Basil. It is part of myself. I -feel that." - -"Well, as soon as you are dry, you shall be varnished, and framed, and -sent home. Then you can do what you like with yourself." And he walked -across the room and rang the bell for tea. "You will have tea, of -course, Dorian? And so will you, Harry? Or do you object to such -simple pleasures?" - -"I adore simple pleasures," said Lord Henry. "They are the last refuge -of the complex. But I don't like scenes, except on the stage. What -absurd fellows you are, both of you! I wonder who it was defined man -as a rational animal. It was the most premature definition ever given. -Man is many things, but he is not rational. I am glad he is not, after -all--though I wish you chaps would not squabble over the picture. You -had much better let me have it, Basil. This silly boy doesn't really -want it, and I really do." - -"If you let any one have it but me, Basil, I shall never forgive you!" -cried Dorian Gray; "and I don't allow people to call me a silly boy." - -"You know the picture is yours, Dorian. I gave it to you before it -existed." - -"And you know you have been a little silly, Mr. Gray, and that you -don't really object to being reminded that you are extremely young." - -"I should have objected very strongly this morning, Lord Henry." - -"Ah! this morning! You have lived since then." - -There came a knock at the door, and the butler entered with a laden -tea-tray and set it down upon a small Japanese table. There was a -rattle of cups and saucers and the hissing of a fluted Georgian urn. -Two globe-shaped china dishes were brought in by a page. Dorian Gray -went over and poured out the tea. The two men sauntered languidly to -the table and examined what was under the covers. - -"Let us go to the theatre to-night," said Lord Henry. "There is sure -to be something on, somewhere. I have promised to dine at White's, but -it is only with an old friend, so I can send him a wire to say that I -am ill, or that I am prevented from coming in consequence of a -subsequent engagement. I think that would be a rather nice excuse: it -would have all the surprise of candour." - -"It is such a bore putting on one's dress-clothes," muttered Hallward. -"And, when one has them on, they are so horrid." - -"Yes," answered Lord Henry dreamily, "the costume of the nineteenth -century is detestable. It is so sombre, so depressing. Sin is the -only real colour-element left in modern life." - -"You really must not say things like that before Dorian, Harry." - -"Before which Dorian? The one who is pouring out tea for us, or the -one in the picture?" - -"Before either." - -"I should like to come to the theatre with you, Lord Henry," said the -lad. - -"Then you shall come; and you will come, too, Basil, won't you?" - -"I can't, really. I would sooner not. I have a lot of work to do." - -"Well, then, you and I will go alone, Mr. Gray." - -"I should like that awfully." - -The painter bit his lip and walked over, cup in hand, to the picture. -"I shall stay with the real Dorian," he said, sadly. - -"Is it the real Dorian?" cried the original of the portrait, strolling -across to him. "Am I really like that?" - -"Yes; you are just like that." - -"How wonderful, Basil!" - -"At least you are like it in appearance. But it will never alter," -sighed Hallward. "That is something." - -"What a fuss people make about fidelity!" exclaimed Lord Henry. "Why, -even in love it is purely a question for physiology. It has nothing to -do with our own will. Young men want to be faithful, and are not; old -men want to be faithless, and cannot: that is all one can say." - -"Don't go to the theatre to-night, Dorian," said Hallward. "Stop and -dine with me." - -"I can't, Basil." - -"Why?" - -"Because I have promised Lord Henry Wotton to go with him." - -"He won't like you the better for keeping your promises. He always -breaks his own. I beg you not to go." - -Dorian Gray laughed and shook his head. - -"I entreat you." - -The lad hesitated, and looked over at Lord Henry, who was watching them -from the tea-table with an amused smile. - -"I must go, Basil," he answered. - -"Very well," said Hallward, and he went over and laid down his cup on -the tray. "It is rather late, and, as you have to dress, you had -better lose no time. Good-bye, Harry. Good-bye, Dorian. Come and see -me soon. Come to-morrow." - -"Certainly." - -"You won't forget?" - -"No, of course not," cried Dorian. - -"And ... Harry!" - -"Yes, Basil?" - -"Remember what I asked you, when we were in the garden this morning." - -"I have forgotten it." - -"I trust you." - -"I wish I could trust myself," said Lord Henry, laughing. "Come, Mr. -Gray, my hansom is outside, and I can drop you at your own place. -Good-bye, Basil. It has been a most interesting afternoon." - -As the door closed behind them, the painter flung himself down on a -sofa, and a look of pain came into his face. - - - -CHAPTER 3 - -At half-past twelve next day Lord Henry Wotton strolled from Curzon -Street over to the Albany to call on his uncle, Lord Fermor, a genial -if somewhat rough-mannered old bachelor, whom the outside world called -selfish because it derived no particular benefit from him, but who was -considered generous by Society as he fed the people who amused him. -His father had been our ambassador at Madrid when Isabella was young -and Prim unthought of, but had retired from the diplomatic service in a -capricious moment of annoyance on not being offered the Embassy at -Paris, a post to which he considered that he was fully entitled by -reason of his birth, his indolence, the good English of his dispatches, -and his inordinate passion for pleasure. The son, who had been his -father's secretary, had resigned along with his chief, somewhat -foolishly as was thought at the time, and on succeeding some months -later to the title, had set himself to the serious study of the great -aristocratic art of doing absolutely nothing. He had two large town -houses, but preferred to live in chambers as it was less trouble, and -took most of his meals at his club. He paid some attention to the -management of his collieries in the Midland counties, excusing himself -for this taint of industry on the ground that the one advantage of -having coal was that it enabled a gentleman to afford the decency of -burning wood on his own hearth. In politics he was a Tory, except when -the Tories were in office, during which period he roundly abused them -for being a pack of Radicals. He was a hero to his valet, who bullied -him, and a terror to most of his relations, whom he bullied in turn. -Only England could have produced him, and he always said that the -country was going to the dogs. His principles were out of date, but -there was a good deal to be said for his prejudices. - -When Lord Henry entered the room, he found his uncle sitting in a rough -shooting-coat, smoking a cheroot and grumbling over _The Times_. "Well, -Harry," said the old gentleman, "what brings you out so early? I -thought you dandies never got up till two, and were not visible till -five." - -"Pure family affection, I assure you, Uncle George. I want to get -something out of you." - -"Money, I suppose," said Lord Fermor, making a wry face. "Well, sit -down and tell me all about it. Young people, nowadays, imagine that -money is everything." - -"Yes," murmured Lord Henry, settling his button-hole in his coat; "and -when they grow older they know it. But I don't want money. It is only -people who pay their bills who want that, Uncle George, and I never pay -mine. Credit is the capital of a younger son, and one lives charmingly -upon it. Besides, I always deal with Dartmoor's tradesmen, and -consequently they never bother me. What I want is information: not -useful information, of course; useless information." - -"Well, I can tell you anything that is in an English Blue Book, Harry, -although those fellows nowadays write a lot of nonsense. When I was in -the Diplomatic, things were much better. But I hear they let them in -now by examination. What can you expect? Examinations, sir, are pure -humbug from beginning to end. If a man is a gentleman, he knows quite -enough, and if he is not a gentleman, whatever he knows is bad for him." - -"Mr. Dorian Gray does not belong to Blue Books, Uncle George," said -Lord Henry languidly. - -"Mr. Dorian Gray? Who is he?" asked Lord Fermor, knitting his bushy -white eyebrows. - -"That is what I have come to learn, Uncle George. Or rather, I know -who he is. He is the last Lord Kelso's grandson. His mother was a -Devereux, Lady Margaret Devereux. I want you to tell me about his -mother. What was she like? Whom did she marry? You have known nearly -everybody in your time, so you might have known her. I am very much -interested in Mr. Gray at present. I have only just met him." - -"Kelso's grandson!" echoed the old gentleman. "Kelso's grandson! ... -Of course.... I knew his mother intimately. I believe I was at her -christening. She was an extraordinarily beautiful girl, Margaret -Devereux, and made all the men frantic by running away with a penniless -young fellow--a mere nobody, sir, a subaltern in a foot regiment, or -something of that kind. Certainly. I remember the whole thing as if -it happened yesterday. The poor chap was killed in a duel at Spa a few -months after the marriage. There was an ugly story about it. They -said Kelso got some rascally adventurer, some Belgian brute, to insult -his son-in-law in public--paid him, sir, to do it, paid him--and that -the fellow spitted his man as if he had been a pigeon. The thing was -hushed up, but, egad, Kelso ate his chop alone at the club for some -time afterwards. He brought his daughter back with him, I was told, -and she never spoke to him again. Oh, yes; it was a bad business. The -girl died, too, died within a year. So she left a son, did she? I had -forgotten that. What sort of boy is he? If he is like his mother, he -must be a good-looking chap." - -"He is very good-looking," assented Lord Henry. - -"I hope he will fall into proper hands," continued the old man. "He -should have a pot of money waiting for him if Kelso did the right thing -by him. His mother had money, too. All the Selby property came to -her, through her grandfather. Her grandfather hated Kelso, thought him -a mean dog. He was, too. Came to Madrid once when I was there. Egad, -I was ashamed of him. The Queen used to ask me about the English noble -who was always quarrelling with the cabmen about their fares. They -made quite a story of it. I didn't dare show my face at Court for a -month. I hope he treated his grandson better than he did the jarvies." - -"I don't know," answered Lord Henry. "I fancy that the boy will be -well off. He is not of age yet. He has Selby, I know. He told me so. -And ... his mother was very beautiful?" - -"Margaret Devereux was one of the loveliest creatures I ever saw, -Harry. What on earth induced her to behave as she did, I never could -understand. She could have married anybody she chose. Carlington was -mad after her. She was romantic, though. All the women of that family -were. The men were a poor lot, but, egad! the women were wonderful. -Carlington went on his knees to her. Told me so himself. She laughed -at him, and there wasn't a girl in London at the time who wasn't after -him. And by the way, Harry, talking about silly marriages, what is -this humbug your father tells me about Dartmoor wanting to marry an -American? Ain't English girls good enough for him?" - -"It is rather fashionable to marry Americans just now, Uncle George." - -"I'll back English women against the world, Harry," said Lord Fermor, -striking the table with his fist. - -"The betting is on the Americans." - -"They don't last, I am told," muttered his uncle. - -"A long engagement exhausts them, but they are capital at a -steeplechase. They take things flying. I don't think Dartmoor has a -chance." - -"Who are her people?" grumbled the old gentleman. "Has she got any?" - -Lord Henry shook his head. "American girls are as clever at concealing -their parents, as English women are at concealing their past," he said, -rising to go. - -"They are pork-packers, I suppose?" - -"I hope so, Uncle George, for Dartmoor's sake. I am told that -pork-packing is the most lucrative profession in America, after -politics." - -"Is she pretty?" - -"She behaves as if she was beautiful. Most American women do. It is -the secret of their charm." - -"Why can't these American women stay in their own country? They are -always telling us that it is the paradise for women." - -"It is. That is the reason why, like Eve, they are so excessively -anxious to get out of it," said Lord Henry. "Good-bye, Uncle George. -I shall be late for lunch, if I stop any longer. Thanks for giving me -the information I wanted. I always like to know everything about my -new friends, and nothing about my old ones." - -"Where are you lunching, Harry?" - -"At Aunt Agatha's. I have asked myself and Mr. Gray. He is her latest -_protege_." - -"Humph! tell your Aunt Agatha, Harry, not to bother me any more with -her charity appeals. I am sick of them. Why, the good woman thinks -that I have nothing to do but to write cheques for her silly fads." - -"All right, Uncle George, I'll tell her, but it won't have any effect. -Philanthropic people lose all sense of humanity. It is their -distinguishing characteristic." - -The old gentleman growled approvingly and rang the bell for his -servant. Lord Henry passed up the low arcade into Burlington Street -and turned his steps in the direction of Berkeley Square. - -So that was the story of Dorian Gray's parentage. Crudely as it had -been told to him, it had yet stirred him by its suggestion of a -strange, almost modern romance. A beautiful woman risking everything -for a mad passion. A few wild weeks of happiness cut short by a -hideous, treacherous crime. Months of voiceless agony, and then a -child born in pain. The mother snatched away by death, the boy left to -solitude and the tyranny of an old and loveless man. Yes; it was an -interesting background. It posed the lad, made him more perfect, as it -were. Behind every exquisite thing that existed, there was something -tragic. Worlds had to be in travail, that the meanest flower might -blow.... And how charming he had been at dinner the night before, as -with startled eyes and lips parted in frightened pleasure he had sat -opposite to him at the club, the red candleshades staining to a richer -rose the wakening wonder of his face. Talking to him was like playing -upon an exquisite violin. He answered to every touch and thrill of the -bow.... There was something terribly enthralling in the exercise of -influence. No other activity was like it. To project one's soul into -some gracious form, and let it tarry there for a moment; to hear one's -own intellectual views echoed back to one with all the added music of -passion and youth; to convey one's temperament into another as though -it were a subtle fluid or a strange perfume: there was a real joy in -that--perhaps the most satisfying joy left to us in an age so limited -and vulgar as our own, an age grossly carnal in its pleasures, and -grossly common in its aims.... He was a marvellous type, too, this lad, -whom by so curious a chance he had met in Basil's studio, or could be -fashioned into a marvellous type, at any rate. Grace was his, and the -white purity of boyhood, and beauty such as old Greek marbles kept for -us. There was nothing that one could not do with him. He could be -made a Titan or a toy. What a pity it was that such beauty was -destined to fade! ... And Basil? From a psychological point of view, -how interesting he was! The new manner in art, the fresh mode of -looking at life, suggested so strangely by the merely visible presence -of one who was unconscious of it all; the silent spirit that dwelt in -dim woodland, and walked unseen in open field, suddenly showing -herself, Dryadlike and not afraid, because in his soul who sought for -her there had been wakened that wonderful vision to which alone are -wonderful things revealed; the mere shapes and patterns of things -becoming, as it were, refined, and gaining a kind of symbolical value, -as though they were themselves patterns of some other and more perfect -form whose shadow they made real: how strange it all was! He -remembered something like it in history. Was it not Plato, that artist -in thought, who had first analyzed it? Was it not Buonarotti who had -carved it in the coloured marbles of a sonnet-sequence? But in our own -century it was strange.... Yes; he would try to be to Dorian Gray -what, without knowing it, the lad was to the painter who had fashioned -the wonderful portrait. He would seek to dominate him--had already, -indeed, half done so. He would make that wonderful spirit his own. -There was something fascinating in this son of love and death. - -Suddenly he stopped and glanced up at the houses. He found that he had -passed his aunt's some distance, and, smiling to himself, turned back. -When he entered the somewhat sombre hall, the butler told him that they -had gone in to lunch. He gave one of the footmen his hat and stick and -passed into the dining-room. - -"Late as usual, Harry," cried his aunt, shaking her head at him. - -He invented a facile excuse, and having taken the vacant seat next to -her, looked round to see who was there. Dorian bowed to him shyly from -the end of the table, a flush of pleasure stealing into his cheek. -Opposite was the Duchess of Harley, a lady of admirable good-nature and -good temper, much liked by every one who knew her, and of those ample -architectural proportions that in women who are not duchesses are -described by contemporary historians as stoutness. Next to her sat, on -her right, Sir Thomas Burdon, a Radical member of Parliament, who -followed his leader in public life and in private life followed the -best cooks, dining with the Tories and thinking with the Liberals, in -accordance with a wise and well-known rule. The post on her left was -occupied by Mr. Erskine of Treadley, an old gentleman of considerable -charm and culture, who had fallen, however, into bad habits of silence, -having, as he explained once to Lady Agatha, said everything that he -had to say before he was thirty. His own neighbour was Mrs. Vandeleur, -one of his aunt's oldest friends, a perfect saint amongst women, but so -dreadfully dowdy that she reminded one of a badly bound hymn-book. -Fortunately for him she had on the other side Lord Faudel, a most -intelligent middle-aged mediocrity, as bald as a ministerial statement -in the House of Commons, with whom she was conversing in that intensely -earnest manner which is the one unpardonable error, as he remarked once -himself, that all really good people fall into, and from which none of -them ever quite escape. - -"We are talking about poor Dartmoor, Lord Henry," cried the duchess, -nodding pleasantly to him across the table. "Do you think he will -really marry this fascinating young person?" - -"I believe she has made up her mind to propose to him, Duchess." - -"How dreadful!" exclaimed Lady Agatha. "Really, some one should -interfere." - -"I am told, on excellent authority, that her father keeps an American -dry-goods store," said Sir Thomas Burdon, looking supercilious. - -"My uncle has already suggested pork-packing, Sir Thomas." - -"Dry-goods! What are American dry-goods?" asked the duchess, raising -her large hands in wonder and accentuating the verb. - -"American novels," answered Lord Henry, helping himself to some quail. - -The duchess looked puzzled. - -"Don't mind him, my dear," whispered Lady Agatha. "He never means -anything that he says." - -"When America was discovered," said the Radical member--and he began to -give some wearisome facts. Like all people who try to exhaust a -subject, he exhausted his listeners. The duchess sighed and exercised -her privilege of interruption. "I wish to goodness it never had been -discovered at all!" she exclaimed. "Really, our girls have no chance -nowadays. It is most unfair." - -"Perhaps, after all, America never has been discovered," said Mr. -Erskine; "I myself would say that it had merely been detected." - -"Oh! but I have seen specimens of the inhabitants," answered the -duchess vaguely. "I must confess that most of them are extremely -pretty. And they dress well, too. They get all their dresses in -Paris. I wish I could afford to do the same." - -"They say that when good Americans die they go to Paris," chuckled Sir -Thomas, who had a large wardrobe of Humour's cast-off clothes. - -"Really! And where do bad Americans go to when they die?" inquired the -duchess. - -"They go to America," murmured Lord Henry. - -Sir Thomas frowned. "I am afraid that your nephew is prejudiced -against that great country," he said to Lady Agatha. "I have travelled -all over it in cars provided by the directors, who, in such matters, -are extremely civil. I assure you that it is an education to visit it." - -"But must we really see Chicago in order to be educated?" asked Mr. -Erskine plaintively. "I don't feel up to the journey." - -Sir Thomas waved his hand. "Mr. Erskine of Treadley has the world on -his shelves. We practical men like to see things, not to read about -them. The Americans are an extremely interesting people. They are -absolutely reasonable. I think that is their distinguishing -characteristic. Yes, Mr. Erskine, an absolutely reasonable people. I -assure you there is no nonsense about the Americans." - -"How dreadful!" cried Lord Henry. "I can stand brute force, but brute -reason is quite unbearable. There is something unfair about its use. -It is hitting below the intellect." - -"I do not understand you," said Sir Thomas, growing rather red. - -"I do, Lord Henry," murmured Mr. Erskine, with a smile. - -"Paradoxes are all very well in their way...." rejoined the baronet. - -"Was that a paradox?" asked Mr. Erskine. "I did not think so. Perhaps -it was. Well, the way of paradoxes is the way of truth. To test -reality we must see it on the tight rope. When the verities become -acrobats, we can judge them." - -"Dear me!" said Lady Agatha, "how you men argue! I am sure I never can -make out what you are talking about. Oh! Harry, I am quite vexed with -you. Why do you try to persuade our nice Mr. Dorian Gray to give up -the East End? I assure you he would be quite invaluable. They would -love his playing." - -"I want him to play to me," cried Lord Henry, smiling, and he looked -down the table and caught a bright answering glance. - -"But they are so unhappy in Whitechapel," continued Lady Agatha. - -"I can sympathize with everything except suffering," said Lord Henry, -shrugging his shoulders. "I cannot sympathize with that. It is too -ugly, too horrible, too distressing. There is something terribly -morbid in the modern sympathy with pain. One should sympathize with -the colour, the beauty, the joy of life. The less said about life's -sores, the better." - -"Still, the East End is a very important problem," remarked Sir Thomas -with a grave shake of the head. - -"Quite so," answered the young lord. "It is the problem of slavery, -and we try to solve it by amusing the slaves." - -The politician looked at him keenly. "What change do you propose, -then?" he asked. - -Lord Henry laughed. "I don't desire to change anything in England -except the weather," he answered. "I am quite content with philosophic -contemplation. But, as the nineteenth century has gone bankrupt -through an over-expenditure of sympathy, I would suggest that we should -appeal to science to put us straight. The advantage of the emotions is -that they lead us astray, and the advantage of science is that it is -not emotional." - -"But we have such grave responsibilities," ventured Mrs. Vandeleur -timidly. - -"Terribly grave," echoed Lady Agatha. - -Lord Henry looked over at Mr. Erskine. "Humanity takes itself too -seriously. It is the world's original sin. If the caveman had known -how to laugh, history would have been different." - -"You are really very comforting," warbled the duchess. "I have always -felt rather guilty when I came to see your dear aunt, for I take no -interest at all in the East End. For the future I shall be able to -look her in the face without a blush." - -"A blush is very becoming, Duchess," remarked Lord Henry. - -"Only when one is young," she answered. "When an old woman like myself -blushes, it is a very bad sign. Ah! Lord Henry, I wish you would tell -me how to become young again." - -He thought for a moment. "Can you remember any great error that you -committed in your early days, Duchess?" he asked, looking at her across -the table. - -"A great many, I fear," she cried. - -"Then commit them over again," he said gravely. "To get back one's -youth, one has merely to repeat one's follies." - -"A delightful theory!" she exclaimed. "I must put it into practice." - -"A dangerous theory!" came from Sir Thomas's tight lips. Lady Agatha -shook her head, but could not help being amused. Mr. Erskine listened. - -"Yes," he continued, "that is one of the great secrets of life. -Nowadays most people die of a sort of creeping common sense, and -discover when it is too late that the only things one never regrets are -one's mistakes." - -A laugh ran round the table. - -He played with the idea and grew wilful; tossed it into the air and -transformed it; let it escape and recaptured it; made it iridescent -with fancy and winged it with paradox. The praise of folly, as he went -on, soared into a philosophy, and philosophy herself became young, and -catching the mad music of pleasure, wearing, one might fancy, her -wine-stained robe and wreath of ivy, danced like a Bacchante over the -hills of life, and mocked the slow Silenus for being sober. Facts fled -before her like frightened forest things. Her white feet trod the huge -press at which wise Omar sits, till the seething grape-juice rose round -her bare limbs in waves of purple bubbles, or crawled in red foam over -the vat's black, dripping, sloping sides. It was an extraordinary -improvisation. He felt that the eyes of Dorian Gray were fixed on him, -and the consciousness that amongst his audience there was one whose -temperament he wished to fascinate seemed to give his wit keenness and -to lend colour to his imagination. He was brilliant, fantastic, -irresponsible. He charmed his listeners out of themselves, and they -followed his pipe, laughing. Dorian Gray never took his gaze off him, -but sat like one under a spell, smiles chasing each other over his lips -and wonder growing grave in his darkening eyes. - -At last, liveried in the costume of the age, reality entered the room -in the shape of a servant to tell the duchess that her carriage was -waiting. She wrung her hands in mock despair. "How annoying!" she -cried. "I must go. I have to call for my husband at the club, to take -him to some absurd meeting at Willis's Rooms, where he is going to be -in the chair. If I am late he is sure to be furious, and I couldn't -have a scene in this bonnet. It is far too fragile. A harsh word -would ruin it. No, I must go, dear Agatha. Good-bye, Lord Henry, you -are quite delightful and dreadfully demoralizing. I am sure I don't -know what to say about your views. You must come and dine with us some -night. Tuesday? Are you disengaged Tuesday?" - -"For you I would throw over anybody, Duchess," said Lord Henry with a -bow. - -"Ah! that is very nice, and very wrong of you," she cried; "so mind you -come"; and she swept out of the room, followed by Lady Agatha and the -other ladies. - -When Lord Henry had sat down again, Mr. Erskine moved round, and taking -a chair close to him, placed his hand upon his arm. - -"You talk books away," he said; "why don't you write one?" - -"I am too fond of reading books to care to write them, Mr. Erskine. I -should like to write a novel certainly, a novel that would be as lovely -as a Persian carpet and as unreal. But there is no literary public in -England for anything except newspapers, primers, and encyclopaedias. -Of all people in the world the English have the least sense of the -beauty of literature." - -"I fear you are right," answered Mr. Erskine. "I myself used to have -literary ambitions, but I gave them up long ago. And now, my dear -young friend, if you will allow me to call you so, may I ask if you -really meant all that you said to us at lunch?" - -"I quite forget what I said," smiled Lord Henry. "Was it all very bad?" - -"Very bad indeed. In fact I consider you extremely dangerous, and if -anything happens to our good duchess, we shall all look on you as being -primarily responsible. But I should like to talk to you about life. -The generation into which I was born was tedious. Some day, when you -are tired of London, come down to Treadley and expound to me your -philosophy of pleasure over some admirable Burgundy I am fortunate -enough to possess." - -"I shall be charmed. A visit to Treadley would be a great privilege. -It has a perfect host, and a perfect library." - -"You will complete it," answered the old gentleman with a courteous -bow. "And now I must bid good-bye to your excellent aunt. I am due at -the Athenaeum. It is the hour when we sleep there." - -"All of you, Mr. Erskine?" - -"Forty of us, in forty arm-chairs. We are practising for an English -Academy of Letters." - -Lord Henry laughed and rose. "I am going to the park," he cried. - -As he was passing out of the door, Dorian Gray touched him on the arm. -"Let me come with you," he murmured. - -"But I thought you had promised Basil Hallward to go and see him," -answered Lord Henry. - -"I would sooner come with you; yes, I feel I must come with you. Do -let me. And you will promise to talk to me all the time? No one talks -so wonderfully as you do." - -"Ah! I have talked quite enough for to-day," said Lord Henry, smiling. -"All I want now is to look at life. You may come and look at it with -me, if you care to." - - - -CHAPTER 4 - -One afternoon, a month later, Dorian Gray was reclining in a luxurious -arm-chair, in the little library of Lord Henry's house in Mayfair. It -was, in its way, a very charming room, with its high panelled -wainscoting of olive-stained oak, its cream-coloured frieze and ceiling -of raised plasterwork, and its brickdust felt carpet strewn with silk, -long-fringed Persian rugs. On a tiny satinwood table stood a statuette -by Clodion, and beside it lay a copy of Les Cent Nouvelles, bound for -Margaret of Valois by Clovis Eve and powdered with the gilt daisies -that Queen had selected for her device. Some large blue china jars and -parrot-tulips were ranged on the mantelshelf, and through the small -leaded panes of the window streamed the apricot-coloured light of a -summer day in London. - -Lord Henry had not yet come in. He was always late on principle, his -principle being that punctuality is the thief of time. So the lad was -looking rather sulky, as with listless fingers he turned over the pages -of an elaborately illustrated edition of Manon Lescaut that he had -found in one of the book-cases. The formal monotonous ticking of the -Louis Quatorze clock annoyed him. Once or twice he thought of going -away. - -At last he heard a step outside, and the door opened. "How late you -are, Harry!" he murmured. - -"I am afraid it is not Harry, Mr. Gray," answered a shrill voice. - -He glanced quickly round and rose to his feet. "I beg your pardon. I -thought--" - -"You thought it was my husband. It is only his wife. You must let me -introduce myself. I know you quite well by your photographs. I think -my husband has got seventeen of them." - -"Not seventeen, Lady Henry?" - -"Well, eighteen, then. And I saw you with him the other night at the -opera." She laughed nervously as she spoke, and watched him with her -vague forget-me-not eyes. She was a curious woman, whose dresses -always looked as if they had been designed in a rage and put on in a -tempest. She was usually in love with somebody, and, as her passion -was never returned, she had kept all her illusions. She tried to look -picturesque, but only succeeded in being untidy. Her name was -Victoria, and she had a perfect mania for going to church. - -"That was at Lohengrin, Lady Henry, I think?" - -"Yes; it was at dear Lohengrin. I like Wagner's music better than -anybody's. It is so loud that one can talk the whole time without other -people hearing what one says. That is a great advantage, don't you -think so, Mr. Gray?" - -The same nervous staccato laugh broke from her thin lips, and her -fingers began to play with a long tortoise-shell paper-knife. - -Dorian smiled and shook his head: "I am afraid I don't think so, Lady -Henry. I never talk during music--at least, during good music. If one -hears bad music, it is one's duty to drown it in conversation." - -"Ah! that is one of Harry's views, isn't it, Mr. Gray? I always hear -Harry's views from his friends. It is the only way I get to know of -them. But you must not think I don't like good music. I adore it, but -I am afraid of it. It makes me too romantic. I have simply worshipped -pianists--two at a time, sometimes, Harry tells me. I don't know what -it is about them. Perhaps it is that they are foreigners. They all -are, ain't they? Even those that are born in England become foreigners -after a time, don't they? It is so clever of them, and such a -compliment to art. Makes it quite cosmopolitan, doesn't it? You have -never been to any of my parties, have you, Mr. Gray? You must come. I -can't afford orchids, but I spare no expense in foreigners. They make -one's rooms look so picturesque. But here is Harry! Harry, I came in -to look for you, to ask you something--I forget what it was--and I -found Mr. Gray here. We have had such a pleasant chat about music. We -have quite the same ideas. No; I think our ideas are quite different. -But he has been most pleasant. I am so glad I've seen him." - -"I am charmed, my love, quite charmed," said Lord Henry, elevating his -dark, crescent-shaped eyebrows and looking at them both with an amused -smile. "So sorry I am late, Dorian. I went to look after a piece of -old brocade in Wardour Street and had to bargain for hours for it. -Nowadays people know the price of everything and the value of nothing." - -"I am afraid I must be going," exclaimed Lady Henry, breaking an -awkward silence with her silly sudden laugh. "I have promised to drive -with the duchess. Good-bye, Mr. Gray. Good-bye, Harry. You are -dining out, I suppose? So am I. Perhaps I shall see you at Lady -Thornbury's." - -"I dare say, my dear," said Lord Henry, shutting the door behind her -as, looking like a bird of paradise that had been out all night in the -rain, she flitted out of the room, leaving a faint odour of -frangipanni. Then he lit a cigarette and flung himself down on the -sofa. - -"Never marry a woman with straw-coloured hair, Dorian," he said after a -few puffs. - -"Why, Harry?" - -"Because they are so sentimental." - -"But I like sentimental people." - -"Never marry at all, Dorian. Men marry because they are tired; women, -because they are curious: both are disappointed." - -"I don't think I am likely to marry, Harry. I am too much in love. -That is one of your aphorisms. I am putting it into practice, as I do -everything that you say." - -"Who are you in love with?" asked Lord Henry after a pause. - -"With an actress," said Dorian Gray, blushing. - -Lord Henry shrugged his shoulders. "That is a rather commonplace -_debut_." - -"You would not say so if you saw her, Harry." - -"Who is she?" - -"Her name is Sibyl Vane." - -"Never heard of her." - -"No one has. People will some day, however. She is a genius." - -"My dear boy, no woman is a genius. Women are a decorative sex. They -never have anything to say, but they say it charmingly. Women -represent the triumph of matter over mind, just as men represent the -triumph of mind over morals." - -"Harry, how can you?" - -"My dear Dorian, it is quite true. I am analysing women at present, so -I ought to know. The subject is not so abstruse as I thought it was. -I find that, ultimately, there are only two kinds of women, the plain -and the coloured. The plain women are very useful. If you want to -gain a reputation for respectability, you have merely to take them down -to supper. The other women are very charming. They commit one -mistake, however. They paint in order to try and look young. Our -grandmothers painted in order to try and talk brilliantly. _Rouge_ and -_esprit_ used to go together. That is all over now. As long as a woman -can look ten years younger than her own daughter, she is perfectly -satisfied. As for conversation, there are only five women in London -worth talking to, and two of these can't be admitted into decent -society. However, tell me about your genius. How long have you known -her?" - -"Ah! Harry, your views terrify me." - -"Never mind that. How long have you known her?" - -"About three weeks." - -"And where did you come across her?" - -"I will tell you, Harry, but you mustn't be unsympathetic about it. -After all, it never would have happened if I had not met you. You -filled me with a wild desire to know everything about life. For days -after I met you, something seemed to throb in my veins. As I lounged -in the park, or strolled down Piccadilly, I used to look at every one -who passed me and wonder, with a mad curiosity, what sort of lives they -led. Some of them fascinated me. Others filled me with terror. There -was an exquisite poison in the air. I had a passion for sensations.... -Well, one evening about seven o'clock, I determined to go out in search -of some adventure. I felt that this grey monstrous London of ours, -with its myriads of people, its sordid sinners, and its splendid sins, -as you once phrased it, must have something in store for me. I fancied -a thousand things. The mere danger gave me a sense of delight. I -remembered what you had said to me on that wonderful evening when we -first dined together, about the search for beauty being the real secret -of life. I don't know what I expected, but I went out and wandered -eastward, soon losing my way in a labyrinth of grimy streets and black -grassless squares. About half-past eight I passed by an absurd little -theatre, with great flaring gas-jets and gaudy play-bills. A hideous -Jew, in the most amazing waistcoat I ever beheld in my life, was -standing at the entrance, smoking a vile cigar. He had greasy -ringlets, and an enormous diamond blazed in the centre of a soiled -shirt. 'Have a box, my Lord?' he said, when he saw me, and he took off -his hat with an air of gorgeous servility. There was something about -him, Harry, that amused me. He was such a monster. You will laugh at -me, I know, but I really went in and paid a whole guinea for the -stage-box. To the present day I can't make out why I did so; and yet if -I hadn't--my dear Harry, if I hadn't--I should have missed the greatest -romance of my life. I see you are laughing. It is horrid of you!" - -"I am not laughing, Dorian; at least I am not laughing at you. But you -should not say the greatest romance of your life. You should say the -first romance of your life. You will always be loved, and you will -always be in love with love. A _grande passion_ is the privilege of -people who have nothing to do. That is the one use of the idle classes -of a country. Don't be afraid. There are exquisite things in store -for you. This is merely the beginning." - -"Do you think my nature so shallow?" cried Dorian Gray angrily. - -"No; I think your nature so deep." - -"How do you mean?" - -"My dear boy, the people who love only once in their lives are really -the shallow people. What they call their loyalty, and their fidelity, -I call either the lethargy of custom or their lack of imagination. -Faithfulness is to the emotional life what consistency is to the life -of the intellect--simply a confession of failure. Faithfulness! I -must analyse it some day. The passion for property is in it. There -are many things that we would throw away if we were not afraid that -others might pick them up. But I don't want to interrupt you. Go on -with your story." - -"Well, I found myself seated in a horrid little private box, with a -vulgar drop-scene staring me in the face. I looked out from behind the -curtain and surveyed the house. It was a tawdry affair, all Cupids and -cornucopias, like a third-rate wedding-cake. The gallery and pit were -fairly full, but the two rows of dingy stalls were quite empty, and -there was hardly a person in what I suppose they called the -dress-circle. Women went about with oranges and ginger-beer, and there -was a terrible consumption of nuts going on." - -"It must have been just like the palmy days of the British drama." - -"Just like, I should fancy, and very depressing. I began to wonder -what on earth I should do when I caught sight of the play-bill. What -do you think the play was, Harry?" - -"I should think 'The Idiot Boy', or 'Dumb but Innocent'. Our fathers -used to like that sort of piece, I believe. The longer I live, Dorian, -the more keenly I feel that whatever was good enough for our fathers is -not good enough for us. In art, as in politics, _les grandperes ont -toujours tort_." - -"This play was good enough for us, Harry. It was Romeo and Juliet. I -must admit that I was rather annoyed at the idea of seeing Shakespeare -done in such a wretched hole of a place. Still, I felt interested, in -a sort of way. At any rate, I determined to wait for the first act. -There was a dreadful orchestra, presided over by a young Hebrew who sat -at a cracked piano, that nearly drove me away, but at last the -drop-scene was drawn up and the play began. Romeo was a stout elderly -gentleman, with corked eyebrows, a husky tragedy voice, and a figure -like a beer-barrel. Mercutio was almost as bad. He was played by the -low-comedian, who had introduced gags of his own and was on most -friendly terms with the pit. They were both as grotesque as the -scenery, and that looked as if it had come out of a country-booth. But -Juliet! Harry, imagine a girl, hardly seventeen years of age, with a -little, flowerlike face, a small Greek head with plaited coils of -dark-brown hair, eyes that were violet wells of passion, lips that were -like the petals of a rose. She was the loveliest thing I had ever seen -in my life. You said to me once that pathos left you unmoved, but that -beauty, mere beauty, could fill your eyes with tears. I tell you, -Harry, I could hardly see this girl for the mist of tears that came -across me. And her voice--I never heard such a voice. It was very low -at first, with deep mellow notes that seemed to fall singly upon one's -ear. Then it became a little louder, and sounded like a flute or a -distant hautboy. In the garden-scene it had all the tremulous ecstasy -that one hears just before dawn when nightingales are singing. There -were moments, later on, when it had the wild passion of violins. You -know how a voice can stir one. Your voice and the voice of Sibyl Vane -are two things that I shall never forget. When I close my eyes, I hear -them, and each of them says something different. I don't know which to -follow. Why should I not love her? Harry, I do love her. She is -everything to me in life. Night after night I go to see her play. One -evening she is Rosalind, and the next evening she is Imogen. I have -seen her die in the gloom of an Italian tomb, sucking the poison from -her lover's lips. I have watched her wandering through the forest of -Arden, disguised as a pretty boy in hose and doublet and dainty cap. -She has been mad, and has come into the presence of a guilty king, and -given him rue to wear and bitter herbs to taste of. She has been -innocent, and the black hands of jealousy have crushed her reedlike -throat. I have seen her in every age and in every costume. Ordinary -women never appeal to one's imagination. They are limited to their -century. No glamour ever transfigures them. One knows their minds as -easily as one knows their bonnets. One can always find them. There is -no mystery in any of them. They ride in the park in the morning and -chatter at tea-parties in the afternoon. They have their stereotyped -smile and their fashionable manner. They are quite obvious. But an -actress! How different an actress is! Harry! why didn't you tell me -that the only thing worth loving is an actress?" - -"Because I have loved so many of them, Dorian." - -"Oh, yes, horrid people with dyed hair and painted faces." - -"Don't run down dyed hair and painted faces. There is an extraordinary -charm in them, sometimes," said Lord Henry. - -"I wish now I had not told you about Sibyl Vane." - -"You could not have helped telling me, Dorian. All through your life -you will tell me everything you do." - -"Yes, Harry, I believe that is true. I cannot help telling you things. -You have a curious influence over me. If I ever did a crime, I would -come and confess it to you. You would understand me." - -"People like you--the wilful sunbeams of life--don't commit crimes, -Dorian. But I am much obliged for the compliment, all the same. And -now tell me--reach me the matches, like a good boy--thanks--what are -your actual relations with Sibyl Vane?" - -Dorian Gray leaped to his feet, with flushed cheeks and burning eyes. -"Harry! Sibyl Vane is sacred!" - -"It is only the sacred things that are worth touching, Dorian," said -Lord Henry, with a strange touch of pathos in his voice. "But why -should you be annoyed? I suppose she will belong to you some day. -When one is in love, one always begins by deceiving one's self, and one -always ends by deceiving others. That is what the world calls a -romance. You know her, at any rate, I suppose?" - -"Of course I know her. On the first night I was at the theatre, the -horrid old Jew came round to the box after the performance was over and -offered to take me behind the scenes and introduce me to her. I was -furious with him, and told him that Juliet had been dead for hundreds -of years and that her body was lying in a marble tomb in Verona. I -think, from his blank look of amazement, that he was under the -impression that I had taken too much champagne, or something." - -"I am not surprised." - -"Then he asked me if I wrote for any of the newspapers. I told him I -never even read them. He seemed terribly disappointed at that, and -confided to me that all the dramatic critics were in a conspiracy -against him, and that they were every one of them to be bought." - -"I should not wonder if he was quite right there. But, on the other -hand, judging from their appearance, most of them cannot be at all -expensive." - -"Well, he seemed to think they were beyond his means," laughed Dorian. -"By this time, however, the lights were being put out in the theatre, -and I had to go. He wanted me to try some cigars that he strongly -recommended. I declined. The next night, of course, I arrived at the -place again. When he saw me, he made me a low bow and assured me that -I was a munificent patron of art. He was a most offensive brute, -though he had an extraordinary passion for Shakespeare. He told me -once, with an air of pride, that his five bankruptcies were entirely -due to 'The Bard,' as he insisted on calling him. He seemed to think -it a distinction." - -"It was a distinction, my dear Dorian--a great distinction. Most -people become bankrupt through having invested too heavily in the prose -of life. To have ruined one's self over poetry is an honour. But when -did you first speak to Miss Sibyl Vane?" - -"The third night. She had been playing Rosalind. I could not help -going round. I had thrown her some flowers, and she had looked at -me--at least I fancied that she had. The old Jew was persistent. He -seemed determined to take me behind, so I consented. It was curious my -not wanting to know her, wasn't it?" - -"No; I don't think so." - -"My dear Harry, why?" - -"I will tell you some other time. Now I want to know about the girl." - -"Sibyl? Oh, she was so shy and so gentle. There is something of a -child about her. Her eyes opened wide in exquisite wonder when I told -her what I thought of her performance, and she seemed quite unconscious -of her power. I think we were both rather nervous. The old Jew stood -grinning at the doorway of the dusty greenroom, making elaborate -speeches about us both, while we stood looking at each other like -children. He would insist on calling me 'My Lord,' so I had to assure -Sibyl that I was not anything of the kind. She said quite simply to -me, 'You look more like a prince. I must call you Prince Charming.'" - -"Upon my word, Dorian, Miss Sibyl knows how to pay compliments." - -"You don't understand her, Harry. She regarded me merely as a person -in a play. She knows nothing of life. She lives with her mother, a -faded tired woman who played Lady Capulet in a sort of magenta -dressing-wrapper on the first night, and looks as if she had seen -better days." - -"I know that look. It depresses me," murmured Lord Henry, examining -his rings. - -"The Jew wanted to tell me her history, but I said it did not interest -me." - -"You were quite right. There is always something infinitely mean about -other people's tragedies." - -"Sibyl is the only thing I care about. What is it to me where she came -from? From her little head to her little feet, she is absolutely and -entirely divine. Every night of my life I go to see her act, and every -night she is more marvellous." - -"That is the reason, I suppose, that you never dine with me now. I -thought you must have some curious romance on hand. You have; but it -is not quite what I expected." - -"My dear Harry, we either lunch or sup together every day, and I have -been to the opera with you several times," said Dorian, opening his -blue eyes in wonder. - -"You always come dreadfully late." - -"Well, I can't help going to see Sibyl play," he cried, "even if it is -only for a single act. I get hungry for her presence; and when I think -of the wonderful soul that is hidden away in that little ivory body, I -am filled with awe." - -"You can dine with me to-night, Dorian, can't you?" - -He shook his head. "To-night she is Imogen," he answered, "and -to-morrow night she will be Juliet." - -"When is she Sibyl Vane?" - -"Never." - -"I congratulate you." - -"How horrid you are! She is all the great heroines of the world in -one. She is more than an individual. You laugh, but I tell you she -has genius. I love her, and I must make her love me. You, who know -all the secrets of life, tell me how to charm Sibyl Vane to love me! I -want to make Romeo jealous. I want the dead lovers of the world to -hear our laughter and grow sad. I want a breath of our passion to stir -their dust into consciousness, to wake their ashes into pain. My God, -Harry, how I worship her!" He was walking up and down the room as he -spoke. Hectic spots of red burned on his cheeks. He was terribly -excited. - -Lord Henry watched him with a subtle sense of pleasure. How different -he was now from the shy frightened boy he had met in Basil Hallward's -studio! His nature had developed like a flower, had borne blossoms of -scarlet flame. Out of its secret hiding-place had crept his soul, and -desire had come to meet it on the way. - -"And what do you propose to do?" said Lord Henry at last. - -"I want you and Basil to come with me some night and see her act. I -have not the slightest fear of the result. You are certain to -acknowledge her genius. Then we must get her out of the Jew's hands. -She is bound to him for three years--at least for two years and eight -months--from the present time. I shall have to pay him something, of -course. When all that is settled, I shall take a West End theatre and -bring her out properly. She will make the world as mad as she has made -me." - -"That would be impossible, my dear boy." - -"Yes, she will. She has not merely art, consummate art-instinct, in -her, but she has personality also; and you have often told me that it -is personalities, not principles, that move the age." - -"Well, what night shall we go?" - -"Let me see. To-day is Tuesday. Let us fix to-morrow. She plays -Juliet to-morrow." - -"All right. The Bristol at eight o'clock; and I will get Basil." - -"Not eight, Harry, please. Half-past six. We must be there before the -curtain rises. You must see her in the first act, where she meets -Romeo." - -"Half-past six! What an hour! It will be like having a meat-tea, or -reading an English novel. It must be seven. No gentleman dines before -seven. Shall you see Basil between this and then? Or shall I write to -him?" - -"Dear Basil! I have not laid eyes on him for a week. It is rather -horrid of me, as he has sent me my portrait in the most wonderful -frame, specially designed by himself, and, though I am a little jealous -of the picture for being a whole month younger than I am, I must admit -that I delight in it. Perhaps you had better write to him. I don't -want to see him alone. He says things that annoy me. He gives me good -advice." - -Lord Henry smiled. "People are very fond of giving away what they need -most themselves. It is what I call the depth of generosity." - -"Oh, Basil is the best of fellows, but he seems to me to be just a bit -of a Philistine. Since I have known you, Harry, I have discovered -that." - -"Basil, my dear boy, puts everything that is charming in him into his -work. The consequence is that he has nothing left for life but his -prejudices, his principles, and his common sense. The only artists I -have ever known who are personally delightful are bad artists. Good -artists exist simply in what they make, and consequently are perfectly -uninteresting in what they are. A great poet, a really great poet, is -the most unpoetical of all creatures. But inferior poets are -absolutely fascinating. The worse their rhymes are, the more -picturesque they look. The mere fact of having published a book of -second-rate sonnets makes a man quite irresistible. He lives the -poetry that he cannot write. The others write the poetry that they -dare not realize." - -"I wonder is that really so, Harry?" said Dorian Gray, putting some -perfume on his handkerchief out of a large, gold-topped bottle that -stood on the table. "It must be, if you say it. And now I am off. -Imogen is waiting for me. Don't forget about to-morrow. Good-bye." - -As he left the room, Lord Henry's heavy eyelids drooped, and he began -to think. Certainly few people had ever interested him so much as -Dorian Gray, and yet the lad's mad adoration of some one else caused -him not the slightest pang of annoyance or jealousy. He was pleased by -it. It made him a more interesting study. He had been always -enthralled by the methods of natural science, but the ordinary -subject-matter of that science had seemed to him trivial and of no -import. And so he had begun by vivisecting himself, as he had ended by -vivisecting others. Human life--that appeared to him the one thing -worth investigating. Compared to it there was nothing else of any -value. It was true that as one watched life in its curious crucible of -pain and pleasure, one could not wear over one's face a mask of glass, -nor keep the sulphurous fumes from troubling the brain and making the -imagination turbid with monstrous fancies and misshapen dreams. There -were poisons so subtle that to know their properties one had to sicken -of them. There were maladies so strange that one had to pass through -them if one sought to understand their nature. And, yet, what a great -reward one received! How wonderful the whole world became to one! To -note the curious hard logic of passion, and the emotional coloured life -of the intellect--to observe where they met, and where they separated, -at what point they were in unison, and at what point they were at -discord--there was a delight in that! What matter what the cost was? -One could never pay too high a price for any sensation. - -He was conscious--and the thought brought a gleam of pleasure into his -brown agate eyes--that it was through certain words of his, musical -words said with musical utterance, that Dorian Gray's soul had turned -to this white girl and bowed in worship before her. To a large extent -the lad was his own creation. He had made him premature. That was -something. Ordinary people waited till life disclosed to them its -secrets, but to the few, to the elect, the mysteries of life were -revealed before the veil was drawn away. Sometimes this was the effect -of art, and chiefly of the art of literature, which dealt immediately -with the passions and the intellect. But now and then a complex -personality took the place and assumed the office of art, was indeed, -in its way, a real work of art, life having its elaborate masterpieces, -just as poetry has, or sculpture, or painting. - -Yes, the lad was premature. He was gathering his harvest while it was -yet spring. The pulse and passion of youth were in him, but he was -becoming self-conscious. It was delightful to watch him. With his -beautiful face, and his beautiful soul, he was a thing to wonder at. -It was no matter how it all ended, or was destined to end. He was like -one of those gracious figures in a pageant or a play, whose joys seem -to be remote from one, but whose sorrows stir one's sense of beauty, -and whose wounds are like red roses. - -Soul and body, body and soul--how mysterious they were! There was -animalism in the soul, and the body had its moments of spirituality. -The senses could refine, and the intellect could degrade. Who could -say where the fleshly impulse ceased, or the psychical impulse began? -How shallow were the arbitrary definitions of ordinary psychologists! -And yet how difficult to decide between the claims of the various -schools! Was the soul a shadow seated in the house of sin? Or was the -body really in the soul, as Giordano Bruno thought? The separation of -spirit from matter was a mystery, and the union of spirit with matter -was a mystery also. - -He began to wonder whether we could ever make psychology so absolute a -science that each little spring of life would be revealed to us. As it -was, we always misunderstood ourselves and rarely understood others. -Experience was of no ethical value. It was merely the name men gave to -their mistakes. Moralists had, as a rule, regarded it as a mode of -warning, had claimed for it a certain ethical efficacy in the formation -of character, had praised it as something that taught us what to follow -and showed us what to avoid. But there was no motive power in -experience. It was as little of an active cause as conscience itself. -All that it really demonstrated was that our future would be the same -as our past, and that the sin we had done once, and with loathing, we -would do many times, and with joy. - -It was clear to him that the experimental method was the only method by -which one could arrive at any scientific analysis of the passions; and -certainly Dorian Gray was a subject made to his hand, and seemed to -promise rich and fruitful results. His sudden mad love for Sibyl Vane -was a psychological phenomenon of no small interest. There was no -doubt that curiosity had much to do with it, curiosity and the desire -for new experiences, yet it was not a simple, but rather a very complex -passion. What there was in it of the purely sensuous instinct of -boyhood had been transformed by the workings of the imagination, -changed into something that seemed to the lad himself to be remote from -sense, and was for that very reason all the more dangerous. It was the -passions about whose origin we deceived ourselves that tyrannized most -strongly over us. Our weakest motives were those of whose nature we -were conscious. It often happened that when we thought we were -experimenting on others we were really experimenting on ourselves. - -While Lord Henry sat dreaming on these things, a knock came to the -door, and his valet entered and reminded him it was time to dress for -dinner. He got up and looked out into the street. The sunset had -smitten into scarlet gold the upper windows of the houses opposite. -The panes glowed like plates of heated metal. The sky above was like a -faded rose. He thought of his friend's young fiery-coloured life and -wondered how it was all going to end. - -When he arrived home, about half-past twelve o'clock, he saw a telegram -lying on the hall table. He opened it and found it was from Dorian -Gray. It was to tell him that he was engaged to be married to Sibyl -Vane. - - - -CHAPTER 5 - -"Mother, Mother, I am so happy!" whispered the girl, burying her face -in the lap of the faded, tired-looking woman who, with back turned to -the shrill intrusive light, was sitting in the one arm-chair that their -dingy sitting-room contained. "I am so happy!" she repeated, "and you -must be happy, too!" - -Mrs. Vane winced and put her thin, bismuth-whitened hands on her -daughter's head. "Happy!" she echoed, "I am only happy, Sibyl, when I -see you act. You must not think of anything but your acting. Mr. -Isaacs has been very good to us, and we owe him money." - -The girl looked up and pouted. "Money, Mother?" she cried, "what does -money matter? Love is more than money." - -"Mr. Isaacs has advanced us fifty pounds to pay off our debts and to -get a proper outfit for James. You must not forget that, Sibyl. Fifty -pounds is a very large sum. Mr. Isaacs has been most considerate." - -"He is not a gentleman, Mother, and I hate the way he talks to me," -said the girl, rising to her feet and going over to the window. - -"I don't know how we could manage without him," answered the elder -woman querulously. - -Sibyl Vane tossed her head and laughed. "We don't want him any more, -Mother. Prince Charming rules life for us now." Then she paused. A -rose shook in her blood and shadowed her cheeks. Quick breath parted -the petals of her lips. They trembled. Some southern wind of passion -swept over her and stirred the dainty folds of her dress. "I love -him," she said simply. - -"Foolish child! foolish child!" was the parrot-phrase flung in answer. -The waving of crooked, false-jewelled fingers gave grotesqueness to the -words. - -The girl laughed again. The joy of a caged bird was in her voice. Her -eyes caught the melody and echoed it in radiance, then closed for a -moment, as though to hide their secret. When they opened, the mist of -a dream had passed across them. - -Thin-lipped wisdom spoke at her from the worn chair, hinted at -prudence, quoted from that book of cowardice whose author apes the name -of common sense. She did not listen. She was free in her prison of -passion. Her prince, Prince Charming, was with her. She had called on -memory to remake him. She had sent her soul to search for him, and it -had brought him back. His kiss burned again upon her mouth. Her -eyelids were warm with his breath. - -Then wisdom altered its method and spoke of espial and discovery. This -young man might be rich. If so, marriage should be thought of. -Against the shell of her ear broke the waves of worldly cunning. The -arrows of craft shot by her. She saw the thin lips moving, and smiled. - -Suddenly she felt the need to speak. The wordy silence troubled her. -"Mother, Mother," she cried, "why does he love me so much? I know why -I love him. I love him because he is like what love himself should be. -But what does he see in me? I am not worthy of him. And yet--why, I -cannot tell--though I feel so much beneath him, I don't feel humble. I -feel proud, terribly proud. Mother, did you love my father as I love -Prince Charming?" - -The elder woman grew pale beneath the coarse powder that daubed her -cheeks, and her dry lips twitched with a spasm of pain. Sybil rushed -to her, flung her arms round her neck, and kissed her. "Forgive me, -Mother. I know it pains you to talk about our father. But it only -pains you because you loved him so much. Don't look so sad. I am as -happy to-day as you were twenty years ago. Ah! let me be happy for -ever!" - -"My child, you are far too young to think of falling in love. Besides, -what do you know of this young man? You don't even know his name. The -whole thing is most inconvenient, and really, when James is going away -to Australia, and I have so much to think of, I must say that you -should have shown more consideration. However, as I said before, if he -is rich ..." - -"Ah! Mother, Mother, let me be happy!" - -Mrs. Vane glanced at her, and with one of those false theatrical -gestures that so often become a mode of second nature to a -stage-player, clasped her in her arms. At this moment, the door opened -and a young lad with rough brown hair came into the room. He was -thick-set of figure, and his hands and feet were large and somewhat -clumsy in movement. He was not so finely bred as his sister. One -would hardly have guessed the close relationship that existed between -them. Mrs. Vane fixed her eyes on him and intensified her smile. She -mentally elevated her son to the dignity of an audience. She felt sure -that the _tableau_ was interesting. - -"You might keep some of your kisses for me, Sibyl, I think," said the -lad with a good-natured grumble. - -"Ah! but you don't like being kissed, Jim," she cried. "You are a -dreadful old bear." And she ran across the room and hugged him. - -James Vane looked into his sister's face with tenderness. "I want you -to come out with me for a walk, Sibyl. I don't suppose I shall ever -see this horrid London again. I am sure I don't want to." - -"My son, don't say such dreadful things," murmured Mrs. Vane, taking up -a tawdry theatrical dress, with a sigh, and beginning to patch it. She -felt a little disappointed that he had not joined the group. It would -have increased the theatrical picturesqueness of the situation. - -"Why not, Mother? I mean it." - -"You pain me, my son. I trust you will return from Australia in a -position of affluence. I believe there is no society of any kind in -the Colonies--nothing that I would call society--so when you have made -your fortune, you must come back and assert yourself in London." - -"Society!" muttered the lad. "I don't want to know anything about -that. I should like to make some money to take you and Sibyl off the -stage. I hate it." - -"Oh, Jim!" said Sibyl, laughing, "how unkind of you! But are you -really going for a walk with me? That will be nice! I was afraid you -were going to say good-bye to some of your friends--to Tom Hardy, who -gave you that hideous pipe, or Ned Langton, who makes fun of you for -smoking it. It is very sweet of you to let me have your last -afternoon. Where shall we go? Let us go to the park." - -"I am too shabby," he answered, frowning. "Only swell people go to the -park." - -"Nonsense, Jim," she whispered, stroking the sleeve of his coat. - -He hesitated for a moment. "Very well," he said at last, "but don't be -too long dressing." She danced out of the door. One could hear her -singing as she ran upstairs. Her little feet pattered overhead. - -He walked up and down the room two or three times. Then he turned to -the still figure in the chair. "Mother, are my things ready?" he asked. - -"Quite ready, James," she answered, keeping her eyes on her work. For -some months past she had felt ill at ease when she was alone with this -rough stern son of hers. Her shallow secret nature was troubled when -their eyes met. She used to wonder if he suspected anything. The -silence, for he made no other observation, became intolerable to her. -She began to complain. Women defend themselves by attacking, just as -they attack by sudden and strange surrenders. "I hope you will be -contented, James, with your sea-faring life," she said. "You must -remember that it is your own choice. You might have entered a -solicitor's office. Solicitors are a very respectable class, and in -the country often dine with the best families." - -"I hate offices, and I hate clerks," he replied. "But you are quite -right. I have chosen my own life. All I say is, watch over Sibyl. -Don't let her come to any harm. Mother, you must watch over her." - -"James, you really talk very strangely. Of course I watch over Sibyl." - -"I hear a gentleman comes every night to the theatre and goes behind to -talk to her. Is that right? What about that?" - -"You are speaking about things you don't understand, James. In the -profession we are accustomed to receive a great deal of most gratifying -attention. I myself used to receive many bouquets at one time. That -was when acting was really understood. As for Sibyl, I do not know at -present whether her attachment is serious or not. But there is no -doubt that the young man in question is a perfect gentleman. He is -always most polite to me. Besides, he has the appearance of being -rich, and the flowers he sends are lovely." - -"You don't know his name, though," said the lad harshly. - -"No," answered his mother with a placid expression in her face. "He -has not yet revealed his real name. I think it is quite romantic of -him. He is probably a member of the aristocracy." - -James Vane bit his lip. "Watch over Sibyl, Mother," he cried, "watch -over her." - -"My son, you distress me very much. Sibyl is always under my special -care. Of course, if this gentleman is wealthy, there is no reason why -she should not contract an alliance with him. I trust he is one of the -aristocracy. He has all the appearance of it, I must say. It might be -a most brilliant marriage for Sibyl. They would make a charming -couple. His good looks are really quite remarkable; everybody notices -them." - -The lad muttered something to himself and drummed on the window-pane -with his coarse fingers. He had just turned round to say something -when the door opened and Sibyl ran in. - -"How serious you both are!" she cried. "What is the matter?" - -"Nothing," he answered. "I suppose one must be serious sometimes. -Good-bye, Mother; I will have my dinner at five o'clock. Everything is -packed, except my shirts, so you need not trouble." - -"Good-bye, my son," she answered with a bow of strained stateliness. - -She was extremely annoyed at the tone he had adopted with her, and -there was something in his look that had made her feel afraid. - -"Kiss me, Mother," said the girl. Her flowerlike lips touched the -withered cheek and warmed its frost. - -"My child! my child!" cried Mrs. Vane, looking up to the ceiling in -search of an imaginary gallery. - -"Come, Sibyl," said her brother impatiently. He hated his mother's -affectations. - -They went out into the flickering, wind-blown sunlight and strolled -down the dreary Euston Road. The passersby glanced in wonder at the -sullen heavy youth who, in coarse, ill-fitting clothes, was in the -company of such a graceful, refined-looking girl. He was like a common -gardener walking with a rose. - -Jim frowned from time to time when he caught the inquisitive glance of -some stranger. He had that dislike of being stared at, which comes on -geniuses late in life and never leaves the commonplace. Sibyl, -however, was quite unconscious of the effect she was producing. Her -love was trembling in laughter on her lips. She was thinking of Prince -Charming, and, that she might think of him all the more, she did not -talk of him, but prattled on about the ship in which Jim was going to -sail, about the gold he was certain to find, about the wonderful -heiress whose life he was to save from the wicked, red-shirted -bushrangers. For he was not to remain a sailor, or a supercargo, or -whatever he was going to be. Oh, no! A sailor's existence was -dreadful. Fancy being cooped up in a horrid ship, with the hoarse, -hump-backed waves trying to get in, and a black wind blowing the masts -down and tearing the sails into long screaming ribands! He was to -leave the vessel at Melbourne, bid a polite good-bye to the captain, -and go off at once to the gold-fields. Before a week was over he was to -come across a large nugget of pure gold, the largest nugget that had -ever been discovered, and bring it down to the coast in a waggon -guarded by six mounted policemen. The bushrangers were to attack them -three times, and be defeated with immense slaughter. Or, no. He was -not to go to the gold-fields at all. They were horrid places, where -men got intoxicated, and shot each other in bar-rooms, and used bad -language. He was to be a nice sheep-farmer, and one evening, as he was -riding home, he was to see the beautiful heiress being carried off by a -robber on a black horse, and give chase, and rescue her. Of course, -she would fall in love with him, and he with her, and they would get -married, and come home, and live in an immense house in London. Yes, -there were delightful things in store for him. But he must be very -good, and not lose his temper, or spend his money foolishly. She was -only a year older than he was, but she knew so much more of life. He -must be sure, also, to write to her by every mail, and to say his -prayers each night before he went to sleep. God was very good, and -would watch over him. She would pray for him, too, and in a few years -he would come back quite rich and happy. - -The lad listened sulkily to her and made no answer. He was heart-sick -at leaving home. - -Yet it was not this alone that made him gloomy and morose. -Inexperienced though he was, he had still a strong sense of the danger -of Sibyl's position. This young dandy who was making love to her could -mean her no good. He was a gentleman, and he hated him for that, hated -him through some curious race-instinct for which he could not account, -and which for that reason was all the more dominant within him. He was -conscious also of the shallowness and vanity of his mother's nature, -and in that saw infinite peril for Sibyl and Sibyl's happiness. -Children begin by loving their parents; as they grow older they judge -them; sometimes they forgive them. - -His mother! He had something on his mind to ask of her, something that -he had brooded on for many months of silence. A chance phrase that he -had heard at the theatre, a whispered sneer that had reached his ears -one night as he waited at the stage-door, had set loose a train of -horrible thoughts. He remembered it as if it had been the lash of a -hunting-crop across his face. His brows knit together into a wedge-like -furrow, and with a twitch of pain he bit his underlip. - -"You are not listening to a word I am saying, Jim," cried Sibyl, "and I -am making the most delightful plans for your future. Do say something." - -"What do you want me to say?" - -"Oh! that you will be a good boy and not forget us," she answered, -smiling at him. - -He shrugged his shoulders. "You are more likely to forget me than I am -to forget you, Sibyl." - -She flushed. "What do you mean, Jim?" she asked. - -"You have a new friend, I hear. Who is he? Why have you not told me -about him? He means you no good." - -"Stop, Jim!" she exclaimed. "You must not say anything against him. I -love him." - -"Why, you don't even know his name," answered the lad. "Who is he? I -have a right to know." - -"He is called Prince Charming. Don't you like the name. Oh! you silly -boy! you should never forget it. If you only saw him, you would think -him the most wonderful person in the world. Some day you will meet -him--when you come back from Australia. You will like him so much. -Everybody likes him, and I ... love him. I wish you could come to the -theatre to-night. He is going to be there, and I am to play Juliet. -Oh! how I shall play it! Fancy, Jim, to be in love and play Juliet! -To have him sitting there! To play for his delight! I am afraid I may -frighten the company, frighten or enthrall them. To be in love is to -surpass one's self. Poor dreadful Mr. Isaacs will be shouting 'genius' -to his loafers at the bar. He has preached me as a dogma; to-night he -will announce me as a revelation. I feel it. And it is all his, his -only, Prince Charming, my wonderful lover, my god of graces. But I am -poor beside him. Poor? What does that matter? When poverty creeps in -at the door, love flies in through the window. Our proverbs want -rewriting. They were made in winter, and it is summer now; spring-time -for me, I think, a very dance of blossoms in blue skies." - -"He is a gentleman," said the lad sullenly. - -"A prince!" she cried musically. "What more do you want?" - -"He wants to enslave you." - -"I shudder at the thought of being free." - -"I want you to beware of him." - -"To see him is to worship him; to know him is to trust him." - -"Sibyl, you are mad about him." - -She laughed and took his arm. "You dear old Jim, you talk as if you -were a hundred. Some day you will be in love yourself. Then you will -know what it is. Don't look so sulky. Surely you should be glad to -think that, though you are going away, you leave me happier than I have -ever been before. Life has been hard for us both, terribly hard and -difficult. But it will be different now. You are going to a new -world, and I have found one. Here are two chairs; let us sit down and -see the smart people go by." - -They took their seats amidst a crowd of watchers. The tulip-beds -across the road flamed like throbbing rings of fire. A white -dust--tremulous cloud of orris-root it seemed--hung in the panting air. -The brightly coloured parasols danced and dipped like monstrous -butterflies. - -She made her brother talk of himself, his hopes, his prospects. He -spoke slowly and with effort. They passed words to each other as -players at a game pass counters. Sibyl felt oppressed. She could not -communicate her joy. A faint smile curving that sullen mouth was all -the echo she could win. After some time she became silent. Suddenly -she caught a glimpse of golden hair and laughing lips, and in an open -carriage with two ladies Dorian Gray drove past. - -She started to her feet. "There he is!" she cried. - -"Who?" said Jim Vane. - -"Prince Charming," she answered, looking after the victoria. - -He jumped up and seized her roughly by the arm. "Show him to me. -Which is he? Point him out. I must see him!" he exclaimed; but at -that moment the Duke of Berwick's four-in-hand came between, and when -it had left the space clear, the carriage had swept out of the park. - -"He is gone," murmured Sibyl sadly. "I wish you had seen him." - -"I wish I had, for as sure as there is a God in heaven, if he ever does -you any wrong, I shall kill him." - -She looked at him in horror. He repeated his words. They cut the air -like a dagger. The people round began to gape. A lady standing close -to her tittered. - -"Come away, Jim; come away," she whispered. He followed her doggedly -as she passed through the crowd. He felt glad at what he had said. - -When they reached the Achilles Statue, she turned round. There was -pity in her eyes that became laughter on her lips. She shook her head -at him. "You are foolish, Jim, utterly foolish; a bad-tempered boy, -that is all. How can you say such horrible things? You don't know -what you are talking about. You are simply jealous and unkind. Ah! I -wish you would fall in love. Love makes people good, and what you said -was wicked." - -"I am sixteen," he answered, "and I know what I am about. Mother is no -help to you. She doesn't understand how to look after you. I wish now -that I was not going to Australia at all. I have a great mind to chuck -the whole thing up. I would, if my articles hadn't been signed." - -"Oh, don't be so serious, Jim. You are like one of the heroes of those -silly melodramas Mother used to be so fond of acting in. I am not -going to quarrel with you. I have seen him, and oh! to see him is -perfect happiness. We won't quarrel. I know you would never harm any -one I love, would you?" - -"Not as long as you love him, I suppose," was the sullen answer. - -"I shall love him for ever!" she cried. - -"And he?" - -"For ever, too!" - -"He had better." - -She shrank from him. Then she laughed and put her hand on his arm. He -was merely a boy. - -At the Marble Arch they hailed an omnibus, which left them close to -their shabby home in the Euston Road. It was after five o'clock, and -Sibyl had to lie down for a couple of hours before acting. Jim -insisted that she should do so. He said that he would sooner part with -her when their mother was not present. She would be sure to make a -scene, and he detested scenes of every kind. - -In Sybil's own room they parted. There was jealousy in the lad's -heart, and a fierce murderous hatred of the stranger who, as it seemed -to him, had come between them. Yet, when her arms were flung round his -neck, and her fingers strayed through his hair, he softened and kissed -her with real affection. There were tears in his eyes as he went -downstairs. - -His mother was waiting for him below. She grumbled at his -unpunctuality, as he entered. He made no answer, but sat down to his -meagre meal. The flies buzzed round the table and crawled over the -stained cloth. Through the rumble of omnibuses, and the clatter of -street-cabs, he could hear the droning voice devouring each minute that -was left to him. - -After some time, he thrust away his plate and put his head in his -hands. He felt that he had a right to know. It should have been told -to him before, if it was as he suspected. Leaden with fear, his mother -watched him. Words dropped mechanically from her lips. A tattered -lace handkerchief twitched in her fingers. When the clock struck six, -he got up and went to the door. Then he turned back and looked at her. -Their eyes met. In hers he saw a wild appeal for mercy. It enraged -him. - -"Mother, I have something to ask you," he said. Her eyes wandered -vaguely about the room. She made no answer. "Tell me the truth. I -have a right to know. Were you married to my father?" - -She heaved a deep sigh. It was a sigh of relief. The terrible moment, -the moment that night and day, for weeks and months, she had dreaded, -had come at last, and yet she felt no terror. Indeed, in some measure -it was a disappointment to her. The vulgar directness of the question -called for a direct answer. The situation had not been gradually led -up to. It was crude. It reminded her of a bad rehearsal. - -"No," she answered, wondering at the harsh simplicity of life. - -"My father was a scoundrel then!" cried the lad, clenching his fists. - -She shook her head. "I knew he was not free. We loved each other very -much. If he had lived, he would have made provision for us. Don't -speak against him, my son. He was your father, and a gentleman. -Indeed, he was highly connected." - -An oath broke from his lips. "I don't care for myself," he exclaimed, -"but don't let Sibyl.... It is a gentleman, isn't it, who is in love -with her, or says he is? Highly connected, too, I suppose." - -For a moment a hideous sense of humiliation came over the woman. Her -head drooped. She wiped her eyes with shaking hands. "Sibyl has a -mother," she murmured; "I had none." - -The lad was touched. He went towards her, and stooping down, he kissed -her. "I am sorry if I have pained you by asking about my father," he -said, "but I could not help it. I must go now. Good-bye. Don't forget -that you will have only one child now to look after, and believe me -that if this man wrongs my sister, I will find out who he is, track him -down, and kill him like a dog. I swear it." - -The exaggerated folly of the threat, the passionate gesture that -accompanied it, the mad melodramatic words, made life seem more vivid -to her. She was familiar with the atmosphere. She breathed more -freely, and for the first time for many months she really admired her -son. She would have liked to have continued the scene on the same -emotional scale, but he cut her short. Trunks had to be carried down -and mufflers looked for. The lodging-house drudge bustled in and out. -There was the bargaining with the cabman. The moment was lost in -vulgar details. It was with a renewed feeling of disappointment that -she waved the tattered lace handkerchief from the window, as her son -drove away. She was conscious that a great opportunity had been -wasted. She consoled herself by telling Sibyl how desolate she felt -her life would be, now that she had only one child to look after. She -remembered the phrase. It had pleased her. Of the threat she said -nothing. It was vividly and dramatically expressed. She felt that -they would all laugh at it some day. - - - -CHAPTER 6 - -"I suppose you have heard the news, Basil?" said Lord Henry that -evening as Hallward was shown into a little private room at the Bristol -where dinner had been laid for three. - -"No, Harry," answered the artist, giving his hat and coat to the bowing -waiter. "What is it? Nothing about politics, I hope! They don't -interest me. There is hardly a single person in the House of Commons -worth painting, though many of them would be the better for a little -whitewashing." - -"Dorian Gray is engaged to be married," said Lord Henry, watching him -as he spoke. - -Hallward started and then frowned. "Dorian engaged to be married!" he -cried. "Impossible!" - -"It is perfectly true." - -"To whom?" - -"To some little actress or other." - -"I can't believe it. Dorian is far too sensible." - -"Dorian is far too wise not to do foolish things now and then, my dear -Basil." - -"Marriage is hardly a thing that one can do now and then, Harry." - -"Except in America," rejoined Lord Henry languidly. "But I didn't say -he was married. I said he was engaged to be married. There is a great -difference. I have a distinct remembrance of being married, but I have -no recollection at all of being engaged. I am inclined to think that I -never was engaged." - -"But think of Dorian's birth, and position, and wealth. It would be -absurd for him to marry so much beneath him." - -"If you want to make him marry this girl, tell him that, Basil. He is -sure to do it, then. Whenever a man does a thoroughly stupid thing, it -is always from the noblest motives." - -"I hope the girl is good, Harry. I don't want to see Dorian tied to -some vile creature, who might degrade his nature and ruin his -intellect." - -"Oh, she is better than good--she is beautiful," murmured Lord Henry, -sipping a glass of vermouth and orange-bitters. "Dorian says she is -beautiful, and he is not often wrong about things of that kind. Your -portrait of him has quickened his appreciation of the personal -appearance of other people. It has had that excellent effect, amongst -others. We are to see her to-night, if that boy doesn't forget his -appointment." - -"Are you serious?" - -"Quite serious, Basil. I should be miserable if I thought I should -ever be more serious than I am at the present moment." - -"But do you approve of it, Harry?" asked the painter, walking up and -down the room and biting his lip. "You can't approve of it, possibly. -It is some silly infatuation." - -"I never approve, or disapprove, of anything now. It is an absurd -attitude to take towards life. We are not sent into the world to air -our moral prejudices. I never take any notice of what common people -say, and I never interfere with what charming people do. If a -personality fascinates me, whatever mode of expression that personality -selects is absolutely delightful to me. Dorian Gray falls in love with -a beautiful girl who acts Juliet, and proposes to marry her. Why not? -If he wedded Messalina, he would be none the less interesting. You -know I am not a champion of marriage. The real drawback to marriage is -that it makes one unselfish. And unselfish people are colourless. -They lack individuality. Still, there are certain temperaments that -marriage makes more complex. They retain their egotism, and add to it -many other egos. They are forced to have more than one life. They -become more highly organized, and to be highly organized is, I should -fancy, the object of man's existence. Besides, every experience is of -value, and whatever one may say against marriage, it is certainly an -experience. I hope that Dorian Gray will make this girl his wife, -passionately adore her for six months, and then suddenly become -fascinated by some one else. He would be a wonderful study." - -"You don't mean a single word of all that, Harry; you know you don't. -If Dorian Gray's life were spoiled, no one would be sorrier than -yourself. You are much better than you pretend to be." - -Lord Henry laughed. "The reason we all like to think so well of others -is that we are all afraid for ourselves. The basis of optimism is -sheer terror. We think that we are generous because we credit our -neighbour with the possession of those virtues that are likely to be a -benefit to us. We praise the banker that we may overdraw our account, -and find good qualities in the highwayman in the hope that he may spare -our pockets. I mean everything that I have said. I have the greatest -contempt for optimism. As for a spoiled life, no life is spoiled but -one whose growth is arrested. If you want to mar a nature, you have -merely to reform it. As for marriage, of course that would be silly, -but there are other and more interesting bonds between men and women. -I will certainly encourage them. They have the charm of being -fashionable. But here is Dorian himself. He will tell you more than I -can." - -"My dear Harry, my dear Basil, you must both congratulate me!" said the -lad, throwing off his evening cape with its satin-lined wings and -shaking each of his friends by the hand in turn. "I have never been so -happy. Of course, it is sudden--all really delightful things are. And -yet it seems to me to be the one thing I have been looking for all my -life." He was flushed with excitement and pleasure, and looked -extraordinarily handsome. - -"I hope you will always be very happy, Dorian," said Hallward, "but I -don't quite forgive you for not having let me know of your engagement. -You let Harry know." - -"And I don't forgive you for being late for dinner," broke in Lord -Henry, putting his hand on the lad's shoulder and smiling as he spoke. -"Come, let us sit down and try what the new _chef_ here is like, and then -you will tell us how it all came about." - -"There is really not much to tell," cried Dorian as they took their -seats at the small round table. "What happened was simply this. After -I left you yesterday evening, Harry, I dressed, had some dinner at that -little Italian restaurant in Rupert Street you introduced me to, and -went down at eight o'clock to the theatre. Sibyl was playing Rosalind. -Of course, the scenery was dreadful and the Orlando absurd. But Sibyl! -You should have seen her! When she came on in her boy's clothes, she -was perfectly wonderful. She wore a moss-coloured velvet jerkin with -cinnamon sleeves, slim, brown, cross-gartered hose, a dainty little -green cap with a hawk's feather caught in a jewel, and a hooded cloak -lined with dull red. She had never seemed to me more exquisite. She -had all the delicate grace of that Tanagra figurine that you have in -your studio, Basil. Her hair clustered round her face like dark leaves -round a pale rose. As for her acting--well, you shall see her -to-night. She is simply a born artist. I sat in the dingy box -absolutely enthralled. I forgot that I was in London and in the -nineteenth century. I was away with my love in a forest that no man -had ever seen. After the performance was over, I went behind and spoke -to her. As we were sitting together, suddenly there came into her eyes -a look that I had never seen there before. My lips moved towards hers. -We kissed each other. I can't describe to you what I felt at that -moment. It seemed to me that all my life had been narrowed to one -perfect point of rose-coloured joy. She trembled all over and shook -like a white narcissus. Then she flung herself on her knees and kissed -my hands. I feel that I should not tell you all this, but I can't help -it. Of course, our engagement is a dead secret. She has not even told -her own mother. I don't know what my guardians will say. Lord Radley -is sure to be furious. I don't care. I shall be of age in less than a -year, and then I can do what I like. I have been right, Basil, haven't -I, to take my love out of poetry and to find my wife in Shakespeare's -plays? Lips that Shakespeare taught to speak have whispered their -secret in my ear. I have had the arms of Rosalind around me, and -kissed Juliet on the mouth." - -"Yes, Dorian, I suppose you were right," said Hallward slowly. - -"Have you seen her to-day?" asked Lord Henry. - -Dorian Gray shook his head. "I left her in the forest of Arden; I -shall find her in an orchard in Verona." - -Lord Henry sipped his champagne in a meditative manner. "At what -particular point did you mention the word marriage, Dorian? And what -did she say in answer? Perhaps you forgot all about it." - -"My dear Harry, I did not treat it as a business transaction, and I did -not make any formal proposal. I told her that I loved her, and she -said she was not worthy to be my wife. Not worthy! Why, the whole -world is nothing to me compared with her." - -"Women are wonderfully practical," murmured Lord Henry, "much more -practical than we are. In situations of that kind we often forget to -say anything about marriage, and they always remind us." - -Hallward laid his hand upon his arm. "Don't, Harry. You have annoyed -Dorian. He is not like other men. He would never bring misery upon -any one. His nature is too fine for that." - -Lord Henry looked across the table. "Dorian is never annoyed with me," -he answered. "I asked the question for the best reason possible, for -the only reason, indeed, that excuses one for asking any -question--simple curiosity. I have a theory that it is always the -women who propose to us, and not we who propose to the women. Except, -of course, in middle-class life. But then the middle classes are not -modern." - -Dorian Gray laughed, and tossed his head. "You are quite incorrigible, -Harry; but I don't mind. It is impossible to be angry with you. When -you see Sibyl Vane, you will feel that the man who could wrong her -would be a beast, a beast without a heart. I cannot understand how any -one can wish to shame the thing he loves. I love Sibyl Vane. I want -to place her on a pedestal of gold and to see the world worship the -woman who is mine. What is marriage? An irrevocable vow. You mock at -it for that. Ah! don't mock. It is an irrevocable vow that I want to -take. Her trust makes me faithful, her belief makes me good. When I -am with her, I regret all that you have taught me. I become different -from what you have known me to be. I am changed, and the mere touch of -Sibyl Vane's hand makes me forget you and all your wrong, fascinating, -poisonous, delightful theories." - -"And those are ...?" asked Lord Henry, helping himself to some salad. - -"Oh, your theories about life, your theories about love, your theories -about pleasure. All your theories, in fact, Harry." - -"Pleasure is the only thing worth having a theory about," he answered -in his slow melodious voice. "But I am afraid I cannot claim my theory -as my own. It belongs to Nature, not to me. Pleasure is Nature's -test, her sign of approval. When we are happy, we are always good, but -when we are good, we are not always happy." - -"Ah! but what do you mean by good?" cried Basil Hallward. - -"Yes," echoed Dorian, leaning back in his chair and looking at Lord -Henry over the heavy clusters of purple-lipped irises that stood in the -centre of the table, "what do you mean by good, Harry?" - -"To be good is to be in harmony with one's self," he replied, touching -the thin stem of his glass with his pale, fine-pointed fingers. -"Discord is to be forced to be in harmony with others. One's own -life--that is the important thing. As for the lives of one's -neighbours, if one wishes to be a prig or a Puritan, one can flaunt -one's moral views about them, but they are not one's concern. Besides, -individualism has really the higher aim. Modern morality consists in -accepting the standard of one's age. I consider that for any man of -culture to accept the standard of his age is a form of the grossest -immorality." - -"But, surely, if one lives merely for one's self, Harry, one pays a -terrible price for doing so?" suggested the painter. - -"Yes, we are overcharged for everything nowadays. I should fancy that -the real tragedy of the poor is that they can afford nothing but -self-denial. Beautiful sins, like beautiful things, are the privilege -of the rich." - -"One has to pay in other ways but money." - -"What sort of ways, Basil?" - -"Oh! I should fancy in remorse, in suffering, in ... well, in the -consciousness of degradation." - -Lord Henry shrugged his shoulders. "My dear fellow, mediaeval art is -charming, but mediaeval emotions are out of date. One can use them in -fiction, of course. But then the only things that one can use in -fiction are the things that one has ceased to use in fact. Believe me, -no civilized man ever regrets a pleasure, and no uncivilized man ever -knows what a pleasure is." - -"I know what pleasure is," cried Dorian Gray. "It is to adore some -one." - -"That is certainly better than being adored," he answered, toying with -some fruits. "Being adored is a nuisance. Women treat us just as -humanity treats its gods. They worship us, and are always bothering us -to do something for them." - -"I should have said that whatever they ask for they had first given to -us," murmured the lad gravely. "They create love in our natures. They -have a right to demand it back." - -"That is quite true, Dorian," cried Hallward. - -"Nothing is ever quite true," said Lord Henry. - -"This is," interrupted Dorian. "You must admit, Harry, that women give -to men the very gold of their lives." - -"Possibly," he sighed, "but they invariably want it back in such very -small change. That is the worry. Women, as some witty Frenchman once -put it, inspire us with the desire to do masterpieces and always -prevent us from carrying them out." - -"Harry, you are dreadful! I don't know why I like you so much." - -"You will always like me, Dorian," he replied. "Will you have some -coffee, you fellows? Waiter, bring coffee, and _fine-champagne_, and -some cigarettes. No, don't mind the cigarettes--I have some. Basil, I -can't allow you to smoke cigars. You must have a cigarette. A -cigarette is the perfect type of a perfect pleasure. It is exquisite, -and it leaves one unsatisfied. What more can one want? Yes, Dorian, -you will always be fond of me. I represent to you all the sins you -have never had the courage to commit." - -"What nonsense you talk, Harry!" cried the lad, taking a light from a -fire-breathing silver dragon that the waiter had placed on the table. -"Let us go down to the theatre. When Sibyl comes on the stage you will -have a new ideal of life. She will represent something to you that you -have never known." - -"I have known everything," said Lord Henry, with a tired look in his -eyes, "but I am always ready for a new emotion. I am afraid, however, -that, for me at any rate, there is no such thing. Still, your -wonderful girl may thrill me. I love acting. It is so much more real -than life. Let us go. Dorian, you will come with me. I am so sorry, -Basil, but there is only room for two in the brougham. You must follow -us in a hansom." - -They got up and put on their coats, sipping their coffee standing. The -painter was silent and preoccupied. There was a gloom over him. He -could not bear this marriage, and yet it seemed to him to be better -than many other things that might have happened. After a few minutes, -they all passed downstairs. He drove off by himself, as had been -arranged, and watched the flashing lights of the little brougham in -front of him. A strange sense of loss came over him. He felt that -Dorian Gray would never again be to him all that he had been in the -past. Life had come between them.... His eyes darkened, and the -crowded flaring streets became blurred to his eyes. When the cab drew -up at the theatre, it seemed to him that he had grown years older. - - - -CHAPTER 7 - -For some reason or other, the house was crowded that night, and the fat -Jew manager who met them at the door was beaming from ear to ear with -an oily tremulous smile. He escorted them to their box with a sort of -pompous humility, waving his fat jewelled hands and talking at the top -of his voice. Dorian Gray loathed him more than ever. He felt as if -he had come to look for Miranda and had been met by Caliban. Lord -Henry, upon the other hand, rather liked him. At least he declared he -did, and insisted on shaking him by the hand and assuring him that he -was proud to meet a man who had discovered a real genius and gone -bankrupt over a poet. Hallward amused himself with watching the faces -in the pit. The heat was terribly oppressive, and the huge sunlight -flamed like a monstrous dahlia with petals of yellow fire. The youths -in the gallery had taken off their coats and waistcoats and hung them -over the side. They talked to each other across the theatre and shared -their oranges with the tawdry girls who sat beside them. Some women -were laughing in the pit. Their voices were horribly shrill and -discordant. The sound of the popping of corks came from the bar. - -"What a place to find one's divinity in!" said Lord Henry. - -"Yes!" answered Dorian Gray. "It was here I found her, and she is -divine beyond all living things. When she acts, you will forget -everything. These common rough people, with their coarse faces and -brutal gestures, become quite different when she is on the stage. They -sit silently and watch her. They weep and laugh as she wills them to -do. She makes them as responsive as a violin. She spiritualizes them, -and one feels that they are of the same flesh and blood as one's self." - -"The same flesh and blood as one's self! Oh, I hope not!" exclaimed -Lord Henry, who was scanning the occupants of the gallery through his -opera-glass. - -"Don't pay any attention to him, Dorian," said the painter. "I -understand what you mean, and I believe in this girl. Any one you love -must be marvellous, and any girl who has the effect you describe must -be fine and noble. To spiritualize one's age--that is something worth -doing. If this girl can give a soul to those who have lived without -one, if she can create the sense of beauty in people whose lives have -been sordid and ugly, if she can strip them of their selfishness and -lend them tears for sorrows that are not their own, she is worthy of -all your adoration, worthy of the adoration of the world. This -marriage is quite right. I did not think so at first, but I admit it -now. The gods made Sibyl Vane for you. Without her you would have -been incomplete." - -"Thanks, Basil," answered Dorian Gray, pressing his hand. "I knew that -you would understand me. Harry is so cynical, he terrifies me. But -here is the orchestra. It is quite dreadful, but it only lasts for -about five minutes. Then the curtain rises, and you will see the girl -to whom I am going to give all my life, to whom I have given everything -that is good in me." - -A quarter of an hour afterwards, amidst an extraordinary turmoil of -applause, Sibyl Vane stepped on to the stage. Yes, she was certainly -lovely to look at--one of the loveliest creatures, Lord Henry thought, -that he had ever seen. There was something of the fawn in her shy -grace and startled eyes. A faint blush, like the shadow of a rose in a -mirror of silver, came to her cheeks as she glanced at the crowded -enthusiastic house. She stepped back a few paces and her lips seemed -to tremble. Basil Hallward leaped to his feet and began to applaud. -Motionless, and as one in a dream, sat Dorian Gray, gazing at her. -Lord Henry peered through his glasses, murmuring, "Charming! charming!" - -The scene was the hall of Capulet's house, and Romeo in his pilgrim's -dress had entered with Mercutio and his other friends. The band, such -as it was, struck up a few bars of music, and the dance began. Through -the crowd of ungainly, shabbily dressed actors, Sibyl Vane moved like a -creature from a finer world. Her body swayed, while she danced, as a -plant sways in the water. The curves of her throat were the curves of -a white lily. Her hands seemed to be made of cool ivory. - -Yet she was curiously listless. She showed no sign of joy when her -eyes rested on Romeo. The few words she had to speak-- - - Good pilgrim, you do wrong your hand too much, - Which mannerly devotion shows in this; - For saints have hands that pilgrims' hands do touch, - And palm to palm is holy palmers' kiss-- - -with the brief dialogue that follows, were spoken in a thoroughly -artificial manner. The voice was exquisite, but from the point of view -of tone it was absolutely false. It was wrong in colour. It took away -all the life from the verse. It made the passion unreal. - -Dorian Gray grew pale as he watched her. He was puzzled and anxious. -Neither of his friends dared to say anything to him. She seemed to -them to be absolutely incompetent. They were horribly disappointed. - -Yet they felt that the true test of any Juliet is the balcony scene of -the second act. They waited for that. If she failed there, there was -nothing in her. - -She looked charming as she came out in the moonlight. That could not -be denied. But the staginess of her acting was unbearable, and grew -worse as she went on. Her gestures became absurdly artificial. She -overemphasized everything that she had to say. The beautiful passage-- - - Thou knowest the mask of night is on my face, - Else would a maiden blush bepaint my cheek - For that which thou hast heard me speak to-night-- - -was declaimed with the painful precision of a schoolgirl who has been -taught to recite by some second-rate professor of elocution. When she -leaned over the balcony and came to those wonderful lines-- - - Although I joy in thee, - I have no joy of this contract to-night: - It is too rash, too unadvised, too sudden; - Too like the lightning, which doth cease to be - Ere one can say, "It lightens." Sweet, good-night! - This bud of love by summer's ripening breath - May prove a beauteous flower when next we meet-- - -she spoke the words as though they conveyed no meaning to her. It was -not nervousness. Indeed, so far from being nervous, she was absolutely -self-contained. It was simply bad art. She was a complete failure. - -Even the common uneducated audience of the pit and gallery lost their -interest in the play. They got restless, and began to talk loudly and -to whistle. The Jew manager, who was standing at the back of the -dress-circle, stamped and swore with rage. The only person unmoved was -the girl herself. - -When the second act was over, there came a storm of hisses, and Lord -Henry got up from his chair and put on his coat. "She is quite -beautiful, Dorian," he said, "but she can't act. Let us go." - -"I am going to see the play through," answered the lad, in a hard -bitter voice. "I am awfully sorry that I have made you waste an -evening, Harry. I apologize to you both." - -"My dear Dorian, I should think Miss Vane was ill," interrupted -Hallward. "We will come some other night." - -"I wish she were ill," he rejoined. "But she seems to me to be simply -callous and cold. She has entirely altered. Last night she was a -great artist. This evening she is merely a commonplace mediocre -actress." - -"Don't talk like that about any one you love, Dorian. Love is a more -wonderful thing than art." - -"They are both simply forms of imitation," remarked Lord Henry. "But -do let us go. Dorian, you must not stay here any longer. It is not -good for one's morals to see bad acting. Besides, I don't suppose you -will want your wife to act, so what does it matter if she plays Juliet -like a wooden doll? She is very lovely, and if she knows as little -about life as she does about acting, she will be a delightful -experience. There are only two kinds of people who are really -fascinating--people who know absolutely everything, and people who know -absolutely nothing. Good heavens, my dear boy, don't look so tragic! -The secret of remaining young is never to have an emotion that is -unbecoming. Come to the club with Basil and myself. We will smoke -cigarettes and drink to the beauty of Sibyl Vane. She is beautiful. -What more can you want?" - -"Go away, Harry," cried the lad. "I want to be alone. Basil, you must -go. Ah! can't you see that my heart is breaking?" The hot tears came -to his eyes. His lips trembled, and rushing to the back of the box, he -leaned up against the wall, hiding his face in his hands. - -"Let us go, Basil," said Lord Henry with a strange tenderness in his -voice, and the two young men passed out together. - -A few moments afterwards the footlights flared up and the curtain rose -on the third act. Dorian Gray went back to his seat. He looked pale, -and proud, and indifferent. The play dragged on, and seemed -interminable. Half of the audience went out, tramping in heavy boots -and laughing. The whole thing was a _fiasco_. The last act was played -to almost empty benches. The curtain went down on a titter and some -groans. - -As soon as it was over, Dorian Gray rushed behind the scenes into the -greenroom. The girl was standing there alone, with a look of triumph -on her face. Her eyes were lit with an exquisite fire. There was a -radiance about her. Her parted lips were smiling over some secret of -their own. - -When he entered, she looked at him, and an expression of infinite joy -came over her. "How badly I acted to-night, Dorian!" she cried. - -"Horribly!" he answered, gazing at her in amazement. "Horribly! It -was dreadful. Are you ill? You have no idea what it was. You have no -idea what I suffered." - -The girl smiled. "Dorian," she answered, lingering over his name with -long-drawn music in her voice, as though it were sweeter than honey to -the red petals of her mouth. "Dorian, you should have understood. But -you understand now, don't you?" - -"Understand what?" he asked, angrily. - -"Why I was so bad to-night. Why I shall always be bad. Why I shall -never act well again." - -He shrugged his shoulders. "You are ill, I suppose. When you are ill -you shouldn't act. You make yourself ridiculous. My friends were -bored. I was bored." - -She seemed not to listen to him. She was transfigured with joy. An -ecstasy of happiness dominated her. - -"Dorian, Dorian," she cried, "before I knew you, acting was the one -reality of my life. It was only in the theatre that I lived. I -thought that it was all true. I was Rosalind one night and Portia the -other. The joy of Beatrice was my joy, and the sorrows of Cordelia -were mine also. I believed in everything. The common people who acted -with me seemed to me to be godlike. The painted scenes were my world. -I knew nothing but shadows, and I thought them real. You came--oh, my -beautiful love!--and you freed my soul from prison. You taught me what -reality really is. To-night, for the first time in my life, I saw -through the hollowness, the sham, the silliness of the empty pageant in -which I had always played. To-night, for the first time, I became -conscious that the Romeo was hideous, and old, and painted, that the -moonlight in the orchard was false, that the scenery was vulgar, and -that the words I had to speak were unreal, were not my words, were not -what I wanted to say. You had brought me something higher, something -of which all art is but a reflection. You had made me understand what -love really is. My love! My love! Prince Charming! Prince of life! -I have grown sick of shadows. You are more to me than all art can ever -be. What have I to do with the puppets of a play? When I came on -to-night, I could not understand how it was that everything had gone -from me. I thought that I was going to be wonderful. I found that I -could do nothing. Suddenly it dawned on my soul what it all meant. -The knowledge was exquisite to me. I heard them hissing, and I smiled. -What could they know of love such as ours? Take me away, Dorian--take -me away with you, where we can be quite alone. I hate the stage. I -might mimic a passion that I do not feel, but I cannot mimic one that -burns me like fire. Oh, Dorian, Dorian, you understand now what it -signifies? Even if I could do it, it would be profanation for me to -play at being in love. You have made me see that." - -He flung himself down on the sofa and turned away his face. "You have -killed my love," he muttered. - -She looked at him in wonder and laughed. He made no answer. She came -across to him, and with her little fingers stroked his hair. She knelt -down and pressed his hands to her lips. He drew them away, and a -shudder ran through him. - -Then he leaped up and went to the door. "Yes," he cried, "you have -killed my love. You used to stir my imagination. Now you don't even -stir my curiosity. You simply produce no effect. I loved you because -you were marvellous, because you had genius and intellect, because you -realized the dreams of great poets and gave shape and substance to the -shadows of art. You have thrown it all away. You are shallow and -stupid. My God! how mad I was to love you! What a fool I have been! -You are nothing to me now. I will never see you again. I will never -think of you. I will never mention your name. You don't know what you -were to me, once. Why, once ... Oh, I can't bear to think of it! I -wish I had never laid eyes upon you! You have spoiled the romance of -my life. How little you can know of love, if you say it mars your art! -Without your art, you are nothing. I would have made you famous, -splendid, magnificent. The world would have worshipped you, and you -would have borne my name. What are you now? A third-rate actress with -a pretty face." - -The girl grew white, and trembled. She clenched her hands together, -and her voice seemed to catch in her throat. "You are not serious, -Dorian?" she murmured. "You are acting." - -"Acting! I leave that to you. You do it so well," he answered -bitterly. - -She rose from her knees and, with a piteous expression of pain in her -face, came across the room to him. She put her hand upon his arm and -looked into his eyes. He thrust her back. "Don't touch me!" he cried. - -A low moan broke from her, and she flung herself at his feet and lay -there like a trampled flower. "Dorian, Dorian, don't leave me!" she -whispered. "I am so sorry I didn't act well. I was thinking of you -all the time. But I will try--indeed, I will try. It came so suddenly -across me, my love for you. I think I should never have known it if -you had not kissed me--if we had not kissed each other. Kiss me again, -my love. Don't go away from me. I couldn't bear it. Oh! don't go -away from me. My brother ... No; never mind. He didn't mean it. He -was in jest.... But you, oh! can't you forgive me for to-night? I will -work so hard and try to improve. Don't be cruel to me, because I love -you better than anything in the world. After all, it is only once that -I have not pleased you. But you are quite right, Dorian. I should -have shown myself more of an artist. It was foolish of me, and yet I -couldn't help it. Oh, don't leave me, don't leave me." A fit of -passionate sobbing choked her. She crouched on the floor like a -wounded thing, and Dorian Gray, with his beautiful eyes, looked down at -her, and his chiselled lips curled in exquisite disdain. There is -always something ridiculous about the emotions of people whom one has -ceased to love. Sibyl Vane seemed to him to be absurdly melodramatic. -Her tears and sobs annoyed him. - -"I am going," he said at last in his calm clear voice. "I don't wish -to be unkind, but I can't see you again. You have disappointed me." - -She wept silently, and made no answer, but crept nearer. Her little -hands stretched blindly out, and appeared to be seeking for him. He -turned on his heel and left the room. In a few moments he was out of -the theatre. - -Where he went to he hardly knew. He remembered wandering through dimly -lit streets, past gaunt, black-shadowed archways and evil-looking -houses. Women with hoarse voices and harsh laughter had called after -him. Drunkards had reeled by, cursing and chattering to themselves -like monstrous apes. He had seen grotesque children huddled upon -door-steps, and heard shrieks and oaths from gloomy courts. - -As the dawn was just breaking, he found himself close to Covent Garden. -The darkness lifted, and, flushed with faint fires, the sky hollowed -itself into a perfect pearl. Huge carts filled with nodding lilies -rumbled slowly down the polished empty street. The air was heavy with -the perfume of the flowers, and their beauty seemed to bring him an -anodyne for his pain. He followed into the market and watched the men -unloading their waggons. A white-smocked carter offered him some -cherries. He thanked him, wondered why he refused to accept any money -for them, and began to eat them listlessly. They had been plucked at -midnight, and the coldness of the moon had entered into them. A long -line of boys carrying crates of striped tulips, and of yellow and red -roses, defiled in front of him, threading their way through the huge, -jade-green piles of vegetables. Under the portico, with its grey, -sun-bleached pillars, loitered a troop of draggled bareheaded girls, -waiting for the auction to be over. Others crowded round the swinging -doors of the coffee-house in the piazza. The heavy cart-horses slipped -and stamped upon the rough stones, shaking their bells and trappings. -Some of the drivers were lying asleep on a pile of sacks. Iris-necked -and pink-footed, the pigeons ran about picking up seeds. - -After a little while, he hailed a hansom and drove home. For a few -moments he loitered upon the doorstep, looking round at the silent -square, with its blank, close-shuttered windows and its staring blinds. -The sky was pure opal now, and the roofs of the houses glistened like -silver against it. From some chimney opposite a thin wreath of smoke -was rising. It curled, a violet riband, through the nacre-coloured air. - -In the huge gilt Venetian lantern, spoil of some Doge's barge, that -hung from the ceiling of the great, oak-panelled hall of entrance, -lights were still burning from three flickering jets: thin blue petals -of flame they seemed, rimmed with white fire. He turned them out and, -having thrown his hat and cape on the table, passed through the library -towards the door of his bedroom, a large octagonal chamber on the -ground floor that, in his new-born feeling for luxury, he had just had -decorated for himself and hung with some curious Renaissance tapestries -that had been discovered stored in a disused attic at Selby Royal. As -he was turning the handle of the door, his eye fell upon the portrait -Basil Hallward had painted of him. He started back as if in surprise. -Then he went on into his own room, looking somewhat puzzled. After he -had taken the button-hole out of his coat, he seemed to hesitate. -Finally, he came back, went over to the picture, and examined it. In -the dim arrested light that struggled through the cream-coloured silk -blinds, the face appeared to him to be a little changed. The -expression looked different. One would have said that there was a -touch of cruelty in the mouth. It was certainly strange. - -He turned round and, walking to the window, drew up the blind. The -bright dawn flooded the room and swept the fantastic shadows into dusky -corners, where they lay shuddering. But the strange expression that he -had noticed in the face of the portrait seemed to linger there, to be -more intensified even. The quivering ardent sunlight showed him the -lines of cruelty round the mouth as clearly as if he had been looking -into a mirror after he had done some dreadful thing. - -He winced and, taking up from the table an oval glass framed in ivory -Cupids, one of Lord Henry's many presents to him, glanced hurriedly -into its polished depths. No line like that warped his red lips. What -did it mean? - -He rubbed his eyes, and came close to the picture, and examined it -again. There were no signs of any change when he looked into the -actual painting, and yet there was no doubt that the whole expression -had altered. It was not a mere fancy of his own. The thing was -horribly apparent. - -He threw himself into a chair and began to think. Suddenly there -flashed across his mind what he had said in Basil Hallward's studio the -day the picture had been finished. Yes, he remembered it perfectly. -He had uttered a mad wish that he himself might remain young, and the -portrait grow old; that his own beauty might be untarnished, and the -face on the canvas bear the burden of his passions and his sins; that -the painted image might be seared with the lines of suffering and -thought, and that he might keep all the delicate bloom and loveliness -of his then just conscious boyhood. Surely his wish had not been -fulfilled? Such things were impossible. It seemed monstrous even to -think of them. And, yet, there was the picture before him, with the -touch of cruelty in the mouth. - -Cruelty! Had he been cruel? It was the girl's fault, not his. He had -dreamed of her as a great artist, had given his love to her because he -had thought her great. Then she had disappointed him. She had been -shallow and unworthy. And, yet, a feeling of infinite regret came over -him, as he thought of her lying at his feet sobbing like a little -child. He remembered with what callousness he had watched her. Why -had he been made like that? Why had such a soul been given to him? -But he had suffered also. During the three terrible hours that the -play had lasted, he had lived centuries of pain, aeon upon aeon of -torture. His life was well worth hers. She had marred him for a -moment, if he had wounded her for an age. Besides, women were better -suited to bear sorrow than men. They lived on their emotions. They -only thought of their emotions. When they took lovers, it was merely -to have some one with whom they could have scenes. Lord Henry had told -him that, and Lord Henry knew what women were. Why should he trouble -about Sibyl Vane? She was nothing to him now. - -But the picture? What was he to say of that? It held the secret of -his life, and told his story. It had taught him to love his own -beauty. Would it teach him to loathe his own soul? Would he ever look -at it again? - -No; it was merely an illusion wrought on the troubled senses. The -horrible night that he had passed had left phantoms behind it. -Suddenly there had fallen upon his brain that tiny scarlet speck that -makes men mad. The picture had not changed. It was folly to think so. - -Yet it was watching him, with its beautiful marred face and its cruel -smile. Its bright hair gleamed in the early sunlight. Its blue eyes -met his own. A sense of infinite pity, not for himself, but for the -painted image of himself, came over him. It had altered already, and -would alter more. Its gold would wither into grey. Its red and white -roses would die. For every sin that he committed, a stain would fleck -and wreck its fairness. But he would not sin. The picture, changed or -unchanged, would be to him the visible emblem of conscience. He would -resist temptation. He would not see Lord Henry any more--would not, at -any rate, listen to those subtle poisonous theories that in Basil -Hallward's garden had first stirred within him the passion for -impossible things. He would go back to Sibyl Vane, make her amends, -marry her, try to love her again. Yes, it was his duty to do so. She -must have suffered more than he had. Poor child! He had been selfish -and cruel to her. The fascination that she had exercised over him -would return. They would be happy together. His life with her would -be beautiful and pure. - -He got up from his chair and drew a large screen right in front of the -portrait, shuddering as he glanced at it. "How horrible!" he murmured -to himself, and he walked across to the window and opened it. When he -stepped out on to the grass, he drew a deep breath. The fresh morning -air seemed to drive away all his sombre passions. He thought only of -Sibyl. A faint echo of his love came back to him. He repeated her -name over and over again. The birds that were singing in the -dew-drenched garden seemed to be telling the flowers about her. - - - -CHAPTER 8 - -It was long past noon when he awoke. His valet had crept several times -on tiptoe into the room to see if he was stirring, and had wondered -what made his young master sleep so late. Finally his bell sounded, -and Victor came in softly with a cup of tea, and a pile of letters, on -a small tray of old Sevres china, and drew back the olive-satin -curtains, with their shimmering blue lining, that hung in front of the -three tall windows. - -"Monsieur has well slept this morning," he said, smiling. - -"What o'clock is it, Victor?" asked Dorian Gray drowsily. - -"One hour and a quarter, Monsieur." - -How late it was! He sat up, and having sipped some tea, turned over -his letters. One of them was from Lord Henry, and had been brought by -hand that morning. He hesitated for a moment, and then put it aside. -The others he opened listlessly. They contained the usual collection -of cards, invitations to dinner, tickets for private views, programmes -of charity concerts, and the like that are showered on fashionable -young men every morning during the season. There was a rather heavy -bill for a chased silver Louis-Quinze toilet-set that he had not yet -had the courage to send on to his guardians, who were extremely -old-fashioned people and did not realize that we live in an age when -unnecessary things are our only necessities; and there were several -very courteously worded communications from Jermyn Street money-lenders -offering to advance any sum of money at a moment's notice and at the -most reasonable rates of interest. - -After about ten minutes he got up, and throwing on an elaborate -dressing-gown of silk-embroidered cashmere wool, passed into the -onyx-paved bathroom. The cool water refreshed him after his long -sleep. He seemed to have forgotten all that he had gone through. A -dim sense of having taken part in some strange tragedy came to him once -or twice, but there was the unreality of a dream about it. - -As soon as he was dressed, he went into the library and sat down to a -light French breakfast that had been laid out for him on a small round -table close to the open window. It was an exquisite day. The warm air -seemed laden with spices. A bee flew in and buzzed round the -blue-dragon bowl that, filled with sulphur-yellow roses, stood before -him. He felt perfectly happy. - -Suddenly his eye fell on the screen that he had placed in front of the -portrait, and he started. - -"Too cold for Monsieur?" asked his valet, putting an omelette on the -table. "I shut the window?" - -Dorian shook his head. "I am not cold," he murmured. - -Was it all true? Had the portrait really changed? Or had it been -simply his own imagination that had made him see a look of evil where -there had been a look of joy? Surely a painted canvas could not alter? -The thing was absurd. It would serve as a tale to tell Basil some day. -It would make him smile. - -And, yet, how vivid was his recollection of the whole thing! First in -the dim twilight, and then in the bright dawn, he had seen the touch of -cruelty round the warped lips. He almost dreaded his valet leaving the -room. He knew that when he was alone he would have to examine the -portrait. He was afraid of certainty. When the coffee and cigarettes -had been brought and the man turned to go, he felt a wild desire to -tell him to remain. As the door was closing behind him, he called him -back. The man stood waiting for his orders. Dorian looked at him for -a moment. "I am not at home to any one, Victor," he said with a sigh. -The man bowed and retired. - -Then he rose from the table, lit a cigarette, and flung himself down on -a luxuriously cushioned couch that stood facing the screen. The screen -was an old one, of gilt Spanish leather, stamped and wrought with a -rather florid Louis-Quatorze pattern. He scanned it curiously, -wondering if ever before it had concealed the secret of a man's life. - -Should he move it aside, after all? Why not let it stay there? What -was the use of knowing? If the thing was true, it was terrible. If it -was not true, why trouble about it? But what if, by some fate or -deadlier chance, eyes other than his spied behind and saw the horrible -change? What should he do if Basil Hallward came and asked to look at -his own picture? Basil would be sure to do that. No; the thing had to -be examined, and at once. Anything would be better than this dreadful -state of doubt. - -He got up and locked both doors. At least he would be alone when he -looked upon the mask of his shame. Then he drew the screen aside and -saw himself face to face. It was perfectly true. The portrait had -altered. - -As he often remembered afterwards, and always with no small wonder, he -found himself at first gazing at the portrait with a feeling of almost -scientific interest. That such a change should have taken place was -incredible to him. And yet it was a fact. Was there some subtle -affinity between the chemical atoms that shaped themselves into form -and colour on the canvas and the soul that was within him? Could it be -that what that soul thought, they realized?--that what it dreamed, they -made true? Or was there some other, more terrible reason? He -shuddered, and felt afraid, and, going back to the couch, lay there, -gazing at the picture in sickened horror. - -One thing, however, he felt that it had done for him. It had made him -conscious how unjust, how cruel, he had been to Sibyl Vane. It was not -too late to make reparation for that. She could still be his wife. -His unreal and selfish love would yield to some higher influence, would -be transformed into some nobler passion, and the portrait that Basil -Hallward had painted of him would be a guide to him through life, would -be to him what holiness is to some, and conscience to others, and the -fear of God to us all. There were opiates for remorse, drugs that -could lull the moral sense to sleep. But here was a visible symbol of -the degradation of sin. Here was an ever-present sign of the ruin men -brought upon their souls. - -Three o'clock struck, and four, and the half-hour rang its double -chime, but Dorian Gray did not stir. He was trying to gather up the -scarlet threads of life and to weave them into a pattern; to find his -way through the sanguine labyrinth of passion through which he was -wandering. He did not know what to do, or what to think. Finally, he -went over to the table and wrote a passionate letter to the girl he had -loved, imploring her forgiveness and accusing himself of madness. He -covered page after page with wild words of sorrow and wilder words of -pain. There is a luxury in self-reproach. When we blame ourselves, we -feel that no one else has a right to blame us. It is the confession, -not the priest, that gives us absolution. When Dorian had finished the -letter, he felt that he had been forgiven. - -Suddenly there came a knock to the door, and he heard Lord Henry's -voice outside. "My dear boy, I must see you. Let me in at once. I -can't bear your shutting yourself up like this." - -He made no answer at first, but remained quite still. The knocking -still continued and grew louder. Yes, it was better to let Lord Henry -in, and to explain to him the new life he was going to lead, to quarrel -with him if it became necessary to quarrel, to part if parting was -inevitable. He jumped up, drew the screen hastily across the picture, -and unlocked the door. - -"I am so sorry for it all, Dorian," said Lord Henry as he entered. -"But you must not think too much about it." - -"Do you mean about Sibyl Vane?" asked the lad. - -"Yes, of course," answered Lord Henry, sinking into a chair and slowly -pulling off his yellow gloves. "It is dreadful, from one point of -view, but it was not your fault. Tell me, did you go behind and see -her, after the play was over?" - -"Yes." - -"I felt sure you had. Did you make a scene with her?" - -"I was brutal, Harry--perfectly brutal. But it is all right now. I am -not sorry for anything that has happened. It has taught me to know -myself better." - -"Ah, Dorian, I am so glad you take it in that way! I was afraid I -would find you plunged in remorse and tearing that nice curly hair of -yours." - -"I have got through all that," said Dorian, shaking his head and -smiling. "I am perfectly happy now. I know what conscience is, to -begin with. It is not what you told me it was. It is the divinest -thing in us. Don't sneer at it, Harry, any more--at least not before -me. I want to be good. I can't bear the idea of my soul being -hideous." - -"A very charming artistic basis for ethics, Dorian! I congratulate you -on it. But how are you going to begin?" - -"By marrying Sibyl Vane." - -"Marrying Sibyl Vane!" cried Lord Henry, standing up and looking at him -in perplexed amazement. "But, my dear Dorian--" - -"Yes, Harry, I know what you are going to say. Something dreadful -about marriage. Don't say it. Don't ever say things of that kind to -me again. Two days ago I asked Sibyl to marry me. I am not going to -break my word to her. She is to be my wife." - -"Your wife! Dorian! ... Didn't you get my letter? I wrote to you this -morning, and sent the note down by my own man." - -"Your letter? Oh, yes, I remember. I have not read it yet, Harry. I -was afraid there might be something in it that I wouldn't like. You -cut life to pieces with your epigrams." - -"You know nothing then?" - -"What do you mean?" - -Lord Henry walked across the room, and sitting down by Dorian Gray, -took both his hands in his own and held them tightly. "Dorian," he -said, "my letter--don't be frightened--was to tell you that Sibyl Vane -is dead." - -A cry of pain broke from the lad's lips, and he leaped to his feet, -tearing his hands away from Lord Henry's grasp. "Dead! Sibyl dead! -It is not true! It is a horrible lie! How dare you say it?" - -"It is quite true, Dorian," said Lord Henry, gravely. "It is in all -the morning papers. I wrote down to you to ask you not to see any one -till I came. There will have to be an inquest, of course, and you must -not be mixed up in it. Things like that make a man fashionable in -Paris. But in London people are so prejudiced. Here, one should never -make one's _debut_ with a scandal. One should reserve that to give an -interest to one's old age. I suppose they don't know your name at the -theatre? If they don't, it is all right. Did any one see you going -round to her room? That is an important point." - -Dorian did not answer for a few moments. He was dazed with horror. -Finally he stammered, in a stifled voice, "Harry, did you say an -inquest? What did you mean by that? Did Sibyl--? Oh, Harry, I can't -bear it! But be quick. Tell me everything at once." - -"I have no doubt it was not an accident, Dorian, though it must be put -in that way to the public. It seems that as she was leaving the -theatre with her mother, about half-past twelve or so, she said she had -forgotten something upstairs. They waited some time for her, but she -did not come down again. They ultimately found her lying dead on the -floor of her dressing-room. She had swallowed something by mistake, -some dreadful thing they use at theatres. I don't know what it was, -but it had either prussic acid or white lead in it. I should fancy it -was prussic acid, as she seems to have died instantaneously." - -"Harry, Harry, it is terrible!" cried the lad. - -"Yes; it is very tragic, of course, but you must not get yourself mixed -up in it. I see by _The Standard_ that she was seventeen. I should have -thought she was almost younger than that. She looked such a child, and -seemed to know so little about acting. Dorian, you mustn't let this -thing get on your nerves. You must come and dine with me, and -afterwards we will look in at the opera. It is a Patti night, and -everybody will be there. You can come to my sister's box. She has got -some smart women with her." - -"So I have murdered Sibyl Vane," said Dorian Gray, half to himself, -"murdered her as surely as if I had cut her little throat with a knife. -Yet the roses are not less lovely for all that. The birds sing just as -happily in my garden. And to-night I am to dine with you, and then go -on to the opera, and sup somewhere, I suppose, afterwards. How -extraordinarily dramatic life is! If I had read all this in a book, -Harry, I think I would have wept over it. Somehow, now that it has -happened actually, and to me, it seems far too wonderful for tears. -Here is the first passionate love-letter I have ever written in my -life. Strange, that my first passionate love-letter should have been -addressed to a dead girl. Can they feel, I wonder, those white silent -people we call the dead? Sibyl! Can she feel, or know, or listen? -Oh, Harry, how I loved her once! It seems years ago to me now. She -was everything to me. Then came that dreadful night--was it really -only last night?--when she played so badly, and my heart almost broke. -She explained it all to me. It was terribly pathetic. But I was not -moved a bit. I thought her shallow. Suddenly something happened that -made me afraid. I can't tell you what it was, but it was terrible. I -said I would go back to her. I felt I had done wrong. And now she is -dead. My God! My God! Harry, what shall I do? You don't know the -danger I am in, and there is nothing to keep me straight. She would -have done that for me. She had no right to kill herself. It was -selfish of her." - -"My dear Dorian," answered Lord Henry, taking a cigarette from his case -and producing a gold-latten matchbox, "the only way a woman can ever -reform a man is by boring him so completely that he loses all possible -interest in life. If you had married this girl, you would have been -wretched. Of course, you would have treated her kindly. One can -always be kind to people about whom one cares nothing. But she would -have soon found out that you were absolutely indifferent to her. And -when a woman finds that out about her husband, she either becomes -dreadfully dowdy, or wears very smart bonnets that some other woman's -husband has to pay for. I say nothing about the social mistake, which -would have been abject--which, of course, I would not have allowed--but -I assure you that in any case the whole thing would have been an -absolute failure." - -"I suppose it would," muttered the lad, walking up and down the room -and looking horribly pale. "But I thought it was my duty. It is not -my fault that this terrible tragedy has prevented my doing what was -right. I remember your saying once that there is a fatality about good -resolutions--that they are always made too late. Mine certainly were." - -"Good resolutions are useless attempts to interfere with scientific -laws. Their origin is pure vanity. Their result is absolutely _nil_. -They give us, now and then, some of those luxurious sterile emotions -that have a certain charm for the weak. That is all that can be said -for them. They are simply cheques that men draw on a bank where they -have no account." - -"Harry," cried Dorian Gray, coming over and sitting down beside him, -"why is it that I cannot feel this tragedy as much as I want to? I -don't think I am heartless. Do you?" - -"You have done too many foolish things during the last fortnight to be -entitled to give yourself that name, Dorian," answered Lord Henry with -his sweet melancholy smile. - -The lad frowned. "I don't like that explanation, Harry," he rejoined, -"but I am glad you don't think I am heartless. I am nothing of the -kind. I know I am not. And yet I must admit that this thing that has -happened does not affect me as it should. It seems to me to be simply -like a wonderful ending to a wonderful play. It has all the terrible -beauty of a Greek tragedy, a tragedy in which I took a great part, but -by which I have not been wounded." - -"It is an interesting question," said Lord Henry, who found an -exquisite pleasure in playing on the lad's unconscious egotism, "an -extremely interesting question. I fancy that the true explanation is -this: It often happens that the real tragedies of life occur in such -an inartistic manner that they hurt us by their crude violence, their -absolute incoherence, their absurd want of meaning, their entire lack -of style. They affect us just as vulgarity affects us. They give us -an impression of sheer brute force, and we revolt against that. -Sometimes, however, a tragedy that possesses artistic elements of -beauty crosses our lives. If these elements of beauty are real, the -whole thing simply appeals to our sense of dramatic effect. Suddenly -we find that we are no longer the actors, but the spectators of the -play. Or rather we are both. We watch ourselves, and the mere wonder -of the spectacle enthralls us. In the present case, what is it that -has really happened? Some one has killed herself for love of you. I -wish that I had ever had such an experience. It would have made me in -love with love for the rest of my life. The people who have adored -me--there have not been very many, but there have been some--have -always insisted on living on, long after I had ceased to care for them, -or they to care for me. They have become stout and tedious, and when I -meet them, they go in at once for reminiscences. That awful memory of -woman! What a fearful thing it is! And what an utter intellectual -stagnation it reveals! One should absorb the colour of life, but one -should never remember its details. Details are always vulgar." - -"I must sow poppies in my garden," sighed Dorian. - -"There is no necessity," rejoined his companion. "Life has always -poppies in her hands. Of course, now and then things linger. I once -wore nothing but violets all through one season, as a form of artistic -mourning for a romance that would not die. Ultimately, however, it did -die. I forget what killed it. I think it was her proposing to -sacrifice the whole world for me. That is always a dreadful moment. -It fills one with the terror of eternity. Well--would you believe -it?--a week ago, at Lady Hampshire's, I found myself seated at dinner -next the lady in question, and she insisted on going over the whole -thing again, and digging up the past, and raking up the future. I had -buried my romance in a bed of asphodel. She dragged it out again and -assured me that I had spoiled her life. I am bound to state that she -ate an enormous dinner, so I did not feel any anxiety. But what a lack -of taste she showed! The one charm of the past is that it is the past. -But women never know when the curtain has fallen. They always want a -sixth act, and as soon as the interest of the play is entirely over, -they propose to continue it. If they were allowed their own way, every -comedy would have a tragic ending, and every tragedy would culminate in -a farce. They are charmingly artificial, but they have no sense of -art. You are more fortunate than I am. I assure you, Dorian, that not -one of the women I have known would have done for me what Sibyl Vane -did for you. Ordinary women always console themselves. Some of them -do it by going in for sentimental colours. Never trust a woman who -wears mauve, whatever her age may be, or a woman over thirty-five who -is fond of pink ribbons. It always means that they have a history. -Others find a great consolation in suddenly discovering the good -qualities of their husbands. They flaunt their conjugal felicity in -one's face, as if it were the most fascinating of sins. Religion -consoles some. Its mysteries have all the charm of a flirtation, a -woman once told me, and I can quite understand it. Besides, nothing -makes one so vain as being told that one is a sinner. Conscience makes -egotists of us all. Yes; there is really no end to the consolations -that women find in modern life. Indeed, I have not mentioned the most -important one." - -"What is that, Harry?" said the lad listlessly. - -"Oh, the obvious consolation. Taking some one else's admirer when one -loses one's own. In good society that always whitewashes a woman. But -really, Dorian, how different Sibyl Vane must have been from all the -women one meets! There is something to me quite beautiful about her -death. I am glad I am living in a century when such wonders happen. -They make one believe in the reality of the things we all play with, -such as romance, passion, and love." - -"I was terribly cruel to her. You forget that." - -"I am afraid that women appreciate cruelty, downright cruelty, more -than anything else. They have wonderfully primitive instincts. We -have emancipated them, but they remain slaves looking for their -masters, all the same. They love being dominated. I am sure you were -splendid. I have never seen you really and absolutely angry, but I can -fancy how delightful you looked. And, after all, you said something to -me the day before yesterday that seemed to me at the time to be merely -fanciful, but that I see now was absolutely true, and it holds the key -to everything." - -"What was that, Harry?" - -"You said to me that Sibyl Vane represented to you all the heroines of -romance--that she was Desdemona one night, and Ophelia the other; that -if she died as Juliet, she came to life as Imogen." - -"She will never come to life again now," muttered the lad, burying his -face in his hands. - -"No, she will never come to life. She has played her last part. But -you must think of that lonely death in the tawdry dressing-room simply -as a strange lurid fragment from some Jacobean tragedy, as a wonderful -scene from Webster, or Ford, or Cyril Tourneur. The girl never really -lived, and so she has never really died. To you at least she was -always a dream, a phantom that flitted through Shakespeare's plays and -left them lovelier for its presence, a reed through which Shakespeare's -music sounded richer and more full of joy. The moment she touched -actual life, she marred it, and it marred her, and so she passed away. -Mourn for Ophelia, if you like. Put ashes on your head because -Cordelia was strangled. Cry out against Heaven because the daughter of -Brabantio died. But don't waste your tears over Sibyl Vane. She was -less real than they are." - -There was a silence. The evening darkened in the room. Noiselessly, -and with silver feet, the shadows crept in from the garden. The -colours faded wearily out of things. - -After some time Dorian Gray looked up. "You have explained me to -myself, Harry," he murmured with something of a sigh of relief. "I -felt all that you have said, but somehow I was afraid of it, and I -could not express it to myself. How well you know me! But we will not -talk again of what has happened. It has been a marvellous experience. -That is all. I wonder if life has still in store for me anything as -marvellous." - -"Life has everything in store for you, Dorian. There is nothing that -you, with your extraordinary good looks, will not be able to do." - -"But suppose, Harry, I became haggard, and old, and wrinkled? What -then?" - -"Ah, then," said Lord Henry, rising to go, "then, my dear Dorian, you -would have to fight for your victories. As it is, they are brought to -you. No, you must keep your good looks. We live in an age that reads -too much to be wise, and that thinks too much to be beautiful. We -cannot spare you. And now you had better dress and drive down to the -club. We are rather late, as it is." - -"I think I shall join you at the opera, Harry. I feel too tired to eat -anything. What is the number of your sister's box?" - -"Twenty-seven, I believe. It is on the grand tier. You will see her -name on the door. But I am sorry you won't come and dine." - -"I don't feel up to it," said Dorian listlessly. "But I am awfully -obliged to you for all that you have said to me. You are certainly my -best friend. No one has ever understood me as you have." - -"We are only at the beginning of our friendship, Dorian," answered Lord -Henry, shaking him by the hand. "Good-bye. I shall see you before -nine-thirty, I hope. Remember, Patti is singing." - -As he closed the door behind him, Dorian Gray touched the bell, and in -a few minutes Victor appeared with the lamps and drew the blinds down. -He waited impatiently for him to go. The man seemed to take an -interminable time over everything. - -As soon as he had left, he rushed to the screen and drew it back. No; -there was no further change in the picture. It had received the news -of Sibyl Vane's death before he had known of it himself. It was -conscious of the events of life as they occurred. The vicious cruelty -that marred the fine lines of the mouth had, no doubt, appeared at the -very moment that the girl had drunk the poison, whatever it was. Or -was it indifferent to results? Did it merely take cognizance of what -passed within the soul? He wondered, and hoped that some day he would -see the change taking place before his very eyes, shuddering as he -hoped it. - -Poor Sibyl! What a romance it had all been! She had often mimicked -death on the stage. Then Death himself had touched her and taken her -with him. How had she played that dreadful last scene? Had she cursed -him, as she died? No; she had died for love of him, and love would -always be a sacrament to him now. She had atoned for everything by the -sacrifice she had made of her life. He would not think any more of -what she had made him go through, on that horrible night at the -theatre. When he thought of her, it would be as a wonderful tragic -figure sent on to the world's stage to show the supreme reality of -love. A wonderful tragic figure? Tears came to his eyes as he -remembered her childlike look, and winsome fanciful ways, and shy -tremulous grace. He brushed them away hastily and looked again at the -picture. - -He felt that the time had really come for making his choice. Or had -his choice already been made? Yes, life had decided that for -him--life, and his own infinite curiosity about life. Eternal youth, -infinite passion, pleasures subtle and secret, wild joys and wilder -sins--he was to have all these things. The portrait was to bear the -burden of his shame: that was all. - -A feeling of pain crept over him as he thought of the desecration that -was in store for the fair face on the canvas. Once, in boyish mockery -of Narcissus, he had kissed, or feigned to kiss, those painted lips -that now smiled so cruelly at him. Morning after morning he had sat -before the portrait wondering at its beauty, almost enamoured of it, as -it seemed to him at times. Was it to alter now with every mood to -which he yielded? Was it to become a monstrous and loathsome thing, to -be hidden away in a locked room, to be shut out from the sunlight that -had so often touched to brighter gold the waving wonder of its hair? -The pity of it! the pity of it! - -For a moment, he thought of praying that the horrible sympathy that -existed between him and the picture might cease. It had changed in -answer to a prayer; perhaps in answer to a prayer it might remain -unchanged. And yet, who, that knew anything about life, would -surrender the chance of remaining always young, however fantastic that -chance might be, or with what fateful consequences it might be fraught? -Besides, was it really under his control? Had it indeed been prayer -that had produced the substitution? Might there not be some curious -scientific reason for it all? If thought could exercise its influence -upon a living organism, might not thought exercise an influence upon -dead and inorganic things? Nay, without thought or conscious desire, -might not things external to ourselves vibrate in unison with our moods -and passions, atom calling to atom in secret love or strange affinity? -But the reason was of no importance. He would never again tempt by a -prayer any terrible power. If the picture was to alter, it was to -alter. That was all. Why inquire too closely into it? - -For there would be a real pleasure in watching it. He would be able to -follow his mind into its secret places. This portrait would be to him -the most magical of mirrors. As it had revealed to him his own body, -so it would reveal to him his own soul. And when winter came upon it, -he would still be standing where spring trembles on the verge of -summer. When the blood crept from its face, and left behind a pallid -mask of chalk with leaden eyes, he would keep the glamour of boyhood. -Not one blossom of his loveliness would ever fade. Not one pulse of -his life would ever weaken. Like the gods of the Greeks, he would be -strong, and fleet, and joyous. What did it matter what happened to the -coloured image on the canvas? He would be safe. That was everything. - -He drew the screen back into its former place in front of the picture, -smiling as he did so, and passed into his bedroom, where his valet was -already waiting for him. An hour later he was at the opera, and Lord -Henry was leaning over his chair. - - - -CHAPTER 9 - -As he was sitting at breakfast next morning, Basil Hallward was shown -into the room. - -"I am so glad I have found you, Dorian," he said gravely. "I called -last night, and they told me you were at the opera. Of course, I knew -that was impossible. But I wish you had left word where you had really -gone to. I passed a dreadful evening, half afraid that one tragedy -might be followed by another. I think you might have telegraphed for -me when you heard of it first. I read of it quite by chance in a late -edition of _The Globe_ that I picked up at the club. I came here at once -and was miserable at not finding you. I can't tell you how -heart-broken I am about the whole thing. I know what you must suffer. -But where were you? Did you go down and see the girl's mother? For a -moment I thought of following you there. They gave the address in the -paper. Somewhere in the Euston Road, isn't it? But I was afraid of -intruding upon a sorrow that I could not lighten. Poor woman! What a -state she must be in! And her only child, too! What did she say about -it all?" - -"My dear Basil, how do I know?" murmured Dorian Gray, sipping some -pale-yellow wine from a delicate, gold-beaded bubble of Venetian glass -and looking dreadfully bored. "I was at the opera. You should have -come on there. I met Lady Gwendolen, Harry's sister, for the first -time. We were in her box. She is perfectly charming; and Patti sang -divinely. Don't talk about horrid subjects. If one doesn't talk about -a thing, it has never happened. It is simply expression, as Harry -says, that gives reality to things. I may mention that she was not the -woman's only child. There is a son, a charming fellow, I believe. But -he is not on the stage. He is a sailor, or something. And now, tell -me about yourself and what you are painting." - -"You went to the opera?" said Hallward, speaking very slowly and with a -strained touch of pain in his voice. "You went to the opera while -Sibyl Vane was lying dead in some sordid lodging? You can talk to me -of other women being charming, and of Patti singing divinely, before -the girl you loved has even the quiet of a grave to sleep in? Why, -man, there are horrors in store for that little white body of hers!" - -"Stop, Basil! I won't hear it!" cried Dorian, leaping to his feet. -"You must not tell me about things. What is done is done. What is -past is past." - -"You call yesterday the past?" - -"What has the actual lapse of time got to do with it? It is only -shallow people who require years to get rid of an emotion. A man who -is master of himself can end a sorrow as easily as he can invent a -pleasure. I don't want to be at the mercy of my emotions. I want to -use them, to enjoy them, and to dominate them." - -"Dorian, this is horrible! Something has changed you completely. You -look exactly the same wonderful boy who, day after day, used to come -down to my studio to sit for his picture. But you were simple, -natural, and affectionate then. You were the most unspoiled creature -in the whole world. Now, I don't know what has come over you. You -talk as if you had no heart, no pity in you. It is all Harry's -influence. I see that." - -The lad flushed up and, going to the window, looked out for a few -moments on the green, flickering, sun-lashed garden. "I owe a great -deal to Harry, Basil," he said at last, "more than I owe to you. You -only taught me to be vain." - -"Well, I am punished for that, Dorian--or shall be some day." - -"I don't know what you mean, Basil," he exclaimed, turning round. "I -don't know what you want. What do you want?" - -"I want the Dorian Gray I used to paint," said the artist sadly. - -"Basil," said the lad, going over to him and putting his hand on his -shoulder, "you have come too late. Yesterday, when I heard that Sibyl -Vane had killed herself--" - -"Killed herself! Good heavens! is there no doubt about that?" cried -Hallward, looking up at him with an expression of horror. - -"My dear Basil! Surely you don't think it was a vulgar accident? Of -course she killed herself." - -The elder man buried his face in his hands. "How fearful," he -muttered, and a shudder ran through him. - -"No," said Dorian Gray, "there is nothing fearful about it. It is one -of the great romantic tragedies of the age. As a rule, people who act -lead the most commonplace lives. They are good husbands, or faithful -wives, or something tedious. You know what I mean--middle-class virtue -and all that kind of thing. How different Sibyl was! She lived her -finest tragedy. She was always a heroine. The last night she -played--the night you saw her--she acted badly because she had known -the reality of love. When she knew its unreality, she died, as Juliet -might have died. She passed again into the sphere of art. There is -something of the martyr about her. Her death has all the pathetic -uselessness of martyrdom, all its wasted beauty. But, as I was saying, -you must not think I have not suffered. If you had come in yesterday -at a particular moment--about half-past five, perhaps, or a quarter to -six--you would have found me in tears. Even Harry, who was here, who -brought me the news, in fact, had no idea what I was going through. I -suffered immensely. Then it passed away. I cannot repeat an emotion. -No one can, except sentimentalists. And you are awfully unjust, Basil. -You come down here to console me. That is charming of you. You find -me consoled, and you are furious. How like a sympathetic person! You -remind me of a story Harry told me about a certain philanthropist who -spent twenty years of his life in trying to get some grievance -redressed, or some unjust law altered--I forget exactly what it was. -Finally he succeeded, and nothing could exceed his disappointment. He -had absolutely nothing to do, almost died of _ennui_, and became a -confirmed misanthrope. And besides, my dear old Basil, if you really -want to console me, teach me rather to forget what has happened, or to -see it from a proper artistic point of view. Was it not Gautier who -used to write about _la consolation des arts_? I remember picking up a -little vellum-covered book in your studio one day and chancing on that -delightful phrase. Well, I am not like that young man you told me of -when we were down at Marlow together, the young man who used to say -that yellow satin could console one for all the miseries of life. I -love beautiful things that one can touch and handle. Old brocades, -green bronzes, lacquer-work, carved ivories, exquisite surroundings, -luxury, pomp--there is much to be got from all these. But the artistic -temperament that they create, or at any rate reveal, is still more to -me. To become the spectator of one's own life, as Harry says, is to -escape the suffering of life. I know you are surprised at my talking -to you like this. You have not realized how I have developed. I was a -schoolboy when you knew me. I am a man now. I have new passions, new -thoughts, new ideas. I am different, but you must not like me less. I -am changed, but you must always be my friend. Of course, I am very -fond of Harry. But I know that you are better than he is. You are not -stronger--you are too much afraid of life--but you are better. And how -happy we used to be together! Don't leave me, Basil, and don't quarrel -with me. I am what I am. There is nothing more to be said." - -The painter felt strangely moved. The lad was infinitely dear to him, -and his personality had been the great turning point in his art. He -could not bear the idea of reproaching him any more. After all, his -indifference was probably merely a mood that would pass away. There -was so much in him that was good, so much in him that was noble. - -"Well, Dorian," he said at length, with a sad smile, "I won't speak to -you again about this horrible thing, after to-day. I only trust your -name won't be mentioned in connection with it. The inquest is to take -place this afternoon. Have they summoned you?" - -Dorian shook his head, and a look of annoyance passed over his face at -the mention of the word "inquest." There was something so crude and -vulgar about everything of the kind. "They don't know my name," he -answered. - -"But surely she did?" - -"Only my Christian name, and that I am quite sure she never mentioned -to any one. She told me once that they were all rather curious to -learn who I was, and that she invariably told them my name was Prince -Charming. It was pretty of her. You must do me a drawing of Sibyl, -Basil. I should like to have something more of her than the memory of -a few kisses and some broken pathetic words." - -"I will try and do something, Dorian, if it would please you. But you -must come and sit to me yourself again. I can't get on without you." - -"I can never sit to you again, Basil. It is impossible!" he exclaimed, -starting back. - -The painter stared at him. "My dear boy, what nonsense!" he cried. -"Do you mean to say you don't like what I did of you? Where is it? -Why have you pulled the screen in front of it? Let me look at it. It -is the best thing I have ever done. Do take the screen away, Dorian. -It is simply disgraceful of your servant hiding my work like that. I -felt the room looked different as I came in." - -"My servant has nothing to do with it, Basil. You don't imagine I let -him arrange my room for me? He settles my flowers for me -sometimes--that is all. No; I did it myself. The light was too strong -on the portrait." - -"Too strong! Surely not, my dear fellow? It is an admirable place for -it. Let me see it." And Hallward walked towards the corner of the -room. - -A cry of terror broke from Dorian Gray's lips, and he rushed between -the painter and the screen. "Basil," he said, looking very pale, "you -must not look at it. I don't wish you to." - -"Not look at my own work! You are not serious. Why shouldn't I look -at it?" exclaimed Hallward, laughing. - -"If you try to look at it, Basil, on my word of honour I will never -speak to you again as long as I live. I am quite serious. I don't -offer any explanation, and you are not to ask for any. But, remember, -if you touch this screen, everything is over between us." - -Hallward was thunderstruck. He looked at Dorian Gray in absolute -amazement. He had never seen him like this before. The lad was -actually pallid with rage. His hands were clenched, and the pupils of -his eyes were like disks of blue fire. He was trembling all over. - -"Dorian!" - -"Don't speak!" - -"But what is the matter? Of course I won't look at it if you don't -want me to," he said, rather coldly, turning on his heel and going over -towards the window. "But, really, it seems rather absurd that I -shouldn't see my own work, especially as I am going to exhibit it in -Paris in the autumn. I shall probably have to give it another coat of -varnish before that, so I must see it some day, and why not to-day?" - -"To exhibit it! You want to exhibit it?" exclaimed Dorian Gray, a -strange sense of terror creeping over him. Was the world going to be -shown his secret? Were people to gape at the mystery of his life? -That was impossible. Something--he did not know what--had to be done -at once. - -"Yes; I don't suppose you will object to that. Georges Petit is going -to collect all my best pictures for a special exhibition in the Rue de -Seze, which will open the first week in October. The portrait will -only be away a month. I should think you could easily spare it for -that time. In fact, you are sure to be out of town. And if you keep -it always behind a screen, you can't care much about it." - -Dorian Gray passed his hand over his forehead. There were beads of -perspiration there. He felt that he was on the brink of a horrible -danger. "You told me a month ago that you would never exhibit it," he -cried. "Why have you changed your mind? You people who go in for -being consistent have just as many moods as others have. The only -difference is that your moods are rather meaningless. You can't have -forgotten that you assured me most solemnly that nothing in the world -would induce you to send it to any exhibition. You told Harry exactly -the same thing." He stopped suddenly, and a gleam of light came into -his eyes. He remembered that Lord Henry had said to him once, half -seriously and half in jest, "If you want to have a strange quarter of -an hour, get Basil to tell you why he won't exhibit your picture. He -told me why he wouldn't, and it was a revelation to me." Yes, perhaps -Basil, too, had his secret. He would ask him and try. - -"Basil," he said, coming over quite close and looking him straight in -the face, "we have each of us a secret. Let me know yours, and I shall -tell you mine. What was your reason for refusing to exhibit my -picture?" - -The painter shuddered in spite of himself. "Dorian, if I told you, you -might like me less than you do, and you would certainly laugh at me. I -could not bear your doing either of those two things. If you wish me -never to look at your picture again, I am content. I have always you -to look at. If you wish the best work I have ever done to be hidden -from the world, I am satisfied. Your friendship is dearer to me than -any fame or reputation." - -"No, Basil, you must tell me," insisted Dorian Gray. "I think I have a -right to know." His feeling of terror had passed away, and curiosity -had taken its place. He was determined to find out Basil Hallward's -mystery. - -"Let us sit down, Dorian," said the painter, looking troubled. "Let us -sit down. And just answer me one question. Have you noticed in the -picture something curious?--something that probably at first did not -strike you, but that revealed itself to you suddenly?" - -"Basil!" cried the lad, clutching the arms of his chair with trembling -hands and gazing at him with wild startled eyes. - -"I see you did. Don't speak. Wait till you hear what I have to say. -Dorian, from the moment I met you, your personality had the most -extraordinary influence over me. I was dominated, soul, brain, and -power, by you. You became to me the visible incarnation of that unseen -ideal whose memory haunts us artists like an exquisite dream. I -worshipped you. I grew jealous of every one to whom you spoke. I -wanted to have you all to myself. I was only happy when I was with -you. When you were away from me, you were still present in my art.... -Of course, I never let you know anything about this. It would have -been impossible. You would not have understood it. I hardly -understood it myself. I only knew that I had seen perfection face to -face, and that the world had become wonderful to my eyes--too -wonderful, perhaps, for in such mad worships there is peril, the peril -of losing them, no less than the peril of keeping them.... Weeks and -weeks went on, and I grew more and more absorbed in you. Then came a -new development. I had drawn you as Paris in dainty armour, and as -Adonis with huntsman's cloak and polished boar-spear. Crowned with -heavy lotus-blossoms you had sat on the prow of Adrian's barge, gazing -across the green turbid Nile. You had leaned over the still pool of -some Greek woodland and seen in the water's silent silver the marvel of -your own face. And it had all been what art should be--unconscious, -ideal, and remote. One day, a fatal day I sometimes think, I -determined to paint a wonderful portrait of you as you actually are, -not in the costume of dead ages, but in your own dress and in your own -time. Whether it was the realism of the method, or the mere wonder of -your own personality, thus directly presented to me without mist or -veil, I cannot tell. But I know that as I worked at it, every flake -and film of colour seemed to me to reveal my secret. I grew afraid -that others would know of my idolatry. I felt, Dorian, that I had told -too much, that I had put too much of myself into it. Then it was that -I resolved never to allow the picture to be exhibited. You were a -little annoyed; but then you did not realize all that it meant to me. -Harry, to whom I talked about it, laughed at me. But I did not mind -that. When the picture was finished, and I sat alone with it, I felt -that I was right.... Well, after a few days the thing left my studio, -and as soon as I had got rid of the intolerable fascination of its -presence, it seemed to me that I had been foolish in imagining that I -had seen anything in it, more than that you were extremely good-looking -and that I could paint. Even now I cannot help feeling that it is a -mistake to think that the passion one feels in creation is ever really -shown in the work one creates. Art is always more abstract than we -fancy. Form and colour tell us of form and colour--that is all. It -often seems to me that art conceals the artist far more completely than -it ever reveals him. And so when I got this offer from Paris, I -determined to make your portrait the principal thing in my exhibition. -It never occurred to me that you would refuse. I see now that you were -right. The picture cannot be shown. You must not be angry with me, -Dorian, for what I have told you. As I said to Harry, once, you are -made to be worshipped." - -Dorian Gray drew a long breath. The colour came back to his cheeks, -and a smile played about his lips. The peril was over. He was safe -for the time. Yet he could not help feeling infinite pity for the -painter who had just made this strange confession to him, and wondered -if he himself would ever be so dominated by the personality of a -friend. Lord Henry had the charm of being very dangerous. But that -was all. He was too clever and too cynical to be really fond of. -Would there ever be some one who would fill him with a strange -idolatry? Was that one of the things that life had in store? - -"It is extraordinary to me, Dorian," said Hallward, "that you should -have seen this in the portrait. Did you really see it?" - -"I saw something in it," he answered, "something that seemed to me very -curious." - -"Well, you don't mind my looking at the thing now?" - -Dorian shook his head. "You must not ask me that, Basil. I could not -possibly let you stand in front of that picture." - -"You will some day, surely?" - -"Never." - -"Well, perhaps you are right. And now good-bye, Dorian. You have been -the one person in my life who has really influenced my art. Whatever I -have done that is good, I owe to you. Ah! you don't know what it cost -me to tell you all that I have told you." - -"My dear Basil," said Dorian, "what have you told me? Simply that you -felt that you admired me too much. That is not even a compliment." - -"It was not intended as a compliment. It was a confession. Now that I -have made it, something seems to have gone out of me. Perhaps one -should never put one's worship into words." - -"It was a very disappointing confession." - -"Why, what did you expect, Dorian? You didn't see anything else in the -picture, did you? There was nothing else to see?" - -"No; there was nothing else to see. Why do you ask? But you mustn't -talk about worship. It is foolish. You and I are friends, Basil, and -we must always remain so." - -"You have got Harry," said the painter sadly. - -"Oh, Harry!" cried the lad, with a ripple of laughter. "Harry spends -his days in saying what is incredible and his evenings in doing what is -improbable. Just the sort of life I would like to lead. But still I -don't think I would go to Harry if I were in trouble. I would sooner -go to you, Basil." - -"You will sit to me again?" - -"Impossible!" - -"You spoil my life as an artist by refusing, Dorian. No man comes -across two ideal things. Few come across one." - -"I can't explain it to you, Basil, but I must never sit to you again. -There is something fatal about a portrait. It has a life of its own. -I will come and have tea with you. That will be just as pleasant." - -"Pleasanter for you, I am afraid," murmured Hallward regretfully. "And -now good-bye. I am sorry you won't let me look at the picture once -again. But that can't be helped. I quite understand what you feel -about it." - -As he left the room, Dorian Gray smiled to himself. Poor Basil! How -little he knew of the true reason! And how strange it was that, -instead of having been forced to reveal his own secret, he had -succeeded, almost by chance, in wresting a secret from his friend! How -much that strange confession explained to him! The painter's absurd -fits of jealousy, his wild devotion, his extravagant panegyrics, his -curious reticences--he understood them all now, and he felt sorry. -There seemed to him to be something tragic in a friendship so coloured -by romance. - -He sighed and touched the bell. The portrait must be hidden away at -all costs. He could not run such a risk of discovery again. It had -been mad of him to have allowed the thing to remain, even for an hour, -in a room to which any of his friends had access. - - - -CHAPTER 10 - -When his servant entered, he looked at him steadfastly and wondered if -he had thought of peering behind the screen. The man was quite -impassive and waited for his orders. Dorian lit a cigarette and walked -over to the glass and glanced into it. He could see the reflection of -Victor's face perfectly. It was like a placid mask of servility. -There was nothing to be afraid of, there. Yet he thought it best to be -on his guard. - -Speaking very slowly, he told him to tell the house-keeper that he -wanted to see her, and then to go to the frame-maker and ask him to -send two of his men round at once. It seemed to him that as the man -left the room his eyes wandered in the direction of the screen. Or was -that merely his own fancy? - -After a few moments, in her black silk dress, with old-fashioned thread -mittens on her wrinkled hands, Mrs. Leaf bustled into the library. He -asked her for the key of the schoolroom. - -"The old schoolroom, Mr. Dorian?" she exclaimed. "Why, it is full of -dust. I must get it arranged and put straight before you go into it. -It is not fit for you to see, sir. It is not, indeed." - -"I don't want it put straight, Leaf. I only want the key." - -"Well, sir, you'll be covered with cobwebs if you go into it. Why, it -hasn't been opened for nearly five years--not since his lordship died." - -He winced at the mention of his grandfather. He had hateful memories -of him. "That does not matter," he answered. "I simply want to see -the place--that is all. Give me the key." - -"And here is the key, sir," said the old lady, going over the contents -of her bunch with tremulously uncertain hands. "Here is the key. I'll -have it off the bunch in a moment. But you don't think of living up -there, sir, and you so comfortable here?" - -"No, no," he cried petulantly. "Thank you, Leaf. That will do." - -She lingered for a few moments, and was garrulous over some detail of -the household. He sighed and told her to manage things as she thought -best. She left the room, wreathed in smiles. - -As the door closed, Dorian put the key in his pocket and looked round -the room. His eye fell on a large, purple satin coverlet heavily -embroidered with gold, a splendid piece of late seventeenth-century -Venetian work that his grandfather had found in a convent near Bologna. -Yes, that would serve to wrap the dreadful thing in. It had perhaps -served often as a pall for the dead. Now it was to hide something that -had a corruption of its own, worse than the corruption of death -itself--something that would breed horrors and yet would never die. -What the worm was to the corpse, his sins would be to the painted image -on the canvas. They would mar its beauty and eat away its grace. They -would defile it and make it shameful. And yet the thing would still -live on. It would be always alive. - -He shuddered, and for a moment he regretted that he had not told Basil -the true reason why he had wished to hide the picture away. Basil -would have helped him to resist Lord Henry's influence, and the still -more poisonous influences that came from his own temperament. The love -that he bore him--for it was really love--had nothing in it that was -not noble and intellectual. It was not that mere physical admiration -of beauty that is born of the senses and that dies when the senses -tire. It was such love as Michelangelo had known, and Montaigne, and -Winckelmann, and Shakespeare himself. Yes, Basil could have saved him. -But it was too late now. The past could always be annihilated. -Regret, denial, or forgetfulness could do that. But the future was -inevitable. There were passions in him that would find their terrible -outlet, dreams that would make the shadow of their evil real. - -He took up from the couch the great purple-and-gold texture that -covered it, and, holding it in his hands, passed behind the screen. -Was the face on the canvas viler than before? It seemed to him that it -was unchanged, and yet his loathing of it was intensified. Gold hair, -blue eyes, and rose-red lips--they all were there. It was simply the -expression that had altered. That was horrible in its cruelty. -Compared to what he saw in it of censure or rebuke, how shallow Basil's -reproaches about Sibyl Vane had been!--how shallow, and of what little -account! His own soul was looking out at him from the canvas and -calling him to judgement. A look of pain came across him, and he flung -the rich pall over the picture. As he did so, a knock came to the -door. He passed out as his servant entered. - -"The persons are here, Monsieur." - -He felt that the man must be got rid of at once. He must not be -allowed to know where the picture was being taken to. There was -something sly about him, and he had thoughtful, treacherous eyes. -Sitting down at the writing-table he scribbled a note to Lord Henry, -asking him to send him round something to read and reminding him that -they were to meet at eight-fifteen that evening. - -"Wait for an answer," he said, handing it to him, "and show the men in -here." - -In two or three minutes there was another knock, and Mr. Hubbard -himself, the celebrated frame-maker of South Audley Street, came in -with a somewhat rough-looking young assistant. Mr. Hubbard was a -florid, red-whiskered little man, whose admiration for art was -considerably tempered by the inveterate impecuniosity of most of the -artists who dealt with him. As a rule, he never left his shop. He -waited for people to come to him. But he always made an exception in -favour of Dorian Gray. There was something about Dorian that charmed -everybody. It was a pleasure even to see him. - -"What can I do for you, Mr. Gray?" he said, rubbing his fat freckled -hands. "I thought I would do myself the honour of coming round in -person. I have just got a beauty of a frame, sir. Picked it up at a -sale. Old Florentine. Came from Fonthill, I believe. Admirably -suited for a religious subject, Mr. Gray." - -"I am so sorry you have given yourself the trouble of coming round, Mr. -Hubbard. I shall certainly drop in and look at the frame--though I -don't go in much at present for religious art--but to-day I only want a -picture carried to the top of the house for me. It is rather heavy, so -I thought I would ask you to lend me a couple of your men." - -"No trouble at all, Mr. Gray. I am delighted to be of any service to -you. Which is the work of art, sir?" - -"This," replied Dorian, moving the screen back. "Can you move it, -covering and all, just as it is? I don't want it to get scratched -going upstairs." - -"There will be no difficulty, sir," said the genial frame-maker, -beginning, with the aid of his assistant, to unhook the picture from -the long brass chains by which it was suspended. "And, now, where -shall we carry it to, Mr. Gray?" - -"I will show you the way, Mr. Hubbard, if you will kindly follow me. -Or perhaps you had better go in front. I am afraid it is right at the -top of the house. We will go up by the front staircase, as it is -wider." - -He held the door open for them, and they passed out into the hall and -began the ascent. The elaborate character of the frame had made the -picture extremely bulky, and now and then, in spite of the obsequious -protests of Mr. Hubbard, who had the true tradesman's spirited dislike -of seeing a gentleman doing anything useful, Dorian put his hand to it -so as to help them. - -"Something of a load to carry, sir," gasped the little man when they -reached the top landing. And he wiped his shiny forehead. - -"I am afraid it is rather heavy," murmured Dorian as he unlocked the -door that opened into the room that was to keep for him the curious -secret of his life and hide his soul from the eyes of men. - -He had not entered the place for more than four years--not, indeed, -since he had used it first as a play-room when he was a child, and then -as a study when he grew somewhat older. It was a large, -well-proportioned room, which had been specially built by the last Lord -Kelso for the use of the little grandson whom, for his strange likeness -to his mother, and also for other reasons, he had always hated and -desired to keep at a distance. It appeared to Dorian to have but -little changed. There was the huge Italian _cassone_, with its -fantastically painted panels and its tarnished gilt mouldings, in which -he had so often hidden himself as a boy. There the satinwood book-case -filled with his dog-eared schoolbooks. On the wall behind it was -hanging the same ragged Flemish tapestry where a faded king and queen -were playing chess in a garden, while a company of hawkers rode by, -carrying hooded birds on their gauntleted wrists. How well he -remembered it all! Every moment of his lonely childhood came back to -him as he looked round. He recalled the stainless purity of his boyish -life, and it seemed horrible to him that it was here the fatal portrait -was to be hidden away. How little he had thought, in those dead days, -of all that was in store for him! - -But there was no other place in the house so secure from prying eyes as -this. He had the key, and no one else could enter it. Beneath its -purple pall, the face painted on the canvas could grow bestial, sodden, -and unclean. What did it matter? No one could see it. He himself -would not see it. Why should he watch the hideous corruption of his -soul? He kept his youth--that was enough. And, besides, might not -his nature grow finer, after all? There was no reason that the future -should be so full of shame. Some love might come across his life, and -purify him, and shield him from those sins that seemed to be already -stirring in spirit and in flesh--those curious unpictured sins whose -very mystery lent them their subtlety and their charm. Perhaps, some -day, the cruel look would have passed away from the scarlet sensitive -mouth, and he might show to the world Basil Hallward's masterpiece. - -No; that was impossible. Hour by hour, and week by week, the thing -upon the canvas was growing old. It might escape the hideousness of -sin, but the hideousness of age was in store for it. The cheeks would -become hollow or flaccid. Yellow crow's feet would creep round the -fading eyes and make them horrible. The hair would lose its -brightness, the mouth would gape or droop, would be foolish or gross, -as the mouths of old men are. There would be the wrinkled throat, the -cold, blue-veined hands, the twisted body, that he remembered in the -grandfather who had been so stern to him in his boyhood. The picture -had to be concealed. There was no help for it. - -"Bring it in, Mr. Hubbard, please," he said, wearily, turning round. -"I am sorry I kept you so long. I was thinking of something else." - -"Always glad to have a rest, Mr. Gray," answered the frame-maker, who -was still gasping for breath. "Where shall we put it, sir?" - -"Oh, anywhere. Here: this will do. I don't want to have it hung up. -Just lean it against the wall. Thanks." - -"Might one look at the work of art, sir?" - -Dorian started. "It would not interest you, Mr. Hubbard," he said, -keeping his eye on the man. He felt ready to leap upon him and fling -him to the ground if he dared to lift the gorgeous hanging that -concealed the secret of his life. "I shan't trouble you any more now. -I am much obliged for your kindness in coming round." - -"Not at all, not at all, Mr. Gray. Ever ready to do anything for you, -sir." And Mr. Hubbard tramped downstairs, followed by the assistant, -who glanced back at Dorian with a look of shy wonder in his rough -uncomely face. He had never seen any one so marvellous. - -When the sound of their footsteps had died away, Dorian locked the door -and put the key in his pocket. He felt safe now. No one would ever -look upon the horrible thing. No eye but his would ever see his shame. - -On reaching the library, he found that it was just after five o'clock -and that the tea had been already brought up. On a little table of -dark perfumed wood thickly incrusted with nacre, a present from Lady -Radley, his guardian's wife, a pretty professional invalid who had -spent the preceding winter in Cairo, was lying a note from Lord Henry, -and beside it was a book bound in yellow paper, the cover slightly torn -and the edges soiled. A copy of the third edition of _The St. James's -Gazette_ had been placed on the tea-tray. It was evident that Victor had -returned. He wondered if he had met the men in the hall as they were -leaving the house and had wormed out of them what they had been doing. -He would be sure to miss the picture--had no doubt missed it already, -while he had been laying the tea-things. The screen had not been set -back, and a blank space was visible on the wall. Perhaps some night he -might find him creeping upstairs and trying to force the door of the -room. It was a horrible thing to have a spy in one's house. He had -heard of rich men who had been blackmailed all their lives by some -servant who had read a letter, or overheard a conversation, or picked -up a card with an address, or found beneath a pillow a withered flower -or a shred of crumpled lace. - -He sighed, and having poured himself out some tea, opened Lord Henry's -note. It was simply to say that he sent him round the evening paper, -and a book that might interest him, and that he would be at the club at -eight-fifteen. He opened _The St. James's_ languidly, and looked through -it. A red pencil-mark on the fifth page caught his eye. It drew -attention to the following paragraph: - - -INQUEST ON AN ACTRESS.--An inquest was held this morning at the Bell -Tavern, Hoxton Road, by Mr. Danby, the District Coroner, on the body of -Sibyl Vane, a young actress recently engaged at the Royal Theatre, -Holborn. A verdict of death by misadventure was returned. -Considerable sympathy was expressed for the mother of the deceased, who -was greatly affected during the giving of her own evidence, and that of -Dr. Birrell, who had made the post-mortem examination of the deceased. - - -He frowned, and tearing the paper in two, went across the room and -flung the pieces away. How ugly it all was! And how horribly real -ugliness made things! He felt a little annoyed with Lord Henry for -having sent him the report. And it was certainly stupid of him to have -marked it with red pencil. Victor might have read it. The man knew -more than enough English for that. - -Perhaps he had read it and had begun to suspect something. And, yet, -what did it matter? What had Dorian Gray to do with Sibyl Vane's -death? There was nothing to fear. Dorian Gray had not killed her. - -His eye fell on the yellow book that Lord Henry had sent him. What was -it, he wondered. He went towards the little, pearl-coloured octagonal -stand that had always looked to him like the work of some strange -Egyptian bees that wrought in silver, and taking up the volume, flung -himself into an arm-chair and began to turn over the leaves. After a -few minutes he became absorbed. It was the strangest book that he had -ever read. It seemed to him that in exquisite raiment, and to the -delicate sound of flutes, the sins of the world were passing in dumb -show before him. Things that he had dimly dreamed of were suddenly -made real to him. Things of which he had never dreamed were gradually -revealed. - -It was a novel without a plot and with only one character, being, -indeed, simply a psychological study of a certain young Parisian who -spent his life trying to realize in the nineteenth century all the -passions and modes of thought that belonged to every century except his -own, and to sum up, as it were, in himself the various moods through -which the world-spirit had ever passed, loving for their mere -artificiality those renunciations that men have unwisely called virtue, -as much as those natural rebellions that wise men still call sin. The -style in which it was written was that curious jewelled style, vivid -and obscure at once, full of _argot_ and of archaisms, of technical -expressions and of elaborate paraphrases, that characterizes the work -of some of the finest artists of the French school of _Symbolistes_. -There were in it metaphors as monstrous as orchids and as subtle in -colour. The life of the senses was described in the terms of mystical -philosophy. One hardly knew at times whether one was reading the -spiritual ecstasies of some mediaeval saint or the morbid confessions -of a modern sinner. It was a poisonous book. The heavy odour of -incense seemed to cling about its pages and to trouble the brain. The -mere cadence of the sentences, the subtle monotony of their music, so -full as it was of complex refrains and movements elaborately repeated, -produced in the mind of the lad, as he passed from chapter to chapter, -a form of reverie, a malady of dreaming, that made him unconscious of -the falling day and creeping shadows. - -Cloudless, and pierced by one solitary star, a copper-green sky gleamed -through the windows. He read on by its wan light till he could read no -more. Then, after his valet had reminded him several times of the -lateness of the hour, he got up, and going into the next room, placed -the book on the little Florentine table that always stood at his -bedside and began to dress for dinner. - -It was almost nine o'clock before he reached the club, where he found -Lord Henry sitting alone, in the morning-room, looking very much bored. - -"I am so sorry, Harry," he cried, "but really it is entirely your -fault. That book you sent me so fascinated me that I forgot how the -time was going." - -"Yes, I thought you would like it," replied his host, rising from his -chair. - -"I didn't say I liked it, Harry. I said it fascinated me. There is a -great difference." - -"Ah, you have discovered that?" murmured Lord Henry. And they passed -into the dining-room. - - - -CHAPTER 11 - -For years, Dorian Gray could not free himself from the influence of -this book. Or perhaps it would be more accurate to say that he never -sought to free himself from it. He procured from Paris no less than -nine large-paper copies of the first edition, and had them bound in -different colours, so that they might suit his various moods and the -changing fancies of a nature over which he seemed, at times, to have -almost entirely lost control. The hero, the wonderful young Parisian -in whom the romantic and the scientific temperaments were so strangely -blended, became to him a kind of prefiguring type of himself. And, -indeed, the whole book seemed to him to contain the story of his own -life, written before he had lived it. - -In one point he was more fortunate than the novel's fantastic hero. He -never knew--never, indeed, had any cause to know--that somewhat -grotesque dread of mirrors, and polished metal surfaces, and still -water which came upon the young Parisian so early in his life, and was -occasioned by the sudden decay of a beau that had once, apparently, -been so remarkable. It was with an almost cruel joy--and perhaps in -nearly every joy, as certainly in every pleasure, cruelty has its -place--that he used to read the latter part of the book, with its -really tragic, if somewhat overemphasized, account of the sorrow and -despair of one who had himself lost what in others, and the world, he -had most dearly valued. - -For the wonderful beauty that had so fascinated Basil Hallward, and -many others besides him, seemed never to leave him. Even those who had -heard the most evil things against him--and from time to time strange -rumours about his mode of life crept through London and became the -chatter of the clubs--could not believe anything to his dishonour when -they saw him. He had always the look of one who had kept himself -unspotted from the world. Men who talked grossly became silent when -Dorian Gray entered the room. There was something in the purity of his -face that rebuked them. His mere presence seemed to recall to them the -memory of the innocence that they had tarnished. They wondered how one -so charming and graceful as he was could have escaped the stain of an -age that was at once sordid and sensual. - -Often, on returning home from one of those mysterious and prolonged -absences that gave rise to such strange conjecture among those who were -his friends, or thought that they were so, he himself would creep -upstairs to the locked room, open the door with the key that never left -him now, and stand, with a mirror, in front of the portrait that Basil -Hallward had painted of him, looking now at the evil and aging face on -the canvas, and now at the fair young face that laughed back at him -from the polished glass. The very sharpness of the contrast used to -quicken his sense of pleasure. He grew more and more enamoured of his -own beauty, more and more interested in the corruption of his own soul. -He would examine with minute care, and sometimes with a monstrous and -terrible delight, the hideous lines that seared the wrinkling forehead -or crawled around the heavy sensual mouth, wondering sometimes which -were the more horrible, the signs of sin or the signs of age. He would -place his white hands beside the coarse bloated hands of the picture, -and smile. He mocked the misshapen body and the failing limbs. - -There were moments, indeed, at night, when, lying sleepless in his own -delicately scented chamber, or in the sordid room of the little -ill-famed tavern near the docks which, under an assumed name and in -disguise, it was his habit to frequent, he would think of the ruin he -had brought upon his soul with a pity that was all the more poignant -because it was purely selfish. But moments such as these were rare. -That curiosity about life which Lord Henry had first stirred in him, as -they sat together in the garden of their friend, seemed to increase -with gratification. The more he knew, the more he desired to know. He -had mad hungers that grew more ravenous as he fed them. - -Yet he was not really reckless, at any rate in his relations to -society. Once or twice every month during the winter, and on each -Wednesday evening while the season lasted, he would throw open to the -world his beautiful house and have the most celebrated musicians of the -day to charm his guests with the wonders of their art. His little -dinners, in the settling of which Lord Henry always assisted him, were -noted as much for the careful selection and placing of those invited, -as for the exquisite taste shown in the decoration of the table, with -its subtle symphonic arrangements of exotic flowers, and embroidered -cloths, and antique plate of gold and silver. Indeed, there were many, -especially among the very young men, who saw, or fancied that they saw, -in Dorian Gray the true realization of a type of which they had often -dreamed in Eton or Oxford days, a type that was to combine something of -the real culture of the scholar with all the grace and distinction and -perfect manner of a citizen of the world. To them he seemed to be of -the company of those whom Dante describes as having sought to "make -themselves perfect by the worship of beauty." Like Gautier, he was one -for whom "the visible world existed." - -And, certainly, to him life itself was the first, the greatest, of the -arts, and for it all the other arts seemed to be but a preparation. -Fashion, by which what is really fantastic becomes for a moment -universal, and dandyism, which, in its own way, is an attempt to assert -the absolute modernity of beauty, had, of course, their fascination for -him. His mode of dressing, and the particular styles that from time to -time he affected, had their marked influence on the young exquisites of -the Mayfair balls and Pall Mall club windows, who copied him in -everything that he did, and tried to reproduce the accidental charm of -his graceful, though to him only half-serious, fopperies. - -For, while he was but too ready to accept the position that was almost -immediately offered to him on his coming of age, and found, indeed, a -subtle pleasure in the thought that he might really become to the -London of his own day what to imperial Neronian Rome the author of the -Satyricon once had been, yet in his inmost heart he desired to be -something more than a mere _arbiter elegantiarum_, to be consulted on the -wearing of a jewel, or the knotting of a necktie, or the conduct of a -cane. He sought to elaborate some new scheme of life that would have -its reasoned philosophy and its ordered principles, and find in the -spiritualizing of the senses its highest realization. - -The worship of the senses has often, and with much justice, been -decried, men feeling a natural instinct of terror about passions and -sensations that seem stronger than themselves, and that they are -conscious of sharing with the less highly organized forms of existence. -But it appeared to Dorian Gray that the true nature of the senses had -never been understood, and that they had remained savage and animal -merely because the world had sought to starve them into submission or -to kill them by pain, instead of aiming at making them elements of a -new spirituality, of which a fine instinct for beauty was to be the -dominant characteristic. As he looked back upon man moving through -history, he was haunted by a feeling of loss. So much had been -surrendered! and to such little purpose! There had been mad wilful -rejections, monstrous forms of self-torture and self-denial, whose -origin was fear and whose result was a degradation infinitely more -terrible than that fancied degradation from which, in their ignorance, -they had sought to escape; Nature, in her wonderful irony, driving out -the anchorite to feed with the wild animals of the desert and giving to -the hermit the beasts of the field as his companions. - -Yes: there was to be, as Lord Henry had prophesied, a new Hedonism -that was to recreate life and to save it from that harsh uncomely -puritanism that is having, in our own day, its curious revival. It was -to have its service of the intellect, certainly, yet it was never to -accept any theory or system that would involve the sacrifice of any -mode of passionate experience. Its aim, indeed, was to be experience -itself, and not the fruits of experience, sweet or bitter as they might -be. Of the asceticism that deadens the senses, as of the vulgar -profligacy that dulls them, it was to know nothing. But it was to -teach man to concentrate himself upon the moments of a life that is -itself but a moment. - -There are few of us who have not sometimes wakened before dawn, either -after one of those dreamless nights that make us almost enamoured of -death, or one of those nights of horror and misshapen joy, when through -the chambers of the brain sweep phantoms more terrible than reality -itself, and instinct with that vivid life that lurks in all grotesques, -and that lends to Gothic art its enduring vitality, this art being, one -might fancy, especially the art of those whose minds have been troubled -with the malady of reverie. Gradually white fingers creep through the -curtains, and they appear to tremble. In black fantastic shapes, dumb -shadows crawl into the corners of the room and crouch there. Outside, -there is the stirring of birds among the leaves, or the sound of men -going forth to their work, or the sigh and sob of the wind coming down -from the hills and wandering round the silent house, as though it -feared to wake the sleepers and yet must needs call forth sleep from -her purple cave. Veil after veil of thin dusky gauze is lifted, and by -degrees the forms and colours of things are restored to them, and we -watch the dawn remaking the world in its antique pattern. The wan -mirrors get back their mimic life. The flameless tapers stand where we -had left them, and beside them lies the half-cut book that we had been -studying, or the wired flower that we had worn at the ball, or the -letter that we had been afraid to read, or that we had read too often. -Nothing seems to us changed. Out of the unreal shadows of the night -comes back the real life that we had known. We have to resume it where -we had left off, and there steals over us a terrible sense of the -necessity for the continuance of energy in the same wearisome round of -stereotyped habits, or a wild longing, it may be, that our eyelids -might open some morning upon a world that had been refashioned anew in -the darkness for our pleasure, a world in which things would have fresh -shapes and colours, and be changed, or have other secrets, a world in -which the past would have little or no place, or survive, at any rate, -in no conscious form of obligation or regret, the remembrance even of -joy having its bitterness and the memories of pleasure their pain. - -It was the creation of such worlds as these that seemed to Dorian Gray -to be the true object, or amongst the true objects, of life; and in his -search for sensations that would be at once new and delightful, and -possess that element of strangeness that is so essential to romance, he -would often adopt certain modes of thought that he knew to be really -alien to his nature, abandon himself to their subtle influences, and -then, having, as it were, caught their colour and satisfied his -intellectual curiosity, leave them with that curious indifference that -is not incompatible with a real ardour of temperament, and that, -indeed, according to certain modern psychologists, is often a condition -of it. - -It was rumoured of him once that he was about to join the Roman -Catholic communion, and certainly the Roman ritual had always a great -attraction for him. The daily sacrifice, more awful really than all -the sacrifices of the antique world, stirred him as much by its superb -rejection of the evidence of the senses as by the primitive simplicity -of its elements and the eternal pathos of the human tragedy that it -sought to symbolize. He loved to kneel down on the cold marble -pavement and watch the priest, in his stiff flowered dalmatic, slowly -and with white hands moving aside the veil of the tabernacle, or -raising aloft the jewelled, lantern-shaped monstrance with that pallid -wafer that at times, one would fain think, is indeed the "_panis -caelestis_," the bread of angels, or, robed in the garments of the -Passion of Christ, breaking the Host into the chalice and smiting his -breast for his sins. The fuming censers that the grave boys, in their -lace and scarlet, tossed into the air like great gilt flowers had their -subtle fascination for him. As he passed out, he used to look with -wonder at the black confessionals and long to sit in the dim shadow of -one of them and listen to men and women whispering through the worn -grating the true story of their lives. - -But he never fell into the error of arresting his intellectual -development by any formal acceptance of creed or system, or of -mistaking, for a house in which to live, an inn that is but suitable -for the sojourn of a night, or for a few hours of a night in which -there are no stars and the moon is in travail. Mysticism, with its -marvellous power of making common things strange to us, and the subtle -antinomianism that always seems to accompany it, moved him for a -season; and for a season he inclined to the materialistic doctrines of -the _Darwinismus_ movement in Germany, and found a curious pleasure in -tracing the thoughts and passions of men to some pearly cell in the -brain, or some white nerve in the body, delighting in the conception of -the absolute dependence of the spirit on certain physical conditions, -morbid or healthy, normal or diseased. Yet, as has been said of him -before, no theory of life seemed to him to be of any importance -compared with life itself. He felt keenly conscious of how barren all -intellectual speculation is when separated from action and experiment. -He knew that the senses, no less than the soul, have their spiritual -mysteries to reveal. - -And so he would now study perfumes and the secrets of their -manufacture, distilling heavily scented oils and burning odorous gums -from the East. He saw that there was no mood of the mind that had not -its counterpart in the sensuous life, and set himself to discover their -true relations, wondering what there was in frankincense that made one -mystical, and in ambergris that stirred one's passions, and in violets -that woke the memory of dead romances, and in musk that troubled the -brain, and in champak that stained the imagination; and seeking often -to elaborate a real psychology of perfumes, and to estimate the several -influences of sweet-smelling roots and scented, pollen-laden flowers; -of aromatic balms and of dark and fragrant woods; of spikenard, that -sickens; of hovenia, that makes men mad; and of aloes, that are said to -be able to expel melancholy from the soul. - -At another time he devoted himself entirely to music, and in a long -latticed room, with a vermilion-and-gold ceiling and walls of -olive-green lacquer, he used to give curious concerts in which mad -gipsies tore wild music from little zithers, or grave, yellow-shawled -Tunisians plucked at the strained strings of monstrous lutes, while -grinning Negroes beat monotonously upon copper drums and, crouching -upon scarlet mats, slim turbaned Indians blew through long pipes of -reed or brass and charmed--or feigned to charm--great hooded snakes and -horrible horned adders. The harsh intervals and shrill discords of -barbaric music stirred him at times when Schubert's grace, and Chopin's -beautiful sorrows, and the mighty harmonies of Beethoven himself, fell -unheeded on his ear. He collected together from all parts of the world -the strangest instruments that could be found, either in the tombs of -dead nations or among the few savage tribes that have survived contact -with Western civilizations, and loved to touch and try them. He had -the mysterious _juruparis_ of the Rio Negro Indians, that women are not -allowed to look at and that even youths may not see till they have been -subjected to fasting and scourging, and the earthen jars of the -Peruvians that have the shrill cries of birds, and flutes of human -bones such as Alfonso de Ovalle heard in Chile, and the sonorous green -jaspers that are found near Cuzco and give forth a note of singular -sweetness. He had painted gourds filled with pebbles that rattled when -they were shaken; the long _clarin_ of the Mexicans, into which the -performer does not blow, but through which he inhales the air; the -harsh _ture_ of the Amazon tribes, that is sounded by the sentinels who -sit all day long in high trees, and can be heard, it is said, at a -distance of three leagues; the _teponaztli_, that has two vibrating -tongues of wood and is beaten with sticks that are smeared with an -elastic gum obtained from the milky juice of plants; the _yotl_-bells of -the Aztecs, that are hung in clusters like grapes; and a huge -cylindrical drum, covered with the skins of great serpents, like the -one that Bernal Diaz saw when he went with Cortes into the Mexican -temple, and of whose doleful sound he has left us so vivid a -description. The fantastic character of these instruments fascinated -him, and he felt a curious delight in the thought that art, like -Nature, has her monsters, things of bestial shape and with hideous -voices. Yet, after some time, he wearied of them, and would sit in his -box at the opera, either alone or with Lord Henry, listening in rapt -pleasure to "Tannhauser" and seeing in the prelude to that great work -of art a presentation of the tragedy of his own soul. - -On one occasion he took up the study of jewels, and appeared at a -costume ball as Anne de Joyeuse, Admiral of France, in a dress covered -with five hundred and sixty pearls. This taste enthralled him for -years, and, indeed, may be said never to have left him. He would often -spend a whole day settling and resettling in their cases the various -stones that he had collected, such as the olive-green chrysoberyl that -turns red by lamplight, the cymophane with its wirelike line of silver, -the pistachio-coloured peridot, rose-pink and wine-yellow topazes, -carbuncles of fiery scarlet with tremulous, four-rayed stars, flame-red -cinnamon-stones, orange and violet spinels, and amethysts with their -alternate layers of ruby and sapphire. He loved the red gold of the -sunstone, and the moonstone's pearly whiteness, and the broken rainbow -of the milky opal. He procured from Amsterdam three emeralds of -extraordinary size and richness of colour, and had a turquoise _de la -vieille roche_ that was the envy of all the connoisseurs. - -He discovered wonderful stories, also, about jewels. In Alphonso's -Clericalis Disciplina a serpent was mentioned with eyes of real -jacinth, and in the romantic history of Alexander, the Conqueror of -Emathia was said to have found in the vale of Jordan snakes "with -collars of real emeralds growing on their backs." There was a gem in -the brain of the dragon, Philostratus told us, and "by the exhibition -of golden letters and a scarlet robe" the monster could be thrown into -a magical sleep and slain. According to the great alchemist, Pierre de -Boniface, the diamond rendered a man invisible, and the agate of India -made him eloquent. The cornelian appeased anger, and the hyacinth -provoked sleep, and the amethyst drove away the fumes of wine. The -garnet cast out demons, and the hydropicus deprived the moon of her -colour. The selenite waxed and waned with the moon, and the meloceus, -that discovers thieves, could be affected only by the blood of kids. -Leonardus Camillus had seen a white stone taken from the brain of a -newly killed toad, that was a certain antidote against poison. The -bezoar, that was found in the heart of the Arabian deer, was a charm -that could cure the plague. In the nests of Arabian birds was the -aspilates, that, according to Democritus, kept the wearer from any -danger by fire. - -The King of Ceilan rode through his city with a large ruby in his hand, -as the ceremony of his coronation. The gates of the palace of John the -Priest were "made of sardius, with the horn of the horned snake -inwrought, so that no man might bring poison within." Over the gable -were "two golden apples, in which were two carbuncles," so that the -gold might shine by day and the carbuncles by night. In Lodge's -strange romance 'A Margarite of America', it was stated that in the -chamber of the queen one could behold "all the chaste ladies of the -world, inchased out of silver, looking through fair mirrours of -chrysolites, carbuncles, sapphires, and greene emeraults." Marco Polo -had seen the inhabitants of Zipangu place rose-coloured pearls in the -mouths of the dead. A sea-monster had been enamoured of the pearl that -the diver brought to King Perozes, and had slain the thief, and mourned -for seven moons over its loss. When the Huns lured the king into the -great pit, he flung it away--Procopius tells the story--nor was it ever -found again, though the Emperor Anastasius offered five hundred-weight -of gold pieces for it. The King of Malabar had shown to a certain -Venetian a rosary of three hundred and four pearls, one for every god -that he worshipped. - -When the Duke de Valentinois, son of Alexander VI, visited Louis XII of -France, his horse was loaded with gold leaves, according to Brantome, -and his cap had double rows of rubies that threw out a great light. -Charles of England had ridden in stirrups hung with four hundred and -twenty-one diamonds. Richard II had a coat, valued at thirty thousand -marks, which was covered with balas rubies. Hall described Henry VIII, -on his way to the Tower previous to his coronation, as wearing "a -jacket of raised gold, the placard embroidered with diamonds and other -rich stones, and a great bauderike about his neck of large balasses." -The favourites of James I wore ear-rings of emeralds set in gold -filigrane. Edward II gave to Piers Gaveston a suit of red-gold armour -studded with jacinths, a collar of gold roses set with -turquoise-stones, and a skull-cap _parseme_ with pearls. Henry II wore -jewelled gloves reaching to the elbow, and had a hawk-glove sewn with -twelve rubies and fifty-two great orients. The ducal hat of Charles -the Rash, the last Duke of Burgundy of his race, was hung with -pear-shaped pearls and studded with sapphires. - -How exquisite life had once been! How gorgeous in its pomp and -decoration! Even to read of the luxury of the dead was wonderful. - -Then he turned his attention to embroideries and to the tapestries that -performed the office of frescoes in the chill rooms of the northern -nations of Europe. As he investigated the subject--and he always had -an extraordinary faculty of becoming absolutely absorbed for the moment -in whatever he took up--he was almost saddened by the reflection of the -ruin that time brought on beautiful and wonderful things. He, at any -rate, had escaped that. Summer followed summer, and the yellow -jonquils bloomed and died many times, and nights of horror repeated the -story of their shame, but he was unchanged. No winter marred his face -or stained his flowerlike bloom. How different it was with material -things! Where had they passed to? Where was the great crocus-coloured -robe, on which the gods fought against the giants, that had been worked -by brown girls for the pleasure of Athena? Where the huge velarium -that Nero had stretched across the Colosseum at Rome, that Titan sail -of purple on which was represented the starry sky, and Apollo driving a -chariot drawn by white, gilt-reined steeds? He longed to see the -curious table-napkins wrought for the Priest of the Sun, on which were -displayed all the dainties and viands that could be wanted for a feast; -the mortuary cloth of King Chilperic, with its three hundred golden -bees; the fantastic robes that excited the indignation of the Bishop of -Pontus and were figured with "lions, panthers, bears, dogs, forests, -rocks, hunters--all, in fact, that a painter can copy from nature"; and -the coat that Charles of Orleans once wore, on the sleeves of which -were embroidered the verses of a song beginning "_Madame, je suis tout -joyeux_," the musical accompaniment of the words being wrought in gold -thread, and each note, of square shape in those days, formed with four -pearls. He read of the room that was prepared at the palace at Rheims -for the use of Queen Joan of Burgundy and was decorated with "thirteen -hundred and twenty-one parrots, made in broidery, and blazoned with the -king's arms, and five hundred and sixty-one butterflies, whose wings -were similarly ornamented with the arms of the queen, the whole worked -in gold." Catherine de Medicis had a mourning-bed made for her of -black velvet powdered with crescents and suns. Its curtains were of -damask, with leafy wreaths and garlands, figured upon a gold and silver -ground, and fringed along the edges with broideries of pearls, and it -stood in a room hung with rows of the queen's devices in cut black -velvet upon cloth of silver. Louis XIV had gold embroidered caryatides -fifteen feet high in his apartment. The state bed of Sobieski, King of -Poland, was made of Smyrna gold brocade embroidered in turquoises with -verses from the Koran. Its supports were of silver gilt, beautifully -chased, and profusely set with enamelled and jewelled medallions. It -had been taken from the Turkish camp before Vienna, and the standard of -Mohammed had stood beneath the tremulous gilt of its canopy. - -And so, for a whole year, he sought to accumulate the most exquisite -specimens that he could find of textile and embroidered work, getting -the dainty Delhi muslins, finely wrought with gold-thread palmates and -stitched over with iridescent beetles' wings; the Dacca gauzes, that -from their transparency are known in the East as "woven air," and -"running water," and "evening dew"; strange figured cloths from Java; -elaborate yellow Chinese hangings; books bound in tawny satins or fair -blue silks and wrought with _fleurs-de-lis_, birds and images; veils of -_lacis_ worked in Hungary point; Sicilian brocades and stiff Spanish -velvets; Georgian work, with its gilt coins, and Japanese _Foukousas_, -with their green-toned golds and their marvellously plumaged birds. - -He had a special passion, also, for ecclesiastical vestments, as indeed -he had for everything connected with the service of the Church. In the -long cedar chests that lined the west gallery of his house, he had -stored away many rare and beautiful specimens of what is really the -raiment of the Bride of Christ, who must wear purple and jewels and -fine linen that she may hide the pallid macerated body that is worn by -the suffering that she seeks for and wounded by self-inflicted pain. -He possessed a gorgeous cope of crimson silk and gold-thread damask, -figured with a repeating pattern of golden pomegranates set in -six-petalled formal blossoms, beyond which on either side was the -pine-apple device wrought in seed-pearls. The orphreys were divided -into panels representing scenes from the life of the Virgin, and the -coronation of the Virgin was figured in coloured silks upon the hood. -This was Italian work of the fifteenth century. Another cope was of -green velvet, embroidered with heart-shaped groups of acanthus-leaves, -from which spread long-stemmed white blossoms, the details of which -were picked out with silver thread and coloured crystals. The morse -bore a seraph's head in gold-thread raised work. The orphreys were -woven in a diaper of red and gold silk, and were starred with -medallions of many saints and martyrs, among whom was St. Sebastian. -He had chasubles, also, of amber-coloured silk, and blue silk and gold -brocade, and yellow silk damask and cloth of gold, figured with -representations of the Passion and Crucifixion of Christ, and -embroidered with lions and peacocks and other emblems; dalmatics of -white satin and pink silk damask, decorated with tulips and dolphins -and _fleurs-de-lis_; altar frontals of crimson velvet and blue linen; and -many corporals, chalice-veils, and sudaria. In the mystic offices to -which such things were put, there was something that quickened his -imagination. - -For these treasures, and everything that he collected in his lovely -house, were to be to him means of forgetfulness, modes by which he -could escape, for a season, from the fear that seemed to him at times -to be almost too great to be borne. Upon the walls of the lonely -locked room where he had spent so much of his boyhood, he had hung with -his own hands the terrible portrait whose changing features showed him -the real degradation of his life, and in front of it had draped the -purple-and-gold pall as a curtain. For weeks he would not go there, -would forget the hideous painted thing, and get back his light heart, -his wonderful joyousness, his passionate absorption in mere existence. -Then, suddenly, some night he would creep out of the house, go down to -dreadful places near Blue Gate Fields, and stay there, day after day, -until he was driven away. On his return he would sit in front of the -picture, sometimes loathing it and himself, but filled, at other -times, with that pride of individualism that is half the -fascination of sin, and smiling with secret pleasure at the misshapen -shadow that had to bear the burden that should have been his own. - -After a few years he could not endure to be long out of England, and -gave up the villa that he had shared at Trouville with Lord Henry, as -well as the little white walled-in house at Algiers where they had more -than once spent the winter. He hated to be separated from the picture -that was such a part of his life, and was also afraid that during his -absence some one might gain access to the room, in spite of the -elaborate bars that he had caused to be placed upon the door. - -He was quite conscious that this would tell them nothing. It was true -that the portrait still preserved, under all the foulness and ugliness -of the face, its marked likeness to himself; but what could they learn -from that? He would laugh at any one who tried to taunt him. He had -not painted it. What was it to him how vile and full of shame it -looked? Even if he told them, would they believe it? - -Yet he was afraid. Sometimes when he was down at his great house in -Nottinghamshire, entertaining the fashionable young men of his own rank -who were his chief companions, and astounding the county by the wanton -luxury and gorgeous splendour of his mode of life, he would suddenly -leave his guests and rush back to town to see that the door had not -been tampered with and that the picture was still there. What if it -should be stolen? The mere thought made him cold with horror. Surely -the world would know his secret then. Perhaps the world already -suspected it. - -For, while he fascinated many, there were not a few who distrusted him. -He was very nearly blackballed at a West End club of which his birth -and social position fully entitled him to become a member, and it was -said that on one occasion, when he was brought by a friend into the -smoking-room of the Churchill, the Duke of Berwick and another -gentleman got up in a marked manner and went out. Curious stories -became current about him after he had passed his twenty-fifth year. It -was rumoured that he had been seen brawling with foreign sailors in a -low den in the distant parts of Whitechapel, and that he consorted with -thieves and coiners and knew the mysteries of their trade. His -extraordinary absences became notorious, and, when he used to reappear -again in society, men would whisper to each other in corners, or pass -him with a sneer, or look at him with cold searching eyes, as though -they were determined to discover his secret. - -Of such insolences and attempted slights he, of course, took no notice, -and in the opinion of most people his frank debonair manner, his -charming boyish smile, and the infinite grace of that wonderful youth -that seemed never to leave him, were in themselves a sufficient answer -to the calumnies, for so they termed them, that were circulated about -him. It was remarked, however, that some of those who had been most -intimate with him appeared, after a time, to shun him. Women who had -wildly adored him, and for his sake had braved all social censure and -set convention at defiance, were seen to grow pallid with shame or -horror if Dorian Gray entered the room. - -Yet these whispered scandals only increased in the eyes of many his -strange and dangerous charm. His great wealth was a certain element of -security. Society--civilized society, at least--is never very ready to -believe anything to the detriment of those who are both rich and -fascinating. It feels instinctively that manners are of more -importance than morals, and, in its opinion, the highest respectability -is of much less value than the possession of a good _chef_. And, after -all, it is a very poor consolation to be told that the man who has -given one a bad dinner, or poor wine, is irreproachable in his private -life. Even the cardinal virtues cannot atone for half-cold _entrees_, as -Lord Henry remarked once, in a discussion on the subject, and there is -possibly a good deal to be said for his view. For the canons of good -society are, or should be, the same as the canons of art. Form is -absolutely essential to it. It should have the dignity of a ceremony, -as well as its unreality, and should combine the insincere character of -a romantic play with the wit and beauty that make such plays delightful -to us. Is insincerity such a terrible thing? I think not. It is -merely a method by which we can multiply our personalities. - -Such, at any rate, was Dorian Gray's opinion. He used to wonder at the -shallow psychology of those who conceive the ego in man as a thing -simple, permanent, reliable, and of one essence. To him, man was a -being with myriad lives and myriad sensations, a complex multiform -creature that bore within itself strange legacies of thought and -passion, and whose very flesh was tainted with the monstrous maladies -of the dead. He loved to stroll through the gaunt cold picture-gallery -of his country house and look at the various portraits of those whose -blood flowed in his veins. Here was Philip Herbert, described by -Francis Osborne, in his Memoires on the Reigns of Queen Elizabeth and -King James, as one who was "caressed by the Court for his handsome -face, which kept him not long company." Was it young Herbert's life -that he sometimes led? Had some strange poisonous germ crept from body -to body till it had reached his own? Was it some dim sense of that -ruined grace that had made him so suddenly, and almost without cause, -give utterance, in Basil Hallward's studio, to the mad prayer that had -so changed his life? Here, in gold-embroidered red doublet, jewelled -surcoat, and gilt-edged ruff and wristbands, stood Sir Anthony Sherard, -with his silver-and-black armour piled at his feet. What had this -man's legacy been? Had the lover of Giovanna of Naples bequeathed him -some inheritance of sin and shame? Were his own actions merely the -dreams that the dead man had not dared to realize? Here, from the -fading canvas, smiled Lady Elizabeth Devereux, in her gauze hood, pearl -stomacher, and pink slashed sleeves. A flower was in her right hand, -and her left clasped an enamelled collar of white and damask roses. On -a table by her side lay a mandolin and an apple. There were large -green rosettes upon her little pointed shoes. He knew her life, and -the strange stories that were told about her lovers. Had he something -of her temperament in him? These oval, heavy-lidded eyes seemed to -look curiously at him. What of George Willoughby, with his powdered -hair and fantastic patches? How evil he looked! The face was -saturnine and swarthy, and the sensual lips seemed to be twisted with -disdain. Delicate lace ruffles fell over the lean yellow hands that -were so overladen with rings. He had been a macaroni of the eighteenth -century, and the friend, in his youth, of Lord Ferrars. What of the -second Lord Beckenham, the companion of the Prince Regent in his -wildest days, and one of the witnesses at the secret marriage with Mrs. -Fitzherbert? How proud and handsome he was, with his chestnut curls -and insolent pose! What passions had he bequeathed? The world had -looked upon him as infamous. He had led the orgies at Carlton House. -The star of the Garter glittered upon his breast. Beside him hung the -portrait of his wife, a pallid, thin-lipped woman in black. Her blood, -also, stirred within him. How curious it all seemed! And his mother -with her Lady Hamilton face and her moist, wine-dashed lips--he knew -what he had got from her. He had got from her his beauty, and his -passion for the beauty of others. She laughed at him in her loose -Bacchante dress. There were vine leaves in her hair. The purple -spilled from the cup she was holding. The carnations of the painting -had withered, but the eyes were still wonderful in their depth and -brilliancy of colour. They seemed to follow him wherever he went. - -Yet one had ancestors in literature as well as in one's own race, -nearer perhaps in type and temperament, many of them, and certainly -with an influence of which one was more absolutely conscious. There -were times when it appeared to Dorian Gray that the whole of history -was merely the record of his own life, not as he had lived it in act -and circumstance, but as his imagination had created it for him, as it -had been in his brain and in his passions. He felt that he had known -them all, those strange terrible figures that had passed across the -stage of the world and made sin so marvellous and evil so full of -subtlety. It seemed to him that in some mysterious way their lives had -been his own. - -The hero of the wonderful novel that had so influenced his life had -himself known this curious fancy. In the seventh chapter he tells how, -crowned with laurel, lest lightning might strike him, he had sat, as -Tiberius, in a garden at Capri, reading the shameful books of -Elephantis, while dwarfs and peacocks strutted round him and the -flute-player mocked the swinger of the censer; and, as Caligula, had -caroused with the green-shirted jockeys in their stables and supped in -an ivory manger with a jewel-frontleted horse; and, as Domitian, had -wandered through a corridor lined with marble mirrors, looking round -with haggard eyes for the reflection of the dagger that was to end his -days, and sick with that ennui, that terrible _taedium vitae_, that comes -on those to whom life denies nothing; and had peered through a clear -emerald at the red shambles of the circus and then, in a litter of -pearl and purple drawn by silver-shod mules, been carried through the -Street of Pomegranates to a House of Gold and heard men cry on Nero -Caesar as he passed by; and, as Elagabalus, had painted his face with -colours, and plied the distaff among the women, and brought the Moon -from Carthage and given her in mystic marriage to the Sun. - -Over and over again Dorian used to read this fantastic chapter, and the -two chapters immediately following, in which, as in some curious -tapestries or cunningly wrought enamels, were pictured the awful and -beautiful forms of those whom vice and blood and weariness had made -monstrous or mad: Filippo, Duke of Milan, who slew his wife and -painted her lips with a scarlet poison that her lover might suck death -from the dead thing he fondled; Pietro Barbi, the Venetian, known as -Paul the Second, who sought in his vanity to assume the title of -Formosus, and whose tiara, valued at two hundred thousand florins, was -bought at the price of a terrible sin; Gian Maria Visconti, who used -hounds to chase living men and whose murdered body was covered with -roses by a harlot who had loved him; the Borgia on his white horse, -with Fratricide riding beside him and his mantle stained with the blood -of Perotto; Pietro Riario, the young Cardinal Archbishop of Florence, -child and minion of Sixtus IV, whose beauty was equalled only by his -debauchery, and who received Leonora of Aragon in a pavilion of white -and crimson silk, filled with nymphs and centaurs, and gilded a boy -that he might serve at the feast as Ganymede or Hylas; Ezzelin, whose -melancholy could be cured only by the spectacle of death, and who had a -passion for red blood, as other men have for red wine--the son of the -Fiend, as was reported, and one who had cheated his father at dice when -gambling with him for his own soul; Giambattista Cibo, who in mockery -took the name of Innocent and into whose torpid veins the blood of -three lads was infused by a Jewish doctor; Sigismondo Malatesta, the -lover of Isotta and the lord of Rimini, whose effigy was burned at Rome -as the enemy of God and man, who strangled Polyssena with a napkin, and -gave poison to Ginevra d'Este in a cup of emerald, and in honour of a -shameful passion built a pagan church for Christian worship; Charles -VI, who had so wildly adored his brother's wife that a leper had warned -him of the insanity that was coming on him, and who, when his brain had -sickened and grown strange, could only be soothed by Saracen cards -painted with the images of love and death and madness; and, in his -trimmed jerkin and jewelled cap and acanthuslike curls, Grifonetto -Baglioni, who slew Astorre with his bride, and Simonetto with his page, -and whose comeliness was such that, as he lay dying in the yellow -piazza of Perugia, those who had hated him could not choose but weep, -and Atalanta, who had cursed him, blessed him. - -There was a horrible fascination in them all. He saw them at night, -and they troubled his imagination in the day. The Renaissance knew of -strange manners of poisoning--poisoning by a helmet and a lighted -torch, by an embroidered glove and a jewelled fan, by a gilded pomander -and by an amber chain. Dorian Gray had been poisoned by a book. There -were moments when he looked on evil simply as a mode through which he -could realize his conception of the beautiful. - - - -CHAPTER 12 - -It was on the ninth of November, the eve of his own thirty-eighth -birthday, as he often remembered afterwards. - -He was walking home about eleven o'clock from Lord Henry's, where he -had been dining, and was wrapped in heavy furs, as the night was cold -and foggy. At the corner of Grosvenor Square and South Audley Street, -a man passed him in the mist, walking very fast and with the collar of -his grey ulster turned up. He had a bag in his hand. Dorian -recognized him. It was Basil Hallward. A strange sense of fear, for -which he could not account, came over him. He made no sign of -recognition and went on quickly in the direction of his own house. - -But Hallward had seen him. Dorian heard him first stopping on the -pavement and then hurrying after him. In a few moments, his hand was -on his arm. - -"Dorian! What an extraordinary piece of luck! I have been waiting for -you in your library ever since nine o'clock. Finally I took pity on -your tired servant and told him to go to bed, as he let me out. I am -off to Paris by the midnight train, and I particularly wanted to see -you before I left. I thought it was you, or rather your fur coat, as -you passed me. But I wasn't quite sure. Didn't you recognize me?" - -"In this fog, my dear Basil? Why, I can't even recognize Grosvenor -Square. I believe my house is somewhere about here, but I don't feel -at all certain about it. I am sorry you are going away, as I have not -seen you for ages. But I suppose you will be back soon?" - -"No: I am going to be out of England for six months. I intend to take -a studio in Paris and shut myself up till I have finished a great -picture I have in my head. However, it wasn't about myself I wanted to -talk. Here we are at your door. Let me come in for a moment. I have -something to say to you." - -"I shall be charmed. But won't you miss your train?" said Dorian Gray -languidly as he passed up the steps and opened the door with his -latch-key. - -The lamplight struggled out through the fog, and Hallward looked at his -watch. "I have heaps of time," he answered. "The train doesn't go -till twelve-fifteen, and it is only just eleven. In fact, I was on my -way to the club to look for you, when I met you. You see, I shan't -have any delay about luggage, as I have sent on my heavy things. All I -have with me is in this bag, and I can easily get to Victoria in twenty -minutes." - -Dorian looked at him and smiled. "What a way for a fashionable painter -to travel! A Gladstone bag and an ulster! Come in, or the fog will -get into the house. And mind you don't talk about anything serious. -Nothing is serious nowadays. At least nothing should be." - -Hallward shook his head, as he entered, and followed Dorian into the -library. There was a bright wood fire blazing in the large open -hearth. The lamps were lit, and an open Dutch silver spirit-case -stood, with some siphons of soda-water and large cut-glass tumblers, on -a little marqueterie table. - -"You see your servant made me quite at home, Dorian. He gave me -everything I wanted, including your best gold-tipped cigarettes. He is -a most hospitable creature. I like him much better than the Frenchman -you used to have. What has become of the Frenchman, by the bye?" - -Dorian shrugged his shoulders. "I believe he married Lady Radley's -maid, and has established her in Paris as an English dressmaker. -Anglomania is very fashionable over there now, I hear. It seems silly -of the French, doesn't it? But--do you know?--he was not at all a bad -servant. I never liked him, but I had nothing to complain about. One -often imagines things that are quite absurd. He was really very -devoted to me and seemed quite sorry when he went away. Have another -brandy-and-soda? Or would you like hock-and-seltzer? I always take -hock-and-seltzer myself. There is sure to be some in the next room." - -"Thanks, I won't have anything more," said the painter, taking his cap -and coat off and throwing them on the bag that he had placed in the -corner. "And now, my dear fellow, I want to speak to you seriously. -Don't frown like that. You make it so much more difficult for me." - -"What is it all about?" cried Dorian in his petulant way, flinging -himself down on the sofa. "I hope it is not about myself. I am tired -of myself to-night. I should like to be somebody else." - -"It is about yourself," answered Hallward in his grave deep voice, "and -I must say it to you. I shall only keep you half an hour." - -Dorian sighed and lit a cigarette. "Half an hour!" he murmured. - -"It is not much to ask of you, Dorian, and it is entirely for your own -sake that I am speaking. I think it right that you should know that -the most dreadful things are being said against you in London." - -"I don't wish to know anything about them. I love scandals about other -people, but scandals about myself don't interest me. They have not got -the charm of novelty." - -"They must interest you, Dorian. Every gentleman is interested in his -good name. You don't want people to talk of you as something vile and -degraded. Of course, you have your position, and your wealth, and all -that kind of thing. But position and wealth are not everything. Mind -you, I don't believe these rumours at all. At least, I can't believe -them when I see you. Sin is a thing that writes itself across a man's -face. It cannot be concealed. People talk sometimes of secret vices. -There are no such things. If a wretched man has a vice, it shows -itself in the lines of his mouth, the droop of his eyelids, the -moulding of his hands even. Somebody--I won't mention his name, but -you know him--came to me last year to have his portrait done. I had -never seen him before, and had never heard anything about him at the -time, though I have heard a good deal since. He offered an extravagant -price. I refused him. There was something in the shape of his fingers -that I hated. I know now that I was quite right in what I fancied -about him. His life is dreadful. But you, Dorian, with your pure, -bright, innocent face, and your marvellous untroubled youth--I can't -believe anything against you. And yet I see you very seldom, and you -never come down to the studio now, and when I am away from you, and I -hear all these hideous things that people are whispering about you, I -don't know what to say. Why is it, Dorian, that a man like the Duke of -Berwick leaves the room of a club when you enter it? Why is it that so -many gentlemen in London will neither go to your house or invite you to -theirs? You used to be a friend of Lord Staveley. I met him at dinner -last week. Your name happened to come up in conversation, in -connection with the miniatures you have lent to the exhibition at the -Dudley. Staveley curled his lip and said that you might have the most -artistic tastes, but that you were a man whom no pure-minded girl -should be allowed to know, and whom no chaste woman should sit in the -same room with. I reminded him that I was a friend of yours, and asked -him what he meant. He told me. He told me right out before everybody. -It was horrible! Why is your friendship so fatal to young men? There -was that wretched boy in the Guards who committed suicide. You were -his great friend. There was Sir Henry Ashton, who had to leave England -with a tarnished name. You and he were inseparable. What about Adrian -Singleton and his dreadful end? What about Lord Kent's only son and -his career? I met his father yesterday in St. James's Street. He -seemed broken with shame and sorrow. What about the young Duke of -Perth? What sort of life has he got now? What gentleman would -associate with him?" - -"Stop, Basil. You are talking about things of which you know nothing," -said Dorian Gray, biting his lip, and with a note of infinite contempt -in his voice. "You ask me why Berwick leaves a room when I enter it. -It is because I know everything about his life, not because he knows -anything about mine. With such blood as he has in his veins, how could -his record be clean? You ask me about Henry Ashton and young Perth. -Did I teach the one his vices, and the other his debauchery? If Kent's -silly son takes his wife from the streets, what is that to me? If -Adrian Singleton writes his friend's name across a bill, am I his -keeper? I know how people chatter in England. The middle classes air -their moral prejudices over their gross dinner-tables, and whisper -about what they call the profligacies of their betters in order to try -and pretend that they are in smart society and on intimate terms with -the people they slander. In this country, it is enough for a man to -have distinction and brains for every common tongue to wag against him. -And what sort of lives do these people, who pose as being moral, lead -themselves? My dear fellow, you forget that we are in the native land -of the hypocrite." - -"Dorian," cried Hallward, "that is not the question. England is bad -enough I know, and English society is all wrong. That is the reason -why I want you to be fine. You have not been fine. One has a right to -judge of a man by the effect he has over his friends. Yours seem to -lose all sense of honour, of goodness, of purity. You have filled them -with a madness for pleasure. They have gone down into the depths. You -led them there. Yes: you led them there, and yet you can smile, as -you are smiling now. And there is worse behind. I know you and Harry -are inseparable. Surely for that reason, if for none other, you should -not have made his sister's name a by-word." - -"Take care, Basil. You go too far." - -"I must speak, and you must listen. You shall listen. When you met -Lady Gwendolen, not a breath of scandal had ever touched her. Is there -a single decent woman in London now who would drive with her in the -park? Why, even her children are not allowed to live with her. Then -there are other stories--stories that you have been seen creeping at -dawn out of dreadful houses and slinking in disguise into the foulest -dens in London. Are they true? Can they be true? When I first heard -them, I laughed. I hear them now, and they make me shudder. What -about your country-house and the life that is led there? Dorian, you -don't know what is said about you. I won't tell you that I don't want -to preach to you. I remember Harry saying once that every man who -turned himself into an amateur curate for the moment always began by -saying that, and then proceeded to break his word. I do want to preach -to you. I want you to lead such a life as will make the world respect -you. I want you to have a clean name and a fair record. I want you to -get rid of the dreadful people you associate with. Don't shrug your -shoulders like that. Don't be so indifferent. You have a wonderful -influence. Let it be for good, not for evil. They say that you -corrupt every one with whom you become intimate, and that it is quite -sufficient for you to enter a house for shame of some kind to follow -after. I don't know whether it is so or not. How should I know? But -it is said of you. I am told things that it seems impossible to doubt. -Lord Gloucester was one of my greatest friends at Oxford. He showed me -a letter that his wife had written to him when she was dying alone in -her villa at Mentone. Your name was implicated in the most terrible -confession I ever read. I told him that it was absurd--that I knew you -thoroughly and that you were incapable of anything of the kind. Know -you? I wonder do I know you? Before I could answer that, I should -have to see your soul." - -"To see my soul!" muttered Dorian Gray, starting up from the sofa and -turning almost white from fear. - -"Yes," answered Hallward gravely, and with deep-toned sorrow in his -voice, "to see your soul. But only God can do that." - -A bitter laugh of mockery broke from the lips of the younger man. "You -shall see it yourself, to-night!" he cried, seizing a lamp from the -table. "Come: it is your own handiwork. Why shouldn't you look at -it? You can tell the world all about it afterwards, if you choose. -Nobody would believe you. If they did believe you, they would like me -all the better for it. I know the age better than you do, though you -will prate about it so tediously. Come, I tell you. You have -chattered enough about corruption. Now you shall look on it face to -face." - -There was the madness of pride in every word he uttered. He stamped -his foot upon the ground in his boyish insolent manner. He felt a -terrible joy at the thought that some one else was to share his secret, -and that the man who had painted the portrait that was the origin of -all his shame was to be burdened for the rest of his life with the -hideous memory of what he had done. - -"Yes," he continued, coming closer to him and looking steadfastly into -his stern eyes, "I shall show you my soul. You shall see the thing -that you fancy only God can see." - -Hallward started back. "This is blasphemy, Dorian!" he cried. "You -must not say things like that. They are horrible, and they don't mean -anything." - -"You think so?" He laughed again. - -"I know so. As for what I said to you to-night, I said it for your -good. You know I have been always a stanch friend to you." - -"Don't touch me. Finish what you have to say." - -A twisted flash of pain shot across the painter's face. He paused for -a moment, and a wild feeling of pity came over him. After all, what -right had he to pry into the life of Dorian Gray? If he had done a -tithe of what was rumoured about him, how much he must have suffered! -Then he straightened himself up, and walked over to the fire-place, and -stood there, looking at the burning logs with their frostlike ashes and -their throbbing cores of flame. - -"I am waiting, Basil," said the young man in a hard clear voice. - -He turned round. "What I have to say is this," he cried. "You must -give me some answer to these horrible charges that are made against -you. If you tell me that they are absolutely untrue from beginning to -end, I shall believe you. Deny them, Dorian, deny them! Can't you see -what I am going through? My God! don't tell me that you are bad, and -corrupt, and shameful." - -Dorian Gray smiled. There was a curl of contempt in his lips. "Come -upstairs, Basil," he said quietly. "I keep a diary of my life from day -to day, and it never leaves the room in which it is written. I shall -show it to you if you come with me." - -"I shall come with you, Dorian, if you wish it. I see I have missed my -train. That makes no matter. I can go to-morrow. But don't ask me to -read anything to-night. All I want is a plain answer to my question." - -"That shall be given to you upstairs. I could not give it here. You -will not have to read long." - - - -CHAPTER 13 - -He passed out of the room and began the ascent, Basil Hallward -following close behind. They walked softly, as men do instinctively at -night. The lamp cast fantastic shadows on the wall and staircase. A -rising wind made some of the windows rattle. - -When they reached the top landing, Dorian set the lamp down on the -floor, and taking out the key, turned it in the lock. "You insist on -knowing, Basil?" he asked in a low voice. - -"Yes." - -"I am delighted," he answered, smiling. Then he added, somewhat -harshly, "You are the one man in the world who is entitled to know -everything about me. You have had more to do with my life than you -think"; and, taking up the lamp, he opened the door and went in. A -cold current of air passed them, and the light shot up for a moment in -a flame of murky orange. He shuddered. "Shut the door behind you," he -whispered, as he placed the lamp on the table. - -Hallward glanced round him with a puzzled expression. The room looked -as if it had not been lived in for years. A faded Flemish tapestry, a -curtained picture, an old Italian _cassone_, and an almost empty -book-case--that was all that it seemed to contain, besides a chair and -a table. As Dorian Gray was lighting a half-burned candle that was -standing on the mantelshelf, he saw that the whole place was covered -with dust and that the carpet was in holes. A mouse ran scuffling -behind the wainscoting. There was a damp odour of mildew. - -"So you think that it is only God who sees the soul, Basil? Draw that -curtain back, and you will see mine." - -The voice that spoke was cold and cruel. "You are mad, Dorian, or -playing a part," muttered Hallward, frowning. - -"You won't? Then I must do it myself," said the young man, and he tore -the curtain from its rod and flung it on the ground. - -An exclamation of horror broke from the painter's lips as he saw in the -dim light the hideous face on the canvas grinning at him. There was -something in its expression that filled him with disgust and loathing. -Good heavens! it was Dorian Gray's own face that he was looking at! -The horror, whatever it was, had not yet entirely spoiled that -marvellous beauty. There was still some gold in the thinning hair and -some scarlet on the sensual mouth. The sodden eyes had kept something -of the loveliness of their blue, the noble curves had not yet -completely passed away from chiselled nostrils and from plastic throat. -Yes, it was Dorian himself. But who had done it? He seemed to -recognize his own brushwork, and the frame was his own design. The -idea was monstrous, yet he felt afraid. He seized the lighted candle, -and held it to the picture. In the left-hand corner was his own name, -traced in long letters of bright vermilion. - -It was some foul parody, some infamous ignoble satire. He had never -done that. Still, it was his own picture. He knew it, and he felt as -if his blood had changed in a moment from fire to sluggish ice. His -own picture! What did it mean? Why had it altered? He turned and -looked at Dorian Gray with the eyes of a sick man. His mouth twitched, -and his parched tongue seemed unable to articulate. He passed his hand -across his forehead. It was dank with clammy sweat. - -The young man was leaning against the mantelshelf, watching him with -that strange expression that one sees on the faces of those who are -absorbed in a play when some great artist is acting. There was neither -real sorrow in it nor real joy. There was simply the passion of the -spectator, with perhaps a flicker of triumph in his eyes. He had taken -the flower out of his coat, and was smelling it, or pretending to do so. - -"What does this mean?" cried Hallward, at last. His own voice sounded -shrill and curious in his ears. - -"Years ago, when I was a boy," said Dorian Gray, crushing the flower in -his hand, "you met me, flattered me, and taught me to be vain of my -good looks. One day you introduced me to a friend of yours, who -explained to me the wonder of youth, and you finished a portrait of me -that revealed to me the wonder of beauty. In a mad moment that, even -now, I don't know whether I regret or not, I made a wish, perhaps you -would call it a prayer...." - -"I remember it! Oh, how well I remember it! No! the thing is -impossible. The room is damp. Mildew has got into the canvas. The -paints I used had some wretched mineral poison in them. I tell you the -thing is impossible." - -"Ah, what is impossible?" murmured the young man, going over to the -window and leaning his forehead against the cold, mist-stained glass. - -"You told me you had destroyed it." - -"I was wrong. It has destroyed me." - -"I don't believe it is my picture." - -"Can't you see your ideal in it?" said Dorian bitterly. - -"My ideal, as you call it..." - -"As you called it." - -"There was nothing evil in it, nothing shameful. You were to me such -an ideal as I shall never meet again. This is the face of a satyr." - -"It is the face of my soul." - -"Christ! what a thing I must have worshipped! It has the eyes of a -devil." - -"Each of us has heaven and hell in him, Basil," cried Dorian with a -wild gesture of despair. - -Hallward turned again to the portrait and gazed at it. "My God! If it -is true," he exclaimed, "and this is what you have done with your life, -why, you must be worse even than those who talk against you fancy you -to be!" He held the light up again to the canvas and examined it. The -surface seemed to be quite undisturbed and as he had left it. It was -from within, apparently, that the foulness and horror had come. -Through some strange quickening of inner life the leprosies of sin were -slowly eating the thing away. The rotting of a corpse in a watery -grave was not so fearful. - -His hand shook, and the candle fell from its socket on the floor and -lay there sputtering. He placed his foot on it and put it out. Then -he flung himself into the rickety chair that was standing by the table -and buried his face in his hands. - -"Good God, Dorian, what a lesson! What an awful lesson!" There was no -answer, but he could hear the young man sobbing at the window. "Pray, -Dorian, pray," he murmured. "What is it that one was taught to say in -one's boyhood? 'Lead us not into temptation. Forgive us our sins. -Wash away our iniquities.' Let us say that together. The prayer of -your pride has been answered. The prayer of your repentance will be -answered also. I worshipped you too much. I am punished for it. You -worshipped yourself too much. We are both punished." - -Dorian Gray turned slowly around and looked at him with tear-dimmed -eyes. "It is too late, Basil," he faltered. - -"It is never too late, Dorian. Let us kneel down and try if we cannot -remember a prayer. Isn't there a verse somewhere, 'Though your sins be -as scarlet, yet I will make them as white as snow'?" - -"Those words mean nothing to me now." - -"Hush! Don't say that. You have done enough evil in your life. My -God! Don't you see that accursed thing leering at us?" - -Dorian Gray glanced at the picture, and suddenly an uncontrollable -feeling of hatred for Basil Hallward came over him, as though it had -been suggested to him by the image on the canvas, whispered into his -ear by those grinning lips. The mad passions of a hunted animal -stirred within him, and he loathed the man who was seated at the table, -more than in his whole life he had ever loathed anything. He glanced -wildly around. Something glimmered on the top of the painted chest -that faced him. His eye fell on it. He knew what it was. It was a -knife that he had brought up, some days before, to cut a piece of cord, -and had forgotten to take away with him. He moved slowly towards it, -passing Hallward as he did so. As soon as he got behind him, he seized -it and turned round. Hallward stirred in his chair as if he was going -to rise. He rushed at him and dug the knife into the great vein that -is behind the ear, crushing the man's head down on the table and -stabbing again and again. - -There was a stifled groan and the horrible sound of some one choking -with blood. Three times the outstretched arms shot up convulsively, -waving grotesque, stiff-fingered hands in the air. He stabbed him -twice more, but the man did not move. Something began to trickle on -the floor. He waited for a moment, still pressing the head down. Then -he threw the knife on the table, and listened. - -He could hear nothing, but the drip, drip on the threadbare carpet. He -opened the door and went out on the landing. The house was absolutely -quiet. No one was about. For a few seconds he stood bending over the -balustrade and peering down into the black seething well of darkness. -Then he took out the key and returned to the room, locking himself in -as he did so. - -The thing was still seated in the chair, straining over the table with -bowed head, and humped back, and long fantastic arms. Had it not been -for the red jagged tear in the neck and the clotted black pool that was -slowly widening on the table, one would have said that the man was -simply asleep. - -How quickly it had all been done! He felt strangely calm, and walking -over to the window, opened it and stepped out on the balcony. The wind -had blown the fog away, and the sky was like a monstrous peacock's -tail, starred with myriads of golden eyes. He looked down and saw the -policeman going his rounds and flashing the long beam of his lantern on -the doors of the silent houses. The crimson spot of a prowling hansom -gleamed at the corner and then vanished. A woman in a fluttering shawl -was creeping slowly by the railings, staggering as she went. Now and -then she stopped and peered back. Once, she began to sing in a hoarse -voice. The policeman strolled over and said something to her. She -stumbled away, laughing. A bitter blast swept across the square. The -gas-lamps flickered and became blue, and the leafless trees shook their -black iron branches to and fro. He shivered and went back, closing the -window behind him. - -Having reached the door, he turned the key and opened it. He did not -even glance at the murdered man. He felt that the secret of the whole -thing was not to realize the situation. The friend who had painted the -fatal portrait to which all his misery had been due had gone out of his -life. That was enough. - -Then he remembered the lamp. It was a rather curious one of Moorish -workmanship, made of dull silver inlaid with arabesques of burnished -steel, and studded with coarse turquoises. Perhaps it might be missed -by his servant, and questions would be asked. He hesitated for a -moment, then he turned back and took it from the table. He could not -help seeing the dead thing. How still it was! How horribly white the -long hands looked! It was like a dreadful wax image. - -Having locked the door behind him, he crept quietly downstairs. The -woodwork creaked and seemed to cry out as if in pain. He stopped -several times and waited. No: everything was still. It was merely -the sound of his own footsteps. - -When he reached the library, he saw the bag and coat in the corner. -They must be hidden away somewhere. He unlocked a secret press that -was in the wainscoting, a press in which he kept his own curious -disguises, and put them into it. He could easily burn them afterwards. -Then he pulled out his watch. It was twenty minutes to two. - -He sat down and began to think. Every year--every month, almost--men -were strangled in England for what he had done. There had been a -madness of murder in the air. Some red star had come too close to the -earth.... And yet, what evidence was there against him? Basil Hallward -had left the house at eleven. No one had seen him come in again. Most -of the servants were at Selby Royal. His valet had gone to bed.... -Paris! Yes. It was to Paris that Basil had gone, and by the midnight -train, as he had intended. With his curious reserved habits, it would -be months before any suspicions would be roused. Months! Everything -could be destroyed long before then. - -A sudden thought struck him. He put on his fur coat and hat and went -out into the hall. There he paused, hearing the slow heavy tread of -the policeman on the pavement outside and seeing the flash of the -bull's-eye reflected in the window. He waited and held his breath. - -After a few moments he drew back the latch and slipped out, shutting -the door very gently behind him. Then he began ringing the bell. In -about five minutes his valet appeared, half-dressed and looking very -drowsy. - -"I am sorry to have had to wake you up, Francis," he said, stepping in; -"but I had forgotten my latch-key. What time is it?" - -"Ten minutes past two, sir," answered the man, looking at the clock and -blinking. - -"Ten minutes past two? How horribly late! You must wake me at nine -to-morrow. I have some work to do." - -"All right, sir." - -"Did any one call this evening?" - -"Mr. Hallward, sir. He stayed here till eleven, and then he went away -to catch his train." - -"Oh! I am sorry I didn't see him. Did he leave any message?" - -"No, sir, except that he would write to you from Paris, if he did not -find you at the club." - -"That will do, Francis. Don't forget to call me at nine to-morrow." - -"No, sir." - -The man shambled down the passage in his slippers. - -Dorian Gray threw his hat and coat upon the table and passed into the -library. For a quarter of an hour he walked up and down the room, -biting his lip and thinking. Then he took down the Blue Book from one -of the shelves and began to turn over the leaves. "Alan Campbell, 152, -Hertford Street, Mayfair." Yes; that was the man he wanted. - - - -CHAPTER 14 - -At nine o'clock the next morning his servant came in with a cup of -chocolate on a tray and opened the shutters. Dorian was sleeping quite -peacefully, lying on his right side, with one hand underneath his -cheek. He looked like a boy who had been tired out with play, or study. - -The man had to touch him twice on the shoulder before he woke, and as -he opened his eyes a faint smile passed across his lips, as though he -had been lost in some delightful dream. Yet he had not dreamed at all. -His night had been untroubled by any images of pleasure or of pain. -But youth smiles without any reason. It is one of its chiefest charms. - -He turned round, and leaning upon his elbow, began to sip his -chocolate. The mellow November sun came streaming into the room. The -sky was bright, and there was a genial warmth in the air. It was -almost like a morning in May. - -Gradually the events of the preceding night crept with silent, -blood-stained feet into his brain and reconstructed themselves there -with terrible distinctness. He winced at the memory of all that he had -suffered, and for a moment the same curious feeling of loathing for -Basil Hallward that had made him kill him as he sat in the chair came -back to him, and he grew cold with passion. The dead man was still -sitting there, too, and in the sunlight now. How horrible that was! -Such hideous things were for the darkness, not for the day. - -He felt that if he brooded on what he had gone through he would sicken -or grow mad. There were sins whose fascination was more in the memory -than in the doing of them, strange triumphs that gratified the pride -more than the passions, and gave to the intellect a quickened sense of -joy, greater than any joy they brought, or could ever bring, to the -senses. But this was not one of them. It was a thing to be driven out -of the mind, to be drugged with poppies, to be strangled lest it might -strangle one itself. - -When the half-hour struck, he passed his hand across his forehead, and -then got up hastily and dressed himself with even more than his usual -care, giving a good deal of attention to the choice of his necktie and -scarf-pin and changing his rings more than once. He spent a long time -also over breakfast, tasting the various dishes, talking to his valet -about some new liveries that he was thinking of getting made for the -servants at Selby, and going through his correspondence. At some of -the letters, he smiled. Three of them bored him. One he read several -times over and then tore up with a slight look of annoyance in his -face. "That awful thing, a woman's memory!" as Lord Henry had once -said. - -After he had drunk his cup of black coffee, he wiped his lips slowly -with a napkin, motioned to his servant to wait, and going over to the -table, sat down and wrote two letters. One he put in his pocket, the -other he handed to the valet. - -"Take this round to 152, Hertford Street, Francis, and if Mr. Campbell -is out of town, get his address." - -As soon as he was alone, he lit a cigarette and began sketching upon a -piece of paper, drawing first flowers and bits of architecture, and -then human faces. Suddenly he remarked that every face that he drew -seemed to have a fantastic likeness to Basil Hallward. He frowned, and -getting up, went over to the book-case and took out a volume at hazard. -He was determined that he would not think about what had happened until -it became absolutely necessary that he should do so. - -When he had stretched himself on the sofa, he looked at the title-page -of the book. It was Gautier's Emaux et Camees, Charpentier's -Japanese-paper edition, with the Jacquemart etching. The binding was -of citron-green leather, with a design of gilt trellis-work and dotted -pomegranates. It had been given to him by Adrian Singleton. As he -turned over the pages, his eye fell on the poem about the hand of -Lacenaire, the cold yellow hand "_du supplice encore mal lavee_," with -its downy red hairs and its "_doigts de faune_." He glanced at his own -white taper fingers, shuddering slightly in spite of himself, and -passed on, till he came to those lovely stanzas upon Venice: - - Sur une gamme chromatique, - Le sein de perles ruisselant, - La Venus de l'Adriatique - Sort de l'eau son corps rose et blanc. - - Les domes, sur l'azur des ondes - Suivant la phrase au pur contour, - S'enflent comme des gorges rondes - Que souleve un soupir d'amour. - - L'esquif aborde et me depose, - Jetant son amarre au pilier, - Devant une facade rose, - Sur le marbre d'un escalier. - - -How exquisite they were! As one read them, one seemed to be floating -down the green water-ways of the pink and pearl city, seated in a black -gondola with silver prow and trailing curtains. The mere lines looked -to him like those straight lines of turquoise-blue that follow one as -one pushes out to the Lido. The sudden flashes of colour reminded him -of the gleam of the opal-and-iris-throated birds that flutter round the -tall honeycombed Campanile, or stalk, with such stately grace, through -the dim, dust-stained arcades. Leaning back with half-closed eyes, he -kept saying over and over to himself: - - "Devant une facade rose, - Sur le marbre d'un escalier." - -The whole of Venice was in those two lines. He remembered the autumn -that he had passed there, and a wonderful love that had stirred him to -mad delightful follies. There was romance in every place. But Venice, -like Oxford, had kept the background for romance, and, to the true -romantic, background was everything, or almost everything. Basil had -been with him part of the time, and had gone wild over Tintoret. Poor -Basil! What a horrible way for a man to die! - -He sighed, and took up the volume again, and tried to forget. He read -of the swallows that fly in and out of the little _cafe_ at Smyrna where -the Hadjis sit counting their amber beads and the turbaned merchants -smoke their long tasselled pipes and talk gravely to each other; he -read of the Obelisk in the Place de la Concorde that weeps tears of -granite in its lonely sunless exile and longs to be back by the hot, -lotus-covered Nile, where there are Sphinxes, and rose-red ibises, and -white vultures with gilded claws, and crocodiles with small beryl eyes -that crawl over the green steaming mud; he began to brood over those -verses which, drawing music from kiss-stained marble, tell of that -curious statue that Gautier compares to a contralto voice, the "_monstre -charmant_" that couches in the porphyry-room of the Louvre. But after a -time the book fell from his hand. He grew nervous, and a horrible fit -of terror came over him. What if Alan Campbell should be out of -England? Days would elapse before he could come back. Perhaps he -might refuse to come. What could he do then? Every moment was of -vital importance. - -They had been great friends once, five years before--almost -inseparable, indeed. Then the intimacy had come suddenly to an end. -When they met in society now, it was only Dorian Gray who smiled: Alan -Campbell never did. - -He was an extremely clever young man, though he had no real -appreciation of the visible arts, and whatever little sense of the -beauty of poetry he possessed he had gained entirely from Dorian. His -dominant intellectual passion was for science. At Cambridge he had -spent a great deal of his time working in the laboratory, and had taken -a good class in the Natural Science Tripos of his year. Indeed, he was -still devoted to the study of chemistry, and had a laboratory of his -own in which he used to shut himself up all day long, greatly to the -annoyance of his mother, who had set her heart on his standing for -Parliament and had a vague idea that a chemist was a person who made up -prescriptions. He was an excellent musician, however, as well, and -played both the violin and the piano better than most amateurs. In -fact, it was music that had first brought him and Dorian Gray -together--music and that indefinable attraction that Dorian seemed to -be able to exercise whenever he wished--and, indeed, exercised often -without being conscious of it. They had met at Lady Berkshire's the -night that Rubinstein played there, and after that used to be always -seen together at the opera and wherever good music was going on. For -eighteen months their intimacy lasted. Campbell was always either at -Selby Royal or in Grosvenor Square. To him, as to many others, Dorian -Gray was the type of everything that is wonderful and fascinating in -life. Whether or not a quarrel had taken place between them no one -ever knew. But suddenly people remarked that they scarcely spoke when -they met and that Campbell seemed always to go away early from any -party at which Dorian Gray was present. He had changed, too--was -strangely melancholy at times, appeared almost to dislike hearing -music, and would never himself play, giving as his excuse, when he was -called upon, that he was so absorbed in science that he had no time -left in which to practise. And this was certainly true. Every day he -seemed to become more interested in biology, and his name appeared once -or twice in some of the scientific reviews in connection with certain -curious experiments. - -This was the man Dorian Gray was waiting for. Every second he kept -glancing at the clock. As the minutes went by he became horribly -agitated. At last he got up and began to pace up and down the room, -looking like a beautiful caged thing. He took long stealthy strides. -His hands were curiously cold. - -The suspense became unbearable. Time seemed to him to be crawling with -feet of lead, while he by monstrous winds was being swept towards the -jagged edge of some black cleft of precipice. He knew what was waiting -for him there; saw it, indeed, and, shuddering, crushed with dank hands -his burning lids as though he would have robbed the very brain of sight -and driven the eyeballs back into their cave. It was useless. The -brain had its own food on which it battened, and the imagination, made -grotesque by terror, twisted and distorted as a living thing by pain, -danced like some foul puppet on a stand and grinned through moving -masks. Then, suddenly, time stopped for him. Yes: that blind, -slow-breathing thing crawled no more, and horrible thoughts, time being -dead, raced nimbly on in front, and dragged a hideous future from its -grave, and showed it to him. He stared at it. Its very horror made -him stone. - -At last the door opened and his servant entered. He turned glazed eyes -upon him. - -"Mr. Campbell, sir," said the man. - -A sigh of relief broke from his parched lips, and the colour came back -to his cheeks. - -"Ask him to come in at once, Francis." He felt that he was himself -again. His mood of cowardice had passed away. - -The man bowed and retired. In a few moments, Alan Campbell walked in, -looking very stern and rather pale, his pallor being intensified by his -coal-black hair and dark eyebrows. - -"Alan! This is kind of you. I thank you for coming." - -"I had intended never to enter your house again, Gray. But you said it -was a matter of life and death." His voice was hard and cold. He -spoke with slow deliberation. There was a look of contempt in the -steady searching gaze that he turned on Dorian. He kept his hands in -the pockets of his Astrakhan coat, and seemed not to have noticed the -gesture with which he had been greeted. - -"Yes: it is a matter of life and death, Alan, and to more than one -person. Sit down." - -Campbell took a chair by the table, and Dorian sat opposite to him. -The two men's eyes met. In Dorian's there was infinite pity. He knew -that what he was going to do was dreadful. - -After a strained moment of silence, he leaned across and said, very -quietly, but watching the effect of each word upon the face of him he -had sent for, "Alan, in a locked room at the top of this house, a room -to which nobody but myself has access, a dead man is seated at a table. -He has been dead ten hours now. Don't stir, and don't look at me like -that. Who the man is, why he died, how he died, are matters that do -not concern you. What you have to do is this--" - -"Stop, Gray. I don't want to know anything further. Whether what you -have told me is true or not true doesn't concern me. I entirely -decline to be mixed up in your life. Keep your horrible secrets to -yourself. They don't interest me any more." - -"Alan, they will have to interest you. This one will have to interest -you. I am awfully sorry for you, Alan. But I can't help myself. You -are the one man who is able to save me. I am forced to bring you into -the matter. I have no option. Alan, you are scientific. You know -about chemistry and things of that kind. You have made experiments. -What you have got to do is to destroy the thing that is upstairs--to -destroy it so that not a vestige of it will be left. Nobody saw this -person come into the house. Indeed, at the present moment he is -supposed to be in Paris. He will not be missed for months. When he is -missed, there must be no trace of him found here. You, Alan, you must -change him, and everything that belongs to him, into a handful of ashes -that I may scatter in the air." - -"You are mad, Dorian." - -"Ah! I was waiting for you to call me Dorian." - -"You are mad, I tell you--mad to imagine that I would raise a finger to -help you, mad to make this monstrous confession. I will have nothing -to do with this matter, whatever it is. Do you think I am going to -peril my reputation for you? What is it to me what devil's work you -are up to?" - -"It was suicide, Alan." - -"I am glad of that. But who drove him to it? You, I should fancy." - -"Do you still refuse to do this for me?" - -"Of course I refuse. I will have absolutely nothing to do with it. I -don't care what shame comes on you. You deserve it all. I should not -be sorry to see you disgraced, publicly disgraced. How dare you ask -me, of all men in the world, to mix myself up in this horror? I should -have thought you knew more about people's characters. Your friend Lord -Henry Wotton can't have taught you much about psychology, whatever else -he has taught you. Nothing will induce me to stir a step to help you. -You have come to the wrong man. Go to some of your friends. Don't -come to me." - -"Alan, it was murder. I killed him. You don't know what he had made -me suffer. Whatever my life is, he had more to do with the making or -the marring of it than poor Harry has had. He may not have intended -it, the result was the same." - -"Murder! Good God, Dorian, is that what you have come to? I shall not -inform upon you. It is not my business. Besides, without my stirring -in the matter, you are certain to be arrested. Nobody ever commits a -crime without doing something stupid. But I will have nothing to do -with it." - -"You must have something to do with it. Wait, wait a moment; listen to -me. Only listen, Alan. All I ask of you is to perform a certain -scientific experiment. You go to hospitals and dead-houses, and the -horrors that you do there don't affect you. If in some hideous -dissecting-room or fetid laboratory you found this man lying on a -leaden table with red gutters scooped out in it for the blood to flow -through, you would simply look upon him as an admirable subject. You -would not turn a hair. You would not believe that you were doing -anything wrong. On the contrary, you would probably feel that you were -benefiting the human race, or increasing the sum of knowledge in the -world, or gratifying intellectual curiosity, or something of that kind. -What I want you to do is merely what you have often done before. -Indeed, to destroy a body must be far less horrible than what you are -accustomed to work at. And, remember, it is the only piece of evidence -against me. If it is discovered, I am lost; and it is sure to be -discovered unless you help me." - -"I have no desire to help you. You forget that. I am simply -indifferent to the whole thing. It has nothing to do with me." - -"Alan, I entreat you. Think of the position I am in. Just before you -came I almost fainted with terror. You may know terror yourself some -day. No! don't think of that. Look at the matter purely from the -scientific point of view. You don't inquire where the dead things on -which you experiment come from. Don't inquire now. I have told you -too much as it is. But I beg of you to do this. We were friends once, -Alan." - -"Don't speak about those days, Dorian--they are dead." - -"The dead linger sometimes. The man upstairs will not go away. He is -sitting at the table with bowed head and outstretched arms. Alan! -Alan! If you don't come to my assistance, I am ruined. Why, they will -hang me, Alan! Don't you understand? They will hang me for what I -have done." - -"There is no good in prolonging this scene. I absolutely refuse to do -anything in the matter. It is insane of you to ask me." - -"You refuse?" - -"Yes." - -"I entreat you, Alan." - -"It is useless." - -The same look of pity came into Dorian Gray's eyes. Then he stretched -out his hand, took a piece of paper, and wrote something on it. He -read it over twice, folded it carefully, and pushed it across the -table. Having done this, he got up and went over to the window. - -Campbell looked at him in surprise, and then took up the paper, and -opened it. As he read it, his face became ghastly pale and he fell -back in his chair. A horrible sense of sickness came over him. He -felt as if his heart was beating itself to death in some empty hollow. - -After two or three minutes of terrible silence, Dorian turned round and -came and stood behind him, putting his hand upon his shoulder. - -"I am so sorry for you, Alan," he murmured, "but you leave me no -alternative. I have a letter written already. Here it is. You see -the address. If you don't help me, I must send it. If you don't help -me, I will send it. You know what the result will be. But you are -going to help me. It is impossible for you to refuse now. I tried to -spare you. You will do me the justice to admit that. You were stern, -harsh, offensive. You treated me as no man has ever dared to treat -me--no living man, at any rate. I bore it all. Now it is for me to -dictate terms." - -Campbell buried his face in his hands, and a shudder passed through him. - -"Yes, it is my turn to dictate terms, Alan. You know what they are. -The thing is quite simple. Come, don't work yourself into this fever. -The thing has to be done. Face it, and do it." - -A groan broke from Campbell's lips and he shivered all over. The -ticking of the clock on the mantelpiece seemed to him to be dividing -time into separate atoms of agony, each of which was too terrible to be -borne. He felt as if an iron ring was being slowly tightened round his -forehead, as if the disgrace with which he was threatened had already -come upon him. The hand upon his shoulder weighed like a hand of lead. -It was intolerable. It seemed to crush him. - -"Come, Alan, you must decide at once." - -"I cannot do it," he said, mechanically, as though words could alter -things. - -"You must. You have no choice. Don't delay." - -He hesitated a moment. "Is there a fire in the room upstairs?" - -"Yes, there is a gas-fire with asbestos." - -"I shall have to go home and get some things from the laboratory." - -"No, Alan, you must not leave the house. Write out on a sheet of -notepaper what you want and my servant will take a cab and bring the -things back to you." - -Campbell scrawled a few lines, blotted them, and addressed an envelope -to his assistant. Dorian took the note up and read it carefully. Then -he rang the bell and gave it to his valet, with orders to return as -soon as possible and to bring the things with him. - -As the hall door shut, Campbell started nervously, and having got up -from the chair, went over to the chimney-piece. He was shivering with a -kind of ague. For nearly twenty minutes, neither of the men spoke. A -fly buzzed noisily about the room, and the ticking of the clock was -like the beat of a hammer. - -As the chime struck one, Campbell turned round, and looking at Dorian -Gray, saw that his eyes were filled with tears. There was something in -the purity and refinement of that sad face that seemed to enrage him. -"You are infamous, absolutely infamous!" he muttered. - -"Hush, Alan. You have saved my life," said Dorian. - -"Your life? Good heavens! what a life that is! You have gone from -corruption to corruption, and now you have culminated in crime. In -doing what I am going to do--what you force me to do--it is not of your -life that I am thinking." - -"Ah, Alan," murmured Dorian with a sigh, "I wish you had a thousandth -part of the pity for me that I have for you." He turned away as he -spoke and stood looking out at the garden. Campbell made no answer. - -After about ten minutes a knock came to the door, and the servant -entered, carrying a large mahogany chest of chemicals, with a long coil -of steel and platinum wire and two rather curiously shaped iron clamps. - -"Shall I leave the things here, sir?" he asked Campbell. - -"Yes," said Dorian. "And I am afraid, Francis, that I have another -errand for you. What is the name of the man at Richmond who supplies -Selby with orchids?" - -"Harden, sir." - -"Yes--Harden. You must go down to Richmond at once, see Harden -personally, and tell him to send twice as many orchids as I ordered, -and to have as few white ones as possible. In fact, I don't want any -white ones. It is a lovely day, Francis, and Richmond is a very pretty -place--otherwise I wouldn't bother you about it." - -"No trouble, sir. At what time shall I be back?" - -Dorian looked at Campbell. "How long will your experiment take, Alan?" -he said in a calm indifferent voice. The presence of a third person in -the room seemed to give him extraordinary courage. - -Campbell frowned and bit his lip. "It will take about five hours," he -answered. - -"It will be time enough, then, if you are back at half-past seven, -Francis. Or stay: just leave my things out for dressing. You can -have the evening to yourself. I am not dining at home, so I shall not -want you." - -"Thank you, sir," said the man, leaving the room. - -"Now, Alan, there is not a moment to be lost. How heavy this chest is! -I'll take it for you. You bring the other things." He spoke rapidly -and in an authoritative manner. Campbell felt dominated by him. They -left the room together. - -When they reached the top landing, Dorian took out the key and turned -it in the lock. Then he stopped, and a troubled look came into his -eyes. He shuddered. "I don't think I can go in, Alan," he murmured. - -"It is nothing to me. I don't require you," said Campbell coldly. - -Dorian half opened the door. As he did so, he saw the face of his -portrait leering in the sunlight. On the floor in front of it the torn -curtain was lying. He remembered that the night before he had -forgotten, for the first time in his life, to hide the fatal canvas, -and was about to rush forward, when he drew back with a shudder. - -What was that loathsome red dew that gleamed, wet and glistening, on -one of the hands, as though the canvas had sweated blood? How horrible -it was!--more horrible, it seemed to him for the moment, than the -silent thing that he knew was stretched across the table, the thing -whose grotesque misshapen shadow on the spotted carpet showed him that -it had not stirred, but was still there, as he had left it. - -He heaved a deep breath, opened the door a little wider, and with -half-closed eyes and averted head, walked quickly in, determined that -he would not look even once upon the dead man. Then, stooping down and -taking up the gold-and-purple hanging, he flung it right over the -picture. - -There he stopped, feeling afraid to turn round, and his eyes fixed -themselves on the intricacies of the pattern before him. He heard -Campbell bringing in the heavy chest, and the irons, and the other -things that he had required for his dreadful work. He began to wonder -if he and Basil Hallward had ever met, and, if so, what they had -thought of each other. - -"Leave me now," said a stern voice behind him. - -He turned and hurried out, just conscious that the dead man had been -thrust back into the chair and that Campbell was gazing into a -glistening yellow face. As he was going downstairs, he heard the key -being turned in the lock. - -It was long after seven when Campbell came back into the library. He -was pale, but absolutely calm. "I have done what you asked me to do," -he muttered. "And now, good-bye. Let us never see each other again." - -"You have saved me from ruin, Alan. I cannot forget that," said Dorian -simply. - -As soon as Campbell had left, he went upstairs. There was a horrible -smell of nitric acid in the room. But the thing that had been sitting -at the table was gone. - - - -CHAPTER 15 - -That evening, at eight-thirty, exquisitely dressed and wearing a large -button-hole of Parma violets, Dorian Gray was ushered into Lady -Narborough's drawing-room by bowing servants. His forehead was -throbbing with maddened nerves, and he felt wildly excited, but his -manner as he bent over his hostess's hand was as easy and graceful as -ever. Perhaps one never seems so much at one's ease as when one has to -play a part. Certainly no one looking at Dorian Gray that night could -have believed that he had passed through a tragedy as horrible as any -tragedy of our age. Those finely shaped fingers could never have -clutched a knife for sin, nor those smiling lips have cried out on God -and goodness. He himself could not help wondering at the calm of his -demeanour, and for a moment felt keenly the terrible pleasure of a -double life. - -It was a small party, got up rather in a hurry by Lady Narborough, who -was a very clever woman with what Lord Henry used to describe as the -remains of really remarkable ugliness. She had proved an excellent -wife to one of our most tedious ambassadors, and having buried her -husband properly in a marble mausoleum, which she had herself designed, -and married off her daughters to some rich, rather elderly men, she -devoted herself now to the pleasures of French fiction, French cookery, -and French _esprit_ when she could get it. - -Dorian was one of her especial favourites, and she always told him that -she was extremely glad she had not met him in early life. "I know, my -dear, I should have fallen madly in love with you," she used to say, -"and thrown my bonnet right over the mills for your sake. It is most -fortunate that you were not thought of at the time. As it was, our -bonnets were so unbecoming, and the mills were so occupied in trying to -raise the wind, that I never had even a flirtation with anybody. -However, that was all Narborough's fault. He was dreadfully -short-sighted, and there is no pleasure in taking in a husband who -never sees anything." - -Her guests this evening were rather tedious. The fact was, as she -explained to Dorian, behind a very shabby fan, one of her married -daughters had come up quite suddenly to stay with her, and, to make -matters worse, had actually brought her husband with her. "I think it -is most unkind of her, my dear," she whispered. "Of course I go and -stay with them every summer after I come from Homburg, but then an old -woman like me must have fresh air sometimes, and besides, I really wake -them up. You don't know what an existence they lead down there. It is -pure unadulterated country life. They get up early, because they have -so much to do, and go to bed early, because they have so little to -think about. There has not been a scandal in the neighbourhood since -the time of Queen Elizabeth, and consequently they all fall asleep -after dinner. You shan't sit next either of them. You shall sit by me -and amuse me." - -Dorian murmured a graceful compliment and looked round the room. Yes: -it was certainly a tedious party. Two of the people he had never seen -before, and the others consisted of Ernest Harrowden, one of those -middle-aged mediocrities so common in London clubs who have no enemies, -but are thoroughly disliked by their friends; Lady Ruxton, an -overdressed woman of forty-seven, with a hooked nose, who was always -trying to get herself compromised, but was so peculiarly plain that to -her great disappointment no one would ever believe anything against -her; Mrs. Erlynne, a pushing nobody, with a delightful lisp and -Venetian-red hair; Lady Alice Chapman, his hostess's daughter, a dowdy -dull girl, with one of those characteristic British faces that, once -seen, are never remembered; and her husband, a red-cheeked, -white-whiskered creature who, like so many of his class, was under the -impression that inordinate joviality can atone for an entire lack of -ideas. - -He was rather sorry he had come, till Lady Narborough, looking at the -great ormolu gilt clock that sprawled in gaudy curves on the -mauve-draped mantelshelf, exclaimed: "How horrid of Henry Wotton to be -so late! I sent round to him this morning on chance and he promised -faithfully not to disappoint me." - -It was some consolation that Harry was to be there, and when the door -opened and he heard his slow musical voice lending charm to some -insincere apology, he ceased to feel bored. - -But at dinner he could not eat anything. Plate after plate went away -untasted. Lady Narborough kept scolding him for what she called "an -insult to poor Adolphe, who invented the _menu_ specially for you," and -now and then Lord Henry looked across at him, wondering at his silence -and abstracted manner. From time to time the butler filled his glass -with champagne. He drank eagerly, and his thirst seemed to increase. - -"Dorian," said Lord Henry at last, as the _chaud-froid_ was being handed -round, "what is the matter with you to-night? You are quite out of -sorts." - -"I believe he is in love," cried Lady Narborough, "and that he is -afraid to tell me for fear I should be jealous. He is quite right. I -certainly should." - -"Dear Lady Narborough," murmured Dorian, smiling, "I have not been in -love for a whole week--not, in fact, since Madame de Ferrol left town." - -"How you men can fall in love with that woman!" exclaimed the old lady. -"I really cannot understand it." - -"It is simply because she remembers you when you were a little girl, -Lady Narborough," said Lord Henry. "She is the one link between us and -your short frocks." - -"She does not remember my short frocks at all, Lord Henry. But I -remember her very well at Vienna thirty years ago, and how _decolletee_ -she was then." - -"She is still _decolletee_," he answered, taking an olive in his long -fingers; "and when she is in a very smart gown she looks like an -_edition de luxe_ of a bad French novel. She is really wonderful, and -full of surprises. Her capacity for family affection is extraordinary. -When her third husband died, her hair turned quite gold from grief." - -"How can you, Harry!" cried Dorian. - -"It is a most romantic explanation," laughed the hostess. "But her -third husband, Lord Henry! You don't mean to say Ferrol is the fourth?" - -"Certainly, Lady Narborough." - -"I don't believe a word of it." - -"Well, ask Mr. Gray. He is one of her most intimate friends." - -"Is it true, Mr. Gray?" - -"She assures me so, Lady Narborough," said Dorian. "I asked her -whether, like Marguerite de Navarre, she had their hearts embalmed and -hung at her girdle. She told me she didn't, because none of them had -had any hearts at all." - -"Four husbands! Upon my word that is _trop de zele_." - -"_Trop d'audace_, I tell her," said Dorian. - -"Oh! she is audacious enough for anything, my dear. And what is Ferrol -like? I don't know him." - -"The husbands of very beautiful women belong to the criminal classes," -said Lord Henry, sipping his wine. - -Lady Narborough hit him with her fan. "Lord Henry, I am not at all -surprised that the world says that you are extremely wicked." - -"But what world says that?" asked Lord Henry, elevating his eyebrows. -"It can only be the next world. This world and I are on excellent -terms." - -"Everybody I know says you are very wicked," cried the old lady, -shaking her head. - -Lord Henry looked serious for some moments. "It is perfectly -monstrous," he said, at last, "the way people go about nowadays saying -things against one behind one's back that are absolutely and entirely -true." - -"Isn't he incorrigible?" cried Dorian, leaning forward in his chair. - -"I hope so," said his hostess, laughing. "But really, if you all -worship Madame de Ferrol in this ridiculous way, I shall have to marry -again so as to be in the fashion." - -"You will never marry again, Lady Narborough," broke in Lord Henry. -"You were far too happy. When a woman marries again, it is because she -detested her first husband. When a man marries again, it is because he -adored his first wife. Women try their luck; men risk theirs." - -"Narborough wasn't perfect," cried the old lady. - -"If he had been, you would not have loved him, my dear lady," was the -rejoinder. "Women love us for our defects. If we have enough of them, -they will forgive us everything, even our intellects. You will never -ask me to dinner again after saying this, I am afraid, Lady Narborough, -but it is quite true." - -"Of course it is true, Lord Henry. If we women did not love you for -your defects, where would you all be? Not one of you would ever be -married. You would be a set of unfortunate bachelors. Not, however, -that that would alter you much. Nowadays all the married men live like -bachelors, and all the bachelors like married men." - -"_Fin de siecle_," murmured Lord Henry. - -"_Fin du globe_," answered his hostess. - -"I wish it were _fin du globe_," said Dorian with a sigh. "Life is a -great disappointment." - -"Ah, my dear," cried Lady Narborough, putting on her gloves, "don't -tell me that you have exhausted life. When a man says that one knows -that life has exhausted him. Lord Henry is very wicked, and I -sometimes wish that I had been; but you are made to be good--you look -so good. I must find you a nice wife. Lord Henry, don't you think -that Mr. Gray should get married?" - -"I am always telling him so, Lady Narborough," said Lord Henry with a -bow. - -"Well, we must look out for a suitable match for him. I shall go -through Debrett carefully to-night and draw out a list of all the -eligible young ladies." - -"With their ages, Lady Narborough?" asked Dorian. - -"Of course, with their ages, slightly edited. But nothing must be done -in a hurry. I want it to be what _The Morning Post_ calls a suitable -alliance, and I want you both to be happy." - -"What nonsense people talk about happy marriages!" exclaimed Lord -Henry. "A man can be happy with any woman, as long as he does not love -her." - -"Ah! what a cynic you are!" cried the old lady, pushing back her chair -and nodding to Lady Ruxton. "You must come and dine with me soon -again. You are really an admirable tonic, much better than what Sir -Andrew prescribes for me. You must tell me what people you would like -to meet, though. I want it to be a delightful gathering." - -"I like men who have a future and women who have a past," he answered. -"Or do you think that would make it a petticoat party?" - -"I fear so," she said, laughing, as she stood up. "A thousand pardons, -my dear Lady Ruxton," she added, "I didn't see you hadn't finished your -cigarette." - -"Never mind, Lady Narborough. I smoke a great deal too much. I am -going to limit myself, for the future." - -"Pray don't, Lady Ruxton," said Lord Henry. "Moderation is a fatal -thing. Enough is as bad as a meal. More than enough is as good as a -feast." - -Lady Ruxton glanced at him curiously. "You must come and explain that -to me some afternoon, Lord Henry. It sounds a fascinating theory," she -murmured, as she swept out of the room. - -"Now, mind you don't stay too long over your politics and scandal," -cried Lady Narborough from the door. "If you do, we are sure to -squabble upstairs." - -The men laughed, and Mr. Chapman got up solemnly from the foot of the -table and came up to the top. Dorian Gray changed his seat and went -and sat by Lord Henry. Mr. Chapman began to talk in a loud voice about -the situation in the House of Commons. He guffawed at his adversaries. -The word _doctrinaire_--word full of terror to the British -mind--reappeared from time to time between his explosions. An -alliterative prefix served as an ornament of oratory. He hoisted the -Union Jack on the pinnacles of thought. The inherited stupidity of the -race--sound English common sense he jovially termed it--was shown to be -the proper bulwark for society. - -A smile curved Lord Henry's lips, and he turned round and looked at -Dorian. - -"Are you better, my dear fellow?" he asked. "You seemed rather out of -sorts at dinner." - -"I am quite well, Harry. I am tired. That is all." - -"You were charming last night. The little duchess is quite devoted to -you. She tells me she is going down to Selby." - -"She has promised to come on the twentieth." - -"Is Monmouth to be there, too?" - -"Oh, yes, Harry." - -"He bores me dreadfully, almost as much as he bores her. She is very -clever, too clever for a woman. She lacks the indefinable charm of -weakness. It is the feet of clay that make the gold of the image -precious. Her feet are very pretty, but they are not feet of clay. -White porcelain feet, if you like. They have been through the fire, -and what fire does not destroy, it hardens. She has had experiences." - -"How long has she been married?" asked Dorian. - -"An eternity, she tells me. I believe, according to the peerage, it is -ten years, but ten years with Monmouth must have been like eternity, -with time thrown in. Who else is coming?" - -"Oh, the Willoughbys, Lord Rugby and his wife, our hostess, Geoffrey -Clouston, the usual set. I have asked Lord Grotrian." - -"I like him," said Lord Henry. "A great many people don't, but I find -him charming. He atones for being occasionally somewhat overdressed by -being always absolutely over-educated. He is a very modern type." - -"I don't know if he will be able to come, Harry. He may have to go to -Monte Carlo with his father." - -"Ah! what a nuisance people's people are! Try and make him come. By -the way, Dorian, you ran off very early last night. You left before -eleven. What did you do afterwards? Did you go straight home?" - -Dorian glanced at him hurriedly and frowned. - -"No, Harry," he said at last, "I did not get home till nearly three." - -"Did you go to the club?" - -"Yes," he answered. Then he bit his lip. "No, I don't mean that. I -didn't go to the club. I walked about. I forget what I did.... How -inquisitive you are, Harry! You always want to know what one has been -doing. I always want to forget what I have been doing. I came in at -half-past two, if you wish to know the exact time. I had left my -latch-key at home, and my servant had to let me in. If you want any -corroborative evidence on the subject, you can ask him." - -Lord Henry shrugged his shoulders. "My dear fellow, as if I cared! -Let us go up to the drawing-room. No sherry, thank you, Mr. Chapman. -Something has happened to you, Dorian. Tell me what it is. You are -not yourself to-night." - -"Don't mind me, Harry. I am irritable, and out of temper. I shall -come round and see you to-morrow, or next day. Make my excuses to Lady -Narborough. I shan't go upstairs. I shall go home. I must go home." - -"All right, Dorian. I dare say I shall see you to-morrow at tea-time. -The duchess is coming." - -"I will try to be there, Harry," he said, leaving the room. As he -drove back to his own house, he was conscious that the sense of terror -he thought he had strangled had come back to him. Lord Henry's casual -questioning had made him lose his nerve for the moment, and he wanted -his nerve still. Things that were dangerous had to be destroyed. He -winced. He hated the idea of even touching them. - -Yet it had to be done. He realized that, and when he had locked the -door of his library, he opened the secret press into which he had -thrust Basil Hallward's coat and bag. A huge fire was blazing. He -piled another log on it. The smell of the singeing clothes and burning -leather was horrible. It took him three-quarters of an hour to consume -everything. At the end he felt faint and sick, and having lit some -Algerian pastilles in a pierced copper brazier, he bathed his hands and -forehead with a cool musk-scented vinegar. - -Suddenly he started. His eyes grew strangely bright, and he gnawed -nervously at his underlip. Between two of the windows stood a large -Florentine cabinet, made out of ebony and inlaid with ivory and blue -lapis. He watched it as though it were a thing that could fascinate -and make afraid, as though it held something that he longed for and yet -almost loathed. His breath quickened. A mad craving came over him. -He lit a cigarette and then threw it away. His eyelids drooped till -the long fringed lashes almost touched his cheek. But he still watched -the cabinet. At last he got up from the sofa on which he had been -lying, went over to it, and having unlocked it, touched some hidden -spring. A triangular drawer passed slowly out. His fingers moved -instinctively towards it, dipped in, and closed on something. It was a -small Chinese box of black and gold-dust lacquer, elaborately wrought, -the sides patterned with curved waves, and the silken cords hung with -round crystals and tasselled in plaited metal threads. He opened it. -Inside was a green paste, waxy in lustre, the odour curiously heavy and -persistent. - -He hesitated for some moments, with a strangely immobile smile upon his -face. Then shivering, though the atmosphere of the room was terribly -hot, he drew himself up and glanced at the clock. It was twenty -minutes to twelve. He put the box back, shutting the cabinet doors as -he did so, and went into his bedroom. - -As midnight was striking bronze blows upon the dusky air, Dorian Gray, -dressed commonly, and with a muffler wrapped round his throat, crept -quietly out of his house. In Bond Street he found a hansom with a good -horse. He hailed it and in a low voice gave the driver an address. - -The man shook his head. "It is too far for me," he muttered. - -"Here is a sovereign for you," said Dorian. "You shall have another if -you drive fast." - -"All right, sir," answered the man, "you will be there in an hour," and -after his fare had got in he turned his horse round and drove rapidly -towards the river. - - - -CHAPTER 16 - -A cold rain began to fall, and the blurred street-lamps looked ghastly -in the dripping mist. The public-houses were just closing, and dim men -and women were clustering in broken groups round their doors. From -some of the bars came the sound of horrible laughter. In others, -drunkards brawled and screamed. - -Lying back in the hansom, with his hat pulled over his forehead, Dorian -Gray watched with listless eyes the sordid shame of the great city, and -now and then he repeated to himself the words that Lord Henry had said -to him on the first day they had met, "To cure the soul by means of the -senses, and the senses by means of the soul." Yes, that was the -secret. He had often tried it, and would try it again now. There were -opium dens where one could buy oblivion, dens of horror where the -memory of old sins could be destroyed by the madness of sins that were -new. - -The moon hung low in the sky like a yellow skull. From time to time a -huge misshapen cloud stretched a long arm across and hid it. The -gas-lamps grew fewer, and the streets more narrow and gloomy. Once the -man lost his way and had to drive back half a mile. A steam rose from -the horse as it splashed up the puddles. The sidewindows of the hansom -were clogged with a grey-flannel mist. - -"To cure the soul by means of the senses, and the senses by means of -the soul!" How the words rang in his ears! His soul, certainly, was -sick to death. Was it true that the senses could cure it? Innocent -blood had been spilled. What could atone for that? Ah! for that there -was no atonement; but though forgiveness was impossible, forgetfulness -was possible still, and he was determined to forget, to stamp the thing -out, to crush it as one would crush the adder that had stung one. -Indeed, what right had Basil to have spoken to him as he had done? Who -had made him a judge over others? He had said things that were -dreadful, horrible, not to be endured. - -On and on plodded the hansom, going slower, it seemed to him, at each -step. He thrust up the trap and called to the man to drive faster. -The hideous hunger for opium began to gnaw at him. His throat burned -and his delicate hands twitched nervously together. He struck at the -horse madly with his stick. The driver laughed and whipped up. He -laughed in answer, and the man was silent. - -The way seemed interminable, and the streets like the black web of some -sprawling spider. The monotony became unbearable, and as the mist -thickened, he felt afraid. - -Then they passed by lonely brickfields. The fog was lighter here, and -he could see the strange, bottle-shaped kilns with their orange, -fanlike tongues of fire. A dog barked as they went by, and far away in -the darkness some wandering sea-gull screamed. The horse stumbled in a -rut, then swerved aside and broke into a gallop. - -After some time they left the clay road and rattled again over -rough-paven streets. Most of the windows were dark, but now and then -fantastic shadows were silhouetted against some lamplit blind. He -watched them curiously. They moved like monstrous marionettes and made -gestures like live things. He hated them. A dull rage was in his -heart. As they turned a corner, a woman yelled something at them from -an open door, and two men ran after the hansom for about a hundred -yards. The driver beat at them with his whip. - -It is said that passion makes one think in a circle. Certainly with -hideous iteration the bitten lips of Dorian Gray shaped and reshaped -those subtle words that dealt with soul and sense, till he had found in -them the full expression, as it were, of his mood, and justified, by -intellectual approval, passions that without such justification would -still have dominated his temper. From cell to cell of his brain crept -the one thought; and the wild desire to live, most terrible of all -man's appetites, quickened into force each trembling nerve and fibre. -Ugliness that had once been hateful to him because it made things real, -became dear to him now for that very reason. Ugliness was the one -reality. The coarse brawl, the loathsome den, the crude violence of -disordered life, the very vileness of thief and outcast, were more -vivid, in their intense actuality of impression, than all the gracious -shapes of art, the dreamy shadows of song. They were what he needed -for forgetfulness. In three days he would be free. - -Suddenly the man drew up with a jerk at the top of a dark lane. Over -the low roofs and jagged chimney-stacks of the houses rose the black -masts of ships. Wreaths of white mist clung like ghostly sails to the -yards. - -"Somewhere about here, sir, ain't it?" he asked huskily through the -trap. - -Dorian started and peered round. "This will do," he answered, and -having got out hastily and given the driver the extra fare he had -promised him, he walked quickly in the direction of the quay. Here and -there a lantern gleamed at the stern of some huge merchantman. The -light shook and splintered in the puddles. A red glare came from an -outward-bound steamer that was coaling. The slimy pavement looked like -a wet mackintosh. - -He hurried on towards the left, glancing back now and then to see if he -was being followed. In about seven or eight minutes he reached a small -shabby house that was wedged in between two gaunt factories. In one of -the top-windows stood a lamp. He stopped and gave a peculiar knock. - -After a little time he heard steps in the passage and the chain being -unhooked. The door opened quietly, and he went in without saying a -word to the squat misshapen figure that flattened itself into the -shadow as he passed. At the end of the hall hung a tattered green -curtain that swayed and shook in the gusty wind which had followed him -in from the street. He dragged it aside and entered a long low room -which looked as if it had once been a third-rate dancing-saloon. Shrill -flaring gas-jets, dulled and distorted in the fly-blown mirrors that -faced them, were ranged round the walls. Greasy reflectors of ribbed -tin backed them, making quivering disks of light. The floor was -covered with ochre-coloured sawdust, trampled here and there into mud, -and stained with dark rings of spilled liquor. Some Malays were -crouching by a little charcoal stove, playing with bone counters and -showing their white teeth as they chattered. In one corner, with his -head buried in his arms, a sailor sprawled over a table, and by the -tawdrily painted bar that ran across one complete side stood two -haggard women, mocking an old man who was brushing the sleeves of his -coat with an expression of disgust. "He thinks he's got red ants on -him," laughed one of them, as Dorian passed by. The man looked at her -in terror and began to whimper. - -At the end of the room there was a little staircase, leading to a -darkened chamber. As Dorian hurried up its three rickety steps, the -heavy odour of opium met him. He heaved a deep breath, and his -nostrils quivered with pleasure. When he entered, a young man with -smooth yellow hair, who was bending over a lamp lighting a long thin -pipe, looked up at him and nodded in a hesitating manner. - -"You here, Adrian?" muttered Dorian. - -"Where else should I be?" he answered, listlessly. "None of the chaps -will speak to me now." - -"I thought you had left England." - -"Darlington is not going to do anything. My brother paid the bill at -last. George doesn't speak to me either.... I don't care," he added -with a sigh. "As long as one has this stuff, one doesn't want friends. -I think I have had too many friends." - -Dorian winced and looked round at the grotesque things that lay in such -fantastic postures on the ragged mattresses. The twisted limbs, the -gaping mouths, the staring lustreless eyes, fascinated him. He knew in -what strange heavens they were suffering, and what dull hells were -teaching them the secret of some new joy. They were better off than he -was. He was prisoned in thought. Memory, like a horrible malady, was -eating his soul away. From time to time he seemed to see the eyes of -Basil Hallward looking at him. Yet he felt he could not stay. The -presence of Adrian Singleton troubled him. He wanted to be where no -one would know who he was. He wanted to escape from himself. - -"I am going on to the other place," he said after a pause. - -"On the wharf?" - -"Yes." - -"That mad-cat is sure to be there. They won't have her in this place -now." - -Dorian shrugged his shoulders. "I am sick of women who love one. -Women who hate one are much more interesting. Besides, the stuff is -better." - -"Much the same." - -"I like it better. Come and have something to drink. I must have -something." - -"I don't want anything," murmured the young man. - -"Never mind." - -Adrian Singleton rose up wearily and followed Dorian to the bar. A -half-caste, in a ragged turban and a shabby ulster, grinned a hideous -greeting as he thrust a bottle of brandy and two tumblers in front of -them. The women sidled up and began to chatter. Dorian turned his -back on them and said something in a low voice to Adrian Singleton. - -A crooked smile, like a Malay crease, writhed across the face of one of -the women. "We are very proud to-night," she sneered. - -"For God's sake don't talk to me," cried Dorian, stamping his foot on -the ground. "What do you want? Money? Here it is. Don't ever talk -to me again." - -Two red sparks flashed for a moment in the woman's sodden eyes, then -flickered out and left them dull and glazed. She tossed her head and -raked the coins off the counter with greedy fingers. Her companion -watched her enviously. - -"It's no use," sighed Adrian Singleton. "I don't care to go back. -What does it matter? I am quite happy here." - -"You will write to me if you want anything, won't you?" said Dorian, -after a pause. - -"Perhaps." - -"Good night, then." - -"Good night," answered the young man, passing up the steps and wiping -his parched mouth with a handkerchief. - -Dorian walked to the door with a look of pain in his face. As he drew -the curtain aside, a hideous laugh broke from the painted lips of the -woman who had taken his money. "There goes the devil's bargain!" she -hiccoughed, in a hoarse voice. - -"Curse you!" he answered, "don't call me that." - -She snapped her fingers. "Prince Charming is what you like to be -called, ain't it?" she yelled after him. - -The drowsy sailor leaped to his feet as she spoke, and looked wildly -round. The sound of the shutting of the hall door fell on his ear. He -rushed out as if in pursuit. - -Dorian Gray hurried along the quay through the drizzling rain. His -meeting with Adrian Singleton had strangely moved him, and he wondered -if the ruin of that young life was really to be laid at his door, as -Basil Hallward had said to him with such infamy of insult. He bit his -lip, and for a few seconds his eyes grew sad. Yet, after all, what did -it matter to him? One's days were too brief to take the burden of -another's errors on one's shoulders. Each man lived his own life and -paid his own price for living it. The only pity was one had to pay so -often for a single fault. One had to pay over and over again, indeed. -In her dealings with man, destiny never closed her accounts. - -There are moments, psychologists tell us, when the passion for sin, or -for what the world calls sin, so dominates a nature that every fibre of -the body, as every cell of the brain, seems to be instinct with fearful -impulses. Men and women at such moments lose the freedom of their -will. They move to their terrible end as automatons move. Choice is -taken from them, and conscience is either killed, or, if it lives at -all, lives but to give rebellion its fascination and disobedience its -charm. For all sins, as theologians weary not of reminding us, are -sins of disobedience. When that high spirit, that morning star of -evil, fell from heaven, it was as a rebel that he fell. - -Callous, concentrated on evil, with stained mind, and soul hungry for -rebellion, Dorian Gray hastened on, quickening his step as he went, but -as he darted aside into a dim archway, that had served him often as a -short cut to the ill-famed place where he was going, he felt himself -suddenly seized from behind, and before he had time to defend himself, -he was thrust back against the wall, with a brutal hand round his -throat. - -He struggled madly for life, and by a terrible effort wrenched the -tightening fingers away. In a second he heard the click of a revolver, -and saw the gleam of a polished barrel, pointing straight at his head, -and the dusky form of a short, thick-set man facing him. - -"What do you want?" he gasped. - -"Keep quiet," said the man. "If you stir, I shoot you." - -"You are mad. What have I done to you?" - -"You wrecked the life of Sibyl Vane," was the answer, "and Sibyl Vane -was my sister. She killed herself. I know it. Her death is at your -door. I swore I would kill you in return. For years I have sought -you. I had no clue, no trace. The two people who could have described -you were dead. I knew nothing of you but the pet name she used to call -you. I heard it to-night by chance. Make your peace with God, for -to-night you are going to die." - -Dorian Gray grew sick with fear. "I never knew her," he stammered. "I -never heard of her. You are mad." - -"You had better confess your sin, for as sure as I am James Vane, you -are going to die." There was a horrible moment. Dorian did not know -what to say or do. "Down on your knees!" growled the man. "I give you -one minute to make your peace--no more. I go on board to-night for -India, and I must do my job first. One minute. That's all." - -Dorian's arms fell to his side. Paralysed with terror, he did not know -what to do. Suddenly a wild hope flashed across his brain. "Stop," he -cried. "How long ago is it since your sister died? Quick, tell me!" - -"Eighteen years," said the man. "Why do you ask me? What do years -matter?" - -"Eighteen years," laughed Dorian Gray, with a touch of triumph in his -voice. "Eighteen years! Set me under the lamp and look at my face!" - -James Vane hesitated for a moment, not understanding what was meant. -Then he seized Dorian Gray and dragged him from the archway. - -Dim and wavering as was the wind-blown light, yet it served to show him -the hideous error, as it seemed, into which he had fallen, for the face -of the man he had sought to kill had all the bloom of boyhood, all the -unstained purity of youth. He seemed little more than a lad of twenty -summers, hardly older, if older indeed at all, than his sister had been -when they had parted so many years ago. It was obvious that this was -not the man who had destroyed her life. - -He loosened his hold and reeled back. "My God! my God!" he cried, "and -I would have murdered you!" - -Dorian Gray drew a long breath. "You have been on the brink of -committing a terrible crime, my man," he said, looking at him sternly. -"Let this be a warning to you not to take vengeance into your own -hands." - -"Forgive me, sir," muttered James Vane. "I was deceived. A chance -word I heard in that damned den set me on the wrong track." - -"You had better go home and put that pistol away, or you may get into -trouble," said Dorian, turning on his heel and going slowly down the -street. - -James Vane stood on the pavement in horror. He was trembling from head -to foot. After a little while, a black shadow that had been creeping -along the dripping wall moved out into the light and came close to him -with stealthy footsteps. He felt a hand laid on his arm and looked -round with a start. It was one of the women who had been drinking at -the bar. - -"Why didn't you kill him?" she hissed out, putting haggard face quite -close to his. "I knew you were following him when you rushed out from -Daly's. You fool! You should have killed him. He has lots of money, -and he's as bad as bad." - -"He is not the man I am looking for," he answered, "and I want no man's -money. I want a man's life. The man whose life I want must be nearly -forty now. This one is little more than a boy. Thank God, I have not -got his blood upon my hands." - -The woman gave a bitter laugh. "Little more than a boy!" she sneered. -"Why, man, it's nigh on eighteen years since Prince Charming made me -what I am." - -"You lie!" cried James Vane. - -She raised her hand up to heaven. "Before God I am telling the truth," -she cried. - -"Before God?" - -"Strike me dumb if it ain't so. He is the worst one that comes here. -They say he has sold himself to the devil for a pretty face. It's nigh -on eighteen years since I met him. He hasn't changed much since then. -I have, though," she added, with a sickly leer. - -"You swear this?" - -"I swear it," came in hoarse echo from her flat mouth. "But don't give -me away to him," she whined; "I am afraid of him. Let me have some -money for my night's lodging." - -He broke from her with an oath and rushed to the corner of the street, -but Dorian Gray had disappeared. When he looked back, the woman had -vanished also. - - - -CHAPTER 17 - -A week later Dorian Gray was sitting in the conservatory at Selby -Royal, talking to the pretty Duchess of Monmouth, who with her husband, -a jaded-looking man of sixty, was amongst his guests. It was tea-time, -and the mellow light of the huge, lace-covered lamp that stood on the -table lit up the delicate china and hammered silver of the service at -which the duchess was presiding. Her white hands were moving daintily -among the cups, and her full red lips were smiling at something that -Dorian had whispered to her. Lord Henry was lying back in a -silk-draped wicker chair, looking at them. On a peach-coloured divan -sat Lady Narborough, pretending to listen to the duke's description of -the last Brazilian beetle that he had added to his collection. Three -young men in elaborate smoking-suits were handing tea-cakes to some of -the women. The house-party consisted of twelve people, and there were -more expected to arrive on the next day. - -"What are you two talking about?" said Lord Henry, strolling over to -the table and putting his cup down. "I hope Dorian has told you about -my plan for rechristening everything, Gladys. It is a delightful idea." - -"But I don't want to be rechristened, Harry," rejoined the duchess, -looking up at him with her wonderful eyes. "I am quite satisfied with -my own name, and I am sure Mr. Gray should be satisfied with his." - -"My dear Gladys, I would not alter either name for the world. They are -both perfect. I was thinking chiefly of flowers. Yesterday I cut an -orchid, for my button-hole. It was a marvellous spotted thing, as -effective as the seven deadly sins. In a thoughtless moment I asked -one of the gardeners what it was called. He told me it was a fine -specimen of _Robinsoniana_, or something dreadful of that kind. It is a -sad truth, but we have lost the faculty of giving lovely names to -things. Names are everything. I never quarrel with actions. My one -quarrel is with words. That is the reason I hate vulgar realism in -literature. The man who could call a spade a spade should be compelled -to use one. It is the only thing he is fit for." - -"Then what should we call you, Harry?" she asked. - -"His name is Prince Paradox," said Dorian. - -"I recognize him in a flash," exclaimed the duchess. - -"I won't hear of it," laughed Lord Henry, sinking into a chair. "From -a label there is no escape! I refuse the title." - -"Royalties may not abdicate," fell as a warning from pretty lips. - -"You wish me to defend my throne, then?" - -"Yes." - -"I give the truths of to-morrow." - -"I prefer the mistakes of to-day," she answered. - -"You disarm me, Gladys," he cried, catching the wilfulness of her mood. - -"Of your shield, Harry, not of your spear." - -"I never tilt against beauty," he said, with a wave of his hand. - -"That is your error, Harry, believe me. You value beauty far too much." - -"How can you say that? I admit that I think that it is better to be -beautiful than to be good. But on the other hand, no one is more ready -than I am to acknowledge that it is better to be good than to be ugly." - -"Ugliness is one of the seven deadly sins, then?" cried the duchess. -"What becomes of your simile about the orchid?" - -"Ugliness is one of the seven deadly virtues, Gladys. You, as a good -Tory, must not underrate them. Beer, the Bible, and the seven deadly -virtues have made our England what she is." - -"You don't like your country, then?" she asked. - -"I live in it." - -"That you may censure it the better." - -"Would you have me take the verdict of Europe on it?" he inquired. - -"What do they say of us?" - -"That Tartuffe has emigrated to England and opened a shop." - -"Is that yours, Harry?" - -"I give it to you." - -"I could not use it. It is too true." - -"You need not be afraid. Our countrymen never recognize a description." - -"They are practical." - -"They are more cunning than practical. When they make up their ledger, -they balance stupidity by wealth, and vice by hypocrisy." - -"Still, we have done great things." - -"Great things have been thrust on us, Gladys." - -"We have carried their burden." - -"Only as far as the Stock Exchange." - -She shook her head. "I believe in the race," she cried. - -"It represents the survival of the pushing." - -"It has development." - -"Decay fascinates me more." - -"What of art?" she asked. - -"It is a malady." - -"Love?" - -"An illusion." - -"Religion?" - -"The fashionable substitute for belief." - -"You are a sceptic." - -"Never! Scepticism is the beginning of faith." - -"What are you?" - -"To define is to limit." - -"Give me a clue." - -"Threads snap. You would lose your way in the labyrinth." - -"You bewilder me. Let us talk of some one else." - -"Our host is a delightful topic. Years ago he was christened Prince -Charming." - -"Ah! don't remind me of that," cried Dorian Gray. - -"Our host is rather horrid this evening," answered the duchess, -colouring. "I believe he thinks that Monmouth married me on purely -scientific principles as the best specimen he could find of a modern -butterfly." - -"Well, I hope he won't stick pins into you, Duchess," laughed Dorian. - -"Oh! my maid does that already, Mr. Gray, when she is annoyed with me." - -"And what does she get annoyed with you about, Duchess?" - -"For the most trivial things, Mr. Gray, I assure you. Usually because -I come in at ten minutes to nine and tell her that I must be dressed by -half-past eight." - -"How unreasonable of her! You should give her warning." - -"I daren't, Mr. Gray. Why, she invents hats for me. You remember the -one I wore at Lady Hilstone's garden-party? You don't, but it is nice -of you to pretend that you do. Well, she made it out of nothing. All -good hats are made out of nothing." - -"Like all good reputations, Gladys," interrupted Lord Henry. "Every -effect that one produces gives one an enemy. To be popular one must be -a mediocrity." - -"Not with women," said the duchess, shaking her head; "and women rule -the world. I assure you we can't bear mediocrities. We women, as some -one says, love with our ears, just as you men love with your eyes, if -you ever love at all." - -"It seems to me that we never do anything else," murmured Dorian. - -"Ah! then, you never really love, Mr. Gray," answered the duchess with -mock sadness. - -"My dear Gladys!" cried Lord Henry. "How can you say that? Romance -lives by repetition, and repetition converts an appetite into an art. -Besides, each time that one loves is the only time one has ever loved. -Difference of object does not alter singleness of passion. It merely -intensifies it. We can have in life but one great experience at best, -and the secret of life is to reproduce that experience as often as -possible." - -"Even when one has been wounded by it, Harry?" asked the duchess after -a pause. - -"Especially when one has been wounded by it," answered Lord Henry. - -The duchess turned and looked at Dorian Gray with a curious expression -in her eyes. "What do you say to that, Mr. Gray?" she inquired. - -Dorian hesitated for a moment. Then he threw his head back and -laughed. "I always agree with Harry, Duchess." - -"Even when he is wrong?" - -"Harry is never wrong, Duchess." - -"And does his philosophy make you happy?" - -"I have never searched for happiness. Who wants happiness? I have -searched for pleasure." - -"And found it, Mr. Gray?" - -"Often. Too often." - -The duchess sighed. "I am searching for peace," she said, "and if I -don't go and dress, I shall have none this evening." - -"Let me get you some orchids, Duchess," cried Dorian, starting to his -feet and walking down the conservatory. - -"You are flirting disgracefully with him," said Lord Henry to his -cousin. "You had better take care. He is very fascinating." - -"If he were not, there would be no battle." - -"Greek meets Greek, then?" - -"I am on the side of the Trojans. They fought for a woman." - -"They were defeated." - -"There are worse things than capture," she answered. - -"You gallop with a loose rein." - -"Pace gives life," was the _riposte_. - -"I shall write it in my diary to-night." - -"What?" - -"That a burnt child loves the fire." - -"I am not even singed. My wings are untouched." - -"You use them for everything, except flight." - -"Courage has passed from men to women. It is a new experience for us." - -"You have a rival." - -"Who?" - -He laughed. "Lady Narborough," he whispered. "She perfectly adores -him." - -"You fill me with apprehension. The appeal to antiquity is fatal to us -who are romanticists." - -"Romanticists! You have all the methods of science." - -"Men have educated us." - -"But not explained you." - -"Describe us as a sex," was her challenge. - -"Sphinxes without secrets." - -She looked at him, smiling. "How long Mr. Gray is!" she said. "Let us -go and help him. I have not yet told him the colour of my frock." - -"Ah! you must suit your frock to his flowers, Gladys." - -"That would be a premature surrender." - -"Romantic art begins with its climax." - -"I must keep an opportunity for retreat." - -"In the Parthian manner?" - -"They found safety in the desert. I could not do that." - -"Women are not always allowed a choice," he answered, but hardly had he -finished the sentence before from the far end of the conservatory came -a stifled groan, followed by the dull sound of a heavy fall. Everybody -started up. The duchess stood motionless in horror. And with fear in -his eyes, Lord Henry rushed through the flapping palms to find Dorian -Gray lying face downwards on the tiled floor in a deathlike swoon. - -He was carried at once into the blue drawing-room and laid upon one of -the sofas. After a short time, he came to himself and looked round -with a dazed expression. - -"What has happened?" he asked. "Oh! I remember. Am I safe here, -Harry?" He began to tremble. - -"My dear Dorian," answered Lord Henry, "you merely fainted. That was -all. You must have overtired yourself. You had better not come down -to dinner. I will take your place." - -"No, I will come down," he said, struggling to his feet. "I would -rather come down. I must not be alone." - -He went to his room and dressed. There was a wild recklessness of -gaiety in his manner as he sat at table, but now and then a thrill of -terror ran through him when he remembered that, pressed against the -window of the conservatory, like a white handkerchief, he had seen the -face of James Vane watching him. - - - -CHAPTER 18 - -The next day he did not leave the house, and, indeed, spent most of the -time in his own room, sick with a wild terror of dying, and yet -indifferent to life itself. The consciousness of being hunted, snared, -tracked down, had begun to dominate him. If the tapestry did but -tremble in the wind, he shook. The dead leaves that were blown against -the leaded panes seemed to him like his own wasted resolutions and wild -regrets. When he closed his eyes, he saw again the sailor's face -peering through the mist-stained glass, and horror seemed once more to -lay its hand upon his heart. - -But perhaps it had been only his fancy that had called vengeance out of -the night and set the hideous shapes of punishment before him. Actual -life was chaos, but there was something terribly logical in the -imagination. It was the imagination that set remorse to dog the feet -of sin. It was the imagination that made each crime bear its misshapen -brood. In the common world of fact the wicked were not punished, nor -the good rewarded. Success was given to the strong, failure thrust -upon the weak. That was all. Besides, had any stranger been prowling -round the house, he would have been seen by the servants or the -keepers. Had any foot-marks been found on the flower-beds, the -gardeners would have reported it. Yes, it had been merely fancy. -Sibyl Vane's brother had not come back to kill him. He had sailed away -in his ship to founder in some winter sea. From him, at any rate, he -was safe. Why, the man did not know who he was, could not know who he -was. The mask of youth had saved him. - -And yet if it had been merely an illusion, how terrible it was to think -that conscience could raise such fearful phantoms, and give them -visible form, and make them move before one! What sort of life would -his be if, day and night, shadows of his crime were to peer at him from -silent corners, to mock him from secret places, to whisper in his ear -as he sat at the feast, to wake him with icy fingers as he lay asleep! -As the thought crept through his brain, he grew pale with terror, and -the air seemed to him to have become suddenly colder. Oh! in what a -wild hour of madness he had killed his friend! How ghastly the mere -memory of the scene! He saw it all again. Each hideous detail came -back to him with added horror. Out of the black cave of time, terrible -and swathed in scarlet, rose the image of his sin. When Lord Henry -came in at six o'clock, he found him crying as one whose heart will -break. - -It was not till the third day that he ventured to go out. There was -something in the clear, pine-scented air of that winter morning that -seemed to bring him back his joyousness and his ardour for life. But -it was not merely the physical conditions of environment that had -caused the change. His own nature had revolted against the excess of -anguish that had sought to maim and mar the perfection of its calm. -With subtle and finely wrought temperaments it is always so. Their -strong passions must either bruise or bend. They either slay the man, -or themselves die. Shallow sorrows and shallow loves live on. The -loves and sorrows that are great are destroyed by their own plenitude. -Besides, he had convinced himself that he had been the victim of a -terror-stricken imagination, and looked back now on his fears with -something of pity and not a little of contempt. - -After breakfast, he walked with the duchess for an hour in the garden -and then drove across the park to join the shooting-party. The crisp -frost lay like salt upon the grass. The sky was an inverted cup of -blue metal. A thin film of ice bordered the flat, reed-grown lake. - -At the corner of the pine-wood he caught sight of Sir Geoffrey -Clouston, the duchess's brother, jerking two spent cartridges out of -his gun. He jumped from the cart, and having told the groom to take -the mare home, made his way towards his guest through the withered -bracken and rough undergrowth. - -"Have you had good sport, Geoffrey?" he asked. - -"Not very good, Dorian. I think most of the birds have gone to the -open. I dare say it will be better after lunch, when we get to new -ground." - -Dorian strolled along by his side. The keen aromatic air, the brown -and red lights that glimmered in the wood, the hoarse cries of the -beaters ringing out from time to time, and the sharp snaps of the guns -that followed, fascinated him and filled him with a sense of delightful -freedom. He was dominated by the carelessness of happiness, by the -high indifference of joy. - -Suddenly from a lumpy tussock of old grass some twenty yards in front -of them, with black-tipped ears erect and long hinder limbs throwing it -forward, started a hare. It bolted for a thicket of alders. Sir -Geoffrey put his gun to his shoulder, but there was something in the -animal's grace of movement that strangely charmed Dorian Gray, and he -cried out at once, "Don't shoot it, Geoffrey. Let it live." - -"What nonsense, Dorian!" laughed his companion, and as the hare bounded -into the thicket, he fired. There were two cries heard, the cry of a -hare in pain, which is dreadful, the cry of a man in agony, which is -worse. - -"Good heavens! I have hit a beater!" exclaimed Sir Geoffrey. "What an -ass the man was to get in front of the guns! Stop shooting there!" he -called out at the top of his voice. "A man is hurt." - -The head-keeper came running up with a stick in his hand. - -"Where, sir? Where is he?" he shouted. At the same time, the firing -ceased along the line. - -"Here," answered Sir Geoffrey angrily, hurrying towards the thicket. -"Why on earth don't you keep your men back? Spoiled my shooting for -the day." - -Dorian watched them as they plunged into the alder-clump, brushing the -lithe swinging branches aside. In a few moments they emerged, dragging -a body after them into the sunlight. He turned away in horror. It -seemed to him that misfortune followed wherever he went. He heard Sir -Geoffrey ask if the man was really dead, and the affirmative answer of -the keeper. The wood seemed to him to have become suddenly alive with -faces. There was the trampling of myriad feet and the low buzz of -voices. A great copper-breasted pheasant came beating through the -boughs overhead. - -After a few moments--that were to him, in his perturbed state, like -endless hours of pain--he felt a hand laid on his shoulder. He started -and looked round. - -"Dorian," said Lord Henry, "I had better tell them that the shooting is -stopped for to-day. It would not look well to go on." - -"I wish it were stopped for ever, Harry," he answered bitterly. "The -whole thing is hideous and cruel. Is the man ...?" - -He could not finish the sentence. - -"I am afraid so," rejoined Lord Henry. "He got the whole charge of -shot in his chest. He must have died almost instantaneously. Come; -let us go home." - -They walked side by side in the direction of the avenue for nearly -fifty yards without speaking. Then Dorian looked at Lord Henry and -said, with a heavy sigh, "It is a bad omen, Harry, a very bad omen." - -"What is?" asked Lord Henry. "Oh! this accident, I suppose. My dear -fellow, it can't be helped. It was the man's own fault. Why did he -get in front of the guns? Besides, it is nothing to us. It is rather -awkward for Geoffrey, of course. It does not do to pepper beaters. It -makes people think that one is a wild shot. And Geoffrey is not; he -shoots very straight. But there is no use talking about the matter." - -Dorian shook his head. "It is a bad omen, Harry. I feel as if -something horrible were going to happen to some of us. To myself, -perhaps," he added, passing his hand over his eyes, with a gesture of -pain. - -The elder man laughed. "The only horrible thing in the world is _ennui_, -Dorian. That is the one sin for which there is no forgiveness. But we -are not likely to suffer from it unless these fellows keep chattering -about this thing at dinner. I must tell them that the subject is to be -tabooed. As for omens, there is no such thing as an omen. Destiny -does not send us heralds. She is too wise or too cruel for that. -Besides, what on earth could happen to you, Dorian? You have -everything in the world that a man can want. There is no one who would -not be delighted to change places with you." - -"There is no one with whom I would not change places, Harry. Don't -laugh like that. I am telling you the truth. The wretched peasant who -has just died is better off than I am. I have no terror of death. It -is the coming of death that terrifies me. Its monstrous wings seem to -wheel in the leaden air around me. Good heavens! don't you see a man -moving behind the trees there, watching me, waiting for me?" - -Lord Henry looked in the direction in which the trembling gloved hand -was pointing. "Yes," he said, smiling, "I see the gardener waiting for -you. I suppose he wants to ask you what flowers you wish to have on -the table to-night. How absurdly nervous you are, my dear fellow! You -must come and see my doctor, when we get back to town." - -Dorian heaved a sigh of relief as he saw the gardener approaching. The -man touched his hat, glanced for a moment at Lord Henry in a hesitating -manner, and then produced a letter, which he handed to his master. -"Her Grace told me to wait for an answer," he murmured. - -Dorian put the letter into his pocket. "Tell her Grace that I am -coming in," he said, coldly. The man turned round and went rapidly in -the direction of the house. - -"How fond women are of doing dangerous things!" laughed Lord Henry. -"It is one of the qualities in them that I admire most. A woman will -flirt with anybody in the world as long as other people are looking on." - -"How fond you are of saying dangerous things, Harry! In the present -instance, you are quite astray. I like the duchess very much, but I -don't love her." - -"And the duchess loves you very much, but she likes you less, so you -are excellently matched." - -"You are talking scandal, Harry, and there is never any basis for -scandal." - -"The basis of every scandal is an immoral certainty," said Lord Henry, -lighting a cigarette. - -"You would sacrifice anybody, Harry, for the sake of an epigram." - -"The world goes to the altar of its own accord," was the answer. - -"I wish I could love," cried Dorian Gray with a deep note of pathos in -his voice. "But I seem to have lost the passion and forgotten the -desire. I am too much concentrated on myself. My own personality has -become a burden to me. I want to escape, to go away, to forget. It -was silly of me to come down here at all. I think I shall send a wire -to Harvey to have the yacht got ready. On a yacht one is safe." - -"Safe from what, Dorian? You are in some trouble. Why not tell me -what it is? You know I would help you." - -"I can't tell you, Harry," he answered sadly. "And I dare say it is -only a fancy of mine. This unfortunate accident has upset me. I have -a horrible presentiment that something of the kind may happen to me." - -"What nonsense!" - -"I hope it is, but I can't help feeling it. Ah! here is the duchess, -looking like Artemis in a tailor-made gown. You see we have come back, -Duchess." - -"I have heard all about it, Mr. Gray," she answered. "Poor Geoffrey is -terribly upset. And it seems that you asked him not to shoot the hare. -How curious!" - -"Yes, it was very curious. I don't know what made me say it. Some -whim, I suppose. It looked the loveliest of little live things. But I -am sorry they told you about the man. It is a hideous subject." - -"It is an annoying subject," broke in Lord Henry. "It has no -psychological value at all. Now if Geoffrey had done the thing on -purpose, how interesting he would be! I should like to know some one -who had committed a real murder." - -"How horrid of you, Harry!" cried the duchess. "Isn't it, Mr. Gray? -Harry, Mr. Gray is ill again. He is going to faint." - -Dorian drew himself up with an effort and smiled. "It is nothing, -Duchess," he murmured; "my nerves are dreadfully out of order. That is -all. I am afraid I walked too far this morning. I didn't hear what -Harry said. Was it very bad? You must tell me some other time. I -think I must go and lie down. You will excuse me, won't you?" - -They had reached the great flight of steps that led from the -conservatory on to the terrace. As the glass door closed behind -Dorian, Lord Henry turned and looked at the duchess with his slumberous -eyes. "Are you very much in love with him?" he asked. - -She did not answer for some time, but stood gazing at the landscape. -"I wish I knew," she said at last. - -He shook his head. "Knowledge would be fatal. It is the uncertainty -that charms one. A mist makes things wonderful." - -"One may lose one's way." - -"All ways end at the same point, my dear Gladys." - -"What is that?" - -"Disillusion." - -"It was my _debut_ in life," she sighed. - -"It came to you crowned." - -"I am tired of strawberry leaves." - -"They become you." - -"Only in public." - -"You would miss them," said Lord Henry. - -"I will not part with a petal." - -"Monmouth has ears." - -"Old age is dull of hearing." - -"Has he never been jealous?" - -"I wish he had been." - -He glanced about as if in search of something. "What are you looking -for?" she inquired. - -"The button from your foil," he answered. "You have dropped it." - -She laughed. "I have still the mask." - -"It makes your eyes lovelier," was his reply. - -She laughed again. Her teeth showed like white seeds in a scarlet -fruit. - -Upstairs, in his own room, Dorian Gray was lying on a sofa, with terror -in every tingling fibre of his body. Life had suddenly become too -hideous a burden for him to bear. The dreadful death of the unlucky -beater, shot in the thicket like a wild animal, had seemed to him to -pre-figure death for himself also. He had nearly swooned at what Lord -Henry had said in a chance mood of cynical jesting. - -At five o'clock he rang his bell for his servant and gave him orders to -pack his things for the night-express to town, and to have the brougham -at the door by eight-thirty. He was determined not to sleep another -night at Selby Royal. It was an ill-omened place. Death walked there -in the sunlight. The grass of the forest had been spotted with blood. - -Then he wrote a note to Lord Henry, telling him that he was going up to -town to consult his doctor and asking him to entertain his guests in -his absence. As he was putting it into the envelope, a knock came to -the door, and his valet informed him that the head-keeper wished to see -him. He frowned and bit his lip. "Send him in," he muttered, after -some moments' hesitation. - -As soon as the man entered, Dorian pulled his chequebook out of a -drawer and spread it out before him. - -"I suppose you have come about the unfortunate accident of this -morning, Thornton?" he said, taking up a pen. - -"Yes, sir," answered the gamekeeper. - -"Was the poor fellow married? Had he any people dependent on him?" -asked Dorian, looking bored. "If so, I should not like them to be left -in want, and will send them any sum of money you may think necessary." - -"We don't know who he is, sir. That is what I took the liberty of -coming to you about." - -"Don't know who he is?" said Dorian, listlessly. "What do you mean? -Wasn't he one of your men?" - -"No, sir. Never saw him before. Seems like a sailor, sir." - -The pen dropped from Dorian Gray's hand, and he felt as if his heart -had suddenly stopped beating. "A sailor?" he cried out. "Did you say -a sailor?" - -"Yes, sir. He looks as if he had been a sort of sailor; tattooed on -both arms, and that kind of thing." - -"Was there anything found on him?" said Dorian, leaning forward and -looking at the man with startled eyes. "Anything that would tell his -name?" - -"Some money, sir--not much, and a six-shooter. There was no name of any -kind. A decent-looking man, sir, but rough-like. A sort of sailor we -think." - -Dorian started to his feet. A terrible hope fluttered past him. He -clutched at it madly. "Where is the body?" he exclaimed. "Quick! I -must see it at once." - -"It is in an empty stable in the Home Farm, sir. The folk don't like -to have that sort of thing in their houses. They say a corpse brings -bad luck." - -"The Home Farm! Go there at once and meet me. Tell one of the grooms -to bring my horse round. No. Never mind. I'll go to the stables -myself. It will save time." - -In less than a quarter of an hour, Dorian Gray was galloping down the -long avenue as hard as he could go. The trees seemed to sweep past him -in spectral procession, and wild shadows to fling themselves across his -path. Once the mare swerved at a white gate-post and nearly threw him. -He lashed her across the neck with his crop. She cleft the dusky air -like an arrow. The stones flew from her hoofs. - -At last he reached the Home Farm. Two men were loitering in the yard. -He leaped from the saddle and threw the reins to one of them. In the -farthest stable a light was glimmering. Something seemed to tell him -that the body was there, and he hurried to the door and put his hand -upon the latch. - -There he paused for a moment, feeling that he was on the brink of a -discovery that would either make or mar his life. Then he thrust the -door open and entered. - -On a heap of sacking in the far corner was lying the dead body of a man -dressed in a coarse shirt and a pair of blue trousers. A spotted -handkerchief had been placed over the face. A coarse candle, stuck in -a bottle, sputtered beside it. - -Dorian Gray shuddered. He felt that his could not be the hand to take -the handkerchief away, and called out to one of the farm-servants to -come to him. - -"Take that thing off the face. I wish to see it," he said, clutching -at the door-post for support. - -When the farm-servant had done so, he stepped forward. A cry of joy -broke from his lips. The man who had been shot in the thicket was -James Vane. - -He stood there for some minutes looking at the dead body. As he rode -home, his eyes were full of tears, for he knew he was safe. - - - -CHAPTER 19 - -"There is no use your telling me that you are going to be good," cried -Lord Henry, dipping his white fingers into a red copper bowl filled -with rose-water. "You are quite perfect. Pray, don't change." - -Dorian Gray shook his head. "No, Harry, I have done too many dreadful -things in my life. I am not going to do any more. I began my good -actions yesterday." - -"Where were you yesterday?" - -"In the country, Harry. I was staying at a little inn by myself." - -"My dear boy," said Lord Henry, smiling, "anybody can be good in the -country. There are no temptations there. That is the reason why -people who live out of town are so absolutely uncivilized. -Civilization is not by any means an easy thing to attain to. There are -only two ways by which man can reach it. One is by being cultured, the -other by being corrupt. Country people have no opportunity of being -either, so they stagnate." - -"Culture and corruption," echoed Dorian. "I have known something of -both. It seems terrible to me now that they should ever be found -together. For I have a new ideal, Harry. I am going to alter. I -think I have altered." - -"You have not yet told me what your good action was. Or did you say -you had done more than one?" asked his companion as he spilled into his -plate a little crimson pyramid of seeded strawberries and, through a -perforated, shell-shaped spoon, snowed white sugar upon them. - -"I can tell you, Harry. It is not a story I could tell to any one -else. I spared somebody. It sounds vain, but you understand what I -mean. She was quite beautiful and wonderfully like Sibyl Vane. I -think it was that which first attracted me to her. You remember Sibyl, -don't you? How long ago that seems! Well, Hetty was not one of our -own class, of course. She was simply a girl in a village. But I -really loved her. I am quite sure that I loved her. All during this -wonderful May that we have been having, I used to run down and see her -two or three times a week. Yesterday she met me in a little orchard. -The apple-blossoms kept tumbling down on her hair, and she was -laughing. We were to have gone away together this morning at dawn. -Suddenly I determined to leave her as flowerlike as I had found her." - -"I should think the novelty of the emotion must have given you a thrill -of real pleasure, Dorian," interrupted Lord Henry. "But I can finish -your idyll for you. You gave her good advice and broke her heart. -That was the beginning of your reformation." - -"Harry, you are horrible! You mustn't say these dreadful things. -Hetty's heart is not broken. Of course, she cried and all that. But -there is no disgrace upon her. She can live, like Perdita, in her -garden of mint and marigold." - -"And weep over a faithless Florizel," said Lord Henry, laughing, as he -leaned back in his chair. "My dear Dorian, you have the most curiously -boyish moods. Do you think this girl will ever be really content now -with any one of her own rank? I suppose she will be married some day -to a rough carter or a grinning ploughman. Well, the fact of having -met you, and loved you, will teach her to despise her husband, and she -will be wretched. From a moral point of view, I cannot say that I -think much of your great renunciation. Even as a beginning, it is -poor. Besides, how do you know that Hetty isn't floating at the -present moment in some starlit mill-pond, with lovely water-lilies -round her, like Ophelia?" - -"I can't bear this, Harry! You mock at everything, and then suggest -the most serious tragedies. I am sorry I told you now. I don't care -what you say to me. I know I was right in acting as I did. Poor -Hetty! As I rode past the farm this morning, I saw her white face at -the window, like a spray of jasmine. Don't let us talk about it any -more, and don't try to persuade me that the first good action I have -done for years, the first little bit of self-sacrifice I have ever -known, is really a sort of sin. I want to be better. I am going to be -better. Tell me something about yourself. What is going on in town? -I have not been to the club for days." - -"The people are still discussing poor Basil's disappearance." - -"I should have thought they had got tired of that by this time," said -Dorian, pouring himself out some wine and frowning slightly. - -"My dear boy, they have only been talking about it for six weeks, and -the British public are really not equal to the mental strain of having -more than one topic every three months. They have been very fortunate -lately, however. They have had my own divorce-case and Alan Campbell's -suicide. Now they have got the mysterious disappearance of an artist. -Scotland Yard still insists that the man in the grey ulster who left -for Paris by the midnight train on the ninth of November was poor -Basil, and the French police declare that Basil never arrived in Paris -at all. I suppose in about a fortnight we shall be told that he has -been seen in San Francisco. It is an odd thing, but every one who -disappears is said to be seen at San Francisco. It must be a -delightful city, and possess all the attractions of the next world." - -"What do you think has happened to Basil?" asked Dorian, holding up his -Burgundy against the light and wondering how it was that he could -discuss the matter so calmly. - -"I have not the slightest idea. If Basil chooses to hide himself, it -is no business of mine. If he is dead, I don't want to think about -him. Death is the only thing that ever terrifies me. I hate it." - -"Why?" said the younger man wearily. - -"Because," said Lord Henry, passing beneath his nostrils the gilt -trellis of an open vinaigrette box, "one can survive everything -nowadays except that. Death and vulgarity are the only two facts in -the nineteenth century that one cannot explain away. Let us have our -coffee in the music-room, Dorian. You must play Chopin to me. The man -with whom my wife ran away played Chopin exquisitely. Poor Victoria! -I was very fond of her. The house is rather lonely without her. Of -course, married life is merely a habit, a bad habit. But then one -regrets the loss even of one's worst habits. Perhaps one regrets them -the most. They are such an essential part of one's personality." - -Dorian said nothing, but rose from the table, and passing into the next -room, sat down to the piano and let his fingers stray across the white -and black ivory of the keys. After the coffee had been brought in, he -stopped, and looking over at Lord Henry, said, "Harry, did it ever -occur to you that Basil was murdered?" - -Lord Henry yawned. "Basil was very popular, and always wore a -Waterbury watch. Why should he have been murdered? He was not clever -enough to have enemies. Of course, he had a wonderful genius for -painting. But a man can paint like Velasquez and yet be as dull as -possible. Basil was really rather dull. He only interested me once, -and that was when he told me, years ago, that he had a wild adoration -for you and that you were the dominant motive of his art." - -"I was very fond of Basil," said Dorian with a note of sadness in his -voice. "But don't people say that he was murdered?" - -"Oh, some of the papers do. It does not seem to me to be at all -probable. I know there are dreadful places in Paris, but Basil was not -the sort of man to have gone to them. He had no curiosity. It was his -chief defect." - -"What would you say, Harry, if I told you that I had murdered Basil?" -said the younger man. He watched him intently after he had spoken. - -"I would say, my dear fellow, that you were posing for a character that -doesn't suit you. All crime is vulgar, just as all vulgarity is crime. -It is not in you, Dorian, to commit a murder. I am sorry if I hurt -your vanity by saying so, but I assure you it is true. Crime belongs -exclusively to the lower orders. I don't blame them in the smallest -degree. I should fancy that crime was to them what art is to us, -simply a method of procuring extraordinary sensations." - -"A method of procuring sensations? Do you think, then, that a man who -has once committed a murder could possibly do the same crime again? -Don't tell me that." - -"Oh! anything becomes a pleasure if one does it too often," cried Lord -Henry, laughing. "That is one of the most important secrets of life. -I should fancy, however, that murder is always a mistake. One should -never do anything that one cannot talk about after dinner. But let us -pass from poor Basil. I wish I could believe that he had come to such -a really romantic end as you suggest, but I can't. I dare say he fell -into the Seine off an omnibus and that the conductor hushed up the -scandal. Yes: I should fancy that was his end. I see him lying now -on his back under those dull-green waters, with the heavy barges -floating over him and long weeds catching in his hair. Do you know, I -don't think he would have done much more good work. During the last -ten years his painting had gone off very much." - -Dorian heaved a sigh, and Lord Henry strolled across the room and began -to stroke the head of a curious Java parrot, a large, grey-plumaged -bird with pink crest and tail, that was balancing itself upon a bamboo -perch. As his pointed fingers touched it, it dropped the white scurf -of crinkled lids over black, glasslike eyes and began to sway backwards -and forwards. - -"Yes," he continued, turning round and taking his handkerchief out of -his pocket; "his painting had quite gone off. It seemed to me to have -lost something. It had lost an ideal. When you and he ceased to be -great friends, he ceased to be a great artist. What was it separated -you? I suppose he bored you. If so, he never forgave you. It's a -habit bores have. By the way, what has become of that wonderful -portrait he did of you? I don't think I have ever seen it since he -finished it. Oh! I remember your telling me years ago that you had -sent it down to Selby, and that it had got mislaid or stolen on the -way. You never got it back? What a pity! it was really a -masterpiece. I remember I wanted to buy it. I wish I had now. It -belonged to Basil's best period. Since then, his work was that curious -mixture of bad painting and good intentions that always entitles a man -to be called a representative British artist. Did you advertise for -it? You should." - -"I forget," said Dorian. "I suppose I did. But I never really liked -it. I am sorry I sat for it. The memory of the thing is hateful to -me. Why do you talk of it? It used to remind me of those curious -lines in some play--Hamlet, I think--how do they run?-- - - "Like the painting of a sorrow, - A face without a heart." - -Yes: that is what it was like." - -Lord Henry laughed. "If a man treats life artistically, his brain is -his heart," he answered, sinking into an arm-chair. - -Dorian Gray shook his head and struck some soft chords on the piano. -"'Like the painting of a sorrow,'" he repeated, "'a face without a -heart.'" - -The elder man lay back and looked at him with half-closed eyes. "By -the way, Dorian," he said after a pause, "'what does it profit a man if -he gain the whole world and lose--how does the quotation run?--his own -soul'?" - -The music jarred, and Dorian Gray started and stared at his friend. -"Why do you ask me that, Harry?" - -"My dear fellow," said Lord Henry, elevating his eyebrows in surprise, -"I asked you because I thought you might be able to give me an answer. -That is all. I was going through the park last Sunday, and close by -the Marble Arch there stood a little crowd of shabby-looking people -listening to some vulgar street-preacher. As I passed by, I heard the -man yelling out that question to his audience. It struck me as being -rather dramatic. London is very rich in curious effects of that kind. -A wet Sunday, an uncouth Christian in a mackintosh, a ring of sickly -white faces under a broken roof of dripping umbrellas, and a wonderful -phrase flung into the air by shrill hysterical lips--it was really very -good in its way, quite a suggestion. I thought of telling the prophet -that art had a soul, but that man had not. I am afraid, however, he -would not have understood me." - -"Don't, Harry. The soul is a terrible reality. It can be bought, and -sold, and bartered away. It can be poisoned, or made perfect. There -is a soul in each one of us. I know it." - -"Do you feel quite sure of that, Dorian?" - -"Quite sure." - -"Ah! then it must be an illusion. The things one feels absolutely -certain about are never true. That is the fatality of faith, and the -lesson of romance. How grave you are! Don't be so serious. What have -you or I to do with the superstitions of our age? No: we have given -up our belief in the soul. Play me something. Play me a nocturne, -Dorian, and, as you play, tell me, in a low voice, how you have kept -your youth. You must have some secret. I am only ten years older than -you are, and I am wrinkled, and worn, and yellow. You are really -wonderful, Dorian. You have never looked more charming than you do -to-night. You remind me of the day I saw you first. You were rather -cheeky, very shy, and absolutely extraordinary. You have changed, of -course, but not in appearance. I wish you would tell me your secret. -To get back my youth I would do anything in the world, except take -exercise, get up early, or be respectable. Youth! There is nothing -like it. It's absurd to talk of the ignorance of youth. The only -people to whose opinions I listen now with any respect are people much -younger than myself. They seem in front of me. Life has revealed to -them her latest wonder. As for the aged, I always contradict the aged. -I do it on principle. If you ask them their opinion on something that -happened yesterday, they solemnly give you the opinions current in -1820, when people wore high stocks, believed in everything, and knew -absolutely nothing. How lovely that thing you are playing is! I -wonder, did Chopin write it at Majorca, with the sea weeping round the -villa and the salt spray dashing against the panes? It is marvellously -romantic. What a blessing it is that there is one art left to us that -is not imitative! Don't stop. I want music to-night. It seems to me -that you are the young Apollo and that I am Marsyas listening to you. -I have sorrows, Dorian, of my own, that even you know nothing of. The -tragedy of old age is not that one is old, but that one is young. I am -amazed sometimes at my own sincerity. Ah, Dorian, how happy you are! -What an exquisite life you have had! You have drunk deeply of -everything. You have crushed the grapes against your palate. Nothing -has been hidden from you. And it has all been to you no more than the -sound of music. It has not marred you. You are still the same." - -"I am not the same, Harry." - -"Yes, you are the same. I wonder what the rest of your life will be. -Don't spoil it by renunciations. At present you are a perfect type. -Don't make yourself incomplete. You are quite flawless now. You need -not shake your head: you know you are. Besides, Dorian, don't deceive -yourself. Life is not governed by will or intention. Life is a -question of nerves, and fibres, and slowly built-up cells in which -thought hides itself and passion has its dreams. You may fancy -yourself safe and think yourself strong. But a chance tone of colour -in a room or a morning sky, a particular perfume that you had once -loved and that brings subtle memories with it, a line from a forgotten -poem that you had come across again, a cadence from a piece of music -that you had ceased to play--I tell you, Dorian, that it is on things -like these that our lives depend. Browning writes about that -somewhere; but our own senses will imagine them for us. There are -moments when the odour of _lilas blanc_ passes suddenly across me, and I -have to live the strangest month of my life over again. I wish I could -change places with you, Dorian. The world has cried out against us -both, but it has always worshipped you. It always will worship you. -You are the type of what the age is searching for, and what it is -afraid it has found. I am so glad that you have never done anything, -never carved a statue, or painted a picture, or produced anything -outside of yourself! Life has been your art. You have set yourself to -music. Your days are your sonnets." - -Dorian rose up from the piano and passed his hand through his hair. -"Yes, life has been exquisite," he murmured, "but I am not going to -have the same life, Harry. And you must not say these extravagant -things to me. You don't know everything about me. I think that if you -did, even you would turn from me. You laugh. Don't laugh." - -"Why have you stopped playing, Dorian? Go back and give me the -nocturne over again. Look at that great, honey-coloured moon that -hangs in the dusky air. She is waiting for you to charm her, and if -you play she will come closer to the earth. You won't? Let us go to -the club, then. It has been a charming evening, and we must end it -charmingly. There is some one at White's who wants immensely to know -you--young Lord Poole, Bournemouth's eldest son. He has already copied -your neckties, and has begged me to introduce him to you. He is quite -delightful and rather reminds me of you." - -"I hope not," said Dorian with a sad look in his eyes. "But I am tired -to-night, Harry. I shan't go to the club. It is nearly eleven, and I -want to go to bed early." - -"Do stay. You have never played so well as to-night. There was -something in your touch that was wonderful. It had more expression -than I had ever heard from it before." - -"It is because I am going to be good," he answered, smiling. "I am a -little changed already." - -"You cannot change to me, Dorian," said Lord Henry. "You and I will -always be friends." - -"Yet you poisoned me with a book once. I should not forgive that. -Harry, promise me that you will never lend that book to any one. It -does harm." - -"My dear boy, you are really beginning to moralize. You will soon be -going about like the converted, and the revivalist, warning people -against all the sins of which you have grown tired. You are much too -delightful to do that. Besides, it is no use. You and I are what we -are, and will be what we will be. As for being poisoned by a book, -there is no such thing as that. Art has no influence upon action. It -annihilates the desire to act. It is superbly sterile. The books that -the world calls immoral are books that show the world its own shame. -That is all. But we won't discuss literature. Come round to-morrow. I -am going to ride at eleven. We might go together, and I will take you -to lunch afterwards with Lady Branksome. She is a charming woman, and -wants to consult you about some tapestries she is thinking of buying. -Mind you come. Or shall we lunch with our little duchess? She says -she never sees you now. Perhaps you are tired of Gladys? I thought -you would be. Her clever tongue gets on one's nerves. Well, in any -case, be here at eleven." - -"Must I really come, Harry?" - -"Certainly. The park is quite lovely now. I don't think there have -been such lilacs since the year I met you." - -"Very well. I shall be here at eleven," said Dorian. "Good night, -Harry." As he reached the door, he hesitated for a moment, as if he -had something more to say. Then he sighed and went out. - - - -CHAPTER 20 - -It was a lovely night, so warm that he threw his coat over his arm and -did not even put his silk scarf round his throat. As he strolled home, -smoking his cigarette, two young men in evening dress passed him. He -heard one of them whisper to the other, "That is Dorian Gray." He -remembered how pleased he used to be when he was pointed out, or stared -at, or talked about. He was tired of hearing his own name now. Half -the charm of the little village where he had been so often lately was -that no one knew who he was. He had often told the girl whom he had -lured to love him that he was poor, and she had believed him. He had -told her once that he was wicked, and she had laughed at him and -answered that wicked people were always very old and very ugly. What a -laugh she had!--just like a thrush singing. And how pretty she had -been in her cotton dresses and her large hats! She knew nothing, but -she had everything that he had lost. - -When he reached home, he found his servant waiting up for him. He sent -him to bed, and threw himself down on the sofa in the library, and -began to think over some of the things that Lord Henry had said to him. - -Was it really true that one could never change? He felt a wild longing -for the unstained purity of his boyhood--his rose-white boyhood, as -Lord Henry had once called it. He knew that he had tarnished himself, -filled his mind with corruption and given horror to his fancy; that he -had been an evil influence to others, and had experienced a terrible -joy in being so; and that of the lives that had crossed his own, it had -been the fairest and the most full of promise that he had brought to -shame. But was it all irretrievable? Was there no hope for him? - -Ah! in what a monstrous moment of pride and passion he had prayed that -the portrait should bear the burden of his days, and he keep the -unsullied splendour of eternal youth! All his failure had been due to -that. Better for him that each sin of his life had brought its sure -swift penalty along with it. There was purification in punishment. -Not "Forgive us our sins" but "Smite us for our iniquities" should be -the prayer of man to a most just God. - -The curiously carved mirror that Lord Henry had given to him, so many -years ago now, was standing on the table, and the white-limbed Cupids -laughed round it as of old. He took it up, as he had done on that -night of horror when he had first noted the change in the fatal -picture, and with wild, tear-dimmed eyes looked into its polished -shield. Once, some one who had terribly loved him had written to him a -mad letter, ending with these idolatrous words: "The world is changed -because you are made of ivory and gold. The curves of your lips -rewrite history." The phrases came back to his memory, and he repeated -them over and over to himself. Then he loathed his own beauty, and -flinging the mirror on the floor, crushed it into silver splinters -beneath his heel. It was his beauty that had ruined him, his beauty -and the youth that he had prayed for. But for those two things, his -life might have been free from stain. His beauty had been to him but a -mask, his youth but a mockery. What was youth at best? A green, an -unripe time, a time of shallow moods, and sickly thoughts. Why had he -worn its livery? Youth had spoiled him. - -It was better not to think of the past. Nothing could alter that. It -was of himself, and of his own future, that he had to think. James -Vane was hidden in a nameless grave in Selby churchyard. Alan Campbell -had shot himself one night in his laboratory, but had not revealed the -secret that he had been forced to know. The excitement, such as it -was, over Basil Hallward's disappearance would soon pass away. It was -already waning. He was perfectly safe there. Nor, indeed, was it the -death of Basil Hallward that weighed most upon his mind. It was the -living death of his own soul that troubled him. Basil had painted the -portrait that had marred his life. He could not forgive him that. It -was the portrait that had done everything. Basil had said things to -him that were unbearable, and that he had yet borne with patience. The -murder had been simply the madness of a moment. As for Alan Campbell, -his suicide had been his own act. He had chosen to do it. It was -nothing to him. - -A new life! That was what he wanted. That was what he was waiting -for. Surely he had begun it already. He had spared one innocent -thing, at any rate. He would never again tempt innocence. He would be -good. - -As he thought of Hetty Merton, he began to wonder if the portrait in -the locked room had changed. Surely it was not still so horrible as it -had been? Perhaps if his life became pure, he would be able to expel -every sign of evil passion from the face. Perhaps the signs of evil -had already gone away. He would go and look. - -He took the lamp from the table and crept upstairs. As he unbarred the -door, a smile of joy flitted across his strangely young-looking face -and lingered for a moment about his lips. Yes, he would be good, and -the hideous thing that he had hidden away would no longer be a terror -to him. He felt as if the load had been lifted from him already. - -He went in quietly, locking the door behind him, as was his custom, and -dragged the purple hanging from the portrait. A cry of pain and -indignation broke from him. He could see no change, save that in the -eyes there was a look of cunning and in the mouth the curved wrinkle of -the hypocrite. The thing was still loathsome--more loathsome, if -possible, than before--and the scarlet dew that spotted the hand seemed -brighter, and more like blood newly spilled. Then he trembled. Had it -been merely vanity that had made him do his one good deed? Or the -desire for a new sensation, as Lord Henry had hinted, with his mocking -laugh? Or that passion to act a part that sometimes makes us do things -finer than we are ourselves? Or, perhaps, all these? And why was the -red stain larger than it had been? It seemed to have crept like a -horrible disease over the wrinkled fingers. There was blood on the -painted feet, as though the thing had dripped--blood even on the hand -that had not held the knife. Confess? Did it mean that he was to -confess? To give himself up and be put to death? He laughed. He felt -that the idea was monstrous. Besides, even if he did confess, who -would believe him? There was no trace of the murdered man anywhere. -Everything belonging to him had been destroyed. He himself had burned -what had been below-stairs. The world would simply say that he was mad. -They would shut him up if he persisted in his story.... Yet it was -his duty to confess, to suffer public shame, and to make public -atonement. There was a God who called upon men to tell their sins to -earth as well as to heaven. Nothing that he could do would cleanse him -till he had told his own sin. His sin? He shrugged his shoulders. -The death of Basil Hallward seemed very little to him. He was thinking -of Hetty Merton. For it was an unjust mirror, this mirror of his soul -that he was looking at. Vanity? Curiosity? Hypocrisy? Had there -been nothing more in his renunciation than that? There had been -something more. At least he thought so. But who could tell? ... No. -There had been nothing more. Through vanity he had spared her. In -hypocrisy he had worn the mask of goodness. For curiosity's sake he -had tried the denial of self. He recognized that now. - -But this murder--was it to dog him all his life? Was he always to be -burdened by his past? Was he really to confess? Never. There was -only one bit of evidence left against him. The picture itself--that -was evidence. He would destroy it. Why had he kept it so long? Once -it had given him pleasure to watch it changing and growing old. Of -late he had felt no such pleasure. It had kept him awake at night. -When he had been away, he had been filled with terror lest other eyes -should look upon it. It had brought melancholy across his passions. -Its mere memory had marred many moments of joy. It had been like -conscience to him. Yes, it had been conscience. He would destroy it. - -He looked round and saw the knife that had stabbed Basil Hallward. He -had cleaned it many times, till there was no stain left upon it. It -was bright, and glistened. As it had killed the painter, so it would -kill the painter's work, and all that that meant. It would kill the -past, and when that was dead, he would be free. It would kill this -monstrous soul-life, and without its hideous warnings, he would be at -peace. He seized the thing, and stabbed the picture with it. - -There was a cry heard, and a crash. The cry was so horrible in its -agony that the frightened servants woke and crept out of their rooms. -Two gentlemen, who were passing in the square below, stopped and looked -up at the great house. They walked on till they met a policeman and -brought him back. The man rang the bell several times, but there was -no answer. Except for a light in one of the top windows, the house was -all dark. After a time, he went away and stood in an adjoining portico -and watched. - -"Whose house is that, Constable?" asked the elder of the two gentlemen. - -"Mr. Dorian Gray's, sir," answered the policeman. - -They looked at each other, as they walked away, and sneered. One of -them was Sir Henry Ashton's uncle. - -Inside, in the servants' part of the house, the half-clad domestics -were talking in low whispers to each other. Old Mrs. Leaf was crying -and wringing her hands. Francis was as pale as death. - -After about a quarter of an hour, he got the coachman and one of the -footmen and crept upstairs. They knocked, but there was no reply. -They called out. Everything was still. Finally, after vainly trying -to force the door, they got on the roof and dropped down on to the -balcony. The windows yielded easily--their bolts were old. - -When they entered, they found hanging upon the wall a splendid portrait -of their master as they had last seen him, in all the wonder of his -exquisite youth and beauty. Lying on the floor was a dead man, in -evening dress, with a knife in his heart. He was withered, wrinkled, -and loathsome of visage. It was not till they had examined the rings -that they recognized who it was. - - - - - - - - - -End of Project Gutenberg's The Picture of Dorian Gray, by Oscar Wilde - -*** END OF THIS PROJECT GUTENBERG EBOOK THE PICTURE OF DORIAN GRAY *** - -***** This file should be named 174.txt or 174.zip ***** -This and all associated files of various formats will be found in: - http://www.gutenberg.org/1/7/174/ - -Produced by Judith Boss. HTML version by Al Haines. - -Updated editions will replace the previous one--the old editions -will be renamed. - -Creating the works from public domain print editions means that no -one owns a United States copyright in these works, so the Foundation -(and you!) can copy and distribute it in the United States without -permission and without paying copyright royalties. Special rules, -set forth in the General Terms of Use part of this license, apply to -copying and distributing Project Gutenberg-tm electronic works to -protect the PROJECT GUTENBERG-tm concept and trademark. Project -Gutenberg is a registered trademark, and may not be used if you -charge for the eBooks, unless you receive specific permission. If you -do not charge anything for copies of this eBook, complying with the -rules is very easy. You may use this eBook for nearly any purpose -such as creation of derivative works, reports, performances and -research. They may be modified and printed and given away--you may do -practically ANYTHING with public domain eBooks. Redistribution is -subject to the trademark license, especially commercial -redistribution. - - - -*** START: FULL LICENSE *** - -THE FULL PROJECT GUTENBERG LICENSE -PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK - -To protect the Project Gutenberg-tm mission of promoting the free -distribution of electronic works, by using or distributing this work -(or any other work associated in any way with the phrase "Project -Gutenberg"), you agree to comply with all the terms of the Full Project -Gutenberg-tm License (available with this file or online at -http://gutenberg.net/license). - - -Section 1. General Terms of Use and Redistributing Project Gutenberg-tm -electronic works - -1.A. By reading or using any part of this Project Gutenberg-tm -electronic work, you indicate that you have read, understand, agree to -and accept all the terms of this license and intellectual property -(trademark/copyright) agreement. If you do not agree to abide by all -the terms of this agreement, you must cease using and return or destroy -all copies of Project Gutenberg-tm electronic works in your possession. -If you paid a fee for obtaining a copy of or access to a Project -Gutenberg-tm electronic work and you do not agree to be bound by the -terms of this agreement, you may obtain a refund from the person or -entity to whom you paid the fee as set forth in paragraph 1.E.8. - -1.B. "Project Gutenberg" is a registered trademark. It may only be -used on or associated in any way with an electronic work by people who -agree to be bound by the terms of this agreement. There are a few -things that you can do with most Project Gutenberg-tm electronic works -even without complying with the full terms of this agreement. See -paragraph 1.C below. There are a lot of things you can do with Project -Gutenberg-tm electronic works if you follow the terms of this agreement -and help preserve free future access to Project Gutenberg-tm electronic -works. See paragraph 1.E below. - -1.C. The Project Gutenberg Literary Archive Foundation ("the Foundation" -or PGLAF), owns a compilation copyright in the collection of Project -Gutenberg-tm electronic works. Nearly all the individual works in the -collection are in the public domain in the United States. If an -individual work is in the public domain in the United States and you are -located in the United States, we do not claim a right to prevent you from -copying, distributing, performing, displaying or creating derivative -works based on the work as long as all references to Project Gutenberg -are removed. Of course, we hope that you will support the Project -Gutenberg-tm mission of promoting free access to electronic works by -freely sharing Project Gutenberg-tm works in compliance with the terms of -this agreement for keeping the Project Gutenberg-tm name associated with -the work. You can easily comply with the terms of this agreement by -keeping this work in the same format with its attached full Project -Gutenberg-tm License when you share it without charge with others. - -1.D. The copyright laws of the place where you are located also govern -what you can do with this work. Copyright laws in most countries are in -a constant state of change. If you are outside the United States, check -the laws of your country in addition to the terms of this agreement -before downloading, copying, displaying, performing, distributing or -creating derivative works based on this work or any other Project -Gutenberg-tm work. The Foundation makes no representations concerning -the copyright status of any work in any country outside the United -States. - -1.E. Unless you have removed all references to Project Gutenberg: - -1.E.1. The following sentence, with active links to, or other immediate -access to, the full Project Gutenberg-tm License must appear prominently -whenever any copy of a Project Gutenberg-tm work (any work on which the -phrase "Project Gutenberg" appears, or with which the phrase "Project -Gutenberg" is associated) is accessed, displayed, performed, viewed, -copied or distributed: - -This eBook is for the use of anyone anywhere at no cost and with -almost no restrictions whatsoever. You may copy it, give it away or -re-use it under the terms of the Project Gutenberg License included -with this eBook or online at www.gutenberg.net - -1.E.2. If an individual Project Gutenberg-tm electronic work is derived -from the public domain (does not contain a notice indicating that it is -posted with permission of the copyright holder), the work can be copied -and distributed to anyone in the United States without paying any fees -or charges. If you are redistributing or providing access to a work -with the phrase "Project Gutenberg" associated with or appearing on the -work, you must comply either with the requirements of paragraphs 1.E.1 -through 1.E.7 or obtain permission for the use of the work and the -Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or -1.E.9. - -1.E.3. If an individual Project Gutenberg-tm electronic work is posted -with the permission of the copyright holder, your use and distribution -must comply with both paragraphs 1.E.1 through 1.E.7 and any additional -terms imposed by the copyright holder. Additional terms will be linked -to the Project Gutenberg-tm License for all works posted with the -permission of the copyright holder found at the beginning of this work. - -1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm -License terms from this work, or any files containing a part of this -work or any other work associated with Project Gutenberg-tm. - -1.E.5. Do not copy, display, perform, distribute or redistribute this -electronic work, or any part of this electronic work, without -prominently displaying the sentence set forth in paragraph 1.E.1 with -active links or immediate access to the full terms of the Project -Gutenberg-tm License. - -1.E.6. You may convert to and distribute this work in any binary, -compressed, marked up, nonproprietary or proprietary form, including any -word processing or hypertext form. However, if you provide access to or -distribute copies of a Project Gutenberg-tm work in a format other than -"Plain Vanilla ASCII" or other format used in the official version -posted on the official Project Gutenberg-tm web site (www.gutenberg.net), -you must, at no additional cost, fee or expense to the user, provide a -copy, a means of exporting a copy, or a means of obtaining a copy upon -request, of the work in its original "Plain Vanilla ASCII" or other -form. Any alternate format must include the full Project Gutenberg-tm -License as specified in paragraph 1.E.1. - -1.E.7. Do not charge a fee for access to, viewing, displaying, -performing, copying or distributing any Project Gutenberg-tm works -unless you comply with paragraph 1.E.8 or 1.E.9. - -1.E.8. You may charge a reasonable fee for copies of or providing -access to or distributing Project Gutenberg-tm electronic works provided -that - -- You pay a royalty fee of 20% of the gross profits you derive from - the use of Project Gutenberg-tm works calculated using the method - you already use to calculate your applicable taxes. The fee is - owed to the owner of the Project Gutenberg-tm trademark, but he - has agreed to donate royalties under this paragraph to the - Project Gutenberg Literary Archive Foundation. Royalty payments - must be paid within 60 days following each date on which you - prepare (or are legally required to prepare) your periodic tax - returns. Royalty payments should be clearly marked as such and - sent to the Project Gutenberg Literary Archive Foundation at the - address specified in Section 4, "Information about donations to - the Project Gutenberg Literary Archive Foundation." - -- You provide a full refund of any money paid by a user who notifies - you in writing (or by e-mail) within 30 days of receipt that s/he - does not agree to the terms of the full Project Gutenberg-tm - License. You must require such a user to return or - destroy all copies of the works possessed in a physical medium - and discontinue all use of and all access to other copies of - Project Gutenberg-tm works. - -- You provide, in accordance with paragraph 1.F.3, a full refund of any - money paid for a work or a replacement copy, if a defect in the - electronic work is discovered and reported to you within 90 days - of receipt of the work. - -- You comply with all other terms of this agreement for free - distribution of Project Gutenberg-tm works. - -1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm -electronic work or group of works on different terms than are set -forth in this agreement, you must obtain permission in writing from -both the Project Gutenberg Literary Archive Foundation and Michael -Hart, the owner of the Project Gutenberg-tm trademark. Contact the -Foundation as set forth in Section 3 below. - -1.F. - -1.F.1. Project Gutenberg volunteers and employees expend considerable -effort to identify, do copyright research on, transcribe and proofread -public domain works in creating the Project Gutenberg-tm -collection. Despite these efforts, Project Gutenberg-tm electronic -works, and the medium on which they may be stored, may contain -"Defects," such as, but not limited to, incomplete, inaccurate or -corrupt data, transcription errors, a copyright or other intellectual -property infringement, a defective or damaged disk or other medium, a -computer virus, or computer codes that damage or cannot be read by -your equipment. - -1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the "Right -of Replacement or Refund" described in paragraph 1.F.3, the Project -Gutenberg Literary Archive Foundation, the owner of the Project -Gutenberg-tm trademark, and any other party distributing a Project -Gutenberg-tm electronic work under this agreement, disclaim all -liability to you for damages, costs and expenses, including legal -fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT -LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE -PROVIDED IN PARAGRAPH F3. YOU AGREE THAT THE FOUNDATION, THE -TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE -LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR -INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH -DAMAGE. - -1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a -defect in this electronic work within 90 days of receiving it, you can -receive a refund of the money (if any) you paid for it by sending a -written explanation to the person you received the work from. If you -received the work on a physical medium, you must return the medium with -your written explanation. The person or entity that provided you with -the defective work may elect to provide a replacement copy in lieu of a -refund. If you received the work electronically, the person or entity -providing it to you may choose to give you a second opportunity to -receive the work electronically in lieu of a refund. If the second copy -is also defective, you may demand a refund in writing without further -opportunities to fix the problem. - -1.F.4. Except for the limited right of replacement or refund set forth -in paragraph 1.F.3, this work is provided to you 'AS-IS' WITH NO OTHER -WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR ANY PURPOSE. - -1.F.5. Some states do not allow disclaimers of certain implied -warranties or the exclusion or limitation of certain types of damages. -If any disclaimer or limitation set forth in this agreement violates the -law of the state applicable to this agreement, the agreement shall be -interpreted to make the maximum disclaimer or limitation permitted by -the applicable state law. The invalidity or unenforceability of any -provision of this agreement shall not void the remaining provisions. - -1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the -trademark owner, any agent or employee of the Foundation, anyone -providing copies of Project Gutenberg-tm electronic works in accordance -with this agreement, and any volunteers associated with the production, -promotion and distribution of Project Gutenberg-tm electronic works, -harmless from all liability, costs and expenses, including legal fees, -that arise directly or indirectly from any of the following which you do -or cause to occur: (a) distribution of this or any Project Gutenberg-tm -work, (b) alteration, modification, or additions or deletions to any -Project Gutenberg-tm work, and (c) any Defect you cause. - - -Section 2. Information about the Mission of Project Gutenberg-tm - -Project Gutenberg-tm is synonymous with the free distribution of -electronic works in formats readable by the widest variety of computers -including obsolete, old, middle-aged and new computers. It exists -because of the efforts of hundreds of volunteers and donations from -people in all walks of life. - -Volunteers and financial support to provide volunteers with the -assistance they need, is critical to reaching Project Gutenberg-tm's -goals and ensuring that the Project Gutenberg-tm collection will -remain freely available for generations to come. In 2001, the Project -Gutenberg Literary Archive Foundation was created to provide a secure -and permanent future for Project Gutenberg-tm and future generations. -To learn more about the Project Gutenberg Literary Archive Foundation -and how your efforts and donations can help, see Sections 3 and 4 -and the Foundation web page at http://www.pglaf.org. - - -Section 3. Information about the Project Gutenberg Literary Archive -Foundation - -The Project Gutenberg Literary Archive Foundation is a non profit -501(c)(3) educational corporation organized under the laws of the -state of Mississippi and granted tax exempt status by the Internal -Revenue Service. The Foundation's EIN or federal tax identification -number is 64-6221541. Its 501(c)(3) letter is posted at -http://pglaf.org/fundraising. Contributions to the Project Gutenberg -Literary Archive Foundation are tax deductible to the full extent -permitted by U.S. federal laws and your state's laws. - -The Foundation's principal office is located at 4557 Melan Dr. S. -Fairbanks, AK, 99712., but its volunteers and employees are scattered -throughout numerous locations. Its business office is located at -809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email -business@pglaf.org. Email contact links and up to date contact -information can be found at the Foundation's web site and official -page at http://pglaf.org - -For additional contact information: - Dr. Gregory B. Newby - Chief Executive and Director - gbnewby@pglaf.org - - -Section 4. Information about Donations to the Project Gutenberg -Literary Archive Foundation - -Project Gutenberg-tm depends upon and cannot survive without wide -spread public support and donations to carry out its mission of -increasing the number of public domain and licensed works that can be -freely distributed in machine readable form accessible by the widest -array of equipment including outdated equipment. Many small donations -($1 to $5,000) are particularly important to maintaining tax exempt -status with the IRS. - -The Foundation is committed to complying with the laws regulating -charities and charitable donations in all 50 states of the United -States. Compliance requirements are not uniform and it takes a -considerable effort, much paperwork and many fees to meet and keep up -with these requirements. We do not solicit donations in locations -where we have not received written confirmation of compliance. To -SEND DONATIONS or determine the status of compliance for any -particular state visit http://pglaf.org - -While we cannot and do not solicit contributions from states where we -have not met the solicitation requirements, we know of no prohibition -against accepting unsolicited donations from donors in such states who -approach us with offers to donate. - -International donations are gratefully accepted, but we cannot make -any statements concerning tax treatment of donations received from -outside the United States. U.S. laws alone swamp our small staff. - -Please check the Project Gutenberg Web pages for current donation -methods and addresses. Donations are accepted in a number of other -ways including including checks, online payments and credit card -donations. To donate, please visit: http://pglaf.org/donate - - -Section 5. General Information About Project Gutenberg-tm electronic -works. - -Professor Michael S. Hart is the originator of the Project Gutenberg-tm -concept of a library of electronic works that could be freely shared -with anyone. For thirty years, he produced and distributed Project -Gutenberg-tm eBooks with only a loose network of volunteer support. - - -Project Gutenberg-tm eBooks are often created from several printed -editions, all of which are confirmed as Public Domain in the U.S. -unless a copyright notice is included. Thus, we do not necessarily -keep eBooks in compliance with any particular paper edition. - - -Most people start at our Web site which has the main PG search facility: - - http://www.gutenberg.net - -This Web site includes information about Project Gutenberg-tm, -including how to make donations to the Project Gutenberg Literary -Archive Foundation, how to help produce our new eBooks, and how to -subscribe to our email newsletter to hear about new eBooks. diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -project(lws-minimal-http-server-generic-sessions) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-generic-sessions) -set(SRCS minimal-http-server-generic-sessions.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_OPENSSL_SUPPORT 1 requirements) -require_lws_config(LWS_WITH_GENERIC_SESSIONS 1 requirements) -require_lws_config(LWS_WITH_LIBUV 1 requirements) -require_lws_config(LWS_WITH_PLUGINS 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/minimal-http-server-generic-sessions.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/minimal-http-server-generic-sessions.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/minimal-http-server-generic-sessions.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/minimal-http-server-generic-sessions.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,202 +0,0 @@ -/* - * lws-minimal-http-server-generic-sessions - * - * Copyright (C) 2019 Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates setting up and using generic sessions - */ - -#include -#include -#include - -static int interrupted; -struct lws_context *context; - -static const struct lws_protocol_vhost_options - pvo_mm1 = { - NULL, NULL, "message-db", (void *)"/var/www/sessions/messageboard.sqlite3" -}, pvo_m1 = { - NULL, &pvo_mm1, "protocol-lws-messageboard", "" -}, - - pvo13 = { - NULL, NULL, "email-confirm-url-base", (void *)"https://localhost:7681/" -}, pvo12 = { - &pvo13, NULL, "urlroot", (void *)"https://127.0.0.1:7681/" -}, pvo11 = { - &pvo12, NULL, "email-contact-person", (void *)"andy@warmcat.com" -}, pvo10 = { - &pvo11, NULL, "email-helo", (void *)"warmcat.com" -}, pvo9 = { - &pvo10, NULL, "email-expire", (void *)"3600" -}, pvo8 = { - &pvo9, NULL, "email-smtp-ip", (void *)"127.0.0.1" -}, pvo7 = { - &pvo8, NULL, "email-from", (void *)"noreply@warmcat.com" -}, pvo6 = { - &pvo7, NULL, "confounder", (void *)"some kind of secret confounder" -}, pvo5 = { - &pvo6, NULL, "timeout-anon-idle-secs", (void *)"1200" -}, pvo4 = { - &pvo5, NULL, "timeout-idle-secs", (void *)"6000" -}, pvo3 = { - &pvo4, NULL, "session-db", (void *)"/var/www/sessions/lws.sqlite3" -}, pvo2 = { - &pvo3, NULL, "admin-password-sha256", - (void *)"25d08521d996bad92605f5a40fe71179dc968e70f669cb1db6190dcd53258200" /* pvo value */ -}, pvo1 = { - &pvo2, NULL, "admin-user", (void *)"admin" -}, pvo = { - &pvo_m1, &pvo1, "protocol-generic-sessions", "" -}, - - interpret1 = { - NULL, NULL, ".js", "protocol-lws-messageboard" -}, - - pvo_hsbph[] = {{ - NULL, NULL, "referrer-policy:", "no-referrer" -}, { - &pvo_hsbph[0], NULL, "x-xss-protection:", "1; mode=block" -}, { - &pvo_hsbph[1], NULL, "x-content-type-options:", "nosniff" -}, { - &pvo_hsbph[2], NULL, "content-security-policy:", - "default-src 'self'; " - "img-src https://www.gravatar.com 'self' data: ; " - "script-src 'self'; " - "font-src 'self'; " - "style-src 'self'; " - "connect-src 'self'; " - "frame-ancestors 'self'; " - "base-uri 'none'; " - "form-action 'self';" -}}; - - static const struct lws_http_mount mount2 = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/needadmin", /* mountpoint URL */ - /* .origin */ "./mount-origin/needadmin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ "protocol-lws-messageboard", - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ &interpret1, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 7, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, - }; - - static const struct lws_http_mount mount1 = { - /* .mount_next */ &mount2, /* linked-list "next" */ - /* .mountpoint */ "/needauth", /* mountpoint URL */ - /* .origin */ "./mount-origin/needauth", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ "protocol-lws-messageboard", - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ &interpret1, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 5, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, - }; - -static const struct lws_http_mount mount = { - /* .mount_next */ &mount1, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ "protocol-lws-messageboard", - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ &interpret1, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - lws_context_destroy(context); - - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - const char *p, *plugin_dirs[] = { - "/usr/local/share/libwebsockets-test-server/plugins", - NULL }; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server TLS | visit https://localhost:7681\n"); - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_EXPLICIT_VHOSTS; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - info.plugin_dirs = plugin_dirs; - info.pvo = &pvo; - - if (lws_cmdline_option(argc, argv, "-h")) - info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - info.headers = &pvo_hsbph[3]; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ - - - - -
- -

404

- Sorry, that file doesn't exist. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/admin-login.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/admin-login.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/admin-login.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/admin-login.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ - -This is an example destination that will appear after successful Admin login. - -This URL cannot be served if you're not logged in as admin. - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -document.addEventListener("DOMContentLoaded", function() { - - var transport_protocol = ""; - - if ( performance && performance.timing.nextHopProtocol ) { - transport_protocol = performance.timing.nextHopProtocol; - } else if ( window.chrome && window.chrome.loadTimes ) { - transport_protocol = window.chrome.loadTimes().connectionInfo; - } else { - - var p = performance.getEntriesByType("resource"); - for (var i=0; i < p.length; i++) { - var value = "nextHopProtocol" in p[i]; - if (value) - transport_protocol = p[i].nextHopProtocol; - } - } - - if (transport_protocol === "h2") - document.getElementById("transport").innerHTML = ""; -}, false); \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/failed-login.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/failed-login.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/failed-login.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/failed-login.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ - -This is an example destination that will appear after a failed login - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/favicon.ico differ Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/http2.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/http2.png differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - -
-
-
- -
- - This is a demo application for lws generic-sessions.

- It's a simple messageboard.

- What's interesting about it is there is no serverside scripting,
- instead client js makes a wss:// connection back to the server
- and then reacts to JSON from the ws protocol. Sessions stuff is
- handled by lws generic sessions, making the actual
- test application
very small.

- And because it's natively websocket, it's naturally connected
- for dynamic events and easy to maintain. -

- Register / Login at the top right to see and create new messages. -
- -
-
- New message
-
- -
-
-
-
- -
- -
-
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lws-common.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lws-common.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lws-common.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lws-common.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,125 +0,0 @@ -/* - * This section around grayOut came from here: - * http://www.codingforums.com/archive/index.php/t-151720.html - * Assumed public domain - * - * Init like this in your main html script, this also reapplies the gray - * - * lws_gray_out(true,{'zindex':'499'}); - * - * To remove the gray - * - * lws_gray_out(false); - * - */ - -function gsize(ptype) -{ - var h = document.compatMode === "CSS1Compat" && - !window.opera ? - document.documentElement.clientHeight : - document.body.clientHeight; - var w = document.compatMode === "CSS1Compat" && - !window.opera ? - document.documentElement.clientWidth : - document.body.clientWidth; - var pageWidth, pageHeight, t; - - if (document.body && - (document.body.scrollWidth || document.body.scrollHeight)) { - t = document.body.scrollWidth; - pageWidth = (w > t) ? ("" + w + "px") : ("" + (t) + "px"); - t = document.body.scrollHeight; - pageHeight = (h > t) ? ("" + h + "px") : ("" + (t) + "px"); - } else if (document.body.offsetWidth) { - t = document.body.offsetWidth; - pageWidth = (w > t) ? ("" + w + "px") : ("" + (t) + "px"); - t = document.body.offsetHeight; - pageHeight =(h > t) ? ("" + h + "px") : ("" + (t) + "px"); - } else { - pageWidth = "100%"; - pageHeight = "100%"; - } - return (ptype === 1) ? pageWidth : pageHeight; -} - -function addEvent( obj, type, fn ) { - if ( obj.attachEvent ) { - obj["e" + type + fn] = fn; - obj[type+fn] = function() { obj["e" + type + fn]( window.event );}; - obj.attachEvent("on" + type, obj[type + fn]); - } else - obj.addEventListener(type, fn, false); -} - -function removeEvent( obj, type, fn ) { - if ( obj.detachEvent ) { - obj.detachEvent("on" + type, obj[type + fn]); - obj[type + fn] = null; - } else - obj.removeEventListener(type, fn, false); -} - -function lws_gray_out(vis, _options) { - - var options = _options || {}; - var zindex = options.zindex || 50; - var opacity = options.opacity || 70; - var opaque = (opacity / 100); - var bgcolor = options.bgcolor || "#000000"; - var dark = document.getElementById("darkenScreenObject"); - - if (!dark) { - var tbody = document.getElementsByTagName("body")[0]; - var tnode = document.createElement("div"); - tnode.style.position = "absolute"; - tnode.style.top = "0px"; - tnode.style.left = "0px"; - tnode.style.overflow = "hidden"; - tnode.style.display ="none"; - tnode.id = "darkenScreenObject"; - tbody.appendChild(tnode); - dark = document.getElementById("darkenScreenObject"); - } - if (vis) { - dark.style.opacity = opaque; - dark.style.MozOpacity = opaque; - // dark.style.filter ='alpha(opacity='+opacity+')'; - dark.style.zIndex = zindex; - dark.style.backgroundColor = bgcolor; - dark.style.width = gsize(1); - dark.style.height = gsize(0); - dark.style.display = "block"; - addEvent(window, "resize", - function() { - dark.style.height = gsize(0); - dark.style.width = gsize(1); - } - ); - } else { - dark.style.display = "none"; - removeEvent(window, "resize", - function() { - dark.style.height = gsize(0); - dark.style.width = gsize(1); - } - ); - } -} - -/* - * end of grayOut related stuff - */ - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -function lws_san(s) -{ - if (s.search("<") !== -1) - return "invalid string"; - - return s; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.css libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.css --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.css 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,144 +0,0 @@ -.body { font-size: 12px } -.gstitle { font-size: 18px } - -.group1 { - vertical-align:middle; - text-align:center; - background:#f0f0e0; - padding:12px; - border-radius:10px; -} -.group2 { - display:block; - vertical-align:middle; - font-size: 22px; - text-align:center; - margin:auto; - align:center; - background-color: rgba(255, 255, 255, 0.8); - padding:12px; - border-radius:10px; -} - -body { - background-color: rgba(205, 205, 205, 1); -} - -div.lwsgs { - z-index: 3; - text-align:right; - background-color: rgba(255, 255, 255, 0.8); -} - -table.lwsgs { - width:100%; - height:100%; - transition: max-height 2s; -} -table.c100 { - text-align:center; - width:100%; -} - -table.r { - vertical-align:top; - text-align:right; -} - -table.l { - vertical-align:top; - text-align:left; -} - -table.fixed { - table-layout: fixed; -} - -td.logo { - vertical-align:top; - text-align:left; - width:200px -} - -td.rlogo { - vertical-align:top; - text-align:right -} - -td.lwsgs { - vertical-align:top; - float:right; -} - -td.h99 { - height:99%; - vertical-align:middle; -} - -td.c { - margin:auto; - align:center -} - -td.tac { - text-align:center -} - -td.ava { - display:inline-block; - vertical-align:top; - word-wrap:break-word; -} - -iframe.hidden { - display:none; -} - -div.hidden { - display:none; -} - -div.hiddenr { - display:none; - text-align:right; -} - -input { - margin: 2px; - padding: 2px; -} - -input.em { - margin: 4px; - font-weight:bold; -} - -input.wide { - margin: 6px; - padding: 6px; -} - -input.hidden { - display: none; -} - -form.r { - text-align:right; -} - -span.bad { - color: red; -} - -span.small { - font-size:8pt; -} - -img.av { - width: 64px; - height: 64px; -} - -.green { - color: green; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,634 +0,0 @@ - - -var lwsgs_user = "$lwsgs_user"; -var lwsgs_auth = "$lwsgs_auth"; -var lwsgs_email = "$lwsgs_email"; - -var lwsgs_html = '\ - \ -\ -
\ -
\ - \ - \ - \ - \ -
\ - \ -
\ - \ -
\ -\ - \ - \ - \ - \ - \ -'; - -/*-- this came from - -- https://raw.githubusercontent.com/blueimp/JavaScript-MD5/master/js/md5.min.js - -- under MIT license */ -!function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t),e=(n>>16)+(t>>16)+(r>>16);return e<<16|65535&r}function r(n,t){return n<>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<>>9<<4)+14]=r;var e,i,a,h,d,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e>5]>>>t%32&255);return r}function h(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t>5]|=(255&n.charCodeAt(t/8))<16&&(o=i(o,8*n.length)),r=0;16>r;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(h(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="0123456789abcdef",o="";for(r=0;r>>4&15)+e.charAt(15&t);return o}function v(n){return unescape(encodeURIComponent(n))}function m(n){return d(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this); - -if (lwsgs_user.substring(0, 1) == "$") { - alert("lwsgs.js: lws generic sessions misconfigured and not providing vars"); -} -function lwsgs_san(s) -{ - if (s.search("<") != -1) - return "invalid string"; - - return s; -} - -function lwsgs_update() -{ - var en_login = 1, en_forgot = 1; - - if (document.getElementById('password').value.length && - document.getElementById('password').value.length < 8) - en_login = 0; - - if (!document.getElementById('username').value || - !document.getElementById('password').value) - en_login = 0; - - if (!document.getElementById('username').value || - document.getElementById('password').value) - en_forgot = 0; - - document.getElementById('login').disabled = !en_login; - document.getElementById('forgot').disabled = !en_forgot; - - if (lwsgs_user) - document.getElementById("curuser").innerHTML = lwsgs_san(lwsgs_user); - - if (lwsgs_user === "") - document.getElementById("dlogin").style.display = "inline"; - else - document.getElementById("dlogout").style.display = "inline"; - } - -function lwsgs_open_registration() -{ - document.getElementById("dadmin").style.display = "none"; - document.getElementById("dlogin").style.display = "none"; - document.getElementById("dlogout").style.display = "none"; - document.getElementById("dchange").style.display = "none"; - document.getElementById("dregister").style.display = "inline"; -} - -function lwsgs_cancel_registration() -{ - document.getElementById("dadmin").style.display = "none"; - document.getElementById("dregister").style.display = "none"; - document.getElementById("dchange").style.display = "none"; - - if (lwsgs_user === "") - document.getElementById("dlogin").style.display = "inline"; - else - document.getElementById("dlogout").style.display = "inline"; -} - -function lwsgs_select_change() -{ - document.getElementById("dlogin").style.display = "none"; - document.getElementById("dlogout").style.display = "none"; - document.getElementById("dregister").style.display = "none"; - if (lwsgs_auth & 2) { - document.getElementById("dadmin").style.display = "inline"; - document.getElementById("dchange").style.display = "none"; - } else { - document.getElementById("dadmin").style.display = "none"; - document.getElementById("dchange").style.display = "inline"; - } - - event.preventDefault() -} - -var lwsgs_user_check = '0'; -var lwsgs_email_check = '0'; - -function lwsgs_rupdate() -{ - var en_register = 1, en_forgot = 0, op; - - if (document.getElementById('rpassword').value == - document.getElementById('password2').value) { - if (document.getElementById('rpassword').value.length) - document.getElementById('match').innerHTML = - "\u2713"; - else - document.getElementById('match').innerHTML = ""; - document.getElementById('pw2').style = ""; - } else { - if (document.getElementById('password2').value || - document.getElementById('email').value) { // ie, he is filling in "register" path and cares - document.getElementById('match').innerHTML = - "\u2718 Passwords do not match"; - } else - document.getElementById('match').innerHTML = - "\u2718 Passwords do not match"; - - en_register = 0; - } - - if (document.getElementById('rpassword').value.length && - document.getElementById('rpassword').value.length < 8) { - en_register = 0; - document.getElementById('rpw1').innerHTML = "Need 8 chars"; - } else - if (document.getElementById('rpassword').value.length) - document.getElementById('rpw1').innerHTML = "\u2713"; - else - document.getElementById('rpw1').innerHTML = ""; - - if (!document.getElementById('rpassword').value || - !document.getElementById('password2').value || - !document.getElementById('rusername').value || - !document.getElementById('email').value || - lwsgs_email_check === '1'|| - lwsgs_user_check === '1') - en_register = 0; - - document.getElementById('register').disabled = !en_register; - document.getElementById('rpassword').disabled = lwsgs_user_check === '1'; - document.getElementById('password2').disabled = lwsgs_user_check === '1'; - document.getElementById('email').disabled = lwsgs_user_check === '1'; - - if (lwsgs_user_check === '0') { - var uc = document.getElementById('uchk'); - - if (uc) { - if (document.getElementById('rusername').value) - uc.innerHTML = "\u2713"; - else - uc.innerHTML = ""; - } - } else { - if (document.getElementById('uchk')) - ocument.getElementById('uchk').innerHTML = "\u2718 Already registered"; - en_forgot = 1; - } - - if (lwsgs_email_check === '0') { - var ec = document.getElementById('echk'); - - if (ec) { - if (document.getElementById('email').value) - ec.innerHTML = "\u2713"; - else - ec.innerHTML = ""; - } - } else { - if (document.getElementById('echk')) - document.getElementById('echk').innerHTML = "\u2718 Already registered"; - en_forgot = 1; - } - - if (en_forgot) - document.getElementById('rforgot').style.display = "inline"; - else - document.getElementById('rforgot').style.display = "none"; - - if (lwsgs_user_check === '1') - op = '0.5'; - else - op = '1.0'; - document.getElementById('rpassword').style.opacity = op; - document.getElementById('password2').style.opacity = op; - document.getElementById('email').style.opacity = op; - } - -function lwsgs_cupdate() -{ - var en_change = 1, en_forgot = 1, pwok = 1, op; - - if (lwsgs_auth & 8) { - document.getElementById('ccurpw').style.display = "none"; - document.getElementById('ccurpw_name').style.display = "none"; - } else { - if (!document.getElementById('ccurpw').value || - document.getElementById('ccurpw').value.length < 8) { - en_change = 0; - pwok = 0; - document.getElementById('cuchk').innerHTML = "\u2718"; - } else { - en_forgot = 0; - document.getElementById('cuchk').innerHTML = ""; - } - document.getElementById('ccurpw').style.display = "inline"; - document.getElementById('ccurpw_name').style.display = "inline"; - } - - if (document.getElementById('cpassword').value == - document.getElementById('cpassword2').value) { - if (document.getElementById('cpassword').value.length) - document.getElementById('cmatch').innerHTML = "\u2713"; - else - document.getElementById('cmatch').innerHTML = ""; - document.getElementById('pw2').style = ""; - } else { - if (document.getElementById('cpassword2').value //|| - //document.getElementById('cemail').value - ) { // ie, he is filling in "register" path and cares - document.getElementById('cmatch').innerHTML = - "\u2718 Passwords do not match"; - } else - document.getElementById('cmatch').innerHTML = "\u2718 Passwords do not match"; - - en_change = 0; - } - - if (document.getElementById('cpassword').value.length && - document.getElementById('cpassword').value.length < 8) { - en_change = 0; - document.getElementById('cpw1').innerHTML = "Need 8 chars"; - } else { - var cpw = document.getElementById('cpw1'); - - if (cpw) { - if (document.getElementById('cpassword').value.length) - cpw.innerHTML = "\u2713"; - else - cpw.innerHTML = ""; - } - } - - if (!document.getElementById('cpassword').value || - !document.getElementById('cpassword2').value || - pwok === 0) - en_change = 0; - - if (document.getElementById('showdel').checked) - document.getElementById('delete').style.display = "inline"; - else - document.getElementById('delete').style.display = "none"; - - document.getElementById('change').disabled = !en_change; - document.getElementById('cpassword').disabled = pwok === 0; - document.getElementById('cpassword2').disabled = pwok === 0; - document.getElementById('showdel').disabled = pwok === 0; - document.getElementById('delete').disabled = pwok === 0; - //document.getElementById('cemail').disabled = pwok === 0; - - /* - if (lwsgs_auth & 8) { - document.getElementById('cemail').style.display = "none"; - document.getElementById('cemail_name').style.display = "none"; - } else { - document.getElementById('cemail').style.display = "inline"; - document.getElementById('cemail_name').style.display = "inline"; - if (lwsgs_email_check === '0' && - document.getElementById('cemail').value != lwsgs_email) { - if (document.getElementById('cemail').value) - document.getElementById('cechk').innerHTML = "\u2713"; - else - document.getElementById('cechk').innerHTML = ""; - } else { - document.getElementById('cechk').innerHTML = "\u2718 Already registered"; - en_forgot = 1; - } - } */ - - if (lwsgs_auth & 8) - en_forgot = 0; - - if (en_forgot) - document.getElementById('cforgot').style.display = "inline"; - else - document.getElementById('cforgot').style.display = "none"; - - if (pwok === 0) - op = '0.5'; - else - op = '1.0'; - document.getElementById('cpassword').style.opacity = op; - document.getElementById('cpassword2').style.opacity = op; - // document.getElementById('cemail').style.opacity = op; - } - -function lwsgs_check_user() -{ - var xmlHttp = new XMLHttpRequest(); - xmlHttp.onreadystatechange = function() { - if (xmlHttp.readyState === 4 && xmlHttp.status === 200) { - lwsgs_user_check = xmlHttp.responseText; - lwsgs_rupdate(); - } - } - xmlHttp.open("GET", "lwsgs-check/username="+document.getElementById('rusername').value, true); - xmlHttp.send(null); -} - -function lwsgs_check_email(id) -{ - var xmlHttp = new XMLHttpRequest(); - xmlHttp.onreadystatechange = function() { - if (xmlHttp.readyState === 4 && xmlHttp.status === 200) { - lwsgs_email_check = xmlHttp.responseText; - lwsgs_rupdate(); - } - } - xmlHttp.open("GET", "lwsgs-check/email="+document.getElementById(id).value, true); - xmlHttp.send(null); -} - -function rupdate_user() -{ - lwsgs_rupdate(); - lwsgs_check_user(); -} - -function rupdate_email() -{ - lwsgs_rupdate(); - lwsgs_check_email('email'); -} - -function cupdate_email() -{ - lwsgs_cupdate(); - lwsgs_check_email('cemail'); -} - - -function lwsgs_initial() -{ - document.getElementById('lwsgs').innerHTML = lwsgs_html; - - if (lwsgs_user) { - document.getElementById("curuser").innerHTML = - "currently logged in as " + lwsgs_san(lwsgs_user) + "
"; - - document.getElementById("ccuruser").innerHTML = - "Login settings for " + - lwsgs_san(lwsgs_user) + "
"; - } - - document.getElementById('username').oninput = lwsgs_update; - document.getElementById('username').onchange = lwsgs_update; - document.getElementById('password').oninput = lwsgs_update; - document.getElementById('password').onchange = lwsgs_update; - document.getElementById('doreg').onclick = lwsgs_open_registration; - document.getElementById('clink').onclick = lwsgs_select_change; - document.getElementById('cancel').onclick =lwsgs_cancel_registration; - document.getElementById('cancel2').onclick =lwsgs_cancel_registration; - document.getElementById('rpassword').oninput = lwsgs_rupdate; - document.getElementById('password2').oninput = lwsgs_rupdate; - document.getElementById('rusername').oninput = rupdate_user; - document.getElementById('email').oninput = rupdate_email; - document.getElementById('ccurpw').oninput = lwsgs_cupdate; - document.getElementById('cpassword').oninput = lwsgs_cupdate; - document.getElementById('cpassword2').oninput = lwsgs_cupdate; - - document.getElementById('showdel').onchange = lwsgs_cupdate; - - if (lwsgs_email) - document.getElementById('grav').innerHTML = - ""; - //if (lwsgs_email) - //document.getElementById('cemail').placeholder = lwsgs_email; - document.getElementById('cusername').value = lwsgs_user; - lwsgs_update(); - lwsgs_cupdate(); -} - -window.addEventListener("load", function() { - lwsgs_initial(); - document.getElementById("nolog").style.display = !!lwsgs_user ? "none" : "inline-block"; - document.getElementById("logged").style.display = !lwsgs_user ? "none" : "inline-block"; - - document.getElementById("msg").onkeyup = mupd; - document.getElementById("msg").onchange = mupd; - - var ws; - - function mb_format(s) - { - var r = "", n, wos = 0; - - for (n = 0; n < s.length; n++) { - if (s[n] == ' ') - wos = 0; - else { - wos++; - if (wos === 40) { - wos = 0; - r = r + ' '; - } - } - if (s[n] == '<') { - r = r + "<"; - continue; - } - if (s[n] == '\n') { - r = r + "
"; - continue; - } - - r = r + s[n]; - } - - return r; - } - - function add_div(n, m) - { - var q = document.getElementById(n); - var d = new Date(m.time * 1000), s = d.toTimeString(), t; - - t = s.indexOf('('); - if (t) - s = s.substring(0, t); - - q.innerHTML = "
" + - "
" + - "" + lwsgs_san(m.username) + "
" + - "" + d.toDateString() + - "
" + s + "

" + - "IP: " + lwsgs_san(m.ip) + - "
" + - mb_format(m.content) + - "

" + q.innerHTML; - } - - function get_appropriate_ws_url() - { - var pcol; - var u = document.URL; - - if (u.substring(0, 5) == "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) == "http") - u = u.substr(7); - } - u = u.split('/'); - - return pcol + u[0] + "/xxx"; - } - - if (lwsgs_user) { - - ws = new WebSocket(get_appropriate_ws_url(), - "protocol-lws-messageboard"); - - try { - ws.onopen = function() { - document.getElementById("debug").textContent = "ws opened"; - } - ws.onmessage =function got_packet(msg) { - add_div("messages", JSON.parse(msg.data)); - } - ws.onclose = function(){ - } - } catch(exception) { - alert('

Error' + exception); - } - } - - function mupd() - { - document.getElementById("send").disabled = !document.getElementById("msg").value; - } -}, false); Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs-logo.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs-logo.png differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/md5.min.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/md5.min.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/md5.min.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/md5.min.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -!function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t),e=(n>>16)+(t>>16)+(r>>16);return e<<16|65535&r}function r(n,t){return n<>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<>>9<<4)+14]=r;var e,i,a,h,d,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e>5]>>>t%32&255);return r}function h(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t>5]|=(255&n.charCodeAt(t/8))<16&&(o=i(o,8*n.length)),r=0;16>r;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(h(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="0123456789abcdef",o="";for(r=0;r>>4&15)+e.charAt(15&t);return o}function v(n){return unescape(encodeURIComponent(n))}function m(n){return d(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this); -//# sourceMappingURL=md5.min.js.map \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needadmin/admin-login.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needadmin/admin-login.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needadmin/admin-login.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needadmin/admin-login.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ - -This is an example destination that will appear after successful Admin login. - -This URL cannot be served if you're not logged in as admin. - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needauth/successful-login.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needauth/successful-login.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needauth/successful-login.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needauth/successful-login.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ - -This is an example destination that will appear after successful non-Admin login - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-fail.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-fail.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-fail.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-fail.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ - -Sorry, something went wrong. - -Click here to continue. - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-ok.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-ok.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-ok.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-ok.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ - -This is a one-time password recovery login. - -Please click here and click your username at the top to reset your password. - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-fail.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-fail.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-fail.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-fail.html 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Registration failed, sorry diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-ok.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-ok.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-ok.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-ok.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ - - - - - - - - - - - - -
- -
- Your registration as is accepted,
- you will receive an email shortly with instructions
- to verify and enable the account for normal use.

- The link is only valid for an hour, after that if it has
- not been verified your account will be deleted. -
- - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-fail.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-fail.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-fail.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-fail.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ - - - - - - - - - - - - -
- -
- Sorry, the link was invalid. -
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-ok.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-ok.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-ok.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-ok.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ - - - - - - - - - - - - -
- -
- Thanks for signing up, your registration as is verified.
-
- Click here to continue. -
- - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/seats.jpg and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/seats.jpg differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-fail.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-fail.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-fail.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-fail.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ - -Sorry, something went wrong. - -Click here to continue. - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-ok.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-ok.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-ok.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-ok.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -An email has been sent to your registered address. - -Please follow the instructions to reset your password. - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/successful-login.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/successful-login.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/successful-login.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/successful-login.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ - -This is an example destination that will appear after successful non-Admin login - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-generic-sessions/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-generic-sessions/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -# lws minimal http server with generic-sessions - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-tls -[2018/03/20 13:23:13:0131] USER: LWS minimal http server TLS | visit https://localhost:7681 -[2018/03/20 13:23:13:0142] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/03/20 13:23:13:0142] NOTICE: Using SSL mode -[2018/03/20 13:23:13:0146] NOTICE: SSL ECDH curve 'prime256v1' -[2018/03/20 13:23:13:0146] NOTICE: HTTP2 / ALPN enabled -[2018/03/20 13:23:13:0195] NOTICE: lws_tls_client_create_vhost_context: doing cert filepath localhost-100y.cert -[2018/03/20 13:23:13:0195] NOTICE: Loaded client cert localhost-100y.cert -[2018/03/20 13:23:13:0195] NOTICE: lws_tls_client_create_vhost_context: doing private key filepath -[2018/03/20 13:23:13:0196] NOTICE: Loaded client cert private key localhost-100y.key -[2018/03/20 13:23:13:0196] NOTICE: created client ssl context for default -[2018/03/20 13:23:14:0207] NOTICE: vhost default: cert expiry: 730459d -``` - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-h2-long-poll/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-h2-long-poll/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-h2-long-poll/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-h2-long-poll/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -project(lws-minimal-http-server-h2-long-poll) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-h2-long-poll) -set(SRCS minimal-http-server.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITH_HTTP2 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-h2-long-poll/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-h2-long-poll/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-h2-long-poll/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-h2-long-poll/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-h2-long-poll/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-h2-long-poll/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-h2-long-poll/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-h2-long-poll/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-h2-long-poll/minimal-http-server.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-h2-long-poll/minimal-http-server.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-h2-long-poll/minimal-http-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-h2-long-poll/minimal-http-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,157 +0,0 @@ -/* - * lws-minimal-http-server-h2-long-poll - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates an h2 server that supports "long poll" - * immortal client connections. For simplicity it doesn't serve - * any regular files, you can add a mount to do it if you want. - * - * The protocol keeps the long poll h2 stream open, and sends - * the time on the stream once per minute. Normally idle h2 - * connections are closed by default within 30s, so this demonstrates - * the stream and network connection are operating as "immortal" - * on both sides. - * - * See http-client/minimal-http-client-h2-long-poll for the - * client example that connects and transitions the stream to the - * immortal long poll mode. - */ - -#include -#include -#include - -static int interrupted; - -struct pss { - struct lws *wsi; - lws_sorted_usec_list_t sul; - char pending; -}; - -static const lws_retry_bo_t retry = { - .secs_since_valid_ping = 5, - .secs_since_valid_hangup = 10, -}; - -static void -sul_cb(lws_sorted_usec_list_t *sul) -{ - struct pss *pss = (struct pss *)lws_container_of(sul, struct pss, sul); - - pss->pending = 1; - lws_callback_on_writable(pss->wsi); - /* interval 1min... longer than any normal timeout */ - lws_sul_schedule(lws_get_context(pss->wsi), 0, &pss->sul, sul_cb, - 60 * LWS_US_PER_SEC); -} - -static int -callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - struct pss * pss = (struct pss *)user; - uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE], - *start = &buf[LWS_PRE], *p = start, - *end = p + sizeof(buf) - LWS_PRE; - int m, n; - - switch (reason) { - case LWS_CALLBACK_HTTP: - lwsl_user("%s: connect\n", __func__); - pss->wsi = wsi; - - if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, - "text/html", - LWS_ILLEGAL_HTTP_CONTENT_LEN, /* no content len */ - &p, end)) - return 1; - if (lws_finalize_write_http_header(wsi, start, &p, end)) - return 1; - - sul_cb(&pss->sul); - return 0; - - case LWS_CALLBACK_CLOSED_HTTP: - if (!pss) - break; - lws_sul_schedule(lws_get_context(wsi), 0, &pss->sul, sul_cb, - LWS_SET_TIMER_USEC_CANCEL); - break; - - case LWS_CALLBACK_HTTP_WRITEABLE: - if (!pss->pending) - break; - n = lws_snprintf((char *)p, sizeof(buf) - LWS_PRE, "%llu", - (unsigned long long)lws_now_usecs()); - m = lws_write(wsi, p, n, LWS_WRITE_HTTP); - if (m < n) { - lwsl_err("ERROR %d writing to socket\n", n); - return -1; - } - break; - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static struct lws_protocols protocols[] = { - { "http", callback_http, sizeof(struct pss), 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server h2 long poll\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - info.protocols = protocols; - info.options = - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_VH_H2_HALF_CLOSED_LONG_POLL | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - /* the default validity check is 5m / 5m10s... -v = 5s / 10s */ - - if (lws_cmdline_option(argc, argv, "-v")) - info.retry_and_idle_policy = &retry; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-h2-long-poll/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-h2-long-poll/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-h2-long-poll/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-h2-long-poll/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -# lws minimal http server - -## build - -``` - $ cmake . && make -``` -## Commandline Options - -Option|Meaning ----|--- --d|Set logging verbosity --s|Serve using TLS selfsigned cert (ie, connect to it with https://...) --v|Connection validity use 3s / 10s instead of default 5m / 5m10s - -## usage - -``` - $ ./lws-minimal-http-server -[2018/03/04 09:30:02:7986] USER: LWS minimal http server | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-http-server-mimetypes) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-mimetypes) -set(SRCS minimal-http-server-mimetypes.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/minimal-http-server-mimetypes.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/minimal-http-server-mimetypes.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/minimal-http-server-mimetypes.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/minimal-http-server-mimetypes.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ -/* - * lws-minimal-http-server - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -static int interrupted; - -static const struct lws_protocol_vhost_options pvo_mime = { - NULL, /* "next" pvo linked-list */ - NULL, /* "child" pvo linked-list */ - ".bz2", /* file suffix to match */ - "application/x-bzip2" /* mimetype to use */ -}; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ &pvo_mime, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ - - - - - - - -
- - Hello from the minimal http server + mimetypes example. -
- This shows how to teach a mount new bindings between file
- suffix and mimetype used to serve it.

- - Lws has a bunch of built-in ones, but you can add as many
- as you like when defining the mount.

- - For example, lws doesn't know the suffix [.tar].bz2
- implies the mimetype application/x-bzip2, but we taught
- this mount about that relationship in the example code, so it
- knows how to serve this example test.tar.bz2. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/test.tar.bz2 and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/mount-origin/test.tar.bz2 differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-mimetypes/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-mimetypes/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -# lws minimal http server mimetypes - -This is the same as the basic minimal http server, but it demonstrates how to -add support for extra mimetypes to a mount. - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server -[2018/03/04 09:30:02:7986] USER: LWS minimal http server | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 and click on the link to download the test.tar.bz2 file. - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-minimal-http-server-multivhost) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-multivhost) -set(SRCS minimal-http-server.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/minimal-http-server.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/minimal-http-server.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/minimal-http-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/minimal-http-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,179 +0,0 @@ -/* - * lws-minimal-http-server-multivhost - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -static int interrupted; - -static const struct lws_http_mount mount_localhost1 = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin-localhost1", - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}, mount_localhost2 = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin-localhost2", - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}, mount_localhost3 = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin-localhost3", - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -void vh_destruction_notification(struct lws_vhost *vh, void *arg) -{ - lwsl_user("%s: called, arg: %p\n", __func__, arg); -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server-multivhost | visit http://localhost:7681 / 7682\n"); - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - /* - * Because of LWS_SERVER_OPTION_EXPLICIT_VHOSTS, this only creates - * the context and no longer creates a default vhost - */ - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* it's our job now to create the vhosts we want: - * - * - "localhost1" listen on 7681 and serve ./mount-origin-localhost1/ - * - "localhost2" listen on 7682 and serve ./mount-origin-localhost2/ - * - "localhost3" share 7682 and serve ./mount-origin-localhost3/ - * - * Note lws supports dynamic vhost creation and destruction at runtime. - * When using multi-vhost with your own protocols, you must provide a - * pvo for each vhost naming each protocol you want enabled on it. - * minimal-ws-server-threads demonstrates how to provide pvos. - */ - - info.port = 7681; - info.mounts = &mount_localhost1; - info.error_document_404 = "/404.html"; - info.vhost_name = "localhost1"; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("Failed to create first vhost\n"); - goto bail; - } - - info.port = 7682; - info.mounts = &mount_localhost2; - info.error_document_404 = "/404.html"; - info.vhost_name = "localhost2"; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("Failed to create second vhost\n"); - goto bail; - } - - /* a second vhost listens on port 7682 */ - info.mounts = &mount_localhost3; - info.error_document_404 = "/404.html"; - info.vhost_name = "localhost3"; - info.finalize = vh_destruction_notification; - info.finalize_arg = NULL; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("Failed to create third vhost\n"); - goto bail; - } - - if (lws_cmdline_option(argc, argv, "--die-after-vhost")) { - lwsl_warn("bailing after creating vhosts\n"); - goto bail; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail: - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404 (vhost localhost1)

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ - - - - - - -
- - - Hello from the minimal http server multivhost example.
-
- This was served from ./mount-origin-localhost1/index.html
-
- You can confirm the 404 page handler by going to this - nonexistant page. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost1/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404 (vhost localhost2)

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ - - - - - - -
- - Hello from the minimal http server multivhost example.
-
- This was served from ./mount-origin-localhost2/index.html
-
- You can confirm the 404 page handler by going to this - nonexistant page. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost2/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404 (vhost localhost3)

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ - - - - - - -
- - Hello from the minimal http server multivhost example.
-
- This was served from ./mount-origin-localhost3/index.html
-
- You can confirm the 404 page handler by going to this - nonexistant page. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/mount-origin-localhost3/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-multivhost/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-multivhost/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -# lws minimal http server multivhost - -This creates a single server that creates three vhosts listening on both :7681 and -:7682. Two separate vhosts share listening on :7682. - -|vhost|listens on port|serves| ----|---|--- -localhost1|7681|./mount-origin-localhost1 -localhost2|7682|./mount-origin-localhost2 -localhost3|7682|./mount-origin-localhost3 - -Notice the last two both listen on 7682. If you visit http://localhost:7682, -by default you will get mapped to the first one, localhost2. - -However if you edit /etc/hosts on your machine and add - -``` -127.0.0.1 localhost3 -``` - -so that you can visit http://localhost3:7682 in your browser, lws will use the -`Host: localhost3` header sent by your browser to select the localhost3 vhost -for the connection, and you will be served content from ./mount-origin-localhost3 - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 ---die-after-vhost | For testing failure handling - -``` - $ ./lws-minimal-http-server-multivhost -[2018/03/16 09:37:20:0866] USER: LWS minimal http server-multivhost | visit http://localhost:7681 / 7682 -[2018/03/16 09:37:20:0867] NOTICE: Creating Vhost 'localhost1' port 7681, 1 protocols, IPv6 off -[2018/03/16 09:37:20:0868] NOTICE: Creating Vhost 'localhost2' port 7682, 1 protocols, IPv6 off -[2018/03/16 09:37:20:0869] NOTICE: Creating Vhost 'localhost3' port 7682, 1 protocols, IPv6 off -[2018/03/16 09:37:20:0869] NOTICE: using listen skt from vhost localhost2 -``` - -Visit http://localhost:7681 and http://localhost:7682 - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -project(lws-minimal-http-server-proxy) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-proxy) -set(SRCS minimal-http-server-proxy.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITH_HTTP_PROXY 1 requirements) -require_lws_config(LWS_OPENSSL_SUPPORT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/minimal-http-server-proxy.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/minimal-http-server-proxy.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/minimal-http-server-proxy.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/minimal-http-server-proxy.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -/* - * lws-minimal-http-server-proxy - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal tls reverse proxy - */ -#include -#include -#include - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "warmcat.com/", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_HTTPS, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server proxy | visit https://localhost:7681\n"); - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/favicon.ico differ Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/http2.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/http2.png differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-proxy/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ - - - - - - -
- - Hello from the minimal https server example. -
- You can confirm the 404 page handler by going to this - nonexistant page. -
-
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -project(lws-minimal-http-server-smp) -cmake_minimum_required(VERSION 2.8) -include(CheckIncludeFile) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-smp) -set(SRCS minimal-http-server-smp.c) - -MACRO(require_pthreads result) - CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) - if (NOT LWS_HAVE_PTHREAD_H) - if (LWS_WITH_MINIMAL_EXAMPLES) - set(result 0) - else() - message(FATAL_ERROR "threading support requires pthreads") - endif() - endif() -ENDMACRO() - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_pthreads(requirements) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared pthread) - add_dependencies(${SAMP} websockets_shared pthread) - else() - target_link_libraries(${SAMP} websockets pthread) - endif() -endif() \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/minimal-http-server-smp.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/minimal-http-server-smp.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/minimal-http-server-smp.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/minimal-http-server-smp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,132 +0,0 @@ -/* - * lws-minimal-http-server-smp - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal multithreaded http server you can make with lws. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - * - * Also for simplicity the number of threads is set in the code... note that - * the real number of threads possible is decided by the LWS_MAX_SMP that lws - * was configured with, by default that is 1. Lws will limit the number of - * requested threads to the number possible. - */ - -#include -#include -#include -#include - -#define COUNT_THREADS 8 - -static struct lws_context *context; -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void *thread_service(void *threadid) -{ - while (lws_service_tsi(context, 10000, - (int)(lws_intptr_t)threadid) >= 0 && - !interrupted) - ; - - pthread_exit(NULL); - - return NULL; -} - -void sigint_handler(int sig) -{ - interrupted = 1; - lws_cancel_service(context); -} - -int main(int argc, const char **argv) -{ - pthread_t pthread_service[COUNT_THREADS]; - struct lws_context_creation_info info; - void *retval; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server SMP | visit http://127.0.0.1:7681\n"); - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - if ((p = lws_cmdline_option(argc, argv, "-t"))) { - info.count_threads = atoi(p); - if (info.count_threads < 1 || info.count_threads > LWS_MAX_SMP) - return 1; - } else - info.count_threads = COUNT_THREADS; - - if (lws_cmdline_option(argc, argv, "-s")) { - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - lwsl_notice(" Service threads: %d\n", lws_get_count_threads(context)); - - /* start all the service threads */ - - for (n = 0; n < lws_get_count_threads(context); n++) - if (pthread_create(&pthread_service[n], NULL, thread_service, - (void *)(lws_intptr_t)n)) - lwsl_err("Failed to start service thread\n"); - - /* wait for all the service threads to exit */ - - while ((--n) >= 0) - pthread_join(pthread_service[n], &retval); - - lws_context_destroy(context); - - return 0; -} Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ - - - - - - -
- - Hello from the minimal http server SMP example. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-smp/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-smp/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -# lws minimal http server with multithreaded service - -Lws supports multithreaded service... build lws with `-DLWS_MAP_SMP=`, the -default is 1. If nonzero, some extra pthreads locking is built into lws and it supports multiple -independent service threads. - -![lws-smp-overview](../../doc-assets/lws-smp-ov.png) - -When an incoming connection is accepted, it is bound to the pt with the lowest current wsi -count, to keep the load on the threads balanced. Only the pt the wsi is bound to can service -the thread, so although there can be as many wsi being serviced simultaneously as there are -service threads, a wsi can only be service by the pt it is bound to. - -The effectiveness of the scalability depends on the load. Here is an example of roughly what can be expected - -![lws-smp-example](../../doc-assets/lws-smp-example.png) - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-smp -[2018/03/07 17:44:20:2409] USER: LWS minimal http server SMP | visit http://localhost:7681 -[2018/03/07 17:44:20:2410] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -[2018/03/07 17:44:20:2411] NOTICE: Service threads: 10 -``` - -Visit http://localhost:7681 and use ab or other testing tools - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-http-server-sse) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-sse) -set(SRCS minimal-http-server-sse.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/minimal-http-server-sse.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/minimal-http-server-sse.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/minimal-http-server-sse.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/minimal-http-server-sse.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,224 +0,0 @@ -/* - * lws-minimal-http-server-sse - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http server that can serve both normal static - * content and server-side event connections. - * - * To keep it simple, it serves the static stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * - * You can change that by changing mount.origin below. - */ - -#include -#include -#include -#include -#include - -/* - * Unlike ws, http is a stateless protocol. This pss only exists for the - * duration of a single http transaction. With http/1.1 keep-alive and http/2, - * that is unrelated to (shorter than) the lifetime of the network connection. - */ -struct pss { - time_t established; -}; - -static int interrupted; - -#define SECS_REPORT 3 - -static int -callback_sse(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - struct pss *pss = (struct pss *)user; - uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE], *start = &buf[LWS_PRE], - *p = start, *end = &buf[sizeof(buf) - 1]; - - switch (reason) { - case LWS_CALLBACK_HTTP: - /* - * `in` contains the url part after our mountpoint /sse, if any - * you can use this to determine what data to return and store - * that in the pss - */ - lwsl_notice("%s: LWS_CALLBACK_HTTP: '%s'\n", __func__, - (const char *)in); - - pss->established = time(NULL); - - /* SSE requires a response with this content-type */ - - if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, - "text/event-stream", - LWS_ILLEGAL_HTTP_CONTENT_LEN, - &p, end)) - return 1; - - if (lws_finalize_write_http_header(wsi, start, &p, end)) - return 1; - - /* - * This tells lws we are no longer a normal http stream, - * but are an "immortal" (plus or minus whatever timeout you - * set on it afterwards) SSE stream. In http/2 case that also - * stops idle timeouts being applied to the network connection - * while this wsi is still open. - */ - lws_http_mark_sse(wsi); - - /* write the body separately */ - - lws_callback_on_writable(wsi); - - return 0; - - case LWS_CALLBACK_HTTP_WRITEABLE: - - lwsl_notice("%s: LWS_CALLBACK_HTTP_WRITEABLE\n", __func__); - - if (!pss) - break; - - /* - * to keep this demo as simple as possible, each client has his - * own private data and timer. - */ - - p += lws_snprintf((char *)p, end - p, - "data: %llu\x0d\x0a\x0d\x0a", - (unsigned long long)time(NULL) - - pss->established); - - if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff(p, start), - LWS_WRITE_HTTP) != lws_ptr_diff(p, start)) - return 1; - - lws_set_timer_usecs(wsi, SECS_REPORT * LWS_USEC_PER_SEC); - - return 0; - - case LWS_CALLBACK_TIMER: - - lwsl_notice("%s: LWS_CALLBACK_TIMER\n", __func__); - lws_callback_on_writable(wsi); - - return 0; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - { "sse", callback_sse, sizeof(struct pss), 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -/* override the default mount for /sse in the URL space */ - -static const struct lws_http_mount mount_sse = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/sse", /* mountpoint URL */ - /* .origin */ NULL, /* protocol */ - /* .def */ NULL, - /* .protocol */ "sse", - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_CALLBACK, /* dynamic */ - /* .mountpoint_len */ 4, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -/* default mount serves the URL space from ./mount-origin */ - -static const struct lws_http_mount mount = { - /* .mount_next */ &mount_sse, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http Server-Side Events | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - - info.protocols = protocols; - info.mounts = &mount; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - info.port = 7681; - if (lws_cmdline_option(argc, argv, "-s")) { - info.port = 443; - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -document.addEventListener("DOMContentLoaded", function() { - -var head = 0, tail = 0, ring = new Array(), es; - - es = new EventSource("/sse/sourcename"); - try { - es.onopen = function() { - // console.log("EventSource opened"); - document.getElementById("r").disabled = 0; - }; - - es.onmessage = function got_packet(msg) { - var n, s = ""; - - // console.log(msg.data); - ring[head] = msg.data + "\n"; - head = (head + 1) % 50; - if (tail === head) - tail = (tail + 1) % 50; - - n = tail; - do { - s = s + ring[n]; - n = (n + 1) % 50; - } while (n !== head); - - document.getElementById("r").value = s; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - /* there is no onclose() for EventSource */ - - } catch(exception) { - alert("

Error" + exception); - } - -}, false); Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ - - - - - - - -
- - Hello from the minimal http Server Side Events example. -

- This is a static page served from ./mount-origin/index.html. -

- It connects back to the server at /sse/sourcename using EventSource()
- and displays the periodic incoming event data below. -

-
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -# lws minimal http Server Side Events - -This demonstates serving both normal content and -content over Server Side Events. - -## build - -``` - $ cmake . && make -``` - -## usage - -You can give -s to listen using https on port :443 - -``` - $ ./lws-minimal-http-server-sse -[2018/04/20 06:09:56:9974] USER: LWS minimal http Server-Side Events | visit http://localhost:7681 -[2018/04/20 06:09:57:0148] NOTICE: Creating Vhost 'default' port 7681, 2 protocols, IPv6 off -``` - -Visit http://localhost:7681, which connects back to the server using SSE -and displays the incoming data. Connecting from multiple browsers shows -content individual to the connection. - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -project(lws-minimal-http-server-sse-ring) -cmake_minimum_required(VERSION 2.8) -include(CheckIncludeFile) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-sse-ring) -set(SRCS minimal-http-server-sse-ring.c) - -MACRO(require_pthreads result) - CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) - if (NOT LWS_HAVE_PTHREAD_H) - if (LWS_WITH_MINIMAL_EXAMPLES) - set(result 0) - else() - message(FATAL_ERROR "threading support requires pthreads") - endif() - endif() -ENDMACRO() - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_pthreads(requirements) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared pthread) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets pthread) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,395 +0,0 @@ -/* - * lws-minimal-http-server-sse - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http server that can serve both normal static - * content and server-side event connections. - * - * To keep it simple, it serves the static stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * - * You can change that by changing mount.origin below. - */ - -#include -#include -#include -#include -#include -#include - -/* one of these created for each message in the ringbuffer */ - -struct msg { - void *payload; /* is malloc'd */ - size_t len; -}; - -/* - * Unlike ws, http is a stateless protocol. This pss only exists for the - * duration of a single http transaction. With http/1.1 keep-alive and http/2, - * that is unrelated to (shorter than) the lifetime of the network connection. - */ -struct pss { - struct pss *pss_list; - struct lws *wsi; - uint32_t tail; -}; - -/* one of these is created for each vhost our protocol is used with */ - -struct vhd { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - - struct pss *pss_list; /* linked-list of live pss*/ - pthread_t pthread_spam[2]; - - pthread_mutex_t lock_ring; /* serialize access to the ring buffer */ - struct lws_ring *ring; /* ringbuffer holding unsent messages */ - char finished; -}; - -static int interrupted; - -#if defined(WIN32) -static void usleep(unsigned long l) { Sleep(l / 1000); } -#endif - - -/* destroys the message when everyone has had a copy of it */ - -static void -__minimal_destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} - -/* - * This runs under the "spam thread" thread context only. - * - * We spawn two threads that generate messages with this. - * - */ - -static void * -thread_spam(void *d) -{ - struct vhd *vhd = (struct vhd *)d; - struct msg amsg; - int len = 128, index = 1, n; - - do { - /* don't generate output if nobody connected */ - if (!vhd->pss_list) - goto wait; - - pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ - - /* only create if space in ringbuffer */ - n = (int)lws_ring_get_count_free_elements(vhd->ring); - if (!n) { - lwsl_user("dropping!\n"); - goto wait_unlock; - } - - amsg.payload = malloc(len); - if (!amsg.payload) { - lwsl_user("OOM: dropping\n"); - goto wait_unlock; - } - n = lws_snprintf((char *)amsg.payload, len, - "%s: tid: %p, msg: %d", __func__, - (void *)pthread_self(), index++); - amsg.len = n; - n = lws_ring_insert(vhd->ring, &amsg, 1); - if (n != 1) { - __minimal_destroy_message(&amsg); - lwsl_user("dropping!\n"); - } else - /* - * This will cause a LWS_CALLBACK_EVENT_WAIT_CANCELLED - * in the lws service thread context. - */ - lws_cancel_service(vhd->context); - -wait_unlock: - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ - -wait: - /* rand() would make more sense but coverity shrieks */ - usleep(100000 + (time(NULL) & 0xffff)); - - } while (!vhd->finished); - - lwsl_notice("thread_spam %p exiting\n", (void *)pthread_self()); - - pthread_exit(NULL); - - return NULL; -} - - -static int -callback_sse(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - struct pss *pss = (struct pss *)user; - struct vhd *vhd = (struct vhd *)lws_protocol_vh_priv_get( - lws_get_vhost(wsi), lws_get_protocol(wsi)); - uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE], - *start = &buf[LWS_PRE], *p = start, - *end = &buf[sizeof(buf) - 1]; - const struct msg *pmsg; - void *retval; - int n; - - switch (reason) { - - /* --- vhost protocol lifecycle --- */ - - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), sizeof(struct vhd)); - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - - vhd->ring = lws_ring_create(sizeof(struct msg), 8, - __minimal_destroy_message); - if (!vhd->ring) - return 1; - - pthread_mutex_init(&vhd->lock_ring, NULL); - - /* start the content-creating threads */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) - if (pthread_create(&vhd->pthread_spam[n], NULL, - thread_spam, vhd)) { - lwsl_err("thread creation failed\n"); - goto init_fail; - } - - return 0; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - init_fail: - vhd->finished = 1; - for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) - if (vhd->pthread_spam[n]) - pthread_join(vhd->pthread_spam[n], &retval); - - if (vhd->ring) - lws_ring_destroy(vhd->ring); - - pthread_mutex_destroy(&vhd->lock_ring); - return 0; - - /* --- http connection lifecycle --- */ - - case LWS_CALLBACK_HTTP: - /* - * `in` contains the url part after our mountpoint /sse, if any - * you can use this to determine what data to return and store - * that in the pss - */ - lwsl_info("%s: LWS_CALLBACK_HTTP: '%s'\n", __func__, - (const char *)in); - - /* SSE requires a http OK response with this content-type */ - - if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, - "text/event-stream", - LWS_ILLEGAL_HTTP_CONTENT_LEN, - &p, end)) - return 1; - - if (lws_finalize_write_http_header(wsi, start, &p, end)) - return 1; - - /* add ourselves to the list of live pss held in the vhd */ - - lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); - pss->tail = lws_ring_get_oldest_tail(vhd->ring); - pss->wsi = wsi; - - /* - * This tells lws we are no longer a normal http stream, - * but are an "immortal" (plus or minus whatever timeout you - * set on it afterwards) SSE stream. In http/2 case that also - * stops idle timeouts being applied to the network connection - * while this wsi is still open. - */ - lws_http_mark_sse(wsi); - - /* write the body separately */ - - lws_callback_on_writable(wsi); - - return 0; - - case LWS_CALLBACK_CLOSED_HTTP: - /* remove our closing pss from the list of live pss */ - - lws_ll_fwd_remove(struct pss, pss_list, pss, vhd->pss_list); - return 0; - - /* --- data transfer --- */ - - case LWS_CALLBACK_HTTP_WRITEABLE: - - lwsl_info("%s: LWS_CALLBACK_HTTP_WRITEABLE\n", __func__); - - pmsg = lws_ring_get_element(vhd->ring, &pss->tail); - if (!pmsg) - break; - - p += lws_snprintf((char *)p, end - p, - "data: %s\x0d\x0a\x0d\x0a", - (const char *)pmsg->payload); - - if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff(p, start), - LWS_WRITE_HTTP) != lws_ptr_diff(p, start)) - return 1; - - lws_ring_consume_and_update_oldest_tail( - vhd->ring, /* lws_ring object */ - struct pss, /* type of objects with tails */ - &pss->tail, /* tail of guy doing the consuming */ - 1, /* number of payload objects being consumed */ - vhd->pss_list, /* head of list of objects with tails */ - tail, /* member name of tail in objects with tails */ - pss_list /* member name of next object in objects with tails */ - ); - - if (lws_ring_get_element(vhd->ring, &pss->tail)) - /* come back as soon as we can write more */ - lws_callback_on_writable(pss->wsi); - - return 0; - - case LWS_CALLBACK_EVENT_WAIT_CANCELLED: - if (!vhd) - break; - /* - * let everybody know we want to write something on them - * as soon as they are ready - */ - lws_start_foreach_llp(struct pss **, ppss, vhd->pss_list) { - lws_callback_on_writable((*ppss)->wsi); - } lws_end_foreach_llp(ppss, pss_list); - return 0; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - { "sse", callback_sse, sizeof(struct pss), 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -/* override the default mount for /sse in the URL space */ - -static const struct lws_http_mount mount_sse = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/sse", /* mountpoint URL */ - /* .origin */ NULL, /* protocol */ - /* .def */ NULL, - /* .protocol */ "sse", - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_CALLBACK, /* dynamic */ - /* .mountpoint_len */ 4, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -/* default mount serves the URL space from ./mount-origin */ - -static const struct lws_http_mount mount = { - /* .mount_next */ &mount_sse, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http Server-Side Events + ring | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.protocols = protocols; - info.mounts = &mount; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -document.addEventListener("DOMContentLoaded", function() { - - var head = 0, tail = 0, ring = new Array(), es; - - es = new EventSource("/sse/sourcename"); - try { - es.onopen = function() { - // console.log("EventSource opened"); - document.getElementById("r").disabled = 0; - }; - - es.onmessage = function got_packet(msg) { - var n, s = ""; - - // console.log(msg.data); - ring[head] = msg.data + "\n"; - head = (head + 1) % 50; - if (tail === head) - tail = (tail + 1) % 50; - - n = tail; - do { - s = s + ring[n]; - n = (n + 1) % 50; - } while (n !== head); - - document.getElementById("r").value = s; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - /* there is no onclose() for EventSource */ - - } catch(exception) { - alert("

Error " + exception); - } - -}, false); Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ - - - - - - - -
- - Hello from the minimal http Server Side Events + Ring example. -

- This is a static page served from ./mount-origin/index.html. -

- It connects back to the server at /sse/sourcename using EventSource()
- and displays the perioding incoming event data below. -

- The data is being produced by two asynchronous threads at the server, - which each sleep for a random period inbetween samples. -

-
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-sse-ring/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-sse-ring/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -# lws minimal http Server Side Events + ringbuffer - -This demonstates serving both normal content and -content over Server Side Events, where all clients -see the same data via a ringbuffer. - -Two separate threads generate content into the -ringbuffer at random intervals. - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-sse -[2018/04/20 06:09:56:9974] USER: LWS minimal http Server-Side Events + ring | visit http://localhost:7681 -[2018/04/20 06:09:57:0148] NOTICE: Creating Vhost 'default' port 7681, 2 protocols, IPv6 off -``` - -Visit http://localhost:7681, which connects back to the server using SSE -and displays the incoming data. Connecting from multiple browsers shows -the same content from the server ringbuffer. - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-http-server-tls) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-tls) -set(SRCS minimal-http-server-tls.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_OPENSSL_SUPPORT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/minimal-http-server-tls.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/minimal-http-server-tls.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/minimal-http-server-tls.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/minimal-http-server-tls.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -/* - * lws-minimal-http-server-tls - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws, - * with three extra lines giving it tls (ssl) capabilities, which in - * turn allow operation with HTTP/2 if lws was configured for it. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server TLS | visit https://localhost:7681\n"); - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - - if (lws_cmdline_option(argc, argv, "-h")) - info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ - - - - -
- -

404

- Sorry, that file doesn't exist. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -document.addEventListener("DOMContentLoaded", function() { - - var transport_protocol = ""; - - if ( performance && performance.timing.nextHopProtocol ) { - transport_protocol = performance.timing.nextHopProtocol; - } else if ( window.chrome && window.chrome.loadTimes ) { - transport_protocol = window.chrome.loadTimes().connectionInfo; - } else { - - var p = performance.getEntriesByType("resource"); - for (var i=0; i < p.length; i++) { - var value = "nextHopProtocol" in p[i]; - if (value) - transport_protocol = p[i].nextHopProtocol; - } - } - - if (transport_protocol === "h2") - document.getElementById("transport").innerHTML = ""; -}, false); Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/favicon.ico differ Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/http2.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/http2.png differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ - - - - - - - -
- - Hello from the minimal https server example. -
- You can confirm the 404 page handler by going to this - nonexistant page. -
-
- - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -# lws minimal http server with tls - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-tls -[2018/03/20 13:23:13:0131] USER: LWS minimal http server TLS | visit https://localhost:7681 -[2018/03/20 13:23:13:0142] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/03/20 13:23:13:0142] NOTICE: Using SSL mode -[2018/03/20 13:23:13:0146] NOTICE: SSL ECDH curve 'prime256v1' -[2018/03/20 13:23:13:0146] NOTICE: HTTP2 / ALPN enabled -[2018/03/20 13:23:13:0195] NOTICE: lws_tls_client_create_vhost_context: doing cert filepath localhost-100y.cert -[2018/03/20 13:23:13:0195] NOTICE: Loaded client cert localhost-100y.cert -[2018/03/20 13:23:13:0195] NOTICE: lws_tls_client_create_vhost_context: doing private key filepath -[2018/03/20 13:23:13:0196] NOTICE: Loaded client cert private key localhost-100y.key -[2018/03/20 13:23:13:0196] NOTICE: created client ssl context for default -[2018/03/20 13:23:14:0207] NOTICE: vhost default: cert expiry: 730459d -``` - -Visit https://localhost:7681 - -Because it uses a selfsigned certificate, you will have to make an exception for it in your browser. - -## Certificate creation - -The selfsigned certs provided were created with - -``` -echo -e "GB\nErewhon\nAll around\nlibwebsockets-test\n\nlocalhost\nnone@invalid.org\n" | openssl req -new -newkey rsa:4096 -days 36500 -nodes -x509 -keyout "localhost-100y.key" -out "localhost-100y.cert" -``` - -they cover "localhost" and last 100 years from 2018-03-20. - -You can replace them with commercial certificates matching your hostname. - -## HTTP/2 - -If you built lws with `-DLWS_WITH_HTTP2=1` at cmake, this simple server is also http/2 capable -out of the box. If the index.html was loaded over http/2, it will display an HTTP 2 png. diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-http-server-tls-80) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-tls-80) -set(SRCS minimal-http-server-tls-80.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_OPENSSL_SUPPORT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/localhost-100y.key libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/minimal-http-server-tls-80.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/minimal-http-server-tls-80.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/minimal-http-server-tls-80.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/minimal-http-server-tls-80.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,136 +0,0 @@ -/* - * lws-minimal-http-server-tls-80 - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws, - * with three extra lines giving it tls (ssl) capabilities, which in - * turn allow operation with HTTP/2 if lws was configured for it. - * - * In addition, it runs a vhost on port 80 with the job of redirecting - * and upgrading http clients that came in on port 80 to https on port 443. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -static int interrupted; - -static const struct lws_http_mount mount80 = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "localhost/", - /* .def */ "/", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_REDIR_HTTPS, /* https redir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server TLS + 80 | visit https://localhost\n"); - lwsl_user(" Run as ROOT so can listen on 443\n"); - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - - info.port = 80; - info.mounts = &mount80; - info.vhost_name = "localhost80"; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("Failed to create tls vhost\n"); - goto bail; - } - - info.port = 443; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - info.vhost_name = "localhost"; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("Failed to create tls vhost\n"); - goto bail; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail: - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ - - - - - - - -
-

404

- Sorry, that file doesn't exist. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -document.addEventListener("DOMContentLoaded", function() { - - var transport_protocol = ""; - - if (performance && performance.timing.nextHopProtocol) { - transport_protocol = performance.timing.nextHopProtocol; - } else if (window.chrome && window.chrome.loadTimes) { - transport_protocol = window.chrome.loadTimes().connectionInfo; - } else { - var p = performance.getEntriesByType("resource"); - for (var i = 0; i < p.length; i++) { - var value = "nextHopProtocol" in p[i]; - if (value) - transport_protocol = p[i].nextHopProtocol; - } - } - - if (transport_protocol === "h2") - document.getElementById("transport").innerHTML = ""; - -}, false); Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/favicon.ico differ Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/http2.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/http2.png differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ - - - - - - - -
- - Hello from the minimal https server example. -
- You can confirm the 404 page handler by going to this - nonexistant page. -
-
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-80/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-80/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -# lws minimal http server with tls and port 80 redirect - -## build - -``` - $ cmake . && make -``` - -## usage - -Because this listens on low ports (80 + 443), it must be run as root. - -``` - $ sudo ./lws-minimal-http-server-tls-80 -[2018/03/20 13:23:13:0131] USER: LWS minimal http server TLS | visit https://localhost:7681 -[2018/03/20 13:23:13:0142] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/03/20 13:23:13:0142] NOTICE: Using SSL mode -[2018/03/20 13:23:13:0146] NOTICE: SSL ECDH curve 'prime256v1' -[2018/03/20 13:23:13:0146] NOTICE: HTTP2 / ALPN enabled -[2018/03/20 13:23:13:0195] NOTICE: lws_tls_client_create_vhost_context: doing cert filepath localhost-100y.cert -[2018/03/20 13:23:13:0195] NOTICE: Loaded client cert localhost-100y.cert -[2018/03/20 13:23:13:0195] NOTICE: lws_tls_client_create_vhost_context: doing private key filepath -[2018/03/20 13:23:13:0196] NOTICE: Loaded client cert private key localhost-100y.key -[2018/03/20 13:23:13:0196] NOTICE: created client ssl context for default -[2018/03/20 13:23:14:0207] NOTICE: vhost default: cert expiry: 730459d -``` - -Visit http://localhost - -This will go first to port 80 using http, where it will be redirected to -https and port 443 - -``` -07:41:48.596918 IP localhost.http > localhost.52662: Flags [P.], seq 1:100, ack 416, win 350, options [nop,nop,TS val 3906619933 ecr 3906619933], length 99: HTTP: HTTP/1.1 301 Redirect - 0x0000: 4500 0097 3f8f 4000 4006 fccf 7f00 0001 E...?.@.@....... - 0x0010: 7f00 0001 0050 cdb6 6601 dfa7 922a 4c06 .....P..f....*L. - 0x0020: 8018 015e fe8b 0000 0101 080a e8da 4a1d ...^..........J. - 0x0030: e8da 4a1d 4854 5450 2f31 2e31 2033 3031 ..J.HTTP/1.1.301 - 0x0040: 2052 6564 6972 6563 740d 0a6c 6f63 6174 .Redirect..locat - 0x0050: 696f 6e3a 2068 7474 7073 3a2f 2f6c 6f63 ion:.https://loc - 0x0060: 616c 686f 7374 2f0d 0a63 6f6e 7465 6e74 alhost/..content - 0x0070: 2d74 7970 653a 2074 6578 742f 6874 6d6c -type:.text/html - 0x0080: 0d0a 636f 6e74 656e 742d 6c65 6e67 7468 ..content-length - 0x0090: 3a20 300d 0a0d 0a -``` - -Because :443 uses a selfsigned certificate, you will have to make an exception for it in your browser. - -## Certificate creation - -The selfsigned certs provided were created with - -``` -echo -e "GB\nErewhon\nAll around\nlibwebsockets-test\n\nlocalhost\nnone@invalid.org\n" | openssl req -new -newkey rsa:4096 -days 36500 -nodes -x509 -keyout "localhost-100y.key" -out "localhost-100y.cert" -``` - -they cover "localhost" and last 100 years from 2018-03-20. - -You can replace them with commercial certificates matching your hostname. - -## HTTP/2 - -If you built lws with `-DLWS_WITH_HTTP2=1` at cmake, this simple server is also http/2 capable -out of the box. If the index.html was loaded over http/2, it will display an HTTP 2 png. diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-http-server-tls-mem) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-http-server-tls-mem) -set(SRCS minimal-http-server-tls-mem.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_OPENSSL_SUPPORT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/minimal-http-server-tls-mem.c libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/minimal-http-server-tls-mem.c --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/minimal-http-server-tls-mem.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/minimal-http-server-tls-mem.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,465 +0,0 @@ -/* - * lws-minimal-http-server-tls - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws, - * with three extra lines giving it tls (ssl) capabilities, which in - * turn allow operation with HTTP/2 if lws was configured for it. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -/* the cert and key as PEM */ - -static const char *cert_pem = - "-----BEGIN CERTIFICATE-----\n" - "MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD\n" - "VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb\n" - "MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx\n" - "HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3\n" - "WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl\n" - "d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0\n" - "cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA\n" - "aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW\n" - "aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8\n" - "Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek\n" - "LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH\n" - "KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6\n" - "jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ\n" - "Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz\n" - "TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK\n" - "Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0\n" - "nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo\n" - "GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p\n" - "sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU\n" - "9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar\n" - "jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow\n" - "YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA\n" - "xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P\n" - "wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34\n" - "H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv\n" - "xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk\n" - "ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g\n" - "1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA\n" - "AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg\n" - "mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s\n" - "8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX\n" - "e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE=\n" - "-----END CERTIFICATE-----\n", - - *key_pem = - "-----BEGIN PRIVATE KEY-----\n" - "MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ\n" - "PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK\n" - "nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ\n" - "toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU\n" - "0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT\n" - "J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS\n" - "Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN\n" - "uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9\n" - "fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn\n" - "zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au\n" - "ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB\n" - "QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f\n" - "qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+\n" - "vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9\n" - "fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A\n" - "Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT\n" - "G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/\n" - "HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8\n" - "YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl\n" - "xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs\n" - "esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw\n" - "zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz\n" - "mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw\n" - "au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77\n" - "40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5\n" - "YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH\n" - "PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj\n" - "W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR\n" - "naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6\n" - "2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m\n" - "39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79\n" - "J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC\n" - "R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp\n" - "Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh\n" - "BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE\n" - "fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ\n" - "x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI\n" - "UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM\n" - "OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L\n" - "65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A\n" - "aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5\n" - "SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S\n" - "me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I\n" - "G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK\n" - "TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY\n" - "56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2\n" - "gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr\n" - "Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E\n" - "NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs\n" - "fBrpEY1IATtPq1taBZZogRqI3rOkkPk=\n" - "-----END PRIVATE KEY-----\n" - ; - -static const uint8_t cert_der[] = { - 0x30, 0x82, 0x05, 0xe6, 0x30, 0x82, 0x03, 0xce, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, - 0xda, 0xb9, 0xd0, 0x8b, 0xb0, 0x3c, 0x52, 0xa0, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, - 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, - 0x0c, 0x07, 0x45, 0x72, 0x65, 0x77, 0x68, 0x6f, 0x6e, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, - 0x04, 0x07, 0x0c, 0x0a, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x31, 0x1b, - 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x12, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06, - 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x31, - 0x1f, 0x30, 0x1d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, - 0x6e, 0x6f, 0x6e, 0x65, 0x40, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x2e, 0x6f, 0x72, 0x67, - 0x30, 0x20, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x33, 0x32, 0x30, 0x30, 0x34, 0x31, 0x36, 0x30, 0x37, - 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x31, 0x38, 0x30, 0x32, 0x32, 0x34, 0x30, 0x34, 0x31, 0x36, 0x30, - 0x37, 0x5a, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, - 0x47, 0x42, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x07, 0x45, 0x72, 0x65, - 0x77, 0x68, 0x6f, 0x6e, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0a, 0x41, - 0x6c, 0x6c, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, - 0x04, 0x0a, 0x0c, 0x12, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x73, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, - 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x09, - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, 0x6e, 0x6f, 0x6e, 0x65, 0x40, - 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x82, 0x02, 0x22, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, - 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xa3, 0x62, 0xdb, 0x96, - 0x68, 0x80, 0x82, 0x63, 0x4b, 0x49, 0x3e, 0xe6, 0xf1, 0xa4, 0x88, 0x08, 0x2f, 0xe5, 0x96, 0x9b, - 0x3f, 0xdf, 0x98, 0xaf, 0x08, 0x42, 0xbd, 0x75, 0x5a, 0xd7, 0x9e, 0xeb, 0xf2, 0x14, 0xc9, 0x49, - 0x68, 0xe4, 0x8e, 0xb4, 0xda, 0x6a, 0xb5, 0xa9, 0xc2, 0xe1, 0x4f, 0xf9, 0x26, 0xa6, 0x84, 0x7c, - 0x0e, 0x2d, 0xc3, 0x02, 0x61, 0xca, 0x9d, 0x25, 0x9d, 0x3d, 0x6b, 0x67, 0xd4, 0x1b, 0x57, 0x2c, - 0x4a, 0xcb, 0x95, 0x48, 0x87, 0x81, 0x90, 0xeb, 0x65, 0x62, 0x27, 0x98, 0x40, 0x63, 0x28, 0xcd, - 0x43, 0x65, 0xff, 0x82, 0xbc, 0xd1, 0x99, 0xf8, 0x4c, 0xcf, 0x80, 0x1b, 0xf9, 0x9d, 0x37, 0xa4, - 0x2d, 0x67, 0x1f, 0x23, 0x96, 0x59, 0xb6, 0x81, 0xae, 0x20, 0xfd, 0x43, 0x97, 0xf2, 0x24, 0x34, - 0x3c, 0x3c, 0xcc, 0x5c, 0xf8, 0x72, 0x98, 0x8c, 0x7b, 0xf0, 0x45, 0x19, 0xe9, 0xb2, 0xc5, 0xd1, - 0xe1, 0x2e, 0xb2, 0x87, 0x4a, 0x6f, 0x04, 0xa3, 0xe9, 0xd3, 0xef, 0x7e, 0x2d, 0x22, 0xd9, 0xc7, - 0x29, 0x3f, 0xe6, 0xe8, 0x34, 0x94, 0xd3, 0x19, 0x59, 0xd7, 0x77, 0x7a, 0x7a, 0x12, 0xd1, 0x9b, - 0xbf, 0xfe, 0x37, 0x1e, 0x3b, 0x33, 0x75, 0xcc, 0x4d, 0x11, 0xf9, 0xa8, 0xa3, 0xff, 0xed, 0x34, - 0xc4, 0xda, 0xcd, 0x14, 0xeb, 0xe3, 0x34, 0xb6, 0xc1, 0x88, 0xdb, 0x3a, 0x51, 0x8b, 0xe9, 0xba, - 0x8f, 0x38, 0x4d, 0xc8, 0xc0, 0x53, 0x27, 0x5b, 0xb9, 0xf2, 0xa0, 0x1e, 0xdd, 0x95, 0xb9, 0xff, - 0xe6, 0x00, 0x8a, 0xe6, 0x58, 0x00, 0x1e, 0xa7, 0xe5, 0xb8, 0x54, 0xa7, 0x8a, 0x05, 0xb8, 0x1e, - 0x70, 0x61, 0xb7, 0x01, 0xcb, 0x05, 0x51, 0xf2, 0xe8, 0xc8, 0x9e, 0x91, 0x7c, 0x6e, 0xe5, 0x90, - 0x52, 0x3c, 0xb9, 0x37, 0xca, 0x52, 0x36, 0x9e, 0xec, 0xcd, 0xd6, 0x2c, 0x9c, 0xb2, 0x69, 0xbc, - 0x07, 0x74, 0xb2, 0x26, 0xeb, 0x34, 0xf8, 0xc2, 0xd0, 0x54, 0x02, 0x36, 0xba, 0x4d, 0x8e, 0x02, - 0x66, 0x20, 0xad, 0xfe, 0x98, 0xa9, 0x38, 0x91, 0x75, 0xfb, 0x65, 0x3c, 0x1e, 0x7e, 0x80, 0x33, - 0x4c, 0xae, 0x25, 0xda, 0x91, 0xcd, 0xb8, 0x2e, 0x77, 0x41, 0x57, 0x3f, 0x10, 0x5f, 0xbe, 0x18, - 0x12, 0xc0, 0xc6, 0x6b, 0xc2, 0x0e, 0xaf, 0x59, 0xa4, 0xc2, 0x18, 0x8b, 0xb3, 0xa6, 0xce, 0x49, - 0x00, 0x28, 0xa0, 0xbd, 0x51, 0xee, 0x84, 0x7f, 0x6d, 0x7b, 0x2c, 0x54, 0x02, 0x14, 0x80, 0x4a, - 0x23, 0x3b, 0xfd, 0x72, 0x08, 0xbd, 0x7f, 0x03, 0xcc, 0x2e, 0x1a, 0xca, 0x95, 0xea, 0x15, 0x44, - 0xdb, 0x1e, 0x70, 0x1b, 0x02, 0x3f, 0x9e, 0xbd, 0x5a, 0x02, 0x57, 0x85, 0x49, 0xf0, 0x7f, 0x69, - 0x68, 0x9f, 0x87, 0xc4, 0x66, 0xbd, 0xfe, 0xbd, 0x1b, 0x9c, 0xf6, 0xc8, 0x5f, 0xaa, 0x75, 0x74, - 0x9c, 0xf3, 0x75, 0x20, 0xc4, 0xa7, 0xcd, 0x70, 0x9a, 0xb2, 0xde, 0xc8, 0xd9, 0xf8, 0xae, 0x45, - 0x77, 0x48, 0xcf, 0xde, 0x8a, 0x8e, 0x51, 0x90, 0xa4, 0xfe, 0x17, 0x7c, 0xd5, 0x40, 0xf9, 0x11, - 0x8b, 0xed, 0xa3, 0x27, 0x58, 0xe1, 0x48, 0x69, 0x5a, 0xca, 0x58, 0xbc, 0xc0, 0xb6, 0x0c, 0xe8, - 0x18, 0xc4, 0xef, 0x3f, 0xf0, 0x2e, 0x7a, 0x12, 0x97, 0x9d, 0xc0, 0x49, 0x85, 0x8b, 0x56, 0xd2, - 0x5b, 0x53, 0x8a, 0x85, 0x71, 0xfb, 0x9c, 0x93, 0x61, 0x20, 0x19, 0x5a, 0x5f, 0x88, 0xb2, 0xc9, - 0x97, 0x8d, 0xe7, 0xf1, 0x26, 0xa6, 0x22, 0xdb, 0xfe, 0xd0, 0x5a, 0x6b, 0xf5, 0x40, 0x2f, 0x69, - 0xb0, 0xd7, 0x23, 0x4c, 0xc6, 0x81, 0x40, 0xb3, 0x74, 0xdd, 0x3d, 0x50, 0x7a, 0x56, 0xec, 0xed, - 0x8d, 0xbb, 0xb3, 0x17, 0x44, 0x9c, 0xd5, 0x2d, 0x87, 0x89, 0x08, 0xfb, 0x02, 0x03, 0x01, 0x00, - 0x01, 0xa3, 0x53, 0x30, 0x51, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, - 0xf6, 0x66, 0x14, 0xdb, 0x7b, 0x56, 0xdb, 0x3b, 0x28, 0x9a, 0x42, 0x93, 0x01, 0x76, 0xab, 0x8e, - 0xbd, 0xaf, 0x8e, 0xeb, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, - 0x14, 0xf6, 0x66, 0x14, 0xdb, 0x7b, 0x56, 0xdb, 0x3b, 0x28, 0x9a, 0x42, 0x93, 0x01, 0x76, 0xab, - 0x8e, 0xbd, 0xaf, 0x8e, 0xeb, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, - 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x36, 0x32, 0x01, 0x32, 0xba, 0x30, - 0x60, 0xd0, 0x9b, 0x84, 0x02, 0x5d, 0x3f, 0xb7, 0x61, 0x96, 0x14, 0xf6, 0x45, 0x41, 0x51, 0x75, - 0xe4, 0x54, 0x24, 0x3d, 0x08, 0xc6, 0xb1, 0xff, 0x86, 0x4b, 0xdb, 0xea, 0x6c, 0x87, 0x1e, 0x72, - 0xbc, 0x9c, 0xe6, 0x1e, 0xcc, 0x53, 0xe3, 0x52, 0x59, 0x91, 0x29, 0x48, 0x0d, 0x10, 0x3b, 0x80, - 0xc5, 0xb9, 0xd7, 0x67, 0x33, 0xdd, 0x09, 0x13, 0x55, 0xf5, 0x5d, 0xa6, 0x4a, 0x16, 0xd7, 0xbc, - 0x2c, 0xa2, 0x0d, 0x8e, 0xd6, 0x09, 0x01, 0x36, 0x06, 0x7e, 0x38, 0xcf, 0x6e, 0x8e, 0xd2, 0xe5, - 0x95, 0x93, 0xee, 0xc3, 0x34, 0xd2, 0xc7, 0xf4, 0x19, 0xe4, 0xc1, 0x4b, 0x4e, 0x9c, 0xcf, 0x4f, - 0xc2, 0xd9, 0x83, 0xf6, 0x98, 0x56, 0x7b, 0x19, 0xb8, 0xab, 0x61, 0xa7, 0x4e, 0xc8, 0x8b, 0xe9, - 0x49, 0x7a, 0x73, 0x2d, 0x10, 0x95, 0x32, 0x56, 0x29, 0x52, 0xc4, 0x51, 0x04, 0x3a, 0xc9, 0xd6, - 0xb9, 0xf3, 0x67, 0xb6, 0xdc, 0x9d, 0x40, 0x5e, 0xab, 0x6a, 0x15, 0xca, 0x5f, 0xa0, 0x4d, 0xf8, - 0x1f, 0x76, 0x9f, 0x12, 0x21, 0xb2, 0xf3, 0xcd, 0x9b, 0xf9, 0x90, 0x62, 0xc2, 0x47, 0x95, 0xfa, - 0x8a, 0xba, 0x5d, 0x51, 0x7c, 0xb0, 0x5c, 0xab, 0xf7, 0x36, 0x2b, 0xbf, 0xd0, 0xaf, 0x59, 0x36, - 0x25, 0x92, 0x94, 0xd0, 0x7c, 0xb4, 0xd9, 0x4a, 0xc8, 0x0f, 0x74, 0x41, 0xd8, 0x55, 0xc8, 0xef, - 0xc5, 0x0d, 0x83, 0xf9, 0x7c, 0x83, 0x47, 0x46, 0x91, 0x2d, 0x19, 0x6f, 0xc5, 0x46, 0xbd, 0x74, - 0x71, 0x85, 0x1c, 0xb2, 0x02, 0x1b, 0x7e, 0x09, 0xba, 0xae, 0x40, 0x8b, 0xa9, 0x4c, 0xd4, 0x4b, - 0x28, 0x0f, 0xc1, 0xd2, 0xb0, 0x9a, 0x4c, 0x72, 0x6a, 0xc7, 0xec, 0xc5, 0xb0, 0xd9, 0xc2, 0xa4, - 0xba, 0x30, 0xb7, 0xac, 0xc7, 0x45, 0x4e, 0xdb, 0x5e, 0xf3, 0x7c, 0x05, 0xd6, 0xeb, 0x85, 0xe0, - 0x58, 0xd4, 0x0b, 0xbd, 0xbe, 0x4a, 0x67, 0x10, 0x37, 0xb0, 0x37, 0xf3, 0xa0, 0x42, 0xfe, 0x79, - 0x36, 0x4d, 0x3b, 0x09, 0x6b, 0x04, 0xc3, 0xce, 0xac, 0x0e, 0xbb, 0xf5, 0x5d, 0x66, 0xfd, 0xa0, - 0xd5, 0x6a, 0x53, 0x1e, 0x5b, 0xa6, 0x94, 0x29, 0x59, 0x78, 0xff, 0x86, 0xfe, 0x39, 0x12, 0xc8, - 0x3c, 0x2a, 0x36, 0x74, 0xee, 0xd5, 0xaa, 0x1d, 0x0e, 0x65, 0x1a, 0xe3, 0x16, 0x68, 0x75, 0xf8, - 0x4f, 0xd4, 0x75, 0x8f, 0xc1, 0x42, 0x85, 0x72, 0xaf, 0x28, 0x42, 0xbd, 0x78, 0xf1, 0x06, 0x00, - 0x00, 0xe9, 0x5b, 0x50, 0xe2, 0x50, 0x53, 0xb4, 0x30, 0x45, 0x67, 0x75, 0x55, 0xb9, 0xf0, 0x84, - 0x3b, 0x50, 0x59, 0x70, 0xbd, 0xd8, 0x0d, 0xb0, 0xd6, 0x7f, 0xf1, 0x91, 0x94, 0x91, 0xd4, 0x13, - 0x3f, 0x35, 0x44, 0x83, 0x86, 0x40, 0x52, 0x51, 0x4d, 0x56, 0x8c, 0xc6, 0xd6, 0x83, 0xa1, 0xa0, - 0x9a, 0x72, 0x19, 0x2d, 0x17, 0xab, 0x40, 0x2b, 0xb5, 0x3a, 0x8c, 0xeb, 0xf3, 0xba, 0xce, 0x42, - 0xa4, 0x1a, 0x90, 0xf9, 0x32, 0xb7, 0xc0, 0x54, 0x48, 0xd2, 0xb7, 0x2b, 0x8d, 0xa3, 0xda, 0xa7, - 0x1f, 0x84, 0x03, 0x8d, 0x75, 0x19, 0x7c, 0x1e, 0xaf, 0x10, 0xb3, 0x9a, 0x6e, 0xa7, 0x2f, 0xac, - 0xf2, 0xc7, 0x42, 0x18, 0x39, 0x70, 0x47, 0x72, 0x4d, 0x08, 0xcb, 0xfa, 0xbb, 0x8f, 0x0e, 0x2b, - 0xce, 0xc5, 0xe2, 0x67, 0x08, 0xc6, 0x19, 0x12, 0x79, 0xf1, 0x49, 0x50, 0x52, 0x08, 0xdb, 0x9a, - 0x42, 0x18, 0xde, 0x56, 0xb4, 0x4e, 0x29, 0xe6, 0x5f, 0xbd, 0x72, 0x73, 0xb5, 0x1a, 0xb2, 0x17, - 0x7b, 0x61, 0xe5, 0xff, 0xb3, 0x34, 0x73, 0xf9, 0x5b, 0x67, 0x81, 0x6f, 0x5e, 0x00, 0x11, 0x95, - 0xec, 0x76, 0xae, 0x48, 0x12, 0xd0, 0xa6, 0xb4, 0xe8, 0x71, -}, key_der[] = { - 0x30, 0x82, 0x09, 0x43, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x09, 0x2d, 0x30, 0x82, 0x09, 0x29, 0x02, 0x01, - 0x00, 0x02, 0x82, 0x02, 0x01, 0x00, 0xa3, 0x62, 0xdb, 0x96, 0x68, 0x80, 0x82, 0x63, 0x4b, 0x49, - 0x3e, 0xe6, 0xf1, 0xa4, 0x88, 0x08, 0x2f, 0xe5, 0x96, 0x9b, 0x3f, 0xdf, 0x98, 0xaf, 0x08, 0x42, - 0xbd, 0x75, 0x5a, 0xd7, 0x9e, 0xeb, 0xf2, 0x14, 0xc9, 0x49, 0x68, 0xe4, 0x8e, 0xb4, 0xda, 0x6a, - 0xb5, 0xa9, 0xc2, 0xe1, 0x4f, 0xf9, 0x26, 0xa6, 0x84, 0x7c, 0x0e, 0x2d, 0xc3, 0x02, 0x61, 0xca, - 0x9d, 0x25, 0x9d, 0x3d, 0x6b, 0x67, 0xd4, 0x1b, 0x57, 0x2c, 0x4a, 0xcb, 0x95, 0x48, 0x87, 0x81, - 0x90, 0xeb, 0x65, 0x62, 0x27, 0x98, 0x40, 0x63, 0x28, 0xcd, 0x43, 0x65, 0xff, 0x82, 0xbc, 0xd1, - 0x99, 0xf8, 0x4c, 0xcf, 0x80, 0x1b, 0xf9, 0x9d, 0x37, 0xa4, 0x2d, 0x67, 0x1f, 0x23, 0x96, 0x59, - 0xb6, 0x81, 0xae, 0x20, 0xfd, 0x43, 0x97, 0xf2, 0x24, 0x34, 0x3c, 0x3c, 0xcc, 0x5c, 0xf8, 0x72, - 0x98, 0x8c, 0x7b, 0xf0, 0x45, 0x19, 0xe9, 0xb2, 0xc5, 0xd1, 0xe1, 0x2e, 0xb2, 0x87, 0x4a, 0x6f, - 0x04, 0xa3, 0xe9, 0xd3, 0xef, 0x7e, 0x2d, 0x22, 0xd9, 0xc7, 0x29, 0x3f, 0xe6, 0xe8, 0x34, 0x94, - 0xd3, 0x19, 0x59, 0xd7, 0x77, 0x7a, 0x7a, 0x12, 0xd1, 0x9b, 0xbf, 0xfe, 0x37, 0x1e, 0x3b, 0x33, - 0x75, 0xcc, 0x4d, 0x11, 0xf9, 0xa8, 0xa3, 0xff, 0xed, 0x34, 0xc4, 0xda, 0xcd, 0x14, 0xeb, 0xe3, - 0x34, 0xb6, 0xc1, 0x88, 0xdb, 0x3a, 0x51, 0x8b, 0xe9, 0xba, 0x8f, 0x38, 0x4d, 0xc8, 0xc0, 0x53, - 0x27, 0x5b, 0xb9, 0xf2, 0xa0, 0x1e, 0xdd, 0x95, 0xb9, 0xff, 0xe6, 0x00, 0x8a, 0xe6, 0x58, 0x00, - 0x1e, 0xa7, 0xe5, 0xb8, 0x54, 0xa7, 0x8a, 0x05, 0xb8, 0x1e, 0x70, 0x61, 0xb7, 0x01, 0xcb, 0x05, - 0x51, 0xf2, 0xe8, 0xc8, 0x9e, 0x91, 0x7c, 0x6e, 0xe5, 0x90, 0x52, 0x3c, 0xb9, 0x37, 0xca, 0x52, - 0x36, 0x9e, 0xec, 0xcd, 0xd6, 0x2c, 0x9c, 0xb2, 0x69, 0xbc, 0x07, 0x74, 0xb2, 0x26, 0xeb, 0x34, - 0xf8, 0xc2, 0xd0, 0x54, 0x02, 0x36, 0xba, 0x4d, 0x8e, 0x02, 0x66, 0x20, 0xad, 0xfe, 0x98, 0xa9, - 0x38, 0x91, 0x75, 0xfb, 0x65, 0x3c, 0x1e, 0x7e, 0x80, 0x33, 0x4c, 0xae, 0x25, 0xda, 0x91, 0xcd, - 0xb8, 0x2e, 0x77, 0x41, 0x57, 0x3f, 0x10, 0x5f, 0xbe, 0x18, 0x12, 0xc0, 0xc6, 0x6b, 0xc2, 0x0e, - 0xaf, 0x59, 0xa4, 0xc2, 0x18, 0x8b, 0xb3, 0xa6, 0xce, 0x49, 0x00, 0x28, 0xa0, 0xbd, 0x51, 0xee, - 0x84, 0x7f, 0x6d, 0x7b, 0x2c, 0x54, 0x02, 0x14, 0x80, 0x4a, 0x23, 0x3b, 0xfd, 0x72, 0x08, 0xbd, - 0x7f, 0x03, 0xcc, 0x2e, 0x1a, 0xca, 0x95, 0xea, 0x15, 0x44, 0xdb, 0x1e, 0x70, 0x1b, 0x02, 0x3f, - 0x9e, 0xbd, 0x5a, 0x02, 0x57, 0x85, 0x49, 0xf0, 0x7f, 0x69, 0x68, 0x9f, 0x87, 0xc4, 0x66, 0xbd, - 0xfe, 0xbd, 0x1b, 0x9c, 0xf6, 0xc8, 0x5f, 0xaa, 0x75, 0x74, 0x9c, 0xf3, 0x75, 0x20, 0xc4, 0xa7, - 0xcd, 0x70, 0x9a, 0xb2, 0xde, 0xc8, 0xd9, 0xf8, 0xae, 0x45, 0x77, 0x48, 0xcf, 0xde, 0x8a, 0x8e, - 0x51, 0x90, 0xa4, 0xfe, 0x17, 0x7c, 0xd5, 0x40, 0xf9, 0x11, 0x8b, 0xed, 0xa3, 0x27, 0x58, 0xe1, - 0x48, 0x69, 0x5a, 0xca, 0x58, 0xbc, 0xc0, 0xb6, 0x0c, 0xe8, 0x18, 0xc4, 0xef, 0x3f, 0xf0, 0x2e, - 0x7a, 0x12, 0x97, 0x9d, 0xc0, 0x49, 0x85, 0x8b, 0x56, 0xd2, 0x5b, 0x53, 0x8a, 0x85, 0x71, 0xfb, - 0x9c, 0x93, 0x61, 0x20, 0x19, 0x5a, 0x5f, 0x88, 0xb2, 0xc9, 0x97, 0x8d, 0xe7, 0xf1, 0x26, 0xa6, - 0x22, 0xdb, 0xfe, 0xd0, 0x5a, 0x6b, 0xf5, 0x40, 0x2f, 0x69, 0xb0, 0xd7, 0x23, 0x4c, 0xc6, 0x81, - 0x40, 0xb3, 0x74, 0xdd, 0x3d, 0x50, 0x7a, 0x56, 0xec, 0xed, 0x8d, 0xbb, 0xb3, 0x17, 0x44, 0x9c, - 0xd5, 0x2d, 0x87, 0x89, 0x08, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x02, 0x00, 0x55, - 0x9e, 0xf0, 0xc4, 0x19, 0x6f, 0x7e, 0xe4, 0xda, 0x07, 0x40, 0x57, 0x76, 0x3a, 0x6a, 0xaf, 0x1f, - 0xaa, 0x89, 0x0a, 0x42, 0xa6, 0xc2, 0x34, 0xb7, 0x77, 0x82, 0x21, 0x85, 0xc1, 0x89, 0x1e, 0xcc, - 0x75, 0xe8, 0x25, 0xf8, 0x3a, 0x0e, 0x2e, 0xe8, 0x67, 0x13, 0x5c, 0x2b, 0x2c, 0x37, 0xe4, 0xb1, - 0x44, 0x82, 0x19, 0x20, 0xb5, 0x0a, 0x84, 0xad, 0x0a, 0xa8, 0xdf, 0x95, 0x4f, 0x22, 0x81, 0xfe, - 0xbd, 0x75, 0x29, 0x58, 0xe8, 0xe7, 0x0a, 0x63, 0x38, 0x9a, 0xe1, 0x40, 0xf7, 0xf7, 0x17, 0xea, - 0x66, 0x0c, 0x73, 0xc4, 0xe6, 0x26, 0xc8, 0x34, 0x7b, 0x02, 0xdd, 0x04, 0x23, 0x99, 0x57, 0x0f, - 0xb0, 0x3c, 0x00, 0x65, 0x6a, 0xac, 0xfe, 0xd1, 0x43, 0xa2, 0x48, 0xc3, 0x1f, 0xb6, 0x99, 0x3d, - 0x7f, 0x3f, 0x49, 0xc0, 0x67, 0x7c, 0x11, 0x1c, 0x81, 0xb1, 0x3f, 0xad, 0x93, 0x74, 0x22, 0xe8, - 0x3d, 0x2f, 0x3d, 0x95, 0x6c, 0x0b, 0x52, 0xaa, 0xc7, 0x12, 0xff, 0x73, 0x02, 0x05, 0x77, 0x71, - 0xdf, 0xd9, 0x90, 0x6d, 0x25, 0x77, 0xb4, 0x28, 0x19, 0xf5, 0xa6, 0x4b, 0x56, 0x86, 0xde, 0x40, - 0x2a, 0xac, 0x7d, 0x9a, 0x57, 0x76, 0x3a, 0xf9, 0x7b, 0x36, 0x38, 0x22, 0x0b, 0x51, 0x71, 0xf6, - 0xbf, 0x9f, 0x67, 0x0f, 0xe2, 0x39, 0xa6, 0xc5, 0x17, 0x04, 0x00, 0xe1, 0xda, 0xfe, 0x47, 0xc9, - 0x84, 0x30, 0xaf, 0xfb, 0x6d, 0xde, 0x15, 0x5d, 0xf4, 0x35, 0xa3, 0xf4, 0x06, 0x19, 0xb3, 0x13, - 0x1b, 0xeb, 0xa5, 0x16, 0xbb, 0x22, 0x0f, 0x23, 0xfe, 0xac, 0x12, 0x00, 0x68, 0x60, 0xb4, 0x8b, - 0xb8, 0x03, 0x8c, 0xb0, 0x08, 0x05, 0x07, 0x83, 0x84, 0xfe, 0x34, 0xf5, 0x98, 0x6c, 0xc0, 0x81, - 0x1c, 0xfc, 0x60, 0x6d, 0x38, 0x35, 0x37, 0xef, 0x66, 0xb6, 0x09, 0x02, 0xbf, 0xbb, 0x84, 0x3f, - 0x1c, 0x14, 0x2f, 0xb8, 0x1b, 0x4a, 0x14, 0xd9, 0x06, 0x52, 0x8a, 0x0b, 0x80, 0x20, 0x9b, 0x17, - 0x1c, 0xe0, 0x35, 0x41, 0x9c, 0xf3, 0x71, 0x81, 0xff, 0xa2, 0x30, 0x6c, 0x43, 0x3b, 0x47, 0x9b, - 0x97, 0xaa, 0xc1, 0x62, 0x13, 0xbd, 0x4b, 0xa6, 0x6a, 0xe8, 0x0f, 0x28, 0xca, 0x4e, 0x54, 0x3c, - 0x61, 0x99, 0x29, 0x21, 0xc2, 0xcd, 0x54, 0xbc, 0x34, 0xba, 0xca, 0x06, 0x60, 0x71, 0x66, 0xda, - 0xbb, 0xc2, 0xc8, 0x45, 0x65, 0x7e, 0xc1, 0x37, 0x51, 0xbf, 0x1c, 0x17, 0x24, 0xc5, 0x93, 0x9d, - 0x12, 0x78, 0xe7, 0x05, 0xd9, 0x02, 0xf6, 0xc7, 0x32, 0xa6, 0x99, 0xb6, 0x44, 0xa5, 0x78, 0x25, - 0xc4, 0x11, 0xd1, 0xd2, 0x18, 0xe0, 0xa2, 0x7d, 0x08, 0x28, 0x90, 0xc6, 0x7e, 0x8a, 0xf8, 0x6c, - 0x73, 0xbb, 0x36, 0xdf, 0xb5, 0x11, 0xc7, 0xbc, 0xbb, 0x6a, 0x13, 0x10, 0xab, 0xe9, 0xcf, 0x96, - 0x88, 0x9f, 0x8e, 0x0e, 0x78, 0x2e, 0x66, 0x02, 0x94, 0x46, 0xcb, 0xcd, 0xff, 0xd1, 0xbb, 0xec, - 0x7a, 0xc9, 0xd6, 0x8c, 0x31, 0x3f, 0x6c, 0x6a, 0x68, 0x4f, 0xca, 0x85, 0xbb, 0x2f, 0xb4, 0xba, - 0xb0, 0xc4, 0x3c, 0xd2, 0x1d, 0xe3, 0x85, 0xdc, 0x26, 0x6d, 0x48, 0x44, 0x89, 0x46, 0xe7, 0xa1, - 0x2b, 0xc4, 0x2d, 0xe5, 0xd2, 0xcd, 0x75, 0xc2, 0xb2, 0x29, 0x4e, 0x65, 0xd7, 0x72, 0x4a, 0xb0, - 0xcc, 0x54, 0x7d, 0xb3, 0x6c, 0xfb, 0x7f, 0x4c, 0xe3, 0x7b, 0x2c, 0x6a, 0x66, 0x0e, 0x0d, 0x4c, - 0xf2, 0x3b, 0xc2, 0x43, 0x37, 0x33, 0xc0, 0x57, 0x96, 0xfa, 0x76, 0x19, 0x30, 0x48, 0x7a, 0x8c, - 0x6b, 0x58, 0x1e, 0x15, 0xdd, 0x80, 0x2b, 0xc2, 0xef, 0x10, 0x17, 0xcd, 0x10, 0x06, 0x05, 0x73, - 0x9a, 0x01, 0xe5, 0xdb, 0x89, 0xd3, 0x83, 0x4d, 0x14, 0x1f, 0x53, 0xa3, 0x66, 0xc0, 0x01, 0x02, - 0x82, 0x01, 0x01, 0x00, 0xce, 0xc5, 0xfb, 0x52, 0x0d, 0xb4, 0xaa, 0x1b, 0x2b, 0x5c, 0x5a, 0xa3, - 0xd8, 0x3f, 0x74, 0x99, 0x1c, 0x05, 0x83, 0x03, 0x43, 0xb8, 0x00, 0x21, 0x0c, 0xf9, 0xe0, 0xb0, - 0x6a, 0xef, 0x40, 0x4a, 0xeb, 0x65, 0xd0, 0x80, 0xe5, 0x34, 0x33, 0x09, 0xf2, 0x70, 0xb6, 0xa6, - 0x1d, 0xb9, 0x04, 0xc7, 0xb9, 0x84, 0x70, 0xd6, 0xa7, 0x67, 0x06, 0x40, 0x9a, 0x20, 0xee, 0x96, - 0x7f, 0xde, 0xa4, 0x28, 0x81, 0x08, 0x68, 0xda, 0x05, 0x27, 0x88, 0xa0, 0xe2, 0x7c, 0xde, 0xfb, - 0xe3, 0x44, 0x1d, 0xca, 0x49, 0x65, 0x4f, 0x34, 0xd5, 0x44, 0xea, 0xa6, 0x3f, 0xcf, 0x9e, 0x7e, - 0xb7, 0x88, 0xbe, 0xa9, 0x73, 0x1e, 0x6b, 0xaa, 0x68, 0x67, 0xc6, 0xb3, 0x9a, 0x13, 0x91, 0x96, - 0x96, 0x8f, 0x9b, 0x2e, 0xf8, 0x1f, 0x9b, 0x4f, 0xef, 0x6b, 0x23, 0x06, 0x5c, 0xc1, 0xfb, 0x39, - 0x61, 0x12, 0x0d, 0x85, 0x04, 0x71, 0xd7, 0xba, 0x9a, 0xfb, 0xec, 0x61, 0xe6, 0x67, 0xc4, 0xdb, - 0x97, 0x3e, 0x33, 0xd7, 0xe2, 0x20, 0x14, 0xe2, 0x35, 0x2a, 0x38, 0x95, 0x3c, 0x56, 0x30, 0x14, - 0xa1, 0x9c, 0xaf, 0x31, 0xac, 0x66, 0x8c, 0x12, 0x63, 0x7b, 0x5b, 0x4a, 0x93, 0x31, 0xb1, 0x47, - 0x3e, 0x04, 0x33, 0xe4, 0x57, 0x31, 0x46, 0x30, 0x82, 0xab, 0x01, 0xe2, 0x97, 0x03, 0x41, 0x78, - 0xb0, 0xd3, 0xa7, 0xf6, 0x44, 0x08, 0x40, 0x7b, 0xcb, 0x7e, 0x24, 0x85, 0x58, 0x79, 0xdf, 0x59, - 0x81, 0x13, 0x69, 0x8d, 0xcd, 0x25, 0x48, 0x41, 0xc1, 0x99, 0x3f, 0x52, 0x3f, 0x0e, 0xf5, 0xe3, - 0x5b, 0xb5, 0x14, 0x35, 0xd8, 0x05, 0xc2, 0x28, 0xbf, 0x19, 0x6f, 0xba, 0x33, 0x4b, 0x94, 0x0f, - 0x2d, 0xb7, 0x51, 0x54, 0x29, 0x6c, 0x5c, 0xdc, 0x57, 0xca, 0x35, 0x0b, 0x69, 0xd9, 0x73, 0x81, - 0x5b, 0xe3, 0x3c, 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0xca, 0x48, 0x99, 0x05, 0xc3, 0x0b, 0x91, - 0x9d, 0xa5, 0x49, 0x4b, 0xa5, 0xb1, 0x38, 0xa8, 0xd7, 0xf0, 0xc0, 0xae, 0xf7, 0xf7, 0x0a, 0x3e, - 0x7c, 0x01, 0xbf, 0x69, 0xa6, 0x23, 0x68, 0xe0, 0x1b, 0x11, 0xd3, 0xc3, 0x9b, 0x2b, 0xdd, 0xa8, - 0x66, 0x17, 0x97, 0x93, 0x6f, 0xc6, 0x68, 0xd7, 0xd0, 0x68, 0xc3, 0x2b, 0x4d, 0xfa, 0xda, 0xfa, - 0xd9, 0x91, 0x68, 0x20, 0x10, 0x3d, 0x51, 0xb7, 0x3d, 0x7a, 0xc1, 0x00, 0x53, 0xc9, 0x77, 0x7e, - 0x08, 0x1d, 0x7c, 0xcf, 0x36, 0x72, 0xe4, 0x7d, 0xb0, 0x67, 0x1f, 0x41, 0x5a, 0x02, 0x87, 0xcb, - 0x4c, 0x83, 0xa0, 0x4f, 0xf0, 0x80, 0x4b, 0x3a, 0x66, 0xd2, 0x52, 0x13, 0x77, 0x3c, 0x6d, 0xa6, - 0xdf, 0xd2, 0x3c, 0xd3, 0x6b, 0xb4, 0x7c, 0x53, 0x55, 0x40, 0x22, 0x4a, 0x87, 0x1d, 0x66, 0xd4, - 0xc1, 0x45, 0x2c, 0xeb, 0xbb, 0x95, 0x57, 0x03, 0x4b, 0xd2, 0x4d, 0xfa, 0x86, 0x15, 0x3d, 0xbe, - 0x8c, 0x0d, 0xf0, 0x4b, 0x9b, 0x98, 0xce, 0x88, 0xfb, 0x98, 0x90, 0x56, 0x78, 0x80, 0x7e, 0xfd, - 0x27, 0xb8, 0x17, 0x23, 0x4f, 0xd8, 0x2a, 0x16, 0x89, 0xef, 0x25, 0xed, 0x85, 0x85, 0x64, 0x76, - 0xb4, 0x85, 0xe8, 0x4a, 0x28, 0x7a, 0xbe, 0x11, 0x66, 0x09, 0x9a, 0xeb, 0x60, 0xdd, 0xd5, 0x53, - 0x73, 0x4a, 0xad, 0xc9, 0x06, 0x8e, 0xab, 0x62, 0x31, 0x7b, 0x2e, 0xf7, 0x7e, 0x47, 0x00, 0xc2, - 0x47, 0x5b, 0x61, 0x1e, 0xb9, 0x9f, 0xfc, 0x85, 0xe9, 0x97, 0x1a, 0x4d, 0x56, 0x4a, 0x0c, 0x57, - 0x1b, 0x73, 0x6e, 0xba, 0xdb, 0x82, 0x70, 0xb6, 0xe5, 0x09, 0xaf, 0x45, 0x87, 0x34, 0xae, 0x54, - 0xbf, 0x92, 0xf3, 0x38, 0xc9, 0x08, 0x4c, 0x1f, 0x77, 0x80, 0xec, 0x8c, 0x9c, 0x0d, 0x93, 0x29, - 0x63, 0xed, 0x31, 0x9b, 0xb2, 0x3b, 0x8d, 0x34, 0xfb, 0x02, 0x82, 0x01, 0x00, 0x62, 0xb3, 0x28, - 0x83, 0x03, 0x5d, 0xd0, 0xb1, 0x05, 0x62, 0xa1, 0x35, 0x82, 0x7c, 0xcf, 0xb8, 0x62, 0x22, 0xd3, - 0x65, 0xd4, 0x86, 0x59, 0x31, 0x6d, 0x93, 0x3d, 0x48, 0x98, 0xd2, 0xb9, 0x7a, 0xc9, 0xa0, 0xa1, - 0x05, 0x55, 0xe3, 0x33, 0xd5, 0xb4, 0xaf, 0x4e, 0xd0, 0x3e, 0x71, 0xd9, 0xb1, 0x48, 0x81, 0xca, - 0xa6, 0xfb, 0xe3, 0x76, 0x9d, 0x91, 0xb4, 0xd4, 0x8e, 0x6c, 0x5d, 0x27, 0x38, 0xda, 0x56, 0xdc, - 0x4d, 0xed, 0x95, 0xf0, 0x66, 0xf3, 0x95, 0xad, 0x8e, 0xc8, 0xed, 0xf3, 0xd6, 0x62, 0x70, 0x84, - 0x7d, 0x70, 0xab, 0xe3, 0xe2, 0x15, 0xa5, 0x92, 0x3f, 0x64, 0x76, 0x56, 0xa4, 0x65, 0xfa, 0x08, - 0x64, 0xa0, 0x4f, 0xa1, 0x0e, 0x8c, 0x26, 0x79, 0x21, 0x4b, 0x9f, 0x22, 0xf1, 0x29, 0xa9, 0x54, - 0xa6, 0xb4, 0x5f, 0x0c, 0xa9, 0xf5, 0xce, 0xf6, 0x8f, 0x6e, 0x21, 0x82, 0xe8, 0x92, 0xb5, 0x90, - 0xc7, 0x57, 0x41, 0x97, 0x95, 0x27, 0xb9, 0x32, 0xc3, 0xab, 0x0f, 0x1b, 0x0a, 0x1a, 0xbb, 0x3b, - 0x9c, 0xba, 0xc9, 0xfb, 0x96, 0x68, 0xe5, 0xaf, 0x2f, 0xb9, 0xf1, 0x23, 0xc3, 0x6f, 0x4a, 0xc7, - 0xe3, 0xe3, 0x2e, 0xb7, 0xe6, 0x02, 0x1a, 0xff, 0x47, 0x45, 0x78, 0x16, 0x19, 0x11, 0xf1, 0xc8, - 0x52, 0x51, 0x9d, 0x35, 0x5a, 0x26, 0xc1, 0x7c, 0x18, 0x13, 0x38, 0x04, 0xfd, 0xcd, 0x7d, 0xae, - 0xe2, 0x28, 0xc1, 0x7e, 0xc7, 0x53, 0xf3, 0x60, 0xc4, 0xc5, 0x93, 0x31, 0x98, 0x69, 0x6b, 0x39, - 0x71, 0x81, 0xeb, 0x17, 0xc9, 0xb7, 0xa5, 0xf9, 0x83, 0x5c, 0x7c, 0x34, 0x38, 0x7b, 0x74, 0x4c, - 0x38, 0xcc, 0xf7, 0x64, 0x58, 0x9a, 0x31, 0xa2, 0x6c, 0x18, 0x63, 0x5f, 0xe3, 0xef, 0x9d, 0xf5, - 0x39, 0x8c, 0x82, 0x4e, 0x0d, 0xb3, 0xaa, 0x03, 0xb3, 0xa4, 0xdb, 0xf4, 0x01, 0x02, 0x82, 0x01, - 0x01, 0x00, 0x96, 0x33, 0x77, 0xe4, 0x8e, 0x62, 0x8d, 0xba, 0x88, 0x1b, 0xb7, 0x9f, 0x0d, 0xcb, - 0xeb, 0x9b, 0x84, 0x7a, 0x1e, 0xb1, 0xa2, 0xef, 0x29, 0x5c, 0x7d, 0x13, 0xbb, 0x88, 0x10, 0xac, - 0xf4, 0x13, 0x45, 0x96, 0x7f, 0x9d, 0x3d, 0xe2, 0x36, 0x03, 0xb0, 0xaa, 0xed, 0x60, 0x46, 0xec, - 0x5c, 0xab, 0xb4, 0xce, 0x8e, 0xde, 0x35, 0x51, 0xda, 0x88, 0x28, 0xef, 0x2f, 0x37, 0xbf, 0xc0, - 0x68, 0x96, 0xaf, 0x0a, 0x96, 0x8a, 0xa0, 0x83, 0x28, 0xc3, 0x2f, 0xda, 0x18, 0x26, 0xef, 0x02, - 0xf8, 0xcd, 0x3e, 0x95, 0x37, 0xba, 0x75, 0x3c, 0x8d, 0xd9, 0x7f, 0xb7, 0x4f, 0x04, 0x5e, 0xce, - 0xfd, 0x4b, 0x92, 0x0a, 0x3d, 0xc8, 0x00, 0xc7, 0xce, 0xec, 0x4d, 0x38, 0xbb, 0x28, 0x33, 0x79, - 0x49, 0x8b, 0x78, 0xb6, 0xbd, 0xae, 0x3c, 0x47, 0xb9, 0xdc, 0xd4, 0xd7, 0xb9, 0x26, 0xad, 0x8a, - 0x51, 0xb9, 0x40, 0x2c, 0x84, 0xc4, 0x81, 0x0b, 0x3a, 0xec, 0xd6, 0x00, 0xc2, 0xb3, 0x83, 0xb0, - 0x80, 0x88, 0x89, 0x4d, 0x4b, 0xd7, 0xe8, 0x59, 0xe2, 0xf2, 0x56, 0x40, 0x60, 0x09, 0x0e, 0x92, - 0x99, 0xef, 0xcb, 0xf2, 0xd6, 0xbe, 0x99, 0x40, 0xf2, 0xdf, 0xb2, 0xba, 0xbc, 0x2d, 0xf8, 0x8e, - 0x1f, 0x6f, 0x2b, 0xdc, 0xab, 0xc0, 0x5e, 0x97, 0xe3, 0x82, 0x2d, 0x46, 0x83, 0x89, 0x69, 0xf0, - 0x9a, 0x55, 0xf1, 0x88, 0xfb, 0x5e, 0xf9, 0xab, 0xf7, 0x96, 0x72, 0xa4, 0xd7, 0xe2, 0xaf, 0x88, - 0x1b, 0x8b, 0x4a, 0x96, 0xce, 0x2c, 0x2f, 0x89, 0xa0, 0x38, 0x92, 0xea, 0xfa, 0xb6, 0xb9, 0xd1, - 0xa6, 0x0c, 0xc5, 0xb7, 0x2e, 0xa2, 0x69, 0x9c, 0xb4, 0xf3, 0x17, 0x53, 0xa0, 0xab, 0xad, 0x8c, - 0x90, 0xa4, 0xf4, 0xc7, 0x30, 0xd5, 0x43, 0x43, 0x2d, 0xad, 0xb4, 0x57, 0x6c, 0xab, 0xd8, 0x8a, - 0x4e, 0x77, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc9, 0xad, 0xff, 0xcc, 0xaf, 0x3d, 0xf9, 0x52, 0xfb, - 0x1b, 0xf7, 0x92, 0x0f, 0xd9, 0x06, 0xf4, 0x7d, 0x24, 0x1d, 0x48, 0x9f, 0x69, 0xf7, 0xad, 0x40, - 0x98, 0x60, 0x3e, 0x3b, 0x45, 0xe2, 0x85, 0xa8, 0x9d, 0x37, 0x56, 0x6a, 0xb9, 0x0b, 0xd9, 0xd8, - 0xe7, 0xab, 0x3d, 0xc3, 0xb3, 0x94, 0x3b, 0xca, 0x5e, 0xac, 0x15, 0xe5, 0x25, 0x89, 0x8a, 0x65, - 0x08, 0x4e, 0xe3, 0x6f, 0x77, 0x96, 0xfc, 0x59, 0x0f, 0x62, 0x2a, 0xe0, 0xd7, 0x19, 0x6d, 0x54, - 0x82, 0x32, 0x81, 0xc0, 0x53, 0x38, 0x73, 0x63, 0x76, 0xeb, 0x76, 0x0b, 0x52, 0x23, 0x16, 0xb6, - 0x80, 0x6b, 0xde, 0x18, 0x07, 0xb3, 0x67, 0x7f, 0x2a, 0x28, 0x85, 0x36, 0xe9, 0xd9, 0x33, 0xed, - 0xd7, 0x84, 0x09, 0x8e, 0x2f, 0xae, 0xc4, 0x64, 0xc2, 0x1a, 0x53, 0x5b, 0x42, 0xc6, 0x54, 0x2a, - 0x63, 0x71, 0x0a, 0x1a, 0x2a, 0xfc, 0xa6, 0x02, 0x80, 0xa6, 0x02, 0xcf, 0x15, 0xda, 0x83, 0x2b, - 0x66, 0x2c, 0x35, 0x61, 0x0f, 0x6e, 0x39, 0x4a, 0x16, 0xc0, 0xea, 0xa6, 0xd7, 0x06, 0x6a, 0x99, - 0x57, 0x0e, 0x5e, 0xf3, 0xc8, 0x4b, 0x68, 0x16, 0x02, 0xcd, 0xdf, 0x42, 0x55, 0xa3, 0x1f, 0xd8, - 0x64, 0x71, 0x04, 0xcc, 0xb1, 0x46, 0x97, 0x40, 0x33, 0x83, 0xd1, 0xaa, 0xa4, 0x49, 0x8d, 0xc4, - 0x36, 0xa3, 0xaf, 0x6c, 0x25, 0x75, 0xfe, 0x85, 0x29, 0x46, 0x2d, 0xf4, 0xef, 0xa9, 0x21, 0x0a, - 0x80, 0x17, 0x23, 0x56, 0xca, 0x4a, 0x7f, 0xc0, 0xbd, 0x1d, 0xca, 0x0c, 0xfd, 0x78, 0x07, 0x9b, - 0x68, 0x1c, 0x8f, 0xc5, 0xe4, 0xe4, 0xd2, 0x12, 0x21, 0xa1, 0x84, 0x77, 0xac, 0x81, 0x1a, 0xec, - 0x7c, 0x1a, 0xe9, 0x11, 0x8d, 0x48, 0x01, 0x3b, 0x4f, 0xab, 0x5b, 0x5a, 0x05, 0x96, 0x68, 0x81, - 0x1a, 0x88, 0xde, 0xb3, 0xa4, 0x90, 0xf9, - -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */, ret = 1; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server TLS | visit https://localhost:7681\n"); - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - if (lws_cmdline_option(argc, argv, "-h")) - info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.server_ssl_cert_mem = cert_pem; - info.server_ssl_cert_mem_len = strlen(cert_pem); - info.server_ssl_private_key_mem = key_pem; - info.server_ssl_private_key_mem_len = strlen(key_pem); - info.vhost_name = "first"; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("Failed to create first vhost\n"); - goto bail; - } - - info.port = 7682; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.server_ssl_cert_mem = cert_der; - info.server_ssl_cert_mem_len = sizeof(cert_der); - info.server_ssl_private_key_mem = key_der; - info.server_ssl_private_key_mem_len = sizeof(key_der); - info.vhost_name = "second"; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("Failed to create second vhost\n"); - goto bail; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - ret = 0; - -bail: - lws_context_destroy(context); - - return ret; -} diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ - - - - -
- -

404

- Sorry, that file doesn't exist. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -document.addEventListener("DOMContentLoaded", function() { - - var transport_protocol = ""; - - if ( performance && performance.timing.nextHopProtocol ) { - transport_protocol = performance.timing.nextHopProtocol; - } else if ( window.chrome && window.chrome.loadTimes ) { - transport_protocol = window.chrome.loadTimes().connectionInfo; - } else { - - var p = performance.getEntriesByType("resource"); - for (var i=0; i < p.length; i++) { - var value = "nextHopProtocol" in p[i]; - if (value) - transport_protocol = p[i].nextHopProtocol; - } - } - - if (transport_protocol === "h2") - document.getElementById("transport").innerHTML = ""; - -}, false); \ No newline at end of file Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/favicon.ico differ Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/http2.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/http2.png differ diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ - - - - - - - -
- - Hello from the minimal https server example. -
- You can confirm the 404 page handler by going to this - nonexistant page. -
-
- - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/README.md libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/minimal-http-server-tls-mem/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/minimal-http-server-tls-mem/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -# lws minimal http server with tls and certs from memory - -This is the same as the minimal-http-server-tls example, but shows how -to init the vhost with both PEM or DER certs from memory instead of files. - -The server listens on port 7681 (initialized with PEM in-memory certs) and -port 7682 (initialized with DER in-memory certs). - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-tls-mem -[2019/02/14 14:46:40:9783] USER: LWS minimal http server TLS | visit https://localhost:7681 -[2019/02/14 14:46:40:9784] NOTICE: Using SSL mode -[2019/02/14 14:46:40:9784] NOTICE: lws_tls_server_vhost_backend_init: vh first: mem CA OK -parsing as der -[2019/02/14 14:46:40:9849] NOTICE: no client cert required -[2019/02/14 14:46:40:9849] NOTICE: created client ssl context for first -[2019/02/14 14:46:40:9849] NOTICE: Using SSL mode -[2019/02/14 14:46:40:9850] NOTICE: lws_tls_server_vhost_backend_init: vh second: mem CA OK -parsing as der -[2019/02/14 14:46:40:9894] NOTICE: no client cert required -[2019/02/14 14:46:40:9894] NOTICE: created client ssl context for second -[2019/02/14 14:46:40:9894] NOTICE: vhost first: cert expiry: 36167d -[2019/02/14 14:46:40:9894] NOTICE: vhost second: cert expiry: 36167d -[2018/03/20 13:23:14:0207] NOTICE: vhost default: cert expiry: 730459d -``` - -Visit https://127.0.0.1:7681 and https://127.0.0.1:7682 - -Because it uses a selfsigned certificate, you will have to make an exception for it in your browser. - -## Certificate creation - -The selfsigned certs provided were created with - -``` -echo -e "GB\nErewhon\nAll around\nlibwebsockets-test\n\nlocalhost\nnone@invalid.org\n" | openssl req -new -newkey rsa:4096 -days 36500 -nodes -x509 -keyout "localhost-100y.key" -out "localhost-100y.cert" -``` - -they cover "localhost" and last 100 years from 2018-03-20. - -You can replace them with commercial certificates matching your hostname. - -The der content was made from PEM like this - -``` - $ cat ../minimal-http-server-tls/localhost-100y.key | grep -v ^- | base64 -d | hexdump -C | tr -s ' ' | cut -d' ' -f2- | cut -d' ' -f-16 | sed "s/|.*//g" | sed "s/0000.*//g" | sed "s/^/0x/g" | sed "s/\ /\,\ 0x/g" | sed "s/\$/,/g" | sed "s/0x,//g" -``` - -## HTTP/2 - -If you built lws with `-DLWS_WITH_HTTP2=1` at cmake, this simple server is also http/2 capable -out of the box. If the index.html was loaded over http/2, it will display an HTTP 2 png. diff -Nru libwebsockets-4.0.20/minimal-examples/http-server/README.md libwebsockets-2.4.2/minimal-examples/http-server/README.md --- libwebsockets-4.0.20/minimal-examples/http-server/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/http-server/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -|Example|Demonstrates| ----|--- -minimal-http-server-basicauth|Shows how to protect a mount using a password file and basic auth -minimal-http-server-custom-headers|Shows how to query custom headers that lws doesn't already know -minimal-http-server-deaddrop|Shows how to use the deaddrop drag and drop file upload + sharing plugin -minimal-http-server-dynamic|Serves both static and dynamically generated http content -minimal-http-server-eventlib-foreign|Demonstrates integrating lws with a foreign event library -minimal-http-server-eventlib-demos|Using the demo plugins with event libraries -minimal-http-server-eventlib|Same as minimal-http-server but works with a supported event library -minimal-http-server-form-get|Process a GET form -minimal-http-server-form-post-file|Process a multipart POST form with file transfer -minimal-http-server-form-post|Process a POST form (no file transfer) -minimal-http-server-fulltext-search|Demonstrates using lws Fulltext Search -minimal-http-server-mimetypes|Shows how to add support for additional mimetypes at runtime -minimal-http-server-multivhost|Same as minimal-http-server but three different vhosts -minimal-http-server-proxy|Reverse Proxy -minimal-http-server-smp|Multiple service threads -minimal-http-server-sse-ring|Server Side Events with ringbuffer and threaded event sources -minimal-http-server-sse|Simple Server Side Events -minimal-http-server-tls-80|Serves a directory over http/1 or http/2 with TLS (SSL), custom 404 handler, redirect to https on port 80 -minimal-http-server-tls-mem|Serves using TLS with the cert and key provided as memory buffers instead of files -minimal-http-server-tls|Serves a directory over http/1 or http/2 with TLS (SSL), custom 404 handler -minimal-http-server|Serves a directory over http/1, custom 404 handler - diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-mqtt-client) -set(SRCS minimal-mqtt-client.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_MQTT 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/minimal-mqtt-client.c libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/minimal-mqtt-client.c --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/minimal-mqtt-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/minimal-mqtt-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,343 +0,0 @@ -/* - * lws-minimal-mqtt-client - * - * Written in 2010-2020 by Andy Green - * Sakthi Kannan - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include -#include -#include - -enum { - STATE_SUBSCRIBE, /* subscribe to the topic */ - STATE_PUBLISH_QOS0, /* Send the message in QoS0 */ - STATE_WAIT_ACK0, /* Wait for the synthetic "ack" */ - STATE_PUBLISH_QOS1, /* Send the message in QoS1 */ - STATE_WAIT_ACK1, /* Wait for the real ack (or timeout + retry) */ - - STATE_TEST_FINISH -}; - -static int interrupted, bad = 1, do_ssl; - -static const lws_retry_bo_t retry = { - .secs_since_valid_ping = 20, /* if idle, PINGREQ after secs */ - .secs_since_valid_hangup = 25, /* hangup if still idle secs */ -}; - -static const lws_mqtt_client_connect_param_t client_connect_param = { - .client_id = "lwsMqttClient", - .keep_alive = 60, - .clean_start = 1, - .will_param = { - .topic = "good/bye", - .message = "sign-off", - .qos = 0, - .retain = 0, - }, - .username = "lwsUser", - .password = "mySecretPassword", -}; - -static lws_mqtt_publish_param_t pub_param; - -static lws_mqtt_topic_elem_t topics[] = { - [0] = { .name = "test/topic0", .qos = QOS0 }, - [1] = { .name = "test/topic1", .qos = QOS1 }, -}; - -static lws_mqtt_subscribe_param_t sub_param = { - .topic = &topics[0], - .num_topics = LWS_ARRAY_SIZE(topics), -}; - -static const char * const test_string = - "No one would have believed in the last years of the nineteenth " - "century that this world was being watched keenly and closely by " - "intelligences greater than man's and yet as mortal as his own; that as " - "men busied themselves about their various concerns they were " - "scrutinised and studied, perhaps almost as narrowly as a man with a " - "microscope might scrutinise the transient creatures that swarm and " - "multiply in a drop of water. With infinite complacency men went to " - "and fro over this globe about their little affairs, serene in their " - "assurance of their empire over matter. It is possible that the " - "infusoria under the microscope do the same. No one gave a thought to " - "the older worlds of space as sources of human danger, or thought of " - "them only to dismiss the idea of life upon them as impossible or " - "improbable. It is curious to recall some of the mental habits of " - "those departed days. At most terrestrial men fancied there might be " - "other men upon Mars, perhaps inferior to themselves and ready to " - "welcome a missionary enterprise. Yet across the gulf of space, minds " - "that are to our minds as ours are to those of the beasts that perish, " - "intellects vast and cool and unsympathetic, regarded this earth with " - "envious eyes, and slowly and surely drew their plans against us. And " - "early in the twentieth century came the great disillusionment. "; - -/* this reflects the length of the string above */ -#define TEST_STRING_LEN 1337 - -struct pss { - int state; - size_t pos; - int retries; -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -static int -connect_client(struct lws_context *context) -{ - struct lws_client_connect_info i; - - memset(&i, 0, sizeof i); - - i.mqtt_cp = &client_connect_param; - i.address = "localhost"; - i.host = "localhost"; - i.protocol = "mqtt"; - i.context = context; - i.method = "MQTT"; - i.alpn = "mqtt"; - i.port = 1883; - - if (do_ssl) { - i.ssl_connection = LCCSCF_USE_SSL; - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - i.port = 8883; - } - - if (!lws_client_connect_via_info(&i)) { - lwsl_err("%s: Client Connect Failed\n", __func__); - - return 1; - } - - return 0; -} - -static int -system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = mgr->parent; - - if (current != LWS_SYSTATE_OPERATIONAL || - target != LWS_SYSTATE_OPERATIONAL) - return 0; - - /* - * We delay trying to do the client connection until - * the protocols have been initialized for each - * vhost... this happens after we have network and - * time so we can judge tls cert validity. - */ - - if (connect_client(context)) - interrupted = 1; - - return 0; - } - - -static int -callback_mqtt(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct pss *pss = (struct pss *)user; - lws_mqtt_publish_param_t *pub; - size_t chunk; - - switch (reason) { - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("%s: CLIENT_CONNECTION_ERROR: %s\n", __func__, - in ? (char *)in : "(null)"); - interrupted = 1; - break; - - case LWS_CALLBACK_MQTT_CLIENT_CLOSED: - lwsl_user("%s: CLIENT_CLOSED\n", __func__); - interrupted = 1; - break; - - case LWS_CALLBACK_MQTT_CLIENT_ESTABLISHED: - lwsl_user("%s: MQTT_CLIENT_ESTABLISHED\n", __func__); - lws_callback_on_writable(wsi); - - return 0; - - case LWS_CALLBACK_MQTT_SUBSCRIBED: - lwsl_user("%s: MQTT_SUBSCRIBED\n", __func__); - break; - - case LWS_CALLBACK_MQTT_CLIENT_WRITEABLE: - /* - * Extra WRITEABLE may appear here other than ones we asked - * for, so we must consult our own state to decide if we want - * to make use of the opportunity - */ - - switch (pss->state) { - case STATE_SUBSCRIBE: - lwsl_user("%s: WRITEABLE: Subscribing\n", __func__); - - if (lws_mqtt_client_send_subcribe(wsi, &sub_param)) { - lwsl_notice("%s: subscribe failed\n", __func__); - - return -1; - } - pss->state++; - break; - - case STATE_PUBLISH_QOS0: - case STATE_PUBLISH_QOS1: - - lwsl_user("%s: WRITEABLE: Publish\n", __func__); - - pub_param.topic = "test/topic"; - pub_param.topic_len = (uint16_t)strlen(pub_param.topic); - pub_param.qos = pss->state == STATE_PUBLISH_QOS0 ? QOS0 : QOS1; - pub_param.payload_len = TEST_STRING_LEN; - - /* We send the message out 300 bytes or less at at time */ - - chunk = 300; - - if (chunk > TEST_STRING_LEN - pss->pos) - chunk = TEST_STRING_LEN - pss->pos; - - if (lws_mqtt_client_send_publish(wsi, &pub_param, - test_string + pss->pos, chunk, - (pss->pos + chunk == TEST_STRING_LEN))) - return -1; - - pss->pos += chunk; - - if (pss->pos == TEST_STRING_LEN) { - pss->pos = 0; - pss->state++; - } - break; - - default: - break; - } - - return 0; - - case LWS_CALLBACK_MQTT_ACK: - lwsl_user("%s: MQTT_ACK\n", __func__); - /* - * We can forget about the message we just sent, it's done. - * - * For our test, that's the indication we can close the wsi. - */ - - pss->state++; - if (pss->state != STATE_TEST_FINISH) - break; - - /* Oh we are done then */ - - bad = 0; - interrupted = 1; - lws_cancel_service(lws_get_context(wsi)); - break; - - case LWS_CALLBACK_MQTT_RESEND: - lwsl_user("%s: MQTT_RESEND\n", __func__); - /* - * We must resend the packet ID mentioned in len - */ - if (++pss->retries == 3) { - interrupted = 1; - break; - } - pss->state--; - pss->pos = 0; - break; - - case LWS_CALLBACK_MQTT_CLIENT_RX: - lwsl_user("%s: MQTT_CLIENT_RX\n", __func__); - - pub = (lws_mqtt_publish_param_t *)in; - assert(pub); - - lwsl_hexdump_notice(pub->topic, pub->topic_len); - lwsl_hexdump_notice(pub->payload, pub->payload_len); - - return 0; - - default: - break; - } - - return 0; -} - -static const struct lws_protocols protocols[] = { - { - .name = "mqtt", - .callback = callback_mqtt, - .per_session_data_size = sizeof(struct pss) - }, - { NULL, NULL, 0, 0 } -}; - -int main(int argc, const char **argv) -{ - lws_state_notify_link_t notifier = { {}, system_notify_cb, "app" }; - lws_state_notify_link_t *na[] = { ¬ifier, NULL }; - struct lws_context_creation_info info; - struct lws_context *context; - int n = 0; - - signal(SIGINT, sigint_handler); - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - lws_cmdline_option_handle_builtin(argc, argv, &info); - - do_ssl = !!lws_cmdline_option(argc, argv, "-s"); - if (do_ssl) - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - - lwsl_user("LWS minimal MQTT client %s [-d][-s]\n", - do_ssl ? "tls enabled": "unencrypted"); - - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - info.register_notifier_list = na; - info.fd_limit_per_thread = 1 + 1 + 1; - info.retry_and_idle_policy = &retry; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./mosq-ca.crt"; -#endif - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* Event loop */ - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - lws_context_destroy(context); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-ca.crt libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-ca.crt --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-ca.crt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-ca.crt 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDjzCCAnegAwIBAgIUAVMnfaOq8yiLnvIB/obE689mulMwDQYJKoZIhvcNAQEL -BQAwVjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE -CgwTRGVmYXVsdCBDb21wYW55IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTE5 -MTEyMDA1NTYyNFoYDzIxMTkxMDI3MDU1NjI0WjBWMQswCQYDVQQGEwJYWDEVMBMG -A1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRk -MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQCyw+kBLg9lCGlBceil0lNqgh7fyguin8IFm5X60bfSJ/pV6i8dZZplVjE+ -g75iFEFBYyfn+6bOPdinfQ7Uu+l6t6y2HWbK6MkoypF/g7cdtUFy9s4cUX0467BZ -hMPJUc4UfnD+bYcXoguPJ6/OH84+Ayg6uvm5nJ32pDiXr6gMd5YljdXaJpCeeh4w -O2UBD1HffyPIklIPT59lxv2ZvKnZbE4UE1uaLLvTWiT+X+gA3i0Syxkq5RlZ61DE -3MyIYAUVSf3coNXCSdJ9wrOsGoP+X+T+aDjnFCCnqus3QX3JOHTKf4+tBoF65cNP -mnHXb5/ZQCcR9HMofacalMpjiGb7AgMBAAGjUzBRMB0GA1UdDgQWBBTl3poLE/22 -R4RXTMoXPHMlc3QRjzAfBgNVHSMEGDAWgBTl3poLE/22R4RXTMoXPHMlc3QRjzAP -BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCwWVnNjKRH9CCBv3yT -Djah51q3NH3E+f1IcBZz2c5WbJHxEtP4QC57ou2x3hC7Cur9iOqIO57VW8vnFP2Y -bD9oHb46grsGhwuaSuA2AlFZ5EuUAe2cgEj5/3Ihd3HYsXN3rfRO1PVGN1iRG1sE -xAxENNm6nOS1Ht1Zy5YmMiSPzghcsTnpg44AqsmowbIED75EpumLwY2NbAl9/7JL -EJil3cxEZ8rl2DVWPU3hAwrOfhl/rkQTCcigyPvZvAqsJ9vYhZftrF6njUsqr5kL -KHENu5ySKPNk5gFR17WjWoqT6iEOZN25qyfFhBRzjpCX6zD1gx0sYcVryCnTH5Y4 -Drjh ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-server.crt libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-server.crt --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-server.crt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-server.crt 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDNTCCAh0CFFu5XIMrh5gPYnjTr8UrXA3UiWqHMA0GCSqGSIb3DQEBCwUAMFYx -CzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0Rl -ZmF1bHQgQ29tcGFueSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0xOTExMjAw -NTU4NTdaGA8yMTE5MTAyNzA1NTg1N1owVjELMAkGA1UEBhMCWFgxFTATBgNVBAcM -DERlZmF1bHQgQ2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDESMBAG -A1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -tldZ5yGrBsLR/7G4b48pwQSSG6fp4egiZdeFV7SRNfbMzpuIDlFdZM9zdcoQQrTl -24aVIGwkvfsMD33Hb/D1WW+r8UFnq4CutigwXArXUxoFX6fa0rwEEjuxwG3f7+xm -vb6p/KXomyWcdAUmAvALaDXIUDEc3tH+Hxik5z36YjIqRjH16jhhs/6T8B3xAWuR -jnDknJWv36QruMIyPUqYYkl2zl4VXUKBgWZr31Opm08kb/FrWJ6lQ7912jZC8G2L -rtwZJB/1psBrX3Oj/Quj+BWHmzkosqVae2G5zAhphZ2NMrdSVfxdctNmakH8oTwf -hRas8DE2olW3whUkfKG2DQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAKEQ7LpPdU -XbJKushJ7wmuljQn3pmW9SjzFMlL9o59KLHWAmxzTDaAm6r3SGgHeSz3ZLwqtJ8I -7pCxQxI6V1ySMkWI1mfi4KPSavxBRaST4o8+YIKJt4c5aLB1seHoghx3Q/jXEGEB -9dFyLMK6u3EhYSletQNeMVGaeK1q/nVZdHNk4LXVIHsXnKlxyMnW3v18iaV3ZhVd -doAWMpnbY91AyCXjOmQrfQaHLL6n3r1Xk2L+cRO3nSor54UIXqIJxHZtj+ZYOy3Z -C5AkQ1yyTTOtEz9WB0Bk2O4ZfNgJO+1MbQSfL0m0YKpuaFnMHD9g5ufUlJGR2aMI -nw1F/oGZoNUl ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-server.key libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-server.key --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-server.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/mosq-server.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEAtldZ5yGrBsLR/7G4b48pwQSSG6fp4egiZdeFV7SRNfbMzpuI -DlFdZM9zdcoQQrTl24aVIGwkvfsMD33Hb/D1WW+r8UFnq4CutigwXArXUxoFX6fa -0rwEEjuxwG3f7+xmvb6p/KXomyWcdAUmAvALaDXIUDEc3tH+Hxik5z36YjIqRjH1 -6jhhs/6T8B3xAWuRjnDknJWv36QruMIyPUqYYkl2zl4VXUKBgWZr31Opm08kb/Fr -WJ6lQ7912jZC8G2LrtwZJB/1psBrX3Oj/Quj+BWHmzkosqVae2G5zAhphZ2NMrdS -VfxdctNmakH8oTwfhRas8DE2olW3whUkfKG2DQIDAQABAoIBACMctwc3CIQIx/+A -7Y8t9lBg3PHOZ89EsDsEQX0eHEhT+iRe9tgq+t0KxaUNAAyYYRrg056mtHyQ90WU -Zu87a0OJqYaPnbL82KfjHUzcGZK7FAXTgOPLqM0KCbSQc+rzjuVC7eDk4eHeYD5H -L4apSskKckRe8LxHm7PJPxf4a1q1EuMEfAyJhh7Tot0oVsG/wABGFUuJVJWXnec1 -0ukPowKh9bg7UyEecwyeYGzXqNqvbjhS3J0dBkjG5vfxuVHae2yIeXk6ZNsCw6tO -K8bklmsmbWAFR5SKpsNve8X/6nlclP0taDDZsz0KSbxJEd2DuRhFcdiRWEoryZVp -7DOORFECgYEA5sdsRjQoHaU85QZuM7ff6NpNT7kMIJbjHRdiauEBakLHs8yVLNEp -Vvg5fcZY4PumqPKyGEjUD6DenlLvb4OBGqzKGGhAJaLz9cpVoWWPz8y1NRBfPjlB -FQdB4GdtBQGXwnZoD9kXPjYHlk4nwZZ/Sitm2w6RibiIxE0adnwLhP8CgYEAykTE -5NZ88OGGf0RWUt54OxTl4fChAcvK93KkdlK9nbokXHs7VIl4QpKPFu1nuMDrkVI4 -fVYwRDcZUjyxqbpBSf/M6T/kuEsMWBYYGv5c9/U87y0UWHbphN0TSdML2DJp9BTy -uy4RleQovof2kOr6sOsKP8lhBGSlhXyJDKn1iPMCgYEAnpvc7HsYPxe7vGQpBV6Q -g0bV777seNF7EhlqSK6P/GodOpOWyxCN6vn6+ViC6U3Lgz4Z7NrQ9FTJ6+JwMSIe -byjmVNQBklxmcz02kRBuQJEe0XOJIgjTlBJC0moC4Xfwx3P9nTbE5LrZiBH6/O/k -WCNwM4nVuOOdC906HMiwWh0CgYEAqn3m3ODydXQTk2i9vqIpA9vsnVLf1Ay8a3El -sVqy26VQCugQrYQmay7wD6pS2Ec9CMQeO3+PtaAf5tKkCmWlrMNCLIWfu7v+jq0o -6m/nW1ZKY2xDDwJEeaqDHKIZBMYRyxxxMVd2mTq1IUynh6WZY9DqVbPf4/0WC/tZ -5ePIxAMCgYEAwwBNT2xjG1mWD4eANvKjQgrsxKFttmaXXCiixZJR+tsQc5bff5Yb -IgvvkIwLHoNpL2Nk7sEjS4sUtAKwzCtIMwvPnhQedICnOEteZ8NPfaFmPewcovcL -gv9k+mFActZ7H8i9FXLrZHyEzOXZaM/vY/mHbrlJSWnSDZsvnVzQv+o= ------END RSA PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/README.md libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/README.md --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -# lws minimal MQTT client - -The application connects to a broker at localhost 1883 (unencrypted) or -8883 (tls) - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --s| Use tls and connect to port 8883 instead of 1883 - -Start mosquitto server locally - -``` -$ mosquitto -``` - -Run the example - -``` -[2020/01/31 10:40:23:7789] U: LWS minimal MQTT client unencrypted [-d][-s] -[2020/01/31 10:40:23:8539] N: lws_mqtt_generate_id: User space provided a client ID 'lwsMqttClient' -[2020/01/31 10:40:23:9893] N: _lws_mqtt_rx_parser: migrated nwsi 0x50febd0 to sid 1 0x5106820 -[2020/01/31 10:40:23:9899] U: callback_mqtt: MQTT_CLIENT_ESTABLISHED -[2020/01/31 10:40:23:9967] U: callback_mqtt: WRITEABLE: Subscribing -[2020/01/31 10:40:24:0068] U: callback_mqtt: MQTT_SUBSCRIBED -``` - -Send something to the test client - - -``` -mosquitto_pub -h 127.0.0.1 -p 1883 -t test/topic0 -m "hello" -``` - -Observe it received at the test client - -``` -[2020/01/31 10:40:27:1845] U: callback_mqtt: MQTT_CLIENT_RX -[2020/01/31 10:40:27:1870] N: -[2020/01/31 10:40:27:1945] N: 0000: 74 65 73 74 2F 74 6F 70 69 63 30 test/topic0 -[2020/01/31 10:40:27:1952] N: - -``` diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-mqtt-client-multi) -set(SRCS minimal-mqtt-client-multi.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_MQTT 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/minimal-mqtt-client-multi.c libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/minimal-mqtt-client-multi.c --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/minimal-mqtt-client-multi.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/minimal-mqtt-client-multi.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,437 +0,0 @@ -/* - * lws-minimal-mqtt-client - * - * Written in 2010-2020 by Andy Green - * Sakthi Kannan - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include -#include -#include - -#define COUNT 8 - -struct test_item { - struct lws_context *context; - struct lws *wsi; - lws_sorted_usec_list_t sul; -} items[COUNT]; - -enum { - STATE_SUBSCRIBE, /* subscribe to the topic */ - STATE_WAIT_SUBACK, - STATE_PUBLISH_QOS0, /* Send the message in QoS0 */ - STATE_WAIT_ACK0, /* Wait for the synthetic "ack" */ - STATE_PUBLISH_QOS1, /* Send the message in QoS1 */ - STATE_WAIT_ACK1, /* Wait for the real ack (or timeout + retry) */ - STATE_UNSUBSCRIBE, - STATE_WAIT_UNSUBACK, - - STATE_TEST_FINISH -}; - -static int interrupted, do_ssl, pipeline, stagger_us = 5000, okay, - done, count = COUNT; - -static const lws_retry_bo_t retry = { - .secs_since_valid_ping = 20, /* if idle, PINGREQ after secs */ - .secs_since_valid_hangup = 25, /* hangup if still idle secs */ -}; - -static const lws_mqtt_client_connect_param_t client_connect_param = { - .client_id = NULL, - .keep_alive = 60, - .clean_start = 1, - .will_param = { - .topic = "good/bye", - .message = "sign-off", - .qos = 0, - .retain = 0, - }, - .username = "lwsUser", - .password = "mySecretPassword", -}; - -static lws_mqtt_topic_elem_t topics[] = { - [0] = { .name = "test/topic0", .qos = QOS0 }, - [1] = { .name = "test/topic1", .qos = QOS1 }, -}; - -static lws_mqtt_subscribe_param_t sub_param = { - .topic = &topics[0], - .num_topics = LWS_ARRAY_SIZE(topics), -}; - -static const char * const test_string = - "No one would have believed in the last years of the nineteenth " - "century that this world was being watched keenly and closely by " - "intelligences greater than man's and yet as mortal as his own; that as " - "men busied themselves about their various concerns they were " - "scrutinised and studied, perhaps almost as narrowly as a man with a " - "microscope might scrutinise the transient creatures that swarm and " - "multiply in a drop of water. With infinite complacency men went to " - "and fro over this globe about their little affairs, serene in their " - "assurance of their empire over matter. It is possible that the " - "infusoria under the microscope do the same. No one gave a thought to " - "the older worlds of space as sources of human danger, or thought of " - "them only to dismiss the idea of life upon them as impossible or " - "improbable. It is curious to recall some of the mental habits of " - "those departed days. At most terrestrial men fancied there might be " - "other men upon Mars, perhaps inferior to themselves and ready to " - "welcome a missionary enterprise. Yet across the gulf of space, minds " - "that are to our minds as ours are to those of the beasts that perish, " - "intellects vast and cool and unsympathetic, regarded this earth with " - "envious eyes, and slowly and surely drew their plans against us. And " - "early in the twentieth century came the great disillusionment. "; - -/* this reflects the length of the string above */ -#define TEST_STRING_LEN 1337 - -struct pss { - lws_mqtt_publish_param_t pub_param; - int state; - size_t pos; - int retries; -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -static int -connect_client(struct lws_context *context, struct test_item *item) -{ - struct lws_client_connect_info i; - - memset(&i, 0, sizeof i); - - i.mqtt_cp = &client_connect_param; - i.opaque_user_data = item; - i.protocol = "test-mqtt"; - i.address = "localhost"; - i.host = "localhost"; - i.pwsi = &item->wsi; - i.context = context; - i.method = "MQTT"; - i.alpn = "mqtt"; - i.port = 1883; - - if (do_ssl) { - i.ssl_connection = LCCSCF_USE_SSL; - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - i.port = 8883; - } - - if (pipeline) - i.ssl_connection |= LCCSCF_PIPELINE; - - if (!lws_client_connect_via_info(&i)) { - lwsl_err("%s: Client Connect Failed\n", __func__); - - return 1; - } - - return 0; -} - -static void -start_conn(struct lws_sorted_usec_list *sul) -{ - struct test_item *item = lws_container_of(sul, struct test_item, sul); - - lwsl_notice("%s: item %d\n", __func__, (int)(item - &items[0])); - - if (connect_client(item->context, item)) - interrupted = 1; -} - - -static int -system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = mgr->parent; - int n; - - if (current != LWS_SYSTATE_OPERATIONAL || - target != LWS_SYSTATE_OPERATIONAL) - return 0; - - /* - * We delay trying to do the client connection until the protocols have - * been initialized for each vhost... this happens after we have network - * and time so we can judge tls cert validity. - * - * Stagger the connection attempts so we get some joining before the - * first has connected and some afterwards - */ - - for (n = 0; n < count; n++) { - items[n].context = context; - lws_sul_schedule(context, 0, &items[n].sul, start_conn, - n * stagger_us); - } - - return 0; -} - - -static int -callback_mqtt(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct test_item *item = (struct test_item *)lws_get_opaque_user_data(wsi); - struct pss *pss = (struct pss *)user; - lws_mqtt_publish_param_t *pub; - size_t chunk; - - switch (reason) { - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("%s: CLIENT_CONNECTION_ERROR: %s\n", __func__, - in ? (char *)in : "(null)"); - - if (++done == count) - goto finish_test; - break; - - case LWS_CALLBACK_MQTT_CLIENT_CLOSED: - lwsl_user("%s: item %d: CLIENT_CLOSED %p\n", __func__, (int)(item - &items[0]), wsi); - - if (++done == count) - goto finish_test; - break; - - case LWS_CALLBACK_MQTT_CLIENT_ESTABLISHED: - lwsl_user("%s: MQTT_CLIENT_ESTABLISHED: %p\n", __func__, wsi); - lws_callback_on_writable(wsi); - - return 0; - - case LWS_CALLBACK_MQTT_SUBSCRIBED: - lwsl_user("%s: MQTT_SUBSCRIBED\n", __func__); - - /* then we can get on with the actual test part */ - - pss->state++; - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_MQTT_UNSUBSCRIBED: - lwsl_user("%s: item %d: UNSUBSCRIBED: %p: Received unsuback\n", - __func__, (int)(item - &item[0]), wsi); - okay++; - - if (++pss->state == STATE_TEST_FINISH) { - lwsl_notice("%s: MQTT_UNSUBACK ending stream %d successfully(%d/%d)\n", - __func__, (int)(item - &items[0]), okay, count); - /* We are done, request to close */ - return -1; - } - break; - - case LWS_CALLBACK_MQTT_CLIENT_WRITEABLE: - - /* - * Extra WRITEABLE may appear here other than ones we asked - * for, so we must consult our own state to decide if we want - * to make use of the opportunity - */ - - switch (pss->state) { - case STATE_SUBSCRIBE: - lwsl_user("%s: item %d: WRITEABLE: %p: Subscribing\n", __func__, (int)(item - &items[0]), wsi); - - if (lws_mqtt_client_send_subcribe(wsi, &sub_param)) { - lwsl_notice("%s: subscribe failed\n", __func__); - - return -1; - } - pss->state++; - break; - - case STATE_PUBLISH_QOS0: - case STATE_PUBLISH_QOS1: - - lwsl_user("%s: item %d: WRITEABLE: %p: Publish\n", __func__, (int)(item - &items[0]), wsi); - - pss->pub_param.topic = pss->state == STATE_PUBLISH_QOS0 ? - "test/topic0" : "test/topic1"; - pss->pub_param.topic_len = (uint16_t)strlen(pss->pub_param.topic); - pss->pub_param.qos = - pss->state == STATE_PUBLISH_QOS0 ? QOS0 : QOS1; - pss->pub_param.payload_len = TEST_STRING_LEN; - - /* We send the message out 300 bytes or less at at time */ - - chunk = 300; - - if (chunk > TEST_STRING_LEN - pss->pos) - chunk = TEST_STRING_LEN - pss->pos; - - lwsl_notice("%s: sending %d at +%d\n", __func__, - (int)chunk, (int)pss->pos); - - if (lws_mqtt_client_send_publish(wsi, &pss->pub_param, - test_string + pss->pos, chunk, - (pss->pos + chunk == TEST_STRING_LEN))) { - lwsl_notice("%s: publish failed\n", __func__); - return -1; - } - - pss->pos += chunk; - - if (pss->pos == TEST_STRING_LEN) { - lwsl_debug("%s: sent message\n", __func__); - pss->pos = 0; - pss->state++; - } - break; - - case STATE_UNSUBSCRIBE: - lwsl_user("%s: item %d: UNSUBSCRIBE: %p: Send unsub\n", - __func__, (int)(item - &item[0]), wsi); - pss->state++; - if (lws_mqtt_client_send_unsubcribe(wsi, &sub_param)) { - lwsl_notice("%s: subscribe failed\n", __func__); - return -1; - } - break; - default: - break; - } - - return 0; - - case LWS_CALLBACK_MQTT_ACK: - lwsl_user("%s: item %d: MQTT_ACK (state %d)\n", __func__, (int)(item - &items[0]), pss->state); - /* - * We can forget about the message we just sent, it's done. - * - * For our test, that's the indication we can close the wsi. - */ - - pss->state++; - if (pss->state != STATE_TEST_FINISH) { - lws_callback_on_writable(wsi); - break; - } - - break; - - case LWS_CALLBACK_MQTT_RESEND: - lwsl_user("%s: MQTT_RESEND\n", __func__); - /* - * We must resend the packet ID mentioned in len - */ - if (++pss->retries == 3) { - lwsl_notice("%s: too many retries\n", __func__); - return 1; /* kill the connection */ - } - pss->state--; - pss->pos = 0; - break; - - case LWS_CALLBACK_MQTT_CLIENT_RX: - pub = (lws_mqtt_publish_param_t *)in; - assert(pub); - lwsl_user("%s: item %d: MQTT_CLIENT_RX (%s) pos %d/%d len %d\n", __func__, - (int)(item - &items[0]), pub->topic, (int)pub->payload_pos, - (int)pub->payload_len, (int)len); - - //lwsl_hexdump_info(pub->payload, len); - - return 0; - - default: - break; - } - - return 0; - -finish_test: - interrupted = 1; - lws_cancel_service(lws_get_context(wsi)); - - return 0; -} - -static const struct lws_protocols protocols[] = { - { - .name = "test-mqtt", - .callback = callback_mqtt, - .per_session_data_size = sizeof(struct pss) - }, - { NULL, NULL, 0, 0 } -}; - -int main(int argc, const char **argv) -{ - lws_state_notify_link_t notifier = { {}, system_notify_cb, "app" }; - lws_state_notify_link_t *na[] = { ¬ifier, NULL }; - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0; - - signal(SIGINT, sigint_handler); - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - lws_cmdline_option_handle_builtin(argc, argv, &info); - - do_ssl = !!lws_cmdline_option(argc, argv, "-s"); - if (do_ssl) - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - - if (lws_cmdline_option(argc, argv, "-p")) - pipeline = 1; - - if ((p = lws_cmdline_option(argc, argv, "-i"))) - stagger_us = atoi(p); - - if ((p = lws_cmdline_option(argc, argv, "-c"))) - count = atoi(p); - - if (count > COUNT) { - count = COUNT; - lwsl_err("%s: clipped count at max %d\n", __func__, count); - } - - lwsl_user("LWS minimal MQTT client %s [-d][-s]\n", - do_ssl ? "tls enabled": "unencrypted"); - - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - info.register_notifier_list = na; - info.fd_limit_per_thread = 1 + COUNT + 1; - info.retry_and_idle_policy = &retry; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./mosq-ca.crt"; -#endif - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* Event loop */ - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lwsl_user("%s: Completed: %d/%d ok, %s\n", __func__, okay, count, - okay != count ? "failed" : "OK"); - lws_context_destroy(context); - - return okay != count; -} diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/README.md libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/README.md --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -# lws minimal MQTT client multi - -## build - -``` - $ cmake . && make -``` - -## usage - -The application goes to https://warmcat.com and receives the page data -same as minimal http client. - -However it does it for 8 client connections concurrently. - -## Commandline Options - -Option|Meaning ----|--- --c |Count of simultaneous connections (default 8) --s|Stagger the connections by 100ms, the last by 1s --p|Use stream binding - - diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/selftest.sh.broken-on-travis libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/selftest.sh.broken-on-travis --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/selftest.sh.broken-on-travis 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/selftest.sh.broken-on-travis 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=1 - -#dotest $1 $2 warmcat - -Q=`which mosquitto` -spawn "" /tmp $Q -v -dotest $1 $2 -p-i100000 -p -i 100000 - -kill $SPID 2>/dev/null -wait $SPID 2>/dev/null -exit $FAILS - diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/warmcat.com.cer libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/warmcat.com.cer --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/warmcat.com.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/warmcat.com.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFUDCCBDigAwIBAgISA4mJfIm3iCGbU9+o8YQa+4nUMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5MjNaFw0x -OTEyMDYwNzA5MjNaMBYxFDASBgNVBAMTC3dhcm1jYXQuY29tMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnEoH9JW3GvpadpxHGZPb5wv1Q6KfAIMWtdq -YCOfotFxaYULuzHVxmrTTgmEqJr+eBqUBkXKmGuRR/9UipOmTu5j02qFyWHotFdF -ZGyp//8z+Rle9Qt1nL68oNIZLDtWkybh5x00b1uo4eyEszXUaa0aLqKP3lH7Q4jI -aSVARZ8snrJR640Gp3ByudvNTYkGz469bpWzRC/8wSNtzzY02DvHs1GxQx9tMXw+ -BbtUxeP7lpYFKEFBjgZaIKLv+4g8ItJIuO7gMSzG2JfpQHxdhrlhxpx7dsaMUcyM -nnYXysNL5JG3KEMhkxbtdpCaEQ8jLSPbl/rnF/+mgce+lSjMuQIDAQABo4ICYjCC -Al4wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD -AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSI9ai12zLFeNTEDHKI9Ghkqcpa2TAf -BgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEw -LgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcw -LwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv -MBYGA1UdEQQPMA2CC3dhcm1jYXQuY29tMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcG -CysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5 -cHQub3JnMIIBBgYKKwYBBAHWeQIEAgSB9wSB9ADyAHcAY/Lbzeg7zCzPC3KEJ1dr -M6SNYXePvXWmOLHHaFRL2I0AAAFtCsVHHAAABAMASDBGAiEAy0q1cR4VwPL3iviL -cBWN67kjJRXk+DwhodmeoM3kb3gCIQC2soAHFs0Umo+0RNdFrL41+hMuidh2cXbb -Ovc6nh5tOQB3AOJpS64m6OlACeiGG7Y7g9Q+5/50iPukjyiTAZ3d8dv+AAABbQrF -R48AAAQDAEgwRgIhANqKQm4t9by263CJ7/DLOaZCjtcK29KgJjPwhv08UMn1AiEA -h35nGTASR8/E7xz+56ZUleqD7U1ABFgWZRZskIzsFO8wDQYJKoZIhvcNAQELBQAD -ggEBADDJBVbKe2LPHmi8k2vxErB3Y0Ty+3gwgPEXKYtEvQ7tos89eE+QmOXAzH5J -GwRarFf7kzmKeJv04tMebiEtshpap47oJfxCxfrtpja8hP8Cdu/v/Ae6eEzu3yet -0N08GJdxQKfgCFaoGUptbaF2RCIZS12SVcX4TPpdP+xaiZdmIx4dGM6tReQ8+y8B -10b4Hi2+d/zW0W1z6+FAemU6yleWriJDUik5oas9XZF5LAAMDb/WgF5eIB6P9CUG -LuAO8lWlk9nBgXvMLTxZ74SJb17H4kFEIrIjvABNshz5gBW8xw9nfr5YIfANtwEj -BDsq06Df3UORYVs/j3T97gPAEZ4= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/wget-log libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/wget-log --- libwebsockets-4.0.20/minimal-examples/mqtt-client/minimal-mqtt-client-multi/wget-log 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/minimal-mqtt-client-multi/wget-log 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ ---2018-11-25 07:54:30-- https://www.gravatar.com/avatar/c50933ca2aa61e0fe2c43d46bb6b59cb/?s=128 -Resolving www.gravatar.com (www.gravatar.com)... 192.0.73.2, 2a04:fa87:fffe::c000:4902 -Connecting to www.gravatar.com (www.gravatar.com)|192.0.73.2|:443... connected. -HTTP request sent, awaiting response... 200 OK -Length: 24761 (24K) [image/png] -Saving to: ‘/tmp/q’ - - /tmp/q 0%[ ] 0 --.-KB/s /tmp/q 100%[================================>] 24.18K --.-KB/s in 0.01s - -2018-11-25 07:54:31 (2.04 MB/s) - ‘/tmp/q’ saved [24761/24761] - diff -Nru libwebsockets-4.0.20/minimal-examples/mqtt-client/README.md libwebsockets-2.4.2/minimal-examples/mqtt-client/README.md --- libwebsockets-4.0.20/minimal-examples/mqtt-client/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/mqtt-client/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -|name|demonstrates| ----|--- -minimal-mqtt-client|Simple demo for mqtt client operation -minimal-mqtt-client-multi|Demonstrates automatic binding / muxing of independent connections to share a single tcp / tls connection diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-tcp/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-tcp/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-tcp/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-tcp/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -project(lws-minimal-raw-adopt-tcp) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-raw-adopt-tcp) -set(SRCS minimal-raw-adopt-tcp.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-tcp/minimal-raw-adopt-tcp.c libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-tcp/minimal-raw-adopt-tcp.c --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-tcp/minimal-raw-adopt-tcp.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-tcp/minimal-raw-adopt-tcp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,188 +0,0 @@ -/* - * lws-minimal-raw-adopt-tcp - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates integrating somebody else's connected tcp - * socket into the lws event loop as a RAW wsi. It's interesting in - * the kind of situation where you already have a connected socket - * in your application, and you need to hand it over to lws to deal with. - * - * Lws supports "adopting" these foreign sockets. - * - * If you simply want a connected client raw socket using lws alone, you - * can just use lws_client_connect_via_info() with info.method = "RAW". - * - */ - -#include -#include -#include -#if !defined(WIN32) -#include -#include -#include -#include -#endif -#include -#include -#include -#include -#include -#include - -static int -callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - - switch (reason) { - - /* callbacks related to raw socket descriptor */ - - case LWS_CALLBACK_RAW_ADOPT: - lwsl_user("LWS_CALLBACK_RAW_ADOPT\n"); - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_RAW_CLOSE: - lwsl_user("LWS_CALLBACK_RAW_CLOSE\n"); - break; - - case LWS_CALLBACK_RAW_RX: - lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len); - lwsl_hexdump_level(LLL_NOTICE, in, len); - break; - - case LWS_CALLBACK_RAW_WRITEABLE: - if (lws_write(wsi, - (uint8_t *)"GET / HTTP/1.1\xd\xa\xd\xa", 18, - LWS_WRITE_RAW) != 18) { - lwsl_notice("%s: raw write failed\n", __func__); - return 1; - } - break; - - default: - break; - } - - return 0; -} - -static struct lws_protocols protocols[] = { - { "raw-test", callback_raw_test, 0, 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - lws_sock_file_fd_type sock; - struct addrinfo h, *r, *rp; - struct lws_vhost *vhost; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal raw adopt tcp\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - info.port = CONTEXT_PORT_NO_LISTEN_SERVER; - info.protocols = protocols; - - vhost = lws_create_vhost(context, &info); - if (!vhost) { - lwsl_err("lws vhost creation failed\n"); - goto bail; - } - - /* - * Connect our own "foreign" socket to libwebsockets.org:80 - * - * Normally you would do this with lws_client_connect_via_info() inside - * the lws event loop, hiding all this detail. But this example - * demonstrates how to integrate an externally-connected "foreign" - * socket, so we create one by hand. - */ - - memset(&h, 0, sizeof(h)); - h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - h.ai_socktype = SOCK_STREAM; - h.ai_protocol = IPPROTO_TCP; - - n = getaddrinfo("libwebsockets.org", "80", &h, &r); - if (n) { - lwsl_err("%s: problem resolving libwebsockets.org: %s\n", __func__, gai_strerror(n)); - return 1; - } - - for (rp = r; rp; rp = rp->ai_next) { - sock.sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (sock.sockfd != LWS_SOCK_INVALID) - break; - } - if (!rp) { - lwsl_err("%s: unable to create INET socket\n", __func__); - freeaddrinfo(r); - - return 1; - } - - lwsl_user("Starting connect...\n"); - if (connect(sock.sockfd, rp->ai_addr, sizeof(*rp->ai_addr)) < 0) { - lwsl_err("%s: unable to connect to libwebsockets.org:80\n", __func__); - freeaddrinfo(r); - return 1; - } - - freeaddrinfo(r); - signal(SIGINT, sigint_handler); - lwsl_user("Connected...\n"); - - /* our foreign socket is connected... adopt it into lws */ - - if (!lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_SOCKET, sock, - protocols[0].name, NULL)) { - lwsl_err("%s: foreign socket adoption failed\n", __func__); - goto bail; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail: - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-tcp/README.md libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-tcp/README.md --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-tcp/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-tcp/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -# lws minimal ws server raw adopt tcp - -This example is only meaningful if you are integrating lws in another -app which generates its own connected sockets. In some cases you may -want lws to "adopt" the socket. - -(If you simply want a connected client raw socket using lws alone, you -can just use lws_client_connect_via_info() with info.method = "RAW". -http-client/minimal-http-client shows how to do that, just set -info.method to "RAW".) - -This example demonstrates how to adopt a foreign, connected socket into lws -as a raw wsi, bound to a specific lws protocol. - -The example connects a socket itself to libwebsockets.org:80, and then -has lws adopt it as a raw wsi. The lws protocol writes "GET / HTTP/1.1" -to the socket and hexdumps what was sent back. - -The socket won't close until the server side times it out, since it's -a raw socket that doesn't understand it's looking at http. - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-raw-adopt-tcp -[2018/03/23 09:03:57:1960] USER: LWS minimal raw adopt tcp -[2018/03/23 09:03:57:1961] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/03/23 09:03:57:2079] USER: Starting connect... -[2018/03/23 09:03:57:4963] USER: Connected... -[2018/03/23 09:03:57:4963] USER: LWS_CALLBACK_RAW_ADOPT -[2018/03/23 09:03:57:7842] USER: LWS_CALLBACK_RAW_RX (186) -[2018/03/23 09:03:57:7842] NOTICE: -[2018/03/23 09:03:57:7842] NOTICE: 0000: 48 54 54 50 2F 31 2E 31 20 33 30 31 20 52 65 64 HTTP/1.1 301 Red -[2018/03/23 09:03:57:7842] NOTICE: 0010: 69 72 65 63 74 0D 0A 73 65 72 76 65 72 3A 20 6C irect..server: l -[2018/03/23 09:03:57:7842] NOTICE: 0020: 77 73 77 73 0D 0A 53 74 72 69 63 74 2D 54 72 61 wsws..Strict-Tra -[2018/03/23 09:03:57:7843] NOTICE: 0030: 6E 73 70 6F 72 74 2D 53 65 63 75 72 69 74 79 3A nsport-Security: -[2018/03/23 09:03:57:7843] NOTICE: 0040: 20 6D 61 78 2D 61 67 65 3D 31 35 37 36 38 30 30 max-age=1576800 -[2018/03/23 09:03:57:7843] NOTICE: 0050: 30 20 3B 20 69 6E 63 6C 75 64 65 53 75 62 44 6F 0 ; includeSubDo -[2018/03/23 09:03:57:7843] NOTICE: 0060: 6D 61 69 6E 73 0D 0A 6C 6F 63 61 74 69 6F 6E 3A mains..location: -[2018/03/23 09:03:57:7843] NOTICE: 0070: 20 68 74 74 70 73 3A 2F 2F 6C 69 62 77 65 62 73 https://libwebs -[2018/03/23 09:03:57:7843] NOTICE: 0080: 6F 63 6B 65 74 73 2E 6F 72 67 0D 0A 63 6F 6E 74 ockets.org..cont -[2018/03/23 09:03:57:7843] NOTICE: 0090: 65 6E 74 2D 74 79 70 65 3A 20 74 65 78 74 2F 68 ent-type: text/h -[2018/03/23 09:03:57:7843] NOTICE: 00A0: 74 6D 6C 0D 0A 63 6F 6E 74 65 6E 74 2D 6C 65 6E tml..content-len -[2018/03/23 09:03:57:7843] NOTICE: 00B0: 67 74 68 3A 20 30 0D 0A 0D 0A gth: 0.... -[2018/03/23 09:03:57:7843] NOTICE: -[2018/03/23 09:04:03:3627] USER: LWS_CALLBACK_RAW_CLOSE - -``` - -Note the example does everything itself, after 5s idle the remote server closes the connection -after which the example continues until you ^C it. diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-udp/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-udp/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-udp/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-udp/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-raw-adopt-udp) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-raw-adopt-udp) -set(SRCS minimal-raw-adopt-udp.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) -require_lws_config(LWS_WITH_UDP 1 requirements) - - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,185 +0,0 @@ -/* - * lws-minimal-raw-adopt-udp - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates integrating a connected udp - * socket into the lws event loop as a RAW wsi. It's interesting in - * the kind of situation where you already have a connected socket - * in your application, and you need to hand it over to lws to deal with. - * - * Lws supports "adopting" these foreign sockets, and also has a helper API - * to create, bind, and adopt them inside lws. - */ - -#include -#include -#include -#if !defined(WIN32) -#include -#include -#include -#include -#endif -#include -#include -#include -#include -#include -#include - -static uint8_t sendbuf[4096]; -static size_t sendlen; -struct lws_udp udp; - -static int -callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - ssize_t n; - int fd; - - switch (reason) { - - /* callbacks related to raw socket descriptor */ - - case LWS_CALLBACK_RAW_ADOPT: - lwsl_user("LWS_CALLBACK_RAW_ADOPT\n"); - break; - - case LWS_CALLBACK_RAW_CLOSE: - lwsl_user("LWS_CALLBACK_RAW_CLOSE\n"); - break; - - case LWS_CALLBACK_RAW_RX: - lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len); - lwsl_hexdump_level(LLL_NOTICE, in, len); - /* - * Take a copy of the buffer and the source socket address... - */ - udp = *(lws_get_udp(wsi)); - sendlen = len; - if (sendlen > sizeof(sendbuf)) - sendlen = sizeof(sendbuf); - memcpy(sendbuf, in, sendlen); - /* - * ... and we send it next time around the event loop. This - * can be extended to having a ringbuffer of different send - * buffers and targets queued. - * - * Note that UDP is ALWAYS writable as far as poll() knows - * because there is no mechanism like the tcp window to - * understand that packets are not being acknowledged. But - * this allows the event loop to share out the work. - */ - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_RAW_WRITEABLE: - - if (!sendlen) - break; - - fd = lws_get_socket_fd(wsi); - if (fd < 0) /* keep Coverity happy: actually it cannot be < 0 */ - break; - - /* - * We can write directly on the UDP socket, specifying - * the peer the write is directed to. - * - * However the kernel may only accept parts of large sendto()s, - * leaving you to try to resend the remainder later. However - * depending on how your protocol on top of UDP works, that - * may involve sticking new headers before the remainder. - * - * For clarity partial sends just drop the remainder here. - */ - n = sendto(fd, -#if defined(WIN32) - (const char *) -#endif - sendbuf, sendlen, 0, &udp.sa, udp.salen); - if (n < (ssize_t)len) - lwsl_notice("%s: send returned %d\n", __func__, (int)n); - break; - - default: - break; - } - - return 0; -} - -static struct lws_protocols protocols[] = { - { "raw-test", callback_raw_test, 0, 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - struct lws_vhost *vhost; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal raw adopt udp | nc -u 127.0.0.1 7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - info.port = CONTEXT_PORT_NO_LISTEN_SERVER; - info.protocols = protocols; - - vhost = lws_create_vhost(context, &info); - if (!vhost) { - lwsl_err("lws vhost creation failed\n"); - goto bail; - } - - /* - * Create our own "foreign" UDP socket bound to 7681/udp - */ - if (!lws_create_adopt_udp(vhost, NULL, 7681, LWS_CAUDP_BIND, - protocols[0].name, NULL, NULL, NULL, NULL)) { - lwsl_err("%s: foreign socket adoption failed\n", __func__); - goto bail; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail: - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-udp/README.md libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-udp/README.md --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-adopt-udp/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-adopt-udp/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -# lws minimal ws server raw adopt udp - -This example demonstrates echoing packets on a UDP socket in lws. - -A "foreign" UDP socket is created, bound (so it can "listen"), and -adopted into lws event loop. It acts like a tcp RAW mode connection in -lws and uses the same callbacks. - -Writing is a bit different for UDP. By default, the system has no -idea about the receiver state and so asking for a callback_on_writable() -always believes that the socket is writeable... the callback will -happen next time around the event loop if there are no pending partials. - -With UDP, there is no "connection". You need to write with sendto() and -direct the packets to a specific destination. You can learn the source -of the last packet that arrived at the LWS_CALLBACK_RAW_RX callback by -getting a `struct lws_udp *` from `lws_get_udp(wsi)`. To be able to -send back to that guy, you should take a copy of the `struct lws_udp *` and -use the .sa and .salen members in your sendto(). - -However the kernel may not accept to buffer / write everything you wanted to send. -So you are responsible to watch the result of sendto() and resend the -unsent part next time. - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-raw-adopt-udp -$ ./lws-minimal-raw-adopt-udp -[2018/03/24 08:12:37:8869] USER: LWS minimal raw adopt udp | nc -u 127.0.0.1 7681 -[2018/03/24 08:12:37:8870] NOTICE: Creating Vhost 'default' (no listener), 1 protocols, IPv6 off -[2018/03/24 08:12:37:8878] USER: LWS_CALLBACK_RAW_ADOPT -[2018/03/24 08:12:41:5656] USER: LWS_CALLBACK_RAW_RX (6) -[2018/03/24 08:12:41:5656] NOTICE: -[2018/03/24 08:12:41:5656] NOTICE: 0000: 68 65 6C 6C 6F 0A hello. -[2018/03/24 08:12:41:5656] NOTICE: -``` - -``` - $ nc -u 127.0.0.1 7681 -hello -hello -``` diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-audio/audio.c libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-audio/audio.c --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-audio/audio.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-audio/audio.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,211 +0,0 @@ -/* - * lws-minimal-raw-audio - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates adopting and managing audio device file descriptors in the - * event loop. - */ - -#include -#include -#include -#include -#include -#include - -#include - -static unsigned int sample_rate = 16000; - -struct raw_vhd { - uint8_t simplebuf[32768 * 2]; - snd_pcm_t *pcm_capture; - snd_pcm_t *pcm_playback; - snd_pcm_hw_params_t *params; - snd_pcm_uframes_t frames; - int filefd; - int rpos; - int wpos; - int times; -}; - -static int -set_hw_params(struct lws_vhost *vh, snd_pcm_t **pcm, int type) -{ - unsigned int rate = sample_rate; - snd_pcm_hw_params_t *params; - lws_sock_file_fd_type u; - struct pollfd pfd; - struct lws *wsi1; - int n; - - n = snd_pcm_open(pcm, "default", type, SND_PCM_NONBLOCK); - if (n < 0) { - lwsl_err("%s: Can't open default for playback: %s\n", - __func__, snd_strerror(n)); - - return -1; - } - - if (snd_pcm_poll_descriptors(*pcm, &pfd, 1) != 1) { - lwsl_err("%s: failed to get playback desc\n", __func__); - return -1; - } - - u.filefd = (lws_filefd_type)(long long)pfd.fd; - wsi1 = lws_adopt_descriptor_vhost(vh, LWS_ADOPT_RAW_FILE_DESC, u, - "lws-audio-test", NULL); - if (!wsi1) { - lwsl_err("%s: Failed to adopt playback desc\n", __func__); - goto bail; - } - if (type == SND_PCM_STREAM_PLAYBACK) - lws_rx_flow_control(wsi1, 0); /* no POLLIN */ - - snd_pcm_hw_params_malloc(¶ms); - snd_pcm_hw_params_any(*pcm, params); - - n = snd_pcm_hw_params_set_access(*pcm, params, - SND_PCM_ACCESS_RW_INTERLEAVED); - if (n < 0) - goto bail1; - - n = snd_pcm_hw_params_set_format(*pcm, params, SND_PCM_FORMAT_S16_LE); - if (n < 0) - goto bail1; - - n = snd_pcm_hw_params_set_channels(*pcm, params, 1); - if (n < 0) - goto bail1; - - n = snd_pcm_hw_params_set_rate_near(*pcm, params, &rate, 0); - if (n < 0) - goto bail1; - - n = snd_pcm_hw_params(*pcm, params); - snd_pcm_hw_params_free(params); - if (n < 0) - goto bail; - - return 0; - -bail1: - snd_pcm_hw_params_free(params); -bail: - lwsl_err("%s: Set hw params failed: %s\n", __func__, snd_strerror(n)); - - return -1; -} - -static int -callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct raw_vhd *vhd = (struct raw_vhd *)lws_protocol_vh_priv_get( - lws_get_vhost(wsi), lws_get_protocol(wsi)); - int n; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), sizeof(struct raw_vhd)); - - if (set_hw_params(lws_get_vhost(wsi), &vhd->pcm_playback, - SND_PCM_STREAM_PLAYBACK)) { - lwsl_err("%s: Can't open default for playback\n", - __func__); - - return -1; - } - - if (set_hw_params(lws_get_vhost(wsi), &vhd->pcm_capture, - SND_PCM_STREAM_CAPTURE)) { - lwsl_err("%s: Can't open default for capture\n", - __func__); - - return -1; - } - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - lwsl_notice("LWS_CALLBACK_PROTOCOL_DESTROY\n"); - if (vhd && vhd->pcm_playback) { - snd_pcm_drain(vhd->pcm_playback); - snd_pcm_close(vhd->pcm_playback); - vhd->pcm_playback = NULL; - } - if (vhd && vhd->pcm_capture) { - snd_pcm_close(vhd->pcm_capture); - vhd->pcm_capture = NULL; - } - break; - - case LWS_CALLBACK_RAW_RX_FILE: - if (vhd->times >= 6) { /* delay amount decided by this */ - n = snd_pcm_writei(vhd->pcm_playback, - &vhd->simplebuf[vhd->rpos], - ((vhd->wpos - vhd->rpos) & - (sizeof(vhd->simplebuf) - 1)) / 2); - vhd->rpos = (vhd->rpos + (n * 2)) & - (sizeof(vhd->simplebuf) - 1); - } - - n = snd_pcm_readi(vhd->pcm_capture, &vhd->simplebuf[vhd->wpos], - (sizeof(vhd->simplebuf) - vhd->wpos) / 2); - lwsl_notice("LWS_CALLBACK_RAW_RX_FILE: %d samples\n", n); - vhd->times++; - - vhd->wpos = (vhd->wpos + (n * 2)) & (sizeof(vhd->simplebuf) - 1); - break; - - default: - break; - } - - return 0; -} - -static struct lws_protocols protocols[] = { - { "lws-audio-test", callback_raw_test, 0, 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - int n = 0; - - signal(SIGINT, sigint_handler); - memset(&info, 0, sizeof info); - lws_cmdline_option_handle_builtin(argc, argv, &info); - - lwsl_user("LWS minimal raw audio\n"); - - info.port = CONTEXT_PORT_NO_LISTEN_SERVER; - info.protocols = protocols; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-audio/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-audio/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-audio/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-audio/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-raw-audio) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-raw-audio) -set(SRCS audio.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_ALSA 1 requirements) -require_lws_config(LWS_WITH_NETWORK 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared asound) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets asound) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-audio/README.md libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-audio/README.md --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-audio/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-audio/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -# lws minimal raw audio - -This demonstrates operating ALSA playback and capture using the lws event loop -via raw file descriptors. - -You need the lws cmake option `-DLWS_WITH_ALSA=1` - -This example opens the default ALSA playback and capture devices and pipes the -capture data into the playback with something over 1s delay via a ringbuffer. - -ALSA doesn't really lend itself to direct use with event loops... this example -uses the capture channel which does create POLLIN normally as the timesource -for the playback as well; they're both set to 16000Hz sample rate. - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-raw-audio -[2019/10/14 18:58:49:3288] U: LWS minimal raw audio -[2019/10/14 18:58:50:3438] N: LWS_CALLBACK_RAW_ADOPT_FILE -[2019/10/14 18:58:50:3455] N: LWS_CALLBACK_RAW_ADOPT_FILE -[2019/10/14 18:58:50:4764] N: LWS_CALLBACK_RAW_RX_FILE: 2062 samples -[2019/10/14 18:58:50:6132] N: LWS_CALLBACK_RAW_RX_FILE: 2205 samples -[2019/10/14 18:58:50:7592] N: LWS_CALLBACK_RAW_RX_FILE: 2328 samples -... -^C[2019/10/14 18:58:56:8460] N: LWS_CALLBACK_RAW_CLOSE_FILE -[2019/10/14 18:58:56:8461] N: LWS_CALLBACK_RAW_CLOSE_FILE -[2019/10/14 18:58:56:8461] N: LWS_CALLBACK_PROTOCOL_DESTROY -$ - -``` diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-raw-fallback-http-server) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-raw-fallback-http-server) -set(SRCS minimal-raw-fallback-http-server.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.key libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,147 +0,0 @@ -/* - * lws-minimal-raw-fallback http-server - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * You can change that by changing mount.origin below. - * - * In addition, if the connection does to seem to be talking http, then it - * falls back to a raw echo protocol. - */ - -#include -#include -#include - -struct pss__raw_echo { - uint8_t buf[2048]; - int len; -}; - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -static int -callback_raw_echo(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - struct pss__raw_echo *pss = (struct pss__raw_echo *)user; - - switch (reason) { - case LWS_CALLBACK_RAW_ADOPT: - lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n"); - break; - - case LWS_CALLBACK_RAW_RX: - lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len); - if (len > sizeof(pss->buf)) - len = sizeof(pss->buf); - memcpy(pss->buf, in, len); - pss->len = len; - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_RAW_CLOSE: - lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n"); - break; - - case LWS_CALLBACK_RAW_WRITEABLE: - lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n"); - lws_write(wsi, pss->buf, pss->len, LWS_WRITE_HTTP); - break; - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { "raw-echo", callback_raw_echo, sizeof(struct pss__raw_echo), 2048 }, - { NULL, NULL, 0, 0 } -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal raw fallback http server | " - "visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.protocols = protocols; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | - LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG; - info.listen_accept_role = "raw-skt"; - info.listen_accept_protocol = "raw-echo"; - - if (lws_cmdline_option(argc, argv, "-s")) { - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - - if (lws_cmdline_option(argc, argv, "-u")) - info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS; - - if (lws_cmdline_option(argc, argv, "-h")) - info.options |= LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER; - } - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ - - - - - - -
- - Hello from the minimal raw fallback http server example. -
- You can confirm the 404 page handler by going to this - nonexistant page. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/README.md libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/README.md --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-fallback-http-server/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-fallback-http-server/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -# lws minimal raw fallback http server - -This is the same as the minimal http server, with one difference... -if you connect to localhost:7681 with something that doesn't send -recognizable http, then the connection will be switched to a -raw-skt role and bind to a protocol that echoes anything sent back -to the sender. - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --s|Configure the server for tls / https and `LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT` --h|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER`, allowing http service on tls port (caution... it's insecure then) --u|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS`, so the server issues a redirect to https to clients that attempt to connect to a server configured for tls with http. - -``` - $ ./lws-minimal-raw-fallback-http-server -[2018/11/29 14:27:34:3014] USER: LWS minimal raw fallback http server | visit http://localhost:7681 -[2018/11/29 14:27:34:3243] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -``` - -Visit http://127.0.0.1:7681 - -This allows testing of various combinations of special features for unexpected -content on an http(s) listening socket. - -|cmdline args|http://127.0.0.1:7681|https://127.0.0.1:7681|ssh -p7681 127.0.0.1|flags| -|---|---|---|---|---| -|none|served|no tls|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG -|-s|echos http GET|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT -|-s -h|served|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT, LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER -|-s -u|redirected to https|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT, LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS - diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-file/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-file/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-file/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-file/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-minimal-raw-file) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-raw-file) -set(SRCS minimal-raw-file.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,160 +0,0 @@ -/* - * lws-minimal-raw-file - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates adopting a file descriptor into the lws event - * loop. - */ - -#include -#include -#include -#include -#include -#include - -struct raw_vhd { -// lws_sock_file_fd_type u; - int filefd; -}; - -static char filepath[256]; - -static int -callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct raw_vhd *vhd = (struct raw_vhd *)lws_protocol_vh_priv_get( - lws_get_vhost(wsi), lws_get_protocol(wsi)); - lws_sock_file_fd_type u; - uint8_t buf[1024]; - int n; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), sizeof(struct raw_vhd)); - vhd->filefd = lws_open(filepath, O_RDWR); - if (vhd->filefd == -1) { - lwsl_err("Unable to open %s\n", filepath); - - return 1; - } - u.filefd = (lws_filefd_type)(long long)vhd->filefd; - if (!lws_adopt_descriptor_vhost(lws_get_vhost(wsi), - LWS_ADOPT_RAW_FILE_DESC, u, - "raw-test", NULL)) { - lwsl_err("Failed to adopt fifo descriptor\n"); - close(vhd->filefd); - vhd->filefd = -1; - - return 1; - } - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - if (vhd && vhd->filefd != -1) - close(vhd->filefd); - break; - - /* callbacks related to raw file descriptor */ - - case LWS_CALLBACK_RAW_ADOPT_FILE: - lwsl_notice("LWS_CALLBACK_RAW_ADOPT_FILE\n"); - break; - - case LWS_CALLBACK_RAW_RX_FILE: - lwsl_notice("LWS_CALLBACK_RAW_RX_FILE\n"); - n = read(vhd->filefd, buf, sizeof(buf)); - if (n < 0) { - lwsl_err("Reading from %s failed\n", filepath); - - return 1; - } - lwsl_hexdump_level(LLL_NOTICE, buf, n); - break; - - case LWS_CALLBACK_RAW_CLOSE_FILE: - lwsl_notice("LWS_CALLBACK_RAW_CLOSE_FILE\n"); - break; - - case LWS_CALLBACK_RAW_WRITEABLE_FILE: - lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE_FILE\n"); - /* - * you can call lws_callback_on_writable() on a raw file wsi as - * usual, and then write directly into the raw filefd here. - */ - break; - - default: - break; - } - - return 0; -} - -static struct lws_protocols protocols[] = { - { "raw-test", callback_raw_test, 0, 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal raw file\n"); - if (argc < 2) { - lwsl_user("Usage: %s " - " eg, /dev/ttyUSB0 or /dev/input/event0 or " - "/proc/self/fd/0\n", argv[0]); - - return 1; - } - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN_SERVER; /* no listen socket for demo */ - info.protocols = protocols; - - lws_strncpy(filepath, argv[1], sizeof(filepath)); - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-file/README.md libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-file/README.md --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-file/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-file/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -# lws minimal ws server - -This demonstrates adopting a file descriptor into the lws event -loop. The filepath to open and adopt is given as an argument to the example app, eg - -``` - $ ./lws-minimal-raw-file -``` - -On a Linux system, some example files for testing might be - - - /proc/self/fd/0 (stdin) - - /dev/ttyUSB0 (a USB <-> serial converter) - - /dev/input/event (needs root... input device events) - -The example application opens the file in the protocol init -handler, and hexdumps data from the file to the lws log -as it becomes available. - -This isn't very useful standalone as shown here for clarity, but you can -freely combine raw file descriptor adoption with other lws server -and client features. - -Becuase raw file events have their own callback reasons, the handlers can -be integrated in a single protocol that also handles http and ws -server and client callbacks without conflict. - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-raw-file /proc/self/fd/0 -[2018/03/22 10:48:53:9709] USER: LWS minimal raw file -[2018/03/22 10:48:53:9876] NOTICE: Creating Vhost 'default' port -2, 1 protocols, IPv6 off -[2018/03/22 10:48:55:0037] NOTICE: LWS_CALLBACK_RAW_ADOPT_FILE - -[2018/03/22 10:48:55:9370] NOTICE: LWS_CALLBACK_RAW_RX_FILE -[2018/03/22 10:48:55:9377] NOTICE: -[2018/03/22 10:48:55:9408] NOTICE: 0000: 0A . - -``` - -The example logs above show the result of typing the Enter key. diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-netcat/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-netcat/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-netcat/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-netcat/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -project(lws-minimal-raw-netcat) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-raw-netcat) -set(SRCS minimal-raw-netcat.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,255 +0,0 @@ -/* - * lws-minimal-raw-netcat - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates sending stdin to a remote socket and printing - * what is returned to stdout. - * - * All the logging is on stderr, so you can tune it out with 2>log - * or whatever. - */ - -#include -#include -#include -#if !defined(WIN32) -#include -#include -#include -#include -#include -#endif -#include -#include -#include -#include -#include - -static struct lws *raw_wsi, *stdin_wsi; -static uint8_t buf[LWS_PRE + 4096]; -static int waiting, interrupted; -static struct lws_context *context; -static int us_wait_after_input_close = LWS_USEC_PER_SEC / 10; - -static int -callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - const char *cp = (const char *)in; - - switch (reason) { - - /* callbacks related to file descriptor */ - - case LWS_CALLBACK_RAW_ADOPT_FILE: - lwsl_user("LWS_CALLBACK_RAW_ADOPT_FILE\n"); - break; - - case LWS_CALLBACK_RAW_CLOSE_FILE: - lwsl_user("LWS_CALLBACK_RAW_CLOSE_FILE\n"); - /* stdin close, wait 1s then close the raw skt */ - stdin_wsi = NULL; /* invalid now we close */ - if (raw_wsi) - lws_set_timer_usecs(raw_wsi, us_wait_after_input_close); - else { - interrupted = 1; - lws_cancel_service(context); - } - break; - - case LWS_CALLBACK_RAW_RX_FILE: - lwsl_user("LWS_CALLBACK_RAW_RX_FILE\n"); - waiting = read(0, buf, sizeof(buf)); - lwsl_notice("raw file read %d\n", waiting); - if (waiting < 0) - return -1; - - if (raw_wsi) - lws_callback_on_writable(raw_wsi); - lws_rx_flow_control(wsi, 0); - break; - - - /* callbacks related to raw socket descriptor */ - - case LWS_CALLBACK_RAW_ADOPT: - lwsl_user("LWS_CALLBACK_RAW_ADOPT\n"); - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_RAW_CLOSE: - lwsl_user("LWS_CALLBACK_RAW_CLOSE\n"); - /* - * If the socket to the remote server closed, we must close - * and drop any remaining stdin - */ - interrupted = 1; - lws_cancel_service(context); - /* our pointer to this wsi is invalid now we close */ - raw_wsi = NULL; - break; - - case LWS_CALLBACK_RAW_RX: - lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len); - while (len--) - putchar(*cp++); - fflush(stdout); - break; - - case LWS_CALLBACK_RAW_WRITEABLE: - lwsl_user("LWS_CALLBACK_RAW_WRITEABLE\n"); - // lwsl_hexdump_info(buf, waiting); - if (stdin_wsi) - lws_rx_flow_control(stdin_wsi, 1); - if (lws_write(wsi, buf, waiting, LWS_WRITE_RAW) != waiting) { - lwsl_notice("%s: raw skt write failed\n", __func__); - - return -1; - } - break; - - case LWS_CALLBACK_TIMER: - lwsl_user("LWS_CALLBACK_TIMER\n"); - interrupted = 1; - lws_cancel_service(context); - return -1; - - default: - break; - } - - return 0; -} - -static struct lws_protocols protocols[] = { - { "raw-test", callback_raw_test, 0, 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - const char *server = "libwebsockets.org", *port = "80"; - struct lws_context_creation_info info; - lws_sock_file_fd_type sock; - struct addrinfo h, *r, *rp; - struct lws_vhost *vhost; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal raw netcat [--server ip] [--port port] [-w ms]\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - info.port = CONTEXT_PORT_NO_LISTEN_SERVER; - info.protocols = protocols; - - vhost = lws_create_vhost(context, &info); - if (!vhost) { - lwsl_err("lws vhost creation failed\n"); - goto bail; - } - - /* - * Connect our own "foreign" socket to libwebsockets.org:80 - * - * Normally you would do this with lws_client_connect_via_info() inside - * the lws event loop, hiding all this detail. But this example - * demonstrates how to integrate an externally-connected "foreign" - * socket, so we create one by hand. - */ - - memset(&h, 0, sizeof(h)); - h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - h.ai_socktype = SOCK_STREAM; - h.ai_protocol = IPPROTO_TCP; - - if ((p = lws_cmdline_option(argc, argv, "--port"))) - port = p; - - if ((p = lws_cmdline_option(argc, argv, "--server"))) - server = p; - - if ((p = lws_cmdline_option(argc, argv, "-w"))) - us_wait_after_input_close = 1000 * atoi(p); - - n = getaddrinfo(server, port, &h, &r); - if (n) { - lwsl_err("%s: problem resolving %s: %s\n", __func__, - server, gai_strerror(n)); - return 1; - } - - for (rp = r; rp; rp = rp->ai_next) { - sock.sockfd = socket(rp->ai_family, rp->ai_socktype, - rp->ai_protocol); - if (sock.sockfd != LWS_SOCK_INVALID) - break; - } - if (!rp) { - lwsl_err("%s: unable to create INET socket\n", __func__); - freeaddrinfo(r); - - return 1; - } - - lwsl_user("Starting connect to %s:%s...\n", server, port); - if (connect(sock.sockfd, rp->ai_addr, sizeof(*rp->ai_addr)) < 0) { - lwsl_err("%s: unable to connect\n", __func__); - freeaddrinfo(r); - return 1; - } - - freeaddrinfo(r); - signal(SIGINT, sigint_handler); - lwsl_user("Connected...\n"); - - /* our foreign socket is connected... adopt it into lws */ - - raw_wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_SOCKET, sock, - protocols[0].name, NULL); - if (!raw_wsi) { - lwsl_err("%s: foreign socket adoption failed\n", __func__); - goto bail; - } - - sock.filefd = 0; - stdin_wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_FILE_DESC, - sock, protocols[0].name, NULL); - if (!stdin_wsi) { - lwsl_err("%s: stdin adoption failed\n", __func__); - goto bail; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail: - - lwsl_user("%s: destroying context\n", __func__); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-netcat/README.md libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-netcat/README.md --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-netcat/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-netcat/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -# lws minimal raw netcat - -This example shows to to create a "netcat" that copies its stdin to -a remote socket and prints what is returned in stdout. - -It has some advantage over the real netcat, it will wait 1s after stdin closes -to print results that are in flight. - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ echo -e -n "GET / http/1.1\r\n\r\n"| ./lws-minimal-raw-netcat -[2018/05/02 08:53:53:2665] USER: LWS minimal raw netcat [--server ip] [--port port] -[2018/05/02 08:53:53:2667] NOTICE: Creating Vhost 'default' (no listener), 1 protocols, IPv6 off -[2018/05/02 08:53:53:2703] USER: Starting connect... -[2018/05/02 08:53:53:5644] USER: Connected to libwebsockets.org:80... -[2018/05/02 08:53:53:5645] USER: LWS_CALLBACK_RAW_ADOPT -[2018/05/02 08:53:53:5645] USER: LWS_CALLBACK_RAW_ADOPT_FILE -[2018/05/02 08:53:53:5646] USER: LWS_CALLBACK_RAW_RX_FILE -[2018/05/02 08:53:53:5646] USER: LWS_CALLBACK_RAW_CLOSE_FILE -[2018/05/02 08:53:53:8600] USER: LWS_CALLBACK_RAW_RX (186) -HTTP/1.1 301 Redirect -server: lwsws -Strict-Transport-Security: max-age=15768000 ; includeSubDomains -location: https://libwebsockets.org -content-type: text/html -content-length: 0 - -``` - -Note the example does everything itself, after 5s idle the remote server closes the connection -after which the example continues until you ^C it. diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -project(lws-minimal-raw-proxy) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-raw-proxy) -set(SRCS minimal-raw-proxy.c) - -# NOTE... if you are building this standalone, you must point LWS_PLUGINS_DIR -# to the lws plugins dir so it can pick up the plugin source. Eg, -# cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_RAW_PROXY 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (LWS_PLUGINS_DIR) - include_directories(${LWS_PLUGINS_DIR}) - endif() - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy/minimal-raw-proxy.c libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy/minimal-raw-proxy.c --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy/minimal-raw-proxy.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy/minimal-raw-proxy.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -/* - * lws-minimal-raw-proxy - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a vhost that acts as a raw tcp proxy. Incoming connections - * cause an outgoing connection to be initiated, and if successfully established - * then traffic coming in one side is placed on a ringbuffer and sent out the - * opposite side as soon as possible. - */ - -#include -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "../plugins/raw-proxy/protocol_lws_raw_proxy.c" - -static struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_RAW_PROXY, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -static struct lws_protocol_vhost_options pvo1 = { - NULL, - NULL, - "onward", /* pvo name */ - "ipv4:127.0.0.1:22" /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo1, /* "child" pvo linked-list */ - "raw-proxy", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; - - -int main(int argc, const char **argv) -{ - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_context_creation_info info; - struct lws_context *context; - char outward[256]; - const char *p; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal raw proxy\n"); - - if ((p = lws_cmdline_option(argc, argv, "-r"))) { - lws_strncpy(outward, p, sizeof(outward)); - pvo1.value = outward; - } - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.protocols = protocols; - info.pvo = &pvo; - info.options = LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG; - info.listen_accept_role = "raw-proxy"; - info.listen_accept_protocol = "raw-proxy"; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy/README.md libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy/README.md --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -# lws minimal ws server raw proxy - -This demonstrates how a vhost can be bound to a specific role and protocol, -with the example using a lws plugin that performs raw packet proxying. - -By default the example will proxy 127.0.0.1:22, usually your ssh server -listen port, on 127.0.0.1:7681. You should be able to ssh into port 7681 -the same as you can port 22. But your ssh server is only listening on port 22... - -## build - -To build this standalone, you must tell cmake where the lws source tree -./plugins directory can be found, since it relies on including the source -of the raw-proxy plugin. - -``` - $ cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --r ipv4:address:port|Configure the remote IP and port that will be proxied, by default ipv4:127.0.0.1:22 - -``` - $ ./lws-minimal-raw-proxy -[2018/11/30 19:22:35:7290] USER: LWS minimal raw proxy | nc localhost 7681 -[2018/11/30 19:22:35:7291] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/11/30 19:22:35:7336] NOTICE: callback_raw_proxy: onward ipv4 127.0.0.1:22 -... -``` - -``` - $ ssh -p7681 me@127.0.0.1 -Last login: Fri Nov 30 19:29:23 2018 from 127.0.0.1 -[me@learn ~]$ -``` - - diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -project(lws-minimal-raw-proxy-fallback) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-raw-proxy-fallback) -set(SRCS minimal-raw-proxy-fallback.c) - -# NOTE... if you are building this standalone, you must point LWS_PLUGINS_DIR -# to the lws plugins dir so it can pick up the plugin source. Eg, -# cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_RAW_PROXY 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (LWS_PLUGINS_DIR) - include_directories(${LWS_PLUGINS_DIR}) - endif() - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.key libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/minimal-raw-proxy-fallback.c libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/minimal-raw-proxy-fallback.c --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/minimal-raw-proxy-fallback.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/minimal-raw-proxy-fallback.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -/* - * lws-minimal-raw-proxy-fallback - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a normal http / https server which if it receives something - * it can't make sense of at the start, falls back to becoming a raw tcp proxy - * to a specified address and port. - * - * Incoming connections cause an outgoing connection to be initiated, and if - * successfully established then traffic coming in one side is placed on a - * ringbuffer and sent out the opposite side as soon as possible. - * - * If it receives expected packets for an http(s) connection, it acts like a - * normal h1 / h2 webserver. - */ - -#include -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "../plugins/raw-proxy/protocol_lws_raw_proxy.c" - -static struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_RAW_PROXY, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -static int interrupted; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -static struct lws_protocol_vhost_options pvo1 = { - NULL, - NULL, - "onward", /* pvo name */ - "ipv4:127.0.0.1:22" /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo1, /* "child" pvo linked-list */ - "raw-proxy", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; - - -int main(int argc, const char **argv) -{ - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_context_creation_info info; - struct lws_context *context; - char outward[256]; - const char *p; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal raw proxy fallback | visit http://localhost:7681\n"); - - if ((p = lws_cmdline_option(argc, argv, "-r"))) { - lws_strncpy(outward, p, sizeof(outward)); - pvo1.value = outward; - } - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.protocols = protocols; - info.pvo = &pvo; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | - LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG; - info.listen_accept_role = "raw-proxy"; - info.listen_accept_protocol = "raw-proxy"; - - if (lws_cmdline_option(argc, argv, "-s")) { - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - - if (lws_cmdline_option(argc, argv, "-u")) - info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS; - - if (lws_cmdline_option(argc, argv, "-h")) - info.options |= LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER; - } - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/404.html libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/404.html --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/404.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/404.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - -
-

404

- Sorry, that file doesn't exist. - - - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ - - - - - - -
- - Hello from the minimal raw fallback http server example. -
- You can confirm the 404 page handler by going to this - nonexistant page. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/README.md libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/README.md --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-proxy-fallback/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-proxy-fallback/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -# lws minimal ws server raw proxy fallback - -This demonstrates how a vhost doing normal http or http(s) duty can be also be -bound to a specific role and protocol as a fallback if the incoming protocol is -unexpected for tls or http. The example makes the fallback role + protocol -an lws plugin that performs raw packet proxying. - -By default the fallback in the example will proxy 127.0.0.1:22, which is usually -your ssh server listen port, on 127.0.0.1:7681. You should be able to ssh into -port 7681 the same as you can port 22. At the same time, you should be able to -visit http://127.0.0.1:7681 in a browser (and if you give -s, to -https://127.0.0.1:7681 while your ssh client can still connect to the same -port. - -## build - -To build this standalone, you must tell cmake where the lws source tree -./plugins directory can be found, since it relies on including the source -of the raw-proxy plugin. - -``` - $ cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --r ipv4:address:port|Configure the remote IP and port that will be proxied, by default ipv4:127.0.0.1:22 --s|Configure the server for tls / https and `LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT` --h|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER`, allowing http service on tls port (caution... it's insecure then) --u|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS`, so the server issues a redirect to https to clients that attempt to connect to a server configured for tls with http. -``` - $ ./lws-minimal-raw-proxy -[2018/11/30 19:22:35:7290] USER: LWS minimal raw proxy-fallback -[2018/11/30 19:22:35:7291] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/11/30 19:22:35:7336] NOTICE: callback_raw_proxy: onward ipv4 127.0.0.1:22 -... -``` - -``` - $ ssh -p7681 me@127.0.0.1 -Last login: Fri Nov 30 19:29:23 2018 from 127.0.0.1 -[me@learn ~]$ -``` - -At the same time, visiting http(s)://127.0.0.1:7681 in a browser works fine. - diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-serial/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-serial/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-serial/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-serial/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-minimal-raw-serial) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-raw-serial) -set(SRCS minimal-raw-file.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-serial/minimal-raw-file.c libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-serial/minimal-raw-file.c --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-serial/minimal-raw-file.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-serial/minimal-raw-file.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,231 +0,0 @@ -/* - * lws-minimal-raw-file - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates dealing with a serial port - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#if defined(__linux__) -#include -#include -#endif - -struct raw_vhd { - lws_sorted_usec_list_t sul; - struct lws *wsi; - int filefd; -}; - -static char filepath[256]; - -static void -sul_cb(lws_sorted_usec_list_t *sul) -{ - struct raw_vhd *v = lws_container_of(sul, struct raw_vhd, sul); - - lws_callback_on_writable(v->wsi); - - lws_sul_schedule(lws_get_context(v->wsi), 0, &v->sul, sul_cb, - 2 * LWS_USEC_PER_SEC); -} - -static int -callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct raw_vhd *vhd = (struct raw_vhd *)lws_protocol_vh_priv_get( - lws_get_vhost(wsi), lws_get_protocol(wsi)); -#if defined(__linux__) - struct serial_struct s_s; -#endif - lws_sock_file_fd_type u; - struct termios tio; - uint8_t buf[1024]; - int n; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), sizeof(struct raw_vhd)); - vhd->filefd = lws_open(filepath, O_RDWR); - if (vhd->filefd == -1) { - lwsl_err("Unable to open %s\n", filepath); - - return 1; - } - - tcflush(vhd->filefd, TCIOFLUSH); - -#if defined(__linux__) - if (ioctl(vhd->filefd, TIOCGSERIAL, &s_s) == 0) { - s_s.closing_wait = ASYNC_CLOSING_WAIT_NONE; - ioctl(vhd->filefd, TIOCSSERIAL, &s_s); - } -#endif - - /* enforce suitable tty state */ - - memset(&tio, 0, sizeof tio); - if (tcgetattr(vhd->filefd, &tio)) { - close(vhd->filefd); - vhd->filefd = -1; - return -1; - } - - cfsetispeed(&tio, B115200); - cfsetospeed(&tio, B115200); - - tio.c_lflag &= ~(ISIG | ICANON | IEXTEN | ECHO | -#if defined(__linux__) - XCASE | -#endif - ECHOE | ECHOK | ECHONL | ECHOCTL | ECHOKE); - tio.c_iflag &= ~(INLCR | IGNBRK | IGNPAR | IGNCR | ICRNL | - IMAXBEL | IXON | IXOFF | IXANY -#if defined(__linux__) - | IUCLC -#endif - | 0xff); - tio.c_oflag = 0; - - tio.c_cc[VMIN] = 1; - tio.c_cc[VTIME] = 0; - tio.c_cc[VEOF] = 1; - tio.c_cflag &= ~( -#if defined(__linux__) - CBAUD | -#endif - CSIZE | CSTOPB | PARENB | CRTSCTS); - tio.c_cflag |= 0x1412 | CS8 | CREAD | CLOCAL; - - tcsetattr(vhd->filefd, TCSANOW, &tio); - - u.filefd = (lws_filefd_type)(long long)vhd->filefd; - if (!lws_adopt_descriptor_vhost(lws_get_vhost(wsi), - LWS_ADOPT_RAW_FILE_DESC, u, - "raw-test", NULL)) { - lwsl_err("Failed to adopt fifo descriptor\n"); - close(vhd->filefd); - vhd->filefd = -1; - - return 1; - } - - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - if (vhd && vhd->filefd != -1) - close(vhd->filefd); - break; - - /* callbacks related to raw file descriptor */ - - case LWS_CALLBACK_RAW_ADOPT_FILE: - lwsl_notice("LWS_CALLBACK_RAW_ADOPT_FILE\n"); - vhd->wsi = wsi; - lws_sul_schedule(lws_get_context(wsi), 0, &vhd->sul, sul_cb, 1); - break; - - case LWS_CALLBACK_RAW_RX_FILE: - lwsl_notice("LWS_CALLBACK_RAW_RX_FILE\n"); - n = read(vhd->filefd, buf, sizeof(buf)); - if (n < 0) { - lwsl_err("Reading from %s failed\n", filepath); - - return 1; - } - lwsl_hexdump_level(LLL_NOTICE, buf, n); - break; - - case LWS_CALLBACK_RAW_CLOSE_FILE: - lwsl_notice("LWS_CALLBACK_RAW_CLOSE_FILE\n"); - lws_sul_schedule(lws_get_context(wsi), 0, &vhd->sul, sul_cb, LWS_SET_TIMER_USEC_CANCEL); - break; - - case LWS_CALLBACK_RAW_WRITEABLE_FILE: - lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE_FILE\n"); - if (lws_write(wsi, (uint8_t *)"hello-this-is-written-every-couple-of-seconds\r\n", 47, LWS_WRITE_RAW) != 47) - return -1; - break; - - default: - break; - } - - return 0; -} - -static struct lws_protocols protocols[] = { - { "raw-test", callback_raw_test, 0, 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal raw serial\n"); - if (argc < 2) { - lwsl_user("Usage: %s " - " eg, /dev/ttyUSB0\n", argv[0]); - - return 1; - } - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN_SERVER; /* no listen socket for demo */ - info.protocols = protocols; - - lws_strncpy(filepath, argv[1], sizeof(filepath)); - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-serial/README.md libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-serial/README.md --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-serial/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-serial/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -# lws minimal raw serial example - -This demonstrates adopting a file descriptor representing a serial device -into the event loop, printing a string on it every couple of seconds and -showing any serial that is received. - -The serial terminal is configured for 115200 8N1. - - -``` - $ ./lws-minimal-raw-serial -``` - - -## build - -``` - $ cmake . && make -``` - -## usage - -``` -[2019/12/08 16:30:53:4436] U: LWS minimal raw serial -[2019/12/08 16:30:53:5016] E: callback_ntpc: set up system ops for set_clock -[2019/12/08 16:30:54:8061] N: callback_ntpc: Unix time: 1575822654 -[2019/12/08 16:30:54:8253] N: LWS_CALLBACK_RAW_ADOPT_FILE -[2019/12/08 16:30:54:8364] N: callback_ntpc: LWS_CALLBACK_RAW_CLOSE -[2019/12/08 16:30:54:8456] N: LWS_CALLBACK_RAW_WRITEABLE_FILE -[2019/12/08 16:30:56:8455] N: LWS_CALLBACK_RAW_WRITEABLE_FILE -[2019/12/08 16:30:58:8460] N: LWS_CALLBACK_RAW_WRITEABLE_FILE -[2019/12/08 16:30:59:1570] N: LWS_CALLBACK_RAW_RX_FILE -[2019/12/08 16:30:59:1604] N: -[2019/12/08 16:30:59:1641] N: 0000: 62 b -[2019/12/08 16:30:59:1644] N: -[2019/12/08 16:31:00:8463] N: LWS_CALLBACK_RAW_WRITEABLE_FILE -[2019/12/08 16:31:01:6392] N: LWS_CALLBACK_RAW_RX_FILE -[2019/12/08 16:31:01:6397] N: -[2019/12/08 16:31:01:6407] N: 0000: 65 e -[2019/12/08 16:31:01:6411] N: -[2019/12/08 16:31:02:8463] N: LWS_CALLBACK_RAW_WRITEABLE_FILE -... . - -``` - -The remote serial connection will show the string sent every 2s. diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-vhost/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-vhost/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-vhost/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-vhost/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -project(lws-minimal-raw-vhost) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-raw-vhost) -set(SRCS minimal-raw-vhost.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-vhost/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-vhost/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-vhost/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-vhost/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-vhost/localhost-100y.key libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-vhost/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-vhost/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-vhost/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,158 +0,0 @@ -/* - * lws-minimal-raw-vhost - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates integrating a raw tcp listener into the lws event loop. - * - * This demo doesn't have any http or ws support. You can connect to it - * using netcat. If you make multiple connections to it, things typed in one - * netcat session are broadcast to all netcat connections. - * - * $ nc localhost 7681 - * - * You can add more vhosts with things like http or ws support, it's as it is - * for clarity. - * - * The main point is the apis and ways of managing raw sockets are almost - * identical to http or ws mode sockets in lws. The callback names for raw - * wsi are changed to be specific to RAW mode is all. - */ - -#include -#include -#include -#include -#include -#include - -struct raw_pss { - struct raw_pss *pss_list; - struct lws *wsi; -}; - -/* one of these is created for each vhost our protocol is used with */ - -struct raw_vhd { - struct raw_pss *pss_list; /* linked-list of live pss*/ - - int len; - uint8_t buf[4096]; -}; - -static int -callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct raw_pss *pss = (struct raw_pss *)user; - struct raw_vhd *vhd = (struct raw_vhd *)lws_protocol_vh_priv_get( - lws_get_vhost(wsi), lws_get_protocol(wsi)); - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), sizeof(struct raw_vhd)); - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - break; - - /* callbacks related to raw socket descriptor */ - - case LWS_CALLBACK_RAW_ADOPT: - lwsl_user("LWS_CALLBACK_RAW_ADOPT\n"); - pss->wsi = wsi; - lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); - break; - - case LWS_CALLBACK_RAW_CLOSE: - lwsl_user("LWS_CALLBACK_RAW_CLOSE\n"); - lws_ll_fwd_remove(struct raw_pss, pss_list, pss, vhd->pss_list); - break; - - case LWS_CALLBACK_RAW_RX: - lwsl_user("LWS_CALLBACK_RAW_RX: %d\n", (int)len); - vhd->len = len; - if (vhd->len > (int)sizeof(vhd->buf)) - vhd->len = sizeof(vhd->buf); - memcpy(vhd->buf, in, vhd->len); - lws_start_foreach_llp(struct raw_pss **, ppss, vhd->pss_list) { - lws_callback_on_writable((*ppss)->wsi); - } lws_end_foreach_llp(ppss, pss_list); - break; - - case LWS_CALLBACK_RAW_WRITEABLE: - if (lws_write(wsi, vhd->buf, vhd->len, LWS_WRITE_RAW) != - vhd->len) { - lwsl_notice("%s: raw write failed\n", __func__); - return 1; - } - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static struct lws_protocols protocols[] = { - { "raw-test", callback_raw_test, sizeof(struct raw_pss), 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal raw vhost | nc localhost 7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.protocols = protocols; - info.options = LWS_SERVER_OPTION_ONLY_RAW; /* vhost accepts RAW */ - - if (lws_cmdline_option(argc, argv, "-s")) { - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-vhost/README.md libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-vhost/README.md --- libwebsockets-4.0.20/minimal-examples/raw/minimal-raw-vhost/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/minimal-raw-vhost/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -# lws minimal ws server raw vhost - -This demonstrates setting up a vhost to listen and accept raw sockets. -Raw sockets are just sockets... lws does not send anything on them or -interpret by itself what it receives on them. So you can implement -arbitrary tcp protocols using them. - -This isn't very useful standalone as shown here for clarity, but you can -freely combine a raw socket vhost with other lws server -and client features and other vhosts handling http or ws. - -Becuase raw socket events have their own callback reasons, the handlers can -be integrated in a single protocol that also handles http and ws -server and client callbacks without conflict. - -## build - -``` - $ cmake . && make -``` - -## usage - - -s means listen using tls - -``` - $ ./lws-minimal-raw-vhost -[2018/03/22 14:49:47:9516] USER: LWS minimal raw vhost -[2018/03/22 14:49:47:9673] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/03/22 14:49:52:3789] USER: LWS_CALLBACK_RAW_ADOPT -[2018/03/22 14:49:57:4271] USER: LWS_CALLBACK_RAW_CLOSE -``` - -``` - $ nc localhost 7681 -hello -hello -``` - -Connect one or more sessions to the server using netcat... lines you type -into netcat are sent to the server, which echos them to all connected clients. - diff -Nru libwebsockets-4.0.20/minimal-examples/raw/README.md libwebsockets-2.4.2/minimal-examples/raw/README.md --- libwebsockets-4.0.20/minimal-examples/raw/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/raw/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -|name|demonstrates| ----|--- -minimal-raw-adopt-tcp|Shows how to have lws adopt an existing tcp socket something else had connected -minimal-raw-adopt-udp|Shows how to create a udp socket and read and write on it -minimal-raw-fallback-http|Shows how to run a normal http(s) server that falls back to a specified role + protocol -minimal-raw-file|Shows how to adopt a file descriptor (device node, fifo, file, etc) into the lws event loop and handle events -minimal-raw-netcat|Writes stdin to a remote server and prints results on stdout -minimal-raw-proxy-fallback|Shows how to run a normal http(s) server that falls back to a proxied connection to a specified IP and port -minimal-raw-proxy|Shows how to set up a vhost so it listens for connections and proxies them to a specified IP and port -minimal-raw-vhost|Shows how to set up a vhost that listens and accepts RAW socket connections - diff -Nru libwebsockets-4.0.20/minimal-examples/README.md libwebsockets-2.4.2/minimal-examples/README.md --- libwebsockets-4.0.20/minimal-examples/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -|name|demonstrates| ----|--- -client-server|Minimal examples providing client and server connections simultaneously -crypto|Minimal examples related to using lws crypto apis -dbus-server|Minimal examples showing how to integrate DBUS into lws event loop -http-client|Minimal examples providing an http client -http-server|Minimal examples providing an http server -raw|Minimal examples related to adopting raw file or socket descriptors into the event loop -secure-streams|Minimal examples related to the Secure Streams client api -ws-client|Minimal examples providing a ws client -ws-server|Minimal examples providing a ws server (and an http server) - -## FAQ - -### Getting started - -Build and install lws itself first (note that after installing lws on \*nix, you need to run `ldconfig` one time so the OS can learn about the new library. Lws installs in `/usr/local` by default, Debian / Ubuntu ldconfig knows to look there already, but Fedora / CentOS need you to add the line `/usr/local/lib` to `/etc/ld.so.conf` and run ldconfig) - -Then start with the simplest: - -`http-server/minimal-http-server` - -### Why are most of the sources split into a main C file file and a protocol file? - -Lws supports three ways to implement the protocol callback code: - - - you can just add it all in the same source file - - - you can separate it as these examples do, and #include it - into the main sources - - - you can build it as a standalone plugin that is discovered - and loaded at runtime. - -The way these examples are structured, you can easily also build -the protocol callback as a plugin just with a different -CMakeLists.txt... see https://github.com/warmcat/libwebsockets/tree/master/plugin-standalone -for an example. - -### Why would we want the protocol as a plugin? - -You will notice a lot of the main C code is the same boilerplate -repeated for each example. The actual interesting part is in -the protocol callback only. - -Lws provides (-DLWS_WITH_LWSWS=1) a generic lightweight server app called 'lwsws' that -can be configured by JSON. Combined with your protocol as a plugin, -it means you don't actually have to make a special server "app" -part, you can just use lwsws and pass per-vhost configuration -from JSON into your protocol. (Of course in some cases you have -an existing app you are bolting lws on to, then you don't care -about this for that particular case). - -Because lwsws has no dependency on whatever your plugin does, it -can mix and match different protocols randomly without needing any code -changes. It reduces the size of the task to just writing the -code you care about in your protocol handler, and nothing else to write -or maintain. - -Lwsws supports advanced features like reload, where it starts a new server -instance with changed config or different plugins, while keeping the old -instance around until the last connection to it closes. - -### I get why there is a pss, but why is there a vhd? - -The pss is instantiated per-connection. But there are almost always -other variables that have a lifetime longer than a single connection. - -You could make these variables "filescope" one-time globals, but that -means your protocol cannot instantiate multiple times. - -Lws supports vhosts (virtual hosts), for example both https://warmcat.com -and https://libwebsockets are running on the same lwsws instance on the -same server and same IP... each of these is a separate vhost. - -Your protocol may be enabled on multiple vhosts, each of these vhosts -provides a different vhd specific to the protocol instance on that -vhost. For example many of the samples keep a linked-list head to -a list of live pss in the vhd... that means it's cleanly a list of -pss opened **on that vhost**. If another vhost has the protocol -enabled, connections to that will point to a different vhd, and the -linked-list head on that vhd will only list connections to his vhost. - -The example "ws-server/minimal-ws-server-threads" demonstrates how to deliver -external configuration data to a specific vhost + protocol -combination using code. In lwsws, this is simply a matter of setting -the desired JSON config. - - diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-secure-streams) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) -require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) - -if (requirements) - add_executable(${SAMP} minimal-secure-streams.c) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() - - if (LWS_WITH_SECURE_STREAMS_PROXY_API) - add_compile_options(-DLWS_SS_USE_SSPC) - - add_executable(${SAMP}-client minimal-secure-streams.c) - if (websockets_shared) - target_link_libraries(${SAMP}-client websockets_shared) - add_dependencies(${SAMP}-client websockets_shared) - else() - target_link_libraries(${SAMP}-client websockets) - endif() - endif() - -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams/minimal-secure-streams.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams/minimal-secure-streams.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams/minimal-secure-streams.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams/minimal-secure-streams.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,421 +0,0 @@ -/* - * lws-minimal-secure-streams - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * - * This demonstrates a minimal http client using secure streams api. - * - * It visits https://warmcat.com/ and receives the html page there. - * - * This example is built two different ways from the same source... one includes - * the policy everything needed to fulfil the stream directly. The other -client - * variant has no policy itself and some other minor init changes, and connects - * to the -proxy example to actually get the connection done. - * - * In the -client build case, the example does not even init the tls libraries - * since the proxy part will take care of all that. - */ - -#include -#include -#include - -/* - * uncomment to force network traffic through 127.0.0.1:1080 - * - * On your local machine, you can run a SOCKS5 proxy like this - * - * $ ssh -N -D 0.0.0.0:1080 localhost -v - * - * If enabled, this also fetches a remote policy that also - * specifies that all traffic should go through the remote - * proxy. - */ -// #define VIA_LOCALHOST_SOCKS - -static int interrupted, bad = 1; -static lws_state_notify_link_t nl; - -/* - * If the -proxy app is fulfilling our connection, then we don't need to have - * the policy in the client. - * - * When we build with LWS_SS_USE_SSPC, the apis hook up to a proxy process over - * a Unix Domain Socket. To test that, you need to separately run the - * ./lws-minimal-secure-streams-proxy test app on the same machine. - */ - -#if !defined(LWS_SS_USE_SSPC) -static const char * const default_ss_policy = - "{" - "\"release\":" "\"01234567\"," - "\"product\":" "\"myproduct\"," - "\"schema-version\":" "1," -#if defined(VIA_LOCALHOST_SOCKS) - "\"via-socks5\":" "\"127.0.0.1:1080\"," -#endif - - "\"retry\": [" /* named backoff / retry strategies */ - "{\"default\": {" - "\"backoff\": [" "1000," - "2000," - "3000," - "5000," - "10000" - "]," - "\"conceal\":" "5," - "\"jitterpc\":" "20," - "\"svalidping\":" "30," - "\"svalidhup\":" "35" - "}}" - "]," - "\"certs\": [" /* named individual certificates in BASE64 DER */ - /* - * Let's Encrypt certs for warmcat.com / libwebsockets.org - * - * We fetch the real policy from there using SS and switch to - * using that. - */ - "{\"isrg_root_x1\": \"" /* ISRG ROOT X1 */ - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" - "\"}," - "{\"LEX3_isrg_root_x1\": \"" /* LE X3 signed by ISRG X1 root */ - "MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1" - "WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg" - "RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi" - "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX" - "NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf" - "89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl" - "Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc" - "Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz" - "uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB" - "AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU" - "BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB" - "FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo" - "SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js" - "LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF" - "BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG" - "AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD" - "VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB" - "ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx" - "A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM" - "UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2" - "DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1" - "eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu" - "OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw" - "p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY" - "2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0" - "ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR" - "PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b" - "rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt" - "\"}" - "]," - "\"trust_stores\": [" /* named cert chains */ - "{" - "\"name\": \"le_via_isrg\"," - "\"stack\": [" - "\"isrg_root_x1\"," - "\"LEX3_isrg_root_x1\"" - "]" - "}" - "]," - "\"s\": [" - "{\"fetch_policy\": {" - "\"endpoint\":" "\"warmcat.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h1\"," - "\"http_method\":" "\"GET\"," -#if defined(VIA_LOCALHOST_SOCKS) - "\"http_url\":" "\"policy/minimal-proxy-socks.json\"," -#else - "\"http_url\":" "\"policy/minimal-proxy.json\"," -#endif - "\"tls\":" "true," - "\"opportunistic\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"le_via_isrg\"" - "}}" - "}" -; - -#endif - -typedef struct myss { - struct lws_ss_handle *ss; - void *opaque_data; - /* ... application specific state ... */ - lws_sorted_usec_list_t sul; -} myss_t; - -static const char *canned_root_token_payload = - "grant_type=refresh_token" - "&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg" - "SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP" - "zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y" - "0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW" - "k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE" - "iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S" - "KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc" - "AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI" - "xL_hDCcTho8opCVX-6QhJHl6SQFlTw13" - "&client_id=" - "amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d"; - -/* secure streams payload interface */ - -static int -myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ -// myss_t *m = (myss_t *)userobj; - - lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags); - lwsl_hexdump_info(buf, len); - - /* - * If we received the whole message, for our example it means - * we are done. - */ - if (flags & LWSSS_FLAG_EOM) { - bad = 0; - interrupted = 1; - } - - return 0; -} - -static int -myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, - int *flags) -{ - //myss_t *m = (myss_t *)userobj; - - return 0; -} - -static int -myss_state(void *userobj, void *sh, lws_ss_constate_t state, - lws_ss_tx_ordinal_t ack) -{ - myss_t *m = (myss_t *)userobj; - - lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - lws_ss_set_metadata(m->ss, "uptag", "myuptag123", 10); - lws_ss_set_metadata(m->ss, "ctype", "myctype", 7); - lws_ss_client_connect(m->ss); - break; - case LWSSSCS_ALL_RETRIES_FAILED: - /* if we're out of retries, we want to close the app and FAIL */ - interrupted = 1; - break; - case LWSSSCS_QOS_ACK_REMOTE: - lwsl_notice("%s: LWSSSCS_QOS_ACK_REMOTE\n", __func__); - break; - default: - break; - } - - return 0; -} - -static int -app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = lws_system_context_from_system_mgr(mgr); - lws_system_blob_t *ab = lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */); - size_t size; - - /* - * For the things we care about, let's notice if we are trying to get - * past them when we haven't solved them yet, and make the system - * state wait while we trigger the dependent action. - */ - switch (target) { - case LWS_SYSTATE_REGISTERED: - size = lws_system_blob_get_size(ab); - if (size) - break; - - /* let's register our canned root token so auth can use it */ - lws_system_blob_direct_set(ab, - (const uint8_t *)canned_root_token_payload, - strlen(canned_root_token_payload)); - break; - - case LWS_SYSTATE_OPERATIONAL: - if (current == LWS_SYSTATE_OPERATIONAL) { - lws_ss_info_t ssi; - - /* We're making an outgoing secure stream ourselves */ - - memset(&ssi, 0, sizeof(ssi)); - ssi.handle_offset = offsetof(myss_t, ss); - ssi.opaque_user_data_offset = offsetof(myss_t, - opaque_data); - ssi.rx = myss_rx; - ssi.tx = myss_tx; - ssi.state = myss_state; - ssi.user_alloc = sizeof(myss_t); - ssi.streamtype = "mintest"; - - if (lws_ss_create(context, 0, &ssi, NULL, NULL, - NULL, NULL)) { - lwsl_err("%s: failed to create secure stream\n", - __func__); - return -1; - } - } - break; - } - - return 0; -} - -static lws_state_notify_link_t * const app_notifier_list[] = { - &nl, NULL -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - int n = 0; - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); - lws_cmdline_option_handle_builtin(argc, argv, &info); - - lwsl_user("LWS secure streams test client [-d]\n"); - - info.fd_limit_per_thread = 1 + 6 + 1; - info.port = CONTEXT_PORT_NO_LISTEN; -#if defined(LWS_SS_USE_SSPC) - info.protocols = lws_sspc_protocols; - { - const char *p; - - /* connect to ssproxy via UDS by default, else via - * tcp connection to this port */ - if ((p = lws_cmdline_option(argc, argv, "-p"))) - info.ss_proxy_port = atoi(p); - - /* UDS "proxy.ss.lws" in abstract namespace, else this socket - * path; when -p given this can specify the network interface - * to bind to */ - if ((p = lws_cmdline_option(argc, argv, "-i"))) - info.ss_proxy_bind = p; - - /* if -p given, -a specifies the proxy address to connect to */ - if ((p = lws_cmdline_option(argc, argv, "-a"))) - info.ss_proxy_address = p; - } -#else - info.pss_policies_json = default_ss_policy; - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; -#endif -#if defined(LWS_WITH_DETAILED_LATENCY) - info.detailed_latency_cb = lws_det_lat_plot_cb; - info.detailed_latency_filepath = "/tmp/lws-latency-ssproxy"; -#endif - - /* integrate us with lws system state management when context created */ - - nl.name = "app"; - nl.notify_cb = app_system_state_nf; - info.register_notifier_list = app_notifier_list; - - /* create the context */ - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* - * Set the related lws_system blobs - * - * ...direct_set() sets a pointer, so the thing pointed to has to have - * a suitable lifetime, eg, something that already exists on the heap or - * a const string in .rodata like this - */ - - lws_system_blob_direct_set(lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_DEVICE_SERIAL, 0), - (const uint8_t *)"SN12345678", 10); - lws_system_blob_direct_set(lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION, 0), - (const uint8_t *)"v0.01", 5); - - /* - * ..._heap_append() appends to a buflist kind of arrangement on heap, - * just one block is fine, otherwise it will concatenate the fragments - * in the order they were appended (and take care of freeing them at - * context destroy time). ..._heap_empty() is also available to remove - * everything that was already allocated. - * - * Here we use _heap_append() just so it's tested as well as direct set. - */ - - lws_system_blob_heap_append(lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_DEVICE_TYPE, 0), - (const uint8_t *)"spacerocket", 11); - - /* the event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams/README.md libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams/README.md --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -# lws minimal secure streams - -The application goes to https://warmcat.com and reads index.html there. - -It does it using Secure Streams... the main code in minimal-secure-streams.c -just sets up the context and opens a secure stream of type "mintest". - -The handler for state changes and payloads for "mintest" is in ss-myss.c - -The information about how a "mintest" stream should connect and the -protocol it uses is kept separated in policy-database.c - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --f| Force connecting to the wrong endpoint to check backoff retry flow --p| Run as proxy server for clients to connect to over unix domain socket - -``` -[2019/08/12 07:16:11:0045] USR: LWS minimal secure streams [-d] [-f] -[2019/08/12 07:16:12:6102] USR: myss_state: LWSSSCS_CREATING, ord 0x0 -[2019/08/12 07:16:12:6107] USR: myss_state: LWSSSCS_POLL, ord 0x0 -[2019/08/12 07:16:12:6117] N: lws_ss_client_connect: connecting h1get warmcat.com / -[2019/08/12 07:16:12:6118] USR: myss_state: LWSSSCS_CONNECTING, ord 0x0 -[2019/08/12 07:16:13:4171] USR: myss_state: LWSSSCS_CONNECTED, ord 0x0 -[2019/08/12 07:16:13:4222] USR: myss_rx: len 1024, flags: 1 -[2019/08/12 07:16:13:4243] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4245] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4246] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4247] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4252] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4264] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4265] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4266] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4267] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4269] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4270] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4278] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4279] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4280] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4281] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4282] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4284] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4287] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4288] USR: myss_rx: len 947, flags: 0 -[2019/08/12 07:16:13:4293] USR: myss_rx: len 0, flags: 2 -[2019/08/12 07:16:13:4399] USR: myss_state: LWSSSCS_DISCONNECTED, ord 0x0 -[2019/08/12 07:16:13:4761] USR: myss_state: LWSSSCS_DESTROYING, ord 0x0 -[2019/08/12 07:16:13:4781] USR: Completed: OK -``` diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/alexa.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/alexa.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/alexa.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/alexa.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,678 +0,0 @@ -/* - * lws-minimal-secure-streams-alexa - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include "private.h" - -struct lws_ss_handle *hss_avs_event, *hss_avs_sync; - -/* this is the type for the long poll event channel */ - -typedef struct ss_avs_event { - struct lws_ss_handle *ss; - void *opaque_data; - /* ... application specific state ... */ - - struct lejp_ctx jctx; -} ss_avs_event_t; - -enum { - LAMP3STATE_IDLE, - LAMP3STATE_SPOOLING, - LAMP3STATE_DRAINING, -}; - -/* this is the type for the utterance metadata (and audio rideshares) */ - -typedef struct ss_avs_metadata { - struct lws_ss_handle *ss; - void *opaque_data; - /* ... application specific state ... */ - - struct lws_buflist *dribble; /* next mp3 data while draining last */ - - struct lejp_ctx jctx; - size_t pos; - size_t mp3_in; - mpg123_handle *mh; - - lws_sorted_usec_list_t sul; - - uint8_t stash_eom[16]; - - uint8_t se_head; - uint8_t se_tail; - - char mp3_state; - char first_mp3; - uint8_t mp3_mime_match; - uint8_t seen; - uint8_t inside_mp3; - -} ss_avs_metadata_t; - -/* - * The remote server only seems to give us a budget of 10s to consume the - * results, after that it doesn't drop the stream, but doesn't send us anything - * further on it. - * - * This makes it impossible to optimize buffering for incoming mp3 since we - * have to go ahead and take it before the 10s is up. - */ - -#define MAX_MP3_IN_BUFFERING_BYTES 32768 - -/* - * Structure of JSON metadata for utterance handling - */ - -static const char *metadata = "{" - "\"event\": {" - "\"header\": {" - "\"namespace\": \"SpeechRecognizer\"," - "\"name\": \"Recognize\"," - "\"messageId\": \"message-123\"," - "\"dialogRequestId\": \"dialog-request-321\"" - "}," - "\"payload\": {" - "\"profile\":" "\"CLOSE_TALK\"," - "\"format\":" "\"AUDIO_L16_RATE_16000_CHANNELS_1\"" - "}" - "}" -"}"; - -/* - * avs metadata - */ - -static void -use_buffer_250ms(lws_sorted_usec_list_t *sul) -{ - ss_avs_metadata_t *m = lws_container_of(sul, ss_avs_metadata_t, sul); - struct lws_context *context = (struct lws_context *)m->opaque_data; - int est = lws_ss_get_est_peer_tx_credit(m->ss); - - lwsl_notice("%s: est txcr %d\n", __func__, est); - - if (est < MAX_MP3_IN_BUFFERING_BYTES - (MAX_MP3_IN_BUFFERING_BYTES / 4)) { - lwsl_notice(" adding %d\n", MAX_MP3_IN_BUFFERING_BYTES / 4); - lws_ss_add_peer_tx_credit(m->ss, MAX_MP3_IN_BUFFERING_BYTES / 4); - } - - lws_sul_schedule(context, 0, &m->sul, use_buffer_250ms, - 250 * LWS_US_PER_MS); -} - -static const char *mp3_mimetype = "application/octet-stream", - *match2 = "\x0d\x0a\x0d\x0a"; - -static int -ss_avs_mp3_open(ss_avs_metadata_t *m) -{ - int r; - - lwsl_notice("%s\n", __func__); - - m->first_mp3 = 1; - m->mh = mpg123_new(NULL, NULL); - if (!m->mh) { - lwsl_err("%s: unable to make new mp3\n", - __func__); - goto bail; - } - mpg123_format_none(m->mh); - r = mpg123_format(m->mh, 16000, MPG123_M_MONO, - MPG123_ENC_SIGNED_16); - if (r) { - lwsl_err("%s: mpg123 format failed %d\n", - __func__, r); - goto bail1; - } - r = mpg123_open_feed(m->mh); - if (r) { - lwsl_err("%s: mpg123 open feed failed %d\n", - __func__, r); - goto bail1; - } - - return 0; - -bail1: - mpg123_delete(m->mh); - m->mh = NULL; - -bail: - return 1; -} - -static int -ss_avs_metadata_rx(void *userobj, const uint8_t *buf, size_t len, int flags); - -/* - * This is called when the mp3 has drained it's input buffer and destroyed - * itself. - */ - -static int -drain_end_cb(void *v) -{ - ss_avs_metadata_t *m = (ss_avs_metadata_t *)v; - struct lws_context *context = (struct lws_context *)m->opaque_data; - int tot = 0; - - lwsl_err("%s\n", __func__); - - /* - * We have drained and destroyed the existing mp3 session. Is there - * a new one pending? - */ - - m->first_mp3 = 1; - m->mp3_state = LAMP3STATE_IDLE; - - if (lws_buflist_total_len(&m->dribble)) { - /* we started another one */ - - /* resume tx credit top up */ - lws_sul_schedule(context, 0, &m->sul, use_buffer_250ms, 1); - - if (ss_avs_mp3_open(m)) - return 1; - - m->mp3_state = LAMP3STATE_SPOOLING; - - /* - * Dump what we stashed from draining into the new mp3 - */ - - while (lws_buflist_total_len(&m->dribble)) { - size_t s; - uint8_t *u, t; - - s = lws_buflist_next_segment_len(&m->dribble, &u); - t = m->stash_eom[m->se_tail]; - lwsl_notice("%s: preload %d: %d\n", __func__, (int)s, t); - - mpg123_feed(m->mh, u, s); - lws_buflist_use_segment(&m->dribble, s); - if (m->first_mp3) { - play_mp3(m->mh, NULL, NULL); - m->first_mp3 = 0; - } - - tot += s; - - m->se_tail = (m->se_tail + 1) % sizeof(m->stash_eom); - if (t) { - lwsl_notice("%s: preloaded EOM\n", __func__); - - /* - * We stashed the whole of the message, we need - * to also do the EOM processing. We will come - * back here if there's another message in the - * stash. - */ - - m->mp3_state = LAMP3STATE_DRAINING; - if (m->mh) - play_mp3(NULL, drain_end_cb, m); - - lws_ss_add_peer_tx_credit(m->ss, tot); -#if 0 - /* - * Put a hold on bringing in any more data - */ - lws_sul_schedule(context, 0, &m->sul, NULL, - LWS_SET_TIMER_USEC_CANCEL); -#endif - /* destroy our copy of the handle */ - m->mh = NULL; - - break; - } - } - - lws_ss_add_peer_tx_credit(m->ss, tot); - } - - return 0; -} - -static int -ss_avs_metadata_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ - ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj; - struct lws_context *context = (struct lws_context *)m->opaque_data; - int n = 0, hit = 0; - - lwsl_notice("%s: len %d, flags %d (est peer txcr %d)\n", __func__, - (int)len, flags, lws_ss_get_est_peer_tx_credit(m->ss)); - - // lwsl_hexdump_warn(buf, len); - - if ((flags & LWSSS_FLAG_SOM) && !m->mh && !m->seen) { - m->mp3_mime_match = 0; - m->seen = 0; - m->inside_mp3 = 0; - } - - if (!m->inside_mp3) { - /* - * Identify the part with the mp3 in, if any - */ - - while (n < (int)len - 24) { - if (!m->seen) { - if (buf[n] == mp3_mimetype[m->mp3_mime_match]) { - m->mp3_mime_match++; - if (m->mp3_mime_match == 24) { - m->mp3_mime_match = 0; - m->seen = 1; - n++; - continue; - } - } else - m->mp3_mime_match = 0; - } else { - if (buf[n] == match2[m->mp3_mime_match]) { - m->mp3_mime_match++; - if (m->mp3_mime_match == 4) { - m->seen = 0; - m->mp3_mime_match = 0; - hit = 1; - n++; - buf += n; - len -= n; - lwsl_notice("identified reply...\n"); - m->inside_mp3 = 1; - break; - } - } else - m->mp3_mime_match = 0; - } - - n++; - } - - if (!hit) { - lws_ss_add_peer_tx_credit(m->ss, len); - return 0; - } - } - - // lwsl_notice("%s: state %d\n", __func__, m->mp3_state); - - switch (m->mp3_state) { - case LAMP3STATE_IDLE: - - if (hit) { - - lws_ss_add_peer_tx_credit(m->ss, n); - - if (ss_avs_mp3_open(m)) - goto bail; - - lws_sul_schedule(context, 0, &m->sul, use_buffer_250ms, 1); - m->mp3_state = LAMP3STATE_SPOOLING; - break; - } - - lws_ss_add_peer_tx_credit(m->ss, len); - - if (!m->inside_mp3) - break; - - /* fallthru */ - - case LAMP3STATE_SPOOLING: - - if (m->dribble) - goto draining; - - if (len) { - /* - * We are shoving encoded mp3 into mpg123-allocated heap - * buffers... unfortunately mpg123 doesn't seem to - * expose where it is in its allocated input so we can - * track how much is stashed. Instead while in playback - * mode, we assume 64kbps mp3 encoding, ie, 8KB/s, and - * run a sul that allows an additional 2KB tx credit - * every 250ms, with 4KB initial credit. - */ - lwsl_notice("%s: SPOOL %d\n", __func__, (int)len); - mpg123_feed(m->mh, buf, len); - - if (m->first_mp3) { - lws_sul_schedule(context, 0, &m->sul, - use_buffer_250ms, 1); - // lws_ss_add_peer_tx_credit(m->ss, - // len + (MAX_MP3_IN_BUFFERING_BYTES / 2)); - play_mp3(m->mh, NULL, NULL); - } //else - // lws_ss_add_peer_tx_credit(m->ss, len); - m->first_mp3 = 0; - } - - if (flags & LWSSS_FLAG_EOM) { - /* - * This means one "message" / mime part with mp3 data - * has finished coming in. But there may be whole other - * parts with other mp3s following, with potentially - * different mp3 parameters. So we want to tell this - * one to drain and finish and destroy the current mp3 - * object before we go on. - * - * But not knowing the length of the current one, there - * will already be outstanding tx credit at the server, - * so it's going to spam us with the next part before we - * have the new mp3 sink for it. - */ - lwsl_notice("%s: EOM\n", __func__); - m->mp3_mime_match = 0; - m->seen = 0; - m->mp3_state = LAMP3STATE_DRAINING; - /* from input POV, we're no longer inside an mp3 */ - m->inside_mp3 = 0; - if (m->mh) - play_mp3(NULL, drain_end_cb, m); -#if 0 - /* - * Put a hold on bringing in any more data - */ - lws_sul_schedule(context, 0, &m->sul, NULL, - LWS_SET_TIMER_USEC_CANCEL); -#endif - /* destroy our copy of the handle */ - m->mh = NULL; - } - break; - - case LAMP3STATE_DRAINING: - -draining: - if (buf && len && m->inside_mp3) { - lwsl_notice("%s: DRAINING: stashing %d: %d %d %d\n", - __func__, (int)len, !!(flags & LWSSS_FLAG_EOM), - m->se_head, m->se_tail); - lwsl_hexdump_notice(buf, len); - if (lws_buflist_append_segment(&m->dribble, buf, len) < 0) - goto bail; - - m->stash_eom[m->se_head] = !!(flags & LWSSS_FLAG_EOM); - m->se_head = (m->se_head + 1) % sizeof(m->stash_eom); - lwsl_notice("%s: next head %d\n", __func__, m->se_head); - - lws_ss_add_peer_tx_credit(m->ss, len); - } - - if (flags & LWSSS_FLAG_EOM) { - if (!len && m->se_head != m->se_tail) { - /* 0-len EOM... retrospectively mark last stash */ - lwsl_notice("%s: retro EOM\n", __func__); - m->stash_eom[(m->se_head - 1) % sizeof(m->stash_eom)] = 1; - } - - lwsl_notice("%s: Draining EOM\n", __func__); - m->inside_mp3 = 0; - } - /* - * Don't provide any additional tx credit... we're just - * mopping up the overspill from the previous mp3 credit - */ - break; - } - - return 0; - -bail: - return -1; -} - -/* - * Because this is multipart mime in h2 currently, use a "rideshare" to handle - * first the native metadata on this secure stream, then the "rideshare" audio - * stream mentioned in the policy. - * - * Lws takes care of interleaving the multipart mime pieces since the policy - * calls for it. - */ - -static int -ss_avs_metadata_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, - size_t *len, int *flags) -{ - ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj; - size_t tot; - int n; - - // lwsl_notice("%s %d\n", __func__, (int)m->pos); - - if ((long)m->pos < 0) { - *len = 0; - lwsl_info("%s: skip\n", __func__); - return 1; - } - - if (!strcmp(lws_ss_rideshare(m->ss), "avs_audio")) { - - /* audio rideshare part */ - - if (!m->pos) - *flags |= LWSSS_FLAG_SOM; - - n = spool_capture(buf, *len); - if (n > 0) - *len = n; - else - *len = 0; - if (!n) { - lwsl_info("%s: trying to skip tx\n", __func__); - return 1; - } - - m->pos += *len; - - if (n < 0) { - *flags |= LWSSS_FLAG_EOM; - m->pos = (long)-1l; /* ban subsequent until new stream */ - } - - lwsl_notice("%s: tx audio %d\n", __func__, (int)*len); - -#if 0 - { - int ff = open("/tmp/z1", O_RDWR | O_CREAT | O_APPEND, 0666); - if (ff == -1) - lwsl_err("%s: errno %d\n", __func__, errno); - write(ff, buf, *len); - close(ff); - } -#endif - - return 0; - } - - /* metadata part */ - - tot = strlen(metadata); - - if (!m->pos) - *flags |= LWSSS_FLAG_SOM; - - if (*len > tot - m->pos) - *len = tot - m->pos; - - memcpy(buf, metadata + m->pos, *len); - - m->pos += *len; - - if (m->pos == tot) { - lwsl_notice("metadata done\n"); - *flags |= LWSSS_FLAG_EOM; - m->pos = 0; /* for next time */ - } - - return 0; -} - -static int -ss_avs_metadata_state(void *userobj, void *sh, - lws_ss_constate_t state, lws_ss_tx_ordinal_t ack) -{ - ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj; - struct lws_context *context = (struct lws_context *)m->opaque_data; - - lwsl_notice("%s: %p: %s, ord 0x%x\n", __func__, m->ss, - lws_ss_state_name(state), (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - lws_ss_client_connect(m->ss); - break; - case LWSSSCS_CONNECTING: - m->pos = 0; - break; - case LWSSSCS_CONNECTED: - lwsl_info("%s: CONNECTED\n", __func__); - lws_ss_request_tx(m->ss); - break; - case LWSSSCS_DISCONNECTED: - lws_sul_schedule(context, 0, &m->sul, NULL, - LWS_SET_TIMER_USEC_CANCEL); - //if (m->mh) { - play_mp3(NULL, NULL, NULL); - m->mh = NULL; - //} - /* - * For this stream encapsulating an alexa exchange, dropping - * is the end of its life - */ - return 1; - - case LWSSSCS_DESTROYING: - lws_buflist_destroy_all_segments(&m->dribble); - break; - default: - break; - } - - return 0; -} - -/* - * avs event - */ - -static int -ss_avs_event_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ - return 0; -} - -static int -ss_avs_event_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, - size_t *len, int *flags) -{ - return 1; /* don't transmit anything */ -} - -static int -ss_avs_event_state(void *userobj, void *sh, - lws_ss_constate_t state, lws_ss_tx_ordinal_t ack) -{ - lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - mpg123_init(); - break; - case LWSSSCS_CONNECTING: - break; - case LWSSSCS_CONNECTED: - lwsl_user("Connected to Alexa... speak \"Alexa, ...\"\n"); - break; - case LWSSSCS_DISCONNECTED: - lwsl_user("Disconnected from Alexa\n"); - break; - case LWSSSCS_DESTROYING: - mpg123_exit(); - break; - default: - break; - } - - return 0; -} - -int -avs_query_start(struct lws_context *context) -{ - lws_ss_info_t ssi; - - lwsl_notice("%s:\n", __func__); - - memset(&ssi, 0, sizeof(ssi)); - ssi.handle_offset = offsetof(ss_avs_metadata_t, ss); - ssi.opaque_user_data_offset = offsetof(ss_avs_metadata_t, opaque_data); - ssi.rx = ss_avs_metadata_rx; - ssi.tx = ss_avs_metadata_tx; - ssi.state = ss_avs_metadata_state; - ssi.user_alloc = sizeof(ss_avs_metadata_t); - ssi.streamtype = "avs_metadata"; - - ssi.manual_initial_tx_credit = 8192; - - if (lws_ss_create(context, 0, &ssi, context, &hss_avs_sync, NULL, NULL)) { - lwsl_err("%s: failed to create avs metadata secstream\n", - __func__); - - return 1; - } - - lwsl_user("%s: created query stream %p\n", __func__, hss_avs_sync); - - return 0; -} - -int -avs_example_start(struct lws_context *context) -{ - lws_ss_info_t ssi; - - if (hss_avs_event) - return 0; - - lwsl_info("%s: Starting AVS stream\n", __func__); - - /* AVS wants us to establish the long poll event stream first */ - - memset(&ssi, 0, sizeof(ssi)); - ssi.handle_offset = offsetof(ss_avs_event_t, ss); - ssi.opaque_user_data_offset = offsetof(ss_avs_event_t, opaque_data); - ssi.rx = ss_avs_event_rx; - ssi.tx = ss_avs_event_tx; - ssi.state = ss_avs_event_state; - ssi.user_alloc = sizeof(ss_avs_event_t); - ssi.streamtype = "avs_event"; - - if (lws_ss_create(context, 0, &ssi, context, &hss_avs_event, NULL, NULL)) { - lwsl_err("%s: failed to create avs event secure stream\n", - __func__); - return 1; - } - - return 0; -} Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/alexa_linux.ppn and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/alexa_linux.ppn differ diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/audio.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/audio.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/audio.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/audio.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,469 +0,0 @@ -/* - * alsa audio handling - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "private.h" - -extern struct lws_ss_handle *hss_avs_event, *hss_avs_sync; - -int -avs_query_start(struct lws_context *context); - -enum { - MODE_IDLE, - MODE_CAPTURING, - MODE_PLAYING -}; - -struct raw_vhd { - int16_t p[8 * 1024]; /* 500ms at 16kHz 16-bit PCM */ - pv_porcupine_object_t *porc; - snd_pcm_t *pcm_capture; - snd_pcm_t *pcm_playback; - snd_pcm_hw_params_t *params; - snd_pcm_uframes_t frames; - int16_t *porcbuf; - - mpg123_handle *mh; - - mp3_done_cb done_cb; - void *opaque; - - int mode; - int rate; - - int porc_spf; - int filefd; - int rpos; - int wpos; - int porcpos; - int npos; - int times; - int quietcount; - int anycount; - - int wplay; - int rplay; - - char last_wake_detect; - char destroy_mh_on_drain; -}; - -static struct raw_vhd *avhd; - -/* - * called from alexa.c to grab the next chunk of audio capture buffer - * for upload - */ - -int -spool_capture(uint8_t *buf, size_t len) -{ - int16_t *sam = (int16_t *)buf; - size_t s, os; - - if (avhd->mode != MODE_CAPTURING) - return -1; - - os = s = len / 2; - - while (s && avhd->wpos != avhd->npos) { - *sam++ = avhd->p[avhd->npos]; - avhd->npos = (avhd->npos + 1) % LWS_ARRAY_SIZE(avhd->p); - s--; - } - - lwsl_info("Copied %d samples (%d %d)\n", (int)(os - s), - avhd->wpos, avhd->npos); - - return (os - s) * 2; -} - -/* - * Called from alexa.c to control when the mp3 playback should begin and end - */ - -int -play_mp3(mpg123_handle *mh, mp3_done_cb cb, void *opaque) -{ - if (mh) { - avhd->mh = mh; - avhd->mode = MODE_PLAYING; - snd_pcm_prepare(avhd->pcm_playback); - - return 0; - } - - avhd->destroy_mh_on_drain = 1; - avhd->done_cb = cb; - avhd->opaque = opaque; - - return 0; -} - -/* - * Helper used to set alsa hwparams on both capture and playback channels - */ - -static int -set_hw_params(struct lws_vhost *vh, snd_pcm_t **pcm, int type) -{ - unsigned int rate = pv_sample_rate(); /* it's 16kHz */ - snd_pcm_hw_params_t *params; - lws_sock_file_fd_type u; - struct pollfd pfd; - struct lws *wsi1; - int n; - - n = snd_pcm_open(pcm, "default", type, SND_PCM_NONBLOCK); - if (n < 0) { - lwsl_err("%s: Can't open default for playback: %s\n", - __func__, snd_strerror(n)); - - return -1; - } - - if (snd_pcm_poll_descriptors(*pcm, &pfd, 1) != 1) { - lwsl_err("%s: failed to get playback desc\n", __func__); - return -1; - } - - u.filefd = (lws_filefd_type)(long long)pfd.fd; - wsi1 = lws_adopt_descriptor_vhost(vh, LWS_ADOPT_RAW_FILE_DESC, u, - "lws-audio-test", NULL); - if (!wsi1) { - lwsl_err("%s: Failed to adopt playback desc\n", __func__); - goto bail; - } - if (type == SND_PCM_STREAM_PLAYBACK) - lws_rx_flow_control(wsi1, 0); /* no POLLIN */ - - snd_pcm_hw_params_malloc(¶ms); - snd_pcm_hw_params_any(*pcm, params); - - n = snd_pcm_hw_params_set_access(*pcm, params, - SND_PCM_ACCESS_RW_INTERLEAVED); - if (n < 0) - goto bail1; - - n = snd_pcm_hw_params_set_format(*pcm, params, SND_PCM_FORMAT_S16_LE); - if (n < 0) - goto bail1; - - n = snd_pcm_hw_params_set_channels(*pcm, params, 1); - if (n < 0) - goto bail1; - - n = snd_pcm_hw_params_set_rate_near(*pcm, params, &rate, 0); - if (n < 0) - goto bail1; - - lwsl_notice("%s: %s rate %d\n", __func__, - type == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture", rate); - - n = snd_pcm_hw_params(*pcm, params); - snd_pcm_hw_params_free(params); - if (n < 0) - goto bail; - - return 0; - -bail1: - snd_pcm_hw_params_free(params); -bail: - lwsl_err("%s: Set hw params failed: %s\n", __func__, snd_strerror(n)); - - return -1; -} - -/* - * The lws RAW file protocol handler that wraps ALSA. - * - * The timing is coming from ALSA capture channel... since they are both set to - * 16kHz, it's enough just to have the one. - */ - -static int -callback_audio(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - struct raw_vhd *vhd = (struct raw_vhd *)lws_protocol_vh_priv_get( - lws_get_vhost(wsi), lws_get_protocol(wsi)); - uint16_t rands[50]; - int16_t temp[256]; - bool det; - long avg; - int n, s; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - - if (avhd) /* just on one vhost */ - return 0; - - avhd = vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), sizeof(struct raw_vhd)); - - /* - * Set up the wakeword library - */ - - n = pv_porcupine_init("porcupine_params.pv", "alexa_linux.ppn", - 1.0, &vhd->porc); - if (n) { - lwsl_err("%s: porcupine init fail %d\n", __func__, n); - - return -1; - } - vhd->porc_spf = pv_porcupine_frame_length(); - vhd->porcbuf = malloc(vhd->porc_spf * 2); - lwsl_info("%s: %s porc frame length is %d samples\n", __func__, - lws_get_vhost_name(lws_get_vhost(wsi)), - vhd->porc_spf); - - vhd->rate = pv_sample_rate(); /* 16kHz */ - - /* set up alsa */ - - if (set_hw_params(lws_get_vhost(wsi), &vhd->pcm_playback, - SND_PCM_STREAM_PLAYBACK)) { - lwsl_err("%s: Can't open default for playback\n", - __func__); - - return -1; - } - - if (set_hw_params(lws_get_vhost(wsi), &vhd->pcm_capture, - SND_PCM_STREAM_CAPTURE)) { - lwsl_err("%s: Can't open default for capture\n", - __func__); - - return -1; - } - - snd_config_update_free_global(); - - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - lwsl_info("%s: LWS_CALLBACK_PROTOCOL_DESTROY\n", __func__); - if (!vhd) - break; - - if (vhd->porcbuf) { - free(vhd->porcbuf); - vhd->porcbuf = NULL; - } - if (vhd->pcm_playback) { - snd_pcm_drop(vhd->pcm_playback); - snd_pcm_close(vhd->pcm_playback); - vhd->pcm_playback = NULL; - } - if (vhd->pcm_capture) { - snd_pcm_drop(vhd->pcm_capture); - snd_pcm_close(vhd->pcm_capture); - vhd->pcm_capture = NULL; - } - if (vhd->porc) { - pv_porcupine_delete(vhd->porc); - vhd->porc = NULL; - } - - /* avoid most of the valgrind mess from alsa */ - snd_config_update_free_global(); - - break; - - case LWS_CALLBACK_RAW_CLOSE_FILE: - lwsl_info("%s: closed\n", __func__); - break; - - case LWS_CALLBACK_RAW_RX_FILE: - /* we come here about every 250ms */ - - /* - * Playing back the mp3? - */ - if (vhd->mode == MODE_PLAYING && vhd->mh) { - size_t amt, try; - - do { - try = snd_pcm_avail(vhd->pcm_playback); - if (try > LWS_ARRAY_SIZE(vhd->p)) - try = LWS_ARRAY_SIZE(vhd->p); - - n = mpg123_read(vhd->mh, (uint8_t *)vhd->p, - try * 2, &amt); - lwsl_info("%s: PLAYING: mpg123 read %d, n %d\n", - __func__, (int)amt, n); - if (n == MPG123_NEW_FORMAT) { - snd_pcm_start(vhd->pcm_playback); - memset(vhd->p, 0, try); - snd_pcm_writei(vhd->pcm_playback, - vhd->p, try / 2); - snd_pcm_prepare(vhd->pcm_playback); - } - } while (n == MPG123_NEW_FORMAT); - - if (amt) { - n = snd_pcm_writei(vhd->pcm_playback, - vhd->p, amt / 2); - if (n < 0) - lwsl_notice("%s: snd_pcm_writei: %d %s\n", - __func__, n, snd_strerror(n)); - if (n == -EPIPE) { - lwsl_err("%s: did EPIPE prep\n", __func__); - snd_pcm_prepare(vhd->pcm_playback); - } - } else - if (vhd->destroy_mh_on_drain && - n != MPG123_NEW_FORMAT) { - snd_pcm_drain(vhd->pcm_playback); - vhd->destroy_mh_on_drain = 0; - lwsl_notice("%s: mp3 destroyed\n", - __func__); - mpg123_close(vhd->mh); - mpg123_delete(vhd->mh); - vhd->mh = NULL; - vhd->mode = MODE_IDLE; - - if (vhd->done_cb) - vhd->done_cb(vhd->opaque); - } - } - - /* - * Get the capture data - */ - - n = snd_pcm_readi(vhd->pcm_capture, temp, LWS_ARRAY_SIZE(temp)); - s = 0; - while (s < n) { - vhd->p[(vhd->wpos + s) % LWS_ARRAY_SIZE(vhd->p)] = temp[s]; - s++; - } - - if (vhd->mode == MODE_CAPTURING) { - - /* - * We are recording an utterance. - * - * Estimate the sound density in the frame by picking 50 - * samples at random and averaging the sampled - * [abs()^2] / 10000 to create a Figure of Merit. - * - * Speaking on my laptop gets us 1000 - 5000, silence - * is typ under 30. The wakeword tells us there was - * speech at the start, end the capture when there's - * ~750ms (12000 samples) under 125 FOM. - */ - -#define SILENCE_THRESH 125 - - avg = 0; - lws_get_random(lws_get_context(wsi), rands, sizeof(rands)); - for (s = 0; s < (int)LWS_ARRAY_SIZE(rands); s++) { - long q; - - q = temp[rands[s] % n]; - - avg += (q * q); - } - avg = (avg / (int)LWS_ARRAY_SIZE(rands)) / 10000; - - lwsl_notice("est audio energy: %ld %d\n", avg, vhd->mode); - - /* - * Only start looking for "silence" after 1.5s, in case - * he does a long pause after the wakeword - */ - - if (vhd->anycount < (3 *vhd->rate) / 2 && - avg < SILENCE_THRESH) { - vhd->quietcount += n; - /* then 500ms of "silence" does it for us */ - if (vhd->quietcount >= ((vhd->rate * 3) / 4)) { - lwsl_warn("%s: ended capture\n", __func__); - vhd->mode = MODE_IDLE; - vhd->quietcount = 0; - } - } - - /* if we're not "silent", reset the count */ - if (avg > SILENCE_THRESH * 2) - vhd->quietcount = 0; - - /* - * Since we are in capturing mode, we have something - * new to send now. - * - * We must send an extra one at the end so we can finish - * the tx. - */ - lws_ss_request_tx(hss_avs_sync); - } - - /* - * Just waiting for a wakeword - */ - - while (vhd->mode == MODE_IDLE) { - int m = 0, ppold = vhd->porcpos; - - s = (vhd->wpos - vhd->porcpos) % LWS_ARRAY_SIZE(vhd->p); - if (s < vhd->porc_spf) - goto eol; - - while (m < vhd->porc_spf) { - vhd->porcbuf[m++] = avhd->p[vhd->porcpos]; - vhd->porcpos = (vhd->porcpos + 1) % - LWS_ARRAY_SIZE(vhd->p); - } - - if (pv_porcupine_process(vhd->porc, vhd->porcbuf, &det)) - lwsl_err("%s: porc_process failed\n", __func__); - - if (!det && vhd->last_wake_detect && - vhd->mode == MODE_IDLE) { - lwsl_warn("************* Wakeword\n"); - if (!avs_query_start(lws_get_context(wsi))) { - vhd->mode = MODE_CAPTURING; - vhd->quietcount = 0; - vhd->last_wake_detect = det; - vhd->npos = ppold; - break; - } - } - vhd->last_wake_detect = det; - } - -eol: - vhd->wpos = (vhd->wpos + n) % LWS_ARRAY_SIZE(vhd->p); - break; - - default: - break; - } - - return 0; -} - -struct lws_protocols protocol_audio_test = - { "lws-audio-test", callback_audio, 0, 0 }; diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-secure-streams-alexa) -set(SRCS main.c alexa.c audio.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) -require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) -require_lws_config(LWS_WITH_ALSA 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared asound pv_porcupine mpg123) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets asound pv_porcupine mpg123) - endif() - - if (LWS_WITH_SECURE_STREAMS_PROXY_API) - add_compile_options(-DLWS_SS_USE_SSPC) - - add_executable(${SAMP}-client ${SRCS}) - if (websockets_shared) - target_link_libraries(${SAMP}-client websockets_shared asound pv_porcupine mpg123) - add_dependencies(${SAMP}-client websockets_shared) - else() - target_link_libraries(${SAMP}-client websockets asound pv_porcupine mpg123) - endif() - endif() - -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,417 +0,0 @@ -/* - * lws-minimal-secure-streams-alexa - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include - -extern int -avs_example_start(struct lws_context *context); - -static int interrupted; -static lws_state_notify_link_t nl; - -#if !defined(LWS_SS_USE_SSPC) - -/* - * If not using the proxy, we need to bring our own policy - */ - -static const char * const default_ss_policy = - "{" - "\"release\":" "\"01234567\"," - "\"product\":" "\"myproduct\"," - "\"schema-version\":" "1," - "\"retry\": [" /* named backoff / retry strategies */ - "{\"default\": {" - "\"backoff\": [" "1000," - "2000," - "3000," - "5000," - "10000" - "]," - "\"conceal\":" "5," - "\"jitterpc\":" "20," - "\"svalidping\":" "60," - "\"svalidhup\":" "64" - "}}" - "]," - "\"certs\": [" /* named individual certificates in BASE64 DER */ - "{\"digicert_global_root_g2\": \"" /* api.amazon.com 2038-01 */ - "MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh" - "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3" - "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH" - "MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT" - "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j" - "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG" - "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI" - "2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx" - "1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ" - "q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz" - "tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ" - "vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP" - "BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV" - "5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY" - "1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4" - "NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG" - "Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91" - "8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe" - "pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl" - "MrY=" - "\"}," - "{\"digicert_global_ca_g2\": \"" /* api.amazon.com 2028-08 */ - "MIIEizCCA3OgAwIBAgIQDI7gyQ1qiRWIBAYe4kH5rzANBgkqhkiG9w0BAQsFADBh" - "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3" - "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH" - "MjAeFw0xMzA4MDExMjAwMDBaFw0yODA4MDExMjAwMDBaMEQxCzAJBgNVBAYTAlVT" - "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxHjAcBgNVBAMTFURpZ2lDZXJ0IEdsb2Jh" - "bCBDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNIfL7zBYZd" - "W9UvhU5L4IatFaxhz1uvPmoKR/uadpFgC4przc/cV35gmAvkVNlW7SHMArZagV+X" - "au4CLyMnuG3UsOcGAngLH1ypmTb+u6wbBfpXzYEQQGfWMItYNdSWYb7QjHqXnxr5" - "IuYUL6nG6AEfq/gmD6yOTSwyOR2Bm40cZbIc22GoiS9g5+vCShjEbyrpEJIJ7RfR" - "ACvmfe8EiRROM6GyD5eHn7OgzS+8LOy4g2gxPR/VSpAQGQuBldYpdlH5NnbQtwl6" - "OErXb4y/E3w57bqukPyV93t4CTZedJMeJfD/1K2uaGvG/w/VNfFVbkhJ+Pi474j4" - "8V4Rd6rfArMCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0P" - "AQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29j" - "c3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmw0LmRp" - "Z2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwN6A1oDOGMWh0dHA6" - "Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwPQYD" - "VR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2lj" - "ZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFCRuKy3QapJRUSVpAaqaR6aJ50AgMB8GA1Ud" - "IwQYMBaAFE4iVCAYlebjbuYP+vq5Eu0GF485MA0GCSqGSIb3DQEBCwUAA4IBAQAL" - "OYSR+ZfrqoGvhOlaOJL84mxZvzbIRacxAxHhBsCsMsdaVSnaT0AC9aHesO3ewPj2" - "dZ12uYf+QYB6z13jAMZbAuabeGLJ3LhimnftiQjXS8X9Q9ViIyfEBFltcT8jW+rZ" - "8uckJ2/0lYDblizkVIvP6hnZf1WZUXoOLRg9eFhSvGNoVwvdRLNXSmDmyHBwW4co" - "atc7TlJFGa8kBpJIERqLrqwYElesA8u49L3KJg6nwd3jM+/AVTANlVlOnAM2BvjA" - "jxSZnE0qnsHhfTuvcqdFuhOWKU4Z0BqYBvQ3lBetoxi6PrABDJXWKTUgNX31EGDk" - "92hiHuwZ4STyhxGs6QiA" - "\"}," - "{\"amazon_root_ca_1\": \"" - "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF" - "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6" - "b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL" - "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv" - "b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj" - "ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM" - "9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw" - "IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6" - "VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L" - "93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm" - "jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC" - "AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA" - "A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI" - "U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs" - "N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv" - "o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU" - "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy" - "rqXRfboQnoZsG4q5WTP468SQvvG5" - "\"}," - "{\"starfield_services_root_ca\": \"" - "MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx" - "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT" - "HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs" - "ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5" - "MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD" - "VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy" - "ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy" - "dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI" - "hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p" - "OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2" - "8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K" - "Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe" - "hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk" - "6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw" - "DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q" - "AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI" - "bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB" - "ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z" - "qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd" - "iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn" - "0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN" - "sSi6" - "\"}," - "{\"starfield_class_2_ca\": \"" - "MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl" - "MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp" - "U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw" - "NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE" - "ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp" - "ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3" - "DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf" - "8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN" - "+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0" - "X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa" - "K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA" - "1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G" - "A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR" - "zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0" - "YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD" - "bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w" - "DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3" - "L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D" - "eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl" - "xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp" - "VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY" - "WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=" - "\"}" - "]," - "\"trust_stores\": [" /* named cert chains */ - "{" /* chain for alexa.na.gateway.devices.a2z.com */ - "\"name\": \"avs_via_starfield\"," - "\"stack\": [" - "\"starfield_class_2_ca\"," - "\"starfield_services_root_ca\"" - "]" - "}," - "{" /* chain for api.amazon.com */ - "\"name\": \"api_amazon_com\"," - "\"stack\": [" - "\"digicert_global_ca_g2\"," - "\"digicert_global_root_g2\"" - "]" - "}" - "]," - "\"s\": [" /* the supported stream types */ - "{\"api_amazon_com_auth\": {" - "\"endpoint\":" "\"api.amazon.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h1\"," - "\"http_method\":" "\"POST\"," - "\"http_url\":" "\"auth/o2/token\"," - "\"plugins\":" "[]," - "\"opportunistic\":" "true," - "\"tls\":" "true," - "\"h2q_oflow_txcr\":" "true," - "\"http_www_form_urlencoded\":" "true," - "\"http_no_content_length\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"api_amazon_com\"" - "}}," - /* - * long poll event listener - */ - "{\"avs_event\": {" - "\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h2\"," - "\"http_method\":" "\"GET\"," - "\"http_url\":" "\"v20160207/directives\"," - "\"h2q_oflow_txcr\":" "true," - "\"http_auth_header\":" "\"authorization:\"," - "\"http_auth_preamble\":" "\"Bearer \"," - "\"nailed_up\":" "true," - "\"long_poll\":" "true," - "\"retry\":" "\"default\"," - "\"plugins\":" "[]," - "\"tls\":" "true," - "\"tls_trust_store\":" "\"avs_via_starfield\"" - "}}," - /* - * Utterance metadata and audio send and reply processing. - * - * "Rideshare" and http_multipart_mime means these both go out - * in one multipart http transaction. - */ - "{\"avs_metadata\": {" - "\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h2\"," - "\"http_method\":" "\"POST\"," - "\"http_url\":" "\"v20160207/events\"," - "\"opportunistic\":" "true," - "\"h2q_oflow_txcr\":" "true," - "\"http_auth_header\":" "\"authorization:\"," - "\"http_auth_preamble\":" "\"Bearer \"," - "\"http_multipart_name\":" "\"metadata\"," - "\"http_mime_content_type\":" "\"application/json; charset=UTF-8\"," - "\"http_no_content_length\":" "true," - "\"rideshare\":" "\"avs_audio\"," - "\"retry\":" "\"default\"," - "\"plugins\":" "[]," - "\"tls\":" "true," - "\"tls_trust_store\":" "\"avs_via_starfield\"" - "}}," - "{\"avs_audio\": {" - "\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h2\"," - "\"http_method\":" "\"POST\"," - "\"http_url\":" "\"v20160207/events\"," - "\"plugins\":" "[]," - "\"tls\":" "true," - "\"h2q_oflow_txcr\":" "true," - "\"http_auth_header\":" "\"authorization:\"," - "\"http_auth_preamble\":" "\"Bearer \"," - "\"http_multipart_name\":" "\"audio\"," - "\"http_mime_content_type\":" "\"application/octet-stream\"," - "\"http_no_content_length\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"avs_via_starfield\"" - "}}" - "]" - "}" -; - -#endif - -static const char *canned_root_token_payload = - "grant_type=refresh_token" - "&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg" - "SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP" - "zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y" - "0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW" - "k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE" - "iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S" - "KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc" - "AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI" - "xL_hDCcTho8opCVX-6QhJHl6SQFlTw13" - "&client_id=" - "amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d"; - -/* - * Register the root token, and make the sticky AVS connection at the - * appropriate times during system startup - */ - -static int -app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = lws_system_context_from_system_mgr(mgr); - lws_system_blob_t *ab = lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */); - size_t size; - - /* - * For the things we care about, let's notice if we are trying to get - * past them when we haven't solved them yet, and make the system - * state wait while we trigger the dependent action. - */ - switch (target) { - case LWS_SYSTATE_REGISTERED: - size = lws_system_blob_get_size(ab); - if (size) - break; - - /* let's register our canned root token so auth can use it */ - lws_system_blob_direct_set(ab, - (const uint8_t *)canned_root_token_payload, - strlen(canned_root_token_payload)); - break; - case LWS_SYSTATE_OPERATIONAL: - if (current == target) - avs_example_start(context); - break; - case LWS_SYSTATE_POLICY_INVALID: - /* - * This is a NOP since we used direct set... but in a real - * system this could easily change to be done on the heap, then - * this would be important - */ - lws_system_blob_destroy(lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, - 1 /* AUTH_IDX_ROOT */)); - break; - } - - return 0; -} - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -static lws_state_notify_link_t * const app_notifier_list[] = { - &nl, NULL -}; - -extern struct lws_protocols protocol_audio_test; -static const struct lws_protocols *protocols[] = { - &protocol_audio_test, -#if defined(LWS_SS_USE_SSPC) - lws_sspc_protocols, -#endif - NULL -}; - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - int n = 0; - - signal(SIGINT, sigint_handler); - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - lws_cmdline_option_handle_builtin(argc, argv, &info); - - lwsl_user("LWS secure streams - Alexa voice test [-d]\n"); - - info.fd_limit_per_thread = 1 + 6 + 1; -#if !defined(LWS_SS_USE_SSPC) - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.pss_policies_json = default_ss_policy; -#else - { - const char *p; - - /* connect to ssproxy via UDS by default, else via - * tcp connection to this port */ - if ((p = lws_cmdline_option(argc, argv, "-p"))) - info.ss_proxy_port = atoi(p); - - /* UDS "proxy.ss.lws" in abstract namespace, else this socket - * path; when -p given this can specify the network interface - * to bind to */ - if ((p = lws_cmdline_option(argc, argv, "-i"))) - info.ss_proxy_bind = p; - - /* if -p given, -a specifies the proxy address to connect to */ - if ((p = lws_cmdline_option(argc, argv, "-a"))) - info.ss_proxy_address = p; - } -#endif - info.port = CONTEXT_PORT_NO_LISTEN; - info.pprotocols = protocols; - -#if defined(LWS_WITH_DETAILED_LATENCY) - info.detailed_latency_cb = lws_det_lat_plot_cb; - info.detailed_latency_filepath = "/tmp/lws-latency-ssproxy"; -#endif - - /* integrate us with lws system state management when context created */ - nl.name = "app"; - nl.notify_cb = app_system_state_nf; - info.register_notifier_list = app_notifier_list; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* create an explicit vhost so the sound protocol is initialized */ - - info.vhost_name = "asound"; - if (!lws_create_vhost(context, &info)) { - lwsl_err("lws init failed\n"); - goto bail; - } - - /* the event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail: - lws_context_destroy(context); - lwsl_user("Completed\n"); - - return 0; -} Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/porcupine_params.pv and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/porcupine_params.pv differ diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/private.h libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/private.h --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/private.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/private.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -typedef int (*mp3_done_cb)(void *opaque); - -int -play_mp3(mpg123_handle *mh, mp3_done_cb cb, void *opaque); - - -int -spool_capture(uint8_t *buf, size_t len); diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/README.md libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/README.md --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-alexa/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-alexa/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -# lws secure streams alexa - -This demonstrates AVS Alexa usage using secure streams. It connects to AVS, -uses your linux computer's microphone to wait for the 'alexa' wakeword, sends -the utterance to AVS and plays back the result. - -## build - -There are some special build considerations: - -1) Build lws with cmake options `-DLWS_WITH_ALSA=1 -DLWS_WITH_SECURE_STREAMS=1` - -2) Install distro build dependency packages: - - |Dependency|Ubuntu package|Fedora Package| - |---|---|---| - |libasound|libasound2-dev|alsa-lib-devel| - |mpg123|libmpg123-dev|mpg123-devel| - -3) Clone Picovoice Porcupine Apache-licensed demo version from here - - https://github.com/Picovoice/porcupine - - It provides binary libs for wakeword detection on various platforms. Copy - the headers and binary lib to your build context, eg, for native x86_64 - -``` - $ sudo cp ./include/* /usr/include - $ sudo cp ./lib/linux/x86_64/libpv_porcupine.* /usr/lib - $ sudo ldconfig -``` - - Enter the minimal example dir for secure-streams-alexa and make the sample - -``` - $ cd ./minimal-examples/secure-streams/minimal-secure-streams-alexa - $ cmake . - $ make -``` - -## usage - -``` - $ ./lws-minimal-secure-streams-alexa -[2019/10/16 16:22:01:1097] U: LWS secure streams - Alex voice test [-d] -[2019/10/16 16:22:01:1115] N: lws_create_context: creating Secure Streams policy -[2019/10/16 16:22:01:1115] N: lwsac_use: alloc 1532 for 1 -[2019/10/16 16:22:01:1119] N: lwsac_use: alloc 288 for 168 -[2019/10/16 16:22:01:1119] N: lws_ss_policy_set: policy lwsac size: 1.796KiB, pad 11% -[2019/10/16 16:22:02:4114] N: lws_ss_client_connect: connecting 0 api.amazon.com /auth/o2/token -[2019/10/16 16:22:02:8686] N: auth_api_amazon_com_parser_cb: expires in 3600 -[2019/10/16 16:22:02:8686] N: ss_api_amazon_auth_rx: acquired 656-byte api.amazon.com auth token -[2019/10/16 16:22:02:8754] N: lws_ss_client_connect: connecting 1 alexa.na.gateway.devices.a2z.com /v20160207/directives -[2019/10/16 16:22:02:3182] N: secstream_h2: h2 client entering LONG_POLL -[2019/10/16 16:22:02:3183] U: Connected to Alexa... speak "Alexa, ..." -[2019/10/16 16:22:06:9380] W: ************* Wakeword -[2019/10/16 16:22:06:9380] N: avs_query_start: -[2019/10/16 16:22:06:9381] N: lws_ss_client_connect: connecting 1 alexa.na.gateway.devices.a2z.com /v20160207/events -[2019/10/16 16:22:06:9381] N: lws_vhost_active_conns: just join h2 directly -[2019/10/16 16:22:06:9384] N: metadata done -[2019/10/16 16:22:06:1524] N: est: 42 1 -[2019/10/16 16:22:06:3723] N: est: 108 1 -[2019/10/16 16:22:07:5914] N: est: 352 1 -[2019/10/16 16:22:07:8112] N: est: 4284 1 -[2019/10/16 16:22:07:0300] N: est: 3369 1 -[2019/10/16 16:22:07:2325] N: est: 577 1 -[2019/10/16 16:22:08:4519] N: est: 9 1 -[2019/10/16 16:22:08:6716] N: est: 3 1 -[2019/10/16 16:22:08:6718] N: est: 11 1 -[2019/10/16 16:22:08:8915] N: est: 10 1 -[2019/10/16 16:22:08:8915] W: callback_audio: ended capture -[2019/10/16 16:22:09:0993] N: identified reply... -^C[2019/10/16 16:22:14:3067] U: Disconnected from Alexa -[2019/10/16 16:22:14:3123] U: Completed -$ - -``` diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-avs/avs.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-avs/avs.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-avs/avs.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-avs/avs.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,424 +0,0 @@ -/* - * lws-minimal-secure-streams-avs - * - * Written in 2019-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This sends a canned WAV and received (and discards) the mp3 response. - * However it rate-limits the response reception to manage a small ringbuffer - * using ss / h2 flow control apis, reflecting consumption at 64kbps and only - * and 8KB buffer, indtended to model optimizing rx buffering on mp3 playback - * on a constrained device. - */ - -#include -#include -#include -#include -#include -#include -#include - -extern int interrupted, bad; -static struct lws_ss_handle *hss_avs_event, *hss_avs_sync; -static uint8_t *wav; -static size_t wav_len; - -typedef struct ss_avs_event { - struct lws_ss_handle *ss; - void *opaque_data; - /* ... application specific state ... */ - struct lejp_ctx jctx; -} ss_avs_event_t; - -typedef struct ss_avs_metadata { - struct lws_ss_handle *ss; - void *opaque_data; - /* ... application specific state ... */ - struct lejp_ctx jctx; - size_t pos; - - /* - * We simulate a ringbuffer that is used up by a sul at 64Kbit/sec - * rate, and managed at the same rate using tx credit - */ - - lws_sorted_usec_list_t sul; - uint8_t buf[256 * 1024]; /* to test rate-limiting, set to 8 * 1024 */ - int head; - int tail; - - char filled; - -} ss_avs_metadata_t; - -static const char *metadata = "{" - "\"event\": {" - "\"header\": {" - "\"namespace\": \"SpeechRecognizer\"," - "\"name\": \"Recognize\"," - "\"messageId\": \"message-123\"," - "\"dialogRequestId\": \"dialog-request-321\"" - "}," - "\"payload\": {" - "\"profile\":" "\"CLOSE_TALK\"," - "\"format\":" "\"AUDIO_L16_RATE_16000_CHANNELS_1\"" - "}" - "}" -"}"; - -/* - * avs metadata - */ - -static void -use_buffer_50ms(lws_sorted_usec_list_t *sul) -{ - ss_avs_metadata_t *m = lws_container_of(sul, ss_avs_metadata_t, sul); - struct lws_context *context = (struct lws_context *)m->opaque_data; - size_t n; - int e; - - /* - * Use up 50ms-worth (8KB / 20) == 401 bytes of buffered data - */ - - /* remaining data in buffer */ - n = ((m->head - m->tail) % sizeof(m->buf)); - lwsl_info("%s: avail %d\n", __func__, (int)n); - - if (n < 401) - lwsl_err("%s: underrun\n", __func__); - - m->tail = (m->tail + 401) % sizeof(m->buf); - n = ((m->head - m->tail) % sizeof(m->buf)); - - e = lws_ss_get_est_peer_tx_credit(m->ss); - - lwsl_info("%s: avail after: %d, curr est %d\n", __func__, (int)n, e); - - if (n < (sizeof(m->buf) * 2) / 3 && e < (int)(sizeof(m->buf) - 1 - n)) { - lwsl_info("%s: requesting additional %d\n", __func__, - (int)(sizeof(m->buf) - 1 - e - n)); - lws_ss_add_peer_tx_credit(m->ss, (sizeof(m->buf) - 1 - e - n)); - } - - lws_sul_schedule(context, 0, &m->sul, use_buffer_50ms, - 50 * LWS_US_PER_MS); -} - -static int -ss_avs_metadata_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ - ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj; - struct lws_context *context = (struct lws_context *)m->opaque_data; - size_t n, n1; - - lwsl_notice("%s: rideshare %s, len %d, flags 0x%x\n", __func__, - lws_ss_rideshare(m->ss), (int)len, flags); - // lwsl_hexdump_warn(buf, len); - - n = sizeof(m->buf) - ((m->head - m->tail) % sizeof(m->buf)); - lwsl_info("%s: len %d, buf h %d, t %d, space %d\n", __func__, - (int)len, (int)m->head, (int)m->tail, (int)n); - lws_ss_get_est_peer_tx_credit(m->ss); - if (len > n) { - lwsl_err("%s: bad len: len %d, n %d\n", __func__, (int)len, (int)n); - assert(0); - - return 1; - } - - if (m->head < m->tail) /* |****h-------t**| */ - memcpy(&m->buf[m->head], buf, len); - else { /* |---t*****h-----| */ - n1 = sizeof(m->buf) - m->head; - if (len < n1) - n1 = len; - memcpy(&m->buf[m->head], buf, n1); - if (n1 != len) - memcpy(m->buf, buf, len - n1); - } - - m->head = (m->head + len) % sizeof(m->buf); - - lws_sul_schedule(context, 0, &m->sul, use_buffer_50ms, - 50 * LWS_US_PER_MS); - - return 0; -} - -static int -ss_avs_metadata_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, - size_t *len, int *flags) -{ - ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj; - //struct lws_context *context = (struct lws_context *)m->opaque_data; - size_t tot; - - if ((long)m->pos < 0) { - *len = 0; - lwsl_debug("%s: skip tx\n", __func__); - return 1; - } - -// lwsl_notice("%s: rideshare '%s'\n", __func__, lws_ss_rideshare(m->ss)); - - if (!strcmp(lws_ss_rideshare(m->ss), "avs_audio")) { - /* audio rideshare */ - - if (!m->pos) - *flags |= LWSSS_FLAG_SOM; - - if (*len > wav_len - m->pos) - *len = wav_len - m->pos; - - memcpy(buf, wav + m->pos, *len); - m->pos += *len; - - if (m->pos == wav_len) { - *flags |= LWSSS_FLAG_EOM; - lwsl_info("%s: tx done\n", __func__); - m->pos = (long)-1l; /* ban subsequent until new stream */ - } else - lws_ss_request_tx(m->ss); - - lwsl_hexdump_info(buf, *len); - - return 0; - } - - /* metadata part */ - - tot = strlen(metadata); - - if (!m->pos) - *flags |= LWSSS_FLAG_SOM; - - if (*len > tot - m->pos) - *len = tot - m->pos; - - memcpy(buf, metadata + m->pos, *len); - - m->pos += *len; - - if (m->pos == tot) { - *flags |= LWSSS_FLAG_EOM; - m->pos = 0; /* for next time */ - lws_ss_request_tx(m->ss); - } - - lwsl_hexdump_info(buf, *len); - - return 0; -} - -static int -ss_avs_metadata_state(void *userobj, void *sh, - lws_ss_constate_t state, lws_ss_tx_ordinal_t ack) -{ - - ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj; - struct lws_context *context = (struct lws_context *)m->opaque_data; - - lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - lwsl_user("%s: CREATING\n", __func__); - lws_ss_client_connect(m->ss); - m->pos = 0; - break; - case LWSSSCS_CONNECTING: - break; - case LWSSSCS_CONNECTED: - lws_ss_request_tx(m->ss); - break; - case LWSSSCS_ALL_RETRIES_FAILED: - /* for this demo app, we want to exit on fail to connect */ - case LWSSSCS_DISCONNECTED: - /* for this demo app, we want to exit after complete flow */ - lws_sul_schedule(context, 0, &m->sul, use_buffer_50ms, - LWS_SET_TIMER_USEC_CANCEL); - interrupted = 1; - break; - case LWSSSCS_DESTROYING: - lws_sul_schedule(context, 0, &m->sul, use_buffer_50ms, - LWS_SET_TIMER_USEC_CANCEL); - break; - default: - break; - } - - return 0; -} - -/* - * avs event - */ - -static int -ss_avs_event_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ - ss_avs_event_t *m = (ss_avs_event_t *)userobj; - // struct lws_context *context = (struct lws_context *)m->opaque_data; - - lwsl_notice("%s: rideshare %s, len %d, flags 0x%x\n", __func__, - lws_ss_rideshare(m->ss), (int)len, flags); - -// lwsl_hexdump_warn(buf, len); - - bad = 0; /* for this demo, receiving something here == success */ - - return 0; -} - -static int -ss_avs_event_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, - size_t *len, int *flags) -{ - ss_avs_event_t *m = (ss_avs_event_t *)userobj; - lwsl_notice("%s: rideshare %s\n", __func__, lws_ss_rideshare(m->ss)); - - return 1; /* don't transmit anything */ -} - -static int -ss_avs_event_state(void *userobj, void *sh, - lws_ss_constate_t state, lws_ss_tx_ordinal_t ack) -{ - ss_avs_event_t *m = (ss_avs_event_t *)userobj; - struct lws_context *context = (struct lws_context *)m->opaque_data; - lws_ss_info_t ssi; - - lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - case LWSSSCS_CONNECTING: - break; - case LWSSSCS_CONNECTED: - if (hss_avs_sync) - break; - - lwsl_notice("%s: starting the second avs stream\n", __func__); - - /* - * When we have established the event stream, we must POST - * on another stream within 10s - */ - - memset(&ssi, 0, sizeof(ssi)); - ssi.handle_offset = offsetof(ss_avs_metadata_t, ss); - ssi.opaque_user_data_offset = offsetof(ss_avs_metadata_t, - opaque_data); - ssi.rx = ss_avs_metadata_rx; - ssi.tx = ss_avs_metadata_tx; - ssi.state = ss_avs_metadata_state; - ssi.user_alloc = sizeof(ss_avs_metadata_t); - ssi.streamtype = "avs_metadata"; - - /* - * We want to allow the other side to fill our buffer, but no - * more. But it's a bit tricky when the payload is inside - * framing like multipart MIME and contains other parts - */ - - /* uncomment to test rate-limiting, doesn't work with AVS servers */ -// ssi.manual_initial_tx_credit = -// sizeof(((ss_avs_metadata_t *)0)->buf) / 2; - - if (lws_ss_create(context, 0, &ssi, context, &hss_avs_sync, - NULL, NULL)) { - lwsl_err("%s: failed to create avs metadata secstream\n", - __func__); - } - break; - case LWSSSCS_ALL_RETRIES_FAILED: - /* for this demo app, we want to exit on fail to connect */ - interrupted = 1; - break; - case LWSSSCS_DISCONNECTED: - break; - case LWSSSCS_DESTROYING: - lwsl_notice("%s: DESTROYING\n", __func__); - if (wav) { - free(wav); - wav = NULL; - } - break; - default: - break; - } - - return 0; -} - -int -avs_example_start(struct lws_context *context) -{ - lws_ss_info_t ssi; - struct stat stat; - int fd; - - if (hss_avs_event) - return 0; - - fd = open("./year.wav", O_RDONLY); - if (fd < 0) { - lwsl_err("%s: failed to open wav file\n", __func__); - - return 1; - } - if (fstat(fd, &stat) < 0) { - lwsl_err("%s: failed to stat wav file\n", __func__); - - goto bail; - } - - wav_len = stat.st_size; - wav = malloc(wav_len); - if (!wav) { - lwsl_err("%s: failed to alloc wav buffer", __func__); - - goto bail; - } - if (read(fd, wav, wav_len) != (int)wav_len) { - lwsl_err("%s: failed to read wav\n", __func__); - - goto bail; - } - close(fd); - - lwsl_user("%s: Starting AVS stream\n", __func__); - - /* AVS wants us to establish the long poll event stream first */ - - memset(&ssi, 0, sizeof(ssi)); - ssi.handle_offset = offsetof(ss_avs_event_t, ss); - ssi.opaque_user_data_offset = offsetof(ss_avs_event_t, opaque_data); - ssi.rx = ss_avs_event_rx; - ssi.tx = ss_avs_event_tx; - ssi.state = ss_avs_event_state; - ssi.user_alloc = sizeof(ss_avs_event_t); - ssi.streamtype = "avs_event"; - - if (lws_ss_create(context, 0, &ssi, context, &hss_avs_event, NULL, NULL)) { - lwsl_err("%s: failed to create avs event secure stream\n", - __func__); - free(wav); - wav = NULL; - return 1; - } - - return 0; - -bail: - close(fd); - - return 1; -} diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-avs/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-avs/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-avs/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-avs/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-secure-streams-avs) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) -require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) - -if (requirements) - add_executable(${SAMP} main.c avs.c) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() - - if (LWS_WITH_SECURE_STREAMS_PROXY_API) - add_compile_options(-DLWS_SS_USE_SSPC) - - add_executable(${SAMP}-client main-client.c avs.c) - if (websockets_shared) - target_link_libraries(${SAMP}-client websockets_shared) - add_dependencies(${SAMP}-client websockets_shared) - else() - target_link_libraries(${SAMP}-client websockets) - endif() - endif() - -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,357 +0,0 @@ -/* - * lws-minimal-secure-streams-avs - * - * Written in 2019-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include - -extern int -avs_example_start(struct lws_context *context); - -int interrupted, bad = 1; -static lws_state_notify_link_t nl; -static const char * const default_ss_policy = - "{" - "\"release\":" "\"01234567\"," - "\"product\":" "\"myproduct\"," - "\"schema-version\":" "1," -// "\"via-socks5\":" "\"127.0.0.1:1080\"," - "\"retry\": [" /* named backoff / retry strategies */ - "{\"default\": {" - "\"backoff\": [" "1000," - "2000," - "3000," - "5000," - "10000" - "]," - "\"conceal\":" "5," - "\"jitterpc\":" "20," - "\"svalidping\":" "60," - "\"svalidhup\":" "64" - "}}" - "]," - "\"certs\": [" /* named individual certificates in BASE64 DER */ - "{\"digicert_global_root_g2\": \"" /* api.amazon.com 2038-01 */ - "MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh" - "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3" - "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH" - "MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT" - "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j" - "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG" - "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI" - "2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx" - "1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ" - "q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz" - "tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ" - "vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP" - "BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV" - "5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY" - "1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4" - "NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG" - "Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91" - "8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe" - "pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl" - "MrY=" - "\"}," - "{\"digicert_global_ca_g2\": \"" /* api.amazon.com 2028-08 */ - "MIIEizCCA3OgAwIBAgIQDI7gyQ1qiRWIBAYe4kH5rzANBgkqhkiG9w0BAQsFADBh" - "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3" - "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH" - "MjAeFw0xMzA4MDExMjAwMDBaFw0yODA4MDExMjAwMDBaMEQxCzAJBgNVBAYTAlVT" - "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxHjAcBgNVBAMTFURpZ2lDZXJ0IEdsb2Jh" - "bCBDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNIfL7zBYZd" - "W9UvhU5L4IatFaxhz1uvPmoKR/uadpFgC4przc/cV35gmAvkVNlW7SHMArZagV+X" - "au4CLyMnuG3UsOcGAngLH1ypmTb+u6wbBfpXzYEQQGfWMItYNdSWYb7QjHqXnxr5" - "IuYUL6nG6AEfq/gmD6yOTSwyOR2Bm40cZbIc22GoiS9g5+vCShjEbyrpEJIJ7RfR" - "ACvmfe8EiRROM6GyD5eHn7OgzS+8LOy4g2gxPR/VSpAQGQuBldYpdlH5NnbQtwl6" - "OErXb4y/E3w57bqukPyV93t4CTZedJMeJfD/1K2uaGvG/w/VNfFVbkhJ+Pi474j4" - "8V4Rd6rfArMCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0P" - "AQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29j" - "c3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmw0LmRp" - "Z2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwN6A1oDOGMWh0dHA6" - "Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwPQYD" - "VR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2lj" - "ZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFCRuKy3QapJRUSVpAaqaR6aJ50AgMB8GA1Ud" - "IwQYMBaAFE4iVCAYlebjbuYP+vq5Eu0GF485MA0GCSqGSIb3DQEBCwUAA4IBAQAL" - "OYSR+ZfrqoGvhOlaOJL84mxZvzbIRacxAxHhBsCsMsdaVSnaT0AC9aHesO3ewPj2" - "dZ12uYf+QYB6z13jAMZbAuabeGLJ3LhimnftiQjXS8X9Q9ViIyfEBFltcT8jW+rZ" - "8uckJ2/0lYDblizkVIvP6hnZf1WZUXoOLRg9eFhSvGNoVwvdRLNXSmDmyHBwW4co" - "atc7TlJFGa8kBpJIERqLrqwYElesA8u49L3KJg6nwd3jM+/AVTANlVlOnAM2BvjA" - "jxSZnE0qnsHhfTuvcqdFuhOWKU4Z0BqYBvQ3lBetoxi6PrABDJXWKTUgNX31EGDk" - "92hiHuwZ4STyhxGs6QiA" - "\"}," - "{\"starfield_services_root_ca\": \"" - "MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx" - "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT" - "HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs" - "ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5" - "MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD" - "VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy" - "ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy" - "dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI" - "hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p" - "OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2" - "8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K" - "Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe" - "hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk" - "6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw" - "DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q" - "AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI" - "bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB" - "ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z" - "qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd" - "iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn" - "0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN" - "sSi6" - "\"}," - "{\"starfield_class_2_ca\": \"" - "MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl" - "MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp" - "U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw" - "NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE" - "ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp" - "ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3" - "DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf" - "8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN" - "+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0" - "X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa" - "K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA" - "1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G" - "A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR" - "zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0" - "YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD" - "bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w" - "DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3" - "L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D" - "eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl" - "xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp" - "VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY" - "WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=" - "\"}" - "]," - "\"trust_stores\": [" /* named cert chains */ - "{" /* chain for alexa.na.gateway.devices.a2z.com */ - "\"name\": \"avs_via_starfield\"," - "\"stack\": [" - "\"starfield_class_2_ca\"," - "\"starfield_services_root_ca\"" - "]" - "}," - "{" /* chain for api.amazon.com */ - "\"name\": \"api_amazon_com\"," - "\"stack\": [" - "\"digicert_global_ca_g2\"," - "\"digicert_global_root_g2\"" - "]" - "}" - "]," - "\"s\": [" /* the supported stream types */ - "{\"api_amazon_com_auth\": {" - "\"endpoint\":" "\"api.amazon.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h1\"," - "\"http_method\":" "\"POST\"," - "\"http_url\":" "\"auth/o2/token\"," - "\"plugins\":" "[]," - "\"opportunistic\":" "true," - "\"tls\":" "true," - "\"h2q_oflow_txcr\":" "true," - "\"http_www_form_urlencoded\":" "true," - "\"http_no_content_length\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"api_amazon_com\"" - "}}," - "{\"avs_event\": {" - "\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h2\"," - "\"http_method\":" "\"GET\"," - "\"http_url\":" "\"v20160207/directives\"," - "\"h2q_oflow_txcr\":" "true," - "\"http_auth_header\":" "\"authorization:\"," - "\"http_auth_preamble\":" "\"Bearer \"," - "\"nailed_up\":" "true," - "\"long_poll\":" "true," - "\"retry\":" "\"default\"," - "\"plugins\":" "[]," - "\"tls\":" "true," - "\"tls_trust_store\":" "\"avs_via_starfield\"" - "}}," - "{\"avs_metadata\": {" - "\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h2\"," - "\"http_method\":" "\"POST\"," - "\"http_url\":" "\"v20160207/events\"," - "\"http_no_content_length\":" "true," - "\"h2q_oflow_txcr\":" "true," - "\"http_auth_header\":" "\"authorization:\"," - "\"http_auth_preamble\":" "\"Bearer \"," - "\"http_multipart_name\":" "\"metadata\"," - "\"http_mime_content_type\":" "\"application/json; charset=UTF-8\"," - "\"rideshare\":" "\"avs_audio\"," - "\"retry\":" "\"default\"," - "\"plugins\":" "[]," - "\"tls\":" "true," - "\"tls_trust_store\":" "\"avs_via_starfield\"" - "}}," - "{\"avs_audio\": {" - "\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h2\"," - "\"http_method\":" "\"POST\"," - "\"http_url\":" "\"v20160207/events\"," - "\"http_no_content_length\":" "true," - "\"plugins\":" "[]," - "\"tls\":" "true," - "\"h2q_oflow_txcr\":" "true," - "\"http_auth_header\":" "\"authorization:\"," - "\"http_auth_preamble\":" "\"Bearer \"," - "\"http_multipart_name\":" "\"audio\"," - "\"http_mime_content_type\":" "\"application/octet-stream\"," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"avs_via_starfield\"" - "}}" - "]" - "}" -; - -static const char *canned_root_token_payload = - "grant_type=refresh_token" - "&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg" - "SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP" - "zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y" - "0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW" - "k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE" - "iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S" - "KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc" - "AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI" - "xL_hDCcTho8opCVX-6QhJHl6SQFlTw13" - "&client_id=" - "amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d"; - -static int -app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = lws_system_context_from_system_mgr(mgr); - lws_system_blob_t *ab = lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */); - size_t size; - - /* - * For the things we care about, let's notice if we are trying to get - * past them when we haven't solved them yet, and make the system - * state wait while we trigger the dependent action. - */ - switch (target) { - case LWS_SYSTATE_REGISTERED: - size = lws_system_blob_get_size(ab); - if (size) - break; - - /* let's register our canned root token so auth can use it */ - lws_system_blob_direct_set(ab, - (const uint8_t *)canned_root_token_payload, - strlen(canned_root_token_payload)); - break; - case LWS_SYSTATE_OPERATIONAL: - if (current == LWS_SYSTATE_OPERATIONAL) - avs_example_start(context); - break; - case LWS_SYSTATE_POLICY_INVALID: - /* - * This is a NOP since we used direct set... but in a real - * system this could easily change to be done on the heap, then - * this would be important - */ - lws_system_blob_destroy(lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, - 1 /* AUTH_IDX_ROOT */)); - break; - } - - return 0; -} - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -static lws_state_notify_link_t * const app_notifier_list[] = { - &nl, NULL -}; - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - int n = 0; - - signal(SIGINT, sigint_handler); - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - lws_cmdline_option_handle_builtin(argc, argv, &info); - - lwsl_user("LWS secure streams - AVS test [-d]\n"); - - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.fd_limit_per_thread = 1 + 6 + 1; - info.pss_policies_json = default_ss_policy; - info.port = CONTEXT_PORT_NO_LISTEN; - -#if defined(LWS_SS_USE_SSPC) - { - const char *p; - - /* connect to ssproxy via UDS by default, else via - * tcp connection to this port */ - if ((p = lws_cmdline_option(argc, argv, "-p"))) - info.ss_proxy_port = atoi(p); - - /* UDS "proxy.ss.lws" in abstract namespace, else this socket - * path; when -p given this can specify the network interface - * to bind to */ - if ((p = lws_cmdline_option(argc, argv, "-i"))) - info.ss_proxy_bind = p; - - /* if -p given, -a specifies the proxy address to connect to */ - if ((p = lws_cmdline_option(argc, argv, "-a"))) - info.ss_proxy_address = p; - } -#endif - -#if defined(LWS_WITH_DETAILED_LATENCY) - info.detailed_latency_cb = lws_det_lat_plot_cb; - info.detailed_latency_filepath = "/tmp/lws-latency-ssproxy"; -#endif - - /* integrate us with lws system state management when context created */ - nl.name = "app"; - nl.notify_cb = app_system_state_nf; - info.register_notifier_list = app_notifier_list; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* the event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-avs/main-client.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-avs/main-client.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-avs/main-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-avs/main-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,128 +0,0 @@ -/* - * lws-minimal-secure-streams-avs - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include - -extern int -avs_example_start(struct lws_context *context); - -int interrupted, bad = 1; -static lws_state_notify_link_t nl; - -static const char *canned_root_token_payload = - "grant_type=refresh_token" - "&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg" - "SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP" - "zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y" - "0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW" - "k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE" - "iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S" - "KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc" - "AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI" - "xL_hDCcTho8opCVX-6QhJHl6SQFlTw13" - "&client_id=" - "amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d"; - -static int -app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = lws_system_context_from_system_mgr(mgr); - lws_system_blob_t *ab = lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */); - size_t size; - - /* - * For the things we care about, let's notice if we are trying to get - * past them when we haven't solved them yet, and make the system - * state wait while we trigger the dependent action. - */ - switch (target) { - case LWS_SYSTATE_REGISTERED: - size = lws_system_blob_get_size(ab); - if (size) - break; - - /* let's register our canned root token so auth can use it */ - lws_system_blob_direct_set(ab, - (const uint8_t *)canned_root_token_payload, - strlen(canned_root_token_payload)); - break; - case LWS_SYSTATE_OPERATIONAL: - if (current == LWS_SYSTATE_OPERATIONAL) - avs_example_start(context); - break; - case LWS_SYSTATE_POLICY_INVALID: - /* - * This is a NOP since we used direct set... but in a real - * system this could easily change to be done on the heap, then - * this would be important - */ - lws_system_blob_destroy(lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, - 1 /* AUTH_IDX_ROOT */)); - break; - } - - return 0; -} - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -static lws_state_notify_link_t * const app_notifier_list[] = { - &nl, NULL -}; - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - int n = 0; - - signal(SIGINT, sigint_handler); - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - lws_cmdline_option_handle_builtin(argc, argv, &info); - - lwsl_user("LWS secure streams - AVS test client [-d]\n"); - - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.fd_limit_per_thread = 1 + 6 + 1; - info.protocols = lws_sspc_protocols; - info.port = CONTEXT_PORT_NO_LISTEN; - -#if defined(LWS_WITH_DETAILED_LATENCY) - info.detailed_latency_cb = lws_det_lat_plot_cb; - info.detailed_latency_filepath = "/tmp/lws-latency-ssproxy"; -#endif - - /* integrate us with lws system state management when context created */ - nl.name = "app"; - nl.notify_cb = app_system_state_nf; - info.register_notifier_list = app_notifier_list; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* the event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-avs/year.wav and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-avs/year.wav differ diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-client-tx/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-client-tx/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-client-tx/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-client-tx/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-secure-streams-client-tx) -set(SRCS minimal-secure-streams-client-tx.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) -require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) -require_lws_config(LWS_WITH_SECURE_STREAMS_PROXY_API 1 requirements) - - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-client-tx/minimal-secure-streams-client-tx.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-client-tx/minimal-secure-streams-client-tx.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-client-tx/minimal-secure-streams-client-tx.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-client-tx/minimal-secure-streams-client-tx.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,198 +0,0 @@ -/* - * lws-minimal-secure-streams-tx - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * - * This demonstrates tx from secure streams. - * - * It opens a stream and fires small 80-byte payloads on it at 50Hz (20ms) - */ - -#include -#include -#include - -#define PKT_SIZE 80 -#define RATE_US 50000 - -static int interrupted, bad = 1; - -typedef struct myss { - struct lws_sspc_handle *ss; - void *opaque_data; - /* ... application specific state ... */ - lws_sorted_usec_list_t sul; - - int count; - char due; -} myss_t; - -/* secure streams payload interface */ - -static int -myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ -// myss_t *m = (myss_t *)userobj; - - //lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags); - //lwsl_hexdump_info(buf, len); - - return 0; -} - -static void -txcb(struct lws_sorted_usec_list *sul) -{ - myss_t *m = lws_container_of(sul, myss_t, sul); - - if (m->count == 1000) { - interrupted = 1; - return; - } - - m->due = 1; - lws_sspc_request_tx(m->ss); - - lws_sul_schedule(lws_sspc_get_context(m->ss), 0, &m->sul, txcb, RATE_US); - - -} - -static int -myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, - int *flags) -{ - myss_t *m = (myss_t *)userobj; - - if (!m->due) - return 0; - - m->due = 0; - - if (lws_get_random(lws_sspc_get_context(m->ss), buf, PKT_SIZE) != PKT_SIZE) - return 1; - - *len = PKT_SIZE; - *flags = 0; - if (!m->count) - *flags |= LWSSS_FLAG_SOM; - if (m->count == 999) { - *flags |= LWSSS_FLAG_EOM; - lwsl_user("%s: sent final packet\n", __func__); - bad = 0; - } - - m->count++; - - lws_sul_schedule(lws_sspc_get_context(m->ss), 0, &m->sul, txcb, RATE_US); - - // lwsl_user("%s: sending pkt %d\n", __func__, m->count); - - return 0; -} - -static int -myss_state(void *userobj, void *sh, lws_ss_constate_t state, - lws_ss_tx_ordinal_t ack) -{ - myss_t *m = (myss_t *)userobj; - struct lws_context *context = lws_sspc_get_context(m->ss); - - lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - break; - case LWSSSCS_CONNECTED: - lws_sul_schedule(context, 0, &m->sul, txcb, RATE_US); - break; - case LWSSSCS_DISCONNECTED: - lws_sul_schedule(context, 0, &m->sul, txcb, - LWS_SET_TIMER_USEC_CANCEL); - break; - case LWSSSCS_ALL_RETRIES_FAILED: - /* if we're out of retries, we want to close the app and FAIL */ - interrupted = 1; - break; - default: - break; - } - - return 0; -} - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_context_creation_info info; - struct lws_context *context; - lws_ss_info_t ssi; - const char *p; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS secure streams client TX [-d]\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - - info.options = //LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.fd_limit_per_thread = 1 + 6 + 1; - info.port = CONTEXT_PORT_NO_LISTEN; - info.protocols = lws_sspc_protocols; -#if defined(LWS_WITH_DETAILED_LATENCY) - info.detailed_latency_cb = lws_det_lat_plot_cb; - info.detailed_latency_filepath = "/tmp/lws-latency-ssclient"; -#endif - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* - * We're requesting a secure stream via proxy... where and how this - * connects are details managed by the proxy policy - */ - - memset(&ssi, 0, sizeof ssi); - ssi.handle_offset = offsetof(myss_t, ss); - ssi.opaque_user_data_offset = offsetof(myss_t, opaque_data); - ssi.rx = myss_rx; - ssi.tx = myss_tx; - ssi.state = myss_state; - ssi.user_alloc = sizeof(myss_t); - ssi.streamtype = "spam"; - - if (lws_sspc_create(context, 0, &ssi, NULL, NULL, NULL, NULL)) { - lwsl_err("%s: create secure stream failed\n", __func__); - goto bail; - } - - /* the event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail: - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-client-tx/README.md libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-client-tx/README.md --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-client-tx/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-client-tx/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -# lws minimal secure streams - -The application goes to https://warmcat.com and reads index.html there. - -It does it using Secure Streams... the main code in minimal-secure-streams.c -just sets up the context and opens a secure stream of type "mintest". - -The handler for state changes and payloads for "mintest" is in ss-myss.c - -The information about how a "mintest" stream should connect and the -protocol it uses is kept separated in policy-database.c - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --f| Force connecting to the wrong endpoint to check backoff retry flow --p| Run as proxy server for clients to connect to over unix domain socket - -``` -[2019/08/12 07:16:11:0045] USR: LWS minimal secure streams [-d] [-f] -[2019/08/12 07:16:12:6102] USR: myss_state: LWSSSCS_CREATING, ord 0x0 -[2019/08/12 07:16:12:6107] USR: myss_state: LWSSSCS_POLL, ord 0x0 -[2019/08/12 07:16:12:6117] N: lws_ss_client_connect: connecting h1get warmcat.com / -[2019/08/12 07:16:12:6118] USR: myss_state: LWSSSCS_CONNECTING, ord 0x0 -[2019/08/12 07:16:13:4171] USR: myss_state: LWSSSCS_CONNECTED, ord 0x0 -[2019/08/12 07:16:13:4222] USR: myss_rx: len 1024, flags: 1 -[2019/08/12 07:16:13:4243] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4245] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4246] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4247] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4252] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4264] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4265] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4266] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4267] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4269] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4270] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4278] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4279] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4280] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4281] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4282] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4284] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4287] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4288] USR: myss_rx: len 947, flags: 0 -[2019/08/12 07:16:13:4293] USR: myss_rx: len 0, flags: 2 -[2019/08/12 07:16:13:4399] USR: myss_state: LWSSSCS_DISCONNECTED, ord 0x0 -[2019/08/12 07:16:13:4761] USR: myss_state: LWSSSCS_DESTROYING, ord 0x0 -[2019/08/12 07:16:13:4781] USR: Completed: OK -``` diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-metadata/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-metadata/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-metadata/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-metadata/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -project(minimal-secure-streams-metadata) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-secure-streams-metadata) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) -require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) - -if (requirements) - add_executable(${SAMP} minimal-secure-streams.c) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() - - if (LWS_WITH_SECURE_STREAMS_PROXY_API) - add_compile_options(-DLWS_SS_USE_SSPC) - - add_executable(${SAMP}-client minimal-secure-streams.c) - if (websockets_shared) - target_link_libraries(${SAMP}-client websockets_shared) - add_dependencies(${SAMP}-client websockets_shared) - else() - target_link_libraries(${SAMP}-client websockets) - endif() - endif() - -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,370 +0,0 @@ -/* - * lws-minimal-secure-streams-metadata - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * - * This demonstrates a minimal http client using secure streams api. - * - * It visits https://warmcat.com/ and receives the html page there. - * - * This example is built two different ways from the same source... one includes - * the policy everything needed to fulfil the stream directly. The other -client - * variant has no policy itself and some other minor init changes, and connects - * to the -proxy example to actually get the connection done. - * - * In the -client build case, the example does not even init the tls libraries - * since the proxy part will take care of all that. - */ - -#include -#include -#include - -/* - * uncomment to force network traffic through 127.0.0.1:1080 - * - * On your local machine, you can run a SOCKS5 proxy like this - * - * $ ssh -N -D 0.0.0.0:1080 localhost -v - * - * If enabled, this also fetches a remote policy that also - * specifies that all traffic should go through the remote - * proxy. - */ -// #define VIA_LOCALHOST_SOCKS - -static int interrupted, bad = 1, force_cpd_fail_portal, - force_cpd_fail_no_internet; -static lws_state_notify_link_t nl; - -/* - * If the -proxy app is fulfilling our connection, then we don't need to have - * the policy in the client. - * - * When we build with LWS_SS_USE_SSPC, the apis hook up to a proxy process over - * a Unix Domain Socket. To test that, you need to separately run the - * ./lws-minimal-secure-streams-proxy test app on the same machine. - */ - -#if !defined(LWS_SS_USE_SSPC) -static const char * const default_ss_policy = - "{" - "\"release\":" "\"01234567\"," - "\"product\":" "\"myproduct\"," - "\"schema-version\":" "1," -#if defined(VIA_LOCALHOST_SOCKS) - "\"via-socks5\":" "\"127.0.0.1:1080\"," -#endif - - "\"retry\": [" /* named backoff / retry strategies */ - "{\"default\": {" - "\"backoff\": [" "1000," - "2000," - "3000," - "5000," - "10000" - "]," - "\"conceal\":" "5," - "\"jitterpc\":" "20," - "\"svalidping\":" "30," - "\"svalidhup\":" "35" - "}}" - "]," - "\"certs\": [" /* named individual certificates in BASE64 DER */ - /* - * Let's Encrypt certs for warmcat.com / libwebsockets.org - * - * We fetch the real policy from there using SS and switch to - * using that. - */ - "{\"isrg_root_x1\": \"" /* ISRG ROOT X1 */ - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" - "\"}," - "{\"LEX3_isrg_root_x1\": \"" /* LE X3 signed by ISRG X1 root */ - "MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1" - "WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg" - "RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi" - "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX" - "NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf" - "89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl" - "Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc" - "Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz" - "uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB" - "AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU" - "BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB" - "FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo" - "SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js" - "LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF" - "BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG" - "AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD" - "VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB" - "ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx" - "A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM" - "UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2" - "DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1" - "eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu" - "OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw" - "p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY" - "2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0" - "ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR" - "PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b" - "rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt" - "\"}" - "]," - "\"trust_stores\": [" /* named cert chains */ - "{" - "\"name\": \"le_via_isrg\"," - "\"stack\": [" - "\"isrg_root_x1\"," - "\"LEX3_isrg_root_x1\"" - "]" - "}" - "]," - "\"s\": [" - "{\"mintest\": {" - "\"endpoint\":" "\"${servername}\"," - "\"port\":" "443," - "\"protocol\":" "\"h1\"," - "\"http_method\":" "\"GET\"," - "\"http_url\":" "\"\"," - "\"tls\":" "true," - "\"opportunistic\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"le_via_isrg\"," - "\"metadata\": [" - "{\"servername\": \"\"}" - "]" - "}}" - "]}" -; - -#endif - -typedef struct myss { - struct lws_ss_handle *ss; - void *opaque_data; - /* ... application specific state ... */ - lws_sorted_usec_list_t sul; -} myss_t; - -/* secure streams payload interface */ - -static int -myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ -// myss_t *m = (myss_t *)userobj; - - lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags); - lwsl_hexdump_info(buf, len); - - /* - * If we received the whole message, for our example it means - * we are done. - */ - if (flags & LWSSS_FLAG_EOM) { - bad = 0; - interrupted = 1; - } - - return 0; -} - -static int -myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, - int *flags) -{ - //myss_t *m = (myss_t *)userobj; - - return 0; -} - -static int -myss_state(void *userobj, void *sh, lws_ss_constate_t state, - lws_ss_tx_ordinal_t ack) -{ - myss_t *m = (myss_t *)userobj; - - lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - lws_ss_set_metadata(m->ss, "servername", "warmcat.com", 11); - lws_ss_client_connect(m->ss); - break; - case LWSSSCS_ALL_RETRIES_FAILED: - /* if we're out of retries, we want to close the app and FAIL */ - interrupted = 1; - break; - case LWSSSCS_QOS_ACK_REMOTE: - lwsl_notice("%s: LWSSSCS_QOS_ACK_REMOTE\n", __func__); - break; - default: - break; - } - - return 0; -} - -static int -app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = lws_system_context_from_system_mgr(mgr); - - /* - * For the things we care about, let's notice if we are trying to get - * past them when we haven't solved them yet, and make the system - * state wait while we trigger the dependent action. - */ - switch (target) { - - case LWS_SYSTATE_OPERATIONAL: - if (current == LWS_SYSTATE_OPERATIONAL) { - lws_ss_info_t ssi; - - /* We're making an outgoing secure stream ourselves */ - - memset(&ssi, 0, sizeof(ssi)); - ssi.handle_offset = offsetof(myss_t, ss); - ssi.opaque_user_data_offset = offsetof(myss_t, - opaque_data); - ssi.rx = myss_rx; - ssi.tx = myss_tx; - ssi.state = myss_state; - ssi.user_alloc = sizeof(myss_t); - ssi.streamtype = "mintest"; - - if (lws_ss_create(context, 0, &ssi, NULL, NULL, - NULL, NULL)) { - lwsl_err("%s: failed to create secure stream\n", - __func__); - return -1; - } - } - break; - } - - return 0; -} - -static lws_state_notify_link_t * const app_notifier_list[] = { - &nl, NULL -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - int n = 0; - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); - lws_cmdline_option_handle_builtin(argc, argv, &info); - - lwsl_user("LWS secure streams test client [-d]\n"); - - /* these options are mutually exclusive if given */ - - if (lws_cmdline_option(argc, argv, "--force-portal")) - force_cpd_fail_portal = 1; - - if (lws_cmdline_option(argc, argv, "--force-no-internet")) - force_cpd_fail_no_internet = 1; - - info.fd_limit_per_thread = 1 + 6 + 1; - info.port = CONTEXT_PORT_NO_LISTEN; - -#if defined(LWS_SS_USE_SSPC) - info.protocols = lws_sspc_protocols; - { - const char *p; - - /* connect to ssproxy via UDS by default, else via - * tcp connection to this port */ - if ((p = lws_cmdline_option(argc, argv, "-p"))) - info.ss_proxy_port = atoi(p); - - /* UDS "proxy.ss.lws" in abstract namespace, else this socket - * path; when -p given this can specify the network interface - * to bind to */ - if ((p = lws_cmdline_option(argc, argv, "-i"))) - info.ss_proxy_bind = p; - - /* if -p given, -a specifies the proxy address to connect to */ - if ((p = lws_cmdline_option(argc, argv, "-a"))) - info.ss_proxy_address = p; - } -#else - info.pss_policies_json = default_ss_policy; - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; -#endif - - /* integrate us with lws system state management when context created */ - - nl.name = "app"; - nl.notify_cb = app_system_state_nf; - info.register_notifier_list = app_notifier_list; - - /* create the context */ - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - - /* the event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-metadata/README.md libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-metadata/README.md --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-metadata/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-metadata/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -# lws minimal secure streams - -The application goes to https://warmcat.com and reads index.html there. - -It does it using Secure Streams... the main code in minimal-secure-streams.c -just sets up the context and opens a secure stream of type "mintest". - -The handler for state changes and payloads for "mintest" is in ss-myss.c - -The information about how a "mintest" stream should connect and the -protocol it uses is kept separated in policy-database.c - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --f| Force connecting to the wrong endpoint to check backoff retry flow --p| Run as proxy server for clients to connect to over unix domain socket ---force-portal|Force the SS Captive Portal Detection to feel it's behind a portal ---force-no-internet|Force the SS Captive Portal Detection to feel it can't reach the internet - -``` -[2019/08/12 07:16:11:0045] USR: LWS minimal secure streams [-d] [-f] -[2019/08/12 07:16:12:6102] USR: myss_state: LWSSSCS_CREATING, ord 0x0 -[2019/08/12 07:16:12:6107] USR: myss_state: LWSSSCS_POLL, ord 0x0 -[2019/08/12 07:16:12:6117] N: lws_ss_client_connect: connecting h1get warmcat.com / -[2019/08/12 07:16:12:6118] USR: myss_state: LWSSSCS_CONNECTING, ord 0x0 -[2019/08/12 07:16:13:4171] USR: myss_state: LWSSSCS_CONNECTED, ord 0x0 -[2019/08/12 07:16:13:4222] USR: myss_rx: len 1024, flags: 1 -[2019/08/12 07:16:13:4243] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4245] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4246] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4247] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4252] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4264] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4265] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4266] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4267] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4269] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4270] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4278] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4279] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4280] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4281] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4282] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4284] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4287] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4288] USR: myss_rx: len 947, flags: 0 -[2019/08/12 07:16:13:4293] USR: myss_rx: len 0, flags: 2 -[2019/08/12 07:16:13:4399] USR: myss_state: LWSSSCS_DISCONNECTED, ord 0x0 -[2019/08/12 07:16:13:4761] USR: myss_state: LWSSSCS_DESTROYING, ord 0x0 -[2019/08/12 07:16:13:4781] USR: Completed: OK -``` diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-proxy/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-proxy/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-proxy/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-proxy/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-secure-streams-proxy) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) -require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) -require_lws_config(LWS_WITH_SECURE_STREAMS_PROXY_API 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-proxy/main.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-proxy/main.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-proxy/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-proxy/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,285 +0,0 @@ -/* - * lws-minimal-secure-streams-proxy - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * - * This is the proxy part for examples built to use it to connect to... it has - * the policy and the core SS function, but it doesn't contain any of the user - * code "business logic"... that's in the clients. - * - * The proxy side has the policy and performs the onward connection proxying - * fulfilment. The clients state the streamtype name they want and ask for the - * client to do the connection part. - * - * Rideshare information is being parsed out at the proxy side; the SSS RX part - * also brings with it rideshare names. - * - * Metadata is passed back over SSS from the client in the TX messages for the - * proxy to use per the policy. - */ - -#include -#include -#include - -static int interrupted, bad = 1, port = 0 /* unix domain socket */; -static const char *ibind = NULL; /* default to unix domain skt "proxy.ss.lws" */ -static lws_state_notify_link_t nl; - -/* - * We just define enough policy so it can fetch the latest one securely - */ - -static const char * const default_ss_policy = - "{" - "\"release\":" "\"01234567\"," - "\"product\":" "\"myproduct\"," - "\"schema-version\":" "1," - "\"retry\": [" /* named backoff / retry strategies */ - "{\"default\": {" - "\"backoff\": [" "1000," - "2000," - "3000," - "5000," - "10000" - "]," - "\"conceal\":" "5," - "\"jitterpc\":" "20," - "\"svalidping\":" "30," - "\"svalidhup\":" "35" - "}}" - "]," - "\"certs\": [" /* named individual certificates in BASE64 DER */ - /* - * Let's Encrypt certs for warmcat.com / libwebsockets.org - * - * We fetch the real policy from there using SS and switch to - * using that. - */ - "{\"isrg_root_x1\": \"" /* ISRG ROOT X1 */ - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" - "\"}," - "{\"LEX3_isrg_root_x1\": \"" /* LE X3 signed by ISRG X1 root */ - "MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1" - "WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg" - "RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi" - "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX" - "NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf" - "89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl" - "Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc" - "Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz" - "uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB" - "AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU" - "BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB" - "FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo" - "SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js" - "LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF" - "BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG" - "AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD" - "VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB" - "ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx" - "A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM" - "UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2" - "DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1" - "eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu" - "OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw" - "p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY" - "2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0" - "ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR" - "PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b" - "rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt" - "\"}" - "]," - "\"trust_stores\": [" /* named cert chains */ - "{" - "\"name\": \"le_via_isrg\"," - "\"stack\": [" - "\"isrg_root_x1\"," - "\"LEX3_isrg_root_x1\"" - "]" - "}" - "]," - "\"s\": [" - "{\"fetch_policy\": {" - "\"endpoint\":" "\"warmcat.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h1\"," - "\"http_method\":" "\"GET\"," - "\"http_url\":" "\"policy/minimal-proxy.json\"," - "\"tls\":" "true," - "\"opportunistic\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"le_via_isrg\"" - "}}" - "}" -; - -static const char *canned_root_token_payload = - "grant_type=refresh_token" - "&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg" - "SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP" - "zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y" - "0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW" - "k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE" - "iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S" - "KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc" - "AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI" - "xL_hDCcTho8opCVX-6QhJHl6SQFlTw13" - "&client_id=" - "amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d"; - -static int -app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link, - int current, int target) -{ - struct lws_context *context = lws_system_context_from_system_mgr(mgr); - lws_system_blob_t *ab = lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */); - size_t size; - - /* - * For the things we care about, let's notice if we are trying to get - * past them when we haven't solved them yet, and make the system - * state wait while we trigger the dependent action. - */ - switch (target) { - case LWS_SYSTATE_REGISTERED: - size = lws_system_blob_get_size(ab); - if (size) - break; - - /* let's register our canned root token so auth can use it */ - lws_system_blob_direct_set(ab, - (const uint8_t *)canned_root_token_payload, - strlen(canned_root_token_payload)); - break; - case LWS_SYSTATE_OPERATIONAL: - if (current == LWS_SYSTATE_OPERATIONAL) - /* - * At this point we have DHCP, ntp, system auth token - * and we can reasonably create the proxy - */ - if (lws_ss_proxy_create(context, ibind, port)) { - lwsl_err("%s: failed to create ss proxy\n", - __func__); - return -1; - } - break; - case LWS_SYSTATE_POLICY_INVALID: - /* - * This is a NOP since we used direct set... but in a real - * system this could easily change to be done on the heap, then - * this would be important - */ - lws_system_blob_destroy(lws_system_get_blob(context, - LWS_SYSBLOB_TYPE_AUTH, - 1 /* AUTH_IDX_ROOT */)); - break; - } - - return 0; -} - -static lws_state_notify_link_t * const app_notifier_list[] = { - &nl, NULL -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - /* connect to ssproxy via UDS by default, else via tcp with this port */ - if ((p = lws_cmdline_option(argc, argv, "-p"))) - port = atoi(p); - - /* UDS "proxy.ss.lws" in abstract namespace, else this socket path; - * when -p given this can specify the network interface to bind to */ - if ((p = lws_cmdline_option(argc, argv, "-i"))) - ibind = p; - - lws_set_log_level(logs, NULL); - lwsl_user("LWS secure streams Proxy [-d]\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.fd_limit_per_thread = 1 + 6 + 1; - info.pss_policies_json = default_ss_policy; - info.port = CONTEXT_PORT_NO_LISTEN; -#if defined(LWS_WITH_DETAILED_LATENCY) - info.detailed_latency_cb = lws_det_lat_plot_cb; - info.detailed_latency_filepath = "/tmp/lws-latency-ssproxy"; -#endif - - /* integrate us with lws system state management when context created */ - nl.name = "app"; - nl.notify_cb = app_system_state_nf; - info.register_notifier_list = app_notifier_list; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* the event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - bad = 0; - - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-proxy/README.md libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-proxy/README.md --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-proxy/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-proxy/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -# lws minimal secure streams proxy - -Operates as a secure streams proxy, by default on a listening unix domain socket -"proxy.ss.lws" in the Linux abstract namespace. - -Give -p to have it listen on a specific tcp port instead. - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --f| Force connecting to the wrong endpoint to check backoff retry flow --p |If not given, proxy listens on a Unix Domain Socket, if given listen on specified tcp port --i |Optionally specify the UDS path (no -p) or network interface to bind to (if -p also given) - -``` -[2020/02/26 15:41:27:5768] U: LWS secure streams Proxy [-d] -[2020/02/26 15:41:27:5770] N: lws_ss_policy_set: 2.064KiB, pad 70%: hardcoded -[2020/02/26 15:41:27:5771] N: lws_tls_client_create_vhost_context: using mem client CA cert 1391 -[2020/02/26 15:41:27:8681] N: lws_ss_policy_set: 4.512KiB, pad 15%: updated -[2020/02/26 15:41:27:8682] N: lws_tls_client_create_vhost_context: using mem client CA cert 837 -[2020/02/26 15:41:27:8683] N: lws_tls_client_create_vhost_context: using mem client CA cert 1043 -[2020/02/26 15:41:27:8684] N: lws_tls_client_create_vhost_context: using mem client CA cert 1167 -[2020/02/26 15:41:27:8684] N: lws_tls_client_create_vhost_context: using mem client CA cert 1391 -[2020/02/26 15:41:28:4226] N: ss_api_amazon_auth_rx: acquired 567-byte api.amazon.com auth token, exp 3600s -``` diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-seq/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-seq/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-seq/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-seq/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-secure-streams-seq) -set(SRCS minimal-secure-streams.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) -require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-seq/minimal-secure-streams.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-seq/minimal-secure-streams.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-seq/minimal-secure-streams.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-seq/minimal-secure-streams.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,443 +0,0 @@ -/* - * lws-minimal-secure-streams-seq - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * - * This demonstrates the a minimal http client using secure streams api. - * - * It visits https://warmcat.com/ and receives the html page there. - * - * This is the "secure streams" api equivalent of minimal-http-client... - * it shows how to use a sequencer to make it easy to build more complex - * schemes on top of this example. - * - * The layering looks like this - * - * lifetime - * - * ------ app ------ process - * ---- sequencer ---- process - * --- secure stream --- process - * ------- wsi ------- connection - * - * see minimal-secure-streams for a similar example without the sequencer. - */ - -#include -#include -#include - -static int interrupted, bad = 1, flag_conn_fail, flag_h1post; -static const char * const default_ss_policy = - "{" - "\"release\":" "\"01234567\"," - "\"product\":" "\"myproduct\"," - "\"schema-version\":" "1," - "\"retry\": [" /* named backoff / retry strategies */ - "{\"default\": {" - "\"backoff\": [" "1000," - "2000," - "3000," - "5000," - "10000" - "]," - "\"conceal\":" "5," - "\"jitterpc\":" "20," - "\"svalidping\":" "300," - "\"svalidhup\":" "310" - "}}" - "]," - "\"certs\": [" /* named individual certificates in BASE64 DER */ - /* - * Need to be in order from root cert... notice sometimes as - * with Let's Encrypt there are multiple possible validation - * paths, all the pieces for one validation path must be - * given, excluding the server cert itself. Let's Encrypt - * intermediate is signed by their ISRG Root CA but also is - * cross-signed by an IdenTrust intermediate that's widely - * deployed in browsers. We use the ISRG path because that - * way we can skip the extra IdenTrust root cert. - */ - "{\"isrg_root_x1\": \"" /* ISRG ROOT X1 */ - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" - "\"}," - "{\"LEX3_isrg_root_x1\": \"" /* LE X3 signed by ISRG X1 root */ - "MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1" - "WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg" - "RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi" - "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX" - "NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf" - "89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl" - "Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc" - "Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz" - "uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB" - "AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU" - "BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB" - "FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo" - "SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js" - "LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF" - "BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG" - "AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD" - "VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB" - "ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx" - "A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM" - "UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2" - "DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1" - "eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu" - "OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw" - "p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY" - "2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0" - "ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR" - "PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b" - "rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt" - "\"}" - "]," - "\"trust_stores\": [" /* named cert chains */ - "{" - "\"name\": \"le_via_isrg\"," - "\"stack\": [" - "\"isrg_root_x1\"," - "\"LEX3_isrg_root_x1\"" - "]" - "}" - "]," - "\"s\": [" /* the supported stream types */ - "{\"mintest\": {" - "\"endpoint\":" "\"warmcat.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h1\"," - "\"http_method\":" "\"GET\"," - "\"http_url\":" "\"index.html\"," - "\"plugins\":" "[]," - "\"tls\":" "true," - "\"opportunistic\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"le_via_isrg\"" - "}}," - "{\"mintest-fail\": {" - "\"endpoint\":" "\"warmcat.com\"," - "\"port\":" "22," - "\"protocol\":" "\"h1\"," - "\"http_method\":" "\"GET\"," - "\"http_url\":" "\"index.html\"," - "\"plugins\":" "[]," - "\"tls\":" "true," - "\"opportunistic\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"le_via_isrg\"" - "}}," - "{\"minpost\": {" - "\"endpoint\":" "\"warmcat.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h1\"," - "\"http_method\":" "\"POST\"," - "\"http_url\":" "\"testserver/formtest\"," - "\"plugins\":" "[]," - "\"tls\":" "true," - "\"opportunistic\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"le_via_isrg\"" - "}}" - "]" - "}" -; - -typedef struct myss { - struct lws_ss_handle *ss; - void *opaque_data; - /* ... application specific state ... */ -} myss_t; - -/* secure streams payload interface */ - -static int -myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ -// myss_t *m = (myss_t *)userobj; - - lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags); - lwsl_hexdump_info(buf, len); - - /* - * If we received the whole message, we let the sequencer know it - * was a success - */ - if (flags & LWSSS_FLAG_EOM) { - bad = 0; - interrupted = 1; - } - - return 0; -} - -static int -myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, - int *flags) -{ - // myss_t *m = (myss_t *)userobj; - - /* in this example, we don't send any payload */ - - return 0; -} - -static int -myss_state(void *userobj, void *sh, lws_ss_constate_t state, - lws_ss_tx_ordinal_t ack) -{ - myss_t *m = (myss_t *)userobj; - - lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - lws_ss_request_tx(m->ss); - break; - case LWSSSCS_ALL_RETRIES_FAILED: - /* if we're out of retries, we want to close the app and FAIL */ - interrupted = 1; - break; - default: - break; - } - - return 0; -} - -typedef enum { - SEQ_IDLE, - SEQ_TRY_CONNECT, - SEQ_RECONNECT_WAIT, - SEQ_CONNECTED, -} myseq_state_t; - -typedef struct myseq { - struct lws_ss_handle *ss; - - myseq_state_t state; - int http_resp; - - uint16_t try; -} myseq_t; - -/* - * This defines the sequence of things the test app does. - */ - -static lws_seq_cb_return_t -min_sec_str_sequencer_cb(struct lws_sequencer *seq, void *user, int event, - void *v, void *a) -{ - struct myseq *s = (struct myseq *)user; - lws_ss_info_t ssi; - - switch ((int)event) { - - /* these messages are created just by virtue of being a sequencer */ - - case LWSSEQ_CREATED: /* our sequencer just got started */ - s->state = SEQ_IDLE; - lwsl_notice("%s: LWSSEQ_CREATED\n", __func__); - - /* We're making an outgoing secure stream ourselves */ - - memset(&ssi, 0, sizeof(ssi)); - ssi.handle_offset = offsetof(myss_t, ss); - ssi.opaque_user_data_offset = offsetof(myss_t, opaque_data); - ssi.rx = myss_rx; - ssi.tx = myss_tx; - ssi.state = myss_state; - ssi.user_alloc = sizeof(myss_t); - - /* requested to fail (to check backoff)? */ - if (flag_conn_fail) - ssi.streamtype = "mintest-fail"; - else - /* request to check h1 POST */ - if (flag_h1post) - ssi.streamtype = "minpost"; - else - /* default to h1 GET */ - ssi.streamtype = "mintest"; - - if (lws_ss_create(lws_seq_get_context(seq), 0, &ssi, NULL, - &s->ss, seq, NULL)) { - lwsl_err("%s: failed to create secure stream\n", - __func__); - - return LWSSEQ_RET_DESTROY; - } - break; - - case LWSSEQ_DESTROYED: - lwsl_notice("%s: LWSSEQ_DESTROYED\n", __func__); - break; - - case LWSSEQ_TIMED_OUT: /* current step timed out */ - if (s->state == SEQ_RECONNECT_WAIT) - lws_ss_request_tx(s->ss); - break; - - /* - * These messages are created because we have a secure stream that was - * bound to this sequencer at creation time. It copies its state - * events to us as its sequencer parent. v is the lws_ss_handle_t * - */ - - case LWSSEQ_SS_STATE_BASE + LWSSSCS_CREATING: - lwsl_info("%s: seq LWSSSCS_CREATING\n", __func__); - lws_ss_request_tx(s->ss); - break; - case LWSSEQ_SS_STATE_BASE + LWSSSCS_DISCONNECTED: - lwsl_info("%s: seq LWSSSCS_DISCONNECTED\n", __func__); - break; - case LWSSEQ_SS_STATE_BASE + LWSSSCS_UNREACHABLE: - lwsl_info("%s: seq LWSSSCS_UNREACHABLE\n", __func__); - break; - case LWSSEQ_SS_STATE_BASE + LWSSSCS_AUTH_FAILED: - lwsl_info("%s: seq LWSSSCS_AUTH_FAILED\n", __func__); - break; - case LWSSEQ_SS_STATE_BASE + LWSSSCS_CONNECTED: - lwsl_info("%s: seq LWSSSCS_CONNECTED\n", __func__); - break; - case LWSSEQ_SS_STATE_BASE + LWSSSCS_CONNECTING: - lwsl_info("%s: seq LWSSSCS_CONNECTING\n", __func__); - break; - case LWSSEQ_SS_STATE_BASE + LWSSSCS_DESTROYING: - lwsl_info("%s: seq LWSSSCS_DESTROYING\n", __func__); - break; - case LWSSEQ_SS_STATE_BASE + LWSSSCS_POLL: - /* somebody called lws_ss_poll() on the stream */ - lwsl_info("%s: seq LWSSSCS_POLL\n", __func__); - break; - case LWSSEQ_SS_STATE_BASE + LWSSSCS_ALL_RETRIES_FAILED: - lwsl_info("%s: seq LWSSSCS_ALL_RETRIES_FAILED\n", __func__); - interrupted = 1; - break; - case LWSSEQ_SS_STATE_BASE + LWSSSCS_QOS_ACK_REMOTE: - lwsl_info("%s: seq LWSSSCS_QOS_ACK_REMOTE\n", __func__); - break; - case LWSSEQ_SS_STATE_BASE + LWSSSCS_QOS_ACK_LOCAL: - lwsl_info("%s: seq LWSSSCS_QOS_ACK_LOCAL\n", __func__); - break; - - /* - * This is the message we send from the ss handler to inform the - * sequencer we had the payload properly - */ - - case LWSSEQ_USER_BASE: - bad = 0; - interrupted = 1; - break; - - default: - break; - } - - return LWSSEQ_RET_CONTINUE; -} - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - struct lws_context_creation_info info; - struct lws_context *context; - lws_seq_info_t i; - const char *p; - myseq_t *ms; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal secure streams [-d][-f][--h1post]\n"); - - flag_conn_fail = !!lws_cmdline_option(argc, argv, "-f"); - flag_h1post = !!lws_cmdline_option(argc, argv, "--h1post"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.fd_limit_per_thread = 1 + 1 + 1; - info.pss_policies_json = default_ss_policy; - info.port = CONTEXT_PORT_NO_LISTEN; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* - * Create the sequencer that performs the steps of the test action - * from inside the event loop. - */ - - memset(&i, 0, sizeof(i)); - i.context = context; - i.user_size = sizeof(myseq_t); - i.puser = (void **)&ms; - i.cb = min_sec_str_sequencer_cb; - i.name = "min-sec-stream-seq"; - - if (!lws_seq_create(&i)) { - lwsl_err("%s: failed to create sequencer\n", __func__); - goto bail; - } - - /* the event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail: - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-seq/README.md libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-seq/README.md --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-seq/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-seq/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -# lws minimal sequre streams seq - -The application goes to https://warmcat.com and reads index.html there. - -It does it using Secure Streams... the main code in minimal-secure-streams.c -just sets up the context and opens a secure stream of type "mintest". - -The handler for state changes and payloads for "mintest" is in ss-myss.c - -The information about how a "mintest" stream should connect and the -protocol it uses is kept separated in policy-database.c - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --f| Force connecting to the wrong endpoint to check backoff retry flow - -``` - $ ./lws-minimal-secure-streams-seq -[2018/03/04 14:43:20:8562] USER: LWS minimal http client -[2018/03/04 14:43:20:8571] NOTICE: Creating Vhost 'default' port -1, 1 protocols, IPv6 on -[2018/03/04 14:43:20:8616] NOTICE: created client ssl context for default -[2018/03/04 14:43:20:8617] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com -[2018/03/04 14:43:21:1496] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com -[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: incoming content length 26520 -[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: client connection up -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 -[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 -[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 974 -[2018/03/04 14:43:22:3022] NOTICE: lws_http_client_read: transaction completed says -1 -[2018/03/04 14:43:23:3042] USER: Completed -``` - - diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-sink/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-sink/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-sink/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-sink/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-secure-streams-sink) -set(SRCS main.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - endif() -ENDMACRO() - - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) -require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-sink/main.c libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-sink/main.c --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-sink/main.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-sink/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,288 +0,0 @@ -/* - * lws-minimal-secure-streams-sink - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#include -#include -#include - -static int interrupted, bad = 1; -static const char * const default_ss_policy = - "{" - "\"release\":" "\"01234567\"," - "\"product\":" "\"myproduct\"," - "\"schema-version\":" "1," - "\"retry\": [" /* named backoff / retry strategies */ - "{\"default\": {" - "\"backoff\": [" "1000," - "2000," - "3000," - "5000," - "10000" - "]," - "\"conceal\":" "5," - "\"jitterpc\":" "20," - "\"svalidping\":" "300," - "\"svalidhup\":" "310" - "}}" - "]," - "\"certs\": [" /* named individual certificates in BASE64 DER */ - /* - * Need to be in order from root cert... notice sometimes as - * with Let's Encrypt there are multiple possible validation - * paths, all the pieces for one validation path must be - * given, excluding the server cert itself. Let's Encrypt - * intermediate is signed by their ISRG Root CA but also is - * cross-signed by an IdenTrust intermediate that's widely - * deployed in browsers. We use the ISRG path because that - * way we can skip the extra IdenTrust root cert. - */ - "{\"isrg_root_x1\": \"" /* ISRG ROOT X1 */ - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" - "\"}," - "{\"LEX3_isrg_root_x1\": \"" /* LE X3 signed by ISRG X1 root */ - "MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1" - "WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg" - "RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi" - "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX" - "NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf" - "89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl" - "Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc" - "Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz" - "uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB" - "AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU" - "BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB" - "FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo" - "SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js" - "LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF" - "BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG" - "AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD" - "VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB" - "ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx" - "A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM" - "UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2" - "DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1" - "eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu" - "OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw" - "p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY" - "2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0" - "ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR" - "PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b" - "rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt" - "\"}" - "]," - "\"trust_stores\": [" /* named cert chains */ - "{" - "\"name\": \"le_via_isrg\"," - "\"stack\": [" - "\"isrg_root_x1\"," - "\"LEX3_isrg_root_x1\"" - "]" - "}" - "]," - "\"s\": [" /* the supported stream types */ - "{\"\": {" - "\"endpoint\":" "\"warmcat.com\"," - "\"port\":" "443," - "\"protocol\":" "\"h2\"," - "\"http_method\":" "\"GET\"," - "\"http_url\":" "\"index.html\"," - "\"plugins\":" "[]," - "\"tls\":" "true," - "\"nailed_up\":" "true," - "\"long_poll\":" "true," - "\"retry\":" "\"default\"," - "\"tls_trust_store\":" "\"le_via_isrg\"" - "}}" - "]" - "}" -; - -typedef struct myss { - struct lws_ss_handle *ss; - void *opaque_data; - /* ... application specific state ... */ - lws_sorted_usec_list_t sul; - - int count; -} myss_t; - -/* secure streams payload interface */ - -static int -myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags) -{ -// myss_t *m = (myss_t *)userobj; - - lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags); - lwsl_hexdump_info(buf, len); - - /* - * If we received the whole message, for our example it means - * we are done. - */ - if (flags & LWSSS_FLAG_EOM) { - bad = 0; - interrupted = 1; - } - - return 0; -} - -static int -myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, - int *flags) -{ - //myss_t *m = (myss_t *)userobj; - - return 0; -} - -static int -myss_state(void *userobj, void *sh, lws_ss_constate_t state, - lws_ss_tx_ordinal_t ack) -{ - myss_t *m = (myss_t *)userobj; - - lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), - (unsigned int)ack); - - switch (state) { - case LWSSSCS_CREATING: - lws_ss_request_tx(m->ss); - break; - case LWSSSCS_ALL_RETRIES_FAILED: - /* if we're out of retries, we want to close the app and FAIL */ - interrupted = 1; - break; - default: - break; - } - - return 0; -} - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - int n = 0; - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - lws_cmdline_option_handle_builtin(argc, argv, &info); - lwsl_user("LWS secure streams [-d] [-f] [-p] [--h1post]\n"); - - info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.fd_limit_per_thread = 1 + 6 + 1; - info.pss_policies_json = default_ss_policy; - info.port = CONTEXT_PORT_NO_LISTEN; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - // puts(default_ss_policy); - - if (lws_cmdline_option(argc, argv, "-p")) { - - /* we are being the proxy */ - -#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) - if (lws_ss_proxy_create(context, NULL, 0)) { - lwsl_err("%s: failed to create ss proxy\n", __func__); - goto bail; - } - lwsl_notice("%s: secure streams proxy mode\n", __func__); -#else - lwsl_err("%s: needs cmake LWS_WITH_SECURE_STREAMS_PROXY_API\n", - __func__); -#endif - } else { - lws_ss_info_t ssi; - - /* We're making an outgoing secure stream ourselves */ - - memset(&ssi, 0, sizeof(ssi)); - ssi.handle_offset = offsetof(myss_t, ss); - ssi.opaque_user_data_offset = offsetof(myss_t, opaque_data); - ssi.rx = myss_rx; - ssi.tx = myss_tx; - ssi.state = myss_state; - ssi.user_alloc = sizeof(myss_t); - - /* requested to fail (to check backoff)? */ - if (lws_cmdline_option(argc, argv, "-f")) - ssi.streamtype = "mintest-fail"; - else - /* request to check h1 POST */ - if (lws_cmdline_option(argc, argv, "--h1post")) - ssi.streamtype = "minpost"; - else - /* default to h1 GET */ - ssi.streamtype = "mintest"; - - if (lws_ss_create(context, 0, &ssi, NULL, NULL, NULL, NULL)) { - lwsl_err("%s: failed to create secure stream\n", - __func__); - goto bail; - } - } - - /* the event loop */ - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - -bail: - - lws_context_destroy(context); - lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); - - return bad; -} diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-sink/README.md libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-sink/README.md --- libwebsockets-4.0.20/minimal-examples/secure-streams/minimal-secure-streams-sink/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/minimal-secure-streams-sink/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -# lws minimal secure streams - -The application goes to https://warmcat.com and reads index.html there. - -It does it using Secure Streams... the main code in minimal-secure-streams.c -just sets up the context and opens a secure stream of type "mintest". - -The handler for state changes and payloads for "mintest" is in ss-myss.c - -The information about how a "mintest" stream should connect and the -protocol it uses is kept separated in policy-database.c - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --f| Force connecting to the wrong endpoint to check backoff retry flow --p| Run as proxy server for clients to connect to over unix domain socket - -``` -[2019/08/12 07:16:11:0045] USR: LWS minimal secure streams [-d] [-f] -[2019/08/12 07:16:12:6102] USR: myss_state: LWSSSCS_CREATING, ord 0x0 -[2019/08/12 07:16:12:6107] USR: myss_state: LWSSSCS_POLL, ord 0x0 -[2019/08/12 07:16:12:6117] N: lws_ss_client_connect: connecting h1get warmcat.com / -[2019/08/12 07:16:12:6118] USR: myss_state: LWSSSCS_CONNECTING, ord 0x0 -[2019/08/12 07:16:13:4171] USR: myss_state: LWSSSCS_CONNECTED, ord 0x0 -[2019/08/12 07:16:13:4222] USR: myss_rx: len 1024, flags: 1 -[2019/08/12 07:16:13:4243] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4245] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4246] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4247] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4252] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4264] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4265] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4266] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4267] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4269] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4270] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4278] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4279] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4280] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4281] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4282] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4284] USR: myss_rx: len 1015, flags: 0 -[2019/08/12 07:16:13:4287] USR: myss_rx: len 1024, flags: 0 -[2019/08/12 07:16:13:4288] USR: myss_rx: len 947, flags: 0 -[2019/08/12 07:16:13:4293] USR: myss_rx: len 0, flags: 2 -[2019/08/12 07:16:13:4399] USR: myss_state: LWSSSCS_DISCONNECTED, ord 0x0 -[2019/08/12 07:16:13:4761] USR: myss_state: LWSSSCS_DESTROYING, ord 0x0 -[2019/08/12 07:16:13:4781] USR: Completed: OK -``` diff -Nru libwebsockets-4.0.20/minimal-examples/secure-streams/README.md libwebsockets-2.4.2/minimal-examples/secure-streams/README.md --- libwebsockets-4.0.20/minimal-examples/secure-streams/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/secure-streams/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -# Secure Streams - -Secure Streams is a client API that strictly decouples the policy for connections -from the payloads. The user code only deals with the stream type name and payloads, -a policy database set at `lws_context` creation time decides all policy about the -connection, including the endpoint, tls CA, and even the wire protocol. - -|name|demonstrates| ----|--- -minimal-secure-streams|Minimal secure streams client / proxy example -minimal-secure-streams-tx|Proxy used for client-tx test below -minimal-secure-streams-client-tx|Secure streams client showing tx and rx - - diff -Nru libwebsockets-4.0.20/minimal-examples/selftests-library.sh libwebsockets-2.4.2/minimal-examples/selftests-library.sh --- libwebsockets-4.0.20/minimal-examples/selftests-library.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/selftests-library.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,110 +0,0 @@ -#!/bin/bash - -if [ -z "$1" -o -z "$2" ] ; then - echo "required args missing" - exit 1 -fi - -IDX=$3 -TOT=$4 -MYTEST=`echo $0 | sed "s/\/[^\/]*\$//g" |sed "s/.*\///g"` -mkdir -p $2/$MYTEST -rm -f $2/$MYTEST/*.log $2/$MYTEST/*.result -FAILS=0 -WHICH=$IDX -SPID= -SCRIPT_DIR=`dirname $0` -SCRIPT_DIR=`readlink -f $SCRIPT_DIR` -LOGPATH=$2 - -feedback() { - if [ "$2" != "$4" ] ; then - FAILS=$(( $FAILS + 1 )) - echo -n -e "\e[31m" - fi - T=" --- killed --- " - if [ ! -z "`cat $LOGPATH/$MYTEST/$3.time`" ] ; then - T="`cat $LOGPATH/$MYTEST/$3.time | grep real | sed "s/.*\ //g"`" - T="$T `cat $LOGPATH/$MYTEST/$3.time | grep user | sed "s/.*\ //g"`" - T="$T `cat $LOGPATH/$MYTEST/$3.time | grep sys | sed "s/.*\ //g"`" - fi - printf "%-35s [ %3s/%3s ]: %3s : %8s : %s\n" $1 $WHICH $TOT $2 "$T" $3 - if [ "$2" != "0" ] ; then - echo -n -e "\e[0m" - fi - WHICH=$(( $WHICH + 1)) -} - -spawn() { - if [ ! -z "$1" ] ; then - if [ `ps $1 | wc -l` -eq 2 ]; then -# echo "prerequisite still up" - return 0 - fi - fi - - QQ=`pwd` - cd $SCRIPT_DIR - cd $2 - $3 $4 $5 > $LOGPATH/$MYTEST/serverside.log 2> $LOGPATH/$MYTEST/serverside.log & - SPID=$! - cd $QQ - sleep 0.5s -# echo "launched prerequisite $SPID" -} - -_dotest() { - EXPRES=0 - if [ ! -z "$4" ] ; then - EXPRES=$4 - fi - T=$3 -# echo "$1/lws-$MYTEST $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14}" - ( - { - /usr/bin/time -p /usr/bin/valgrind -q $1/lws-$MYTEST $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} > $2/$MYTEST/$T.log 2> $2/$MYTEST/$T.log ; - echo $? > $2/$MYTEST/$T.result - } 2> $2/$MYTEST/$T.time >/dev/null - ) >/dev/null 2> /dev/null & - W=$! - WT=0 - while [ $WT -le 820 ] ; do - kill -0 $W 2>/dev/null - if [ $? -ne 0 ] ; then - WT=10000 - else - if [ $WT -ge 800 ] ; then - WT=10000 - kill $W 2>/dev/null - wait $W 2>/dev/null - fi - fi - sleep 0.1s - WT=$(( $WT + 1 )) - done - - R=254 - if [ -e $2/$MYTEST/$T.result ] ; then - R=`cat $2/$MYTEST/$T.result` - cat $2/$MYTEST/$T.log | tail -n 3 > $2/$MYTEST/$T.time - if [ $R -ne $EXPRES ] ; then - pwd - echo Expected result $EXPRES but got $R - echo - cat $2/$MYTEST/$T.log - echo - fi - fi - - feedback $MYTEST $R $T $EXPRES -} - -dotest() -{ - _dotest $1 $2 $3 0 "$4" "$5" "$6" "$7" "$8" "$9" "${10}" "${11}" "${12}" "${13}" -} - -dofailtest() -{ - _dotest $1 $2 $3 1 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} -} diff -Nru libwebsockets-4.0.20/minimal-examples/selftests.sh libwebsockets-2.4.2/minimal-examples/selftests.sh --- libwebsockets-4.0.20/minimal-examples/selftests.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/selftests.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -#!/bin/bash -# -# run this from your build dir having configured -# -DLWS_WITH_MINIMAL_EXAMPLES=1 to get all the examples -# that apply built into ./bin -# -# Eg, -# -# build $ ../minimal-examples/selftests.sh - -echo -echo "----------------------------------------------" -echo "------- tests: lws minimal example selftests" -echo - -LOGGING_PATH=/tmp/logs - -# for mebedtls, we need the CA certs in ./build where we run from - -cp ../minimal-examples/http-client/minimal-http-client-multi/warmcat.com.cer . -cp ../minimal-examples/http-client/minimal-http-client-post/libwebsockets.org.cer . - -MINEX=`dirname $0` -MINEX=`realpath $MINEX` -TESTS=0 -for i in `find $MINEX -name selftest.sh` ; do - BN=`echo -n "$i" | sed "s/\/[^\/]*\$//g" | sed "s/.*\///g"` - if [ -e `pwd`/bin/lws-$BN ] ; then - C=`cat $i | grep COUNT_TESTS= | cut -d= -f2` - TESTS=$(( $TESTS + $C )) - fi -done - -FAILS=0 -WH=1 - -for i in `find $MINEX -name selftest.sh` ; do - BN=`echo -n "$i" | sed "s/\/[^\/]*\$//g" | sed "s/.*\///g"` - if [ -e `pwd`/bin/lws-$BN ] ; then - C=`cat $i | grep COUNT_TESTS= | cut -d= -f2` - sh $i `pwd`/bin $LOGGING_PATH $WH $TESTS $MINEX - FAILS=$(( $FAILS + $? )) - - L=`ps fax | grep lws- | cut -d' ' -f2` - kill $L 2>/dev/null - kill -9 $L 2>/dev/null - wait $L 2>/dev/null - - WH=$(( $WH + $C )) - fi -done - -if [ $FAILS -eq 0 ] ; then - echo "All $TESTS passed" - exit 0 -else - echo "Failed: $FAILS / $TESTS" - exit 1 -fi - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-ws-client-ping) -cmake_minimum_required(VERSION 2.8) -include(CheckIncludeFile) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-client) -set(SRCS minimal-ws-client.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client/libwebsockets.org.cer libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client/libwebsockets.org.cer --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client/libwebsockets.org.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client/libwebsockets.org.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFWjCCBEKgAwIBAgISA9x0/oj5PLdW46hsmR82/7ytMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5NDBaFw0x -OTEyMDYwNzA5NDBaMBwxGjAYBgNVBAMTEWxpYndlYnNvY2tldHMub3JnMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPinIkleLmvEcA/YuBss6ASXVi7g -yr6Sss7cB3vTy7Fp8OB2c1N25prHZxVpORAUo0UreiaY2Ws4NFvDaYp08ZffevuC -UhThsEJlbkD0uvt7dPapJt9PNJtlxjNFWyvHEy6PijzIaMYDROiStcCJQn7kAew/ -Za2+5kNVgKqT+7OXukJEFdSdVZI6QC/npeQlkIrFSq1WVthCGBNJehxxES0hSWzk -0gNVKlkD3/SbkupsfUpe73XiawMtrtsSE7cdnul7VZmiP8I/3sJr1+4/3xZ+DEYg -mVB82B0vd08VJYzU7Nf0pz0PWusAmzRoRn81IXkOfBg9ohlSSEoZhHYS7QIDAQAB -o4ICZjCCAmIwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr -BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRmKKyGjufWgp7pR2x0tWxG -D9G+WTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcB -AQRjMGEwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlw -dC5vcmcwLwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlw -dC5vcmcvMBwGA1UdEQQVMBOCEWxpYndlYnNvY2tldHMub3JnMEwGA1UdIARFMEMw -CAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9j -cHMubGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcAdH7a -gzGtMxCRIZzOJU9CcMK//V5CIAjGNzV55hB7zFYAAAFtCsWIfgAABAMASDBGAiEA -0H55VqSKV3otHK7uHNbcR0QwoUYtCmeObhsqxzCnmDwCIQD3mtuSKrxTD3oA+Yde -nmTgWfFyS4TNgLNEPCJYo2s75gB1ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM -9OVFR/R4AAABbQrFil4AAAQDAEYwRAIgNSpvz/1JA2aP6fh6ujGNuYfrAvWjlxXo -CJtVGe4XaDYCIGmK1/9tl1uQbVD46P5NswnULq06KQmuOrlI3HO4r86HMA0GCSqG -SIb3DQEBCwUAA4IBAQBiAlV7wkCsWE99VmZHBmcbZChWyWUHG3LM1hnaQRQjTSYk -CIlauCpWzlUd6weuvra85KqBbCYo+1hxbwITI796uAdgtHmBE8nj0VltHwKeSq2s -KKiGXBRT7Z7t0VHYSLOlGOVn1auuQFaWBArc0cQ/m1ZsoHvOiHTlKQvVsA4HnIxA -CjGY9OOQoh0c36ecbJZ44XKnU9J/OXtDx00aW6QodaZmgMp/OOCghFQUvufkgTUL -LZid873/8dJVWjAaj1VdadO1nSbdAfBbeWXy93+vg1aAoig80RoscrzYCaNlwmR7 -EO5zWxL3l+xUZogQSJuICgUgNzVB3wjn8HeHGsqt ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client/minimal-ws-client.c libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client/minimal-ws-client.c --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client/minimal-ws-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client/minimal-ws-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,214 +0,0 @@ -/* - * lws-minimal-ws-client - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a ws client that connects by default to libwebsockets.org - * dumb increment ws server. - */ - -#include -#include -#include -#include - -/* - * This represents your object that "contains" the client connection and has - * the client connection bound to it - */ - -static struct my_conn { - lws_sorted_usec_list_t sul; /* schedule connection retry */ - struct lws *wsi; /* related wsi if any */ - uint16_t retry_count; /* count of consequetive retries */ -} mco; - -static struct lws_context *context; -static int interrupted, port = 443, ssl_connection = LCCSCF_USE_SSL; -static const char *server_address = "libwebsockets.org", - *pro = "dumb-increment-protocol"; - -/* - * The retry and backoff policy we want to use for our client connections - */ - -static const uint32_t backoff_ms[] = { 1000, 2000, 3000, 4000, 5000 }; - -static const lws_retry_bo_t retry = { - .retry_ms_table = backoff_ms, - .retry_ms_table_count = LWS_ARRAY_SIZE(backoff_ms), - .conceal_count = LWS_ARRAY_SIZE(backoff_ms), - - .secs_since_valid_ping = 3, /* force PINGs after secs idle */ - .secs_since_valid_hangup = 10, /* hangup after secs idle */ - - .jitter_percent = 20, -}; - -/* - * Scheduled sul callback that starts the connection attempt - */ - -static void -connect_client(lws_sorted_usec_list_t *sul) -{ - struct my_conn *mco = lws_container_of(sul, struct my_conn, sul); - struct lws_client_connect_info i; - - memset(&i, 0, sizeof(i)); - - i.context = context; - i.port = port; - i.address = server_address; - i.path = "/"; - i.host = i.address; - i.origin = i.address; - i.ssl_connection = ssl_connection; - i.protocol = pro; - i.local_protocol_name = "lws-minimal-client"; - i.pwsi = &mco->wsi; - i.retry_and_idle_policy = &retry; - i.userdata = mco; - - if (!lws_client_connect_via_info(&i)) - /* - * Failed... schedule a retry... we can't use the _retry_wsi() - * convenience wrapper api here because no valid wsi at this - * point. - */ - if (lws_retry_sul_schedule(context, 0, sul, &retry, - connect_client, &mco->retry_count)) { - lwsl_err("%s: connection attempts exhausted\n", __func__); - interrupted = 1; - } -} - -static int -callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct my_conn *mco = (struct my_conn *)user; - - switch (reason) { - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - goto do_retry; - break; - - case LWS_CALLBACK_CLIENT_RECEIVE: - lwsl_hexdump_notice(in, len); - break; - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - lwsl_user("%s: established\n", __func__); - break; - - case LWS_CALLBACK_CLIENT_CLOSED: - goto do_retry; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); - -do_retry: - /* - * retry the connection to keep it nailed up - * - * For this example, we try to conceal any problem for one set of - * backoff retries and then exit the app. - * - * If you set retry.conceal_count to be larger than the number of - * elements in the backoff table, it will never give up and keep - * retrying at the last backoff delay plus the random jitter amount. - */ - if (lws_retry_sul_schedule_retry_wsi(wsi, &mco->sul, connect_client, - &mco->retry_count)) { - lwsl_err("%s: connection attempts exhausted\n", __func__); - interrupted = 1; - } - - return 0; -} - -static const struct lws_protocols protocols[] = { - { "lws-minimal-client", callback_minimal, 0, 0, }, - { NULL, NULL, 0, 0 } -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - const char *p; - int n = 0; - - signal(SIGINT, sigint_handler); - memset(&info, 0, sizeof info); - lws_cmdline_option_handle_builtin(argc, argv, &info); - - lwsl_user("LWS minimal ws client\n"); - - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./libwebsockets.org.cer"; -#endif - - if ((p = lws_cmdline_option(argc, argv, "--protocol"))) - pro = p; - - if ((p = lws_cmdline_option(argc, argv, "-s"))) - server_address = p; - - if ((p = lws_cmdline_option(argc, argv, "-p"))) - port = atoi(p); - - if (lws_cmdline_option(argc, argv, "-j")) - ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - - if (lws_cmdline_option(argc, argv, "-k")) - ssl_connection |= LCCSCF_ALLOW_INSECURE; - - if (lws_cmdline_option(argc, argv, "-m")) - ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK; - - if (lws_cmdline_option(argc, argv, "-e")) - ssl_connection |= LCCSCF_ALLOW_EXPIRED; - - info.fd_limit_per_thread = 1 + 1 + 1; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* schedule the first client connection attempt to happen immediately */ - lws_sul_schedule(context, 0, &mco.sul, connect_client, 1); - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - lwsl_user("Completed\n"); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client/README.md libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client/README.md --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -# lws minimal ws client - -This connects to libwebsockets.org using the dumb-increment-protocol. - -It demonstrates how to use the connection retry and backoff stuff in lws. - -## build - -``` - $ cmake . && make -``` - -## Commandline Options - -Option|Meaning ----|--- --d|Set logging verbosity --s|Use a specific server instead of libwebsockets.org, eg `--server localhost`. Implies LCCSCF_ALLOW_SELFSIGNED --p|Use a specific port instead of 443, eg `--port 7681` --j|Allow selfsigned tls cert --k|Allow insecure certs --m|Skip server hostname check --e|Allow expired certs ---protocol|Use a specific ws subprotocol rather than dumb-increment-protocol, eg, `--protocol myprotocol` - - -## usage - -Just run it, it will connect to libwebsockets.org and spew incrementing numbers -sent by the server at 20Hz - -``` - $ ./lws-minimal-ws-client -[2020/01/22 05:38:47:3409] U: LWS minimal ws client -[2020/01/22 05:38:47:4456] N: Loading client CA for verification ./libwebsockets.org.cer -[2020/01/22 05:38:48:1649] U: callback_minimal: established -[2020/01/22 05:38:48:1739] N: -[2020/01/22 05:38:48:1763] N: 0000: 30 0 -[2020/01/22 05:38:48:1765] N: - -... -``` - -To test against the lws test server instead of libwebsockets.org, run the test -server as - -``` -$ libwebsockets-test-server -s -``` - -and run this test app with - -``` -$ ./lws-minimal-ws-client -s localhost -p 7681 -j -``` - -You can kill and restart the server to confirm the client connection is re- -established if done within the backoff period. diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-echo/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-echo/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-echo/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-echo/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-ws-client-echo) -cmake_minimum_required(VERSION 2.8.9) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-client-echo) -set(SRCS minimal-ws-client-echo.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) -require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-echo/minimal-ws-client-echo.c libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-echo/minimal-ws-client-echo.c --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-echo/minimal-ws-client-echo.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-echo/minimal-ws-client-echo.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,172 +0,0 @@ -/* - * lws-minimal-ws-client-echo - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a ws client that echoes back what it was sent, in a - * way compatible with autobahn -m fuzzingserver - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal_client_echo.c" - -static struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_CLIENT_ECHO, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static struct lws_context *context; -static int interrupted, port = 7681, options = 0; -static const char *url = "/", *ads = "localhost", *iface = NULL; - -/* pass pointers to shared vars to the protocol */ - -static const struct lws_protocol_vhost_options pvo_iface = { - NULL, - NULL, - "iface", /* pvo name */ - (void *)&iface /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo_ads = { - &pvo_iface, - NULL, - "ads", /* pvo name */ - (void *)&ads /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo_url = { - &pvo_ads, - NULL, - "url", /* pvo name */ - (void *)&url /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo_options = { - &pvo_url, - NULL, - "options", /* pvo name */ - (void *)&options /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo_port = { - &pvo_options, - NULL, - "port", /* pvo name */ - (void *)&port /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo_interrupted = { - &pvo_port, - NULL, - "interrupted", /* pvo name */ - (void *)&interrupted /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo_interrupted, /* "child" pvo linked-list */ - "lws-minimal-client-echo", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; -static const struct lws_extension extensions[] = { - { - "permessage-deflate", - lws_extension_callback_pm_deflate, - "permessage-deflate" - "; client_no_context_takeover" - "; client_max_window_bits" - }, - { NULL, NULL, NULL /* terminator */ } -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - const char *p; - int n, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws client echo + permessage-deflate + multifragment bulk message\n"); - lwsl_user(" lws-minimal-ws-client-echo [-n (no exts)] [-u url] [-p port] [-o (once)]\n"); - - if ((p = lws_cmdline_option(argc, argv, "-u"))) - url = p; - - if ((p = lws_cmdline_option(argc, argv, "-p"))) - port = atoi(p); - - if (lws_cmdline_option(argc, argv, "-o")) - options |= 1; - - if (lws_cmdline_option(argc, argv, "--ssl")) - options |= 2; - - if ((p = lws_cmdline_option(argc, argv, "-s"))) - ads = p; - - if ((p = lws_cmdline_option(argc, argv, "-i"))) - iface = p; - - lwsl_user("options %d, ads %s\n", options, ads); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.protocols = protocols; - info.pvo = &pvo; - if (!lws_cmdline_option(argc, argv, "-n")) - info.extensions = extensions; - info.pt_serv_buf_size = 32 * 1024; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_VALIDATE_UTF8; - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + 1 + 1; - - if (lws_cmdline_option(argc, argv, "--libuv")) - info.options |= LWS_SERVER_OPTION_LIBUV; - else - signal(SIGINT, sigint_handler); - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (!lws_service(context, 0) && !interrupted) - ; - - lws_context_destroy(context); - - n = (options & 1) ? interrupted != 2 : interrupted == 3; - lwsl_user("Completed %d %s\n", interrupted, !n ? "OK" : "failed"); - - return n; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,328 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal-client-echo" - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * The protocol shows how to send and receive bulk messages over a ws connection - * that optionally may have the permessage-deflate extension negotiated on it. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -#define RING_DEPTH 1024 - -/* one of these created for each message */ - -struct msg { - void *payload; /* is malloc'd */ - size_t len; - char binary; - char first; - char final; -}; - -struct per_session_data__minimal_client_echo { - struct lws_ring *ring; - uint32_t tail; - char flow_controlled; - uint8_t completed:1; - uint8_t write_consume_pending:1; -}; - -struct vhd_minimal_client_echo { - struct lws_context *context; - struct lws_vhost *vhost; - struct lws *client_wsi; - - int *interrupted; - int *options; - const char **url; - const char **ads; - const char **iface; - int *port; -}; - -static int -connect_client(struct vhd_minimal_client_echo *vhd) -{ - struct lws_client_connect_info i; - char host[128]; - - lws_snprintf(host, sizeof(host), "%s:%u", *vhd->ads, *vhd->port); - - memset(&i, 0, sizeof(i)); - - i.context = vhd->context; - i.port = *vhd->port; - i.address = *vhd->ads; - i.path = *vhd->url; - i.host = host; - i.origin = host; - i.ssl_connection = 0; - if ((*vhd->options) & 2) - i.ssl_connection |= LCCSCF_USE_SSL; - i.vhost = vhd->vhost; - i.iface = *vhd->iface; - //i.protocol = ; - i.pwsi = &vhd->client_wsi; - - lwsl_user("connecting to %s:%d/%s\n", i.address, i.port, i.path); - - return !lws_client_connect_via_info(&i); -} - -static void -__minimal_destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} - -static void -schedule_callback(struct lws *wsi, int reason, int secs) -{ - lws_timed_callback_vh_protocol(lws_get_vhost(wsi), - lws_get_protocol(wsi), reason, secs); -} - -static int -callback_minimal_client_echo(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal_client_echo *pss = - (struct per_session_data__minimal_client_echo *)user; - struct vhd_minimal_client_echo *vhd = (struct vhd_minimal_client_echo *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - const struct msg *pmsg; - struct msg amsg; - int n, m, flags; - - switch (reason) { - - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct vhd_minimal_client_echo)); - if (!vhd) - return -1; - - vhd->context = lws_get_context(wsi); - vhd->vhost = lws_get_vhost(wsi); - - /* get the pointer to "interrupted" we were passed in pvo */ - vhd->interrupted = (int *)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "interrupted")->value; - vhd->port = (int *)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "port")->value; - vhd->options = (int *)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "options")->value; - vhd->ads = (const char **)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "ads")->value; - vhd->url = (const char **)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "url")->value; - vhd->iface = (const char **)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "iface")->value; - - if (connect_client(vhd)) - schedule_callback(wsi, LWS_CALLBACK_USER, 1); - break; - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - lwsl_user("LWS_CALLBACK_CLIENT_ESTABLISHED\n"); - pss->ring = lws_ring_create(sizeof(struct msg), RING_DEPTH, - __minimal_destroy_message); - if (!pss->ring) - return 1; - pss->tail = 0; - break; - - case LWS_CALLBACK_CLIENT_WRITEABLE: - - lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE\n"); - - if (pss->write_consume_pending) { - /* perform the deferred fifo consume */ - lws_ring_consume_single_tail(pss->ring, &pss->tail, 1); - pss->write_consume_pending = 0; - } - pmsg = lws_ring_get_element(pss->ring, &pss->tail); - if (!pmsg) { - lwsl_user(" (nothing in ring)\n"); - break; - } - - flags = lws_write_ws_flags( - pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT, - pmsg->first, pmsg->final); - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, ((unsigned char *)pmsg->payload) + - LWS_PRE, pmsg->len, flags); - if (m < (int)pmsg->len) { - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - lwsl_user(" wrote %d: flags: 0x%x first: %d final %d\n", - m, flags, pmsg->first, pmsg->final); - - if ((*vhd->options & 1) && pmsg && pmsg->final) - pss->completed = 1; - - /* - * Workaround deferred deflate in pmd extension by only - * consuming the fifo entry when we are certain it has been - * fully deflated at the next WRITABLE callback. You only need - * this if you're using pmd. - */ - pss->write_consume_pending = 1; - lws_callback_on_writable(wsi); - - if (pss->flow_controlled && - (int)lws_ring_get_count_free_elements(pss->ring) > RING_DEPTH - 5) { - lws_rx_flow_control(wsi, 1); - pss->flow_controlled = 0; - } - - break; - - case LWS_CALLBACK_CLIENT_RECEIVE: - - lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %4d (rpp %5d, first %d, last %d, bin %d)\n", - (int)len, (int)lws_remaining_packet_payload(wsi), - lws_is_first_fragment(wsi), - lws_is_final_fragment(wsi), - lws_frame_is_binary(wsi)); - - // lwsl_hexdump_notice(in, len); - - amsg.first = lws_is_first_fragment(wsi); - amsg.final = lws_is_final_fragment(wsi); - amsg.binary = lws_frame_is_binary(wsi); - n = (int)lws_ring_get_count_free_elements(pss->ring); - if (!n) { - lwsl_user("dropping!\n"); - break; - } - - amsg.len = len; - /* notice we over-allocate by LWS_PRE */ - amsg.payload = malloc(LWS_PRE + len); - if (!amsg.payload) { - lwsl_user("OOM: dropping\n"); - break; - } - - memcpy((char *)amsg.payload + LWS_PRE, in, len); - if (!lws_ring_insert(pss->ring, &amsg, 1)) { - __minimal_destroy_message(&amsg); - lwsl_user("dropping!\n"); - break; - } - lws_callback_on_writable(wsi); - - if (!pss->flow_controlled && n < 3) { - pss->flow_controlled = 1; - lws_rx_flow_control(wsi, 0); - } - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - vhd->client_wsi = NULL; - //schedule_callback(wsi, LWS_CALLBACK_USER, 1); - //if (*vhd->options & 1) { - if (!*vhd->interrupted) - *vhd->interrupted = 3; - lws_cancel_service(lws_get_context(wsi)); - //} - break; - - case LWS_CALLBACK_CLIENT_CLOSED: - lwsl_user("LWS_CALLBACK_CLIENT_CLOSED\n"); - lws_ring_destroy(pss->ring); - vhd->client_wsi = NULL; - // schedule_callback(wsi, LWS_CALLBACK_USER, 1); - //if (*vhd->options & 1) { - if (!*vhd->interrupted) - *vhd->interrupted = 1 + pss->completed; - lws_cancel_service(lws_get_context(wsi)); - // } - break; - - /* rate-limited client connect retries */ - - case LWS_CALLBACK_USER: - lwsl_notice("%s: LWS_CALLBACK_USER\n", __func__); - if (connect_client(vhd)) - schedule_callback(wsi, LWS_CALLBACK_USER, 1); - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL_CLIENT_ECHO \ - { \ - "lws-minimal-client-echo", \ - callback_minimal_client_echo, \ - sizeof(struct per_session_data__minimal_client_echo), \ - 1024, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_CLIENT_ECHO -}; - -int -init_protocol_minimal_client_echo(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal_client_echo(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-echo/README.md libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-echo/README.md --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-echo/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-echo/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -# lws minimal ws client + permessage-deflate echo - -This example opens a ws client connection to localhost:7681 and -echoes back anything that comes from the server. - -You can use it for testing lws against Autobahn. - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --p port|Port to connect to --u url|URL path part to connect to --o|Finish after one connection ---ssl|Open client connection with ssl --i |Bind the client connection to interface iface - -``` - $ ./lws-minimal-ws-client-echo -[2018/04/22 20:03:50:2343] USER: LWS minimal ws client echo + permessage-deflate + multifragment bulk message -[2018/04/22 20:03:50:2344] USER: lws-minimal-ws-client-echo [-n (no exts)] [-u url] [-o (once)] -[2018/04/22 20:03:50:2344] USER: options 0 -[2018/04/22 20:03:50:2345] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off -[2018/04/22 20:03:51:2356] USER: connecting to localhost:9001//runCase?case=362&agent=libwebsockets -[2018/04/22 20:03:51:2385] NOTICE: checking client ext permessage-deflate -[2018/04/22 20:03:51:2386] NOTICE: instantiating client ext permessage-deflate -[2018/04/22 20:03:51:2386] USER: LWS_CALLBACK_CLIENT_ESTABLISHED -... -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-ping/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-ping/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-ping/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-ping/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -project(lws-minimal-ws-client-ping) -cmake_minimum_required(VERSION 2.8) -include(CheckIncludeFile) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-client-ping) -set(SRCS minimal-ws-client-ping.c) - -MACRO(require_pthreads result) - CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) - if (NOT LWS_HAVE_PTHREAD_H) - if (LWS_WITH_MINIMAL_EXAMPLES) - set(result 0) - else() - message(FATAL_ERROR "threading support requires pthreads") - endif() - endif() -ENDMACRO() -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_pthreads(requirements) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared pthread) - add_dependencies(${SAMP} websockets_shared pthread) - else() - target_link_libraries(${SAMP} websockets pthread) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-ping/libwebsockets.org.cer libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-ping/libwebsockets.org.cer --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-ping/libwebsockets.org.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-ping/libwebsockets.org.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFWjCCBEKgAwIBAgISA9x0/oj5PLdW46hsmR82/7ytMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5NDBaFw0x -OTEyMDYwNzA5NDBaMBwxGjAYBgNVBAMTEWxpYndlYnNvY2tldHMub3JnMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPinIkleLmvEcA/YuBss6ASXVi7g -yr6Sss7cB3vTy7Fp8OB2c1N25prHZxVpORAUo0UreiaY2Ws4NFvDaYp08ZffevuC -UhThsEJlbkD0uvt7dPapJt9PNJtlxjNFWyvHEy6PijzIaMYDROiStcCJQn7kAew/ -Za2+5kNVgKqT+7OXukJEFdSdVZI6QC/npeQlkIrFSq1WVthCGBNJehxxES0hSWzk -0gNVKlkD3/SbkupsfUpe73XiawMtrtsSE7cdnul7VZmiP8I/3sJr1+4/3xZ+DEYg -mVB82B0vd08VJYzU7Nf0pz0PWusAmzRoRn81IXkOfBg9ohlSSEoZhHYS7QIDAQAB -o4ICZjCCAmIwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr -BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRmKKyGjufWgp7pR2x0tWxG -D9G+WTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcB -AQRjMGEwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlw -dC5vcmcwLwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlw -dC5vcmcvMBwGA1UdEQQVMBOCEWxpYndlYnNvY2tldHMub3JnMEwGA1UdIARFMEMw -CAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9j -cHMubGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcAdH7a -gzGtMxCRIZzOJU9CcMK//V5CIAjGNzV55hB7zFYAAAFtCsWIfgAABAMASDBGAiEA -0H55VqSKV3otHK7uHNbcR0QwoUYtCmeObhsqxzCnmDwCIQD3mtuSKrxTD3oA+Yde -nmTgWfFyS4TNgLNEPCJYo2s75gB1ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM -9OVFR/R4AAABbQrFil4AAAQDAEYwRAIgNSpvz/1JA2aP6fh6ujGNuYfrAvWjlxXo -CJtVGe4XaDYCIGmK1/9tl1uQbVD46P5NswnULq06KQmuOrlI3HO4r86HMA0GCSqG -SIb3DQEBCwUAA4IBAQBiAlV7wkCsWE99VmZHBmcbZChWyWUHG3LM1hnaQRQjTSYk -CIlauCpWzlUd6weuvra85KqBbCYo+1hxbwITI796uAdgtHmBE8nj0VltHwKeSq2s -KKiGXBRT7Z7t0VHYSLOlGOVn1auuQFaWBArc0cQ/m1ZsoHvOiHTlKQvVsA4HnIxA -CjGY9OOQoh0c36ecbJZ44XKnU9J/OXtDx00aW6QodaZmgMp/OOCghFQUvufkgTUL -LZid873/8dJVWjAaj1VdadO1nSbdAfBbeWXy93+vg1aAoig80RoscrzYCaNlwmR7 -EO5zWxL3l+xUZogQSJuICgUgNzVB3wjn8HeHGsqt ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-ping/minimal-ws-client-ping.c libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-ping/minimal-ws-client-ping.c --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-ping/minimal-ws-client-ping.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-ping/minimal-ws-client-ping.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,157 +0,0 @@ -/* - * lws-minimal-ws-client-ping - * - * Written in 2010-2020 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates keeping a ws connection validated by the lws validity - * timer stuff without having to do anything in the code. Use debug logging - * -d1039 to see lws doing the pings / pongs in the background. - */ - -#include -#include -#include -#include - -static struct lws_context *context; -static struct lws *client_wsi; -static int interrupted, port = 443, ssl_connection = LCCSCF_USE_SSL; -static const char *server_address = "libwebsockets.org", *pro = "lws-mirror-protocol"; -static lws_sorted_usec_list_t sul; - -static const lws_retry_bo_t retry = { - .secs_since_valid_ping = 3, - .secs_since_valid_hangup = 10, -}; - -static void -connect_cb(lws_sorted_usec_list_t *_sul) -{ - struct lws_client_connect_info i; - - lwsl_notice("%s: connecting\n", __func__); - - memset(&i, 0, sizeof(i)); - - i.context = context; - i.port = port; - i.address = server_address; - i.path = "/"; - i.host = i.address; - i.origin = i.address; - i.ssl_connection = ssl_connection; - i.protocol = pro; - i.alpn = "h2;http/1.1"; - i.local_protocol_name = "lws-ping-test"; - i.pwsi = &client_wsi; - i.retry_and_idle_policy = &retry; - - if (!lws_client_connect_via_info(&i)) - lws_sul_schedule(context, 0, _sul, connect_cb, 5 * LWS_USEC_PER_SEC); -} - -static int -callback_minimal_pingtest(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - - switch (reason) { - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - lws_sul_schedule(context, 0, &sul, connect_cb, 5 * LWS_USEC_PER_SEC); - break; - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - lwsl_user("%s: established\n", __func__); - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "lws-ping-test", - callback_minimal_pingtest, - 0, - 0, - }, - { NULL, NULL, 0, 0 } -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws client PING\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./libwebsockets.org.cer"; -#endif - - if ((p = lws_cmdline_option(argc, argv, "--protocol"))) - pro = p; - - if ((p = lws_cmdline_option(argc, argv, "--server"))) { - server_address = p; - pro = "lws-minimal"; - ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - } - - if ((p = lws_cmdline_option(argc, argv, "--port"))) - port = atoi(p); - - info.fd_limit_per_thread = 1 + 1 + 1; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - lws_sul_schedule(context, 0, &sul, connect_cb, 100); - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - lwsl_user("Completed\n"); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-ping/README.md libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-ping/README.md --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-ping/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-ping/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,117 +0,0 @@ -# lws minimal ws client PING - -This connects to libwebsockets.org using the lws-mirror-protocol. - -It sets a validity regime of testing validity with PING every 3s and failing -if it didn't get the PONG back within 10s. - -## build - -``` - $ cmake . && make -``` - -## Commandline Options - -Option|Meaning ----|--- --d|Set logging verbosity (you want 1039 to see the validity ping / pong) ---server|Use a specific server instead of libwebsockets.org, eg `--server localhost`. Implies LCCSCF_ALLOW_SELFSIGNED ---port|Use a specific port instead of 443, eg `--port 7681` ---protocol|Use a specific ws subprotocol rather than lws-mirror-protocol, eg, `--protocol myprotocol` - - -## usage - -Just run it, wait for the connect and then there will be PINGs sent -at 5s intervals. - -``` - $ ./lws-minimal-ws-client-ping -d1039 -[2020/03/18 13:13:47:1114] U: LWS minimal ws client PING -[2020/03/18 13:13:47:1503] I: Initial logging level 1039 -[2020/03/18 13:13:47:1507] I: Libwebsockets version: 4.0.99 v4.0.0-20-gc6165f868 -[2020/03/18 13:13:47:1508] I: IPV6 not compiled in -[2020/03/18 13:13:47:1512] I: LWS_DEF_HEADER_LEN : 4096 -[2020/03/18 13:13:47:1514] I: LWS_MAX_SMP : 1 -[2020/03/18 13:13:47:1519] I: sizeof (*info) : 720 -[2020/03/18 13:13:47:1520] I: SYSTEM_RANDOM_FILEPATH: '/dev/urandom' -[2020/03/18 13:13:47:1522] I: HTTP2 support : available -[2020/03/18 13:13:47:1552] N: lws_create_context: using ss proxy bind '(null)', port 0, ads '(null)' -[2020/03/18 13:13:47:1557] I: context created -[2020/03/18 13:13:47:1575] I: Using event loop: poll -[2020/03/18 13:13:47:1583] I: Default ALPN advertisment: h2,http/1.1 -[2020/03/18 13:13:47:1585] I: default timeout (secs): 20 -[2020/03/18 13:13:47:1614] I: Threads: 1 each 5 fds -[2020/03/18 13:13:47:1623] I: mem: context: 8152 B (4056 ctx + (1 thr x 4096)) -[2020/03/18 13:13:47:1625] I: mem: http hdr size: (4096 + 976), max count 5 -[2020/03/18 13:13:47:1629] I: mem: pollfd map: 40 B -[2020/03/18 13:13:47:1633] I: mem: platform fd map: 40 B -[2020/03/18 13:13:47:1692] I: Compiled with OpenSSL support -[2020/03/18 13:13:47:1695] I: Doing SSL library init -[2020/03/18 13:13:47:3103] I: canonical_hostname = constance -[2020/03/18 13:13:47:3140] I: Creating Vhost 'default' (serving disabled), 4 protocols, IPv6 off -[2020/03/18 13:13:47:4072] I: lws_tls_client_create_vhost_context: vh default: created new client ctx 0 -[2020/03/18 13:13:47:7468] I: created client ssl context for default -[2020/03/18 13:13:47:7482] I: Creating Vhost 'default' (serving disabled), 4 protocols, IPv6 off -[2020/03/18 13:13:47:7490] I: lws_tls_client_create_vhost_context: vh default: reusing client ctx 0: use 2 -[2020/03/18 13:13:47:7491] I: created client ssl context for default -[2020/03/18 13:13:47:7494] I: mem: per-conn: 792 bytes + protocol rx buf -[2020/03/18 13:13:47:7497] I: lws_plat_drop_app_privileges: not changing group -[2020/03/18 13:13:47:7499] I: lws_plat_drop_app_privileges: not changing user -[2020/03/18 13:13:47:7512] I: lws_cancel_service -[2020/03/18 13:13:47:7568] I: lws_state_notify_protocol_init: LWS_SYSTATE_CPD_PRE_TIME -[2020/03/18 13:13:47:7577] N: lws_ss_create: unknown stream type captive_portal_detect -[2020/03/18 13:13:47:7580] I: lws_ss_sys_cpd: Create stream failed (policy?) -[2020/03/18 13:13:47:7582] I: lws_state_notify_protocol_init: LWS_SYSTATE_CPD_PRE_TIME -[2020/03/18 13:13:47:7582] N: lws_ss_create: unknown stream type captive_portal_detect -[2020/03/18 13:13:47:7583] I: lws_ss_sys_cpd: Create stream failed (policy?) -[2020/03/18 13:13:47:7585] I: lws_state_notify_protocol_init: doing protocol init on POLICY_VALID -[2020/03/18 13:13:47:7588] I: lws_protocol_init -[2020/03/18 13:13:47:7623] I: lws_state_transition_steps: CONTEXT_CREATED -> OPERATIONAL -[2020/03/18 13:13:47:7628] N: connect_cb: connecting -[2020/03/18 13:13:47:7656] I: lws_client_connect_via_info: role binding to h1 -[2020/03/18 13:13:47:7662] I: lws_client_connect_via_info: protocol binding to lws-ping-test -[2020/03/18 13:13:47:7699] I: lws_client_connect_via_info: wsi 0x5669090: h1 lws-ping-test entry -[2020/03/18 13:13:47:7720] I: lws_header_table_attach: wsi 0x5669090: ah (nil) (tsi 0, count = 0) in -[2020/03/18 13:13:47:7729] I: _lws_create_ah: created ah 0x5669620 (size 4096): pool length 1 -[2020/03/18 13:13:47:7735] I: lws_header_table_attach: did attach wsi 0x5669090: ah 0x5669620: count 1 (on exit) -[2020/03/18 13:13:47:7780] I: lws_client_connect_2_dnsreq: 0x5669090: lookup libwebsockets.org:443 -[2020/03/18 13:13:47:8784] I: lws_getaddrinfo46: getaddrinfo 'libwebsockets.org' says 0 -[2020/03/18 13:13:47:8804] I: lws_client_connect_3_connect: libwebsockets.org ipv4 46.105.127.147 -[2020/03/18 13:13:47:9176] I: lws_client_connect_3_connect: getsockopt check: conn OK -[2020/03/18 13:13:47:9179] I: lws_client_connect_3_connect: Connection started 0x5682cc0 -[2020/03/18 13:13:47:9197] I: lws_client_connect_4_established: wsi 0x5669090: h1 lws-ping-test client created own conn (raw 0) vh defaultm st 0x202 -[2020/03/18 13:13:47:9418] I: h1 client conn using alpn list 'http/1.1' -[2020/03/18 13:13:48:4523] I: lws_role_call_alpn_negotiated: 'http/1.1' -[2020/03/18 13:13:48:4531] I: client connect OK -[2020/03/18 13:13:48:4543] I: lws_openssl_describe_cipher: wsi 0x5669090: TLS_AES_256_GCM_SHA384, TLS_AES_256_GCM_SHA384, 256 bits, TLSv1.3 -[2020/03/18 13:13:48:4717] I: lws_client_socket_service: HANDSHAKE2: 0x5669090: sending headers (wsistate 0x10000204), w sock 5 -[2020/03/18 13:13:48:4992] I: lws_buflist_aware_read: wsi 0x5669090: lws_client_socket_service: ssl_capable_read -4 -[2020/03/18 13:13:48:5005] I: lws_buflist_aware_read: wsi 0x5669090: lws_client_socket_service: ssl_capable_read 174 -[2020/03/18 13:13:48:5166] I: __lws_header_table_detach: wsi 0x5669090: ah 0x5669620 (tsi=0, count = 1) -[2020/03/18 13:13:48:5171] I: __lws_header_table_detach: nobody usable waiting -[2020/03/18 13:13:48:5175] I: _lws_destroy_ah: freed ah 0x5669620 : pool length 0 -[2020/03/18 13:13:48:5180] I: __lws_header_table_detach: wsi 0x5669090: ah 0x5669620 (tsi=0, count = 0) -[2020/03/18 13:13:48:5197] I: _lws_validity_confirmed_role: wsi 0x5669090: setting validity timer 3s (hup 0) -[2020/03/18 13:13:48:5208] U: callback_minimal_broker: established -[2020/03/18 13:13:51:5218] I: lws_validity_cb: wsi 0x5669090: scheduling validity check -[2020/03/18 13:13:51:5325] I: rops_handle_POLLOUT_ws: issuing ping on wsi 0x5669090: ws lws-ping-test h2: 0 -[2020/03/18 13:13:51:5504] I: lws_issue_raw: ssl_capable_write (6) says 6 -[2020/03/18 13:13:51:5809] I: lws_ws_client_rx_sm: client 0x5669090 received pong -[2020/03/18 13:13:51:5819] I: _lws_validity_confirmed_role: wsi 0x5669090: setting validity timer 3s (hup 0) -[2020/03/18 13:13:51:5831] I: Client doing pong callback -[2020/03/18 13:13:54:5821] I: lws_validity_cb: wsi 0x5669090: scheduling validity check -[2020/03/18 13:13:54:5825] I: rops_handle_POLLOUT_ws: issuing ping on wsi 0x5669090: ws lws-ping-test h2: 0 -[2020/03/18 13:13:54:5833] I: lws_issue_raw: ssl_capable_write (6) says 6 -[2020/03/18 13:13:54:6258] I: lws_ws_client_rx_sm: client 0x5669090 received pong -[2020/03/18 13:13:54:6261] I: _lws_validity_confirmed_role: wsi 0x5669090: setting validity timer 3s (hup 0) -[2020/03/18 13:13:54:6263] I: Client doing pong callback -[2020/03/18 13:13:57:6263] I: lws_validity_cb: wsi 0x5669090: scheduling validity check -[2020/03/18 13:13:57:6267] I: rops_handle_POLLOUT_ws: issuing ping on wsi 0x5669090: ws lws-ping-test h2: 0 -[2020/03/18 13:13:57:6275] I: lws_issue_raw: ssl_capable_write (6) says 6 -[2020/03/18 13:13:58:0034] I: lws_ws_client_rx_sm: client 0x5669090 received pong - -... -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-ws-client-pmd-bulk) -cmake_minimum_required(VERSION 2.8.9) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-client-pmd-bulk) -set(SRCS minimal-ws-client-pmd-bulk.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) -#require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/minimal-ws-client-pmd-bulk.c libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/minimal-ws-client-pmd-bulk.c --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/minimal-ws-client-pmd-bulk.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/minimal-ws-client-pmd-bulk.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,132 +0,0 @@ -/* - * lws-minimal-ws-client-pmd-bulk - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a ws client that sends bulk data in multiple - * ws fragments, in a way compatible with per-message deflate. - * - * It shows how to send huge messages without needing a lot of memory. - * - * Build and start the minimal-examples/ws-server/minmal-ws-server-pmd-bulk - * example first. Running this sends a large message to the server and - * exits. - * - * If you give both sides the -n commandline option, it disables permessage- - * deflate compression extension. - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal_pmd_bulk.c" - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted, options; - -/* pass pointers to shared vars to the protocol */ - -static const struct lws_protocol_vhost_options pvo_options = { - NULL, - NULL, - "options", /* pvo name */ - (void *)&options /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo_interrupted = { - &pvo_options, - NULL, - "interrupted", /* pvo name */ - (void *)&interrupted /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo_interrupted, /* "child" pvo linked-list */ - "lws-minimal-pmd-bulk", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; -static const struct lws_extension extensions[] = { - { - "permessage-deflate", - lws_extension_callback_pm_deflate, - "permessage-deflate" - "; client_no_context_takeover" - "; client_max_window_bits" - }, - { NULL, NULL, NULL /* terminator */ } -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws client + permessage-deflate + multifragment bulk message\n"); - lwsl_user(" needs minimal-ws-server-pmd-bulk running to communicate with\n"); - lwsl_user(" %s [-n (no exts)] [-c (compressible)]\n", argv[0]); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; - info.protocols = protocols; - info.pvo = &pvo; - if (!lws_cmdline_option(argc, argv, "-n")) - info.extensions = extensions; - info.pt_serv_buf_size = 32 * 1024; - - if (lws_cmdline_option(argc, argv, "-c")) - options |= 1; - - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + 1 + 1; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - lwsl_user("Completed %s\n", interrupted == 2 ? "OK" : "failed"); - - return interrupted != 2; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/protocol_lws_minimal_pmd_bulk.c libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/protocol_lws_minimal_pmd_bulk.c --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/protocol_lws_minimal_pmd_bulk.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/protocol_lws_minimal_pmd_bulk.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,319 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal-pmd-bulk" - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * The protocol shows how to send and receive bulk messages over a ws connection - * that optionally may have the permessage-deflate extension negotiated on it. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -/* - * We will produce a large ws message either from this text repeated many times, - * or from 0x40 + a 6-bit pseudorandom number - */ - -static const char * const redundant_string = - "No one would have believed in the last years of the nineteenth " - "century that this world was being watched keenly and closely by " - "intelligences greater than man's and yet as mortal as his own; that as " - "men busied themselves about their various concerns they were " - "scrutinised and studied, perhaps almost as narrowly as a man with a " - "microscope might scrutinise the transient creatures that swarm and " - "multiply in a drop of water. With infinite complacency men went to " - "and fro over this globe about their little affairs, serene in their " - "assurance of their empire over matter. It is possible that the " - "infusoria under the microscope do the same. No one gave a thought to " - "the older worlds of space as sources of human danger, or thought of " - "them only to dismiss the idea of life upon them as impossible or " - "improbable. It is curious to recall some of the mental habits of " - "those departed days. At most terrestrial men fancied there might be " - "other men upon Mars, perhaps inferior to themselves and ready to " - "welcome a missionary enterprise. Yet across the gulf of space, minds " - "that are to our minds as ours are to those of the beasts that perish, " - "intellects vast and cool and unsympathetic, regarded this earth with " - "envious eyes, and slowly and surely drew their plans against us. And " - "early in the twentieth century came the great disillusionment. " -; - -/* this reflects the length of the string above */ -#define REPEAT_STRING_LEN 1337 -/* this is the total size of the ws message we will send */ -#define MESSAGE_SIZE (100 * REPEAT_STRING_LEN) -/* this is how much we will send each time the connection is writable */ -#define MESSAGE_CHUNK_SIZE (1 * 1024) - -/* one of these is created for each client connecting to us */ - -struct per_session_data__minimal_pmd_bulk { - int position_tx, position_rx; - uint64_t rng_rx, rng_tx; -}; - -struct vhd_minimal_pmd_bulk { - struct lws_context *context; - struct lws_vhost *vhost; - struct lws *client_wsi; - - int *interrupted; - int *options; -}; - -static uint64_t rng(uint64_t *r) -{ - *r ^= *r << 21; - *r ^= *r >> 35; - *r ^= *r << 4; - - return *r; -} - -static int -connect_client(struct vhd_minimal_pmd_bulk *vhd) -{ - struct lws_client_connect_info i; - - memset(&i, 0, sizeof(i)); - - i.context = vhd->context; - i.port = 7681; - i.address = "localhost"; - i.path = "/"; - i.host = i.address; - i.origin = i.address; - i.ssl_connection = 0; - i.vhost = vhd->vhost; - i.protocol = "lws-minimal-pmd-bulk"; - i.pwsi = &vhd->client_wsi; - - return !lws_client_connect_via_info(&i); -} - -static void -schedule_callback(struct lws *wsi, int reason, int secs) -{ - lws_timed_callback_vh_protocol(lws_get_vhost(wsi), - lws_get_protocol(wsi), reason, secs); -} - -static int -callback_minimal_pmd_bulk(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal_pmd_bulk *pss = - (struct per_session_data__minimal_pmd_bulk *)user; - struct vhd_minimal_pmd_bulk *vhd = (struct vhd_minimal_pmd_bulk *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - uint8_t buf[LWS_PRE + MESSAGE_CHUNK_SIZE], *start = &buf[LWS_PRE], *p; - int n, m, flags; - - switch (reason) { - - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct vhd_minimal_pmd_bulk)); - if (!vhd) - return -1; - - vhd->context = lws_get_context(wsi); - vhd->vhost = lws_get_vhost(wsi); - - /* get the pointer to "interrupted" we were passed in pvo */ - vhd->interrupted = (int *)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "interrupted")->value; - vhd->options = (int *)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "options")->value; - - if (connect_client(vhd)) - schedule_callback(wsi, LWS_CALLBACK_USER, 1); - break; - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - pss->rng_tx = 4; - pss->rng_rx = 4; - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_CLIENT_WRITEABLE: - - /* - * when we connect, we will send the server a message - */ - - if (pss->position_tx == MESSAGE_SIZE) - break; - - /* fill up one chunk's worth of message content */ - - p = start; - n = MESSAGE_CHUNK_SIZE; - if (n > MESSAGE_SIZE - pss->position_tx) - n = MESSAGE_SIZE - pss->position_tx; - - flags = lws_write_ws_flags(LWS_WRITE_BINARY, !pss->position_tx, - pss->position_tx + n == MESSAGE_SIZE); - - /* - * select between producing compressible repeated text, - * or uncompressible PRNG output - */ - - if (*vhd->options & 1) { - while (n) { - size_t s; - - m = pss->position_tx % REPEAT_STRING_LEN; - s = REPEAT_STRING_LEN - m; - if (s > (size_t)n) - s = n; - memcpy(p, &redundant_string[m], s); - pss->position_tx += s; - p += s; - n -= s; - } - } else { - pss->position_tx += n; - while (n--) - *p++ = rng(&pss->rng_tx); - } - - n = lws_ptr_diff(p, start); - m = lws_write(wsi, start, n, flags); - if (m < n) { - lwsl_err("ERROR %d writing ws\n", m); - return -1; - } - if (pss->position_tx != MESSAGE_SIZE) /* if more to do... */ - lws_callback_on_writable(wsi); - else - /* if we sent and received everything */ - if (pss->position_rx == MESSAGE_SIZE) - *vhd->interrupted = 2; - break; - - case LWS_CALLBACK_CLIENT_RECEIVE: - - /* - * When we connect, the server will send us a message too - */ - - lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %4d (rpp %5d, last %d)\n", - (int)len, (int)lws_remaining_packet_payload(wsi), - lws_is_final_fragment(wsi)); - - if (*vhd->options & 1) { - while (len) { - size_t s; - - m = pss->position_rx % REPEAT_STRING_LEN; - s = REPEAT_STRING_LEN - m; - if (s > len) - s = len; - if (memcmp(in, &redundant_string[m], s)) { - lwsl_user("echo'd data doesn't match\n"); - return -1; - } - pss->position_rx += s; - in = ((unsigned char *)in) + s; - len -= s; - } - } else { - p = (uint8_t *)in; - pss->position_rx += len; - while (len--) - if (*p++ != (uint8_t)rng(&pss->rng_rx)) { - lwsl_user("echo'd data doesn't match\n"); - return -1; - } - } - - /* if we sent and received everything */ - - if (pss->position_rx == MESSAGE_SIZE && - pss->position_tx == MESSAGE_SIZE) - *vhd->interrupted = 2; - - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - vhd->client_wsi = NULL; - schedule_callback(wsi, LWS_CALLBACK_USER, 1); - break; - - case LWS_CALLBACK_CLIENT_CLOSED: - vhd->client_wsi = NULL; - schedule_callback(wsi, LWS_CALLBACK_USER, 1); - break; - - /* rate-limited client connect retries */ - - case LWS_CALLBACK_USER: - lwsl_notice("%s: LWS_CALLBACK_USER\n", __func__); - if (connect_client(vhd)) - schedule_callback(wsi, LWS_CALLBACK_USER, 1); - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK \ - { \ - "lws-minimal-pmd-bulk", \ - callback_minimal_pmd_bulk, \ - sizeof(struct per_session_data__minimal_pmd_bulk), \ - 4096, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK -}; - -int -init_protocol_minimal_pmd_bulk(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal_pmd_bulk(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/README.md libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/README.md --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,164 +0,0 @@ -# lws minimal ws client + permessage-deflate for bulk traffic - -This example opens a client connection to localhost:7681 where it -expects to find minimal-ws-server-pmd-bulk running. - -It sends and receives a large, multifragment message, and then exits. - -## build - -``` - $ cmake . && make -``` - -## usage - -Both the server and client side must use the same options - - - `-n` disable permessage-deflate extension - - `-c` send compressible text instead of uncompressible binary data - -``` - $ ./lws-minimal-ws-client-pmd-bulk -[2018/04/05 12:08:58:9120] USER: LWS minimal ws client + permessage-deflate + multifragment bulk message -[2018/04/05 12:08:58:9120] USER: ./lws-minimal-ws-client-pmd-bulk [-n (no exts)] [-c (compressible)] -[2018/04/05 12:08:58:9120] NOTICE: Creating Vhost 'default' (serving disabled), 2 protocols, IPv6 on -[2018/04/05 12:08:59:9139] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9139] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9139] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9139] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9139] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9140] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9140] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9140] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9140] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9140] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9140] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9141] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9141] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9141] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9142] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9142] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9142] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9142] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9142] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9142] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9143] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9143] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9143] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9143] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9143] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9143] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9144] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9144] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9144] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9144] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9144] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9144] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9145] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9145] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9145] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9145] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9146] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9146] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9146] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9146] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9146] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9146] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9147] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9147] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9147] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9147] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9147] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9148] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9148] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9148] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9148] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9148] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9148] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9149] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9149] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9149] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9149] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9149] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9149] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9150] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9150] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9150] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9150] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9150] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9150] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9151] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9151] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9151] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9151] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9152] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9152] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9152] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9152] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9152] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9152] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9153] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9153] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9153] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9153] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9153] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9153] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9154] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9154] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9154] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9154] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9154] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9154] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9155] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9155] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9155] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9155] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9155] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9155] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9156] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9156] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9156] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9156] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9157] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9157] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9157] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9157] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9157] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9158] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9158] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9158] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9158] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9158] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9158] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9159] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9159] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9159] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9159] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9159] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9159] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9160] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9160] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9160] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9160] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9160] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9160] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9161] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9161] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9161] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9161] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9161] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9161] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9162] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9162] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9162] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9162] USER: LWS_CALLBACK_CLIENT_RECEIVE: 1024 (rpp 0, last 0) -[2018/04/05 12:08:59:9162] USER: LWS_CALLBACK_CLIENT_RECEIVE: 580 (rpp 0, last 1) -[2018/04/05 12:08:59:9180] USER: Completed OK -``` - -Visit http://localhost:7681 in your browser - -One or another kind of bulk ws transfer is made to the browser. - -The ws connection is made via permessage-deflate extension. diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-rx/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-rx/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-rx/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-rx/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-ws-client-rx) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-client-rx) -set(SRCS minimal-ws-client.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-rx/libwebsockets.org.cer libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-rx/libwebsockets.org.cer --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-rx/libwebsockets.org.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-rx/libwebsockets.org.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFWjCCBEKgAwIBAgISA9x0/oj5PLdW46hsmR82/7ytMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5NDBaFw0x -OTEyMDYwNzA5NDBaMBwxGjAYBgNVBAMTEWxpYndlYnNvY2tldHMub3JnMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPinIkleLmvEcA/YuBss6ASXVi7g -yr6Sss7cB3vTy7Fp8OB2c1N25prHZxVpORAUo0UreiaY2Ws4NFvDaYp08ZffevuC -UhThsEJlbkD0uvt7dPapJt9PNJtlxjNFWyvHEy6PijzIaMYDROiStcCJQn7kAew/ -Za2+5kNVgKqT+7OXukJEFdSdVZI6QC/npeQlkIrFSq1WVthCGBNJehxxES0hSWzk -0gNVKlkD3/SbkupsfUpe73XiawMtrtsSE7cdnul7VZmiP8I/3sJr1+4/3xZ+DEYg -mVB82B0vd08VJYzU7Nf0pz0PWusAmzRoRn81IXkOfBg9ohlSSEoZhHYS7QIDAQAB -o4ICZjCCAmIwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr -BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRmKKyGjufWgp7pR2x0tWxG -D9G+WTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcB -AQRjMGEwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlw -dC5vcmcwLwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlw -dC5vcmcvMBwGA1UdEQQVMBOCEWxpYndlYnNvY2tldHMub3JnMEwGA1UdIARFMEMw -CAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9j -cHMubGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcAdH7a -gzGtMxCRIZzOJU9CcMK//V5CIAjGNzV55hB7zFYAAAFtCsWIfgAABAMASDBGAiEA -0H55VqSKV3otHK7uHNbcR0QwoUYtCmeObhsqxzCnmDwCIQD3mtuSKrxTD3oA+Yde -nmTgWfFyS4TNgLNEPCJYo2s75gB1ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM -9OVFR/R4AAABbQrFil4AAAQDAEYwRAIgNSpvz/1JA2aP6fh6ujGNuYfrAvWjlxXo -CJtVGe4XaDYCIGmK1/9tl1uQbVD46P5NswnULq06KQmuOrlI3HO4r86HMA0GCSqG -SIb3DQEBCwUAA4IBAQBiAlV7wkCsWE99VmZHBmcbZChWyWUHG3LM1hnaQRQjTSYk -CIlauCpWzlUd6weuvra85KqBbCYo+1hxbwITI796uAdgtHmBE8nj0VltHwKeSq2s -KKiGXBRT7Z7t0VHYSLOlGOVn1auuQFaWBArc0cQ/m1ZsoHvOiHTlKQvVsA4HnIxA -CjGY9OOQoh0c36ecbJZ44XKnU9J/OXtDx00aW6QodaZmgMp/OOCghFQUvufkgTUL -LZid873/8dJVWjAaj1VdadO1nSbdAfBbeWXy93+vg1aAoig80RoscrzYCaNlwmR7 -EO5zWxL3l+xUZogQSJuICgUgNzVB3wjn8HeHGsqt ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,149 +0,0 @@ -/* - * lws-minimal-ws-client - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the a minimal ws client using lws. - * - * It connects to https://libwebsockets.org/ and makes a - * wss connection to the dumb-increment protocol there. While - * connected, it prints the numbers it is being sent by - * dumb-increment protocol. - */ - -#include -#include -#include - -static int interrupted, rx_seen, test; -static struct lws *client_wsi; - -static int -callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - switch (reason) { - - /* because we are protocols[0] ... */ - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - client_wsi = NULL; - break; - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - lwsl_user("%s: established\n", __func__); - break; - - case LWS_CALLBACK_CLIENT_RECEIVE: - lwsl_user("RX: %s\n", (const char *)in); - rx_seen++; - if (test && rx_seen == 10) - interrupted = 1; - break; - - case LWS_CALLBACK_CLIENT_CLOSED: - client_wsi = NULL; - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "dumb-increment-protocol", - callback_dumb_increment, - 0, - 0, - }, - { NULL, NULL, 0, 0 } -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_client_connect_info i; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, lws - * must have been configured with -DCMAKE_BUILD_TYPE=DEBUG - * instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - test = !!lws_cmdline_option(argc, argv, "-t"); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws client rx [-d ] [--h2] [-t (test)]\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./libwebsockets.org.cer"; -#endif - - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + 1 + 1; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ - i.context = context; - i.port = 443; - i.address = "libwebsockets.org"; - i.path = "/"; - i.host = i.address; - i.origin = i.address; - i.ssl_connection = LCCSCF_USE_SSL; - i.protocol = protocols[0].name; /* "dumb-increment-protocol" */ - i.pwsi = &client_wsi; - - if (lws_cmdline_option(argc, argv, "--h2")) - i.alpn = "h2"; - - lws_client_connect_via_info(&i); - - while (n >= 0 && client_wsi && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - lwsl_user("Completed %s\n", rx_seen > 10 ? "OK" : "Failed"); - - return rx_seen > 10; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-rx/README.md libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-rx/README.md --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-rx/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-rx/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -# lws minimal ws client rx - -## build - -``` - $ cmake . && make -``` - -## usage - -The application goes to https://libwebsockets.org and makes a wss connection -using the dumb-increment-protocol. It shows the incrementing number it is -being sent over ws as it arrives. - -This example only receives things to keep it simple. See minimal-ws-client-tx -for code related to sending things. Of course rx and tx are supported in the -same protocol. - -``` -./lws-minimal-ws-client-rx -[2018/03/14 11:57:24:0689] USER: LWS minimal ws client rx -[2018/03/14 11:57:24:0705] NOTICE: Creating Vhost 'default' port -1, 1 protocols, IPv6 off -[2018/03/14 11:57:24:0710] NOTICE: created client ssl context for default -[2018/03/14 11:57:24:0788] NOTICE: lws_client_connect_2: 0x15b8310: address libwebsockets.org -[2018/03/14 11:57:24:7643] NOTICE: lws_client_connect_2: 0x15b8310: address libwebsockets.org -[2018/03/14 11:57:26:9191] USER: RX: 0 -[2018/03/14 11:57:26:9318] USER: RX: 1 -[2018/03/14 11:57:27:2182] USER: RX: 2 -[2018/03/14 11:57:27:2336] USER: RX: 3 -[2018/03/14 11:57:27:2838] USER: RX: 4 -[2018/03/14 11:57:27:5173] USER: RX: 5 -[2018/03/14 11:57:27:5352] USER: RX: 6 -[2018/03/14 11:57:27:5854] USER: RX: 7 -[2018/03/14 11:57:27:8156] USER: RX: 8 -[2018/03/14 11:57:27:8359] USER: RX: 9 -^C[2018/03/14 11:57:27:9884] USER: Completed -``` - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-rx/selftest.sh libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-rx/selftest.sh --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-rx/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-rx/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=1 - -dotest $1 $2 warmcat -t - -exit $FAILS diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-spam/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-spam/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-spam/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-spam/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -project(lws-minimal-ws-client-spam) -cmake_minimum_required(VERSION 2.8) -include(CheckIncludeFile) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-client-spam) -set(SRCS minimal-ws-client-spam.c) - -MACRO(require_pthreads result) - CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) - if (NOT LWS_HAVE_PTHREAD_H) - if (LWS_WITH_MINIMAL_EXAMPLES) - set(result 0) - else() - message(FATAL_ERROR "threading support requires pthreads") - endif() - endif() -ENDMACRO() -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_pthreads(requirements) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared pthread) - add_dependencies(${SAMP} websockets_shared pthread) - else() - target_link_libraries(${SAMP} websockets pthread) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-spam/libwebsockets.org.cer libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-spam/libwebsockets.org.cer --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-spam/libwebsockets.org.cer 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-spam/libwebsockets.org.cer 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFWjCCBEKgAwIBAgISA9x0/oj5PLdW46hsmR82/7ytMA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5NDBaFw0x -OTEyMDYwNzA5NDBaMBwxGjAYBgNVBAMTEWxpYndlYnNvY2tldHMub3JnMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPinIkleLmvEcA/YuBss6ASXVi7g -yr6Sss7cB3vTy7Fp8OB2c1N25prHZxVpORAUo0UreiaY2Ws4NFvDaYp08ZffevuC -UhThsEJlbkD0uvt7dPapJt9PNJtlxjNFWyvHEy6PijzIaMYDROiStcCJQn7kAew/ -Za2+5kNVgKqT+7OXukJEFdSdVZI6QC/npeQlkIrFSq1WVthCGBNJehxxES0hSWzk -0gNVKlkD3/SbkupsfUpe73XiawMtrtsSE7cdnul7VZmiP8I/3sJr1+4/3xZ+DEYg -mVB82B0vd08VJYzU7Nf0pz0PWusAmzRoRn81IXkOfBg9ohlSSEoZhHYS7QIDAQAB -o4ICZjCCAmIwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr -BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRmKKyGjufWgp7pR2x0tWxG -D9G+WTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcB -AQRjMGEwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlw -dC5vcmcwLwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlw -dC5vcmcvMBwGA1UdEQQVMBOCEWxpYndlYnNvY2tldHMub3JnMEwGA1UdIARFMEMw -CAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9j -cHMubGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcAdH7a -gzGtMxCRIZzOJU9CcMK//V5CIAjGNzV55hB7zFYAAAFtCsWIfgAABAMASDBGAiEA -0H55VqSKV3otHK7uHNbcR0QwoUYtCmeObhsqxzCnmDwCIQD3mtuSKrxTD3oA+Yde -nmTgWfFyS4TNgLNEPCJYo2s75gB1ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM -9OVFR/R4AAABbQrFil4AAAQDAEYwRAIgNSpvz/1JA2aP6fh6ujGNuYfrAvWjlxXo -CJtVGe4XaDYCIGmK1/9tl1uQbVD46P5NswnULq06KQmuOrlI3HO4r86HMA0GCSqG -SIb3DQEBCwUAA4IBAQBiAlV7wkCsWE99VmZHBmcbZChWyWUHG3LM1hnaQRQjTSYk -CIlauCpWzlUd6weuvra85KqBbCYo+1hxbwITI796uAdgtHmBE8nj0VltHwKeSq2s -KKiGXBRT7Z7t0VHYSLOlGOVn1auuQFaWBArc0cQ/m1ZsoHvOiHTlKQvVsA4HnIxA -CjGY9OOQoh0c36ecbJZ44XKnU9J/OXtDx00aW6QodaZmgMp/OOCghFQUvufkgTUL -LZid873/8dJVWjAaj1VdadO1nSbdAfBbeWXy93+vg1aAoig80RoscrzYCaNlwmR7 -EO5zWxL3l+xUZogQSJuICgUgNzVB3wjn8HeHGsqt ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-spam/minimal-ws-client-spam.c libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-spam/minimal-ws-client-spam.c --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-spam/minimal-ws-client-spam.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-spam/minimal-ws-client-spam.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,265 +0,0 @@ -/* - * lws-minimal-ws-client-spam - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a ws client that makes continuous mass ws connections - * asynchronously - */ - -#include -#include -#include -#include - -enum { - CLIENT_IDLE, - CLIENT_CONNECTING, - CLIENT_AWAITING_SEND, -}; - -struct client { - struct lws *wsi; - int index; - int state; -}; - -static struct lws_context *context; -static struct client clients[200]; -static int interrupted, port = 443, ssl_connection = LCCSCF_USE_SSL; -static const char *server_address = "libwebsockets.org", - *pro = "lws-mirror-protocol"; -static int concurrent = 3, conn, tries, est, errors, closed, sent, limit = 15; - -struct pss { - int conn; -}; - -static int -connect_client(int idx) -{ - struct lws_client_connect_info i; - - if (tries == limit) { - lwsl_user("Reached limit... finishing\n"); - return 0; - } - - memset(&i, 0, sizeof(i)); - - i.context = context; - i.port = port; - i.address = server_address; - i.path = "/"; - i.host = i.address; - i.origin = i.address; - i.ssl_connection = ssl_connection; - i.protocol = pro; - i.local_protocol_name = pro; - i.pwsi = &clients[idx].wsi; - - clients[idx].state = CLIENT_CONNECTING; - tries++; - - if (!lws_client_connect_via_info(&i)) { - clients[idx].wsi = NULL; - clients[idx].state = CLIENT_IDLE; - - return 1; - } - - return 0; -} - -static int -callback_minimal_spam(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct pss *pss = (struct pss *)user; - uint8_t ping[LWS_PRE + 125]; - int n, m; - - switch (reason) { - - case LWS_CALLBACK_PROTOCOL_INIT: - for (n = 0; n < concurrent; n++) { - clients[n].index = n; - connect_client(n); - } - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - errors++; - lwsl_err("CLIENT_CONNECTION_ERROR: %s (try %d, est %d, closed %d, err %d)\n", - in ? (char *)in : "(null)", tries, est, closed, errors); - for (n = 0; n < concurrent; n++) { - if (clients[n].wsi == wsi) { - clients[n].wsi = NULL; - clients[n].state = CLIENT_IDLE; - connect_client(n); - break; - } - } - if (tries == closed + errors) - interrupted = 1; - break; - - /* --- client callbacks --- */ - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - lwsl_user("%s: established (try %d, est %d, closed %d, err %d)\n", - __func__, tries, est, closed, errors); - est++; - pss->conn = conn++; - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_CLIENT_CLOSED: - closed++; - if (tries == closed + errors) - interrupted = 1; - if (tries == limit) { - lwsl_user("%s: leaving CLOSED (try %d, est %d, sent %d, closed %d, err %d)\n", - __func__, tries, est, sent, closed, errors); - break; - } - - for (n = 0; n < concurrent; n++) { - if (clients[n].wsi == wsi) { - connect_client(n); - lwsl_user("%s: reopening (try %d, est %d, closed %d, err %d)\n", - __func__, tries, est, closed, errors); - break; - } - } - if (n == concurrent) - lwsl_user("CLOSED: can't find client wsi\n"); - break; - - case LWS_CALLBACK_CLIENT_WRITEABLE: - n = lws_snprintf((char *)ping + LWS_PRE, sizeof(ping) - LWS_PRE, - "hello %d", pss->conn); - - m = lws_write(wsi, ping + LWS_PRE, n, LWS_WRITE_TEXT); - if (m < n) { - lwsl_err("sending ping failed: %d\n", m); - - return -1; - } - lws_set_timeout(wsi, PENDING_TIMEOUT_USER_OK, LWS_TO_KILL_ASYNC); - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "lws-spam-test", - callback_minimal_spam, - sizeof(struct pss), - 0, - }, - { NULL, NULL, 0, 0 } -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws client SPAM\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; -#if defined(LWS_WITH_MBEDTLS) - /* - * OpenSSL uses the system trust store. mbedTLS has to be told which - * CA to trust explicitly. - */ - info.client_ssl_ca_filepath = "./libwebsockets.org.cer"; -#endif - - if ((p = lws_cmdline_option(argc, argv, "--server"))) { - server_address = p; - ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - } - - if ((p = lws_cmdline_option(argc, argv, "--port"))) - port = atoi(p); - - if ((p = lws_cmdline_option(argc, argv, "-l"))) - limit = atoi(p); - - if ((p = lws_cmdline_option(argc, argv, "-c"))) - concurrent = atoi(p); - - if (lws_cmdline_option(argc, argv, "-n")) { - ssl_connection = 0; - info.options = 0; - } - - if (concurrent < 0 || - concurrent > (int)LWS_ARRAY_SIZE(clients)) { - lwsl_err("%s: -c %d larger than max concurrency %d\n", __func__, - concurrent, (int)LWS_ARRAY_SIZE(clients)); - - return 1; - } - - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and n (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + concurrent + 1; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - if (tries == limit && closed == tries) { - lwsl_user("Completed\n"); - return 0; - } - - lwsl_err("Failed\n"); - - return 1; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-spam/README.md libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-spam/README.md --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-spam/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-spam/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -# lws minimal ws client SPAM - -This connects to libwebsockets.org using the lws-mirror-protocol. - -By default is has 10 concurrent connections and connects 100 times. - -## build - -``` - $ cmake . && make -``` - -## Commandline Options - -Option|Meaning ----|--- --d|Set logging verbosity ---server|Use a specific server instead of libwebsockets.org, eg `--server localhost`. Implies LCCSCF_ALLOW_SELFSIGNED ---port|Use a specific port instead of 443, eg `--port 7681` --c|Amount of concurrent connections --l|Test limit (total number of connections to make) - -## usage - -Just run it, it will repeatedly connect and reconnect to libwebsockets.org -until it hits the test limit. - -You can also direct it to use the lws test server in tls mode by running that -with `libwebsockets-test-server -s` and running this using, eg - -``` - $ ./lws-minimal-ws-client-spam -c 20 -l 200 --server localhost --port 7681 -``` - -``` - $ ./lws-minimal-ws-client-spam -[2018/11/15 09:53:19:9639] USER: LWS minimal ws client SPAM -[2018/11/15 09:53:19:9647] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off -[2018/11/15 09:53:19:9695] NOTICE: created client ssl context for default -[2018/11/15 09:53:21:0976] USER: callback_minimal_spam: established (try 10, est 0, closed 0, err 0) -[2018/11/15 09:53:21:1041] USER: callback_minimal_spam: established (try 10, est 1, closed 0, err 0) -[2018/11/15 09:53:21:1089] USER: callback_minimal_spam: established (try 10, est 2, closed 0, err 0) -[2018/11/15 09:53:21:1132] USER: callback_minimal_spam: established (try 10, est 3, closed 0, err 0) -[2018/11/15 09:53:21:1166] USER: callback_minimal_spam: established (try 10, est 4, closed 0, err 0) -[2018/11/15 09:53:21:1531] USER: callback_minimal_spam: established (try 10, est 5, closed 0, err 0) -[2018/11/15 09:53:21:1563] USER: callback_minimal_spam: established (try 10, est 6, closed 0, err 0) -[2018/11/15 09:53:21:1589] USER: callback_minimal_spam: established (try 10, est 7, closed 0, err 0) -[2018/11/15 09:53:21:1616] USER: callback_minimal_spam: established (try 10, est 8, closed 0, err 0) -[2018/11/15 09:53:21:1671] USER: callback_minimal_spam: established (try 10, est 9, closed 0, err 0) -[2018/11/15 09:53:21:3778] USER: callback_minimal_spam: reopening (try 11, est 10, closed 1, err 0) -... -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-spam/selftest.sh libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-spam/selftest.sh --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-spam/selftest.sh 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-spam/selftest.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -#!/bin/bash -# -# $1: path to minimal example binaries... -# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 -# that will be ./bin from your build dir -# -# $2: path for logs and results. The results will go -# in a subdir named after the directory this script -# is in -# -# $3: offset for test index count -# -# $4: total test count -# -# $5: path to ./minimal-examples dir in lws -# -# Test return code 0: OK, 254: timed out, other: error indication - -. $5/selftests-library.sh - -COUNT_TESTS=1 - -dotest $1 $2 warmcat - -exit $FAILS - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-tx/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-tx/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-tx/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-tx/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -project(lws-minimal-ws-client-tx) -cmake_minimum_required(VERSION 2.8) -include(CheckIncludeFile) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-client-tx) -set(SRCS minimal-ws-client.c) - -MACRO(require_pthreads result) - CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) - if (NOT LWS_HAVE_PTHREAD_H) - if (LWS_WITH_MINIMAL_EXAMPLES) - set(result 0) - else() - message(FATAL_ERROR "threading support requires pthreads") - endif() - endif() -ENDMACRO() -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_pthreads(requirements) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_CLIENT 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared pthread) - add_dependencies(${SAMP} websockets_shared pthread) - else() - target_link_libraries(${SAMP} websockets pthread) - endif() -endif() \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-tx/minimal-ws-client.c libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-tx/minimal-ws-client.c --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-tx/minimal-ws-client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-tx/minimal-ws-client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,342 +0,0 @@ -/* - * lws-minimal-ws-client-tx - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a ws "publisher" to go with the minimal-ws-broker - * example. - * - * Two threads are spawned that produce messages to be sent to the broker, - * via a local ringbuffer. Locking is provided to make ringbuffer access - * threadsafe. - * - * When a nailed-up client connection to the broker is established, the - * ringbuffer is sent to the broker, which distributes the events to all - * connected clients. - */ - -#include -#include -#include -#include - -static int interrupted; - -/* one of these created for each message */ - -struct msg { - void *payload; /* is malloc'd */ - size_t len; -}; - -struct per_vhost_data__minimal { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - pthread_t pthread_spam[2]; - - pthread_mutex_t lock_ring; /* serialize access to the ring buffer */ - struct lws_ring *ring; /* ringbuffer holding unsent messages */ - uint32_t tail; - - struct lws_client_connect_info i; - struct lws *client_wsi; - - int counter; - char finished; - char established; -}; - -#if defined(WIN32) -static void usleep(unsigned long l) { Sleep(l / 1000); } -#endif - -static void -__minimal_destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} - -static void * -thread_spam(void *d) -{ - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *)d; - struct msg amsg; - int len = 128, index = 1, n; - - do { - /* don't generate output if client not connected */ - if (!vhd->established) - goto wait; - - pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ - - /* only create if space in ringbuffer */ - n = (int)lws_ring_get_count_free_elements(vhd->ring); - if (!n) { - lwsl_user("dropping!\n"); - goto wait_unlock; - } - - amsg.payload = malloc(LWS_PRE + len); - if (!amsg.payload) { - lwsl_user("OOM: dropping\n"); - goto wait_unlock; - } - n = lws_snprintf((char *)amsg.payload + LWS_PRE, len, - "tid: %p, msg: %d", - (void *)pthread_self(), index++); - amsg.len = n; - n = lws_ring_insert(vhd->ring, &amsg, 1); - if (n != 1) { - __minimal_destroy_message(&amsg); - lwsl_user("dropping!\n"); - } else - /* - * This will cause a LWS_CALLBACK_EVENT_WAIT_CANCELLED - * in the lws service thread context. - */ - lws_cancel_service(vhd->context); - -wait_unlock: - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ - -wait: - usleep(100000); - - } while (!vhd->finished); - - lwsl_notice("thread_spam %p exiting\n", (void *)pthread_self()); - - pthread_exit(NULL); - - return NULL; -} - -static int -connect_client(struct per_vhost_data__minimal *vhd) -{ - vhd->i.context = vhd->context; - vhd->i.port = 7681; - vhd->i.address = "localhost"; - vhd->i.path = "/publisher"; - vhd->i.host = vhd->i.address; - vhd->i.origin = vhd->i.address; - vhd->i.ssl_connection = 0; - - vhd->i.protocol = "lws-minimal-broker"; - vhd->i.pwsi = &vhd->client_wsi; - - return !lws_client_connect_via_info(&vhd->i); -} - -static int -callback_minimal_broker(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - const struct msg *pmsg; - void *retval; - int n, m, r = 0; - - switch (reason) { - - /* --- protocol lifecycle callbacks --- */ - - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__minimal)); - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - - vhd->ring = lws_ring_create(sizeof(struct msg), 8, - __minimal_destroy_message); - if (!vhd->ring) - return 1; - - pthread_mutex_init(&vhd->lock_ring, NULL); - - /* start the content-creating threads */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) - if (pthread_create(&vhd->pthread_spam[n], NULL, - thread_spam, vhd)) { - lwsl_err("thread creation failed\n"); - r = 1; - goto init_fail; - } - - if (connect_client(vhd)) - lws_timed_callback_vh_protocol(vhd->vhost, - vhd->protocol, LWS_CALLBACK_USER, 1); - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: -init_fail: - vhd->finished = 1; - for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) - if (vhd->pthread_spam[n]) - pthread_join(vhd->pthread_spam[n], &retval); - - if (vhd->ring) - lws_ring_destroy(vhd->ring); - - pthread_mutex_destroy(&vhd->lock_ring); - - return r; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", - in ? (char *)in : "(null)"); - vhd->client_wsi = NULL; - lws_timed_callback_vh_protocol(vhd->vhost, - vhd->protocol, LWS_CALLBACK_USER, 1); - break; - - /* --- client callbacks --- */ - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - lwsl_user("%s: established\n", __func__); - vhd->established = 1; - break; - - case LWS_CALLBACK_CLIENT_WRITEABLE: - pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ - pmsg = lws_ring_get_element(vhd->ring, &vhd->tail); - if (!pmsg) - goto skip; - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE, - pmsg->len, LWS_WRITE_TEXT); - if (m < (int)pmsg->len) { - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock */ - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - lws_ring_consume_single_tail(vhd->ring, &vhd->tail, 1); - - /* more to do for us? */ - if (lws_ring_get_element(vhd->ring, &vhd->tail)) - /* come back as soon as we can write more */ - lws_callback_on_writable(wsi); - -skip: - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ - break; - - case LWS_CALLBACK_CLIENT_CLOSED: - vhd->client_wsi = NULL; - vhd->established = 0; - lws_timed_callback_vh_protocol(vhd->vhost, vhd->protocol, - LWS_CALLBACK_USER, 1); - break; - - case LWS_CALLBACK_EVENT_WAIT_CANCELLED: - /* - * When the "spam" threads add a message to the ringbuffer, - * they create this event in the lws service thread context - * using lws_cancel_service(). - * - * We respond by scheduling a writable callback for the - * connected client, if any. - */ - if (vhd && vhd->client_wsi && vhd->established) - lws_callback_on_writable(vhd->client_wsi); - break; - - /* rate-limited client connect retries */ - - case LWS_CALLBACK_USER: - lwsl_notice("%s: LWS_CALLBACK_USER\n", __func__); - if (connect_client(vhd)) - lws_timed_callback_vh_protocol(vhd->vhost, - vhd->protocol, - LWS_CALLBACK_USER, 1); - break; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols protocols[] = { - { - "lws-minimal-broker", - callback_minimal_broker, - 0, - 0, - }, - { NULL, NULL, 0, 0 } -}; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws client tx\n"); - lwsl_user(" Run minimal-ws-broker and browse to that\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ - info.protocols = protocols; - /* - * since we know this lws context is only ever going to be used with - * one client wsis / fds / sockets at a time, let lws know it doesn't - * have to use the default allocations for fd tables up to ulimit -n. - * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we - * will use. - */ - info.fd_limit_per_thread = 1 + 1 + 1; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - lwsl_user("Completed\n"); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-tx/README.md libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-tx/README.md --- libwebsockets-4.0.20/minimal-examples/ws-client/minimal-ws-client-tx/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/minimal-ws-client-tx/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -# lws minimal ws client tx - -This demonstrates a ws "publisher" to go with the minimal-ws-broker example. - -Two threads are spawned that produce messages to be sent to the broker, -via a local ringbuffer. Locking is provided to make ringbuffer access threadsafe. - -When a nailed-up client connection to the broker is established, the -ringbuffer is sent to the broker, which distributes the events to all -connected clients. - -## build - -``` - $ cmake . && make -``` - -## usage - -This example connects to ws-server/minimal-ws-broker, so you need to build and run -that in another terminal. - -``` - $ ./lws-minimal-ws-client-tx -[2018/03/16 16:04:33:5774] USER: LWS minimal ws client tx -[2018/03/16 16:04:33:5774] USER: Run minimal-ws-broker and browse to that -[2018/03/16 16:04:33:5774] NOTICE: Creating Vhost 'default' port -1, 1 protocols, IPv6 off -[2018/03/16 16:04:34:5794] USER: callback_minimal_broker: established -``` - -If you open a browser on http://localhost:7681 , you will see the subscribed -messages from the threads in this app via the broker app. - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-client/README.md libwebsockets-2.4.2/minimal-examples/ws-client/README.md --- libwebsockets-4.0.20/minimal-examples/ws-client/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-client/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -|name|demonstrates| ----|--- -minimal-ws-client|Simple client that connects to libwebsockets.org dumb increment protocol and demonstrates retry and backoff -minimal-ws-client-echo|Simple client that connects to a ws server and echos anything the server sends -minimal-ws-client-ping|Ws ping test client -minimal-ws-client-pmd-bulk|Client that sends bulk multifragment data to the minimal-ws-server-pmd-bulk example -minimal-ws-client-rx|Connects to the dumb-increment-protocol wss server at https://libwebsockets.org and demonstrates receiving ws data -minimal-ws-client-spam|Spams ws connections in parallel to a server for stability testing -minimal-ws-client-tx|Connects to the minimal-ws-broker example as a publisher, demonstrating sending ws data diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -project(lws-minimal-ws-broker) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-broker) -set(SRCS minimal-ws-broker.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/minimal-ws-broker.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/minimal-ws-broker.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/minimal-ws-broker.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/minimal-ws-broker.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -/* - * lws-minimal-ws-broker - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws, - * with an added publish / broker / subscribe ws server. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal.c" - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - LWS_PLUGIN_PROTOCOL_MINIMAL, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws broker | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ - -function get_appropriate_ws_url(extra_url) -{ - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; -} - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -document.addEventListener("DOMContentLoaded", function() { - - var subscriber_ws = new_ws(get_appropriate_ws_url(""), "lws-minimal-broker"); - try { - subscriber_ws.onopen = function() { - document.getElementById("b").disabled = 0; - }; - - subscriber_ws.onmessage =function got_packet(msg) { - document.getElementById("r").value = - document.getElementById("r").value + msg.data + "\n"; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - subscriber_ws.onclose = function(){ - document.getElementById("b").disabled = 1; - }; - } catch(exception) { - alert("

Error " + exception); - } - - var publisher_ws = new_ws(get_appropriate_ws_url("/publisher"), "lws-minimal-broker"); - try { - publisher_ws.onopen = function() { - document.getElementById("m").disabled = 0; - }; - - publisher_ws.onmessage =function got_packet(msg) { - }; - - publisher_ws.onclose = function(){ - document.getElementById("m").disabled = 1; - }; - } catch(exception) { - alert("

Error " + exception); - } - - function sendmsg() - { - publisher_ws.send(document.getElementById("m").value); - document.getElementById("m").value = ""; - } - - document.getElementById("b").addEventListener("click", sendmsg); - -}, false); - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ - - - - - - - -
- - LWS chat minimal ws broker example.
- This page opens two separate ws connections...
- A subscriber ws connection fills this textarea
- with data it receives from the broker... -
-
-
-
- ... and a publisher ws connection sends the string
- in the box below to the broker when you press Send.
- - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/protocol_lws_minimal.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/protocol_lws_minimal.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/protocol_lws_minimal.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/protocol_lws_minimal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,250 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal-broker" - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This implements a minimal "broker", for systems that look like this - * - * [ publisher ws client ] <-> [ ws server broker ws server ] <-> [ ws client subscriber ] - * - * The "publisher" role is to add data to the broker. - * - * The "subscriber" role is to hear about all data added to the system. - * - * The "broker" role is to manage incoming data from publishers and pass it out - * to subscribers. - * - * Any number of publishers and subscribers are supported. - * - * This example implements a single ws server, using one ws protocol, that treats ws - * connections as being in publisher or subscriber mode according to the URL the ws - * connection was made to. ws connections to "/publisher" URL are understood to be - * publishing data and to any other URL, subscribing. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -/* one of these created for each message */ - -struct msg { - void *payload; /* is malloc'd */ - size_t len; -}; - -/* one of these is created for each client connecting to us */ - -struct per_session_data__minimal { - struct per_session_data__minimal *pss_list; - struct lws *wsi; - uint32_t tail; - char publishing; /* nonzero: peer is publishing to us */ -}; - -/* one of these is created for each vhost our protocol is used with */ - -struct per_vhost_data__minimal { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - - struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ - - struct lws_ring *ring; /* ringbuffer holding unsent messages */ -}; - -/* destroys the message when everyone has had a copy of it */ - -static void -__minimal_destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} - -static int -callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal *pss = - (struct per_session_data__minimal *)user; - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - const struct msg *pmsg; - struct msg amsg; - char buf[32]; - int n, m; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__minimal)); - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - - vhd->ring = lws_ring_create(sizeof(struct msg), 8, - __minimal_destroy_message); - if (!vhd->ring) - return 1; - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - lws_ring_destroy(vhd->ring); - break; - - case LWS_CALLBACK_ESTABLISHED: - pss->tail = lws_ring_get_oldest_tail(vhd->ring); - pss->wsi = wsi; - if (lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI) > 0) - pss->publishing = !strcmp(buf, "/publisher"); - if (!pss->publishing) - /* add subscribers to the list of live pss held in the vhd */ - lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); - break; - - case LWS_CALLBACK_CLOSED: - /* remove our closing pss from the list of live pss */ - lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, - pss, vhd->pss_list); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - - if (pss->publishing) - break; - - pmsg = lws_ring_get_element(vhd->ring, &pss->tail); - if (!pmsg) - break; - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE, - pmsg->len, LWS_WRITE_TEXT); - if (m < (int)pmsg->len) { - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - lws_ring_consume_and_update_oldest_tail( - vhd->ring, /* lws_ring object */ - struct per_session_data__minimal, /* type of objects with tails */ - &pss->tail, /* tail of guy doing the consuming */ - 1, /* number of payload objects being consumed */ - vhd->pss_list, /* head of list of objects with tails */ - tail, /* member name of tail in objects with tails */ - pss_list /* member name of next object in objects with tails */ - ); - - /* more to do? */ - if (lws_ring_get_element(vhd->ring, &pss->tail)) - /* come back as soon as we can write more */ - lws_callback_on_writable(pss->wsi); - break; - - case LWS_CALLBACK_RECEIVE: - - if (!pss->publishing) - break; - - /* - * For test, our policy is ignore publishing when there are - * no subscribers connected. - */ - if (!vhd->pss_list) - break; - - n = (int)lws_ring_get_count_free_elements(vhd->ring); - if (!n) { - lwsl_user("dropping!\n"); - break; - } - - amsg.len = len; - /* notice we over-allocate by LWS_PRE */ - amsg.payload = malloc(LWS_PRE + len); - if (!amsg.payload) { - lwsl_user("OOM: dropping\n"); - break; - } - - memcpy((char *)amsg.payload + LWS_PRE, in, len); - if (!lws_ring_insert(vhd->ring, &amsg, 1)) { - __minimal_destroy_message(&amsg); - lwsl_user("dropping 2!\n"); - break; - } - - /* - * let every subscriber know we want to write something - * on them as soon as they are ready - */ - lws_start_foreach_llp(struct per_session_data__minimal **, - ppss, vhd->pss_list) { - if (!(*ppss)->publishing) - lws_callback_on_writable((*ppss)->wsi); - } lws_end_foreach_llp(ppss, pss_list); - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL \ - { \ - "lws-minimal-broker", \ - callback_minimal, \ - sizeof(struct per_session_data__minimal), \ - 128, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/README.md libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-broker/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-broker/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -# lws minimal ws broker - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-ws-broker -[2018/03/15 12:23:12:1559] USER: LWS minimal ws broker | visit http://localhost:7681 -[2018/03/15 12:23:12:1560] NOTICE: Creating Vhost 'default' port 7681, 2 protocols, IPv6 off -``` - -Visit http://localhost:7681 on multiple browser windows - -The page opens a subscribe mode ws connection back to the broker, -and a publisher mode ws connection back to the broker. - -The textarea shows the data from the subscription connection. - -If you type text is in the text box and press send, the text -is passed to the broker on the publisher ws connection and -sent to all subscribers. diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-ws-server) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-server) -set(SRCS minimal-ws-server.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() \ No newline at end of file diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/localhost-100y.key libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/minimal-ws-server.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/minimal-ws-server.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/minimal-ws-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/minimal-ws-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,117 +0,0 @@ -/* - * lws-minimal-ws-server - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws, - * with an added websocket chat server. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal.c" - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - LWS_PLUGIN_PROTOCOL_MINIMAL, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static const lws_retry_bo_t retry = { - .secs_since_valid_ping = 3, - .secs_since_valid_hangup = 10, -}; - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws server | visit http://localhost:7681 (-s = use TLS / https)\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - info.vhost_name = "localhost"; - info.ws_ping_pong_interval = 10; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - if (lws_cmdline_option(argc, argv, "-s")) { - lwsl_user("Server using TLS\n"); - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - if (lws_cmdline_option(argc, argv, "-h")) - info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK; - - if (lws_cmdline_option(argc, argv, "-v")) - info.retry_and_idle_policy = &retry; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - -function get_appropriate_ws_url(extra_url) -{ - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; -} - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -document.addEventListener("DOMContentLoaded", function() { - - var ws = new_ws(get_appropriate_ws_url(""), "lws-minimal"); - try { - ws.onopen = function() { - document.getElementById("m").disabled = 0; - document.getElementById("b").disabled = 0; - }; - - ws.onmessage =function got_packet(msg) { - document.getElementById("r").value = - document.getElementById("r").value + msg.data + "\n"; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - ws.onclose = function(){ - document.getElementById("m").disabled = 1; - document.getElementById("b").disabled = 1; - }; - } catch(exception) { - alert("

Error " + exception); - } - - function sendmsg() - { - ws.send(document.getElementById("m").value); - document.getElementById("m").value = ""; - } - - document.getElementById("b").addEventListener("click", sendmsg); - -}, false); - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ - - - - - - - -
- - LWS chat minimal ws server example.
- Chat is sent to all browsers open on this page. -
-
-
- - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,187 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal" - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This version holds a single message at a time, which may be lost if a new - * message comes. See the minimal-ws-server-ring sample for the same thing - * but using an lws_ring ringbuffer to hold up to 8 messages at a time. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -/* one of these created for each message */ - -struct msg { - void *payload; /* is malloc'd */ - size_t len; -}; - -/* one of these is created for each client connecting to us */ - -struct per_session_data__minimal { - struct per_session_data__minimal *pss_list; - struct lws *wsi; - int last; /* the last message number we sent */ -}; - -/* one of these is created for each vhost our protocol is used with */ - -struct per_vhost_data__minimal { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - - struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ - - struct msg amsg; /* the one pending message... */ - int current; /* the current message number we are caching */ -}; - -/* destroys the message when everyone has had a copy of it */ - -static void -__minimal_destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} - -static int -callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal *pss = - (struct per_session_data__minimal *)user; - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - int m; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__minimal)); - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - break; - - case LWS_CALLBACK_ESTABLISHED: - /* add ourselves to the list of live pss held in the vhd */ - lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); - pss->wsi = wsi; - pss->last = vhd->current; - break; - - case LWS_CALLBACK_CLOSED: - /* remove our closing pss from the list of live pss */ - lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, - pss, vhd->pss_list); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - if (!vhd->amsg.payload) - break; - - if (pss->last == vhd->current) - break; - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, ((unsigned char *)vhd->amsg.payload) + - LWS_PRE, vhd->amsg.len, LWS_WRITE_TEXT); - if (m < (int)vhd->amsg.len) { - lwsl_err("ERROR %d writing to ws\n", m); - return -1; - } - - pss->last = vhd->current; - break; - - case LWS_CALLBACK_RECEIVE: - if (vhd->amsg.payload) - __minimal_destroy_message(&vhd->amsg); - - vhd->amsg.len = len; - /* notice we over-allocate by LWS_PRE */ - vhd->amsg.payload = malloc(LWS_PRE + len); - if (!vhd->amsg.payload) { - lwsl_user("OOM: dropping\n"); - break; - } - - memcpy((char *)vhd->amsg.payload + LWS_PRE, in, len); - vhd->current++; - - /* - * let everybody know we want to write something on them - * as soon as they are ready - */ - lws_start_foreach_llp(struct per_session_data__minimal **, - ppss, vhd->pss_list) { - lws_callback_on_writable((*ppss)->wsi); - } lws_end_foreach_llp(ppss, pss_list); - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL \ - { \ - "lws-minimal", \ - callback_minimal, \ - sizeof(struct per_session_data__minimal), \ - 128, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/README.md libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -# lws minimal ws server - -## build - -``` - $ cmake . && make -``` - -## Commandline Options - -Option|Meaning ----|--- --d|Set logging verbosity --s|Serve using TLS selfsigned cert (ie, connect to it with https://...) --h|Strict Host: header checking against vhost name (localhost) and port --v|Connection validity use 3s / 10s instead of default 5m / 5m10s - -## usage - -``` - $ ./lws-minimal-ws-server -[2018/03/04 09:30:02:7986] USER: LWS minimal ws server | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 on multiple browser windows - -Text you type in any browser window is sent to all of them. - -For simplicity of this example, only one line of text is cached at the server. diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-echo/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-echo/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-echo/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-echo/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-ws-server-echo) -cmake_minimum_required(VERSION 2.8.9) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-server-echo) -set(SRCS minimal-ws-server-echo.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-echo/minimal-ws-server-echo.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-echo/minimal-ws-server-echo.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-echo/minimal-ws-server-echo.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-echo/minimal-ws-server-echo.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,118 +0,0 @@ -/* - * lws-minimal-ws-server-echo - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a ws server that echoes back what it was sent, in a way - * compatible with autobahn -m fuzzingclient - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal_server_echo.c" - -static struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_SERVER_ECHO, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted, port = 7681, options; - -/* pass pointers to shared vars to the protocol */ - -static const struct lws_protocol_vhost_options pvo_options = { - NULL, - NULL, - "options", /* pvo name */ - (void *)&options /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo_interrupted = { - &pvo_options, - NULL, - "interrupted", /* pvo name */ - (void *)&interrupted /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo_interrupted, /* "child" pvo linked-list */ - "lws-minimal-server-echo", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; -static const struct lws_extension extensions[] = { - { - "permessage-deflate", - lws_extension_callback_pm_deflate, - "permessage-deflate" - "; client_no_context_takeover" - "; client_max_window_bits" - }, - { NULL, NULL, NULL /* terminator */ } -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws client echo + permessage-deflate + multifragment bulk message\n"); - lwsl_user(" lws-minimal-ws-client-echo [-n (no exts)] [-p port] [-o (once)]\n"); - - - if ((p = lws_cmdline_option(argc, argv, "-p"))) - port = atoi(p); - - if (lws_cmdline_option(argc, argv, "-o")) - options |= 1; - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = port; - info.protocols = protocols; - info.pvo = &pvo; - if (!lws_cmdline_option(argc, argv, "-n")) - info.extensions = extensions; - info.pt_serv_buf_size = 32 * 1024; - info.options = LWS_SERVER_OPTION_VALIDATE_UTF8 | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - lwsl_user("Completed %s\n", interrupted == 2 ? "OK" : "failed"); - - return interrupted != 2; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,265 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal-server-echo" - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * The protocol shows how to send and receive bulk messages over a ws connection - * that optionally may have the permessage-deflate extension negotiated on it. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -#define RING_DEPTH 4096 - -/* one of these created for each message */ - -struct msg { - void *payload; /* is malloc'd */ - size_t len; - char binary; - char first; - char final; -}; - -struct per_session_data__minimal_server_echo { - struct lws_ring *ring; - uint32_t msglen; - uint32_t tail; - uint8_t completed:1; - uint8_t flow_controlled:1; - uint8_t write_consume_pending:1; -}; - -struct vhd_minimal_server_echo { - struct lws_context *context; - struct lws_vhost *vhost; - - int *interrupted; - int *options; -}; - -static void -__minimal_destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} -#include -static int -callback_minimal_server_echo(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal_server_echo *pss = - (struct per_session_data__minimal_server_echo *)user; - struct vhd_minimal_server_echo *vhd = (struct vhd_minimal_server_echo *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - const struct msg *pmsg; - struct msg amsg; - int m, n, flags; - - switch (reason) { - - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct vhd_minimal_server_echo)); - if (!vhd) - return -1; - - vhd->context = lws_get_context(wsi); - vhd->vhost = lws_get_vhost(wsi); - - /* get the pointers we were passed in pvo */ - - vhd->interrupted = (int *)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "interrupted")->value; - vhd->options = (int *)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "options")->value; - break; - - case LWS_CALLBACK_ESTABLISHED: - /* generate a block of output before travis times us out */ - lwsl_warn("LWS_CALLBACK_ESTABLISHED\n"); - pss->ring = lws_ring_create(sizeof(struct msg), RING_DEPTH, - __minimal_destroy_message); - if (!pss->ring) - return 1; - pss->tail = 0; - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - - lwsl_user("LWS_CALLBACK_SERVER_WRITEABLE\n"); - - if (pss->write_consume_pending) { - /* perform the deferred fifo consume */ - lws_ring_consume_single_tail(pss->ring, &pss->tail, 1); - pss->write_consume_pending = 0; - } - - pmsg = lws_ring_get_element(pss->ring, &pss->tail); - if (!pmsg) { - lwsl_user(" (nothing in ring)\n"); - break; - } - - flags = lws_write_ws_flags( - pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT, - pmsg->first, pmsg->final); - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, ((unsigned char *)pmsg->payload) + - LWS_PRE, pmsg->len, flags); - if (m < (int)pmsg->len) { - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - lwsl_user(" wrote %d: flags: 0x%x first: %d final %d\n", - m, flags, pmsg->first, pmsg->final); - /* - * Workaround deferred deflate in pmd extension by only - * consuming the fifo entry when we are certain it has been - * fully deflated at the next WRITABLE callback. You only need - * this if you're using pmd. - */ - pss->write_consume_pending = 1; - lws_callback_on_writable(wsi); - - if (pss->flow_controlled && - (int)lws_ring_get_count_free_elements(pss->ring) > RING_DEPTH - 5) { - lws_rx_flow_control(wsi, 1); - pss->flow_controlled = 0; - } - - if ((*vhd->options & 1) && pmsg && pmsg->final) - pss->completed = 1; - - break; - - case LWS_CALLBACK_RECEIVE: - - lwsl_user("LWS_CALLBACK_RECEIVE: %4d (rpp %5d, first %d, " - "last %d, bin %d, msglen %d (+ %d = %d))\n", - (int)len, (int)lws_remaining_packet_payload(wsi), - lws_is_first_fragment(wsi), - lws_is_final_fragment(wsi), - lws_frame_is_binary(wsi), pss->msglen, (int)len, - (int)pss->msglen + (int)len); - - if (len) { - ; - //puts((const char *)in); - //lwsl_hexdump_notice(in, len); - } - - amsg.first = lws_is_first_fragment(wsi); - amsg.final = lws_is_final_fragment(wsi); - amsg.binary = lws_frame_is_binary(wsi); - n = (int)lws_ring_get_count_free_elements(pss->ring); - if (!n) { - lwsl_user("dropping!\n"); - break; - } - - if (amsg.final) - pss->msglen = 0; - else - pss->msglen += len; - - amsg.len = len; - /* notice we over-allocate by LWS_PRE */ - amsg.payload = malloc(LWS_PRE + len); - if (!amsg.payload) { - lwsl_user("OOM: dropping\n"); - break; - } - - memcpy((char *)amsg.payload + LWS_PRE, in, len); - if (!lws_ring_insert(pss->ring, &amsg, 1)) { - __minimal_destroy_message(&amsg); - lwsl_user("dropping!\n"); - break; - } - lws_callback_on_writable(wsi); - - if (n < 3 && !pss->flow_controlled) { - pss->flow_controlled = 1; - lws_rx_flow_control(wsi, 0); - } - break; - - case LWS_CALLBACK_CLOSED: - lwsl_user("LWS_CALLBACK_CLOSED\n"); - lws_ring_destroy(pss->ring); - - if (*vhd->options & 1) { - if (!*vhd->interrupted) - *vhd->interrupted = 1 + pss->completed; - lws_cancel_service(lws_get_context(wsi)); - } - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL_SERVER_ECHO \ - { \ - "lws-minimal-server-echo", \ - callback_minimal_server_echo, \ - sizeof(struct per_session_data__minimal_server_echo), \ - 1024, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_SERVER_ECHO -}; - -int -init_protocol_minimal_server_echo(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal_server_echo(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-echo/README.md libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-echo/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-echo/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-echo/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -# lws minimal ws server + permessage-deflate echo - -This example serves no-protocl-name ws on localhost:7681 -and echoes back anything that comes from the client. - -You can use it for testing lws against Autobahn (use the --p option to tell it to listen on 9001 for that) - -## build - -``` - $ cmake . && make -``` - -## usage - -Commandline option|Meaning ----|--- --d |Debug verbosity in decimal, eg, -d15 --p port|Port to connect to --u url|URL path part to connect to --o|Finish after one connection - -``` - $ ./lws-minimal-ws-server-echo -[2018/04/24 10:29:34:6212] USER: LWS minimal ws server echo + permessage-deflate + multifragment bulk message -[2018/04/24 10:29:34:6213] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -... -``` - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-ws-server-pmd) -cmake_minimum_required(VERSION 2.8.9) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-server-pmd) -set(SRCS minimal-ws-server-pmd.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/minimal-ws-server-pmd.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/minimal-ws-server-pmd.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/minimal-ws-server-pmd.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/minimal-ws-server-pmd.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,108 +0,0 @@ -/* - * lws-minimal-ws-server - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal.c" - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - LWS_PLUGIN_PROTOCOL_MINIMAL, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -static const struct lws_extension extensions[] = { - { - "permessage-deflate", - lws_extension_callback_pm_deflate, - "permessage-deflate" - "; client_no_context_takeover" - "; client_max_window_bits" - }, - { NULL, NULL, NULL /* terminator */ } -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws server + permessage-deflate | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - info.extensions = extensions; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ - -function get_appropriate_ws_url(extra_url) -{ - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; -} - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -document.addEventListener("DOMContentLoaded", function() { - - var ws = new_ws(get_appropriate_ws_url(""), "lws-minimal"); - try { - ws.onopen = function() { - document.getElementById("m").disabled = 0; - document.getElementById("b").disabled = 0; - document.getElementById("status").textContent = "ws open "+ ws.extensions; - }; - - ws.onmessage =function got_packet(msg) { - document.getElementById("r").value = - document.getElementById("r").value + msg.data + "\n"; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - ws.onclose = function(){ - document.getElementById("m").disabled = 1; - document.getElementById("b").disabled = 1; - document.getElementById("status").textContent = "ws closed"; - }; - } catch(exception) { - alert("

Error " + exception); - } - - function sendmsg() - { - ws.send(document.getElementById("m").value); - document.getElementById("m").value = ""; - } - - document.getElementById("b").addEventListener("click", sendmsg); - -}, false); - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ - - - - - - - - -
- - LWS chat minimal ws server example.
- Chat is sent to all browsers open on this page.
-
- Ws closed
-
-
- - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,193 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal" - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This version holds a single message at a time, which may be lost if a new - * message comes. See the minimal-ws-server-ring sample for the same thing - * but using an lws_ring ringbuffer to hold up to 8 messages at a time. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -/* one of these created for each message */ - -struct msg { - void *payload; /* is malloc'd */ - size_t len; -}; - -/* one of these is created for each client connecting to us */ - -struct per_session_data__minimal { - struct per_session_data__minimal *pss_list; - struct lws *wsi; - int last; /* the last message number we sent */ -}; - -/* one of these is created for each vhost our protocol is used with */ - -struct per_vhost_data__minimal { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - - struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ - - struct msg amsg; /* the one pending message... */ - int current; /* the current message number we are caching */ -}; - -/* destroys the message when everyone has had a copy of it */ - -static void -__minimal_destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} - -static int -callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal *pss = - (struct per_session_data__minimal *)user; - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - int m; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__minimal)); - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - break; - - case LWS_CALLBACK_ESTABLISHED: - /* add ourselves to the list of live pss held in the vhd */ - pss->pss_list = vhd->pss_list; - vhd->pss_list = pss; - pss->wsi = wsi; - pss->last = vhd->current; - break; - - case LWS_CALLBACK_CLOSED: - /* remove our closing pss from the list of live pss */ - lws_start_foreach_llp(struct per_session_data__minimal **, - ppss, vhd->pss_list) { - if (*ppss == pss) { - *ppss = pss->pss_list; - break; - } - } lws_end_foreach_llp(ppss, pss_list); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - if (!vhd->amsg.payload) - break; - - if (pss->last == vhd->current) - break; - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, ((unsigned char *)vhd->amsg.payload) + - LWS_PRE, vhd->amsg.len, LWS_WRITE_TEXT); - if (m < (int)vhd->amsg.len) { - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - pss->last = vhd->current; - break; - - case LWS_CALLBACK_RECEIVE: - if (vhd->amsg.payload) - __minimal_destroy_message(&vhd->amsg); - - vhd->amsg.len = len; - /* notice we over-allocate by LWS_PRE */ - vhd->amsg.payload = malloc(LWS_PRE + len); - if (!vhd->amsg.payload) { - lwsl_user("OOM: dropping\n"); - break; - } - - memcpy((char *)vhd->amsg.payload + LWS_PRE, in, len); - vhd->current++; - - /* - * let everybody know we want to write something on them - * as soon as they are ready - */ - lws_start_foreach_llp(struct per_session_data__minimal **, - ppss, vhd->pss_list) { - lws_callback_on_writable((*ppss)->wsi); - } lws_end_foreach_llp(ppss, pss_list); - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL \ - { \ - "lws-minimal", \ - callback_minimal, \ - sizeof(struct per_session_data__minimal), \ - 128, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/README.md libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -# lws minimal ws server + permessage-deflate - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-ws-server -[2018/03/04 09:30:02:7986] USER: LWS minimal ws server | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 on multiple browser windows - -Text you type in any browser window is sent to all of them. - -For simplicity of this example, only one line of text is cached at the server. - -The ws connection is made via permessage-deflate extension. diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-ws-server-pmd-bulk) -cmake_minimum_required(VERSION 2.8.9) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-server-pmd-bulk) -set(SRCS minimal-ws-server-pmd-bulk.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -#require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/minimal-ws-server-pmd-bulk.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/minimal-ws-server-pmd-bulk.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/minimal-ws-server-pmd-bulk.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/minimal-ws-server-pmd-bulk.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -/* - * lws-minimal-ws-server - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal_pmd_bulk.c" - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted, options; - -/* pass pointers to shared vars to the protocol */ - -static const struct lws_protocol_vhost_options pvo_options = { - NULL, - NULL, - "options", /* pvo name */ - (void *)&options /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo_interrupted = { - &pvo_options, - NULL, - "interrupted", /* pvo name */ - (void *)&interrupted /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo_interrupted, /* "child" pvo linked-list */ - "lws-minimal-pmd-bulk", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -static const struct lws_extension extensions[] = { - { - "permessage-deflate", - lws_extension_callback_pm_deflate, - "permessage-deflate" - "; client_no_context_takeover" - "; client_max_window_bits" - }, - { NULL, NULL, NULL /* terminator */ } -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws server + permessage-deflate | visit http://localhost:7681\n"); - lwsl_user(" %s [-n (no exts)] [-c (compressible)] [-b (blob)]\n", argv[0]); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - info.pvo = &pvo; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - if (!lws_cmdline_option(argc, argv, "-n")) - info.extensions = extensions; - - if (lws_cmdline_option(argc, argv, "-c")) - options |= 1; /* send compressible text */ - - if (lws_cmdline_option(argc, argv, "-b")) - options |= 2; /* send in one giant blob */ - - info.pt_serv_buf_size = 32 * 1024; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ - -function get_appropriate_ws_url(extra_url) -{ - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; -} - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -document.addEventListener("DOMContentLoaded", function() { - - var ws = new_ws(get_appropriate_ws_url(""), "lws-minimal-pmd-bulk"); - try { - ws.onopen = function() { - document.getElementById("r").disabled = 0; - document.getElementById("status").textContent = "ws open "+ ws.extensions; - }; - - ws.onmessage = function got_packet(msg) { - console.log("Received ws message len " + msg.data.size); - document.getElementById("r").value = - document.getElementById("r").value + "\nReceived: " + msg.data.size + " bytes\n"; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - - /* echo it back */ - ws.send(msg.data); - }; - - ws.onclose = function(){ - document.getElementById("r").disabled = 1; - document.getElementById("status").textContent = "ws closed"; - }; - } catch(exception) { - alert("

Error " + exception); - } - -}, false); - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ - - - - - - - -
- - LWS bulk transfer example.
- A large ws message is sent to all browsers open on this page.
- The browser js echoes the large ws message back to the server.
-
- Ws closed
-
-
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/protocol_lws_minimal_pmd_bulk.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/protocol_lws_minimal_pmd_bulk.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/protocol_lws_minimal_pmd_bulk.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/protocol_lws_minimal_pmd_bulk.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,256 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal-pmd-bulk" - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * The protocol shows how to send and receive bulk messages over a ws connection - * that optionally may have the permessage-deflate extension negotiated on it. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -/* - * We will produce a large ws message either from this text repeated many times, - * or from 0x40 + a 6-bit pseudorandom number - */ - -static const char * const redundant_string = - "No one would have believed in the last years of the nineteenth " - "century that this world was being watched keenly and closely by " - "intelligences greater than man's and yet as mortal as his own; that as " - "men busied themselves about their various concerns they were " - "scrutinised and studied, perhaps almost as narrowly as a man with a " - "microscope might scrutinise the transient creatures that swarm and " - "multiply in a drop of water. With infinite complacency men went to " - "and fro over this globe about their little affairs, serene in their " - "assurance of their empire over matter. It is possible that the " - "infusoria under the microscope do the same. No one gave a thought to " - "the older worlds of space as sources of human danger, or thought of " - "them only to dismiss the idea of life upon them as impossible or " - "improbable. It is curious to recall some of the mental habits of " - "those departed days. At most terrestrial men fancied there might be " - "other men upon Mars, perhaps inferior to themselves and ready to " - "welcome a missionary enterprise. Yet across the gulf of space, minds " - "that are to our minds as ours are to those of the beasts that perish, " - "intellects vast and cool and unsympathetic, regarded this earth with " - "envious eyes, and slowly and surely drew their plans against us. And " - "early in the twentieth century came the great disillusionment. " -; - -/* this reflects the length of the string above */ -#define REPEAT_STRING_LEN 1337 -/* this is the total size of the ws message we will send */ -#define MESSAGE_SIZE (100 * REPEAT_STRING_LEN) -/* this is how much we will send each time the connection is writable */ -#define MESSAGE_CHUNK_SIZE (1 * 1024) - -/* one of these is created for each client connecting to us */ - -struct per_session_data__minimal_pmd_bulk { - int position_tx, position_rx; - uint64_t rng_rx, rng_tx; -}; - -struct vhd_minimal_pmd_bulk { - int *interrupted; - /* - * b0 = 1: test compressible text, = 0: test uncompressible binary - * b1 = 1: send as a single blob, = 0: send as fragments - */ - int *options; -}; - -static uint64_t rng(uint64_t *r) -{ - *r ^= *r << 21; - *r ^= *r >> 35; - *r ^= *r << 4; - - return *r; -} - -static int -callback_minimal_pmd_bulk(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal_pmd_bulk *pss = - (struct per_session_data__minimal_pmd_bulk *)user; - struct vhd_minimal_pmd_bulk *vhd = (struct vhd_minimal_pmd_bulk *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - uint8_t buf[LWS_PRE + MESSAGE_SIZE], *start = &buf[LWS_PRE], *p; - int n, m, flags, olen, amount; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct vhd_minimal_pmd_bulk)); - if (!vhd) - return -1; - - /* get the pointer to "interrupted" we were passed in pvo */ - vhd->interrupted = (int *)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "interrupted")->value; - vhd->options = (int *)lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "options")->value; - break; - - case LWS_CALLBACK_ESTABLISHED: - pss->rng_tx = 4; - pss->rng_rx = 4; - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - if (pss->position_tx == MESSAGE_SIZE) - break; - - amount = MESSAGE_CHUNK_SIZE; - if ((*vhd->options) & 2) { - amount = MESSAGE_SIZE; - lwsl_user("(writing as one blob of %d)\n", amount); - } - - /* fill up one chunk's worth of message content */ - - p = start; - n = amount; - if (n > MESSAGE_SIZE - pss->position_tx) - n = MESSAGE_SIZE - pss->position_tx; - - flags = lws_write_ws_flags(LWS_WRITE_BINARY, !pss->position_tx, - pss->position_tx + n == MESSAGE_SIZE); - - /* - * select between producing compressible repeated text, - * or uncompressible PRNG output - */ - - if (*vhd->options & 1) { - while (n) { - size_t s; - - m = pss->position_tx % REPEAT_STRING_LEN; - s = REPEAT_STRING_LEN - m; - if (s > (size_t)n) - s = n; - memcpy(p, &redundant_string[m], s); - pss->position_tx += s; - p += s; - n -= s; - } - } else { - pss->position_tx += n; - while (n--) - *p++ = rng(&pss->rng_tx); - } - - n = lws_ptr_diff(p, start); - m = lws_write(wsi, start, n, flags); - lwsl_user("LWS_CALLBACK_SERVER_WRITEABLE: wrote %d\n", n); - if (m < n) { - lwsl_err("ERROR %d / %d writing ws\n", m, n); - return -1; - } - if (pss->position_tx != MESSAGE_SIZE) /* if more to do... */ - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_RECEIVE: - lwsl_user("LWS_CALLBACK_RECEIVE: %4d (pss->pos=%d, rpp %5d, last %d)\n", - (int)len, (int)pss->position_rx, (int)lws_remaining_packet_payload(wsi), - lws_is_final_fragment(wsi)); - olen = len; - - if (*vhd->options & 1) { - while (len) { - size_t s; - m = pss->position_rx % REPEAT_STRING_LEN; - s = REPEAT_STRING_LEN - m; - if (s > len) - s = len; - if (memcmp(in, &redundant_string[m], s)) { - lwsl_user("echo'd data doesn't match\n"); - return -1; - } - pss->position_rx += s; - in = ((char *)in) + s; - len -= s; - } - } else { - p = (uint8_t *)in; - pss->position_rx += len; - while (len--) { - if (*p++ != (uint8_t)rng(&pss->rng_rx)) { - lwsl_user("echo'd data doesn't match: 0x%02X 0x%02X (%d)\n", - *(p - 1), (int)(0x40 + (pss->rng_rx & 0x3f)), - (int)((pss->position_rx - olen) + olen - len)); - lwsl_hexdump_notice(in, olen); - return -1; - } - } - if (pss->position_rx == MESSAGE_SIZE) - pss->position_rx = 0; - } - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK \ - { \ - "lws-minimal-pmd-bulk", \ - callback_minimal_pmd_bulk, \ - sizeof(struct per_session_data__minimal_pmd_bulk), \ - 4096, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK -}; - -int -init_protocol_minimal_pmd_bulk(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal_pmd_bulk(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/README.md libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -# lws minimal ws server + permessage-deflate for bulk traffic - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-ws-server-pmd-bulk -[2018/03/04 09:30:02:7986] USER: LWS minimal ws server | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 in your browser - -One or another kind of bulk ws transfer is made to the browser. - -The ws connection is made via permessage-deflate extension. diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -project(lws-minimal-ws-server-pmd-corner) -cmake_minimum_required(VERSION 2.8.9) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-server-pmd-corner) -set(SRCS minimal-ws-server-pmd-corner.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/minimal-ws-server-pmd-corner.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/minimal-ws-server-pmd-corner.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/minimal-ws-server-pmd-corner.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/minimal-ws-server-pmd-corner.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,108 +0,0 @@ -/* - * lws-minimal-ws-server - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal.c" - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - LWS_PLUGIN_PROTOCOL_MINIMAL, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -static const struct lws_extension extensions[] = { - { - "permessage-deflate", - lws_extension_callback_pm_deflate, - "permessage-deflate" - "; client_no_context_takeover" - "; client_max_window_bits" - }, - { NULL, NULL, NULL /* terminator */ } -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws server + permessage-deflate Corner Cases | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - info.extensions = extensions; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ - -function get_appropriate_ws_url(extra_url) -{ - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; -} - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -var ws = new Array(); - -function conn(n) -{ - ws[n] = new_ws(get_appropriate_ws_url("/" + (n + 1)), "lws-minimal"); - ws[n].n = n; - try { - ws[n].onopen = function() { - document.getElementById("r").disabled = 0; - document.getElementById("status").textContent = - document.getElementById("status").textContent + " " + - "ws open "+ ws[n].extensions; - }; - - ws[n].onmessage = function got_packet(msg) { - if (typeof msg.data !== "string") { - //console.log(msg.data); - document.getElementById("r").value = - document.getElementById("r").value + - ws[n].n + " " + "blob uncompressed length " + - msg.data.size + "\n"; - } else - document.getElementById("r").value = - document.getElementById("r").value + msg.data + "\n"; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - ws[n].onclose = function(){ - document.getElementById("r").disabled = 1; - document.getElementById("status").textContent = "ws closed"; - }; - } catch(exception) { - alert("

Error " + exception); - } -} - -window.addEventListener("load", function() { - - var n; - - /* - * we make 5 individual connections. Because if we don't, by default pmd - * will reuse its dictionary to make subsequent tests very short. - */ - - for (n = 0; n < 5; n++) - conn(n); - - console.log("load"); - -}, false); - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ - - - - - - - - -
- - LWS pmd corner case test.
- A ws link is made back to the server and results shown here.
- It should show four binary blobs of increasing size. -
-
- Ws closed
-
- - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/protocol_lws_minimal.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/protocol_lws_minimal.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/protocol_lws_minimal.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/protocol_lws_minimal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,304 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal" - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This version holds a single message at a time, which may be lost if a new - * message comes. See the minimal-ws-server-ring sample for the same thing - * but using an lws_ring ringbuffer to hold up to 8 messages at a time. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -/* - * This came from... - * - * cat /dev/urandom | hexdump -C -n 1024 | tr -s ' ' | cut -d' ' -f 2-17 | head -n-1 | sed "s/\ /, 0x/g" | sed "s/^/0x/g" | sed "s/\$/,/g" - * - * ...then the length tuned by hand to get the ciphertext sizes that we want to - * confirm are OK. - * - * We can only pass in a maximum of one compression buffer of input at a time, - * which is 1024 by default. - */ - -unsigned char uncompressible[] = { - 0xfe, 0xcc, 0x47, 0xcb, 0x10, 0xf4, 0x3c, 0x85, - 0x8e, 0xd4, 0xe2, 0xf6, 0xd1, 0xd1, 0xdb, 0x64, - 0x94, 0x50, 0xf6, 0x14, 0x25, 0x03, 0x09, 0x3a, - 0xb1, 0x47, 0x86, 0xa8, 0x3c, 0x4f, 0x3b, 0x98, - 0x7b, 0x3e, 0x67, 0x3e, 0x22, 0xc5, 0x4c, 0x45, - 0xf4, 0xf7, 0xb5, 0x79, 0xc0, 0x26, 0x6e, 0x5c, - 0xf4, 0x10, 0x04, 0xa9, 0x3c, 0x4f, 0xed, 0xc5, - 0x3d, 0xd4, 0x9f, 0x9f, 0xa3, 0xdb, 0x29, 0xeb, - 0x1e, 0xe1, 0x52, 0xab, 0xb5, 0x75, 0x25, 0x86, - 0x86, 0x02, 0x2c, 0x9d, 0x9c, 0x86, 0x46, 0x92, - 0xe9, 0x04, 0xd8, 0x2c, 0x7d, 0x8a, 0x56, 0xe1, - 0xe1, 0xb6, 0x84, 0x4d, 0x17, 0x30, 0x01, 0x60, - 0xa6, 0xf4, 0xba, 0xc9, 0x5a, 0x29, 0xe3, 0x05, - 0xe1, 0xb4, 0x0b, 0x23, 0x74, 0x93, 0x25, 0x76, - 0xce, 0x15, 0xe4, 0x82, 0x9f, 0xbf, 0xe8, 0x6a, - 0x4a, 0xc5, 0xc2, 0x22, 0x91, 0x80, 0xb5, 0xd7, - 0xb3, 0xce, 0x70, 0x0e, 0xf7, 0xbb, 0x2f, 0xc5, - 0x83, 0x39, 0x86, 0xe5, 0x3e, 0xb7, 0x83, 0x87, - 0xc2, 0xeb, 0xc8, 0xed, 0x59, 0x26, 0xc1, 0xe6, - 0x80, 0x17, 0x3c, 0x29, 0x53, 0x4c, 0x1c, 0x3f, - 0x54, 0xbe, 0x34, 0x26, 0x72, 0xed, 0x38, 0x10, - 0xd1, 0x37, 0x07, 0x2d, 0x12, 0x31, 0x9b, 0xc5, - 0x92, 0x09, 0x13, 0x5d, 0x8e, 0xef, 0xdb, 0x52, - 0x7f, 0x7d, 0x6f, 0x62, 0x1e, 0x17, 0xd2, 0xf9, - 0x72, 0x74, 0xc7, 0xd6, 0x1f, 0x8b, 0x9c, 0x4c, - 0x26, 0xd2, 0x6f, 0x7c, 0x33, 0x06, 0xee, 0xc2, - 0xa3, 0x41, 0x43, 0x4f, 0x40, 0x2a, 0x9c, 0xb3, - 0x4a, 0xb1, 0x88, 0x4e, 0x6f, 0xf2, 0xb7, 0x38, - 0xde, 0x87, 0x0d, 0xdc, 0x15, 0x6a, 0x36, 0x6b, - 0xf3, 0x6c, 0x61, 0xf5, 0x24, 0x8e, 0xb6, 0xcc, - 0x8a, 0x3a, 0xa0, 0xb4, 0x9b, 0xae, 0x85, 0x87, - 0x75, 0xf5, 0xbd, 0x50, 0x1f, 0xb5, 0x0c, 0xdb, - 0x6c, 0x68, 0x59, 0xef, 0x37, 0x5a, 0x2a, 0x85, - 0xf0, 0xce, 0x4d, 0x58, 0xa1, 0xa5, 0xde, 0x73, - 0x9b, 0x1a, 0x3d, 0x8a, 0x00, 0xba, 0x2f, 0xe2, - 0xda, 0xad, 0x3c, 0x63, 0x8a, 0x33, 0x39, 0xc4, - 0x07, 0x29, 0x1d, 0xa7, 0x40, 0x3b, 0xa4, 0xa6, - 0xae, 0xee, 0x37, 0x08, 0x83, 0xd1, 0x72, 0x66, - 0x3d, 0x43, 0xe3, 0x7a, 0x48, 0xfc, 0xf8, 0xd4, - 0xe3, 0xab, 0xd0, 0xe9, 0xb1, 0xf4, 0x4d, 0x3c, - 0x6b, 0x58, 0xde, 0x3c, 0x91, 0x0d, 0x3e, 0xec, - 0x35, 0x6d, 0x53, 0xe6, 0xb6, 0x4b, 0xc0, 0x80, - 0x18, 0xab, 0x96, 0x7f, 0x05, 0xd7, 0xd4, 0x81, - 0x0f, 0x92, 0x2b, 0xaf, 0x72, 0x59, 0xc2, 0x14, - 0xca, 0x62, 0x82, 0xac, 0xe3, 0x17, 0x43, 0x61, - 0x4d, 0x1e, 0xfc, 0x72, 0xaf, 0xfc, 0x55, 0x2a, - 0x2b, 0xb6, 0x8e, 0x6e, 0xe6, 0x86, 0xeb, 0xcc, - 0x26, 0x6c, 0xdf, 0xac, 0x02, 0x58, 0xa1, 0x5d, - 0x1b, 0x07, 0xe2, 0x5d, 0x50, 0xb9, 0xbf, 0x2e, - 0x1f, 0x49, 0x39, 0xe6, 0x7f, 0x2f, 0x0e, 0x9d, - 0x09, 0x42, 0xc7, 0xa1, 0xcc, 0xeb, 0x5b, 0x06, - 0x1c, 0x11, 0x9f, 0xea, 0xc1, 0x96, 0x82, 0xa9, - 0x30, 0x6a, 0xda, 0x98, 0x87, 0x43, 0xfd, 0x25, - 0xe7, 0x27, 0x53, 0x9a, 0xb3, 0x2f, 0x19, 0xa9, - 0x1a, 0xf4, 0xd6, 0xf3, 0x9e, 0xba, 0x9a, 0x91, - 0x52, 0x8f, 0x20, 0x6b, 0x4c, 0x3a, 0x2a, 0x3d, - 0xa0, 0xff, 0x8d, 0x61, 0x04, 0xee, 0x26, 0x55, - 0xdd, 0xd7, 0x67, 0xe4, 0x84, 0x0d, 0xf1, 0x5d, - 0xc7, 0xeb, 0xb3, 0x8c, 0x67, 0xa2, 0xc8, 0x1f, - 0x53, 0x02, 0xc4, 0x8c, 0x89, 0xd5, 0x51, 0xc8, - 0x8b, 0xb7, 0xc8, 0x11, 0xbe, 0x0e, 0xc2, 0xb1, - 0x00, 0x35, 0x81, 0x96, 0xac, 0x90, 0x9c, 0xbc, - 0x09, 0x82, 0x75, 0xc3, 0xe7, 0x66, 0x4e, 0x68, - 0xdc, 0xa1, 0xf0, 0xd0, 0x2d, 0x49, 0x3b, 0x47, - 0xba, 0x19, 0xc8, 0x9b, 0x90, 0x12, 0xc0, 0xdf, - 0xda, 0x32, 0x0f, 0x79, 0x6d, 0x1a, 0x5f, 0x92, - 0x51, 0x70, 0xfc, 0xca, 0x08, 0xd4, 0x7f, 0x1a, - 0x56, 0x04, 0x99, 0x33, 0x89, 0x3d, 0x6f, 0x89, - 0x10, 0x25, 0x81, 0xe2, 0xbd, 0x06, 0xd6, 0xaa, - 0x02, 0x8e, 0x4c, 0xa3, 0x60, 0xfd, 0xaf, 0x9c, - 0x81, 0x75, 0xaf, 0x2f, 0xe1, 0x72, 0xe0, 0x6e, - 0x15, 0xdd, 0xbb, 0x92, 0xd1, 0xbe, 0x8e, 0x9b, - 0xfb, 0x82, 0xb9, 0x47, 0x6f, 0x02, 0x28, 0x2a, - 0x67, 0x50, 0xed, 0x24, 0x9b, 0x4d, 0x69, 0xd7, - 0xa9, 0x66, 0x3e, 0x14, 0x4b, 0x00, 0x2a, 0xe4, - 0x3d, 0x63, 0xb2, 0x10, 0xd4, 0x05, 0x9d, 0xe3, - 0xde, 0xce, 0xd8, 0x04, 0x41, 0x03, 0xb5, 0xda, - 0xb0, 0x6f, 0xca, 0x63, 0x64, 0x04, 0xff, 0x07, - 0x58, 0x5f, 0x96, 0xf7, 0x6c, 0xb7, 0x67, 0x05, - 0xd6, 0x85, 0xf2, 0x1e, 0xc1, 0xdc, 0x76, 0x12, - 0x50, 0x83, 0x78, 0xa2, 0x51, 0x94, 0xe1, 0x2e, - 0xb8, 0x97, 0x5b, 0x08, 0x81, 0xac, 0x59, 0x43, - 0xe9, 0x01, 0x09, 0xa2, 0xed, 0x10, 0x4f, 0xb1, - 0x5b, 0xb8, 0x67, 0xe8, 0x61, 0x8d, 0xc8, 0xd9, - 0xc3, 0x5f, 0x65, 0xd7, 0xaa, 0x30, 0x0e, 0xc9, - 0x43, 0x98, 0x1d, 0xf1, 0xa5, 0x28, 0xd5, 0xa1, - 0x6b, 0x8f, 0x89, 0x76, 0x97, 0xa1, 0x3e, 0x6f, - 0x39, 0xf4, 0xb9, 0x6b, 0xa7, 0xfe, 0x58, 0x24, - 0xcd, 0x75, 0xa8, 0xec, 0x9e, 0x1c, 0x8e, 0x02, - 0x2a, 0xce, 0xe9, 0x0a, 0x24, 0x31, 0x89, 0x5a, - 0xd5, 0xdd, 0x70, 0x8e, 0x5f, 0xee, 0xc1, 0x34, - 0xf8, 0xe2, 0x8a, 0xca, 0xf1, 0xf2, 0x71, 0x4c, - 0x31, 0x56, 0xeb, 0x03, 0xf9, 0x6c, 0x0d, 0xa9, - 0x65, 0x6e, 0x88, 0x4f, 0x8e, 0x80, 0x69, 0xd7, - 0xd4, 0x63, 0x45, 0x9c, 0xab, 0x8c, 0x3d, 0x08, - 0x8b, 0xd9, 0x97, 0xdc, 0x88, 0x59, 0x19, 0x2d, - 0xb2, 0x84, 0xf4, 0x78, 0x3e, 0xce, 0x80, 0xba, - 0xeb, 0x34, 0x5a, 0x9e, 0x8e, 0x98, 0xc4, 0x45, - 0x9d, 0x59, 0xb2, 0x7e, 0xc1, 0x7e, 0x5b, 0x89, - 0xd0, 0x02, 0xcb, 0xa4, 0xf1, 0xf2, 0xa7, 0x3a, - 0x05, 0xc3, 0x7d, 0x43, 0x64, 0x7f, 0xf0, 0xc1, - 0xf8, 0x71, 0x3b, 0x38, 0x39, 0xc7, 0x1b, 0xf4, - 0x2f, 0x5a, 0x5c, 0x43, 0x1b, 0xe3, 0x93, 0xe8, - 0x79, 0xe8, 0x35, 0x63, 0x34, 0x7e, 0x25, 0x41, - 0x6f, 0x08, 0xce, 0x6f, 0x95, 0x2a, 0xc2, 0xdc, - 0x65, 0xe2, 0xa5, 0xc0, 0xfd, 0xf1, 0x78, 0x32, - 0x23, 0x09, 0x75, 0x99, 0x12, 0x7a, 0x83, 0xfd, - 0xae, 0x1e, 0xb2, 0xe9, 0x12, 0x5c, 0x3d, 0x03, - 0x68, 0x12, 0x1e, 0xe3, 0x8f, 0xff, 0x47, 0xe3, - 0xb4, 0x7e, 0x9b, 0x7e, 0x60, 0x2e, 0xf4, 0x06, - 0xba, 0x10, 0x08, 0x6b, 0xf9, 0x25, 0x59, 0xf3, - 0x61, 0x13, 0x2b, 0xd1, 0x2f, 0x04, 0x5f, 0xd6, - 0xd3, 0x42, 0xf6, 0x21, 0x57, 0xf6, 0xd3, 0xb3, - 0xec, 0xec, 0x07, 0x33, 0xbf, 0x69, 0x04, 0xec, - 0x88, 0x8d, 0x06, 0x2b, 0xfa, 0xee, 0xb2, 0x7b, - 0x41, 0x2a, 0x49, 0x0f, 0x30, 0x52, 0x41, 0x29, - 0x70, 0xd0, 0xf6, 0xb6, 0xbf, 0x27, 0x1a, 0x56, - 0x9a, 0x4b, 0x2a, 0x67, 0xfb, 0xc8, 0x16, 0x46, - 0x59, 0xc7, 0xf5, 0x5f, 0x20, 0x10, 0x25, 0x6c, - 0x1e, 0x36, 0x20, 0x0c, 0x3e, 0x7e, 0x15, 0x6c, - 0xa2, 0xbd, 0x22, 0xc4, 0x3d, 0xc9, 0x74, 0x56, - 0xab, 0x31, 0x92, 0xb8, 0x9f, 0xa1, 0x05, 0x2e, - 0xc4, 0xdb, 0x32, 0x91, 0xcb, 0x0f, 0x4a, 0x73, - 0x7f, 0xe1, 0xe6, 0x65, 0x2e, 0x5e, 0xa6, 0xaf, - 0xae, 0xa9, 0x04, 0x14, 0x83, 0xef, 0x19, 0x70, - 0x5e, 0xcb, 0xf5, 0x87, 0xcc, 0x45, 0xf7, 0x60, - 0xd7, 0x9d, 0x1e, 0x2e, /* 1012 */ - - /* up to here, this generates a 1022-byte single packet of compressed - * data that is well-formed and produces 1012 bytes of plaintext. - * - * The compressed packet ends - * - * 03F0: 70 5E CB F5 87 CC 45 F7 60 D7 9D 1E 2E 00 - */ - - 0x54, /* 1013 */ - - /* up to here, this generates a 1023-byte single packet of compressed - * data that is well-formed and produces 1013 bytes of plaintext. - * - * The compressed packet ends - * - * 03F0: 70 5E CB F5 87 CC 45 F7 60 D7 9D 1E 2E 54 00 - */ - - 0x83, /* 1014 */ - - /* up to here, a 1023-byte + 3-byte (1 byte payload) packet - * of uncompressed length 1014 */ - - 0x09, 0x99, 0xf9, 0x71, 0x9f, 0x15, 0x49, 0xda, 0xa8, 0x99, /* 1024 */ - - /* up to here, a 1023-byte (1020 payload) + 3-byte (1 payload) packet - * of uncompressed length 1019 */ - - 0xf5, 0xe6, 0xa1, 0x71, 0x64, 0x9a, 0x95, 0xed, - - -}; - -/* generates ciphertext: 1022 1023 1023 + 3 1023 + 3 */ -static int corner_lengths[] = { -/* bytes plaintext, ciphertext */ - 1012, /* 1019 */ - 1013, /* 1020 */ - 1014, /* 1021 */ - 1019, /* 1021 */ - 1024, /* 1021*/ -}; - - -/* one of these is created for each client connecting to us */ - -struct per_session_data__minimal { - int which; - int last; /* 0 no test, else test number in corner_lengths[] + 1 */ -}; - -static int -callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal *pss = - (struct per_session_data__minimal *)user; - unsigned char buf[LWS_PRE + 2048]; - int m; - - switch (reason) { - case LWS_CALLBACK_ESTABLISHED: - if (lws_hdr_copy(wsi, (char *)buf, sizeof(buf), - WSI_TOKEN_GET_URI) < 0) - return -1; - - pss->last = atoi((char *)buf + 1); - - if (pss->last > (int)LWS_ARRAY_SIZE(corner_lengths)) - pss->last = 0; - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - if (!pss->last) - break; - - lwsl_err("%s: writable %d, %d\n", __func__, pss->last, - corner_lengths[pss->last - 1]); - - memcpy(buf + LWS_PRE, uncompressible, - corner_lengths[pss->last - 1]); - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, buf + LWS_PRE, corner_lengths[pss->last - 1], - LWS_WRITE_BINARY); - if (m < corner_lengths[pss->last - 1]) { - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - pss->last = 0; - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL \ - { \ - "lws-minimal", \ - callback_minimal, \ - sizeof(struct per_session_data__minimal), \ - 2048, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/README.md libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-pmd-corner/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-pmd-corner/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -# lws minimal ws server + permessage-deflate corner case tests - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-ws-server-pmd-corner -[2018/11/21 16:47:49:0171] USER: LWS minimal ws server + permessage-deflate Corner Cases | visit http://localhost:7681 -[2018/11/21 16:47:49:0172] NOTICE: Creating Vhost 'default' port 7681, 2 protocols, IPv6 off - -``` - -Visit http://localhost:7681 - -5 ws connections are made via permessage-deflate extension. - -When the ws connection is established, various amounts of data are sent -resulting in ciphertext packets of a known size. - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-ws-server-ring) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-server-ring) -set(SRCS minimal-ws-server-ring.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/minimal-ws-server-ring.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/minimal-ws-server-ring.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/minimal-ws-server-ring.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/minimal-ws-server-ring.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -/* - * lws-minimal-ws-server - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws, - * with an added websocket chat server using a ringbuffer. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - */ - -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal.c" - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - LWS_PLUGIN_PROTOCOL_MINIMAL, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws server (lws_ring) | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -function get_appropriate_ws_url(extra_url) -{ - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; -} - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -document.addEventListener("DOMContentLoaded", function() { - - var ws = new_ws(get_appropriate_ws_url(""), "lws-minimal"); - try { - ws.onopen = function() { - document.getElementById("m").disabled = 0; - document.getElementById("b").disabled = 0; - }; - - ws.onmessage =function got_packet(msg) { - document.getElementById("r").value = - document.getElementById("r").value + msg.data + "\n"; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - ws.onclose = function(){ - document.getElementById("m").disabled = 1; - document.getElementById("b").disabled = 1; - }; - } catch(exception) { - alert("

Error " + exception); - } - - function sendmsg() - { - ws.send(document.getElementById("m").value); - document.getElementById("m").value = ""; - } - - document.getElementById("b").addEventListener("click", sendmsg); - -}, false); - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ - - - - - - - -
- - LWS chat minimal ws server example.
- Chat is sent to all browsers open on this page. -
-
-
- - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/protocol_lws_minimal.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/protocol_lws_minimal.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/protocol_lws_minimal.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/protocol_lws_minimal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,314 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal" - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This version uses an lws_ring ringbuffer to cache up to 8 messages at a time, - * so it's not so easy to lose messages. - * - * This also demonstrates how to "cull", ie, kill, connections that can't - * keep up for some reason. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -/* one of these created for each message */ - -struct msg { - void *payload; /* is malloc'd */ - size_t len; -}; - -/* one of these is created for each client connecting to us */ - -struct per_session_data__minimal { - struct per_session_data__minimal *pss_list; - struct lws *wsi; - uint32_t tail; - - unsigned int culled:1; -}; - -/* one of these is created for each vhost our protocol is used with */ - -struct per_vhost_data__minimal { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - - struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ - - struct lws_ring *ring; /* ringbuffer holding unsent messages */ -}; - -static void -cull_lagging_clients(struct per_vhost_data__minimal *vhd) -{ - uint32_t oldest_tail = lws_ring_get_oldest_tail(vhd->ring); - struct per_session_data__minimal *old_pss = NULL; - int most = 0, before = lws_ring_get_count_waiting_elements(vhd->ring, - &oldest_tail), m; - - /* - * At least one guy with the oldest tail has lagged too far, filling - * the ringbuffer with stuff waiting for them, while new stuff is - * coming in, and they must close, freeing up ringbuffer entries. - */ - - lws_start_foreach_llp_safe(struct per_session_data__minimal **, - ppss, vhd->pss_list, pss_list) { - - if ((*ppss)->tail == oldest_tail) { - old_pss = *ppss; - - lwsl_user("Killing lagging client %p\n", (*ppss)->wsi); - - lws_set_timeout((*ppss)->wsi, PENDING_TIMEOUT_LAGGING, - /* - * we may kill the wsi we came in on, - * so the actual close is deferred - */ - LWS_TO_KILL_ASYNC); - - /* - * We might try to write something before we get a - * chance to close. But this pss is now detached - * from the ring buffer. Mark this pss as culled so we - * don't try to do anything more with it. - */ - - (*ppss)->culled = 1; - - /* - * Because we can't kill it synchronously, but we - * know it's closing momentarily and don't want its - * participation any more, remove its pss from the - * vhd pss list early. (This is safe to repeat - * uselessly later in the close flow). - * - * Notice this changes *ppss! - */ - - lws_ll_fwd_remove(struct per_session_data__minimal, - pss_list, (*ppss), vhd->pss_list); - - /* use the changed *ppss so we won't skip anything */ - - continue; - - } else { - /* - * so this guy is a survivor of the cull. Let's track - * what is the largest number of pending ring elements - * for any survivor. - */ - m = lws_ring_get_count_waiting_elements(vhd->ring, - &((*ppss)->tail)); - if (m > most) - most = m; - } - - } lws_end_foreach_llp_safe(ppss); - - /* it would mean we lost track of oldest... but Coverity insists */ - if (!old_pss) - return; - - /* - * Let's recover (ie, free up) all the ring slots between the - * original oldest's last one and the "worst" survivor. - */ - - lws_ring_consume_and_update_oldest_tail(vhd->ring, - struct per_session_data__minimal, &old_pss->tail, before - most, - vhd->pss_list, tail, pss_list); - - lwsl_user("%s: shrunk ring from %d to %d\n", __func__, before, most); -} - -/* destroys the message when everyone has had a copy of it */ - -static void -__minimal_destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} - -static int -callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal *pss = - (struct per_session_data__minimal *)user; - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - const struct msg *pmsg; - struct msg amsg; - int n, m; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__minimal)); - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - - vhd->ring = lws_ring_create(sizeof(struct msg), 8, - __minimal_destroy_message); - if (!vhd->ring) - return 1; - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - lws_ring_destroy(vhd->ring); - break; - - case LWS_CALLBACK_ESTABLISHED: - /* add ourselves to the list of live pss held in the vhd */ - lwsl_user("LWS_CALLBACK_ESTABLISHED: wsi %p\n", wsi); - lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); - pss->tail = lws_ring_get_oldest_tail(vhd->ring); - pss->wsi = wsi; - break; - - case LWS_CALLBACK_CLOSED: - lwsl_user("LWS_CALLBACK_CLOSED: wsi %p\n", wsi); - /* remove our closing pss from the list of live pss */ - lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, - pss, vhd->pss_list); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - if (pss->culled) - break; - pmsg = lws_ring_get_element(vhd->ring, &pss->tail); - if (!pmsg) - break; - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, ((unsigned char *)pmsg->payload) + - LWS_PRE, pmsg->len, LWS_WRITE_TEXT); - if (m < (int)pmsg->len) { - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - lws_ring_consume_and_update_oldest_tail( - vhd->ring, /* lws_ring object */ - struct per_session_data__minimal, /* type of objects with tails */ - &pss->tail, /* tail of guy doing the consuming */ - 1, /* number of payload objects being consumed */ - vhd->pss_list, /* head of list of objects with tails */ - tail, /* member name of tail in objects with tails */ - pss_list /* member name of next object in objects with tails */ - ); - - /* more to do for us? */ - if (lws_ring_get_element(vhd->ring, &pss->tail)) - /* come back as soon as we can write more */ - lws_callback_on_writable(pss->wsi); - break; - - case LWS_CALLBACK_RECEIVE: - n = (int)lws_ring_get_count_free_elements(vhd->ring); - if (!n) { - /* forcibly make space */ - cull_lagging_clients(vhd); - n = (int)lws_ring_get_count_free_elements(vhd->ring); - } - if (!n) - break; - - lwsl_user("LWS_CALLBACK_RECEIVE: free space %d\n", n); - - amsg.len = len; - /* notice we over-allocate by LWS_PRE... */ - amsg.payload = malloc(LWS_PRE + len); - if (!amsg.payload) { - lwsl_user("OOM: dropping\n"); - break; - } - - /* ...and we copy the payload in at +LWS_PRE */ - memcpy((char *)amsg.payload + LWS_PRE, in, len); - if (!lws_ring_insert(vhd->ring, &amsg, 1)) { - __minimal_destroy_message(&amsg); - lwsl_user("dropping!\n"); - break; - } - - /* - * let everybody know we want to write something on them - * as soon as they are ready - */ - lws_start_foreach_llp(struct per_session_data__minimal **, - ppss, vhd->pss_list) { - lws_callback_on_writable((*ppss)->wsi); - } lws_end_foreach_llp(ppss, pss_list); - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL \ - { \ - "lws-minimal", \ - callback_minimal, \ - sizeof(struct per_session_data__minimal), \ - 0, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/README.md libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-ring/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-ring/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -# lws minimal ws server (lws_ring) - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-ws-server -[2018/03/04 09:30:02:7986] USER: LWS minimal ws server (lws_ring) | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 on multiple browser windows - -Text you type in any browser window is sent to all of them. - -A ringbuffer holds up to 8 lines of text. - -This also demonstrates how the ringbuffer can take action against lagging or -disconnected clients that cause the ringbuffer to fill. diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -project(lws-minimal-ws-server-threadpool) -cmake_minimum_required(VERSION 2.8) -include(CheckIncludeFile) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-server-threadpool) -set(SRCS minimal-ws-server-threadpool.c) - -MACRO(require_pthreads result) - CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) - if (NOT LWS_HAVE_PTHREAD_H) - if (LWS_WITH_MINIMAL_EXAMPLES) - set(result 0) - else() - message(FATAL_ERROR "threading support requires pthreads") - endif() - endif() -ENDMACRO() - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_pthreads(requirements) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITH_THREADPOOL 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared pthread) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets pthread) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/minimal-ws-server-threadpool.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/minimal-ws-server-threadpool.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/minimal-ws-server-threadpool.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/minimal-ws-server-threadpool.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,129 +0,0 @@ -/* - * lws-minimal-ws-server=threadpool - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal ws server that can cooperate with - * other threads cleanly. Two other threads are started, which fill - * a ringbuffer with strings at 10Hz. - * - * The actual work and thread spawning etc are done in the protocol - * implementation in protocol_lws_minimal.c. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - */ - -#include -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal_threadpool.c" - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - LWS_PLUGIN_PROTOCOL_MINIMAL, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -/* - * This demonstrates how to pass a pointer into a specific protocol handler - * running on a specific vhost. In this case, it's our default vhost and - * we pass the pvo named "config" with the value a const char * "myconfig". - * - * This is the preferred way to pass configuration into a specific vhost + - * protocol instance. - */ - -static const struct lws_protocol_vhost_options pvo_ops = { - NULL, - NULL, - "config", /* pvo name */ - (void *)"myconfig" /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo_ops, /* "child" pvo linked-list */ - "lws-minimal", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws server + threadpool | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - info.pvo = &pvo; /* per-vhost options */ - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* start the threads that create content */ - - while (!interrupted) - if (lws_service(context, 0)) - interrupted = 1; - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -var head = 0, tail = 0, ring = new Array(); - -function get_appropriate_ws_url(extra_url) -{ - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; -} - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -document.addEventListener("DOMContentLoaded", function() { - - var n, wsa = new Array, alive = 0; - - for (n = 0; n < 8; n++) { - - var ws = new_ws(get_appropriate_ws_url(""), "lws-minimal"); - wsa.push(ws); - try { - ws.onopen = function() { - document.getElementById("r").disabled = 0; - alive++; - }; - - ws.onmessage = function got_packet(msg) { - var n, s = ""; - - ring[head] = msg.data + "\n"; - head = (head + 1) % 50; - if (tail === head) - tail = (tail + 1) % 50; - - n = tail; - do { - s = s + ring[n]; - n = (n + 1) % 50; - } while (n !== head); - - document.getElementById("r").value = s; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - ws.onclose = function(){ - alive--; - if (alive === 0) - document.getElementById("r").disabled = 1; - }; - } catch(exception) { - alert("

Error " + exception); - } - } -}, false); Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ - - - - - - - -
- - Minimal ws server threadpool example.
- 8 x ws connections are opened back to the example server.
- There are three threads in the pool to service them, the
- remainder are queued until a thread in the pool is free.

- The textarea show the last 50 lines received. -
-
-
- - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/protocol_lws_minimal_threadpool.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/protocol_lws_minimal_threadpool.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/protocol_lws_minimal_threadpool.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/protocol_lws_minimal_threadpool.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,343 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal" demonstrating lws threadpool - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * The main reason some things are as they are is that the task lifecycle may - * be unrelated to the wsi lifecycle that queued that task. - * - * Consider the task may call an external library and run for 30s without - * "checking in" to see if it should stop. The wsi that started the task may - * have closed at any time before the 30s are up, with the browser window - * closing or whatever. - * - * So data shared between the asynchronous task and the wsi must have its - * lifecycle determined by the task, not the wsi. That means a separate struct - * that can be freed by the task. - * - * In the case the wsi outlives the task, the tasks do not get destroyed until - * the service thread has called lws_threadpool_task_status() on the completed - * task. So there is no danger of the shared task private data getting randomly - * freed. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -struct per_vhost_data__minimal { - struct lws_threadpool *tp; - const char *config; -}; - -struct task_data { - char result[64]; - - uint64_t pos, end; -}; - -/* - * Create the private data for the task - * - * Notice we hand over responsibility for the cleanup and freeing of the - * allocated task_data to the threadpool, because the wsi it was originally - * bound to may close while the thread is still running. So we allocate - * something discrete for the task private data that can be definitively owned - * and freed by the threadpool, not the wsi... the pss won't do, as it only - * exists for the lifecycle of the wsi connection. - * - * When the task is created, we also tell it how to destroy the private data - * by giving it args.cleanup as cleanup_task_private_data() defined below. - */ - -static struct task_data * -create_task_private_data(void) -{ - struct task_data *priv = malloc(sizeof(*priv)); - - return priv; -} - -/* - * Destroy the private data for the task - * - * Notice the wsi the task was originally bound to may be long gone, in the - * case we are destroying the lws context and the thread was doing something - * for a long time without checking in. - */ -static void -cleanup_task_private_data(struct lws *wsi, void *user) -{ - struct task_data *priv = (struct task_data *)user; - - free(priv); -} - -/* - * This runs in its own thread, from the threadpool. - * - * The implementation behind this in lws uses pthreads, but no pthreadisms are - * required in the user code. - * - * The example counts to 10M, "checking in" to see if it should stop after every - * 100K and pausing to sync with the service thread to send a ws message every - * 1M. It resumes after the service thread determines the wsi is writable and - * the LWS_CALLBACK_SERVER_WRITEABLE indicates the task thread can continue by - * calling lws_threadpool_task_sync(). - */ - -static enum lws_threadpool_task_return -task_function(void *user, enum lws_threadpool_task_status s) -{ - struct task_data *priv = (struct task_data *)user; - int budget = 100 * 1000; - - if (priv->pos == priv->end) - return LWS_TP_RETURN_FINISHED; - - /* - * Preferably replace this with ~100ms of your real task, so it - * can "check in" at short intervals to see if it has been asked to - * stop. - * - * You can just run tasks atomically here with the thread dedicated - * to it, but it will cause odd delays while shutting down etc and - * the task will run to completion even if the wsi that started it - * has since closed. - */ - - while (budget--) - priv->pos++; - - usleep(100000); - - if (!(priv->pos % (1000 * 1000))) { - lws_snprintf(priv->result + LWS_PRE, - sizeof(priv->result) - LWS_PRE, - "pos %llu", (unsigned long long)priv->pos); - - return LWS_TP_RETURN_SYNC; - } - - return LWS_TP_RETURN_CHECKING_IN; -} - -static int -callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - const struct lws_protocol_vhost_options *pvo; - struct lws_threadpool_create_args cargs; - struct lws_threadpool_task_args args; - struct lws_threadpool_task *task; - struct task_data *priv; - int n, m, r = 0; - char name[32]; - void *_user; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - /* create our per-vhost struct */ - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__minimal)); - if (!vhd) - return 1; - - /* recover the pointer to the globals struct */ - pvo = lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "config"); - if (!pvo || !pvo->value) { - lwsl_err("%s: Can't find \"config\" pvo\n", __func__); - return 1; - } - vhd->config = pvo->value; - - memset(&cargs, 0, sizeof(cargs)); - - cargs.max_queue_depth = 8; - cargs.threads = 3; - vhd->tp = lws_threadpool_create(lws_get_context(wsi), - &cargs, "%s", - lws_get_vhost_name(lws_get_vhost(wsi))); - if (!vhd->tp) - return 1; - - lws_timed_callback_vh_protocol(lws_get_vhost(wsi), - lws_get_protocol(wsi), - LWS_CALLBACK_USER, 1); - - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - lws_threadpool_finish(vhd->tp); - lws_threadpool_destroy(vhd->tp); - break; - - case LWS_CALLBACK_USER: - - /* - * in debug mode, dump the threadpool stat to the logs once - * a second - */ - lws_threadpool_dump(vhd->tp); - lws_timed_callback_vh_protocol(lws_get_vhost(wsi), - lws_get_protocol(wsi), - LWS_CALLBACK_USER, 1); - break; - - case LWS_CALLBACK_ESTABLISHED: - - memset(&args, 0, sizeof(args)); - priv = args.user = create_task_private_data(); - if (!args.user) - return 1; - - priv->pos = 0; - priv->end = 10 * 1000 * 1000; - - /* queue the task... the task takes on responsibility for - * destroying args.user. pss->priv just has a copy of it */ - - args.wsi = wsi; - args.task = task_function; - args.cleanup = cleanup_task_private_data; - - lws_get_peer_simple(wsi, name, sizeof(name)); - - if (!lws_threadpool_enqueue(vhd->tp, &args, "ws %s", name)) { - lwsl_user("%s: Couldn't enqueue task\n", __func__); - cleanup_task_private_data(wsi, priv); - return 1; - } - - lws_set_timeout(wsi, PENDING_TIMEOUT_THREADPOOL, 30); - - /* - * so the asynchronous worker will let us know the next step - * by causing LWS_CALLBACK_SERVER_WRITEABLE - */ - - break; - - case LWS_CALLBACK_CLOSED: - break; - - case LWS_CALLBACK_WS_SERVER_DROP_PROTOCOL: - lwsl_debug("LWS_CALLBACK_WS_SERVER_DROP_PROTOCOL: %p\n", wsi); - lws_threadpool_dequeue(wsi); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - - /* - * even completed tasks wait in a queue until we call the - * below on them. Then they may destroy themselves and their - * args.user data (by calling the cleanup callback). - * - * If you need to get things from the still-valid private task - * data, copy it here before calling - * lws_threadpool_task_status() that may free the task and the - * private task data. - */ - - n = lws_threadpool_task_status_wsi(wsi, &task, &_user); - lwsl_debug("%s: LWS_CALLBACK_SERVER_WRITEABLE: status %d\n", - __func__, n); - switch(n) { - - case LWS_TP_STATUS_FINISHED: - case LWS_TP_STATUS_STOPPED: - case LWS_TP_STATUS_QUEUED: - case LWS_TP_STATUS_RUNNING: - case LWS_TP_STATUS_STOPPING: - return 0; - - case LWS_TP_STATUS_SYNCING: - /* the task has paused for us to do something */ - break; - default: - return -1; - } - - priv = (struct task_data *)_user; - - lws_set_timeout(wsi, PENDING_TIMEOUT_THREADPOOL_TASK, 5); - - n = strlen(priv->result + LWS_PRE); - m = lws_write(wsi, (unsigned char *)priv->result + LWS_PRE, - n, LWS_WRITE_TEXT); - if (m < n) { - lwsl_err("ERROR %d writing to ws socket\n", m); - lws_threadpool_task_sync(task, 1); - return -1; - } - - /* - * service thread has done whatever it wanted to do with the - * data the task produced: if it's waiting to do more it can - * continue now. - */ - lws_threadpool_task_sync(task, 0); - break; - - default: - break; - } - - return r; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL \ - { \ - "lws-minimal", \ - callback_minimal, \ - 0, \ - 128, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/README.md libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threadpool/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threadpool/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -# lws minimal ws server (threadpool) - -## build - -``` - $ cmake . && make -``` - -Pthreads is required on your system. - -This demonstrates how to cleanly assign tasks bound to a wsi to a thread pool, -with a queue if the pool is occupied. - -It creates a threadpool with 3 worker threads and a maxiumum queue size of 4. - -The web page at http://localhost:7681 then starts up 8 x ws connections. - -## usage - -``` - $ ./lws-minimal-ws-server-threadpool -[2018/03/13 13:09:52:2208] USER: LWS minimal ws server + threadpool | visit http://localhost:7681 -[2018/03/13 13:09:52:2365] NOTICE: Creating Vhost 'default' port 7681, 2 protocols, IPv6 off -``` - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -project(lws-minimal-ws-server-threads) -cmake_minimum_required(VERSION 2.8) -include(CheckIncludeFile) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-server-threads) -set(SRCS minimal-ws-server.c) - -MACRO(require_pthreads result) - CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) - if (NOT LWS_HAVE_PTHREAD_H) - if (LWS_WITH_MINIMAL_EXAMPLES) - set(result 0) - else() - message(FATAL_ERROR "threading support requires pthreads") - endif() - endif() -ENDMACRO() - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -if (WIN32) - set(requirements 0) -endif() -require_pthreads(requirements) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared pthread) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets pthread) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/minimal-ws-server.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/minimal-ws-server.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/minimal-ws-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/minimal-ws-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,129 +0,0 @@ -/* - * lws-minimal-ws-server - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal ws server that can cooperate with - * other threads cleanly. Two other threads are started, which fill - * a ringbuffer with strings at 10Hz. - * - * The actual work and thread spawning etc are done in the protocol - * implementation in protocol_lws_minimal.c. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - */ - -#include -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal.c" - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - LWS_PLUGIN_PROTOCOL_MINIMAL, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -/* - * This demonstrates how to pass a pointer into a specific protocol handler - * running on a specific vhost. In this case, it's our default vhost and - * we pass the pvo named "config" with the value a const char * "myconfig". - * - * This is the preferred way to pass configuration into a specific vhost + - * protocol instance. - */ - -static const struct lws_protocol_vhost_options pvo_ops = { - NULL, - NULL, - "config", /* pvo name */ - (void *)"myconfig" /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo_ops, /* "child" pvo linked-list */ - "lws-minimal", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws server + threads | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - info.pvo = &pvo; /* per-vhost options */ - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - /* start the threads that create content */ - - while (!interrupted) - if (lws_service(context, 0)) - interrupted = 1; - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -var head = 0, tail = 0, ring = new Array(); - -function get_appropriate_ws_url(extra_url) -{ - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; -} - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -document.addEventListener("DOMContentLoaded", function() { - - var ws = new_ws(get_appropriate_ws_url(""), "lws-minimal"); - try { - ws.onopen = function() { - document.getElementById("r").disabled = 0; - }; - - ws.onmessage =function got_packet(msg) { - var n, s = ""; - - ring[head] = msg.data + "\n"; - head = (head + 1) % 50; - if (tail === head) - tail = (tail + 1) % 50; - - n = tail; - do { - s = s + ring[n]; - n = (n + 1) % 50; - } while (n !== head); - - document.getElementById("r").value = s; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - ws.onclose = function(){ - document.getElementById("r").disabled = 1; - }; - } catch(exception) { - alert("

Error " + exception); - } - -}, false); Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ - - - - - - -
-
- - Minimal ws server threads example.
- Strings generated by server threads are sent to - all browsers open on this page.
- The textarea show the last 50 lines received. -
-
-
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/protocol_lws_minimal.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/protocol_lws_minimal.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/protocol_lws_minimal.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/protocol_lws_minimal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,326 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal" demonstrating multithread - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -/* one of these created for each message in the ringbuffer */ - -struct msg { - void *payload; /* is malloc'd */ - size_t len; -}; - -/* - * One of these is created for each client connecting to us. - * - * It is ONLY read or written from the lws service thread context. - */ - -struct per_session_data__minimal { - struct per_session_data__minimal *pss_list; - struct lws *wsi; - uint32_t tail; -}; - -/* one of these is created for each vhost our protocol is used with */ - -struct per_vhost_data__minimal { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - - struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ - pthread_t pthread_spam[2]; - - pthread_mutex_t lock_ring; /* serialize access to the ring buffer */ - struct lws_ring *ring; /* {lock_ring} ringbuffer holding unsent content */ - - const char *config; - char finished; -}; - -#if defined(WIN32) -static void usleep(unsigned long l) { Sleep(l / 1000); } -#endif - -/* - * This runs under both lws service and "spam threads" contexts. - * Access is serialized by vhd->lock_ring. - */ - -static void -__minimal_destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} - -/* - * This runs under the "spam thread" thread context only. - * - * We spawn two threads that generate messages with this. - * - */ - -static void * -thread_spam(void *d) -{ - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *)d; - struct msg amsg; - int len = 128, index = 1, n; - - do { - /* don't generate output if nobody connected */ - if (!vhd->pss_list) - goto wait; - - pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ - - /* only create if space in ringbuffer */ - n = (int)lws_ring_get_count_free_elements(vhd->ring); - if (!n) { - lwsl_user("dropping!\n"); - goto wait_unlock; - } - - amsg.payload = malloc(LWS_PRE + len); - if (!amsg.payload) { - lwsl_user("OOM: dropping\n"); - goto wait_unlock; - } - n = lws_snprintf((char *)amsg.payload + LWS_PRE, len, - "%s: tid: %p, msg: %d", vhd->config, - (void *)pthread_self(), index++); - amsg.len = n; - n = lws_ring_insert(vhd->ring, &amsg, 1); - if (n != 1) { - __minimal_destroy_message(&amsg); - lwsl_user("dropping!\n"); - } else - /* - * This will cause a LWS_CALLBACK_EVENT_WAIT_CANCELLED - * in the lws service thread context. - */ - lws_cancel_service(vhd->context); - -wait_unlock: - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ - -wait: - usleep(100000); - - } while (!vhd->finished); - - lwsl_notice("thread_spam %p exiting\n", (void *)pthread_self()); - - pthread_exit(NULL); - - return NULL; -} - -/* this runs under the lws service thread context only */ - -static int -callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal *pss = - (struct per_session_data__minimal *)user; - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - const struct lws_protocol_vhost_options *pvo; - const struct msg *pmsg; - void *retval; - int n, m, r = 0; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - /* create our per-vhost struct */ - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__minimal)); - if (!vhd) - return 1; - - pthread_mutex_init(&vhd->lock_ring, NULL); - - /* recover the pointer to the globals struct */ - pvo = lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "config"); - if (!pvo || !pvo->value) { - lwsl_err("%s: Can't find \"config\" pvo\n", __func__); - return 1; - } - vhd->config = pvo->value; - - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - - vhd->ring = lws_ring_create(sizeof(struct msg), 8, - __minimal_destroy_message); - if (!vhd->ring) { - lwsl_err("%s: failed to create ring\n", __func__); - return 1; - } - - /* start the content-creating threads */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) - if (pthread_create(&vhd->pthread_spam[n], NULL, - thread_spam, vhd)) { - lwsl_err("thread creation failed\n"); - r = 1; - goto init_fail; - } - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: -init_fail: - vhd->finished = 1; - for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) - if (vhd->pthread_spam[n]) - pthread_join(vhd->pthread_spam[n], &retval); - - if (vhd->ring) - lws_ring_destroy(vhd->ring); - - pthread_mutex_destroy(&vhd->lock_ring); - break; - - case LWS_CALLBACK_ESTABLISHED: - /* add ourselves to the list of live pss held in the vhd */ - lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); - pss->tail = lws_ring_get_oldest_tail(vhd->ring); - pss->wsi = wsi; - break; - - case LWS_CALLBACK_CLOSED: - /* remove our closing pss from the list of live pss */ - lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, - pss, vhd->pss_list); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ - - pmsg = lws_ring_get_element(vhd->ring, &pss->tail); - if (!pmsg) { - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ - break; - } - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE, - pmsg->len, LWS_WRITE_TEXT); - if (m < (int)pmsg->len) { - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - lws_ring_consume_and_update_oldest_tail( - vhd->ring, /* lws_ring object */ - struct per_session_data__minimal, /* type of objects with tails */ - &pss->tail, /* tail of guy doing the consuming */ - 1, /* number of payload objects being consumed */ - vhd->pss_list, /* head of list of objects with tails */ - tail, /* member name of tail in objects with tails */ - pss_list /* member name of next object in objects with tails */ - ); - - /* more to do? */ - if (lws_ring_get_element(vhd->ring, &pss->tail)) - /* come back as soon as we can write more */ - lws_callback_on_writable(pss->wsi); - - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ - break; - - case LWS_CALLBACK_RECEIVE: - break; - - case LWS_CALLBACK_EVENT_WAIT_CANCELLED: - if (!vhd) - break; - /* - * When the "spam" threads add a message to the ringbuffer, - * they create this event in the lws service thread context - * using lws_cancel_service(). - * - * We respond by scheduling a writable callback for all - * connected clients. - */ - lws_start_foreach_llp(struct per_session_data__minimal **, - ppss, vhd->pss_list) { - lws_callback_on_writable((*ppss)->wsi); - } lws_end_foreach_llp(ppss, pss_list); - break; - - default: - break; - } - - return r; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL \ - { \ - "lws-minimal", \ - callback_minimal, \ - sizeof(struct per_session_data__minimal), \ - 128, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/README.md libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -# lws minimal ws server (threads) - -## build - -``` - $ cmake . && make -``` - -Pthreads is required on your system. - -## usage - -``` - $ ./lws-minimal-ws-server-threads -[2018/03/13 13:09:52:2208] USER: LWS minimal ws server + threads | visit http://localhost:7681 -[2018/03/13 13:09:52:2365] NOTICE: Creating Vhost 'default' port 7681, 2 protocols, IPv6 off -``` - -Visit http://localhost:7681 on multiple browser windows - -Two asynchronous threads generate strings and add them to a ringbuffer, -signalling lws to send new entries to all the browser windows. - -This demonstrates how to safely manage asynchronously generated content -and hook it up to the lws service thread. diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -project(lws-minimal-ws-server-threads-smp) -cmake_minimum_required(VERSION 2.8) -include(CheckIncludeFile) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-server-threads-smp) -set(SRCS minimal-ws-server.c) - -MACRO(require_pthreads result) - CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) - if (NOT LWS_HAVE_PTHREAD_H) - if (LWS_WITH_MINIMAL_EXAMPLES) - set(result 0) - else() - message(FATAL_ERROR "threading support requires pthreads") - endif() - endif() -ENDMACRO() - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_pthreads(requirements) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared pthread) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets pthread) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/minimal-ws-server.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/minimal-ws-server.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/minimal-ws-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/minimal-ws-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,148 +0,0 @@ -/* - * lws-minimal-ws-server - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal ws server that can cooperate with - * other threads cleanly. Two other threads are started, which fill - * a ringbuffer with strings at 10Hz. - * - * The actual work and thread spawning etc are done in the protocol - * implementation in protocol_lws_minimal.c. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - */ - -#include -#include -#include -#include - -#define LWS_PLUGIN_STATIC -#include "protocol_lws_minimal.c" - -#define COUNT_THREADS 2 - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - LWS_PLUGIN_PROTOCOL_MINIMAL, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static struct lws_context *context; -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -/* - * This demonstrates how to pass a pointer into a specific protocol handler - * running on a specific vhost. In this case, it's our default vhost and - * we pass the pvo named "config" with the value a const char * "myconfig". - * - * This is the preferred way to pass configuration into a specific vhost + - * protocol instance. - */ - -static const struct lws_protocol_vhost_options pvo_ops = { - NULL, - NULL, - "config", /* pvo name */ - (void *)"myconfig" /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo_ops, /* "child" pvo linked-list */ - "lws-minimal", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; - -void *thread_service(void *threadid) -{ - while (lws_service_tsi(context, 1000, - (int)(lws_intptr_t)threadid) >= 0 && - !interrupted) - ; - - pthread_exit(NULL); - - return NULL; -} - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - int n, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - pthread_t pthread_service[COUNT_THREADS]; - struct lws_context_creation_info info; - const char *p; - void *retval; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws server + threads + smp | visit http://localhost:7681\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - info.pvo = &pvo; /* per-vhost options */ - info.count_threads = COUNT_THREADS; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - lwsl_notice(" Service threads: %d\n", lws_get_count_threads(context)); - - /* start all the service threads */ - - for (n = 0; n < lws_get_count_threads(context); n++) - if (pthread_create(&pthread_service[n], NULL, thread_service, - (void *)(lws_intptr_t)n)) - lwsl_err("Failed to start service thread\n"); - - /* wait for all the service threads to exit */ - - while ((--n) >= 0) - pthread_join(pthread_service[n], &retval); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -var head = 0, tail = 0, ring = new Array(); - -function get_appropriate_ws_url(extra_url) -{ - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; -} - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -document.addEventListener("DOMContentLoaded", function() { - - var ws = new_ws(get_appropriate_ws_url(""), "lws-minimal"); - try { - ws.onopen = function() { - document.getElementById("r").disabled = 0; - }; - - ws.onmessage =function got_packet(msg) { - var n, s = ""; - - ring[head] = msg.data + "\n"; - head = (head + 1) % 50; - if (tail === head) - tail = (tail + 1) % 50; - - n = tail; - do { - s = s + ring[n]; - n = (n + 1) % 50; - } while (n !== head); - - document.getElementById("r").value = s; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - ws.onclose = function(){ - document.getElementById("r").disabled = 1; - }; - } catch(exception) { - alert("

Error " + exception); - } - -}, false); Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ - - - - - - - -
- - Minimal ws server threads SMP example.
- Strings generated by server threads are sent to - all browsers open on this page.
- The textarea show the last 50 lines received. -
-
-
- - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/protocol_lws_minimal.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/protocol_lws_minimal.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/protocol_lws_minimal.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/protocol_lws_minimal.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,333 +0,0 @@ -/* - * ws protocol handler plugin for "lws-minimal" demonstrating multithread - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include - -/* one of these created for each message in the ringbuffer */ - -struct msg { - void *payload; /* is malloc'd */ - size_t len; -}; - -/* - * One of these is created for each client connecting to us. - * - * It is ONLY read or written from the lws service thread context. - */ - -struct per_session_data__minimal { - struct per_session_data__minimal *pss_list; - struct lws *wsi; - uint32_t tail; -}; - -/* one of these is created for each vhost our protocol is used with */ - -struct per_vhost_data__minimal { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - - struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ - pthread_t pthread_spam[2]; - - pthread_mutex_t lock_ring; /* serialize access to the ring buffer */ - struct lws_ring *ring; /* {lock_ring} ringbuffer holding unsent content */ - - const char *config; - char finished; -}; - -#if defined(WIN32) -static void usleep(unsigned long l) { Sleep(l / 1000); } -#endif - -/* - * This runs under both lws service and "spam threads" contexts. - * Access is serialized by vhd->lock_ring. - */ - -static void -__minimal_destroy_message(void *_msg) -{ - struct msg *msg = _msg; - - free(msg->payload); - msg->payload = NULL; - msg->len = 0; -} - -/* - * This runs under the "spam thread" thread context only. - * - * We spawn two threads that generate messages with this. - * - */ - -static void * -thread_spam(void *d) -{ - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *)d; - struct msg amsg; - int len = 128, index = 1, n; - - do { - /* don't generate output if nobody connected */ - if (!vhd->pss_list) - goto wait; - - pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ - - /* only create if space in ringbuffer */ - n = (int)lws_ring_get_count_free_elements(vhd->ring); - if (!n) { - lwsl_user("dropping!\n"); - goto wait_unlock; - } - - amsg.payload = malloc(LWS_PRE + len); - if (!amsg.payload) { - lwsl_user("OOM: dropping\n"); - goto wait_unlock; - } - n = lws_snprintf((char *)amsg.payload + LWS_PRE, len, - "%s: spam tid: %p, msg: %d", vhd->config, - (void *)pthread_self(), index++); - amsg.len = n; - n = lws_ring_insert(vhd->ring, &amsg, 1); - if (n != 1) { - __minimal_destroy_message(&amsg); - lwsl_user("dropping!\n"); - } else - /* - * This will cause a LWS_CALLBACK_EVENT_WAIT_CANCELLED - * in the lws service thread context. - */ - lws_cancel_service(vhd->context); - -wait_unlock: - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ - -wait: - usleep(100000); - - } while (!vhd->finished); - - lwsl_notice("thread_spam %p exiting\n", (void *)pthread_self()); - - pthread_exit(NULL); - - return NULL; -} - -/* this runs under the lws service thread context only */ - -static int -callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__minimal *pss = - (struct per_session_data__minimal *)user; - struct per_vhost_data__minimal *vhd = - (struct per_vhost_data__minimal *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - const struct lws_protocol_vhost_options *pvo; - const struct msg *pmsg; - char temp[LWS_PRE + 256]; - void *retval; - int n, m, r = 0; - - switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - /* create our per-vhost struct */ - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__minimal)); - if (!vhd) - return 1; - - pthread_mutex_init(&vhd->lock_ring, NULL); - - /* recover the pointer to the globals struct */ - pvo = lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "config"); - if (!pvo || !pvo->value) { - lwsl_err("%s: Can't find \"config\" pvo\n", __func__); - return 1; - } - vhd->config = pvo->value; - - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - - vhd->ring = lws_ring_create(sizeof(struct msg), 8, - __minimal_destroy_message); - if (!vhd->ring) { - lwsl_err("%s: failed to create ring\n", __func__); - return 1; - } - - /* start the content-creating threads */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) - if (pthread_create(&vhd->pthread_spam[n], NULL, - thread_spam, vhd)) { - lwsl_err("thread creation failed\n"); - r = 1; - goto init_fail; - } - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: -init_fail: - vhd->finished = 1; - for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) - if (vhd->pthread_spam[n]) - pthread_join(vhd->pthread_spam[n], &retval); - - if (vhd->ring) - lws_ring_destroy(vhd->ring); - - pthread_mutex_destroy(&vhd->lock_ring); - break; - - case LWS_CALLBACK_ESTABLISHED: - /* add ourselves to the list of live pss held in the vhd */ - lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); - pss->tail = lws_ring_get_oldest_tail(vhd->ring); - pss->wsi = wsi; - break; - - case LWS_CALLBACK_CLOSED: - /* remove our closing pss from the list of live pss */ - lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, - pss, vhd->pss_list); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ - - pmsg = lws_ring_get_element(vhd->ring, &pss->tail); - if (!pmsg) { - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ - break; - } - - n = lws_snprintf(temp + LWS_PRE, sizeof(temp) - LWS_PRE, - "svc tid:%p, %s", (void *)pthread_self(), - (char *)pmsg->payload + LWS_PRE); - - /* notice we allowed for LWS_PRE in the payload already */ - m = lws_write(wsi, (unsigned char *)temp + LWS_PRE, n, - LWS_WRITE_TEXT); - if (m < n) { - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - lws_ring_consume_and_update_oldest_tail( - vhd->ring, /* lws_ring object */ - struct per_session_data__minimal, /* type of objects with tails */ - &pss->tail, /* tail of guy doing the consuming */ - 1, /* number of payload objects being consumed */ - vhd->pss_list, /* head of list of objects with tails */ - tail, /* member name of tail in objects with tails */ - pss_list /* member name of next object in objects with tails */ - ); - - /* more to do? */ - if (lws_ring_get_element(vhd->ring, &pss->tail)) - /* come back as soon as we can write more */ - lws_callback_on_writable(pss->wsi); - - pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ - break; - - case LWS_CALLBACK_RECEIVE: - break; - - case LWS_CALLBACK_EVENT_WAIT_CANCELLED: - lwsl_notice("LWS_CALLBACK_EVENT_WAIT_CANCELLED in svc tid %p\n", - (void *)pthread_self()); - if (!vhd) - break; - /* - * When the "spam" threads add a message to the ringbuffer, - * they create this event in the lws service thread context - * using lws_cancel_service(). - * - * We respond by scheduling a writable callback for all - * connected clients. - */ - lws_start_foreach_llp(struct per_session_data__minimal **, - ppss, vhd->pss_list) { - lws_callback_on_writable((*ppss)->wsi); - } lws_end_foreach_llp(ppss, pss_list); - break; - - default: - break; - } - - return r; -} - -#define LWS_PLUGIN_PROTOCOL_MINIMAL \ - { \ - "lws-minimal", \ - callback_minimal, \ - sizeof(struct per_session_data__minimal), \ - 128, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/README.md libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-threads-smp/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-threads-smp/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -# lws minimal ws server (threads) + SMP - -This demonstrates both independent threads creating content -as in the -threads example, and multiple service threads -as in the http-server-smp example (but with ws). - -## build - -You must first build libwebsockets itself with cmake `-DLWS_MAX_SMP=8` -or some other number greater than one. - -``` - $ cmake . && make -``` - -Pthreads is required on your system. - -## usage - -``` - $ ./lws-minimal-ws-server-threads-smp -[2019/01/28 06:59:17:4217] USER: LWS minimal ws server + threads + smp | visit http://localhost:7681 -[2019/01/28 06:59:17:4219] NOTICE: Service threads: 2 -[2019/01/28 06:59:17:4220] NOTICE: LWS_CALLBACK_EVENT_WAIT_CANCELLED in svc tid 0x7fec48af8700 -[2019/01/28 06:59:17:4220] NOTICE: LWS_CALLBACK_EVENT_WAIT_CANCELLED in svc tid 0x7fec48af8700 -... -``` - -Visit http://localhost:7681 on multiple browser windows. You may need to open -4 before the second service thread is used (check "svc tid" in the browser output). - -Two lws service threads are started. - -Two separate asynchronous threads generate strings and add them to a ringbuffer, -signalling all lws service threads to send new entries to all the browser windows. - -This demonstrates how to safely manage asynchronously generated content -and hook it up to the lws service threads. - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/CMakeLists.txt libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/CMakeLists.txt --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/CMakeLists.txt 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -project(lws-minimal-ws-server-timer) -cmake_minimum_required(VERSION 2.8) -include(CheckCSourceCompiles) - -set(SAMP lws-minimal-ws-server-timer) -set(SRCS minimal-ws-server.c) - -# If we are being built as part of lws, confirm current build config supports -# reqconfig, else skip building ourselves. -# -# If we are being built externally, confirm installed lws was configured to -# support reqconfig, else error out with a helpful message about the problem. -# -MACRO(require_lws_config reqconfig _val result) - - if (DEFINED ${reqconfig}) - if (${reqconfig}) - set (rq 1) - else() - set (rq 0) - endif() - else() - set(rq 0) - endif() - - if (${_val} EQUAL ${rq}) - set(SAME 1) - else() - set(SAME 0) - endif() - - if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) - if (${_val}) - message("${SAMP}: skipping as lws being built without ${reqconfig}") - else() - message("${SAMP}: skipping as lws built with ${reqconfig}") - endif() - set(${result} 0) - else() - if (LWS_WITH_MINIMAL_EXAMPLES) - set(MET ${SAME}) - else() - CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) - if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) - set(HAS_${reqconfig} 0) - else() - set(HAS_${reqconfig} 1) - endif() - if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) - set(MET 1) - else() - set(MET 0) - endif() - endif() - if (NOT MET) - if (${_val}) - message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") - else() - message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") - endif() - endif() - - endif() -ENDMACRO() - -set(requirements 1) -require_lws_config(LWS_ROLE_WS 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets) - endif() -endif() diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/localhost-100y.cert libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/localhost-100y.cert --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/localhost-100y.cert 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/localhost-100y.cert 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD -VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb -MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx -HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3 -WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl -d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0 -cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA -aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW -aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8 -Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek -LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH -KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6 -jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ -Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz -TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK -Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0 -nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo -GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p -sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU -9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar -jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow -YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA -xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P -wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34 -H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv -xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk -ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g -1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA -AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg -mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s -8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/localhost-100y.key libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/localhost-100y.key --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/localhost-100y.key 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/localhost-100y.key 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ -PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK -nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ -toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU -0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT -J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS -Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN -uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9 -fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn -zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au -ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB -QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f -qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+ -vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9 -fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A -Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT -G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/ -HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8 -YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl -xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs -esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw -zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz -mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw -au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77 -40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5 -YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH -PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj -W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR -naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6 -2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m -39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79 -J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC -R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp -Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh -BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE -fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ -x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI -UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM -OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L -65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A -aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5 -SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S -me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I -G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK -TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY -56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2 -gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr -Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E -NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/minimal-ws-server.c libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/minimal-ws-server.c --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/minimal-ws-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/minimal-ws-server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -/* - * lws-minimal-ws-server-timer - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws, - * with an added websocket chat server. - * - * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of - * the directory it was started in. - * You can change that by changing mount.origin. - */ - -#include -#include -#include - -static int -callback_protocol(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - switch (reason) { - - case LWS_CALLBACK_ESTABLISHED: - lwsl_user("LWS_CALLBACK_ESTABLISHED\n"); - lws_set_timer_usecs(wsi, 20 * LWS_USEC_PER_SEC); - lws_set_timeout(wsi, 1, 60); - break; - - case LWS_CALLBACK_TIMER: - lwsl_user("LWS_CALLBACK_TIMER\n"); - lws_set_timer_usecs(wsi, 20 * LWS_USEC_PER_SEC); - lws_set_timeout(wsi, 1, 60); - break; - - case LWS_CALLBACK_CLOSED: - lwsl_user("LWS_CALLBACK_CLOSED\n"); - break; - - default: - break; - } - - return 0; -} - -static struct lws_protocols protocols[] = { - { "http", lws_callback_http_dummy, 0, 0 }, - { "timer", callback_protocol, 0, 0 }, - { NULL, NULL, 0, 0 } /* terminator */ -}; - -static const lws_retry_bo_t retry = { - .secs_since_valid_ping = 3, - .secs_since_valid_hangup = 10, -}; - -static int interrupted; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal ws server | visit http://localhost:7681 (-s = use TLS / https)\n"); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.protocols = protocols; - info.vhost_name = "localhost"; - info.ws_ping_pong_interval = 10; - info.options = - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - if (lws_cmdline_option(argc, argv, "-s")) { - lwsl_user("Server using TLS\n"); - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - - if (lws_cmdline_option(argc, argv, "-h")) - info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK; - - if (lws_cmdline_option(argc, argv, "-v")) - info.retry_and_idle_policy = &retry; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/example.js libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/example.js --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/example.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/example.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - -function get_appropriate_ws_url(extra_url) -{ - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; -} - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -document.addEventListener("DOMContentLoaded", function() { - - var ws = new_ws(get_appropriate_ws_url(""), "timer"); - try { - ws.onopen = function() { - document.getElementById("m").disabled = 0; - document.getElementById("b").disabled = 0; - }; - - ws.onmessage =function got_packet(msg) { - document.getElementById("r").value = - document.getElementById("r").value + msg.data + "\n"; - document.getElementById("r").scrollTop = - document.getElementById("r").scrollHeight; - }; - - ws.onclose = function(){ - document.getElementById("m").disabled = 1; - document.getElementById("b").disabled = 1; - }; - } catch(exception) { - alert("

Error " + exception); - } - - function sendmsg() - { - ws.send(document.getElementById("m").value); - document.getElementById("m").value = ""; - } - - document.getElementById("b").addEventListener("click", sendmsg); - -}, false); - Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/favicon.ico and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/favicon.ico differ diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/index.html libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/index.html --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ - - - - - - - -
- - LWS wsi timer minimal ws server timer example.
- This opens a ws connection back to the server and just sits there - setting the timer to fire every 20s, which resets the wsi timeout - for 60s each timer. It should just stay like that forever. - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/libwebsockets.org-logo.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/libwebsockets.org-logo.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/libwebsockets.org-logo.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/libwebsockets.org-logo.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/strict-csp.svg libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/strict-csp.svg --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/strict-csp.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/mount-origin/strict-csp.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/README.md libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/minimal-ws-server-timer/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/minimal-ws-server-timer/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -# lws minimal ws server timer - -This is designed to confirm long term stability of ws timers on a -particular platform. - -## build - -``` - $ cmake . && make -``` - -## Commandline Options - -Option|Meaning ----|--- --d|Set logging verbosity --s|Serve using TLS selfsigned cert (ie, connect to it with https://...) --h|Strict Host: header checking against vhost name (localhost) and port --v|Connection validity use 3s / 10s instead of default 5m / 5m10s - -## usage - -``` - $ ./lws-minimal-ws-server-timer -[2018/03/04 09:30:02:7986] USER: LWS minimal ws server | visit http://localhost:7681 -[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on -``` - -Visit http://localhost:7681 and the browser will connect back to the test -server, you'll see ESTABLISHED logged. That triggers a TIMER event at 20s -intervals which sets the wsi timeout to 60s. It should just stay like -that forever doing the TIMER events at 20s intervals and not sending any -traffic either way. - diff -Nru libwebsockets-4.0.20/minimal-examples/ws-server/README.md libwebsockets-2.4.2/minimal-examples/ws-server/README.md --- libwebsockets-4.0.20/minimal-examples/ws-server/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/minimal-examples/ws-server/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -|Example|Demonstrates| ----|--- -minimal-ws-broker|Simple ws server with a publish / broker / subscribe architecture -minimal-ws-server-echo|Simple ws server that listens and echos back anything clients send -minimal-ws-server-pmd-bulk|Simple ws server showing how to pass bulk data with permessage-deflate -minimal-ws-server-pmd-corner|Corner-case tests for permessage-deflate -minimal-ws-server-pmd|Simple ws server with permessage-deflate support -minimal-ws-server-ring|Like minimal-ws-server but holds the chat in a multi-tail ringbuffer -minimal-ws-server-threadpool|Demonstrates how to use a worker thread pool with lws -minimal-ws-server-threads-smp|SMP ws server where data is produced by different threads with multiple lws service threads too -minimal-ws-server-threads|Simple ws server where data is produced by different threads -minimal-ws-server|Serves an index.html over http that opens a ws shared chat client in a browser - diff -Nru libwebsockets-4.0.20/plugins/acme-client/protocol_lws_acme_client.c libwebsockets-2.4.2/plugins/acme-client/protocol_lws_acme_client.c --- libwebsockets-4.0.20/plugins/acme-client/protocol_lws_acme_client.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/plugins/acme-client/protocol_lws_acme_client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1658 +0,0 @@ -/* - * libwebsockets ACME client protocol plugin - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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. - * - * Acme is in a big messy transition at the moment from a homebrewed api - * to an IETF one. The old repo for the homebrew api (they currently - * implement) is marked up as deprecated and "not accurate[ly] reflect[ing]" - * what they implement, but the IETF standard, currently at v7 is not yet - * implemented at let's encrypt (ETA Jan 2018). - * - * This implementation follows draft 7 of the IETF standard, and falls back - * to whatever differences exist for Boulder's tls-sni-01 challenge. The - * tls-sni-02 support is there but nothing to test it against at the time of - * writing (Nov 1 2017). - */ - -#if !defined (LWS_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include -#include - -typedef enum { - ACME_STATE_DIRECTORY, /* get the directory JSON using GET + parse */ - ACME_STATE_NEW_NONCE, /* get the replay nonce */ - ACME_STATE_NEW_ACCOUNT, /* register a new RSA key + email combo */ - ACME_STATE_NEW_ORDER, /* start the process to request a cert */ - ACME_STATE_AUTHZ, /* */ - ACME_STATE_START_CHALL, /* notify server ready for one challenge */ - ACME_STATE_POLLING, /* he should be trying our challenge */ - ACME_STATE_POLLING_CSR, /* sent CSR, checking result */ - ACME_STATE_DOWNLOAD_CERT, - - ACME_STATE_FINISHED -} lws_acme_state; - -struct acme_connection { - char buf[4096]; - char replay_nonce[64]; - char chall_token[64]; - char challenge_uri[256]; - char detail[64]; - char status[16]; - char key_auth[256]; - char http01_mountpoint[256]; - struct lws_http_mount mount; - char urls[6][100]; /* directory contents */ - char active_url[100]; - char authz_url[100]; - char order_url[100]; - char finalize_url[100]; - char cert_url[100]; - char acct_id[100]; - char *kid; - lws_acme_state state; - struct lws_client_connect_info i; - struct lejp_ctx jctx; - struct lws_context_creation_info ci; - struct lws_vhost *vhost; - - struct lws *cwsi; - - const char *real_vh_name; - const char *real_vh_iface; - - char *alloc_privkey_pem; - - char *dest; - int pos; - int len; - int resp; - int cpos; - - int real_vh_port; - int goes_around; - - size_t len_privkey_pem; - - unsigned int yes:2; - unsigned int use:1; - unsigned int is_sni_02:1; -}; - -struct per_vhost_data__lws_acme_client { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - - /* - * the vhd is allocated for every vhost using the plugin. - * But ac is only allocated when we are doing the server auth. - */ - struct acme_connection *ac; - - struct lws_jwk jwk; - struct lws_genrsa_ctx rsactx; - - char *pvo_data; - char *pvop[LWS_TLS_TOTAL_COUNT]; - const char *pvop_active[LWS_TLS_TOTAL_COUNT]; - int count_live_pss; - char *dest; - int pos; - int len; - - int fd_updated_cert; /* these are opened while we have root... */ - int fd_updated_key; /* ...if nonempty next startup will replace old */ -}; - -static int -callback_chall_http01(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct lws_vhost *vhost = lws_get_vhost(wsi); - struct acme_connection *ac = lws_vhost_user(vhost); - uint8_t buf[LWS_PRE + 2048], *start = &buf[LWS_PRE], *p = start, - *end = &buf[sizeof(buf) - LWS_PRE - 1]; - int n; - - switch (reason) { - case LWS_CALLBACK_HTTP: - lwsl_notice("%s: ca connection received, key_auth %s\n", - __func__, ac->key_auth); - - if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) { - lwsl_notice("%s: add status failed\n", __func__); - return -1; - } - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_TYPE, - (unsigned char *)"text/plain", 10, - &p, end)) { - lwsl_notice("%s: add content_type failed\n", __func__); - return -1; - } - - n = strlen(ac->key_auth); - if (lws_add_http_header_content_length(wsi, n, &p, end)) { - lwsl_notice("%s: add content_length failed\n", - __func__); - return -1; - } - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_DISPOSITION, - (unsigned char *)"attachment", 10, - &p, end)) { - lwsl_notice("%s: add content_dispo failed\n", __func__); - return -1; - } - - if (lws_finalize_write_http_header(wsi, start, &p, end)) { - lwsl_notice("%s: finalize http header failed\n", - __func__); - return -1; - } - - lws_callback_on_writable(wsi); - return 0; - - case LWS_CALLBACK_HTTP_WRITEABLE: - p += lws_snprintf((char *)p, end - p, "%s", ac->key_auth); - lwsl_notice("%s: len %d\n", __func__, lws_ptr_diff(p, start)); - if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff(p, start), - LWS_WRITE_HTTP_FINAL) != lws_ptr_diff(p, start)) { - lwsl_err("_write content failed\n"); - return 1; - } - - if (lws_http_transaction_completed(wsi)) - return -1; - - return 0; - - default: - break; - } - - return lws_callback_http_dummy(wsi, reason, user, in, len); -} - -static const struct lws_protocols chall_http01_protocols[] = { - { "http", callback_chall_http01, 0, 0, 0, NULL, 0 }, - { NULL, NULL, 0, 0, 0, NULL, 0 } -}; - -static int -jws_create_packet(struct lws_jwe *jwe, const char *payload, size_t len, - const char *nonce, const char *url, const char *kid, - char *out, size_t out_len, struct lws_context *context) -{ - char *buf, *start, *p, *end, *p1, *end1; - struct lws_jws jws; - int n, m; - - lws_jws_init(&jws, &jwe->jwk, context); - - /* - * This buffer is local to the function, the actual output is prepared - * into out. Only the plaintext protected header - * (which contains the public key, 512 bytes for 4096b) goes in - * here temporarily. - */ - n = LWS_PRE + 2048; - buf = malloc(n); - if (!buf) { - lwsl_notice("%s: malloc %d failed\n", __func__, n); - return -1; - } - - p = start = buf + LWS_PRE; - end = buf + n - LWS_PRE - 1; - - /* - * temporary JWS protected header plaintext - */ - if (!jwe->jose.alg || !jwe->jose.alg->alg) - goto bail; - - p += lws_snprintf(p, end - p, "{\"alg\":\"RS256\""); - if (kid) - p += lws_snprintf(p, end - p, ",\"kid\":\"%s\"", kid); - else { - p += lws_snprintf(p, end - p, ",\"jwk\":"); - m = end - p; - n = lws_jwk_export(&jwe->jwk, 0, p, &m); - if (n < 0) { - lwsl_notice("failed to export jwk\n"); - goto bail; - } - p += n; - } - p += lws_snprintf(p, end - p, ",\"url\":\"%s\"", url); - p += lws_snprintf(p, end - p, ",\"nonce\":\"%s\"}", nonce); - - /* - * prepare the signed outer JSON with all the parts in - */ - p1 = out; - end1 = out + out_len - 1; - - p1 += lws_snprintf(p1, end1 - p1, "{\"protected\":\""); - jws.map_b64.buf[LJWS_JOSE] = p1; - n = lws_jws_base64_enc(start, p - start, p1, end1 - p1); - if (n < 0) { - lwsl_notice("%s: failed to encode protected\n", __func__); - goto bail; - } - jws.map_b64.len[LJWS_JOSE] = n; - p1 += n; - - p1 += lws_snprintf(p1, end1 - p1, "\",\"payload\":\""); - jws.map_b64.buf[LJWS_PYLD] = p1; - n = lws_jws_base64_enc(payload, len, p1, end1 - p1); - if (n < 0) { - lwsl_notice("%s: failed to encode payload\n", __func__); - goto bail; - } - jws.map_b64.len[LJWS_PYLD] = n; - p1 += n; - - p1 += lws_snprintf(p1, end1 - p1, "\",\"signature\":\""); - - /* - * taking the b64 protected header and the b64 payload, sign them - * and place the signature into the packet - */ - n = lws_jws_sign_from_b64(&jwe->jose, &jws, p1, end1 - p1); - if (n < 0) { - lwsl_notice("sig gen failed\n"); - - goto bail; - } - jws.map_b64.buf[LJWS_SIG] = p1; - jws.map_b64.len[LJWS_SIG] = n; - - p1 += n; - p1 += lws_snprintf(p1, end1 - p1, "\"}"); - - free(buf); - - return p1 - out; - -bail: - lws_jws_destroy(&jws); - free(buf); - - return -1; -} - -static int -callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len); - -#define LWS_PLUGIN_PROTOCOL_LWS_ACME_CLIENT \ -{ \ - "lws-acme-client", \ - callback_acme_client, \ - 0, \ - 512, \ - 0, NULL, 0 \ -} - -/* directory JSON parsing */ - -static const char * const jdir_tok[] = { - "keyChange", - "meta.termsOfService", - "newAccount", - "newNonce", - "newOrder", - "revokeCert", -}; - -enum enum_jdir_tok { - JAD_KEY_CHANGE_URL, - JAD_TOS_URL, - JAD_NEW_ACCOUNT_URL, - JAD_NEW_NONCE_URL, - JAD_NEW_ORDER_URL, - JAD_REVOKE_CERT_URL, -}; - -static signed char -cb_dir(struct lejp_ctx *ctx, char reason) -{ - struct per_vhost_data__lws_acme_client *s = - (struct per_vhost_data__lws_acme_client *)ctx->user; - - if (reason == LEJPCB_VAL_STR_START && ctx->path_match) { - s->pos = 0; - s->len = sizeof(s->ac->urls[0]) - 1; - s->dest = s->ac->urls[ctx->path_match - 1]; - return 0; - } - - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - if (s->pos + ctx->npos > s->len) { - lwsl_notice("url too long\n"); - return -1; - } - - memcpy(s->dest + s->pos, ctx->buf, ctx->npos); - s->pos += ctx->npos; - s->dest[s->pos] = '\0'; - - return 0; -} - - -/* order JSON parsing */ - -static const char * const jorder_tok[] = { - "status", - "expires", - "identifiers[].type", - "identifiers[].value", - "authorizations", - "finalize", - "certificate" -}; - -enum enum_jorder_tok { - JAO_STATUS, - JAO_EXPIRES, - JAO_IDENTIFIERS_TYPE, - JAO_IDENTIFIERS_VALUE, - JAO_AUTHORIZATIONS, - JAO_FINALIZE, - JAO_CERT -}; - -static signed char -cb_order(struct lejp_ctx *ctx, char reason) -{ - struct acme_connection *s = (struct acme_connection *)ctx->user; - - if (reason == LEJPCB_CONSTRUCTED) - s->authz_url[0] = '\0'; - - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - switch (ctx->path_match - 1) { - case JAO_STATUS: - lws_strncpy(s->status, ctx->buf, sizeof(s->status)); - break; - case JAO_EXPIRES: - break; - case JAO_IDENTIFIERS_TYPE: - break; - case JAO_IDENTIFIERS_VALUE: - break; - case JAO_AUTHORIZATIONS: - lws_snprintf(s->authz_url, sizeof(s->authz_url), "%s", - ctx->buf); - break; - case JAO_FINALIZE: - lws_snprintf(s->finalize_url, sizeof(s->finalize_url), "%s", - ctx->buf); - break; - case JAO_CERT: - lws_snprintf(s->cert_url, sizeof(s->cert_url), "%s", ctx->buf); - break; - } - - return 0; -} - -/* authz JSON parsing */ - -static const char * const jauthz_tok[] = { - "identifier.type", - "identifier.value", - "status", - "expires", - "challenges[].type", - "challenges[].status", - "challenges[].url", - "challenges[].token", - "detail" -}; - -enum enum_jauthz_tok { - JAAZ_ID_TYPE, - JAAZ_ID_VALUE, - JAAZ_STATUS, - JAAZ_EXPIRES, - JAAZ_CHALLENGES_TYPE, - JAAZ_CHALLENGES_STATUS, - JAAZ_CHALLENGES_URL, - JAAZ_CHALLENGES_TOKEN, - JAAZ_DETAIL, -}; - -static signed char -cb_authz(struct lejp_ctx *ctx, char reason) -{ - struct acme_connection *s = (struct acme_connection *)ctx->user; - - if (reason == LEJPCB_CONSTRUCTED) { - s->yes = 0; - s->use = 0; - s->chall_token[0] = '\0'; - } - - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - switch (ctx->path_match - 1) { - case JAAZ_ID_TYPE: - break; - case JAAZ_ID_VALUE: - break; - case JAAZ_STATUS: - break; - case JAAZ_EXPIRES: - break; - case JAAZ_DETAIL: - lws_snprintf(s->detail, sizeof(s->detail), "%s", ctx->buf); - break; - case JAAZ_CHALLENGES_TYPE: - lwsl_notice("JAAZ_CHALLENGES_TYPE: %s\n", ctx->buf); - s->use = !strcmp(ctx->buf, "http-01"); - break; - case JAAZ_CHALLENGES_STATUS: - lws_strncpy(s->status, ctx->buf, sizeof(s->status)); - break; - case JAAZ_CHALLENGES_URL: - lwsl_notice("JAAZ_CHALLENGES_URL: %s %d\n", ctx->buf, s->use); - if (s->use) { - lws_strncpy(s->challenge_uri, ctx->buf, - sizeof(s->challenge_uri)); - s->yes |= 2; - } - break; - case JAAZ_CHALLENGES_TOKEN: - lwsl_notice("JAAZ_CHALLENGES_TOKEN: %s %d\n", ctx->buf, s->use); - if (s->use) { - lws_strncpy(s->chall_token, ctx->buf, - sizeof(s->chall_token)); - s->yes |= 1; - } - break; - } - - return 0; -} - -/* challenge accepted JSON parsing */ - -static const char * const jchac_tok[] = { - "type", - "status", - "uri", - "token", - "error.detail" -}; - -enum enum_jchac_tok { - JCAC_TYPE, - JCAC_STATUS, - JCAC_URI, - JCAC_TOKEN, - JCAC_DETAIL, -}; - -static signed char -cb_chac(struct lejp_ctx *ctx, char reason) -{ - struct acme_connection *s = (struct acme_connection *)ctx->user; - - if (reason == LEJPCB_CONSTRUCTED) { - s->yes = 0; - s->use = 0; - } - - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - switch (ctx->path_match - 1) { - case JCAC_TYPE: - if (strcmp(ctx->buf, "http-01")) - return 1; - break; - case JCAC_STATUS: - lws_strncpy(s->status, ctx->buf, sizeof(s->status)); - break; - case JCAC_URI: - s->yes |= 2; - break; - case JCAC_TOKEN: - lws_strncpy(s->chall_token, ctx->buf, sizeof(s->chall_token)); - s->yes |= 1; - break; - case JCAC_DETAIL: - lws_snprintf(s->detail, sizeof(s->detail), "%s", ctx->buf); - break; - } - - return 0; -} - -static int -lws_acme_report_status(struct lws_vhost *v, int state, const char *json) -{ - lws_callback_vhost_protocols_vhost(v, LWS_CALLBACK_VHOST_CERT_UPDATE, - (void *)json, state); - - return 0; -} - -/* - * Notice: trashes i and url - */ -static struct lws * -lws_acme_client_connect(struct lws_context *context, struct lws_vhost *vh, - struct lws **pwsi, struct lws_client_connect_info *i, - char *url, const char *method) -{ - const char *prot, *p; - char path[200], _url[256]; - struct lws *wsi; - - memset(i, 0, sizeof(*i)); - i->port = 443; - lws_strncpy(_url, url, sizeof(_url)); - if (lws_parse_uri(_url, &prot, &i->address, &i->port, &p)) { - lwsl_err("unable to parse uri %s\n", url); - - return NULL; - } - - /* add back the leading / on path */ - path[0] = '/'; - lws_strncpy(path + 1, p, sizeof(path) - 1); - i->path = path; - i->context = context; - i->vhost = vh; - i->ssl_connection = LCCSCF_USE_SSL; - i->host = i->address; - i->origin = i->address; - i->method = method; - i->pwsi = pwsi; - i->protocol = "lws-acme-client"; - - wsi = lws_client_connect_via_info(i); - if (!wsi) { - lws_snprintf(path, sizeof(path) - 1, - "Unable to connect to %s", url); - lwsl_notice("%s: %s\n", __func__, path); - lws_acme_report_status(vh, LWS_CUS_FAILED, path); - } - - return wsi; -} - -static void -lws_acme_finished(struct per_vhost_data__lws_acme_client *vhd) -{ - lwsl_notice("%s\n", __func__); - - if (vhd->ac) { - if (vhd->ac->vhost) - lws_vhost_destroy(vhd->ac->vhost); - if (vhd->ac->alloc_privkey_pem) - free(vhd->ac->alloc_privkey_pem); - free(vhd->ac); - } - - lws_genrsa_destroy(&vhd->rsactx); - lws_jwk_destroy(&vhd->jwk); - - vhd->ac = NULL; -#if defined(LWS_WITH_ESP32) - lws_esp32.acme = 0; /* enable scanning */ -#endif -} - -static const char * const pvo_names[] = { - "country", - "state", - "locality", - "organization", - "common-name", - "subject-alt-name", - "email", - "directory-url", - "auth-path", - "cert-path", - "key-path", -}; - -static int -lws_acme_load_create_auth_keys(struct per_vhost_data__lws_acme_client *vhd, - int bits) -{ - int n; - - if (!lws_jwk_load(&vhd->jwk, vhd->pvop[LWS_TLS_SET_AUTH_PATH], - NULL, NULL)) - return 0; - - vhd->jwk.kty = LWS_GENCRYPTO_KTY_RSA; - - lwsl_notice("Generating ACME %d-bit keypair... " - "will take a little while\n", bits); - n = lws_genrsa_new_keypair(vhd->context, &vhd->rsactx, LGRSAM_PKCS1_1_5, - vhd->jwk.e, bits); - if (n) { - lwsl_notice("failed to create keypair\n"); - return 1; - } - - lwsl_notice("...keypair generated\n"); - - if (lws_jwk_save(&vhd->jwk, vhd->pvop[LWS_TLS_SET_AUTH_PATH])) { - lwsl_notice("unable to save %s\n", - vhd->pvop[LWS_TLS_SET_AUTH_PATH]); - return 1; - } - - return 0; -} - -static int -lws_acme_start_acquisition(struct per_vhost_data__lws_acme_client *vhd, - struct lws_vhost *v) -{ - char buf[128]; - - /* ...and we were given enough info to do the update? */ - - if (!vhd->pvop[LWS_TLS_REQ_ELEMENT_COMMON_NAME]) - return -1; - - /* - * ...well... we should try to do something about it then... - */ - lwsl_notice("%s: ACME cert needs creating / updating: " - "vhost %s\n", __func__, lws_get_vhost_name(vhd->vhost)); - - vhd->ac = malloc(sizeof(*vhd->ac)); - memset(vhd->ac, 0, sizeof(*vhd->ac)); - - /* - * So if we don't have it, the first job is get the directory. - * - * If we already have the directory, jump straight into trying - * to register our key. - * - * We always try to register the keys... if it's not the first - * time, we will get a JSON body in the (legal, nonfatal) - * response like this - * - * { - * "type": "urn:acme:error:malformed", - * "detail": "Registration key is already in use", - * "status": 409 - * } - */ - if (!vhd->ac->urls[0][0]) { - vhd->ac->state = ACME_STATE_DIRECTORY; - lws_snprintf(buf, sizeof(buf) - 1, "%s", - vhd->pvop_active[LWS_TLS_SET_DIR_URL]); - } else { - vhd->ac->state = ACME_STATE_NEW_ACCOUNT; - lws_snprintf(buf, sizeof(buf) - 1, "%s", - vhd->ac->urls[JAD_NEW_ACCOUNT_URL]); - } - - vhd->ac->real_vh_port = lws_get_vhost_port(vhd->vhost); - vhd->ac->real_vh_name = lws_get_vhost_name(vhd->vhost); - vhd->ac->real_vh_iface = lws_get_vhost_iface(vhd->vhost); - - lws_acme_report_status(vhd->vhost, LWS_CUS_STARTING, NULL); - -#if defined(LWS_WITH_ESP32) - lws_acme_report_status(vhd->vhost, LWS_CUS_CREATE_KEYS, - "Generating keys, please wait"); - if (lws_acme_load_create_auth_keys(vhd, 2048)) - goto bail; - lws_acme_report_status(vhd->vhost, LWS_CUS_CREATE_KEYS, - "Auth keys created"); -#endif - - if (lws_acme_client_connect(vhd->context, vhd->vhost, - &vhd->ac->cwsi, &vhd->ac->i, buf, "GET")) - return 0; - -#if defined(LWS_WITH_ESP32) -bail: -#endif - free(vhd->ac); - vhd->ac = NULL; - - return 1; -} - -static int -callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_vhost_data__lws_acme_client *vhd = - (struct per_vhost_data__lws_acme_client *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - char buf[LWS_PRE + 2536], *start = buf + LWS_PRE, *p = start, - *end = buf + sizeof(buf) - 1, digest[32], *failreason = NULL; - const struct lws_protocol_vhost_options *pvo; - struct lws_acme_cert_aging_args *caa; - struct acme_connection *ac = NULL; - unsigned char **pp, *pend; - const char *content_type; - struct lws_jwe jwe; - struct lws *cwsi; - int n, m; - - if (vhd) - ac = vhd->ac; - - lws_jwe_init(&jwe, lws_get_context(wsi)); - - switch ((int)reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__lws_acme_client)); - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - - /* compute how much we need to hold all the pvo payloads */ - m = 0; - pvo = (const struct lws_protocol_vhost_options *)in; - while (pvo) { - m += strlen(pvo->value) + 1; - pvo = pvo->next; - } - p = vhd->pvo_data = malloc(m); - if (!p) - return -1; - - pvo = (const struct lws_protocol_vhost_options *)in; - while (pvo) { - start = p; - n = strlen(pvo->value) + 1; - memcpy(start, pvo->value, n); - p += n; - - for (m = 0; m < (int)LWS_ARRAY_SIZE(pvo_names); m++) - if (!strcmp(pvo->name, pvo_names[m])) - vhd->pvop[m] = start; - - pvo = pvo->next; - } - - n = 0; - for (m = 0; m < (int)LWS_ARRAY_SIZE(pvo_names); m++) { - if (!vhd->pvop[m] && - m >= LWS_TLS_REQ_ELEMENT_COMMON_NAME && - m != LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME) { - lwsl_notice("%s: require pvo '%s'\n", __func__, - pvo_names[m]); - n |= 1; - } else { - if (vhd->pvop[m]) - lwsl_info(" %s: %s\n", pvo_names[m], - vhd->pvop[m]); - } - } - if (n) { - free(vhd->pvo_data); - vhd->pvo_data = NULL; - - return -1; - } - -#if !defined(LWS_WITH_ESP32) - /* - * load (or create) the registration keypair while we - * still have root - */ - if (lws_acme_load_create_auth_keys(vhd, 4096)) - return 1; - - /* - * in case we do an update, open the update files while we - * still have root - */ - lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", - vhd->pvop[LWS_TLS_SET_CERT_PATH]); - vhd->fd_updated_cert = lws_open(buf, - LWS_O_WRONLY | LWS_O_CREAT | - LWS_O_TRUNC, 0600); - if (vhd->fd_updated_cert < 0) { - lwsl_err("unable to create update cert file %s\n", buf); - return -1; - } - lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", - vhd->pvop[LWS_TLS_SET_KEY_PATH]); - vhd->fd_updated_key = lws_open(buf, LWS_O_WRONLY | LWS_O_CREAT | - LWS_O_TRUNC, 0600); - if (vhd->fd_updated_key < 0) { - lwsl_err("unable to create update key file %s\n", buf); - return -1; - } -#endif - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - if (vhd && vhd->pvo_data) { - free(vhd->pvo_data); - vhd->pvo_data = NULL; - } - if (vhd) - lws_acme_finished(vhd); - break; - - case LWS_CALLBACK_VHOST_CERT_AGING: - if (!vhd) - break; - - caa = (struct lws_acme_cert_aging_args *)in; - /* - * Somebody is telling us about a cert some vhost is using. - * - * First see if the cert is getting close enough to expiry that - * we *want* to do something about it. - */ - if ((int)(ssize_t)len > 14) - break; - - /* - * ...is this a vhost we were configured on? - */ - if (vhd->vhost != caa->vh) - return 1; - - for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pvop);n++) - if (caa->element_overrides[n]) - vhd->pvop_active[n] = caa->element_overrides[n]; - else - vhd->pvop_active[n] = vhd->pvop[n]; - - lwsl_notice("starting acme acquisition on %s: %s\n", - lws_get_vhost_name(caa->vh), - vhd->pvop_active[LWS_TLS_SET_DIR_URL]); - - lws_acme_start_acquisition(vhd, caa->vh); - break; - - /* - * Client - */ - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - lwsl_notice("%s: CLIENT_ESTABLISHED\n", __func__); - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_notice("%s: CLIENT_CONNECTION_ERROR: %p\n", __func__, wsi); - break; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - lwsl_notice("%s: CLOSED_CLIENT_HTTP: %p\n", __func__, wsi); - break; - - case LWS_CALLBACK_CLOSED: - lwsl_notice("%s: CLOSED: %p\n", __func__, wsi); - break; - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - lwsl_notice("%s: ESTABLISHED_CLIENT_HTTP:" - "%p, state:%d, status:%d\n", __func__, wsi, - ac->state, lws_http_client_http_response(wsi)); - if (!ac) - break; - ac->resp = lws_http_client_http_response(wsi); - /* we get a new nonce each time */ - if (lws_hdr_total_length(wsi, WSI_TOKEN_REPLAY_NONCE) && - lws_hdr_copy(wsi, ac->replay_nonce, - sizeof(ac->replay_nonce), - WSI_TOKEN_REPLAY_NONCE) < 0) { - lwsl_notice("%s: nonce too large\n", __func__); - - goto failed; - } - - switch (ac->state) { - case ACME_STATE_DIRECTORY: - lejp_construct(&ac->jctx, cb_dir, vhd, jdir_tok, - LWS_ARRAY_SIZE(jdir_tok)); - break; - case ACME_STATE_NEW_NONCE: - /* - * we try to * register our keys next. - * It's OK if it ends up * they're already registered, - * this eliminates any * gaps where we stored the key - * but registration did not complete for some reason... - */ - ac->state = ACME_STATE_NEW_ACCOUNT; - lws_acme_report_status(vhd->vhost, LWS_CUS_REG, NULL); - - strcpy(buf, ac->urls[JAD_NEW_ACCOUNT_URL]); - cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, - &ac->cwsi, &ac->i, buf, "POST"); - if (!cwsi) { - lwsl_notice("%s: failed to connect to acme\n", - __func__); - goto failed; - } - - return -1; - - case ACME_STATE_NEW_ACCOUNT: - if (!lws_hdr_total_length(wsi, - WSI_TOKEN_HTTP_LOCATION)) { - lwsl_notice("%s: no Location\n", __func__); - goto failed; - } - - if (lws_hdr_copy(wsi, ac->acct_id, sizeof(ac->acct_id), - WSI_TOKEN_HTTP_LOCATION) < 0) { - lwsl_notice("%s: Location too large\n", - __func__); - goto failed; - } - - ac->kid = ac->acct_id; - - lwsl_notice("Location: %s\n", ac->acct_id); - break; - - case ACME_STATE_NEW_ORDER: - if (lws_hdr_copy(wsi, ac->order_url, - sizeof(ac->order_url), - WSI_TOKEN_HTTP_LOCATION) < 0) { - lwsl_notice("%s: missing cert location:\n", - __func__); - - goto failed; - } - - lejp_construct(&ac->jctx, cb_order, ac, jorder_tok, - LWS_ARRAY_SIZE(jorder_tok)); - break; - - case ACME_STATE_AUTHZ: - lejp_construct(&ac->jctx, cb_authz, ac, jauthz_tok, - LWS_ARRAY_SIZE(jauthz_tok)); - break; - - case ACME_STATE_START_CHALL: - lejp_construct(&ac->jctx, cb_chac, ac, jchac_tok, - LWS_ARRAY_SIZE(jchac_tok)); - break; - - case ACME_STATE_POLLING: - case ACME_STATE_POLLING_CSR: - lejp_construct(&ac->jctx, cb_order, ac, jorder_tok, - LWS_ARRAY_SIZE(jorder_tok)); - break; - - case ACME_STATE_DOWNLOAD_CERT: - ac->cpos = 0; - break; - - default: - break; - } - break; - - case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: - if (!ac) - break; - - switch (ac->state) { - case ACME_STATE_DIRECTORY: - case ACME_STATE_NEW_NONCE: - break; - - case ACME_STATE_NEW_ACCOUNT: - p += lws_snprintf(p, end - p, "{" - "\"termsOfServiceAgreed\":true" - ",\"contact\": [\"mailto:%s\"]}", - vhd->pvop_active[LWS_TLS_REQ_ELEMENT_EMAIL]); - - puts(start); - strcpy(ac->active_url, ac->urls[JAD_NEW_ACCOUNT_URL]); -pkt_add_hdrs: - if (lws_gencrypto_jwe_alg_to_definition("RSA1_5", - &jwe.jose.alg)) { - ac->len = 0; - lwsl_notice("%s: no RSA1_5\n", __func__); - goto failed; - } - jwe.jwk = vhd->jwk; - - ac->len = jws_create_packet(&jwe, - start, p - start, - ac->replay_nonce, - ac->active_url, - ac->kid, - &ac->buf[LWS_PRE], - sizeof(ac->buf) - LWS_PRE, - lws_get_context(wsi)); - if (ac->len < 0) { - ac->len = 0; - lwsl_notice("jws_create_packet failed\n"); - goto failed; - } - - pp = (unsigned char **)in; - pend = (*pp) + len; - - ac->pos = 0; - content_type = "application/jose+json"; - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_TYPE, - (uint8_t *)content_type, 21, pp, - pend)) { - lwsl_notice("could not add content type\n"); - goto failed; - } - - n = sprintf(buf, "%d", ac->len); - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_LENGTH, - (uint8_t *)buf, n, pp, pend)) { - lwsl_notice("could not add content length\n"); - goto failed; - } - - lws_client_http_body_pending(wsi, 1); - lws_callback_on_writable(wsi); - break; - - case ACME_STATE_NEW_ORDER: - p += lws_snprintf(p, end - p, - "{" - "\"identifiers\":[{" - "\"type\":\"dns\"," - "\"value\":\"%s\"" - "}]" - "}", - vhd->pvop_active[LWS_TLS_REQ_ELEMENT_COMMON_NAME]); - - puts(start); - strcpy(ac->active_url, ac->urls[JAD_NEW_ORDER_URL]); - goto pkt_add_hdrs; - - case ACME_STATE_AUTHZ: - puts(start); - strcpy(ac->active_url, ac->authz_url); - goto pkt_add_hdrs; - - case ACME_STATE_START_CHALL: - p = start; - end = &buf[sizeof(buf) - 1]; - - p += lws_snprintf(p, end - p, "{}"); - puts(start); - strcpy(ac->active_url, ac->challenge_uri); - goto pkt_add_hdrs; - - case ACME_STATE_POLLING: - strcpy(ac->active_url, ac->order_url); - goto pkt_add_hdrs; - - case ACME_STATE_POLLING_CSR: - if (ac->goes_around) - break; - - p += lws_snprintf(p, end - p, "{\"csr\":\""); - n = lws_tls_acme_sni_csr_create(vhd->context, - &vhd->pvop_active[0], - (uint8_t *)p, end - p, - &ac->alloc_privkey_pem, - &ac->len_privkey_pem); - if (n < 0) { - lwsl_notice("CSR generation failed\n"); - goto failed; - } - p += n; - p += lws_snprintf(p, end - p, "\"}"); - puts(start); - strcpy(ac->active_url, ac->finalize_url); - goto pkt_add_hdrs; - - case ACME_STATE_DOWNLOAD_CERT: - strcpy(ac->active_url, ac->cert_url); - goto pkt_add_hdrs; - break; - - default: - break; - } - break; - - case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: - lwsl_notice("LWS_CALLBACK_CLIENT_HTTP_WRITEABLE\n"); - - if (!ac) - break; - - if (ac->pos == ac->len) - break; - - ac->buf[LWS_PRE + ac->len] = '\0'; - if (lws_write(wsi, (uint8_t *)ac->buf + LWS_PRE, - ac->len, LWS_WRITE_HTTP_FINAL) < 0) - return -1; - lwsl_notice("wrote %d\n", ac->len); - ac->pos = ac->len; - lws_client_http_body_pending(wsi, 0); - break; - - /* chunked content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - if (!ac) - return -1; - - switch (ac->state) { - case ACME_STATE_POLLING_CSR: - case ACME_STATE_POLLING: - case ACME_STATE_START_CHALL: - case ACME_STATE_AUTHZ: - case ACME_STATE_NEW_ORDER: - case ACME_STATE_DIRECTORY: - ((char *)in)[len] = '\0'; - puts(in); - m = (int)(signed char)lejp_parse(&ac->jctx, - (uint8_t *)in, len); - if (m < 0 && m != LEJP_CONTINUE) { - lwsl_notice("lejp parse failed %d\n", m); - goto failed; - } - break; - case ACME_STATE_NEW_ACCOUNT: - ((char *)in)[len] = '\0'; - puts(in); - break; - case ACME_STATE_DOWNLOAD_CERT: - ((char *)in)[len] = '\0'; - puts(in); - /* it should be the DER cert! */ - if (ac->cpos + len > sizeof(ac->buf)) { - lwsl_notice("Incoming cert is too large!\n"); - goto failed; - } - memcpy(&ac->buf[ac->cpos], in, len); - ac->cpos += len; - break; - default: - break; - } - break; - - /* unchunked content */ - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - lwsl_notice("%s: LWS_CALLBACK_RECEIVE_CLIENT_HTTP\n", __func__); - if (!ac) - return -1; - switch (ac->state) { - default: - { - char buffer[2048 + LWS_PRE]; - char *px = buffer + LWS_PRE; - int lenx = sizeof(buffer) - LWS_PRE; - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - break; - } - break; - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_notice("%s: COMPLETED_CLIENT_HTTP\n", __func__); - - if (!ac) - return -1; - - switch (ac->state) { - case ACME_STATE_DIRECTORY: - lejp_destruct(&ac->jctx); - - /* check dir validity */ - - for (n = 0; n < 6; n++) - lwsl_notice(" %d: %s\n", n, ac->urls[n]); - - ac->state = ACME_STATE_NEW_NONCE; - - strcpy(buf, ac->urls[JAD_NEW_NONCE_URL]); - cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, - &ac->cwsi, &ac->i, buf, - "GET"); - if (!cwsi) { - lwsl_notice("%s: failed to connect to acme\n", - __func__); - goto failed; - } - return -1; /* close the completed client connection */ - - case ACME_STATE_NEW_ACCOUNT: - if ((ac->resp >= 200 && ac->resp < 299) || - ac->resp == 409) { - /* - * Our account already existed, or exists now. - * - */ - ac->state = ACME_STATE_NEW_ORDER; - - strcpy(buf, ac->urls[JAD_NEW_ORDER_URL]); - cwsi = lws_acme_client_connect(vhd->context, - vhd->vhost, &ac->cwsi, - &ac->i, buf, "POST"); - if (!cwsi) - lwsl_notice("%s: failed to connect\n", - __func__); - - /* close the completed client connection */ - return -1; - } else { - lwsl_notice("newAccount replied %d\n", - ac->resp); - goto failed; - } - return -1; /* close the completed client connection */ - - case ACME_STATE_NEW_ORDER: - lejp_destruct(&ac->jctx); - if (!ac->authz_url[0]) { - lwsl_notice("no authz\n"); - goto failed; - } - - /* - * Move on to requesting a cert auth. - */ - ac->state = ACME_STATE_AUTHZ; - lws_acme_report_status(vhd->vhost, LWS_CUS_AUTH, - NULL); - - strcpy(buf, ac->authz_url); - cwsi = lws_acme_client_connect(vhd->context, - vhd->vhost, &ac->cwsi, - &ac->i, buf, "POST"); - if (!cwsi) - lwsl_notice("%s: failed to connect\n", - __func__); - - return -1; /* close the completed client connection */ - - case ACME_STATE_AUTHZ: - lejp_destruct(&ac->jctx); - if (ac->resp / 100 == 4) { - lws_snprintf(buf, sizeof(buf), - "Auth failed: %s", ac->detail); - failreason = buf; - lwsl_notice("auth failed\n"); - goto failed; - } - lwsl_notice("chall: %s (%d)\n", ac->chall_token, - ac->resp); - if (!ac->chall_token[0]) { - lwsl_notice("no challenge\n"); - goto failed; - } - - ac->state = ACME_STATE_START_CHALL; - lws_acme_report_status(vhd->vhost, LWS_CUS_CHALLENGE, - NULL); - - memset(&ac->ci, 0, sizeof(ac->ci)); - - /* compute the key authorization */ - - p = ac->key_auth; - end = p + sizeof(ac->key_auth) - 1; - - p += lws_snprintf(p, end - p, "%s.", ac->chall_token); - lws_jwk_rfc7638_fingerprint(&vhd->jwk, digest); - n = lws_jws_base64_enc(digest, 32, p, end - p); - if (n < 0) - goto failed; - - lwsl_notice("key_auth: '%s'\n", ac->key_auth); - - lws_snprintf(ac->http01_mountpoint, - sizeof(ac->http01_mountpoint), - "/.well-known/acme-challenge/%s", - ac->chall_token); - - memset(&ac->mount, 0, sizeof (struct lws_http_mount)); - ac->mount.protocol = "http"; - ac->mount.mountpoint = ac->http01_mountpoint; - ac->mount.mountpoint_len = - strlen(ac->http01_mountpoint); - ac->mount.origin_protocol = LWSMPRO_CALLBACK; - - ac->ci.mounts = &ac->mount; - - /* listen on the same port as the vhost that triggered - * us */ - ac->ci.port = 80; - - /* make ourselves protocols[0] for the new vhost */ - ac->ci.protocols = chall_http01_protocols; - - /* - * vhost .user points to the ac associated with the - * temporary vhost - */ - ac->ci.user = ac; - - ac->vhost = lws_create_vhost(lws_get_context(wsi), - &ac->ci); - if (!ac->vhost) - goto failed; - - lwsl_notice("challenge_uri %s\n", ac->challenge_uri); - - /* - * The challenge-specific vhost is up... let the ACME - * server know we are ready to roll... - */ - ac->goes_around = 0; - cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, - &ac->cwsi, &ac->i, - ac->challenge_uri, - "POST"); - if (!cwsi) { - lwsl_notice("%s: connect failed\n", __func__); - goto failed; - } - return -1; /* close the completed client connection */ - - case ACME_STATE_START_CHALL: - lwsl_notice("%s: COMPLETED start chall: %s\n", - __func__, ac->challenge_uri); -poll_again: - ac->state = ACME_STATE_POLLING; - lws_acme_report_status(vhd->vhost, LWS_CUS_CHALLENGE, - NULL); - - if (ac->goes_around++ == 20) { - lwsl_notice("%s: too many chall retries\n", - __func__); - - goto failed; - } - - strcpy(buf, ac->order_url); - cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, - &ac->cwsi, &ac->i, buf, - "POST"); - if (!cwsi) { - lwsl_notice("%s: failed to connect to acme\n", - __func__); - - goto failed; - } - return -1; /* close the completed client connection */ - - case ACME_STATE_POLLING: - - if (ac->resp == 202 && strcmp(ac->status, "invalid") && - strcmp(ac->status, "valid")) { - lwsl_notice("status: %s\n", ac->status); - goto poll_again; - } - - if (!strcmp(ac->status, "pending")) { - lwsl_notice("status: %s\n", ac->status); - goto poll_again; - } - - if (!strcmp(ac->status, "invalid")) { - lwsl_notice("%s: Challenge failed\n", __func__); - lws_snprintf(buf, sizeof(buf), - "Challenge Invalid: %s", - ac->detail); - failreason = buf; - goto failed; - } - - lwsl_notice("Challenge passed\n"); - - /* - * The challenge was validated... so delete the - * temp vhost now its job is done - */ - if (ac->vhost) - lws_vhost_destroy(ac->vhost); - ac->vhost = NULL; - - /* - * now our JWK is accepted as authorized to make - * requests for the domain, next move is create the - * CSR signed with the JWK, and send it to the ACME - * server to request the actual certs. - */ - ac->state = ACME_STATE_POLLING_CSR; - lws_acme_report_status(vhd->vhost, LWS_CUS_REQ, NULL); - ac->goes_around = 0; - - strcpy(buf, ac->finalize_url); - cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, - &ac->cwsi, &ac->i, buf, - "POST"); - if (!cwsi) { - lwsl_notice("%s: failed to connect to acme\n", - __func__); - - goto failed; - } - return -1; /* close the completed client connection */ - - case ACME_STATE_POLLING_CSR: - if (ac->resp < 200 || ac->resp > 202) { - lwsl_notice("CSR poll failed on resp %d\n", - ac->resp); - goto failed; - } - - if (ac->resp != 200) { - if (ac->goes_around++ == 30) { - lwsl_notice("%s: too many retries\n", - __func__); - - goto failed; - } - strcpy(buf, ac->finalize_url); - cwsi = lws_acme_client_connect(vhd->context, - vhd->vhost, - &ac->cwsi, &ac->i, buf, - "POST"); - if (!cwsi) { - lwsl_notice("%s: " - "failed to connect to acme\n", - __func__); - - goto failed; - } - /* close the completed client connection */ - return -1; - } - - ac->state = ACME_STATE_DOWNLOAD_CERT; - - strcpy(buf, ac->cert_url); - cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, - &ac->cwsi, &ac->i, buf, - "POST"); - if (!cwsi) { - lwsl_notice("%s: failed to connect to acme\n", - __func__); - - goto failed; - } - return -1; - - case ACME_STATE_DOWNLOAD_CERT: - - if (ac->resp != 200) { - lwsl_notice("download cert failed on resp %d\n", - ac->resp); - goto failed; - } - lwsl_notice("The cert was sent..\n"); - - lws_acme_report_status(vhd->vhost, LWS_CUS_ISSUE, NULL); - - /* - * That means we have the issued cert in - * ac->buf, length in ac->cpos; and the key in - * ac->alloc_privkey_pem, length in - * ac->len_privkey_pem. - */ - n = lws_plat_write_cert(vhd->vhost, 0, - vhd->fd_updated_cert, - ac->buf, - ac->cpos); - if (n) { - lwsl_err("unable to write ACME cert! %d\n", n); - goto failed; - } - - /* - * don't close it... we may update the certs - * again - */ - if (lws_plat_write_cert(vhd->vhost, 1, - vhd->fd_updated_key, - ac->alloc_privkey_pem, - ac->len_privkey_pem)) { - lwsl_err("unable to write ACME key!\n"); - goto failed; - } - - /* - * we have written the persistent copies - */ - lwsl_notice("%s: Updated certs written for %s " - "to %s.upd and %s.upd\n", __func__, - vhd->pvop_active[LWS_TLS_REQ_ELEMENT_COMMON_NAME], - vhd->pvop_active[LWS_TLS_SET_CERT_PATH], - vhd->pvop_active[LWS_TLS_SET_KEY_PATH]); - - /* notify lws there was a cert update */ - - if (lws_tls_cert_updated(vhd->context, - vhd->pvop_active[LWS_TLS_SET_CERT_PATH], - vhd->pvop_active[LWS_TLS_SET_KEY_PATH], - ac->buf, ac->cpos, - ac->alloc_privkey_pem, - ac->len_privkey_pem)) { - lwsl_notice("problem setting certs\n"); - } - - lws_acme_finished(vhd); - lws_acme_report_status(vhd->vhost, - LWS_CUS_SUCCESS, NULL); - - return -1; - - default: - break; - } - break; - - case LWS_CALLBACK_USER + 0xac33: - if (!vhd) - break; - cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, - &ac->cwsi, &ac->i, - ac->challenge_uri, - "GET"); - if (!cwsi) { - lwsl_notice("%s: failed to connect\n", __func__); - goto failed; - } - break; - - default: - break; - } - - return 0; - -failed: - lwsl_notice("%s: failed out\n", __func__); - lws_acme_report_status(vhd->vhost, LWS_CUS_FAILED, failreason); - lws_acme_finished(vhd); - - return -1; -} - -#if !defined (LWS_PLUGIN_STATIC) - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_LWS_ACME_CLIENT -}; - -LWS_VISIBLE int -init_protocol_lws_acme_client(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_lws_acme_client(struct lws_context *context) -{ - return 0; -} - -#endif diff -Nru libwebsockets-4.0.20/plugins/deaddrop/assets/deaddrop.css libwebsockets-2.4.2/plugins/deaddrop/assets/deaddrop.css --- libwebsockets-4.0.20/plugins/deaddrop/assets/deaddrop.css 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/plugins/deaddrop/assets/deaddrop.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -.td { padding: 8px } -.h1 { } -.dd-fileinfo { font-size: 8pt; } -table td { - display: table-cell; - vertical-align: top; - background-color: rgba(247, 247, 232, 0.6); - text-align: center -} -table { - border: 2px solid #ccc; - padding: 4px; - border-radius: 12px; - transition: background-color 0.5s ease; -} -table.nb { border: 0px; border-radius: 0px; transition: opacity 0.5s; } -table.noconn { background-color: #ddd; } - -div { transition: opacity 0.5s; } -div.da { padding-left: 20px; padding-right:20px; } -div.trot { - animation: scale 0.5s linear infinite; -} -div.uplbox { padding-bottom: 8px; } -div.disa { opacity: 0.2; } - -td.ogn { text-align:left; font-size: 8pt; padding-left: 4px; padding-right: 4px;} -td.dow { text-align:left; font-size: 9pt; padding-left: 4px; padding-right: 4px;} -td.r { text-align: right; } -td.err { color: red; font-weight: bold; } -td.vm { display: table-cell; vertical-align: middle; padding-top: 12px; padding-bottom: 12px; } - -h3 { font-size: 12pt; margin-bottom: 6px; } -span { font-size: 9pt; } -a { font-size: 9pt; } - -input.ubtn { font-size: 16pt; margin-top: 4px; text-align: center } - -img.working { - display: inline-block; - float:left; - background: url("data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIKICAgICB3aWR0aD0iMjRweCIgaGVpZ2h0PSIzMHB4IiB2aWV3Qm94PSIwIDAgMjQgMzAiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUwIDUwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CiAgICA8cmVjdCB4PSIwIiB5PSIxMCIgd2lkdGg9IjQiIGhlaWdodD0iMTAiIGZpbGw9IiMzMzMiIG9wYWNpdHk9IjAuMiI+CiAgICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9Im9wYWNpdHkiIGF0dHJpYnV0ZVR5cGU9IlhNTCIgdmFsdWVzPSIwLjI7IDE7IC4yIiBiZWdpbj0iMHMiIGR1cj0iMC42cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIC8+CiAgICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImhlaWdodCIgYXR0cmlidXRlVHlwZT0iWE1MIiB2YWx1ZXM9IjEwOyAyMDsgMTAiIGJlZ2luPSIwcyIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICAgICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ieSIgYXR0cmlidXRlVHlwZT0iWE1MIiB2YWx1ZXM9IjEwOyA1OyAxMCIgYmVnaW49IjBzIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0iOCIgeT0iMTAiIHdpZHRoPSI0IiBoZWlnaHQ9IjEwIiBmaWxsPSIjMzMzIiAgb3BhY2l0eT0iMC4yIj4KICAgICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ib3BhY2l0eSIgYXR0cmlidXRlVHlwZT0iWE1MIiB2YWx1ZXM9IjAuMjsgMTsgLjIiIGJlZ2luPSIwLjE1cyIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICAgICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iaGVpZ2h0IiBhdHRyaWJ1dGVUeXBlPSJYTUwiIHZhbHVlcz0iMTA7IDIwOyAxMCIgYmVnaW49IjAuMTVzIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogICAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ5IiBhdHRyaWJ1dGVUeXBlPSJYTUwiIHZhbHVlcz0iMTA7IDU7IDEwIiBiZWdpbj0iMC4xNXMiIGR1cj0iMC42cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIC8+CiAgICA8L3JlY3Q+CiAgICA8cmVjdCB4PSIxNiIgeT0iMTAiIHdpZHRoPSI0IiBoZWlnaHQ9IjEwIiBmaWxsPSIjMzMzIiAgb3BhY2l0eT0iMC4yIj4KICAgICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ib3BhY2l0eSIgYXR0cmlidXRlVHlwZT0iWE1MIiB2YWx1ZXM9IjAuMjsgMTsgLjIiIGJlZ2luPSIwLjNzIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogICAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJoZWlnaHQiIGF0dHJpYnV0ZVR5cGU9IlhNTCIgdmFsdWVzPSIxMDsgMjA7IDEwIiBiZWdpbj0iMC4zcyIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICAgICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ieSIgYXR0cmlidXRlVHlwZT0iWE1MIiB2YWx1ZXM9IjEwOyA1OyAxMCIgYmVnaW49IjAuM3MiIGR1cj0iMC42cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIC8+CiAgICA8L3JlY3Q+Cjwvc3ZnPg=="); - width:0px; - height:0px; - cursor:pointer; - padding:0.6em 1em; - background-repeat: no-repeat; - vertical-align:middle; - color: rgba(0, 0, 0, 0); -} - -img.delbtn { - display: inline-block; - float:left; - background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAzNC4yMTcxMzMgMzQuMTQ0MjQ5IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KCTxkZWZzPgoJCTxyYWRpYWxHcmFkaWVudCBpZD0iYSIgY3g9IjEzNTguNSIgY3k9Ijk3Ny43MiIgcj0iNC4xMTYiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMS41MDM1IDAgMCAyLjQ5NDQgLTI1NzcuNCAtMTgyMy44KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCQk8c3RvcCBzdG9wLWNvbG9yPSIjZmZmIiBvZmZzZXQ9IjAiLz4KCQkJPHN0b3Agc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIwIiBvZmZzZXQ9IjEiLz4KCQk8L3JhZGlhbEdyYWRpZW50PgoJCTxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjEzNjYuMyIgeDI9IjEzNDQuOCIgeTE9Ijk3OC4xOSIgeTI9Ijk1OC43MyIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgtMjE3NCAtMTA3Ni4xKSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCQk8c3RvcCBzdG9wLWNvbG9yPSIjOWM5NzlkIiBvZmZzZXQ9IjAiLz4KCQkJPHN0b3Agc3RvcC1jb2xvcj0iI2Y0ZmVmMyIgc3RvcC1vcGFjaXR5PSIuNzQwMTYiIG9mZnNldD0iMSIvPgoJCTwvbGluZWFyR3JhZGllbnQ+Cgk8L2RlZnM+Cgk8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4MzQuMjkgMTIzLjc1KSI+CgkJPGcgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj4KCQkJPGNpcmNsZSBjeD0iLTgxNy4yMiIgY3k9Ii0xMDYuNjgiIHI9IjE2LjA5IiBmaWxsPSJ1cmwoI2IpIiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMS45NjUiLz4KCQkJPHBhdGggZD0ibS04MjUuNjctMTE0LjcxYzE3LjMzIDE3LjMzIDE3IDE2Ljk5OSAxNyAxNi45OTkiIGZpbGw9IiNmNTUiIHN0cm9rZT0iI2EwMCIgc3Ryb2tlLXdpZHRoPSIxLjY2NSIvPgoJCQk8cGF0aCBkPSJtLTgwOC45Ni0xMTQuNjFjLTE3LjMzIDE3LjMzLTE3IDE2Ljk5OS0xNyAxNi45OTkiIGZpbGw9IiNmNTUiIHN0cm9rZT0iI2EwMCIgc3Ryb2tlLXdpZHRoPSIxLjY2NSIvPgoJCTwvZz4KCQk8ZWxsaXBzZSB0cmFuc2Zvcm09InJvdGF0ZSg1NS44ODUpIiBjeD0iLTUzNC45NiIgY3k9IjYxNS4wNyIgcng9IjYuMTg4MyIgcnk9IjEwLjI2NyIgZmlsbD0idXJsKCNhKSIvPgoJPC9nPgo8L3N2Zz4="); - width:0px; - height:0px; - cursor:pointer; - padding:0.45em; - background-repeat: no-repeat; - vertical-align:middle; - color: rgba(0, 0, 0, 0); -} - -@keyframes scale { - 50% { - opacity: 0.5; - transform:scale(1.1) rotate(2deg); - } -} diff -Nru libwebsockets-4.0.20/plugins/deaddrop/assets/deaddrop.js libwebsockets-2.4.2/plugins/deaddrop/assets/deaddrop.js --- libwebsockets-4.0.20/plugins/deaddrop/assets/deaddrop.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/plugins/deaddrop/assets/deaddrop.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,296 +0,0 @@ -(function() { - - var server_max_size = 0, ws; - - function san(s) - { - if (!s) - return ""; - - return s.replace(/&/g, "&"). - replace(/\/g, ">"). - replace(/\"/g, """). - replace(/%/g, "%"); - } - - function lws_urlencode(s) - { - return encodeURI(s).replace(/@/g, "%40"); - } - - function trim(num) - { - var s = num.toString(); - - if (!s.indexOf(".")) - return s; - - while (s.length && s[s.length - 1] === "0") - s = s.substring(0, s.length - 1); - - if (s[s.length - 1] === ".") - s = s.substring(0, s.length - 1); - - return s; - } - - function humanize(n) - { - if (n < 1024) - return n + "B"; - - if (n < 1024 * 1024) - return trim((n / 1024).toFixed(2)) + "KiB"; - - if (n < 1024 * 1024 * 1024) - return trim((n / (1024 * 1024)).toFixed(2)) + "MiB"; - - return trim((n / (1024 * 1024 * 1024)).toFixed(2)) + "GiB"; - } - - function da_enter(e) - { - var da = document.getElementById("da"); - - e.preventDefault(); - da.classList.add("trot"); - } - - function da_leave(e) - { - var da = document.getElementById("da"); - - e.preventDefault(); - da.classList.remove("trot"); - } - - function da_over(e) - { - var da = document.getElementById("da"); - - e.preventDefault(); - da.classList.add("trot"); - } - - function clear_errors() { - var n, t = document.getElementById("ongoing"); - - for (n = 0; n < t.rows.length; n++) - if (t.rows[n].cells[0].classList.contains("err")) - t.deleteRow(n); - } - - function do_upload(file) { - var formData = new FormData(); - var t = document.getElementById("ongoing"); - - formData.append("file", file); - - var row = t.insertRow(0), c1 = row.insertCell(0), - c2 = row.insertCell(1), c3 = row.insertCell(2); - - c1.classList.add("ogn"); - c1.classList.add("r"); - - if (file.size > server_max_size) { - c1.innerHTML = "Too Large"; - c1.classList.add("err"); - } else - c1.innerHTML = ""; - - c2.classList.add("ogn"); - c2.classList.add("r"); - c2.innerHTML = humanize(file.size); - - c3.classList.add("ogn"); - c3.innerHTML = file.name; - - if (file.size > server_max_size) - return; - - fetch("upload/" + lws_urlencode(file.name), { - method: "POST", - body: formData - }) - .then((e) => { /* this just means we got a response code */ - var us = e.url.split("/"), ul = us[us.length - 1], n; - - for (n = 0; n < t.rows.length; n++) - if (ul === lws_urlencode( - t.rows[n].cells[2].textContent)) { - if (e.ok === true) { - t.deleteRow(n); - } else { - t.rows[n].cells[0].textContent = - "Failed " + san(e.status.toString()); - t.rows[n].cells[0]. - classList.add("err"); - } - break; - } - }) - .catch((e) => { - var us = e.url.split("/"), ul = us[us.length - 1], n; - - for (n = 0; n < t.rows.length; n++) - if (ul === lws_urlencode( - t.rows[n].cells[2].textContent)) { - t.rows[n].cells[0] = "FAIL"; - break; - } - }); - } - - function da_drop(e) { - var da = document.getElementById("da"); - - e.preventDefault(); - da.classList.remove("trot"); - - clear_errors(); - - ([...e.dataTransfer.files]).forEach(do_upload); - } - - function upl_button(e) { - var fi = document.getElementById("file"); - - clear_errors(); - e.preventDefault(); - - ([...fi.files]).forEach(do_upload); - } - - function body_drop(e) { - e.preventDefault(); - } - - function inp() { - var fi = document.getElementById("file"), - upl = document.getElementById("upl"); - console.log("inp"); - upl.disabled = !fi.files.length; - } - - function delfile(e) - { - e.stopPropagation(); - e.preventDefault(); - - ws.send("{\"del\":\"" + decodeURI(e.target.getAttribute("file")) + - "\"}"); - } - - function get_appropriate_ws_url(extra_url) - { - var pcol; - var u = document.URL; - - /* - * We open the websocket encrypted if this page came on an - * https:// url itself, otherwise unencrypted - */ - - if (u.substring(0, 5) === "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) === "http") - u = u.substr(7); - } - - u = u.split("/"); - - /* + "/xxx" bit is for IE10 workaround */ - - return pcol + u[0] + "/" + extra_url; - } - - function new_ws(urlpath, protocol) - { - return new WebSocket(urlpath, protocol); - } - - document.addEventListener("DOMContentLoaded", function() { - var da = document.getElementById("da"), - fi = document.getElementById("file"), - upl = document.getElementById("upl"); - - da.addEventListener("dragenter", da_enter, false); - da.addEventListener("dragleave", da_leave, false); - da.addEventListener("dragover", da_over, false); - da.addEventListener("drop", da_drop, false); - - upl.addEventListener("click", upl_button, false); - fi.addEventListener("change", inp, false); - - window.addEventListener("dragover", body_drop, false); - window.addEventListener("drop", body_drop, false); - - ws = new_ws(get_appropriate_ws_url(""), "lws-deaddrop"); - try { - ws.onopen = function() { - var dd = document.getElementById("ddrop"), - da = document.getElementById("da"); - - dd.classList.remove("noconn"); - da.classList.remove("disa"); - }; - - ws.onmessage = function got_packet(msg) { - var j = JSON.parse(msg.data), s = "", n, - t = document.getElementById("dd-list"); - - server_max_size = j.max_size; - document.getElementById("size").innerHTML = - "Server maximum file size " + - humanize(j.max_size); - - s += ""; - for (n = 0; n < j.files.length; n++) { - var date = new Date(j.files[n].mtime * 1000); - s += ""; - } - s += "
" + - humanize(j.files[n].size) + - "" + - date.toDateString() + " " + - date.toLocaleTimeString() + - ""; - if (j.files[n].yours === 1) - s += ""; - else - s += " "; - - s += "" + - san(j.files[n].name) + "
"; - - t.innerHTML = s; - - for (n = 0; n < j.files.length; n++) { - var d = document.getElementById("d" + n); - if (d) - d.addEventListener("click", - delfile, false); - } - }; - - ws.onclose = function() { - var dd = document.getElementById("ddrop"), - da = document.getElementById("da"); - - dd.classList.add("noconn"); - da.classList.add("disa"); - }; - } catch(exception) { - alert("

Error " + exception); - } - - }); -}()); diff -Nru libwebsockets-4.0.20/plugins/deaddrop/assets/drop.svg libwebsockets-2.4.2/plugins/deaddrop/assets/drop.svg --- libwebsockets-4.0.20/plugins/deaddrop/assets/drop.svg 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/plugins/deaddrop/assets/drop.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru libwebsockets-4.0.20/plugins/deaddrop/assets/index.html libwebsockets-2.4.2/plugins/deaddrop/assets/index.html --- libwebsockets-4.0.20/plugins/deaddrop/assets/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/plugins/deaddrop/assets/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ - - - - - - - -

LWS Deaddrop

-
- - - -
-
-
-

...or select files to upload:

-
-
- - -
- -
-
-
-
- -
-
-
- - \ No newline at end of file diff -Nru libwebsockets-4.0.20/plugins/deaddrop/protocol_lws_deaddrop.c libwebsockets-2.4.2/plugins/deaddrop/protocol_lws_deaddrop.c --- libwebsockets-4.0.20/plugins/deaddrop/protocol_lws_deaddrop.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/plugins/deaddrop/protocol_lws_deaddrop.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,716 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * 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_PLUGIN_STATIC) -#define LWS_DLL -#define LWS_INTERNAL -#include -#endif - -#include -#include -#include -#include -#include -#include -#ifdef WIN32 -#include -#endif -#include -#include - -struct dir_entry { - lws_list_ptr next; /* sorted by mtime */ - char user[32]; - unsigned long long size; - time_t mtime; -}; -/* filename follows */ - -#define lp_to_dir_entry(p, _n) lws_list_ptr_container(p, struct dir_entry, _n) - -struct pss_deaddrop; - -struct vhd_deaddrop { - struct lws_context *context; - struct lws_vhost *vh; - const struct lws_protocols *protocol; - - struct pss_deaddrop *pss_head; - - const char *upload_dir; - - struct lwsac *lwsac_head; - struct dir_entry *dire_head; - int filelist_version; - - unsigned long long max_size; -}; - -struct pss_deaddrop { - struct lws_spa *spa; - struct vhd_deaddrop *vhd; - struct lws *wsi; - char result[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE]; - char filename[256]; - char user[32]; - unsigned long long file_length; - lws_filefd_type fd; - int response_code; - - struct pss_deaddrop *pss_list; - - struct lwsac *lwsac_head; - struct dir_entry *dire; - int filelist_version; - - uint8_t completed:1; - uint8_t sent_headers:1; - uint8_t sent_body:1; - uint8_t first:1; -}; - -static const char * const param_names[] = { - "text", - "send", - "file", - "upload", -}; - -enum enum_param_names { - EPN_TEXT, - EPN_SEND, - EPN_FILE, - EPN_UPLOAD, -}; - -static int -de_mtime_sort(lws_list_ptr a, lws_list_ptr b) -{ - struct dir_entry *p1 = lp_to_dir_entry(a, next), - *p2 = lp_to_dir_entry(b, next); - - return (int)(p2->mtime - p1->mtime); -} - -static void -start_sending_dir(struct pss_deaddrop *pss) -{ - if (pss->vhd->lwsac_head) - lwsac_reference(pss->vhd->lwsac_head); - pss->lwsac_head = pss->vhd->lwsac_head; - pss->dire = pss->vhd->dire_head; - pss->filelist_version = pss->vhd->filelist_version; - pss->first = 1; -} - -static int -scan_upload_dir(struct vhd_deaddrop *vhd) -{ - char filepath[256], subdir[3][128], *p; - int m, sp = 0, initial, found = 0; - struct lwsac *lwsac_head = NULL; - lws_list_ptr sorted_head = NULL; - struct dir_entry *dire; - struct dirent *de; - struct stat s; - DIR *dir[3]; - - initial = strlen(vhd->upload_dir) + 1; - lws_strncpy(subdir[sp], vhd->upload_dir, sizeof(subdir[sp])); - dir[sp] = opendir(vhd->upload_dir); - if (!dir[sp]) { - lwsl_err("%s: Unable to walk upload dir '%s'\n", __func__, - vhd->upload_dir); - return -1; - } - - do { - de = readdir(dir[sp]); - if (!de) { - closedir(dir[sp]); -#if !defined(__COVERITY__) - if (!sp) -#endif - break; -#if !defined(__COVERITY__) - sp--; - continue; -#endif - } - - p = filepath; - - for (m = 0; m <= sp; m++) - p += lws_snprintf(p, (filepath + sizeof(filepath)) - p, - "%s/", subdir[m]); - - lws_snprintf(p, (filepath + sizeof(filepath)) - p, "%s", - de->d_name); - - /* ignore temp files */ - if (de->d_name[strlen(de->d_name) - 1] == '~') - continue; -#if defined(__COVERITY__) - s.st_size = 0; - s.st_mtime = 0; -#else - /* coverity[toctou] */ - if (stat(filepath, &s)) - continue; - - if (S_ISDIR(s.st_mode)) { - if (!strcmp(de->d_name, ".") || - !strcmp(de->d_name, "..")) - continue; - sp++; - if (sp == LWS_ARRAY_SIZE(dir)) { - lwsl_err("%s: Skipping too-deep subdir %s\n", - __func__, filepath); - sp--; - continue; - } - lws_strncpy(subdir[sp], de->d_name, sizeof(subdir[sp])); - dir[sp] = opendir(filepath); - if (!dir[sp]) { - lwsl_err("%s: Unable to open subdir '%s'\n", - __func__, filepath); - goto bail; - } - continue; - } -#endif - - m = strlen(filepath + initial) + 1; - dire = lwsac_use(&lwsac_head, sizeof(*dire) + m, 0); - if (!dire) { - lwsac_free(&lwsac_head); - - goto bail; - } - - dire->next = NULL; - dire->size = s.st_size; - dire->mtime = s.st_mtime; - dire->user[0] = '\0'; -#if !defined(__COVERITY__) - if (sp) - lws_strncpy(dire->user, subdir[1], sizeof(dire->user)); -#endif - - found++; - - memcpy(&dire[1], filepath + initial, m); - - lws_list_ptr_insert(&sorted_head, &dire->next, de_mtime_sort); - } while (1); - - /* the old lwsac continues to live while someone else is consuming it */ - if (vhd->lwsac_head) - lwsac_detach(&vhd->lwsac_head); - - /* we replace it with the fresh one */ - vhd->lwsac_head = lwsac_head; - if (sorted_head) - vhd->dire_head = lp_to_dir_entry(sorted_head, next); - else - vhd->dire_head = NULL; - - vhd->filelist_version++; - - lwsl_info("%s: found %d\n", __func__, found); - - lws_start_foreach_llp(struct pss_deaddrop **, ppss, vhd->pss_head) { - start_sending_dir(*ppss); - lws_callback_on_writable((*ppss)->wsi); - } lws_end_foreach_llp(ppss, pss_list); - - return 0; - -bail: - while (sp >= 0) - closedir(dir[sp--]); - - return -1; -} - -static int -file_upload_cb(void *data, const char *name, const char *filename, - char *buf, int len, enum lws_spa_fileupload_states state) -{ - struct pss_deaddrop *pss = (struct pss_deaddrop *)data; - char filename2[256]; - int n; - - (void)n; - - switch (state) { - case LWS_UFS_OPEN: - lws_urldecode(filename2, filename, sizeof(filename2) - 1); - lws_filename_purify_inplace(filename2); - if (pss->user[0]) { - lws_filename_purify_inplace(pss->user); - lws_snprintf(pss->filename, sizeof(pss->filename), - "%s/%s", pss->vhd->upload_dir, pss->user); - if (mkdir(pss->filename -#if !defined(WIN32) - , 0700 -#endif - ) < 0) - lwsl_debug("%s: mkdir failed\n", __func__); - lws_snprintf(pss->filename, sizeof(pss->filename), - "%s/%s/%s~", pss->vhd->upload_dir, - pss->user, filename2); - } else - lws_snprintf(pss->filename, sizeof(pss->filename), - "%s/%s~", pss->vhd->upload_dir, filename2); - lwsl_notice("%s: filename '%s'\n", __func__, pss->filename); - - pss->fd = (lws_filefd_type)(long long)lws_open(pss->filename, - O_CREAT | O_TRUNC | O_RDWR, 0600); - if (pss->fd == LWS_INVALID_FILE) { - pss->response_code = HTTP_STATUS_INTERNAL_SERVER_ERROR; - lwsl_err("%s: unable to open %s (errno %d)\n", __func__, - pss->filename, errno); - return -1; - } - break; - - case LWS_UFS_FINAL_CONTENT: - case LWS_UFS_CONTENT: - if (len) { - pss->file_length += len; - - /* if the file length is too big, drop it */ - if (pss->file_length > pss->vhd->max_size) { - pss->response_code = - HTTP_STATUS_REQ_ENTITY_TOO_LARGE; - close((int)(lws_intptr_t)pss->fd); - pss->fd = LWS_INVALID_FILE; - unlink(pss->filename); - - return -1; - } - - if (pss->fd != LWS_INVALID_FILE) { - n = write((int)(lws_intptr_t)pss->fd, buf, len); - lwsl_debug("%s: write %d says %d\n", __func__, - len, n); - lws_set_timeout(pss->wsi, PENDING_TIMEOUT_HTTP_CONTENT, 30); - } - } - if (state == LWS_UFS_CONTENT) - break; - - if (pss->fd != LWS_INVALID_FILE) - close((int)(lws_intptr_t)pss->fd); - - /* the temp filename without the ~ */ - lws_strncpy(filename2, pss->filename, sizeof(filename2)); - filename2[strlen(filename2) - 1] = '\0'; - if (rename(pss->filename, filename2) < 0) - lwsl_err("%s: unable to rename\n", __func__); - - pss->fd = LWS_INVALID_FILE; - pss->response_code = HTTP_STATUS_OK; - scan_upload_dir(pss->vhd); - - break; - case LWS_UFS_CLOSE: - break; - } - - return 0; -} - -/* - * returns length in bytes - */ - -static int -format_result(struct pss_deaddrop *pss) -{ - unsigned char *p, *start, *end; - - p = (unsigned char *)pss->result + LWS_PRE; - start = p; - end = p + sizeof(pss->result) - LWS_PRE - 1; - - p += lws_snprintf((char *)p, end -p, - "" - "" - ""); - p += lws_snprintf((char *)p, end - p, ""); - - return (int)lws_ptr_diff(p, start); -} - -static int -callback_deaddrop(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct vhd_deaddrop *vhd = (struct vhd_deaddrop *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - struct pss_deaddrop *pss = (struct pss_deaddrop *)user; - uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE], - *start = &buf[LWS_PRE], *p = start, - *end = &buf[sizeof(buf) - LWS_PRE - 1]; - char fname[256], *wp; - const char *cp; - int n, m, was; - - switch (reason) { - - case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */ - lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct vhd_deaddrop)); - - vhd = (struct vhd_deaddrop *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - - vhd->context = lws_get_context(wsi); - vhd->vh = lws_get_vhost(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->max_size = 20 * 1024 * 1024; /* default without pvo */ - - if (!lws_pvo_get_str(in, "max-size", &cp)) - vhd->max_size = atoll(cp); - if (lws_pvo_get_str(in, "upload-dir", &vhd->upload_dir)) { - lwsl_err("%s: requires 'upload-dir' pvo\n", __func__); - return -1; - } - - scan_upload_dir(vhd); - - lwsl_notice(" deaddrop: vh %s, upload dir %s, max size %llu\n", - lws_get_vhost_name(vhd->vh), vhd->upload_dir, - vhd->max_size); - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - lwsac_free(&vhd->lwsac_head); - break; - - /* WS-related */ - - case LWS_CALLBACK_ESTABLISHED: - pss->vhd = vhd; - pss->wsi = wsi; - /* add ourselves to the list of live pss held in the vhd */ - pss->pss_list = vhd->pss_head; - vhd->pss_head = pss; - - m = lws_hdr_copy(wsi, pss->user, sizeof(pss->user), - WSI_TOKEN_HTTP_AUTHORIZATION); - if (m > 0) - lwsl_info("%s: basic auth user: %s\n", - __func__, pss->user); - else - pss->user[0] = '\0'; - - start_sending_dir(pss); - lws_callback_on_writable(wsi); - return 0; - - case LWS_CALLBACK_CLOSED: - if (pss->lwsac_head) - lwsac_unreference(&pss->lwsac_head); - /* remove our closing pss from the list of live pss */ - lws_start_foreach_llp(struct pss_deaddrop **, - ppss, vhd->pss_head) { - if (*ppss == pss) { - *ppss = pss->pss_list; - break; - } - } lws_end_foreach_llp(ppss, pss_list); - return 0; - - case LWS_CALLBACK_RECEIVE: - /* we get this kind of thing {"del":"agreen/no-entry.svg"} */ - if (!pss || len < 10) - break; - - if (strncmp((const char *)in, "{\"del\":\"", 8)) - break; - - cp = strchr((const char *)in, '/'); - if (cp) { - n = ((void *)cp - in) - 8; - - if ((int)strlen(pss->user) != n || - memcmp(pss->user, ((const char *)in) + 8, n)) { - lwsl_notice("%s: del: auth mismatch " - " '%s' '%s' (%d)\n", - __func__, pss->user, - ((const char *)in) + 8, n); - break; - } - } - - lws_strncpy(fname, ((const char *)in) + 8, sizeof(fname)); - lws_filename_purify_inplace(fname); - wp = strchr((const char *)fname, '\"'); - if (wp) - *wp = '\0'; - - lws_snprintf((char *)buf, sizeof(buf), "%s/%s", vhd->upload_dir, - fname); - - lwsl_notice("%s: del: path %s\n", __func__, (const char *)buf); - - if (unlink((const char *)buf) < 0) - lwsl_err("%s: unlink %s failed\n", __func__, - (const char *)buf); - - scan_upload_dir(vhd); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - if (pss->lwsac_head && !pss->dire) - return 0; - - was = 0; - if (pss->first) { - p += lws_snprintf((char *)p, lws_ptr_diff(end, p), - "{\"max_size\":%llu, \"files\": [", - vhd->max_size); - was = 1; - } - - m = 5; - while (m-- && pss->dire) { - p += lws_snprintf((char *)p, lws_ptr_diff(end, p), - "%c{\"name\":\"%s\", " - "\"size\":%llu," - "\"mtime\":%llu," - "\"yours\":%d}", - pss->first ? ' ' : ',', - (const char *)&pss->dire[1], - pss->dire->size, - (unsigned long long)pss->dire->mtime, - !strcmp(pss->user, pss->dire->user) && - pss->user[0]); - pss->first = 0; - pss->dire = lp_to_dir_entry(pss->dire->next, next); - } - - if (!pss->dire) { - p += lws_snprintf((char *)p, lws_ptr_diff(end, p), - "]}"); - if (pss->lwsac_head) { - lwsac_unreference(&pss->lwsac_head); - pss->lwsac_head = NULL; - } - } - - n = lws_write(wsi, start, lws_ptr_diff(p, start), - lws_write_ws_flags(LWS_WRITE_TEXT, was, - !pss->dire)); - if (n < 0) { - lwsl_notice("%s: ws write failed\n", __func__); - return 1; - } - if (pss->dire) { - lws_callback_on_writable(wsi); - - return 0; - } - - /* ie, we finished */ - - if (pss->filelist_version != pss->vhd->filelist_version) { - lwsl_info("%s: restart send\n", __func__); - /* what we just sent is already out of date */ - start_sending_dir(pss); - lws_callback_on_writable(wsi); - } - - return 0; - - /* POST-related */ - - case LWS_CALLBACK_HTTP_BODY: - - /* create the POST argument parser if not already existing */ - if (!pss->spa) { - pss->vhd = vhd; - pss->wsi = wsi; - pss->spa = lws_spa_create(wsi, param_names, - LWS_ARRAY_SIZE(param_names), - 1024, file_upload_cb, pss); - if (!pss->spa) - return -1; - - pss->filename[0] = '\0'; - pss->file_length = 0; - /* catchall */ - pss->response_code = HTTP_STATUS_SERVICE_UNAVAILABLE; - - m = lws_hdr_copy(wsi, pss->user, sizeof(pss->user), - WSI_TOKEN_HTTP_AUTHORIZATION); - if (m > 0) - lwsl_info("basic auth user: %s\n", pss->user); - else - pss->user[0] = '\0'; - } - - /* let it parse the POST data */ - if (lws_spa_process(pss->spa, in, (int)len)) { - lwsl_notice("spa saw a problem\n"); - /* some problem happened */ - lws_spa_finalize(pss->spa); - - pss->completed = 1; - lws_callback_on_writable(wsi); - } - break; - - case LWS_CALLBACK_HTTP_BODY_COMPLETION: - /* call to inform no more payload data coming */ - lws_spa_finalize(pss->spa); - - pss->completed = 1; - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_HTTP_WRITEABLE: - if (!pss->completed) - break; - - p = (unsigned char *)pss->result + LWS_PRE; - start = p; - end = p + sizeof(pss->result) - LWS_PRE - 1; - - if (!pss->sent_headers) { - n = format_result(pss); - - if (lws_add_http_header_status(wsi, pss->response_code, - &p, end)) - goto bail; - - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_TYPE, - (unsigned char *)"text/html", 9, - &p, end)) - goto bail; - if (lws_add_http_header_content_length(wsi, n, &p, end)) - goto bail; - if (lws_finalize_http_header(wsi, &p, end)) - goto bail; - - /* first send the headers ... */ - n = lws_write(wsi, start, lws_ptr_diff(p, start), - LWS_WRITE_HTTP_HEADERS | - LWS_WRITE_H2_STREAM_END); - if (n < 0) - goto bail; - - pss->sent_headers = 1; - lws_callback_on_writable(wsi); - break; - } - - if (!pss->sent_body) { - n = format_result(pss); - n = lws_write(wsi, (unsigned char *)start, n, - LWS_WRITE_HTTP_FINAL); - - pss->sent_body = 1; - if (n < 0) { - lwsl_err("%s: writing body failed\n", __func__); - return 1; - } - goto try_to_reuse; - } - break; - - case LWS_CALLBACK_HTTP_DROP_PROTOCOL: - /* called when our wsi user_space is going to be destroyed */ - if (pss->spa) { - lws_spa_destroy(pss->spa); - pss->spa = NULL; - } - break; - - default: - break; - } - - return 0; - -bail: - - return 1; - -try_to_reuse: - if (lws_http_transaction_completed(wsi)) - return -1; - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_DEADDROP \ - { \ - "lws-deaddrop", \ - callback_deaddrop, \ - sizeof(struct pss_deaddrop), \ - 1024, \ - 0, NULL, 0 \ - } - -#if !defined (LWS_PLUGIN_STATIC) - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_DEADDROP -}; - -LWS_VISIBLE int -init_protocol_deaddrop(struct lws_context *context, - struct lws_plugin_capability *c) -{ - if (c->api_magic != LWS_PLUGIN_API_MAGIC) { - lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, - c->api_magic); - return 1; - } - - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_deaddrop(struct lws_context *context) -{ - return 0; -} - -#endif diff -Nru libwebsockets-4.0.20/plugins/deaddrop/README.md libwebsockets-2.4.2/plugins/deaddrop/README.md --- libwebsockets-4.0.20/plugins/deaddrop/README.md 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/plugins/deaddrop/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -# Deaddrop: File upload and sharing plugin - -## Building the plugin - -Just configure lws with `cmake .. -DLWS_WITH_PLUGINS=1` and build lws as normal. - -## Configurable settings - -|pvo name|value meaning| -|---|---| -|upload-dir|A writeable directory where uploaded files will go| -|max-size|Maximum individual file size in bytes| -|basic-auth|Path to basic auth credential file so wss can also be protected| - -## Required mounts - -To use deaddrop meaningfully, all the mounts and the ws protocol must be -protected by basic auth. And to use basic auth securely, the connection must -be protected from snooping by tls. - -1) Set the basic-auth pvo to require valid credentials as described above - -2) Protect your basic fileserving mount by the same basic auth file... this is - used to serve index.html, the css etc. - -3) Add a callback mount into "lws-deaddrop" protocol at "upload"... so if your - URL for deaddrop is "/tools/share", this would be at "/tools/share/upload". - It must also be protected by the basic auth file. - -4) Add a fileserving mount at the url "get" (continuing the example above, it - would be "/tools/share/get" whose origin matches the "upload-dir" pvo - value you selected. This mount needs any additional mimtype mappings since - it's where the uploaded files are shared from. - -## Using with C - -See ./minimal-examples/http-server/minimal-example-http-server-deaddrop for -how to use the plugin directly with C. - -## Using with lwsws / lejp-conf - -As a plugin, you can configure the mounts and pvos per-vhost easily in JSON. - -All the snippets here - -The mountpoints would look something like this (added to vhost/mounts) - -``` - { - "mountpoint": "/tools/share", - "origin": "file:///var/www/deaddrop", - "default": "index.html", - "basic-auth": "/var/www/ba" - }, { - "mountpoint": "/tools/share/upload", - "origin": "callback://lws-deaddrop", - "basic-auth": "/var/www/ba" - }, { - "mountpoint": "/tools/share/get", - "origin": "file:///var/cache/deaddrop-uploads", - "basic-auth": "/var/www/ba", - - "extra-mimetypes": { - ".bin": "application/octet-stream", - ".ttf": "application/x-font-truetype", - ".otf": "application/font-sfnt", - ".zip": "application/zip", - ".webm": "video/webm", - ".romfs": "application/octet-stream", - ".pdf": "application/pdf", - ".odt": "application/vnd.oasis.opendocument.text", - ".tgz": "application/x-gzip", - ".tar.gz": "application/x-gzip" - } - } -``` - -This enables the plugin on the vhost, configures the pvos, and makes -the wss serving also depend on having a valid basic auth credential. - -``` - "ws-protocols": [{ - "lws-deaddrop": { - "status": "ok", - "upload-dir": "/var/cache/deaddrop-uploads", - "max-size": "52428800", - "basic-auth": "/var/www/ba" - } - }], -``` - diff -Nru libwebsockets-4.0.20/plugins/generic-sessions/assets/index.html libwebsockets-2.4.2/plugins/generic-sessions/assets/index.html --- libwebsockets-4.0.20/plugins/generic-sessions/assets/index.html 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/plugins/generic-sessions/assets/index.html 2018-03-08 10:28:37.000000000 +0000 @@ -1,44 +1,52 @@ - - - - + + + - - - + +
- - - @@ -186,7 +255,7 @@ - +
-
+
+
- - - + - - - - - - + + + + +
- +
+ +
+ - +
- + + diff -Nru libwebsockets-4.0.20/plugins/generic-sessions/assets/lwsgs.css libwebsockets-2.4.2/plugins/generic-sessions/assets/lwsgs.css --- libwebsockets-4.0.20/plugins/generic-sessions/assets/lwsgs.css 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/plugins/generic-sessions/assets/lwsgs.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -.body { font-size: 12px } -.gstitle { font-size: 18px } - -.group1 { - vertical-align:middle; - text-align:center; - background:#f0f0e0; - padding:12px; - border-radius:10px; -} -.group2 { - display:none; - vertical-align:middle; - font-size: 22px; - text-align:center; - margin:auto; - align:center; - background-color: rgba(255, 255, 255, 0.8); - padding:12px; - border-radius:10px; -} - -body.seats { - background-image:url(seats.jpg) -} - -div.lwsgs { - z-index: 3; - text-align:right; - background-color: rgba(255, 255, 255, 0.8); -} - -table.lwsgs { - width:100%; - height:100%; - transition: max-height 2s; -} -table.c100 { - text-align:center; - width:100%; -} - -table.r { - vertical-align:top; - text-align:right; -} - -table.l { - vertical-align:top; - text-align:left; -} - -table.fixed { - table-layout: fixed; -} - -td.logo { - vertical-align:top; - text-align:left; - width:200px -} - -td.lwsgs { - vertical-align:top; - float:right; -} - -td.h99 { - height:99%; - vertical-align:middle; -} - -td.c { - margin:auto; - align:center -} - -td.tac { - text-align:center -} - -td.ava { - display:inline-block; - vertical-align:top; - word-wrap:break-word; -} - -iframe.hidden { - display:none; -} - -div.hidden { - display:none; -} - -div.hiddenr { - display:none; - text-align:right; -} - -input { - margin: 2px; - padding: 2px; -} - -input.em { - margin: 4px; - font-weight:bold; -} - -input.wide { - margin: 6px; - padding: 6px; -} - -input.hidden { - display: none; -} - -form.r { - text-align:right; -} - -span.bad { - color: red; -} - -span.small { - font-size:8pt; -} - -.green { - color: green; -} diff -Nru libwebsockets-4.0.20/plugins/generic-sessions/assets/lwsgs.js libwebsockets-2.4.2/plugins/generic-sessions/assets/lwsgs.js --- libwebsockets-4.0.20/plugins/generic-sessions/assets/lwsgs.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/plugins/generic-sessions/assets/lwsgs.js 2018-03-08 10:28:37.000000000 +0000 @@ -5,7 +5,7 @@ var lwsgs_email = "$lwsgs_email"; var lwsgs_html = '\ -
Websocket connection not initialized Open and close testing - Open and close testing
-To help with open and close testing, you can open and close a connection by -hand using the buttons.
+
+To help with open and close testing, you can open and close a connection by hand using + the buttons.
"Close" closes the connection from the browser with code 3000 and reason 'Bye!".
"Request Server Close" sends a message asking the server to initiate the close, which it does with code 1001 and reason "Seeya".
- - -
@@ -173,8 +243,7 @@
Websocket connection not initialized
- Server Info - +Server Info
@@ -209,28 +278,47 @@ This tests POST handling in lws. -
+
FORM 1: send with urlencoded POST body args
- Some text: + Some text:
+ -
+
FORM 2: send with multipart/form-data
(can handle file upload, test limited to 100KB)
-
- Some text: + + Some text:
-   -
+  
@@ -244,15 +332,523 @@
-Looking for support? -https://libwebsockets.org, - - https://github.com/warmcat/libwebsockets
-Join the mailing list: - - https://libwebsockets.org/mailman/listinfo/libwebsockets +Looking for support? https://libwebsockets.org, https://github.com/warmcat/libwebsockets
+Join the mailing list: https://libwebsockets.org/mailman/listinfo/libwebsockets + + diff -Nru libwebsockets-4.0.20/test-apps/test.js libwebsockets-2.4.2/test-apps/test.js --- libwebsockets-4.0.20/test-apps/test.js 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,543 +0,0 @@ -(function () { -function check_file() -{ - var f = document.getElementById("file").files[0]; - var max_len = 100000; - var dis = 0; - - if (f) { - if (f.size >= max_len) { - dis = 1; - document.getElementById("file_info").innerHTML = - "File larger than " + - max_len+""; - } else - document.getElementById("file_info").innerHTML = - "File length "+f.size; - } else - dis = 1; - - document.getElementById("upload").disabled = dis; -} - -/* - * We display untrusted stuff in html context... reject anything - * that has HTML stuff in it - */ - -function san(s) -{ - if (s.search("<") !== -1) - return "invalid string"; - - return s; -} - -/* BrowserDetect came from http://www.quirksmode.org/js/detect.html */ - -var BrowserDetect = { - init: function () { - this.browser = this.searchString(this.dataBrowser) || - "An unknown browser"; - this.version = this.searchVersion(navigator.userAgent) - || this.searchVersion(navigator.appVersion) - || "an unknown version"; - this.OS = this.searchString(this.dataOS) || "an unknown OS"; - }, - searchString: function (data) { - for (var i=0;iwebsocket connection opened
" + - san(socket_di.extensions); - }; - - socket_di.onmessage =function got_packet(msg) { - document.getElementById("number").textContent = msg.data + "\n"; - }; - - socket_di.onclose = function(){ - document.getElementById("wsdi_statustd").style.backgroundColor = - "#ff4040"; - document.getElementById("wsdi_status").textContent = - " websocket connection CLOSED "; - }; - } catch(exception) { - alert("

Error" + exception); - } -} - - var socket_status, jso, s; - -function ws_open_status() -{ - - socket_status = new_ws(get_appropriate_ws_url(""), "lws-status"); - - try { - socket_status.onopen = function() { - document.getElementById("s_statustd").style.backgroundColor = - "#40ff40"; - document.getElementById("s_status").innerHTML = - " websocket connection opened
" + - san(socket_status.extensions); - }; - - socket_status.onmessage =function got_packet(msg) { - var s; - - console.log(msg.data); - - jso = JSON.parse(msg.data); - - if (jso.wss_over_h2 === "1") - document.getElementById("wstransport").innerHTML = - ""; - - document.getElementById("servinfo").innerHTML = - "" + - "" + - "
Build info"+ - san(jso.version) + "
Server info" + - san(jso.hostname) + "
"; - s=""; - var n; - for (n = 0; n < jso.conns.length; n++) { - var d = new Date(parseInt(jso.conns[n].time, 10) * 1000); - - s = s + ""; - } - s = s + "
client " + (n + 1) + - "" + san(jso.conns[n].peer) + - "
" + san(d.toString()) + - "
" + san(jso.conns[n].ua) + - "
"; - - document.getElementById("conninfo").innerHTML = s; - }; - - socket_status.onclose = function(){ - document.getElementById("s_statustd").style.backgroundColor = - "#ff4040"; - document.getElementById("s_status").textContent = - " websocket connection CLOSED "; - }; - } catch(exception) { - alert("

Error" + exception); - } -} - -function reset() { - socket_di.send("reset\n"); -} - - -function junk() { - for(var word = ""; word.length < 9000; word += "a"){} - socket_di.send(word); -} - -function on_pmd() { - socket_status.send("{ \"RequestType\":\"DDoS\", \"blob\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAJbElEQVR4Xu2af4xUVxXHv+e9O0OhIEibgrRlF8rOG6CWKiumrUlTo6byj6mmNFta2DdQsRJJrP5j+0cnjSVp1BoSU1yzO28LEeL6h7GJNhpJNa2CBQ1gd9k3QPlRBU2LLV0W2p173zFvZvbtzOybmbd7J9DEd/+befece97nfe+95/4gxEWLAGlZx8aIAWqKIAYYA9QkoGkeKzAGqElA0zxWYAxQk4CmeazAGKAmAU3zWIExQE0CmuaxAmOAmgQ0zWMFxgA1CWiaxwqMAWoS0DRvrQJ3ujeb17Hrx6SuzLwV32x7VzO+1pj3HJprJue8BcBQCSONRzr+2RrHaO2OtHDc1wDcUwqOj0s7nWpVoDp+hOMeA5Au+zggbesuHX+Vti1VoOh3j4BxRxEf8xmVSbe3KlAdP2bOPUmEpaXviiGZsVbq+IsBxgCnpp9YgVPjNan2RwagmXMfUpnUAEAc9k6Rx8AHB0zzy3euU5nUXk02RXMzl+9Ssw8PYN06FeYvOkAmM5dfpzLWL6LGFXkSMR13jIAEmKXMpBM6AIXjeih+BVLqTEcSWfJ/T71ks4bZ9vAYgU1/epC2ZegAFLnhAogEM0tV5x1r/UcGKBw3UJ1sTyVwH8laZ5EU2DO4WCTFmXFbOTbnemxZdHnq9AAMvDVTjF4ObKVS7di8IvA97jOSArNsiLZ8oGBpW5HYRKrkB1IFcGwkiS2dhWkB7B1qE6Z5uiUAew7NEsk5o4EveEtgLw98TwngwIApRlcFoogBVnzdSArUAWg6+T4QS3U69Xi98eiqK7A4vnX1+YsldWbPJmSzk8fJa6FAP67FXTv9sVLZqU1k9rvHibGsmKQzBlTGeih0gqgcA69CFxaO+zKA+4uxEF6W3dbaSXFdA4Bmzt1DhK5SLHycTMd9n4A5pT9oSNqp0GXO1VagcFx/cphZhnZF2tasjwJAkRs+CqJPFvEBIzHA8a8ScQyMAdbbTGgJwAY7FS3LA3edXCyUbJoHitzwKIhK3Zb5ssykr5/Uha9BHigc9whQ3nHyu7DIDe8A0TY/OI/xtJexngnP5ocLRCRAkLLbas1KhOGps6lE2MxvOO6TBvBsMS7Pe8rbtHz7pLhavRLpdwtgCDCkzIS/o9Gff8pg/n55EtlRSqT7h+8B0xhs62AYvHJlf524QZ3duzs0pfCT7aj7gQNsmqPHH1V2qr9+ewBePHYXIICNHfsb1TNzw7Y6e343svdNWh35dpHyQL9iKUV5VGVSu+qt94tx7DrZCa8wA93pP0deiTR80fLDyACjOGthncgAp9FmqwG+CsbnynHkpW1Z04ip5Sai3x0CY3kpU+P9sjt9d6saaSlA9JybJWaMXARDyRFjLrZ1fNiqQLX8DAwmxai4CCAhzblzsWFhsH7W8lvM8eOiRSAGqIUvVqAmvhhgDFCbgKaDqY+B2ayBBV2fQJJnwzQlSL6HjcsvhMaRHUwG/2dXjjWMtXdwPjBjPryCARKXIcQ7sJd8EOn9fFuTbkCBCDOMUZw69596SXWVv4HB2bicXAClBARdwqm95+stEurFER1gz6GESM45DGBFmLNJW+C5E7cKUmfH68pkoh3rl046r8CL7s3CQ+hdFQY2KNvaXS94o8/\" }"); - socket_status.send("{ \"RequestType\":\"SendImage\", \"RequestID\":\"283463389\", \"toType\":\"toUser\", \"toID\":\"1036\", \"fileType\":\"image/jpeg\", \"blob\":\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAGqAoADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArlviX43Pw78H3fipdHl1RreSGJLSKQRtK0kqxgBiDjlvSuprhvjNpmoav4INnpljPdznU9Ok8qCMu2xbuJmbA5wFBJPYA0AL4r+K2j+G/hxH8Rra0k1G3ureOezto3CPOXXcFBwcHbk9O1bmg+L9H12wW7juEimSygvrmAtlreOVCy7jjngN+VeOal4L8Vz3Wv8Ag99DvG0Hw5b317pEixMUupLpS0UUf94w5ZMD1FWdLfW/CFxqcF34S126l1vwxpttZfZdPllU3EUMitFIyqREcuOXIHXninYD0y8+KPgCwNkt34ntU/tCGO4tyA7BopPuOSBhQc8bsVYXxtpEc+tHULm2tbPRVheW5afIKyAkEjHy9OOTnNeEHQNX0bw/pTQaH4l0/W38NabbNCNFkvbS/kjgUeTMqrmF1bKncVx1rZ1Lwz4tuV1bVL3wpdSJDf8Ah+/urCGBmFzFAxaeOIf8tNv90ZzgDvRYD2fw34w8NeLoppvDurRXqwNslChlZD1GVYAj8q0rq9tLIRtd3CRCVxGhc4BY9Bn3rlfBl/Z61reqazp/g660q3kjhiW8u7SS0muyoOR5UiqwVc4DEc89q2fFQSTRprVtFk1VrjESWyjhmPQs38AHXdkYx60gNivPb74uwWXhPW/EX/CPzyXmjal/ZQ05Zh5k85ZFjCtjjcZFxxXXeGdO1LSdDtNP1bUPtt1CmJJvXngZ74HGcDOM4FeYXfhPXm+Nq28elXDeG7yaHXbi68s+SLmGORRGW6bi7RNjr8hoA6XU/i3p1pYeHr+w043a+INPk1NMziMQwIiNuY7T/fA/OtS8+J3gnSntLbWtftbO6u4IpxESzhA4BG5gMKPc4ryPSvBHipIvGdheaDei20HT7jRtB/ct/pMLzSSK0Yx8w8t4k47oaj8XaLqVglzJpmjeIbfV7rRLSAWw0WS/sdUdIiBFJsX9yQSVJZhgHPanYD1B/jD4PsfEWseHNdvo9Nm0q7itUeRiwn8yGOQN8q/IP3m3k9R1ra8ceLoPBfgzVfGTWpvYdMs3vPKSQL5qqucBsHGfXFebDwpq15YfE681LwvIuoaotuLdRbl/NK2EAKxHHzgSBxx3Bre+IukavqHwA1TRbPTbq41GXw75C2scTNM0vkAbAgGS2eMYzmgDt9c1xNF0CfXXiRlhjWQo8mwckDlsHHX0rMf4leCYNUg0O78QW0OoT+WogO4gO+Nql8bQTkYBIrjfFvjJfFvgjUfDeleE/Fq3s1qgQXHh68gQlXTI3PGBnr3rjdc0rWtM1K8Ph3QdeXVbq6tJW0250iS50/UGUIPN89V2wEYOSzfKVziiwHsPhL4k+GfGer6zoujXLvcaLcfZ5gyMA3yg7gSOmTj8K6qvPfh9aT6V418b2d3ot5bG81NL22uDZusE0Jt4l+WXGwkMrZUHI9K9CpAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUVma34m8OeGvsf/CQ67YaZ/aNyllafa7hIvtFw/3Yk3EbnPZRyaANOiisbQfGfhHxTc39l4a8T6XqtxpUohvorO7jme1kOcLIFJKHg8HHQ0AbNFZ2u+ItA8MWa6j4j1qx0u1eVIFmvLhYUMjnCoGYgbieg6mi08R+H7/VrnQbLW7G41KzjSa4s47hWmhjb7rOgOVB7EjmgDRoorO1/wAQ6D4V0uXXPE2tWOladAVEt3ezrDChZgq5diAMkgDnqRQBo0UgORkUtABRVbUtT07RrCfVNWvreys7VDJPcTyCOONB1ZmPAHuaNO1LT9XsYNT0q9gvLO5QSQzwSB45EPRlYcEe4oAs0VT1fWNJ8P6Zcazrup2un2FonmT3V1KsUUS+rMxAA9zU9tc297bRXlnPHPBOiyxSxsGV0YZDAjggggg0AS0UUmRnGeaAFooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooqrqGpWumQ+dcsQCcKBySaaTk7ITairstUVz3/AAmdh/z63H/jv+NH/CZ2H/Prcf8Ajv8AjW31ar/KZfWKfc6Giue/4TOw/wCfW4/8d/xo/wCEzsP+fW4/8d/xo+rVf5Q+sU+50NFc9/wmdh/z63H/AI7/AI0f8JnYf8+tx/47/jR9Wq/yh9Yp9zoaK57/AITOw/59bj/x3/GtDTNcstVLJBuWRRkow5xUyoVIK8kVGtCTsmaNFFFZGgUUUUAFFFFABXin7YfhS78TfAbxBeaUCup+HVj16ykUZaOS1cSkr7lVYfjXtdVtRsLXVLC50y+hWW3u4nhljboyMMEH8DQBxNz8UNOb4IH4tQyrHaz6AurREH7peEMg/wC+mAr52/Zu8OXnwh+KfhZtT3xn4seHLi9u1JO3+0Y5DcA/UxMwA9qxNG1PUX+COl/s0vcNJrFt46m8ITg9Xs7aYzE/TyvK9sGvaf2ptPTwl4J8LfEvS4WB+HGuWGolYx8xsg4imT6eU7E/SgBn7Qif8Jp8Ufhb8KU/eQTanJ4h1KP0gtgBG3/fbH8q6zw7rXw8j+M/jO3stIltPENlpdrNq2oSSnypbfLbAATgbcEk4rmPh40fj39pfx146jdZ7DwtYWnhrT5lOV8woJ7gA/70gH4Vm6DDpFx+0b8XLfX7mO30yXwzZpeTSSCNY4T5gdix4UAZ57UAbEH7S2r67YyeJfAvwY8TeIfDCFzHq0UkcX2iNTgyRRN8zrwcHIzXK/tQ+P8Aw78Sf2QNf8X+Grh3sriWzVllXZJDIt5EHjcdmB6irvg3wv8AtE/BnwxZ6B4BPhj4ieEbGEDSkkujZXv2Xqi7yDG+FxghuRiuE+Mvivwf4x/Y78djw34Tl8MXVtq8EOs6TKuJLe+a7hMhPJB3cEEcGgD2LU/2htUjt5tY8J/CHxL4j8O2u7fq9u0cccqL96SFG+aROCQeMjpXo/gHx34f+JPhSx8Y+GLhpbC/Qsu9drowOGRh2YHgitXTNNsdM0q10mxtkitLaBIIoVGFWNVwFA9McV4l+xt8vw28QQrwkPjDV0Reyr5q8D86AO2/aF/5Ij40/wCwRN/KpPgF/wAkX8G/9giD/wBBqP8AaF/5Ij40/wCwRN/KpPgF/wAkX8G/9giD/wBBoAn+Nt74T074UeJb7xzpcupaDDZFr+0ico8se5flBBBHOO9Y3iH4w+Gfh/4c8Jabo3h/UtV1HxDZxLoWh2QDTyRLCG5ZjhVVcZY1D+1X/wAm8eOv+wWf/Q1rktd+Hep+Lrb4aeLPh5440vRvHnhbQIns7W+Hmw3NrNbxrKskaneFPHzAH9aAOn8P/H+ZvF2neC/iJ8OtY8GX2tFk0uW6lSe3unUZKCRcbWx2IryTxZ8WPF1p+1VodxD8JvFM/wBj0TULaOxjeLddruT9+g3Y2jHfnmuq174jeNPDus+HbL9o/wCEumHSm1SKHT/EWk3v2m3t71uI2eMhZI89Bwa1vEP/ACeL4Q/7FbUv/Qo6APcrOeS6tIbmW3e3eWNXaKT70ZIztOO46VNRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXKeNSfNtRnjax/UV1dcp41/wBda/7rfzFdOD/jI58V/CZzVeear8Q9V0r4tWHg6e3g/sa8tlDT4PmR3D58vJ6YYgL9WFeh15/4t+HOo+IdY1fVrW/t4JLnTYobB23boLuKQSRyHA+6GVTxzxX0eF9lzNVdrf0/keFiPacqdPe5Qb4sXMPxG1fRbiGIeH9Js2Zp1UmWSdCBJjnGFZtv1U11Ws/ELw1oKeZqc8sajS5NX3bMj7OhQHv97Mi8Vx8fwf1MWcEMmq2rTto89reTYbMt7LIZXlAx90uSfXmn/wDCAePdSujfaxceHo3h8PzaNDAvmTxu7PEdz7lX5SI8EDkZ4zXVKGFk009Fo/Pz+ZzxniIp3Wr/AK/A2n+JNneQwG3t77S5jfQWzx39n8zrIpK7drYwcdcnGOlWrP4m+H77V49LtrXUmimuTaRX/wBm/wBFeYdUD5znIxnGM965DTvhN4lW4M00+n6fafbbW5XT4Lya4iTyg+91aRQQW3gbeny1veGPCXjXw+bTQFvdK/sKyvJLlZlLm6ljZy4jKFdo+ZuWDHipqU8Mk+V/j/V/QcJ4htcyO/rV8LkjWoAD1D5/75NZVavhf/kNwfR//QTXk1v4cvRnp0f4kfU7uiiivBPZCiiigAooooAKKKKAPEbP9mu3tf2kLv47HX1ayngLx6P5JxFetFHG9wGzjJEYzxnnrXqHjzwlZePfBWueC9RIW31uwnsJGIztEiFdw9xnNb1FAHl/7O/wZl+B/wAPh4Tv9eGt6ncXk99f6j5Rj+0SyOSCQSTwu1eT2p0HwVtp/iH428Wa3qEV7pfjPSItIn0/yirLGoYPl88hgxHAFenUUAeC+G/hh+0R8OdFh8D+CPiV4fvvD9kgttNm1nS3lvLK3UYSPcjqsm0YA3L0Apus/suyan8E/EXw0HjBpNc8VX8Wp6prdzb7vNuFmRziNSMLhNoGeM175RQAijaoX0GK8/8Agp8L7j4TeG9T0G51iPUm1DW73VhIkJjCLOwITBJyRjr3r0GigDm/iP4Tl8d+BNc8HQ3q2b6vZvarOybxGW7lQRn868h8L/CT9qDwj4e0/wAM6R8dPC62WmQLbwB/C+5gijjJMnNfQVFAHhmsfCL46eNfBviXwX8QPi1oWo2euaebSE2ugfZzBIWB3kh8sMDGOK0PGHwM128bwh4o8B+MF0Pxh4Q09NNivHtvNtr238tUkimiJ5VtoI5yp6GvY6KAPDL/AOEXxa+Juo6Snxj8ZaIdB0i9i1AaVolg8Qu54jmNpZJHZtoPO1cVr/FL4ReMvEPxB8PfFD4c+MLLRNa0O1uLGSO+sftMFzBNjIIDKQRtGCDXrlFAENmt0lpCl9KklwsaiV0XarPjkgdhntU1FFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFZ+r6RDq0IR2KOnKOO1aFFVGTg+aO4pRUlZnKf8IVN/wBBBP8Av2f8aP8AhCpv+ggn/fs/411dFb/XK3cw+q0uxyn/AAhU3/QQT/v2f8aP+EKm/wCggn/fs/411dFH1yt3D6rS7HKf8IVN/wBBBP8Av2f8aP8AhCpv+ggn/fs/411dFH1yt3D6rS7HKf8ACFTf9BBP+/Z/xrT0bw7FpUhuHl82XGAcYArYoqZ4mrNcrZUcPTg7pBRRRWBsFFFFABRRRQAVFcPcRqDbwLK2eQX24H5GpaKAKX2jVP8AoGx/+BH/ANjR9o1T/oGx/wDgR/8AY1dooApfaNU/6Bsf/gR/9jR9o1T/AKBsf/gR/wDY1dooApfaNU/6Bsf/AIEf/Y0faNU/6Bsf/gR/9jV2igCl9o1T/oGx/wDgR/8AY0faNU/6Bsf/AIEf/Y1dooApfaNU/wCgbH/4Ef8A2NH2jVP+gbH/AOBH/wBjV2igCl9o1T/oGx/+BH/2NH2jVP8AoGx/+BH/ANjV2igCl9o1T/oGx/8AgR/9jR9o1T/oGx/+BH/2NXaKAKX2jVP+gbH/AOBH/wBjR9o1T/oGx/8AgR/9jV2igCl9o1T/AKBsf/gR/wDY0faNU/6Bsf8A4Ef/AGNXaKAKX2jVP+gbH/4Ef/Y0faNU/wCgbH/4Ef8A2NXaKAKX2jVP+gbH/wCBH/2NH2jVP+gbH/4Ef/Y1dooApfaNU/6Bsf8A4Ef/AGNH2jVP+gbH/wCBH/2NXaKAKX2jVP8AoGx/+BH/ANjR9o1T/oGx/wDgR/8AY1dooApfaNU/6Bsf/gR/9jR9o1T/AKBsf/gR/wDY1dooApfaNU/6Bsf/AIEf/Y0faNU/6Bsf/gR/9jV2igCl9o1T/oGx/wDgR/8AY0faNU/6Bsf/AIEf/Y1dooApfaNU/wCgbH/4Ef8A2NH2jVP+gbH/AOBH/wBjV2igCl9o1T/oGx/+BH/2NH2jVP8AoGx/+BH/ANjV2igCl9o1T/oGx/8AgR/9jR9o1T/oGx/+BH/2NXaKAKX2jVP+gbH/AOBH/wBjR9o1T/oGx/8AgR/9jV2igCl9o1T/AKBsf/gR/wDY0faNU/6Bsf8A4Ef/AGNXaKAKX2jVP+gbH/4Ef/Y0faNU/wCgbH/4Ef8A2NXaKAKX2jVP+gbH/wCBH/2NH2jVP+gbH/4Ef/Y1dooApfaNU/6Bsf8A4Ef/AGNH2jVP+gbH/wCBH/2NXaKAKX2jVP8AoGx/+BH/ANjR9o1T/oGx/wDgR/8AY1dooApfaNU/6Bsf/gR/9jR9o1T/AKBsf/gR/wDY1dooApfaNU/6Bsf/AIEf/Y0faNU/6Bsf/gR/9jV2igCl9o1T/oGx/wDgR/8AY0faNU/6Bsf/AIEf/Y1dooApfaNU/wCgbH/4Ef8A2NH2jVP+gbH/AOBH/wBjV2igCl9o1T/oGx/+BH/2NH2jVP8AoGx/+BH/ANjV2igCl9o1T/oGx/8AgR/9jR9o1T/oGx/+BH/2NXaKAKX2jVP+gbH/AOBH/wBjR9o1T/oGx/8AgR/9jV2igCl9o1T/AKBsf/gR/wDY0faNU/6Bsf8A4Ef/AGNXaKAKX2jVP+gbH/4Ef/Y0faNU/wCgbH/4Ef8A2NXaKAKX2jVP+gbH/wCBH/2NH2jVP+gbH/4Ef/Y1dooApfaNU/6Bsf8A4Ef/AGNOSfUWdRJp8aqSMsJ84HrjFW6KACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACioZry0t/8AX3Mcf+8wFUZvEekxcfaN5/2VJosBqUVz03jC3GRBau3+8QKozeLb9/8AVRRxj86fKwOvorg5tc1Sb7124HoOKjtdUvba4WcXDtgjILZBFPlA9AoooqQCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACori5gtYzLcSqijualrkPFsztfpAWO1EBA9zTSuBqzeK9NjyIxJIfYYFUpvGMh/497NR/vtn+Vc3RV8qA1pvE+rS/dlWMf7Kj+tUZtRv7j/XXcrD0LnFV6KdkAUUUUAFFFFABQOtFA60AelDpS0g6UtZAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFcb4r/5Cv/bJf612Vcb4r/5Cv/bJf61UdwMaiiirAKKKVVZztVSSewFACUVch0fU5/8AV2UuD3K4H61eh8KanJ/rDFF/vNn+VF0Bi0V08Pg5BzPeE+yrV2HwxpUX3o3kP+01LmQHF0DrXb3ui6b9ilCWqIVQkEdQRXEDrQncD0odKWkHSlrMAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArjfFf8AyFf+2S/1rsq43xX/AMhX/tkv9aqO4FfSNGl1UuVkEaJwWIzzW7D4RsU5lmlkPpnApvg//jzn/wCuv9BW/Q27gZ8Og6TD92zRv9/5v51djhihG2KJUHooxT6KkAooooAKKKKAIbz/AI9Jv+ubfyrzoda9FvP+PSb/AK5t/KvOh1q4gelDpS0g6UtQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAVxviv8A5Cv/AGyX+tdlXG+K/wDkK/8AbJf61UdwNPwf/wAec3/XX+grfrA8H/8AHnN/11/oK36T3AKKKKQBRRRQAUUUUAQ3n/HpN/1zb+VedDrXot5/x6Tf9c2/lXnQ61cQPSh0paQdKWoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK43xX/yFf+2S/wBa7KuN8V/8hX/tkv8AWqjuBp+D/wDjzm/66/0Fb9YHg/8A485v+uv9BW/Se4BRRRSAKKKKACiiigCG8/49Jv8Arm38q86HWvRbz/j0m/65t/KvOh1q4gelDpS0g6UtQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAVxviv/AJCv/bJf612Vcb4r/wCQr/2yX+tVHcDT8H/8ec3/AF1/oK36wPB//HnN/wBdf6Ct+k9wCiiikAUUVDc3dtZp5lzMsY7ZPX6UATUViy+K9MQ4QSye4XH86j/4S+w/59p/yH+NOzA2Lw4tJs/882/lXnQ610GqeKBdW7W9pCyBxhmbGce1c+OtXFWA9KHSlpB0pazAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiioLy9t7GEz3MgVR09SfQUAT0Vy9z4vkJItLYAdi55/Kqh8Vaqe8Q+iVXKwOzri/FDq+qttOdqKp+vNNfxNqrqV81Fz3C81mO7yOXdizMcknvTirAdV4P/AOPOb/rr/QVv1geD/wDjzm/66/0Fb9S9wCiiikBXv7tLG1kuX/gHA9TXBXd3PeztPO5Zj09h6Cuq8WFhpqgdDIM/rXH1cUAUUUVQBQOtFS2sTT3MUKjJdwv5mgD0UdKWkpayAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACuH8QX73l+67j5cR2qP5mu4rzzUI2ivZ43HIc/zqo7gV6KKKsAooooA6zweP8AQpj/ANNf6Ct+snwzbNb6WjMMGUl/w7fpWtWb3AKKKKQFXU7IX9lJbHgsMqfQ1wU8EttK0MyFWU4INej1UvtLstQXFzECezDgiqTsB5/RXUyeDoScxXjKPQrmmr4OT+K+P4J/9eq5kBzFdH4Y0h/MGo3CFQv+rB7n1rSs/DWnWrB2VpWHQv0/KtUAAYAwBUuXYBaKKKkAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK5/xFocl03220XMgHzr/e966CimnYDzZ0eNtsiFSOxGKbXok9laXP8Ar7dH9yOar/2FpH/PjH+tVzAcGAScAZNbOjeH57yRZrpCkAOcHgt7V1EOm2Fucw2kan6Zq1ScgEVQoCqAABgAUtFFSAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUV4145vPGfw28Sv8A8InG2oW3jeZbK0jnmythqTD5ZOTnyioJIHdR60Aey0V4pot5490ix1zSvB+pWctl4LPk3UupK8s+qXIjE07bs/uwS5A68+grpPCfxLv/ABPLr08dvFFb2Oj2WoWyEfMJJUlLqx7gGMYp2A9HorzjVPEWp678BR4qlYR3+oaBDev5JKASvGrHb3AyaxtB8X/ELw7NpFh4ludIvYNT0CW9tgmYPIlhiVgskrHDKRnLHGDSA9gorwvw/wDFbxvqGrSaLDrOj61PdaLcajbS2dlIkEU0WCYlkJ2zAhsblP4Vrat8a7qy0hPENnZxT2lj4dl1fUI8c/aMhI4Qf4cvv59ENOwHr1FeA/8AC8fFNlb38S3mn6zP/Z5u4JYbCa3jt5lkRTE+8fMCH4bg8Hiuz8P+IfHL+K7fwp4tu9Omi1zRZdRtZLOFo2tmVo1ZGyfm4k4PHTpRYD0uivJNOtPG2hfFTw/4WTxTHcaRZ+H4xLHLAS8/l/Izls/fLDOfTitz4oeLdY8OzaXZaVruk6QL1pDJcXiGeVto4SKBSGkJ7kdAKQHf0V4x4c+KXjbxtpnhvStE+wWWs6oL57u7uLd/KjjtZjESsJw25yM7SRt71l6B4v8AH2malq3h1prKTxFrPij+zop3LNbQItuZXkCHnGxGwvHJ/GnYD3uivAL3x54w+HviHxjdeKLy0vbyKPTbW0liV1t2MgbEhi5K4AOQM5IqxafG7xKTceH7aSy1fU7uezttNv1s5bW3Ek8yxESK4z8m4NwTkA9KLAe70V5R4afxxa/GWXTPF2qWl6q6BHJDJaI0UbgzSZLRkkBgQRnJyAPpTvin4+8SeHNXaw0bX9H08Q2LXSwy27Xl1dOOiiGM7lX/AG8HrSA9Vorxaw+I/wAR/GCyT+Gm0nToYfDljrjG5haUl5oi5iGCOOOvb0Naum/Fy9tlj1TxNBBBpt14ck1iFkHPmwf69M98g7gPRTTsB6pRXC6v4n8W6D8JD4q1C2tR4gisIZ5odpESzuVDJjrgFiPwrmbfxD8Y7vxPH4STVPD8ct5pX9rrdm1ci3UMqmLZn5yTIvzZHAPFKwHsFFeY/Dn4ma14vv8AQre/treFdR8PR6lOsYJxP5jo20/3fl4FcrffGDxsdO0y/mu7HQ7C5F75uqz6dLcWwlimKRxPs4iBUElmOOKdgPeKK8XvPibfaZfT+IpYbG6n/wCER0+8X7LcmSCS4muJUARgdpTcQQwGSK7fwkfiZDqYj8Xy6Ve2Fxa+cs9mhia3myP3RUklxgn5h6dOaQHY0V5D4m8S+L/DfxV1e9l1iCXQ9P8ADceoiwEJ3N+9lUDdn7xZfvemB2q5pPjD4gaXqXhe68XSaXcab4skEEcVpEySWUjxNJGCxP7wELtJ4wTTsB6lRXI+J/Fl74Z8WaNb3nlDRNThuI5JSvzx3KAMoz6Mu7j/AGTXIaV48+IXi2+0/QdFfTdNury1uNVkubmAyLFarKI4kCAjczbgxORgUgPXaK8s8JfE7xBrHiPRvDWqWtrFdedq1lqZiyVaa0dEDxk9FbcTg+uO1ZsnxM+IGq67beHPD0GmLcXusahp6zXCNshighVw5xyTyeO5wOOtFgPZaK5Pxz4i1jwnpWl6rH5EsS39vBqRKHHkvlSy+nzlfzrhrD4weJNTn1TToLS1S6m1mytNGJUkSWssxjkkbnkoEkY49BRYD2WivBrn4i+KtFspTYPDpmnyeINcgutVlspryK3MV46xq6qcoGAPzEhRjirEGv8AjrxV408MTeHNf0dLi98OXslxcxMbmzIW4hAkRAQGJ4HJGNx9KdgPcaK4zwL4n1TxX8Pm1jV44Y9Qj+2WlwYMhDLBI8TMueQCUJH1rzPRfFvibS9J0a40XTpNX1NPCmrXkELO7PNLHdIFXGfm6+hPGBRYD3+ivALz4i+Mte8F+JU0zxto095YW8E4kjs3trq3LPh45LdzvXthz15GK9p8J/2ofDOmNrV3Hc3rWsbTTRpsV2IzkDtxilYDWooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACsPxJ4TsvE11ot3eXM8TaJqCajCI8Yd1UgK2QePm7YNblFAHCa58K4tU1PVb7TPFWraNBrwX+1bWz8ry7ohAhbLoWRigCkoVJAqG/+D2nyXckuieI9U0W1u9Oi0u9tbPy9lxbxghRllLI2GYblIPJ5r0Gii4HNxeBtOh8AwfD1bu5Nlb6dFpqzEr5pjRAoY8Y3YX0xWf4l+FeheKrS1sdSu7wQ2ulXGkBY2Ub4povLYk4zux0x3rtKKAOD0b4XXGneItL8T3/AI21fUrvSraSzjWaOBI2gfb8hVI1GcqDuHzH1xTtK+D3hTTNL8R6M/2i6tPE25blJnH7uMhv3cZABVQXcjuCa7qigDgH+FDahY3Nl4h8ba5qwmtltImnMKmGMOGyAiAMx2jLMCfeuh/4RCxHiTTPE32mf7Rpeny6dHHxsZHZCWbjOf3Y6HHJreooA57UvB0V94v07xjDq13aXNhA1q8MQjMdzESTtfcpI5OcqQfeqninwE+v6/YeJtN8SX+jajYW8toJbaOGTfDIVLKVlRgOVUggZ4rrKKAPNrD4KWekWNlDo/i3WLS90y7u7iyvx5TzRpcPvlibehEiljn5wT70sHwT0u3spwPE2sNqkuqLrMeqM0Znhugu3co27SCuQVIIwSMYr0iincDzmP4L6dM2s3eteJtX1PUNb+zvLeSmNHhkh/1bxBFCoR0wBj25NWbj4Tw6rp93B4h8Waxqd7cNBJBfu0cUlo8L743iWNVRWDAEnbzjnIrvaKVwOM8OfDh9G8UzeMtU8WaprWqT2a2LPdLEiCNWLAKkaKq8seg569aj174YprHiG+16z8U6ppn9rWkdlqEFsIitxEm7ADMheM4ZhlCp5rt6KAOH8IfCrTfCNlNaRazf3hm0mDRy84jBEMKFEI2qBu2nGfauf8UfDf8AtF/Bvgmz028l07RLtbq41GRlCmBQ2+FsY3eYG2kYxgk9q9YoouBleJ/D1r4q0G78P3k0sMF2qqzxY3ABg3GQR2qtb+D7G28SxeJ0uZzcQ6YdKEZxsMZdG3dM7sxjvjk1vUUAedW/wZtNLg0ZNA8Wavpk+kWTad9oiELNcW5YttcMhAOScMoBGetGn/B86BYWdn4Y8c65pr2scsLyfuZ/PSR953pKjLuB6MAD716LRQB55ZfBDwjZae2lJLePato0OjGNnH3I5XlWXIGQ+9yfTgcVr+GPAb6DqY1jU/E+p65dxWpsrd7wRqIISQSFWNVBJ2rljknHWusooA5TXvh7p+v+JD4huNRuoxNpp0q7tFCGK5t9zsA25SwIMjcqQaz9D+FUWl6npV7qXirVtYt9B3f2VaXfleXakqUDZRAzkKxUFyxANd3RQBwHxh0C/wDF+jWXhWw0q4ma8u45GvEYKlmqn5mY5zkqWUAepq/rXw5t7y70zVNA12+0HUdKtDYRXNmsbl7chcxukisjDKqRkZBHFdhRQB55/wAKc0+0tNK/sTxJqunanpc9zc/2mhjknuJLhg07SiRWVtzDPTjtirHhj4R6P4Y1Cy1ODWNSu57K7u70NcOrGSS4jCPuwo44yMfy4ru6KLgZfifw/Z+KtAvvD2oPIlvfRGJ2jIDLzkMM9wQCPpXM6V8IfDuk614c1uC7vGm8N2ktrAjsu2YuCDJIMcuNzYIx9413VFAHBn4VfY2a48PeMNY0m6a+vr1pYhFIr/apmldGjkRkIDMdpIyB3qfwl8K9E8IajZ6rY3t3LPa2dzaMZSuJTPKkjyMABhtyDgYGCeK7WigDC8N+EbHwzoM3h+0uZ5YZp7qcvJjcDPK8jDgAYBcgewFc6vwc0NbW1tU1fVI/sel3WlRSRSiOQJPKspkDKAQ6soxjj1Brv6KAPPf+FQwX76jdeJvFWp6xe39gNNW5ljgiaGAMWG0RIqk7jnLA10Wg+F73RZ7SWfxVqmoJa2IsvJn8tY5CGBEpVFA3gDbxgYzxmugooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Z\"}"); - socket_status.send("{ \"RequestType\":\"SendImage\", \"RequestID\":\"788346414\", \"toType\":\"toUser\", \"toID\":\"1036\", \"fileType\":\"image/jpeg\", \"blob\":\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAHgAoADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAopNy/wB4fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/AHh+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv8AeH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/wB4fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/AHh+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv8AeH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/wB4fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/AHh+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv8AeH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/wB4fnRuX+8PzoAWik3L/eH50bl/vD86AFopNy/3h+dG5f7w/OgBaKTcv94fnRuX+8PzoAxst6mky3940UmRQA7J9TRk+ppKKAAFh/EaCWP8RoooAXJ9TRk+ppKKADL/AN40Zf8AvGkwKMCgA3N/eNG5v7xoI70Ad6AF3Mf4iPxpMsP4j+dHSlxmgBCWH8R/OlBb+8fzpCM96OgoAVWb+8fzoLN6n86OBTSc0AOy3940Zb+8aTg0YFACkv8A3jRlv7xpNvvSYx3oAXLn+I0Ev/eNAwOc0YBNABubHU/nSbm/vH86VaMc5oATc394/nTt3+0aTb70baAAl8/eP50uW/vGk20baADc3940B27k0babQA/Lf3jRlv7xpMAdTRgHpQA4lj/EaTLf3jTSMUvFAC5b+8aMt/eNJjHNGAelAC5b+8aMt/eNJxQSD3oAXLf3jRlv7xpNvvRjHegBSW/vGgFv7xpuB60YHrQA7Lf3jRlv7xpAQO9IMDvQA7Lf3jRlv7xpuM96XbQAuW/vGjLf3jSfLR8tAC5b+8aAW/vGkAFJgetADst/eNGW/vGk2+9IAPWgB5LH+I0mW/vGmkYpeKAFy3940Zb+8aTGOaMA9KAFy3940Zb+8aTigkHvQAuW/vGjLf3jSbfejGO9ACkt/eNALf3jTcD1owPWgB2Wz940mWzjcaP4aMDFAC5b+8aPm/vGmjA70pGaAF+b+8aPm/vGm4HrRgetADst/eNALf3jTcD1pQPegBQX/vGjLf3jSAClOO9AAS2fvGgFs/eNJgYzRgYzQApLf3jQS3940hA9aQjHegB2W/vGjLf3jTcD1owPWgB1FFFABRRRQAUUUUAGc0UnzUtACZFGRRk+lGT6UALSZx2paMigApMr1paTJ9KAA57UDPelpPmoAMCjAoyKMigA+Wj5aMn0oyfSgBcYowKKKAEwKCAO1Lz6UhJoAMg0ZApfwo/CgBMCjApAT0FOoATAowKWigBMCjgUZPpRk+lAAeDmlpDycUtACfNSgGkzntRz9KADn1oOR3oyBQSD3oAXoKRTnqaXp1ozmgAooooAKKT5qPmoAXPGaQEmlBzScCgAwfWjB9aMijIoADntQM96BnvQc9qAFpP4qXPOKOM0AITilHNHtRQAnzUoBpM57Uc/SgA59aDkd6MgUEg96AF6CkU56ml6daM5oAKKKKACik+aj5qADqOKFpQc0ZFACEZpQMUgPrS0AIc9qBnvQc9qBnvQAtJto3UtACE54FKOKAc0ZxQAUUm72o3e1AAc9qTBFKCaDntQAtFITigHNACg5oJxQBigjNAATQPeikyKAFoozijOaACg57UUUAJgUv4Ugz3paAEwKMCl59KOfSgAooooATgUGl59KKADgUmRS0mT6UALRwKKTJ9KADg0vAoOR2pMn0oACfalyKTJ9KUnFABRjFFGRQAcdBzQeODwPSuN+IXxa8EfDLTW1DxZq8NrGASFJyx/Ac189aj/AMFFvhRaXTQ2K/aVU4D/ADCgD635AyRj3oPTI596+evh3+218HvHl8mlx6wlrdyHARwcZ+pr362vrW8tFvbW4SSJ13KynIIoAm45/hPal6ngY968W179q34XeHfGjeBtQ1YDUldY/L2Hqa9lgnS6hSdPuSorg+xGRQA89KWkPSloAQDFLRRQAc+lI1LnFITxQANSjgUUUAIc8sB16ilHp+leZ/Fz4++Bfgy1jH4xvxbPqG7yvlJzt+ldN8PfH+hfErw1beKfDtx59ldZ8t8Yzg4oA6XHt06UuOpHJpMkHPf0rB8ceM9H8A+HLnxNrkvlWVqAZG69aAN4nC5xk96Mc4HXtXlvwq/aJ8AfF2+ubHwjqQmltSBINpHX616iB6jPvQA7A60UUhGaAA57UtIc9qWgABzSYpQMUZoAQ9KWkPSloAQDFLRRQAc+lI1LnFITxQANSjiiigAooooAMYoPNIc9qWgAHSiijPOKACiiigBPmo+ag57Upz2oATrQDmjpQBigBaKKKAEBz2paQYzSk4oAQnFKeKQjNKeaAEY0ueM0YzQelAAcnvR+NJxnOaOtAC4zRSEZpaACiiigAooooAKKBzRQAUUUUAH40UHHeigAopMCjAoAX8aPxpDjvScetADufWjn1pDjvQMdqADB9aMH1oyKMigA59aXn1pDjvSkjvQAgyc4rmfiR40sfh94O1LxPqMgWO0iJBJ744/Wum6jBr5Z/wCChOuXen/Bi6s7aRkW5xvIPo1AHxBdah8R/wBrj4vnTo7u4aGe4ZQoY7Y0B/LpX2j4Q/4J4/CnTdESDX4ft96yAyS5K849K8x/4Ji+FbSX/hIvEVzErzr5XkuRyvXNfoGAoHAz70Afl5+1T+x3cfBy2HjPwJPM2nodzxxAgxc8c9TXuP7Anxn1/wAV+Hb3wT4j8+STTY8RSSggnIPrX2B4h8N6N4p02TStbtEubab78bDg1z/hv4V+BfBBnu/DWix2csincy9+KAPzF+M8m39rm4ZjhRdxdT06V+j+rfHf4X+BrCytPEfii3t5fs8YKr838I9K/MH9qI6j/wANIawulyFblp40jYDueK+h/CP7Auv+OvDCeJfG3jO4i1O6hDrEyltvHHf0oA+1vBnxW8B+P4xJ4X1+C6B7AgH8utdaCBkk5B6e1fjday+NP2avjpHoEGq3BksrqOMqWO11cgdM46Gv0i/aI+Nn/Cqvg5/wk0L41G6tIzAueSWUZP60Ad74w+MHw+8CKW8SeIbe229VDBiPwFZ3hX4+/CzxpdCz8PeKLeeU8YY7f51+dfwU+A/j39qnVr3xb4p8Tz2tk8m4SsxIcEnjGe1bHx6/ZA8SfAnSR448G+LJruCyIeXYSu054780Afp7G6yrvQh1PRgeKz9a8RaN4dtGvdX1CG0iQZLO4Gfzr5i/Yq/aIu/iN4GvNK8S3e/UdDiy0jHlxg/0FfLPx9+LPjv9oP4vt4A8MXs6WInMMMUTEdPvHIoA+/JP2pfgjHfGwk8YwifO3btOM/WvQ/D/AIr0DxVbC70LU4LqMjIKOCfyr4bsv+CbUkvhcXdx41mGpvF5m3YchsZxnNeS/BzWfiz+zv8AGhPC98uoXGmpOIrhSrMrg8Kc84oA9N/4KiFheeEDuwczYHr0r6E/YZOf2fNAYntJwe3zV86f8FN5zcDwRc7cNMkjn2yAa2vhh+0h4f8Agn+y3owa5SbWJ45RBbhuc7utAH2jrXxE8HeHbr7Hq+u20Ew6ozjNeTftU69pev8A7P2v32lXAmhZF+ZenWvjP4OfCf4lftUeO38ceKdQu4tIabe77yFK54AANfX/AO0f4N0rwN+zTq+g6OhjhtokVcsWJOeetAHy/wD8E6NWsNI8R+Ib7VLyO2hjZSWdgB39a+1Zf2nfgvFqR0mTxhCtyG2425GfrX5UfAnwN45+JnimTwV4TvJrW3vpMXUkf8IBr6s8Xf8ABOO7sfC8moaL4xmn1OCIyFdpBY4yec0Afd+i6/pHiG0F9o2oRXMDAEFGBq+8ixxtJKQEHcnFflj+yj8a/GPwg+K8fw78V3s7afLMYZklYnZjgdfU19O/tw/tEX/w08I2eieGLny9Q1qM7ZF6oMD+hoA9y8VfHn4XeDJWt9f8UW8MgOMKd38qd4T+Ovwy8bTeToHii3mc8BWIX+dfnz8AP2Q/F3x7sH8b+L/EN1Z2t0S0buWbfzzxnioPj/8AsneMv2ereLxr4T8Q3V5ZW7b5HQlAnPHGeaAP1Gmu7eC3N08gMaruLA8Yrmbb4p+BLu8TT4fEVs1ySVEe8A5/Ovn/APYt+Ok/xm8DXXhjxROZb+yjEUjE8sDx/KvmD9rj4NeKfgj8QI/G/hm5vP7Knm85cSMRHg5PfuaAP1Ha4hihNzJIqx7dxYngCuWh+KngWe7+wQ+ILd7gttCKwJJ/CvhLxj+3edT+BltoemztH4luofJlYdUxx+oq5+wr+z7rXiPUh8VfGU939n3mSzSSRsSEk7uM0AfoYjh1DL0IBFOPtTY0WNAi9FAApSaAFOT3opOBRkGgBaKKKAAmijIoJxQAmD60YPrRkUZFAC/jQOKQAUvAoATPGaM8ZoPSg9KAFooooAAc0E4oooAKKM5ooATgUvBpODS8CgAopMijIoAWiiigBM84paT+KjHOaAAUZ5xS0e9ABRRRQAUUUUAJ81HzUmB60YHrQAvzUfNSYHrRgetAClvSgZ70tJkUALRSA5oJxQAtI1GRQT2oAGoPShqD0oAAcmj+KgHn60fxUAB7YoPSgnmgnigAB4znrXzR+3v4TvPEHwS1C7so2lktApCqOeWr6XyNpBXkdKy/Evh+x8T6Jd6HqcKvFdxlCp5HSgD8+f8Agml4/sNH1vXPB+ozrHPfmNbdWOOVzmv0ZJ6gYr8mPjT8BviX+zf8Qz4u8JJcPYrMZra5hUngnJGB+VereEv+Ckuv6XoyWPiXwXNdXUKBfOZypcgemKAPsT9oX4sR/B/4dX3ilNjXUQHkxFgCxzivF/2Vf2pfFvx31nVLLUtNMNvZr97dkcg18ffFn41fFX9qjxFbaLY6Rdw2Uj7YrRVJVQfVsV94/slfs/j4LeAWfU4h/a9/FuuOORxxQB8MfGlFk/a4uFcAj7ZD1/Cv1f0dVTRbJEGB9mj/APQBX5UfGWyvH/a2uHWznK/a4fmEZI7d6/VjSfl0ezXv9mj/APQRQB+U/wC2QAP2oJ8Lg/bLbt7ivb/+Ch73q/DXwWsDN5JtB5uOn3VxXi/7Y1jezftNzSRWk7j7ZbfMsZI6r3r7e/aK+DUvxf8AgfDpmnW+7Uraxia3Hf7oJ/lQB8Ufs/eG/wBrDUvBFvP8JrnGjHOxVKZHPfPNdx4q+FX7c3ifRbjR/EtwJrCcASI7RqPzrhPgN+0J45/Zf1a78I+JvDt1PYb9vlEECPB7HHeu5+Nf7dPib4laGfCXgHw7dWcl5hWmj3E5zx2oA2P2afgB8T/hND4l1XxBbRwQ3FuxPlzq+fkPYGvKP2NTbP8AtMRPqpUuLqfbv79fWvrb9jb4Z+OrHwXfav8AEW5nkl1iLEcMpJ2jBH9a+Tfjp8LfHv7OvxjPjnw7p85sTOZraWJSQRnLdKAP1aBVUBBBGPyFeb618Qvg1ZeJX0jVbywGrKyqytEpbcenNfJw/wCCj8p8IG1bwfMdX8nys7jndjGeleU/s7/Df4h/H74zr478QxXkVgJ/NuJX3KMdVGKAPRf+CoLo7eDnh6MsxXHcYGK+VdI+C/xL8TfD6bx3a2E8mjaeAyHdkcnnAr6u/wCCmunXIbwXb20EswgWRPkQnoAO1e7/ALHPh201j9mrS9I1awGy5ilR1dMHknrQB5F/wT7+PdlNp/8Awq7W3igurc7bbIAL85NfQX7YRB+A2vt1BROfxr8+/jp8MvFP7N/xmXXvDkVwtq9x5ttJEhIIzlhxX1r45+KsPxf/AGTdR1eCGUXawRrPEUO4EHFAHj//AATVOlDxTr3nbBdl18vPXvnFfovPsMTBz8m07s9xivxa+Cni/wCIPwo8Uv448PaReTW9lJm4QIwyCcelfVnij/go7cXPhOW10nwjPDqs8Xl5ycqcYJxigDwj9oQRp+1tfroG3aLy32lOnUZrr/29/tv/AAmvhtrzd5XlQ7M9Purmsz9lb4Q+MfjV8YY/Hviayn/s+GYzXM0qkbs8r1r6l/bh/Z51H4leDrPXPDFoZNQ0OM7Y1HMg4/kBQB67+zCbD/hTegiwKeWYjkrjk1T/AGsf7OPwR13+0dhTyxgN65r4c/Z7/a68W/AnTj4H8WeHrq7sbViibwy+Wc89uai/aF/ay8X/AB/ij8FeEvD11a2M7BHCBm83J47cUAdD/wAE2zc/8J9riwqRbiQbj2xzivq79sXxF4E0j4TalB4yjhla4TFvG33ic9u9cN+x78G5PgT8Or3xj4xgMF5eRCaRCMlcAkfnXyf8cPHnjX9qD4vw+G9Ftbs6atx5dvFsYKFzhjQB876W9tZ67bajfWbyaeLoOFIIBTf6/Sv2n+Avinwh4q+G+kah4N8lbIQhfKTqpAAOfxr5/wDHv7E3h6b4EW+haPZr/bunW5lS4A+ZifmIr54/ZA+NXiT4JfECTwL4qhu00u4n8ly0bERkHA7dzQB+poPpSDOeaitLqG+tormE/LKgcfQjNS8YwaADvR3o70d6AAnFHQUEZpaAAc80HnikJxS4xQAU3JpQc0EZoAU8UgNKeaQCgA3UZFHy0fLQAZFGRR8tHy0AGQaMgUny0fLQAuRRn3pDtxSjaaADvR3o70d6ADn1oOR3oIHrRgHpQAEkDrQARzRjPNAAoACcUtITiloAQDvS0UUAJ3o70d6O9AC0UUGgBAeaCOc0mBig4xQDVg5zkUvPrQMdKBjPFJK/UNWHA4ozQwxQRTt5hdCE0ueKRqXHFHzC6BaAecYoFIetO6DQUkjvQcgdaQ896OtK6DQXIHSjIoA4pB1o1DUM0ZoHWlajUNRaMUh6YoWlqBS1PRdM1u2az1OyhuYmGCsiA/zrzLUv2V/gjrF013f+DIWlY5JDkD9K9ZODgmg4DYLZFPUZyHhL4SfD7wOAPDvh62tj2YoGI/E115A/1eARjFKeRjHSj5cZzk+lOzE7HJXnwr8DX+sHXbzQoJL1iG84gZyK6tFVVWNQAEGAPQU8YIwBSdyCdxPalqLc5TWvhd4I17UjrOraFBPeFg3mMBnI6V1EUccUaQxgeWi7QuO1PHXnj0o3EfL2FFhnGeLPg98PPG0vmeI/DltPJ/eChT+grO8O/AL4U+FrsX2jeFreKVTkFhu/nXooOBn0oJzyOlFgGRxRQxpHDGqIvACjAFZ+u+GtE8SWr2OuWEN1E4xh0BxWkOufXtRz0zg0rtB7zPK3/Zf+Cz3f2tvBkPmk7t27jP0r0LQvDei+GrNNP0TT4bWKMYARAP1rSIz06+tKD2LZI600nILM57xR4C8LeMjC3iPS4rtrfOwuM4zWlouh6X4c0+PStHtVt7SP7iKOBV7nlg2c0vG0ZGCKAOf8TeBPCvjARf8ACQaPDeeVnbvUZGarWXw08GaZpU+i2mjRR2VxjzIh0P4V1HGN4GCetB9hgetAHJad8KvAWmW1xaWfh21SO5H71SgOf0rmpv2Zvgxc3IvJfCEAk3bg2e/0r1IEgbs5IoXptxwaAMzQfDWheGLJdP0OwhtYIhgBEAzWk6LIrCVAynjBGQaXg8E5pM8H+8egoA4PxN8C/hj4vuTda74Xt5ZW5LKoX+VL4X+B/wAMfB9yLnQfC9vFKvIZlDY/MV3mcDBPTtS8AdeT2p2sw0XQpalpOn6rZNpl9CJLZxhkAwMVzuifCjwJ4d1AappPh+3guUJKOFBPPWuu7YB4WjqcjqaSTb0YIR1V1MTKCMYxXGXvwe+Heo6k2qXXh23a5dg28KAcjvXaZ6ZOcUDjhjgetF0O5Fb20NpAlvAMKgCgegFSnOQc0ZA+ajjv3ouguLRSAYpScUm3HYQhOKWjIFFHqAhGaWk3Uuad0AUUgOaDzTugFHFNPWnZpo60XQCnpQOlLRRdAIM96Dk0ZFGRS0QW1uGD60YPrRkUZFK6YO973A9KAMUtJupgAOaCcUZA4oPSgBc0mD60ZAoyKADJ9KMn0oyKMigANABpcZpuB60ALg+tGD60ZFGRQAvPrRRkUUAJg+tGD60YFGBQAHPagjNGBQCACc9KG1b3gbSXvBjvnHvRjuR+NeX/ABB/aE8BfD+8bTr7UUe7X70YycfiK4cftneAedqcDqea46mMoU92cVTMMNSdnI+iM/7X6UZ/2v0r54/4bO8Bdk/nR/w2d4B/ufzqf7QofzELNMK/tH0OcHvQMDvXzv8A8NneAv7n86P+GzvAX9z+dH9oYf8AmD+1ML/MfRHPrRz6187/APDZ3gP+5/Oj/hs7wH/c/nR/aFD+YP7Uwv8AMfRB3HvSYP8AEa+eD+2d4C7J/Omy/tpeAIo8uvT61rTxtGbsmH9qYX+Y+icn1oHHevmj/huj4c/3P50f8Nz/AA6/ufzr2FleKkrqI/7Uw38x9L4PpQQT2r5nP7c/w4/55/zo/wCG6Phx3jP60/7KxX8of2phv5j6ZNA4/wD118zf8N0fDf8A55n9aP8Ahuj4b/8APM/rR/ZWK/lF/aWH/mPpgcUdTXzP/wAN0fDf/nmf1o/4bn+HHaM/rT/srFfyh/aeG/mPpnnOB0pAMn6V8zn9uf4c8/uzx9asWf7bnw6vrlLaKP5n+tH9lYr+Ul5thIK8pH0jn/apPoa8NH7WHgkgEJ/Oj/hq/wAFdk/nT/snFP7Bxf6yZbF2lUPc8H1owfWvDP8Ahq/wX/d/nR/w1f4L/u/zo/sjF/yh/rLlv/Pw9zz/ALX6UZ9/0rwz/hrDwV/c/nR/w1f4K/ufzpf2Ti/5Q/1ly3/n4e5k+9A69a8M/wCGsPBX9z+dH/DV/gofwfzprKMU/sjXEuXJfGe5kkKTnk0AgMQK8MP7V/gv72z+dXNL/ae8Iatex2FvH+8f61FTLMVBX5S6Gf4CtKymez5HrRn/AGv0rhB8VtNIBFu2D9aX/haunf8APu361wNW0Z7SaaujusH1owfWuE/4Wrp3/Pu360f8LV07/n3b9aBnd5/2v0oz/tfpXCf8LV07/n3b9aP+Fq6d/wA+7frQB3fXvSH1zXC/8LV07/n3b9aP+Fq6d/z7t+tAHdHjk85pQDjrxXC/8LV0wHi3bH41R1T41aJpVv8AabqEhR9axxFanhoe2qOyNKVGVeXLBHpBDY4NAz3NePf8NI+Ev7v86P8AhpHwlj7v868KXFWWN6VLHpvIsdNXjA9hyfSjn0rx7/hpHwn/AHf50n/DSXhP+7/Oj/WrK/8An4P+wcd/IexfhRz6V49/w0n4U/u/zo/4aT8J+n86P9asr/5+B/YWO/kPYefSgZHavHv+GkvCfp/Oj/hpLwp6fzprivLLXdQP7Ax/8h7CB37mgnHJPSvHz+0l4U/u9PrWN4j/AGtvBHhu3FxdD5W+taUeJMtxMuWNQqPD+Pe0D3nPtRn2/WvmA/t2/Dj+7/Ok/wCG7fhx/cP616Sx9D+Y2/1WzP8A59M+oM+360Z9v1r5f/4bu+HH9w/rR/w3d8OP7h/Wj6/Q/mD/AFWzT/n0z6gz7frRn2/Wvl//AIbu+HB/gP60f8N3fDj+4f1p/XqH8wf6rZp/z6Z9QZpMj0r5g/4bt+HH9w/rSj9u34b/ANw/rS+v0P5h/wCq2af8+mfTxzwc8GlIGeelfMcP7dfw2aRUYbVY8nnivZfhx8X/AAZ8TrP7V4Z1RLgoBuToR+dXTxdGo7JnLisix+DhzVoWR3GBRnFIDmlxmult9DyNluIRmgDFAIoJxQAfNR81Hy0fLQApz2oGe9J8tHy0AAOe1BOO1G6jdQAZ5xR/FS55xSfxUALRz6UnzUc+tAC0UUUAFFFFACcHLetY3i6+k07w7f3kJ+ZIWI/I1skk4OOBXPeP9p8I6mB3gb+RqKvwMyry9x+h+ZHiPUr7Xtcu9Sv5WkkkmcfMc9CapfZMZIA5q3sBvLknn9/Jj/vo1YWMAZ71+e1qj52flderLnkjM+yn+7R9kI6CtTyh6UeUPSsXOSdkY+0ktDK+yn+7S/ZT6VqeUPSjyhjOKfPJD9pK9rmX9k9qPsp9K1PKHpR5Q9KFO60H7WWzMo2xGeKo6pBttJDjtXRGNcEkVl62irYyYHQV6GVyviYJ9wjVu7HExQbh071L9m5HFS2Y+Q8d6thAa/pXDQjKlGy6IcqjUtWUPsgoFoK0PKo8ut/ZQWpPtJt6Gf8AZBR9kHpWh5dHl0eyXQftGt2Z5tKPsnPStDy6PLpuiJVuhnG268Ve0CDbq0P1pzR8ZqxoqY1WH60vZR7GGJqP2TPSoLYsi8dqn+xHd0q1Yx5iX6VdEIHOKtU0fn9WvJS3Mj7H7Uv2L2rW8kelHkj0o9mk0kiPrGm5k/YvQUfYj6VriIelHlewodKKshfWJRd4syPsXtR9i9q1vJHpR5I9KPZK6G8RJK9zHNlz0rc8CwbPFFnx/FUTQgdK0vByAeKLPA/jrz8ypWw0rHs5BXlLHU031Pqe00xXhjIQcqO3tVj+x1I/1Y/KtbS4Fa2iyP4F/lWkLZcdK/LJ/Ez+jKXwL0OY/sdP+eQ/Kj+x0/55D8q6j7MPQUfZl9BUlnL/ANjp/wA8h+VH9jp/zyH5V1H2ZfQUfZh6CgDl/wCx0/55D8qP7HT/AJ5D8q6j7MPQUfZh6CgDljpA/wCeYx9K4H4tWSw+HnOwD8PevZZLdQM4ry741RKvhtyPUfzr5/idtZbUPWyTXGwR4BHaZHAqUWRx0rQtIlKjirawJ6V/M9SvKMnqftlOlHkWhifYj6UGxz2rb8lPSgQIe1R9Zk1uV7GOljE+xcfdo+w/7Nbfkp6UeSnpU/Wp7Jh7GLaujE+xe1H2I+lbnkL6UeQvpVfWJ8urGqMXJJGEbJvSvNfjNBs0mH8a9kkhXB4ryj42pjSocep/nXs5DXlLGQVzowlH98rnhywbuop32fBq1EgqXy81+tqbaPs44eLitCh9nGelH2cDtWh5Q9KPL9qfPbqX9VjfYz/s6+lH2celaHlD+7R5Y9KTqX0QfVoJNWM/7MB2pfs6+lX/ACx/dpPLX0oc7ISw0exnmADOT16V7T+yX4n1TQ/i3pem21w/kXjMJV3ccDjivJWjHOF6da9F/ZsG340+Hzjje+fyrrwVR+1jqeDxHhYf2fUuuh+qcZLRo3qoNPOccVFBnyY/9wfyqT5q+6Xwn8vTilKXqLQBimg4pQc1RAfxUNRjnNH8VABkUuRRz6Uc+lACZFGRRk+lHPpQAHpQOlLRQAUUUUAFFFFABRRRQAh6Vzvj/wD5FLUv+uDfyNdEOc1zvxA/5FLUv+uDfyNZVvgZlX/hy9D8yf8Al5uP+u8n/oRqyOlVx/x9XH/XeT/0I1aAr86qfGz8nr/HL1A9RluvQ0AHgbcepz1o7EYz6V6P8Lvgn4i+JQlns42htYlyZcZyadKlOtLlp7ioYedafLT1Z5zgnIxmun+H/wAP9b+IGsw6VpVs5RmHmNjhRW7pfwR8YXvjM+E202RXjkAd8cBfX8q+0/AHw/8ACvwd8K+dKYklSPdNO3UnFelg8ulXqc1TRI9fL8oliJ81TRLc+Zvi5+zTN4H8PRa3pdyJljXNxkYwa8AKkEgr0OGr3r9oH493XjO7m0DRJCumxkqWH8deDHnG7qefrXPmHsvaWo7HLmaoKrbDidjWRrufsMn0rXI4NZGu/wDHjJ9KrKrPFwXmcCepylkBtH1NXF6VTs/uD8auL0r+n8M7YeK8kKau7sXoQSeOwpOnbHtS4J4ByB0969Z+Fn7PHiz4oabcarZwNDbwrujcj79KtXpYWHNVZpRoVMTK1M8mABbIruvhP8J/EHxR12HTdMt38ncPOl28KK2vB37PvjXxH43bwpNp0sIgkAmkI4Uf/qr738G+DPBfwI8Fb5DDB5EW6aZgMsa8TM86jh48lDWTPZy7KJV3zV9Io+Pfj/8Asv3Pwy0iLxDpFwJrJFHng8bD/XmvnVjghvzFe9ftG/tD6h8TtSl0XSnMejwsVAB4krwckkliceld+VLEOhzYndnDmUqCq8uH2Qh6VNo//ITi+tQnpU+jf8hOH616LR5GJ/gs9Z08fukq+DVHT/8AUp9KvDvVxR+d1viF6YK9uvtR1BP3g3X2o5+WvRPBnwW8ReMdGn1eCMxxqu6IY+/61jiMRTw0eao7GuDwNbHVHToRuzzvAHAGAK7T4cfDXVfH2pi3ghZLYEeZLjgCr3gr4P8AiDxJ4jOk3No8Mdu+JnI7V9X6bp/hn4VeGOTFBHAmST1Jrw81zqNL93h9ZM+u4f4XliZe3xq5acT5l+LnwYn8CRrqFk4ltSBu7bT/AFrycAZGOlel/F74sX3j3UmtYnKWEbEKg715qMngngdK9PK/b/V08R8R4GffVPrklgvgQ1hxitHweP8Aip7PP9+s9q0fB/Piiz/36WaL/ZpJmnDzisfTXW59jaT/AMe0X+4v8q1FrL0n/j2i/wBxf5VqDpX5RP4mf0lS+Begc8DGcdqD97nr29qGIXndhQCSfSvCfH37W3gHwJ42tPB9xcpIXcpPID/qj2qSz3bv0+prxn9oL9ozw58F9Ckle4S41KUfuIA3JP8ASs745ftQ+Efh14OXUtNv4ry+vYibaNG7kf8A16/O1U+IX7SHxCABnu7i8l99qLn8hxQB+hP7MX7TVl8arB7G/iaDU7cncuCQwJ45+lfQGSTnHTrXjf7O37PehfBfw6kcSh9WuEU3E2OSa9lOeMNn8KAGS/dryz43f8i5J+H869Tl+7Xlnxt/5FyT6j+dfP8AE7Tyyoevkf8AvsDxW05UfSrvQVStBhV+lXB0r+X63xP1P3Ck7U0GB/hS8HqKFUllK9+MV3WmfCTxBqXh+TWkjYEDdHFjlh3roweW4jMZNYdXsYYnG0MIlKu7XOF2jcAOQf1rufh18M7/AMY3PmyQtHZp1cjrVj4d/C3VPE2pbr+FobWFvm3DrX0BqGpeH/h1oG3EcSxJgKOpNfa8OcJqSeLzBWprv1Pl864hcGsNgdZv8D5x+I3w9n8GXow++3f7prjM/wAIHPb2rqPHnji/8Y6m9zKSLdSfLT0Fct83ds4r5LOvqzxklgvgPosqWIeGj9Z+LqNkHymvJfjeP+JVDj3/AJ1605yDXk3xw50mH6n+dbcO6Y6ET2cIl7eJ4zF0qeoYugqev2B7H29NqMUg4+8TgUnQYIx71JDDLcTLDCpZ2IUL6k17Rbfss+ObnwL/AMJgsDl9m8W+3kitaeHlVWhw47M8Nl0l9Zmo32PFApY/J1PQV7v+z9+zVrHxRn/tPVIXt9LQH5mX73HH61e/Z5/Zm1jx7rSal4jspLfTLaTL7xjfg/8A1q+zvHPjnwT8B/BXlRiCHyItsUS4yxxXr4HLo8nta2x8FxPxhUVRYDKveqy6rofn18dPgxqHwi8QiwkkElpOT5MnHzAdeO1eY46DORXc/Fn4qa38U/EUmrapKRAHPkx/3BXC8keory8T7P2j9nsfdZOsX9Sh9dd6ltRH6V6H+zf/AMlq0D/ff+VeeP0r0P8AZv8A+S16B/vP/KrwS/fROTiPXL6nofqfbHMMf+4KkJxUdt/qI/8AdH8qlr71fCfyrW+J+oUgHNLRVGYhGe9KRmiigApPmpaT5qAFPtSDPelPtSDPegBaMiijAoATrR0paKACiiigAooooAMVzvxAA/4RLUuf+WDfyNdDj3rnviAMeEtSP/TBv5Gsq2kGY19KTPzJH/H1cf8AXeT/ANCNWR1FV1/4+rn/AK7yf+hGrA7V+d1fjZ+UV9arFP6Gvoz9mr462/hMxeEteVFs3O2ObAG3/GvnPHG3OPUUqM8Tb0Yqy8hga0wteeHmpwNcJip4OoqkT9RZ9S8M2FhJ4ldrdUZN5m4yRj1r4z+Pvx+v/Gd5LoegzNHpsRKsVb79eb3XxX8Y3vhmPwrPqsn2KIYxnkj61xuSSWLEnv716eLzV4iHJT0PYx+ePE0/Z0lbuKxLEsxJJ9aKQUA5rxNz53fcQ96ydd/48ZPpWse9ZOu/8eMn0r0Mq/3uHqVHc5Sz+4KuLVSy+4PqatrX9PYbWhG/ZClJ82gobDAgYAORX1v+yl+0ZZ6EsPgfxN5cUBIWGUgDFfJBA4+bOf0p0c8tvIkkLFGQ5BBwc1jj8DTzCl7OR04HFTwVX2iP1y1bW/CfhnTJvFVw9rFGyeY0wAy3HFfAn7Rf7ReqfEnVZdH0e4eLSIWKqqnHmV57rfxi8ca94atvCmoavLJZW4KhemR9a4jcxADHNeNluQrCy9rX1fQ9bMM6liV7Ojog5PJPWgZ70c0gz3r6bW10fP2V7MG61Po//ITh+tQN1qfR/wDkJw/WkzDE/wAFnrdgcxJ9BV/PSqFgMRJ9BV/0qon53W+MVSVYSDqpyPevpH4BfGWyihh8K63sjI+WKQ8Bq+bQOPX29KfBPJbyLNBKUKHII4INcOY4Cnj6bpz3PTyXN6mT4lVobdT9BNa1vw74Z06bW53hhTbuLgD5vSvkL4s/FvUfHWoyW9vM0dhGxEag9a5zXPiJ4m1/TLfS9Qv3eCEFQOma5gf7TV5eVZEsHP2lfWXQ+gz/AIulmUVRw/ux6hnNFFFfSHw71dxDWh4OP/FT2n+/Wea0PB3/ACM9p/v15uaf7tI93h7/AJGEPU+x9J/49ov9xf5VqLWXpP8Ax7Rf7i/yrUWvyifxM/pKl8C9Bs8azRPC4yJFKt9CK/PX9sD9ljV9H1G5+IPhKOW4tpG8yaMEkx/jX6Gjhsk9aq6lplpq1nLY6hAs8MylWVhkEVJZ+MngnwV43+KviK28MWSXVy6vsO8kiMd+tfp9+z1+zz4d+Dfh6FRbRy6pMgM9wV5zXVeBfgt4E+Ht/d6j4d0aK3mum3O/U59s13g65z06UAAAPNAwe1LSAY70ANk/1Zryv42f8i3J+H869Uk/1Zryv42f8i3J+H86+f4n/wCRZUPXyP8A32B4ta/dH0q6eoqla/dH0q6eor+Xq/xM/caX8NDomMUqP3Qgivo74RfFCx1i0j0PVCkdyg2qT0YV83H72c/Sp7O9ubC4jubSUxOhySK9zh7PquR1lUirxe6PJznKaebUeSWjWzPsbxJ4k0Pwfpcl6/lJxkBcDJr5b8deOdQ8X6k8s07CIE+XGOgFUfEPi7WfEpRdSu2kSMYVegFYoO3kHr39K9niXi6ebr2ND3YHnZJw5HLf3tb3ph0FA5FIOuKOp4r4Y+pW42Toa8m+N/8AyCYfqa9ak6V5L8cP+QVF9TXucPf79A6MJ/GR41D92phyKhh+7Uw4r9etdWZ9vT1SZa02/m028hv7fG+FwwyM5wa/Q39mv9obQfH+jw+GtcaGHUIkCbWxh/6V+dJycnOCa0dB1/VPDmpRappFy8E8JyCprvwWM+pvyPm+JuHKfENDlbtNbM/Ur4n/ABP8H/CDw3Les0ETlSYokABY/hX5vfFf4seIfijr8+p6lcP5Ic+VDu4UVn+OviX4q+IN0s/iHUZJ/KUCNScDp6VynGPQ1vjsyeI92HwnmcK8H08kXtsR71TuLRRRXk+h92rX0GSdK9D/AGbv+S16B/vP/KvPJfu16H+zd/yWrQP99/5V2YP+NE8LiPTL6iXY/VG34gj/ANwfyqSo4P8AUx/7g/lUlfexfuo/lOprNt92FAGKCcUVRmJjnNB6UtFACHpS0h6UtABRRRQAUUUUAFFFFABRRRQAUUUUAGMsR61zvxAwPCGp5/54N/I10OTuA9Ky/E2ntquh3lggy0sLAfXBqKq5oMyrq9Nn5eJzdXBzx50n/oRqyvI54Bq/4q8N6l4U8RXuk6payRPHMxUlTggknrWasqbfvZPpX53WpyjN6H5Xiaco1XoS0UzzV9aTzU9RWXJNdDD2c+iJKTB9ab5qetIZU7mhwl0QOnN7okoqMyoO4pfNX1/Wjkl2Dkl2HHpWVrv/AB5S/StEypjiszXHU2UmPSvRyqLWLhddSowl2OUs87B9aueuTVKzYBOfWrXmJzzX9M4acPZRbfRBKEnokSYPrRg+tM80daXzV9a256b1TJ5Ki0aHUUzzEHejzEPWhVV3B02ug/B9aMH1pvmrSeatNVIN2uHsprWw5j2qxo3Opw896qmRc1Y0ZgdVi+tJzj3McTCXsXoeuaef3S89hV8YwOazrB1ES5PYVd81OOaqM49z88rUanN8LJKKZ5y+opfMT+8Kp1ILVsyWHqb8rHUUzzk9R+dHmp6inzR6MPYztqmPopnnJ6j86POT1H50c8e4OjU/lY5ulaPg8Z8T2f8Av1lGZDxkVp+D2U+J7PB/jrzcznF4eVme5w/TmsfBtM+yNJ/49ov9xf5VqD2NYulX1oLeMGZPuD+IelaI1Cz7zx/99CvyqfxM/o+l8CLLUo+tVf7Qs/8AnvH/AN9Cg6hZ9p4/++hUllnB9aMH1qr/AGhZ/wDPeP8A76o/tCz/AOe8f/fVAFoYoOKrf2hZf890/wC+hR/aFl/z3T/voUATy/dxmvK/jdn/AIRyTnuP516W+oWewkTx/wDfQrzD403EEvh6QJIrHjgHPevA4mTeW1Ej1skaWNg2eN2h4H0q4elUbWRNo57Va82PH3hX8w1qNTmfus/b6dSHs1qSUH61H5sfrR5sfZh+dZOjWbvys09pB63Hk89aUnio/MT+8KPNTGMj86FRqr7LEqkU7XHnpQtM8yP1A/GgSx+o/On7Gp/KxKpC+4rk7SDXk3xw/wCQVF9T/OvV3ljKk5FeTfG8g6XDj1P869zh6lNY6LaZ0YWpD2y1PHIcham5x1qCJuKl3DGK/XeSUldI+2hXpctnJfePopu8elIXHalyN6NF+2pS2kvvHUtM3il3ijkklZIft6ezkvvHUU3eKTeKfI3okL29GWikr+oPggdh6V6J+zfx8a9Ay3LO/wCHFedFwScjPpXt/wCyR4B1rX/ihY67HZyCz09iZJGXA5HFdmChL2sdD5zijF06WXT95bH6S25P2eL/AHR/KpabEu2NU/ugCnGvu0vdR/LtWXNNpAelIBigHNLTICiiigAooooAKRaWigAopcH0NGD6GgBo6UtAVsdDS4PoaAEowKXB9DRg+hoASilwfQ0bT6GgBoPFID+tP2N/dP5Umw56Gl7z0YWvuch4u+Ffgfxud3iLRI7lu7A7T+YrlG/Zb+C+Qf8AhFj/AN/mr1raR2NG3sQR+FYywtOW6OWeEoTeqPJf+GW/gvjJ8Lf+R2o/4Zb+C2M/8It/5HavWtr47/lRtOO/5VH1Sl/KT9Rw/Y8lH7LnwWH/ADK//kZqP+GW/gt/0K//AJGavW9rf3T+VG1v7p/Kj6pS/lD6jh/5TyQ/sufBU/8AMrn/AL/NR/wy78F/+hW/8jtXrWxvQ/lRhvf8qHhKP8ofUcP/ACnkv/DLnwXzz4WP/f8Aakk/ZX+Ckow3hXI/67NXrZR+pB/KlKsRwD+VXDD0ou6QfUcP/KeM/wDDI3wJ/wChR/8AI7Uf8Mi/Ar/oUv8AyO1eyhWx0P5UYbP3TXorG10rKTH9Rw/8p4z/AMMi/Av/AKFL/wAjtS/8Mi/Ar/oUv/I7V7LsPoaNh9DT+u4j+Zh9Rw/8p41/wyL8Cv8AoUv/ACO1H/DI3wK/6FL/AMjtXsuG9D+VGG9DR9exH8zD6jh/5Txr/hkb4Ff9Cj/5Hag/si/Ar/oUf/I7V7LsPoaNh9DR9dxH8zD6jh/5Txo/sjfAwAf8UlwP+m7VLbfsn/A+1mE9t4Sww/6btXsAQg5IODQVPoQPpR9er/zMmWX0JKzieZ/8M6fCYdPD2P8Atq1H/DO3wn/6F8/9/Wr0zBHY0YJ7H8qP7Qr/AMzOZ5LgnvBHmZ/Z1+FA/wCZfP8A39agfs6/Cg/8y+f+/rV6YVbPQ/lQA3ofyp/X6/8AMw/sTBfyI8z/AOGdvhP/ANC8f+/rUf8ADO3wn/6F4/8Af1q9M5/umjn+6aX16v8AzMP7EwX/AD7R5of2dPhT/wBC8f8Av81N/wCGdPhV/wBC8f8Av61emkE/wGk2n+4aax9f+Zg8jwT+wjzT/hnP4UFiP+EePP8A01arFj8Avhhp1yt3Z6BslTofNNeiEEjaSQfpS7Tx8pI7molja8tHJmlPKMHSd1BXOVHwz8I9rBx/21NH/CtPCX/PjJ/39aup2n+6aNp/umuZu56KVlY5b/hWnhH/AJ8ZP+/rUf8ACtPCP/PjJ/39aup2n+6aNh7KaBnLf8K08Jf8+En/AH9aj/hWnhL/AJ8JP+/rV1JDd0NADdkNAHLH4a+Ev+fCQ/8AbVqT/hWnhP8A58JP+/zV1O0/3DRtP9w0Acv/AMK08In5Rp8mP+uzVXvfhP4Hvo/Ju9LZl95WNdkEI4OfypMMOCpIrKrRjiI8s1oVCq6UrxPPj8Cvhz/DouP+2ho/4UV8Of8AoDH/AL+GvQQpH8JoCE/wGvLWQZfJ3nTR3vM8Sl7s39558fgV8Ou2jH/v6aT/AIUV8O/+gN/5FNehFTn7po2kdFNP+wMv/wCfaJ/tTF/zv7zz7/hRfw7/AOgMf+/rUn/Civh3/wBAb/yK1eh4/wBk0Y/2TR/YGX/8+0P+1MX/ADv7zz0/Ar4ddtGP/f00n/Ci/h0P+YL/AORGr0LaT2NJtP8AdNC4fwFv4aB5pi/53955/wD8KL+HWMHRj/38NZ2sfs2/CfXIfJ1PQPNT08xq9SKnGMGja4GApAqqWTYKjK6poazXFr7b+88U/wCGP/gX28J/+Rmo/wCGP/gZ/wBCn/5Hava9r/5FG1/Su76rSX2TX+2sb/z8f3s8UP7IHwK7+Ff/ACM1A/ZA+BX/AEKn/kZq9rCnupoKHspo+q0v5Q/trHf8/X97PFP+GPvgX/0Kf/kZqP8Ahj/4Gf8AQp/+R2r2sq3YGja/p+lH1Wl/KH9tY7/n6/vZ4mf2QPgX28K/+RmpR+x/8CyP+RU/8jNXtW1v7hpdpHRTR9Vpdg/trHf8/H97PF4v2QvgdG4b/hE+VOR++avSvCvgnw54LshYeH9MjtYlGPlHP51vBTkkgk+lCq3J2EVUcPGHQwrZlisQuWrNteoCil2n+6fyo2n0NbX6HBs7oTpRS7T6GjafQ0AJSYFO2n0NGD6GgBM4oo2k9jS7T6GgBKM5pdp9DSbSOxoA2tq/3R+VG1f7o/KlooATav8AdH5UbV/uj8qWigBNq/3R+VG1f7o/KlooATav90flRtX+6PypaKAGlR2UflSbB6CnfjSHPehXFZMNqjsPypML/dFKtLkdKA2EwvoKMJ6D8qNvvRt96Bi/L6CjC+gpNvvRt96A0FwPQUbV/uj8qTHvRt96QC4X0FJhfQUbfejb70wDC/3R+VGF/uj8qXHSjHSgBML/AHR+VGF/uj8qXHSjHSkGgmF/uj8qML/dH5UuOlGOlAaCYX+6Pyowv90flS46UY96YaBtX+6PypNo/uj8qdSZFAtRNq/3RRgDHyilyKTbS1GLtX+6Pyo2r/dH5UYo20C0Dav90flRtX+6Pyo20baA0Dav90flRtX+6PypNvvS7aY9g2r/AHR+VN2+wp9FK7Fa4m1f7o/Kjav90flS0UxibV/uj8qNq/3R+VLRQAm1f7o/Kjav90flS0UAJtX+6Pyo2r/dH5UtFACbV7gflSYT0H5U6kyKV2hbjSF7AUYHoKfRRa4xuxfQUbF9BTqKYDdi+go2L6D8qdRQAzavt+VKFX0FKDmlpagN2p3Ao2r/AHRTqTFMWobV/uj8qNq/3R+VGKNtINA2r/dH5UbV/uj8qNtG2gNA2r/dH5UbV/uj8qNtG2gNA2r/AHR+VG1f7o/KjbRtoDQQKOuBS7V9BR+NLRqwGkKP4RS7V/uj8qWm7fejYYu1f7o/Kjav90flS0UwE2r/AHR+VG1f7o/KlooATav90flRtX+6PypaKAE2r/dH5UbV/uj8qWigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9k=\"}"); -} - -var socket_ot; - -function ot_open() { - socket_ot = new_ws(get_appropriate_ws_url(""), "dumb-increment-protocol"); - - console.log("ot_open"); - - try { - socket_ot.onopen = function() { - document.getElementById("ot_statustd").style.backgroundColor = - "#40ff40"; - document.getElementById("ot_status").innerHTML = - " websocket connection opened
" + - san(socket_di.extensions); - document.getElementById("ot_open_btn").disabled = true; - document.getElementById("ot_close_btn").disabled = false; - document.getElementById("ot_req_close_btn").disabled = false; - console.log("ot_open.onopen"); - }; - - socket_ot.onclose = function(e){ - document.getElementById("ot_statustd").style.backgroundColor = - "#ff4040"; - document.getElementById("ot_status").textContent = - " websocket connection CLOSED, code: " + e.code + - ", reason: " + e.reason; - document.getElementById("ot_open_btn").disabled = false; - document.getElementById("ot_close_btn").disabled = true; - document.getElementById("ot_req_close_btn").disabled = true; - }; - } catch(exception) { - alert("

Error" + exception); - } -} - -/* browser will close the ws in a controlled way */ -function ot_close() { - socket_ot.close(3000, "Bye!"); -} - -/* we ask the server to close the ws in a controlled way */ -function ot_req_close() { - socket_ot.send("closeme\n"); -} - -var socket_lm; -var pending = ""; - -function lm_timer_handler(ev) { - socket_lm.send(pending); - pending=""; -} - -/* lws-mirror protocol */ - -var down = 0; -var no_last = 1; -var last_x = 0, last_y = 0; -var ctx; -var color = "#000000"; -var lm_timer; - -function ev_mousemove (ev) { - var x, y; - - if (ev.offsetX) { - x = ev.offsetX; - y = ev.offsetY; - } else { - x = ev.layerX - offsetX; - y = ev.layerY - offsetY; - - } - - if (!down) - return; - if (no_last) { - no_last = 0; - last_x = x; - last_y = y; - return; - } - pending = pending + "d " + color + " " + last_x + " " + last_y + - " " + x + " " + y + ";"; - - if (pending.length > 400) { - socket_lm.send(pending); - clearTimeout(lm_timer); - pending = ""; - } else - lm_timer = setTimeout(lm_timer_handler, 1); - - last_x = x; - last_y = y; -} - -function ev_mousedown (ev) { - down = 1; -} - -function ev_mouseup(ev) { - down = 0; - no_last = 1; -} - - -function ws_open_mirror() -{ - socket_lm = new_ws(get_appropriate_ws_url("?mirror=" + mirror_name), - "lws-mirror-protocol"); - try { - socket_lm.onopen = function() { - document.getElementById("wslm_statustd").style.backgroundColor = - "#40ff40"; - document.getElementById("wslm_status").innerHTML = - " websocket connection opened
" + - san(socket_lm.extensions); - lws_gray_out(false); - }; - - socket_lm.onmessage =function got_packet(msg) { - j = msg.data.split(";"); - var f = 0; - while (f < j.length - 1) { - i = j[f].split(" "); - if (i[0] === "d") { - ctx.strokeStyle = i[1]; - ctx.beginPath(); - ctx.moveTo(+(i[2]), +(i[3])); - ctx.lineTo(+(i[4]), +(i[5])); - ctx.stroke(); - } - if (i[0] === "c") { - ctx.strokeStyle = i[1]; - ctx.beginPath(); - ctx.arc(+(i[2]), +(i[3]), +(i[4]), 0, Math.PI*2, true); - ctx.stroke(); - } - - f++; - } - }; - - socket_lm.onclose = function(){ - document.getElementById("wslm_statustd").style.backgroundColor = - "#ff4040"; - document.getElementById("wslm_status").textContent = - " websocket connection CLOSED "; - lws_gray_out(true,{"zindex":"499"}); - }; - } catch(exception) { - alert("

Error" + exception); - } - - var canvas = document.createElement("canvas"); - canvas.height = 300; - canvas.width = 480; - ctx = canvas.getContext("2d"); - - document.getElementById("wslm_drawing").appendChild(canvas); - - canvas.addEventListener("mousemove", ev_mousemove, false); - canvas.addEventListener("mousedown", ev_mousedown, false); - canvas.addEventListener("mouseup", ev_mouseup, false); - - offsetX = offsetY = 0; - element = canvas; - if (element.offsetParent) { - do { - offsetX += element.offsetLeft; - offsetY += element.offsetTop; - element = element.offsetParent; - } while (element); - } -} - -function update_color() { - color = document.getElementById("color").value; -} - -/* stuff that has to be delayed until all the page assets are loaded */ - -window.addEventListener("load", function() { - - lws_gray_out(true,{"zindex":"499"}); - - document.getElementById("file").onchange = check_file; - document.getElementById("offset").onclick = reset; - document.getElementById("junk").onclick = junk; - document.getElementById("color").onclick = update_color; - document.getElementById("ot_open_btn").onclick = ot_open; - document.getElementById("ot_close_btn").onclick = ot_close; - document.getElementById("ot_req_close_btn").onclick = ot_req_close; - document.getElementById("pmd").onclick = on_pmd; - - var transport_protocol = ""; - - if ( performance && performance.timing.nextHopProtocol ) { - transport_protocol = performance.timing.nextHopProtocol; - } else if ( window.chrome && window.chrome.loadTimes ) { - transport_protocol = window.chrome.loadTimes().connectionInfo; - } else { - - var p = performance.getEntriesByType("resource"); - for (var i=0; i < p.length; i++) { - var value = "nextHopProtocol" in p[i]; - if (value) - transport_protocol = p[i].nextHopProtocol; - } - } - - console.log("transport protocol " + transport_protocol); - - if (transport_protocol === "h2") - document.getElementById("transport").innerHTML = - ""; - - BrowserDetect.init(); - - document.getElementById("brow").textContent = " " + - BrowserDetect.browser + " " + BrowserDetect.version + " " + - BrowserDetect.OS +" "; - - document.getElementById("number").textContent = - get_appropriate_ws_url(mirror_name); - - /* create the ws connections back to the server */ - - ws_open_dumb_increment(); - ws_open_status(); - ws_open_mirror(); - -}, false); - -}()); diff -Nru libwebsockets-4.0.20/test-apps/test-lejp.c libwebsockets-2.4.2/test-apps/test-lejp.c --- libwebsockets-4.0.20/test-apps/test-lejp.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-lejp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,121 +0,0 @@ -/* - * lejp test app - * - * Written in 2010-2019 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates a minimal http server that performs a form GET with a couple - * of parameters. It dumps the parameters to the console log and redirects - * to another page. - */ - -#include -#include - - -static const char * const reason_names[] = { - "LEJPCB_CONSTRUCTED", - "LEJPCB_DESTRUCTED", - "LEJPCB_START", - "LEJPCB_COMPLETE", - "LEJPCB_FAILED", - "LEJPCB_PAIR_NAME", - "LEJPCB_VAL_TRUE", - "LEJPCB_VAL_FALSE", - "LEJPCB_VAL_NULL", - "LEJPCB_VAL_NUM_INT", - "LEJPCB_VAL_NUM_FLOAT", - "LEJPCB_VAL_STR_START", - "LEJPCB_VAL_STR_CHUNK", - "LEJPCB_VAL_STR_END", - "LEJPCB_ARRAY_START", - "LEJPCB_ARRAY_END", - "LEJPCB_OBJECT_START", - "LEJPCB_OBJECT_END", - "LEJPCB_OBJECT_END_PRE", -}; - -static const char * const tok[] = { - "dummy___" -}; - -static signed char -cb(struct lejp_ctx *ctx, char reason) -{ - char buf[1024], *p = buf, *end = &buf[sizeof(buf)]; - int n; - - for (n = 0; n < ctx->sp; n++) - *p++ = ' '; - *p = '\0'; - - if (reason & LEJP_FLAG_CB_IS_VALUE) { - p += lws_snprintf(p, p - end, " value '%s' ", ctx->buf); - if (ctx->ipos) { - int n; - - p += lws_snprintf(p, p - end, "(array indexes: "); - for (n = 0; n < ctx->ipos; n++) - p += lws_snprintf(p, p - end, "%d ", ctx->i[n]); - p += lws_snprintf(p, p - end, ") "); - } - lwsl_notice("%s (%s)\r\n", buf, - reason_names[(unsigned int) - (reason) & (LEJP_FLAG_CB_IS_VALUE - 1)]); - - (void)reason_names; /* NO_LOGS... */ - return 0; - } - - switch (reason) { - case LEJPCB_COMPLETE: - lwsl_notice("%sParsing Completed (LEJPCB_COMPLETE)\n", buf); - break; - case LEJPCB_PAIR_NAME: - lwsl_notice("%spath: '%s' (LEJPCB_PAIR_NAME)\n", buf, ctx->path); - break; - } - - lwsl_notice("%s%s: path %s match %d statckp %d\r\n", buf, reason_names[(unsigned int) - (reason) & (LEJP_FLAG_CB_IS_VALUE - 1)], ctx->path, - ctx->path_match, ctx->pst[ctx->pst_sp].ppos); - - return 0; -} - -int -main(int argc, char *argv[]) -{ - int fd, n = 1, ret = 1, m; - struct lejp_ctx ctx; - char buf[128]; - - lws_set_log_level(7, NULL); - - lwsl_notice("libwebsockets-test-lejp (C) 2017 - 2018 andy@warmcat.com\n"); - lwsl_notice(" usage: cat my.json | libwebsockets-test-lejp\n\n"); - - lejp_construct(&ctx, cb, NULL, tok, LWS_ARRAY_SIZE(tok)); - - fd = 0; - - while (n > 0) { - n = read(fd, buf, sizeof(buf)); - if (n <= 0) - continue; - - m = lejp_parse(&ctx, (uint8_t *)buf, n); - if (m < 0 && m != LEJP_CONTINUE) { - lwsl_err("parse failed %d\n", m); - goto bail; - } - } - lwsl_notice("okay\n"); - ret = 0; -bail: - lejp_destruct(&ctx); - - return ret; -} diff -Nru libwebsockets-4.0.20/test-apps/test-ping.c libwebsockets-2.4.2/test-apps/test-ping.c --- libwebsockets-4.0.20/test-apps/test-ping.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-ping.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,571 @@ +/* + * libwebsockets-test-ping - libwebsockets test floodping + * + * Copyright (C) 2011-2016 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * The person who associated a work with this deed has dedicated + * the work to the public domain by waiving all of his or her rights + * to the work worldwide under copyright law, including all related + * and neighboring rights, to the extent allowed by law. You can copy, + * modify, distribute and perform the work, even for commercial purposes, + * all without asking permission. + * + * The test apps are intended to be adapted for use in your code, which + * may be proprietary. So unlike the library itself, they are licensed + * Public Domain. + */ + +#include +#include +#include +#include +#include +#include + +#include "../lib/libwebsockets.h" + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#else +#include "gettimeofday.h" +#endif + +#ifdef __ANDROID__ +#include +#endif + +#ifdef __sun +#include +#endif + +/* + * this is specified in the 04 standard, control frames can only have small + * payload length styles + */ +#define MAX_PING_PAYLOAD 125 +#define MAX_MIRROR_PAYLOAD 4096 +#define MAX_PING_CLIENTS 256 +#define PING_RINGBUFFER_SIZE 256 + +static struct lws *ping_wsi[MAX_PING_CLIENTS]; +static unsigned int interval_us = 1000000; +static unsigned int size = 64; +static int flood; +static const char *address; +static unsigned char pingbuf[LWS_PRE + MAX_MIRROR_PAYLOAD]; +static char peer_name[128]; +static unsigned long started; +static int screen_width = 80; +static int use_mirror; +static unsigned int write_options; + +static unsigned long rtt_min = 100000000; +static unsigned long rtt_max; +static unsigned long rtt_avg; +static unsigned long global_rx_count; +static unsigned long global_tx_count; +static int clients = 1; +static unsigned long interrupted_time; + +struct ping { + unsigned long issue_timestamp; + unsigned long index; + unsigned int seen; +}; + +struct per_session_data__ping { + unsigned long long ping_index; + + struct ping ringbuffer[PING_RINGBUFFER_SIZE]; + int ringbuffer_head; + int ringbuffer_tail; + + unsigned long rx_count; +}; + +/* + * uses the ping pong protocol features to provide an equivalent for the + * ping utility for 04+ websockets + */ + +enum demo_protocols { + + PROTOCOL_LWS_MIRROR, + + /* always last */ + DEMO_PROTOCOL_COUNT +}; + + +static int +callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct per_session_data__ping *psd = user; + struct timeval tv; + unsigned char *p; + unsigned long iv; + int match = 0; + unsigned long long l; + int shift; + int n; + + switch (reason) { + case LWS_CALLBACK_CLOSED: + fprintf(stderr, "LWS_CALLBACK_CLOSED on %p\n", (void *)wsi); + + /* remove closed guy */ + + for (n = 0; n < clients; n++) + if (ping_wsi[n] == wsi) { + clients--; + while (n < clients) { + ping_wsi[n] = ping_wsi[n + 1]; + n++; + } + } + + break; + + case LWS_CALLBACK_CLIENT_ESTABLISHED: + + psd->rx_count = 0; + psd->ping_index = 1; + psd->ringbuffer_head = 0; + psd->ringbuffer_tail = 0; + + /* + * start the ball rolling, + * LWS_CALLBACK_CLIENT_WRITEABLE will come next service + */ + + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_CLIENT_RECEIVE: + case LWS_CALLBACK_CLIENT_RECEIVE_PONG: + gettimeofday(&tv, NULL); + iv = (tv.tv_sec * 1000000) + tv.tv_usec; + + psd->rx_count++; + + shift = 56; + p = in; + l = 0; + + while (shift >= 0) { + l |= ((lws_intptr_t)*p++) << shift; + shift -= 8; + } + + /* find it in the ringbuffer, look backwards from head */ + n = psd->ringbuffer_head; + while (!match) { + + if (psd->ringbuffer[n].index == l) { + psd->ringbuffer[n].seen++; + match = 1; + continue; + } + + if (n == psd->ringbuffer_tail) { + match = -1; + continue; + } + + if (n == 0) + n = PING_RINGBUFFER_SIZE - 1; + else + n--; + } + + if (match < 1) { + + if (!flood) + fprintf(stderr, "%d bytes from %s: req=%ld " + "time=(unknown)\n", (int)len, address, + (long)l); + else + fprintf(stderr, "\b \b"); + + break; + } + + if (psd->ringbuffer[n].seen > 1) + fprintf(stderr, "DUP! "); + + if ((iv - psd->ringbuffer[n].issue_timestamp) < rtt_min) + rtt_min = iv - psd->ringbuffer[n].issue_timestamp; + + if ((iv - psd->ringbuffer[n].issue_timestamp) > rtt_max) + rtt_max = iv - psd->ringbuffer[n].issue_timestamp; + + rtt_avg += iv - psd->ringbuffer[n].issue_timestamp; + global_rx_count++; + + if (!flood) + fprintf(stderr, "%d bytes from %s: req=%ld " + "time=%lu.%lums\n", (int)len, address, (long)l, + (iv - psd->ringbuffer[n].issue_timestamp) / 1000, + ((iv - psd->ringbuffer[n].issue_timestamp) / 100) % 10); + else + fprintf(stderr, "\b \b"); + break; + + case LWS_CALLBACK_CLIENT_WRITEABLE: + + shift = 56; + p = &pingbuf[LWS_PRE]; + + /* 64-bit ping index in network byte order */ + + while (shift >= 0) { + *p++ = (unsigned char)(psd->ping_index >> shift); + shift -= 8; + } + + while ((unsigned int)(p - &pingbuf[LWS_PRE]) < size) + *p++ = 0; + + gettimeofday(&tv, NULL); + + psd->ringbuffer[psd->ringbuffer_head].issue_timestamp = + (tv.tv_sec * 1000000) + tv.tv_usec; + psd->ringbuffer[psd->ringbuffer_head].index = (unsigned long)psd->ping_index++; + psd->ringbuffer[psd->ringbuffer_head].seen = 0; + + if (psd->ringbuffer_head == PING_RINGBUFFER_SIZE - 1) + psd->ringbuffer_head = 0; + else + psd->ringbuffer_head++; + + /* snip any re-used tail so we keep to the ring length */ + + if (psd->ringbuffer_tail == psd->ringbuffer_head) { + if (psd->ringbuffer_tail == PING_RINGBUFFER_SIZE - 1) + psd->ringbuffer_tail = 0; + else + psd->ringbuffer_tail++; + } + + global_tx_count++; + + if (use_mirror) + n = lws_write(wsi, + &pingbuf[LWS_PRE], + size, write_options | LWS_WRITE_BINARY); + else + n = lws_write(wsi, + &pingbuf[LWS_PRE], + size, write_options | LWS_WRITE_PING); + + if (n < 0) + return -1; + if (n < (int)size) { + lwsl_err("Partial write\n"); + return -1; + } + + if (flood && + (psd->ping_index - psd->rx_count) < (screen_width - 1)) + fprintf(stderr, "."); + break; + + default: + break; + } + + return 0; +} + + +/* list of supported protocols and callbacks */ + +static struct lws_protocols protocols[] = { + + { + "lws-mirror-protocol", + callback_lws_mirror, + sizeof (struct per_session_data__ping), + }, + { + NULL, NULL, 0/* end of list */ + } +}; + +static const struct lws_extension exts[] = { + { + "permessage-deflate", + lws_extension_callback_pm_deflate, + "permessage-deflate; client_no_context_takeover; client_max_window_bits" + }, + { + "deflate-frame", + lws_extension_callback_pm_deflate, + "deflate_frame" + }, + { NULL, NULL, NULL /* terminator */ } +}; + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "debug", required_argument, NULL, 'd' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 't' }, + { "interval", required_argument, NULL, 'i' }, + { "size", required_argument, NULL, 's' }, + { "protocol", required_argument, NULL, 'n' }, + { "flood", no_argument, NULL, 'f' }, + { "mirror", no_argument, NULL, 'm' }, + { "replicate", required_argument, NULL, 'r' }, + { "killmask", no_argument, NULL, 'k' }, + { "version", required_argument, NULL, 'v' }, + { NULL, 0, 0, 0 } +}; + +#ifndef _WIN32 +static void +signal_handler(int sig, siginfo_t *si, void *v) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + interrupted_time = (tv.tv_sec * 1000000) + tv.tv_usec; +} +#endif + +int main(int argc, char **argv) +{ + int n = 0; + int port = 7681; + int use_ssl = 0; + struct lws_context *context; + char protocol_name[256], ads_port[300]; + char ip[30]; +#ifndef _WIN32 + struct sigaction sa; + struct winsize w; +#endif + struct timeval tv; + unsigned long oldus = 0; + unsigned long l; + int ietf_version = -1; + struct lws_context_creation_info info; + struct lws_client_connect_info i; + + memset(&info, 0, sizeof info); + + if (argc < 2) + goto usage; + + while (n >= 0) { + n = getopt_long(argc, argv, "v:kr:hmfts:n:i:p:d:", options, NULL); + if (n < 0) + continue; + switch (n) { + case 'd': + lws_set_log_level(atoi(optarg), NULL); + break; + case 'm': + use_mirror = 1; + break; + case 't': + use_ssl = 2; /* 2 = allow selfsigned */ + break; + case 'p': + port = atoi(optarg); + break; + case 'n': + strncpy(protocol_name, optarg, sizeof protocol_name); + protocol_name[(sizeof protocol_name) - 1] = '\0'; + protocols[PROTOCOL_LWS_MIRROR].name = protocol_name; + break; + case 'i': + interval_us = (unsigned int)(1000000.0 * atof(optarg)); + break; + case 's': + size = atoi(optarg); + break; + case 'f': + flood = 1; + break; + case 'r': + clients = atoi(optarg); + if (clients > MAX_PING_CLIENTS || clients < 1) { + fprintf(stderr, "Max clients supported = %d\n", + MAX_PING_CLIENTS); + return 1; + } + break; + case 'k': + write_options = LWS_WRITE_CLIENT_IGNORE_XOR_MASK; + break; + case 'v': + ietf_version = atoi(optarg); + break; + + case 'h': + goto usage; + } + } + + if (!use_mirror) { + if (size > MAX_PING_PAYLOAD) { + fprintf(stderr, "Max ping opcode payload size %d\n", + MAX_PING_PAYLOAD); + return 1; + } + } else { + if (size > MAX_MIRROR_PAYLOAD) { + fprintf(stderr, "Max mirror payload size %d\n", + MAX_MIRROR_PAYLOAD); + return 1; + } + } + +#ifndef _WIN32 + if (isatty(STDOUT_FILENO)) + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) + if (w.ws_col > 0) + screen_width = w.ws_col; +#endif + + info.port = CONTEXT_PORT_NO_LISTEN; + info.protocols = protocols; + info.extensions = exts; + + info.gid = -1; + info.uid = -1; + + if (use_ssl) + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + + context = lws_create_context(&info); + if (context == NULL) { + fprintf(stderr, "Creating libwebsocket context failed\n"); + return 1; + } + + /* create client websockets using dumb increment protocol */ + + address = argv[optind]; + lws_snprintf(ads_port, sizeof(ads_port), "%s:%u", + address, port & 65535); + lwsl_notice("Connecting to %s...\n", ads_port); + memset(&i, 0, sizeof(i)); + i.context = context; + i.address = address; + i.port = port; + i.ssl_connection = use_ssl; + i.path = "/"; + i.host = ads_port; + i.origin = ads_port; + i.protocol = protocols[PROTOCOL_LWS_MIRROR].name; + i.ietf_version_or_minus_one = ietf_version; + + for (n = 0; n < clients; n++) { + ping_wsi[n] = lws_client_connect_via_info(&i); + if (ping_wsi[n] == NULL) { + lwsl_err("client %d failed to connect\n", n); + return 1; + } + } + + lws_get_peer_addresses(ping_wsi[0], lws_get_socket_fd(ping_wsi[0]), + peer_name, sizeof peer_name, ip, sizeof ip); + + lwsl_notice("libwebsockets test server ping - license LGPL2.1+SLE\n"); + lwsl_notice("(C) Copyright 2010-2016 Andy Green \n"); + fprintf(stderr, "Websocket PING %s (%s) %d bytes of data.\n", + peer_name, ip, size); + +#ifndef _WIN32 + /* set the ^C handler */ + sa.sa_sigaction = signal_handler; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); +#endif + + gettimeofday(&tv, NULL); + started = (tv.tv_sec * 1000000) + tv.tv_usec; + + /* service loop */ + + n = 0; + while (n >= 0) { + + gettimeofday(&tv, NULL); + l = (tv.tv_sec * 1000000) + tv.tv_usec; + + /* servers can hang up on us */ + + if (clients == 0) { + n = -1; + continue; + } + + if (!interrupted_time) { + if ((l - oldus) > interval_us) { + for (n = 0; n < clients; n++) + lws_callback_on_writable(ping_wsi[n]); + oldus = l; + } + } else + + /* allow time for in-flight pongs to come */ + + if ((l - interrupted_time) > 250000) { + n = -1; + continue; + } + + if (!interval_us) + n = lws_service(context, 0); + else + n = lws_service(context, 1); + } + + /* stats */ + + if (global_rx_count && global_tx_count) + fprintf(stderr, "\n--- %s websocket ping statistics " + "using %d connections ---\n" + "%lu packets transmitted, %lu received, " + "%lu%% packet loss, time %ldms\n" + "rtt min/avg/max = %0.3f/%0.3f/%0.3f ms\n" + "payload bandwidth average %0.3f KiBytes/sec\n", + peer_name, clients, global_tx_count, global_rx_count, + ((global_tx_count - global_rx_count) * 100) / global_tx_count, + (l - started) / 1000, + ((double)rtt_min) / 1000.0, + ((double)rtt_avg / global_rx_count) / 1000.0, + ((double)rtt_max) / 1000.0, + ((double)global_rx_count * (double)size) / + ((double)(l - started) / 1000000.0) / 1024.0); + + lws_context_destroy(context); + + return 0; + +usage: + fprintf(stderr, "Usage: libwebsockets-test-ping " + " [--port=

] " + "[--ssl] [--interval=] " + "[--size=] " + "[--protocol=] " + "[--mirror] " + "[--replicate=clients>] " + "[--version ] " + "[-d ]" + "\n"); + return 1; +} diff -Nru libwebsockets-4.0.20/test-apps/test-server.c libwebsockets-2.4.2/test-apps/test-server.c --- libwebsockets-4.0.20/test-apps/test-server.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-server.c 2018-03-08 10:28:37.000000000 +0000 @@ -1,7 +1,7 @@ /* * libwebsockets-test-server - libwebsockets test implementation * - * Written in 2010-2019 by Andy Green + * Copyright (C) 2010-2017 Andy Green * * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. @@ -18,24 +18,13 @@ * Public Domain. */ -#include -#include -#include -#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32) -#include -#endif -#include - -#if defined(WIN32) || defined(_WIN32) -#else -#include -#endif +#include "test-server.h" int close_testing; int max_poll_elements; -int debug_level = LLL_USER | 7; +int debug_level = 7; -#if defined(LWS_WITH_EXTERNAL_POLL) +#ifdef EXTERNAL_POLL struct lws_pollfd *pollfds; int *fd_lookup; int count_pollfds; @@ -44,12 +33,11 @@ struct lws_vhost *dynamic_vhost; struct lws_context *context; struct lws_plat_file_ops fops_plat; -static int test_options; /* http server gets files from this path */ #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server" char *resource_path = LOCAL_RESOURCE_PATH; -#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param) +#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param) char crl_path[1024] = ""; #endif @@ -78,85 +66,75 @@ */ #define LWS_PLUGIN_STATIC -#if defined(LWS_ROLE_WS) #include "../plugins/protocol_lws_mirror.c" #include "../plugins/protocol_lws_status.c" -#include "../plugins/protocol_dumb_increment.c" -#endif -#include "../plugins/protocol_post_demo.c" - -static int -lws_callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - const unsigned char *c; - char buf[1024]; - int n = 0, hlen; - - switch (reason) { - case LWS_CALLBACK_HTTP: - - /* non-mount-handled accesses will turn up here */ - - /* dump the headers */ - - do { - c = lws_token_to_string(n); - if (!c) { - n++; - continue; - } - - hlen = lws_hdr_total_length(wsi, n); - if (!hlen || hlen > (int)sizeof(buf) - 1) { - n++; - continue; - } +#include "../plugins/protocol_lws_meta.c" - if (lws_hdr_copy(wsi, buf, sizeof buf, n) < 0) - fprintf(stderr, " %s (too big)\n", (char *)c); - else { - buf[sizeof(buf) - 1] = '\0'; +/* singlethreaded version --> no locks */ - fprintf(stderr, " %s = %s\n", (char *)c, buf); - } - n++; - } while (c); - - /* dump the individual URI Arg parameters */ - - n = 0; - while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf), - WSI_TOKEN_HTTP_URI_ARGS, n) > 0) { - lwsl_notice("URI Arg %d: %s\n", ++n, buf); - } +void test_server_lock(int care) +{ +} +void test_server_unlock(int care) +{ +} - if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL)) - return -1; +/* + * This demo server shows how to use libwebsockets for one or more + * websocket protocols in the same server + * + * It defines the following websocket protocols: + * + * dumb-increment-protocol: once the socket is opened, an incrementing + * ascii string is sent down it every 50ms. + * If you send "reset\n" on the websocket, then + * the incrementing number is reset to 0. + * + * lws-mirror-protocol: copies any received packet to every connection also + * using this protocol, including the sender + * + * lws-status: informs connected browsers of who else is + * connected. + */ - if (lws_http_transaction_completed(wsi)) - return -1; +enum demo_protocols { + /* always first */ + PROTOCOL_HTTP = 0, + + PROTOCOL_DUMB_INCREMENT, + PROTOCOL_LWS_MIRROR, + PROTOCOL_LWS_STATUS, - return 0; - default: - break; - } + PROTOCOL_LWS_META, - return lws_callback_http_dummy(wsi, reason, user, in, len); -} + /* always last */ + DEMO_PROTOCOL_COUNT +}; /* list of supported protocols and callbacks */ static struct lws_protocols protocols[] = { /* first protocol must always be HTTP handler */ - { "http-only", lws_callback_http, 0, 0, }, -#if defined(LWS_ROLE_WS) - LWS_PLUGIN_PROTOCOL_DUMB_INCREMENT, + { + "http-only", /* name */ + callback_http, /* callback */ + sizeof (struct per_session_data__http), /* per_session_data_size */ + 0, /* max frame size / rx buffer */ + }, + { + "dumb-increment-protocol", + callback_dumb_increment, + sizeof(struct per_session_data__dumb_increment), + 10, /* rx buf size must be >= permessage-deflate rx size + * dumb-increment only sends very small packets, so we set + * this accordingly. If your protocol will send bigger + * things, adjust this to match */ + }, LWS_PLUGIN_PROTOCOL_MIRROR, LWS_PLUGIN_PROTOCOL_LWS_STATUS, -#endif - LWS_PLUGIN_PROTOCOL_POST_DEMO, + + LWS_PLUGIN_PROTOCOL_LWS_META, { NULL, NULL, 0, 0 } /* terminator */ }; @@ -196,7 +174,6 @@ * port + 1 */ dynamic_vhost_enable ^= 1; - lws_cancel_service(context); lwsl_notice("SIGUSR1: dynamic_vhost_enable: %d\n", dynamic_vhost_enable); return; @@ -212,100 +189,16 @@ lws_extension_callback_pm_deflate, "permessage-deflate" }, + { + "deflate-frame", + lws_extension_callback_pm_deflate, + "deflate_frame" + }, { NULL, NULL, NULL /* terminator */ } }; -/* - * mount handlers for sections of the URL space - */ - -static const struct lws_http_mount mount_ziptest = { - NULL, /* linked-list pointer to next*/ - "/ziptest", /* mountpoint in URL namespace on this vhost */ - LOCAL_RESOURCE_PATH"/candide.zip", /* handler */ - NULL, /* default filename if none given */ - NULL, - NULL, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - LWSMPRO_FILE, /* origin points to a callback */ - 8, /* strlen("/ziptest"), ie length of the mountpoint */ - NULL, - - { NULL, NULL } // sentinel -}; -static const struct lws_http_mount mount_post = { - (struct lws_http_mount *)&mount_ziptest, /* linked-list pointer to next*/ - "/formtest", /* mountpoint in URL namespace on this vhost */ - "protocol-post-demo", /* handler */ - NULL, /* default filename if none given */ - NULL, - NULL, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - LWSMPRO_CALLBACK, /* origin points to a callback */ - 9, /* strlen("/formtest"), ie length of the mountpoint */ - NULL, - - { NULL, NULL } // sentinel -}; - -/* - * mount a filesystem directory into the URL space at / - * point it to our /usr/share directory with our assets in - * stuff from here is autoserved by the library - */ -static const struct lws_http_mount mount = { - (struct lws_http_mount *)&mount_post, /* linked-list pointer to next*/ - "/", /* mountpoint in URL namespace on this vhost */ - LOCAL_RESOURCE_PATH, /* where to go on the filesystem for that */ - "test.html", /* default filename if none given */ - NULL, - NULL, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - LWSMPRO_FILE, /* mount type is a directory in a filesystem */ - 1, /* strlen("/"), ie length of the mountpoint */ - NULL, - - { NULL, NULL } // sentinel -}; - -static const struct lws_protocol_vhost_options pvo_options = { - NULL, - NULL, - "options", /* pvo name */ - (void *)&test_options /* pvo value */ -}; - -static const struct lws_protocol_vhost_options pvo = { - NULL, /* "next" pvo linked-list */ - &pvo_options, /* "child" pvo linked-list */ - "dumb-increment-protocol", /* protocol name we belong to on this vhost */ - "" /* ignored */ -}; - -#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32) static struct option options[] = { { "help", no_argument, NULL, 'h' }, { "debug", required_argument, NULL, 'd' }, @@ -317,27 +210,27 @@ { "ssl-cert", required_argument, NULL, 'C' }, { "ssl-key", required_argument, NULL, 'K' }, { "ssl-ca", required_argument, NULL, 'A' }, -#if defined(LWS_WITH_TLS) +#if defined(LWS_OPENSSL_SUPPORT) { "ssl-verify-client", no_argument, NULL, 'v' }, #if defined(LWS_HAVE_SSL_CTX_set1_param) { "ssl-crl", required_argument, NULL, 'R' }, #endif #endif { "libev", no_argument, NULL, 'e' }, - { "unix-socket", required_argument, NULL, 'U' }, #ifndef LWS_NO_DAEMONIZE { "daemonize", no_argument, NULL, 'D' }, #endif + { "resource_path", required_argument, NULL, 'r' }, { "pingpong-secs", required_argument, NULL, 'P' }, { NULL, 0, 0, 0 } }; -#endif int main(int argc, char **argv) { struct lws_context_creation_info info; struct lws_vhost *vhost; char interface_name[128] = ""; + unsigned int ms, oldms = 0; const char *iface = NULL; char cert_path[1024] = ""; char key_path[1024] = ""; @@ -347,6 +240,14 @@ int pp_secs = 0; int opts = 0; int n = 0; +#ifndef _WIN32 +/* LOG_PERROR is not POSIX standard, and may not be portable */ +#ifdef __sun + int syslog_options = LOG_PID; +#else + int syslog_options = LOG_PID | LOG_PERROR; +#endif +#endif #ifndef LWS_NO_DAEMONIZE int daemonize = 0; #endif @@ -359,11 +260,7 @@ info.port = 7681; while (n >= 0) { -#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32) - n = getopt_long(argc, argv, "eci:hsap:d:DC:K:A:R:vu:g:P:kU:n", options, NULL); -#else - n = getopt(argc, argv, "eci:hsap:d:DC:K:A:R:vu:g:P:kU:n"); -#endif + n = getopt_long(argc, argv, "eci:hsap:d:Dr:C:K:A:R:vu:g:P:k", options, NULL); if (n < 0) continue; switch (n) { @@ -373,6 +270,9 @@ #ifndef LWS_NO_DAEMONIZE case 'D': daemonize = 1; + #if !defined(_WIN32) && !defined(__sun) + syslog_options &= ~LOG_PERROR; + #endif break; #endif case 'u': @@ -384,10 +284,6 @@ case 'd': debug_level = atoi(optarg); break; - case 'n': - /* no dumb increment send */ - test_options |= 1; - break; case 's': use_ssl = 1; opts |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; @@ -399,13 +295,9 @@ info.port = atoi(optarg); break; case 'i': - lws_strncpy(interface_name, optarg, sizeof interface_name); - iface = interface_name; - break; - case 'U': - lws_strncpy(interface_name, optarg, sizeof interface_name); + strncpy(interface_name, optarg, sizeof interface_name); + interface_name[(sizeof interface_name) - 1] = '\0'; iface = interface_name; - opts |= LWS_SERVER_OPTION_UNIX_SOCK; break; case 'k': info.bind_iface = 1; @@ -420,20 +312,27 @@ "client after 50 dumb increments" "and suppresses lws_mirror spam\n"); break; + case 'r': + resource_path = optarg; + printf("Setting resource path to \"%s\"\n", resource_path); + break; case 'C': - lws_strncpy(cert_path, optarg, sizeof(cert_path)); + strncpy(cert_path, optarg, sizeof(cert_path) - 1); + cert_path[sizeof(cert_path) - 1] = '\0'; break; case 'K': - lws_strncpy(key_path, optarg, sizeof(key_path)); + strncpy(key_path, optarg, sizeof(key_path) - 1); + key_path[sizeof(key_path) - 1] = '\0'; break; case 'A': - lws_strncpy(ca_path, optarg, sizeof(ca_path)); + strncpy(ca_path, optarg, sizeof(ca_path) - 1); + ca_path[sizeof(ca_path) - 1] = '\0'; break; case 'P': pp_secs = atoi(optarg); lwsl_notice("Setting pingpong interval to %d\n", pp_secs); break; -#if defined(LWS_WITH_TLS) +#if defined(LWS_OPENSSL_SUPPORT) case 'v': use_ssl = 1; opts |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT; @@ -441,14 +340,16 @@ #if defined(LWS_HAVE_SSL_CTX_set1_param) case 'R': - lws_strncpy(crl_path, optarg, sizeof(crl_path)); + strncpy(crl_path, optarg, sizeof(crl_path) - 1); + crl_path[sizeof(crl_path) - 1] = '\0'; break; #endif #endif case 'h': fprintf(stderr, "Usage: test-server " "[--port=

] [--ssl] " - "[-d ]\n"); + "[-d ] " + "[--resource_path ]\n"); exit(1); } } @@ -472,19 +373,21 @@ signal(SIGUSR1, sighandler); #endif - /* tell the library what debug level to emit and to send it to stderr */ - lws_set_log_level(debug_level, NULL); +#ifndef _WIN32 + /* we will only try to log things according to our debug_level */ + setlogmask(LOG_UPTO (LOG_DEBUG)); + openlog("lwsts", syslog_options, LOG_DAEMON); +#endif - lwsl_notice("libwebsockets test server - license MIT\n"); - lwsl_notice("(C) Copyright 2010-2018 Andy Green \n"); + /* tell the library what debug level to emit and to send it to syslog */ + lws_set_log_level(debug_level, lwsl_emit_syslog); + + lwsl_notice("libwebsockets test server - license LGPL2.1+SLE\n"); + lwsl_notice("(C) Copyright 2010-2017 Andy Green \n"); printf("Using resource path \"%s\"\n", resource_path); -#if defined(LWS_WITH_EXTERNAL_POLL) -#if !defined(WIN32) && !defined(_WIN32) && !defined(__ANDROID__) +#ifdef EXTERNAL_POLL max_poll_elements = getdtablesize(); -#else - max_poll_elements = sysconf(_SC_OPEN_MAX); -#endif pollfds = malloc(max_poll_elements * sizeof (struct lws_pollfd)); fd_lookup = malloc(max_poll_elements * sizeof (int)); if (pollfds == NULL || fd_lookup == NULL) { @@ -522,6 +425,7 @@ } info.gid = gid; info.uid = uid; + info.max_http_header_pool = 256; info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_EXPLICIT_VHOSTS; info.extensions = exts; info.timeout_secs = 5; @@ -538,9 +442,8 @@ "!DHE-RSA-AES256-SHA256:" "!AES256-GCM-SHA384:" "!AES256-SHA256"; - info.mounts = &mount; info.ip_limit_ah = 24; /* for testing */ - info.ip_limit_wsi = 400; /* for testing */ + info.ip_limit_wsi = 105; /* for testing */ if (use_ssl) /* redirect guys coming on http */ @@ -552,8 +455,6 @@ return -1; } - info.pvo = &pvo; - vhost = lws_create_vhost(context, &info); if (!vhost) { lwsl_err("vhost creation failed\n"); @@ -568,7 +469,7 @@ info.port++; -#if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_TLS) +#if !defined(LWS_NO_CLIENT) && defined(LWS_OPENSSL_SUPPORT) lws_init_vhost_client_ssl(&info, vhost); #endif @@ -582,6 +483,9 @@ lws_get_fops(context)->open = test_server_fops_open; n = 0; +#ifdef EXTERNAL_POLL + int ms_1sec = 0; +#endif while (n >= 0 && !force_exit) { struct timeval tv; @@ -593,11 +497,19 @@ * as soon as it can take more packets (usually immediately) */ -#if defined(LWS_WITH_EXTERNAL_POLL) + ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + if ((ms - oldms) > 50) { + lws_callback_on_writable_all_protocol(context, + &protocols[PROTOCOL_DUMB_INCREMENT]); + oldms = ms; + } + +#ifdef EXTERNAL_POLL /* * this represents an existing server's single poll action * which also includes libwebsocket sockets */ + n = poll(pollfds, count_pollfds, 50); if (n < 0) continue; @@ -619,15 +531,25 @@ lwsl_notice("extpoll doing forced service!\n"); lws_service_tsi(context, -1, 0); } + } else { + /* no revents, but before polling again, make lws check for any timeouts */ + if (ms - ms_1sec > 1000) { + lwsl_notice("1 per sec\n"); + lws_service_fd(context, NULL); + ms_1sec = ms; + } } #else /* * If libwebsockets sockets are all we care about, * you can use this api which takes care of the poll() * and looping through finding who needed service. + * + * If no socket needs service, it'll return anyway after + * the number of ms in the second argument. */ - n = lws_service(context, 0); + n = lws_service(context, 50); #endif if (dynamic_vhost_enable && !dynamic_vhost) { @@ -642,7 +564,7 @@ } -#if defined(LWS_WITH_EXTERNAL_POLL) +#ifdef EXTERNAL_POLL done: #endif @@ -650,5 +572,9 @@ lwsl_notice("libwebsockets-test-server exited cleanly\n"); +#ifndef _WIN32 + closelog(); +#endif + return 0; } diff -Nru libwebsockets-4.0.20/test-apps/test-server-dumb-increment.c libwebsockets-2.4.2/test-apps/test-server-dumb-increment.c --- libwebsockets-4.0.20/test-apps/test-server-dumb-increment.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-server-dumb-increment.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,94 @@ +/* + * libwebsockets-test-server - libwebsockets test implementation + * + * Copyright (C) 2010-2016 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * The person who associated a work with this deed has dedicated + * the work to the public domain by waiving all of his or her rights + * to the work worldwide under copyright law, including all related + * and neighboring rights, to the extent allowed by law. You can copy, + * modify, distribute and perform the work, even for commercial purposes, + * all without asking permission. + * + * The test apps are intended to be adapted for use in your code, which + * may be proprietary. So unlike the library itself, they are licensed + * Public Domain. + */ +#include "test-server.h" + +/* dumb_increment protocol */ + +int +callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + unsigned char buf[LWS_PRE + 512]; + struct per_session_data__dumb_increment *pss = + (struct per_session_data__dumb_increment *)user; + unsigned char *p = &buf[LWS_PRE]; + int n, m; + + switch (reason) { + + case LWS_CALLBACK_ESTABLISHED: + pss->number = 0; + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + n = sprintf((char *)p, "%d", pss->number++); + m = lws_write(wsi, p, n, LWS_WRITE_TEXT); + if (m < n) { + lwsl_err("ERROR %d writing to di socket\n", n); + return -1; + } + if (close_testing && pss->number == 50) { + lwsl_info("close tesing limit, closing\n"); + return -1; + } + break; + + case LWS_CALLBACK_RECEIVE: + if (len < 6) + break; + if (strcmp((const char *)in, "reset\n") == 0) + pss->number = 0; + if (strcmp((const char *)in, "closeme\n") == 0) { + lwsl_notice("dumb_inc: closing as requested\n"); + lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY, + (unsigned char *)"seeya", 5); + return -1; + } + break; + /* + * this just demonstrates how to use the protocol filter. If you won't + * study and reject connections based on header content, you don't need + * to handle this callback + */ + case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: + dump_handshake_info(wsi); + /* you could return non-zero here and kill the connection */ + break; + + /* + * this just demonstrates how to handle + * LWS_CALLBACK_WS_PEER_INITIATED_CLOSE and extract the peer's close + * code and auxiliary data. You can just not handle it if you don't + * have a use for this. + */ + case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: + lwsl_notice("LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: len %lu\n", + (unsigned long)len); + for (n = 0; n < (int)len; n++) + lwsl_notice(" %d: 0x%02X\n", n, + ((unsigned char *)in)[n]); + break; + + default: + break; + } + + return 0; +} diff -Nru libwebsockets-4.0.20/test-apps/test-server.h libwebsockets-2.4.2/test-apps/test-server.h --- libwebsockets-4.0.20/test-apps/test-server.h 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-server.h 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,118 @@ +/* + * libwebsockets-test-server - libwebsockets test implementation + * + * Copyright (C) 2010-2016 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * The person who associated a work with this deed has dedicated + * the work to the public domain by waiving all of his or her rights + * to the work worldwide under copyright law, including all related + * and neighboring rights, to the extent allowed by law. You can copy, + * modify, distribute and perform the work, even for commercial purposes, + * all without asking permission. + * + * The test apps are intended to be adapted for use in your code, which + * may be proprietary. So unlike the library itself, they are licensed + * Public Domain. + */ + +#if defined(_WIN32) && defined(EXTERNAL_POLL) +#define WINVER 0x0600 +#define _WIN32_WINNT 0x0600 +#define poll(fdArray, fds, timeout) WSAPoll((LPWSAPOLLFD)(fdArray), (ULONG)(fds), (INT)(timeout)) +#endif + +#include "lws_config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../lib/libwebsockets.h" + +#ifdef _WIN32 +#include +#include "gettimeofday.h" +#else +#include +#include +#include +#endif + +extern int close_testing; +extern int max_poll_elements; + +#ifdef EXTERNAL_POLL +extern struct lws_pollfd *pollfds; +extern int *fd_lookup; +extern int count_pollfds; +#endif +extern volatile int force_exit; +extern struct lws_context *context; +extern char *resource_path; +#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param) +extern char crl_path[1024]; +#endif + +extern void test_server_lock(int care); +extern void test_server_unlock(int care); + +#ifndef __func__ +#define __func__ __FUNCTION__ +#endif + +struct per_session_data__http { + lws_fop_fd_t fop_fd; +#ifdef LWS_WITH_CGI + struct lws_cgi_args args; +#endif +#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT) + int reason_bf; +#endif + unsigned int client_finished:1; + + + struct lws_spa *spa; + char result[500 + LWS_PRE]; + int result_len; + + char filename[256]; + long file_length; + lws_filefd_type post_fd; +}; + +/* + * one of these is auto-created for each connection and a pointer to the + * appropriate instance is passed to the callback in the user parameter + * + * for this example protocol we use it to individualize the count for each + * connection. + */ + +#if !defined(DI_HANDLED_BY_PLUGIN) +struct per_session_data__dumb_increment { + int number; +}; +#endif + + +extern int +callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, + void *in, size_t len); + +#if !defined(DI_HANDLED_BY_PLUGIN) +extern int +callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len); +#endif + + +extern void +dump_handshake_info(struct lws *wsi); diff -Nru libwebsockets-4.0.20/test-apps/test-server-http.c libwebsockets-2.4.2/test-apps/test-server-http.c --- libwebsockets-4.0.20/test-apps/test-server-http.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-server-http.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,812 @@ +/* + * libwebsockets-test-server - libwebsockets test implementation + * + * Copyright (C) 2010-2016 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * The person who associated a work with this deed has dedicated + * the work to the public domain by waiving all of his or her rights + * to the work worldwide under copyright law, including all related + * and neighboring rights, to the extent allowed by law. You can copy, + * modify, distribute and perform the work, even for commercial purposes, + * all without asking permission. + * + * The test apps are intended to be adapted for use in your code, which + * may be proprietary. So unlike the library itself, they are licensed + * Public Domain. + */ +#include "test-server.h" + +/* + * This demo server shows how to use libwebsockets for one or more + * websocket protocols in the same server + * + * It defines the following websocket protocols: + * + * dumb-increment-protocol: once the socket is opened, an incrementing + * ascii string is sent down it every 50ms. + * If you send "reset\n" on the websocket, then + * the incrementing number is reset to 0. + * + * lws-mirror-protocol: copies any received packet to every connection also + * using this protocol, including the sender + */ + +#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param) +/* location of the certificate revocation list */ +extern char crl_path[1024]; +#endif + +extern int debug_level; + +enum demo_protocols { + /* always first */ + PROTOCOL_HTTP = 0, + + PROTOCOL_DUMB_INCREMENT, + PROTOCOL_LWS_MIRROR, + + /* always last */ + DEMO_PROTOCOL_COUNT +}; + +/* + * We take a strict whitelist approach to stop ../ attacks + */ +struct serveable { + const char *urlpath; + const char *mimetype; +}; + +/* + * this is just an example of parsing handshake headers, you don't need this + * in your code unless you will filter allowing connections by the header + * content + */ +void +dump_handshake_info(struct lws *wsi) +{ + int n = 0, len; + char buf[256]; + const unsigned char *c; + + do { + c = lws_token_to_string(n); + if (!c) { + n++; + continue; + } + + len = lws_hdr_total_length(wsi, n); + if (!len || len > sizeof(buf) - 1) { + n++; + continue; + } + + lws_hdr_copy(wsi, buf, sizeof buf, n); + buf[sizeof(buf) - 1] = '\0'; + + fprintf(stderr, " %s = %s\n", (char *)c, buf); + n++; + } while (c); +} + +const char * get_mimetype(const char *file) +{ + int n = strlen(file); + + if (n < 5) + return NULL; + + if (!strcmp(&file[n - 4], ".ico")) + return "image/x-icon"; + + if (!strcmp(&file[n - 4], ".png")) + return "image/png"; + + if (!strcmp(&file[n - 5], ".html")) + return "text/html"; + + if (!strcmp(&file[n - 4], ".css")) + return "text/css"; + + if (!strcmp(&file[n - 3], ".js")) + return "text/javascript"; + + return NULL; +} + + +static const char * const param_names[] = { + "text", + "send", + "file", + "upload", +}; + +enum enum_param_names { + EPN_TEXT, + EPN_SEND, + EPN_FILE, + EPN_UPLOAD, +}; + +static int +file_upload_cb(void *data, const char *name, const char *filename, + char *buf, int len, enum lws_spa_fileupload_states state) +{ + struct per_session_data__http *pss = + (struct per_session_data__http *)data; + int n; + + (void)n; + + switch (state) { + case LWS_UFS_OPEN: + strncpy(pss->filename, filename, sizeof(pss->filename) - 1); + /* we get the original filename in @filename arg, but for + * simple demo use a fixed name so we don't have to deal with + * attacks */ + pss->post_fd = (lws_filefd_type)open("/tmp/post-file", + O_CREAT | O_TRUNC | O_RDWR, 0600); + break; + case LWS_UFS_FINAL_CONTENT: + case LWS_UFS_CONTENT: + if (len) { + pss->file_length += len; + + /* if the file length is too big, drop it */ + if (pss->file_length > 100000) + return 1; + + n = write((int)pss->post_fd, buf, len); + lwsl_notice("%s: write %d says %d\n", __func__, len, n); + } + if (state == LWS_UFS_CONTENT) + break; + close((int)pss->post_fd); + pss->post_fd = LWS_INVALID_FILE; + break; + } + + return 0; +} + +/* this protocol server (always the first one) handles HTTP, + * + * Some misc callbacks that aren't associated with a protocol also turn up only + * here on the first protocol server. + */ + +int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, + void *in, size_t len) +{ + struct per_session_data__http *pss = + (struct per_session_data__http *)user; + unsigned char buffer[4096 + LWS_PRE]; + lws_filepos_t amount, file_len, sent; + char leaf_path[1024]; + const char *mimetype; + char *other_headers; + unsigned char *end, *start; + struct timeval tv; + unsigned char *p; +#ifndef LWS_NO_CLIENT + struct per_session_data__http *pss1; + struct lws *wsi1; +#endif + char buf[256]; + char b64[64]; + int n, m; +#ifdef EXTERNAL_POLL + struct lws_pollargs *pa = (struct lws_pollargs *)in; +#endif + + + switch (reason) { + case LWS_CALLBACK_HTTP: + + lwsl_info("lws_http_serve: %s\n", (const char *)in); + + if (debug_level & LLL_INFO) { + dump_handshake_info(wsi); + + /* dump the individual URI Arg parameters */ + n = 0; + while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf), + WSI_TOKEN_HTTP_URI_ARGS, n) > 0) { + lwsl_notice("URI Arg %d: %s\n", ++n, buf); + } + } + + if (lws_get_peer_simple(wsi, buf, sizeof(buf))) + lwsl_info("HTTP connect from %s\n", buf); + + if (len < 1) { + lws_return_http_status(wsi, + HTTP_STATUS_BAD_REQUEST, NULL); + goto try_to_reuse; + } + +#if !defined(LWS_NO_CLIENT) && defined(LWS_OPENSSL_SUPPORT) + if (!strncmp(in, "/proxytest", 10)) { + struct lws_client_connect_info i; + char *rootpath = "/git/"; + const char *p = (const char *)in; + + if (lws_get_child(wsi)) + break; + + pss->client_finished = 0; + memset(&i, 0, sizeof(i)); + i.context = lws_get_context(wsi); + i.address = "libwebsockets.org"; + i.port = 443; + i.ssl_connection = 1; + if (p[10]) + i.path = (char *)in + 10; + else + i.path = rootpath; + i.host = i.address; + i.origin = NULL; + i.method = "GET"; + i.parent_wsi = wsi; + i.uri_replace_from = "libwebsockets.org/git/"; + i.uri_replace_to = "/proxytest/"; + + if (!lws_client_connect_via_info(&i)) { + lwsl_err("proxy connect fail\n"); + break; + } + + break; + } +#endif + +#if 1 + /* this example server has no concept of directories */ + if (strchr((const char *)in + 1, '/')) { + lws_return_http_status(wsi, HTTP_STATUS_NOT_ACCEPTABLE, NULL); + goto try_to_reuse; + } +#endif + + /* if a legal POST URL, let it continue and accept data */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) { + n = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_POST_URI); + if (n < 0) + return -1; + if (strcmp(buf, "/formtest")) { + lws_return_http_status(wsi, HTTP_STATUS_NOT_ACCEPTABLE, NULL); + return -1; + } + return 0; + } + + /* check for the "send a big file by hand" example case */ + + if (!strcmp((const char *)in, "/leaf.jpg")) { + lws_fop_flags_t flags = LWS_O_RDONLY; + + if (strlen(resource_path) > sizeof(leaf_path) - 10) + return -1; + sprintf(leaf_path, "%s/leaf.jpg", resource_path); + + /* well, let's demonstrate how to send the hard way */ + + p = buffer + LWS_PRE; + end = p + sizeof(buffer) - LWS_PRE; + + pss->fop_fd = lws_vfs_file_open( + lws_get_fops(lws_get_context(wsi)), + leaf_path, &flags); + if (!pss->fop_fd) { + lwsl_err("failed to open file %s\n", leaf_path); + return -1; + } + file_len = lws_vfs_get_length(pss->fop_fd); + + /* + * we will send a big jpeg file, but it could be + * anything. Set the Content-Type: appropriately + * so the browser knows what to do with it. + * + * Notice we use the APIs to build the header, which + * will do the right thing for HTTP 1/1.1 and HTTP2 + * depending on what connection it happens to be working + * on + */ + if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) + return 1; + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, + (unsigned char *)"libwebsockets", + 13, &p, end)) + return 1; + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)"image/jpeg", + 10, &p, end)) + return 1; + if (lws_add_http_header_content_length(wsi, + file_len, &p, + end)) + return 1; + if (lws_finalize_http_header(wsi, &p, end)) + return 1; + + /* + * send the http headers... + * this won't block since it's the first payload sent + * on the connection since it was established + * (too small for partial) + * + * Notice they are sent using LWS_WRITE_HTTP_HEADERS + * which also means you can't send body too in one step, + * this is mandated by changes in HTTP2 + */ + + *p = '\0'; + lwsl_info("%s\n", buffer + LWS_PRE); + + n = lws_write(wsi, buffer + LWS_PRE, + p - (buffer + LWS_PRE), + LWS_WRITE_HTTP_HEADERS); + if (n < 0) { + lws_vfs_file_close(&pss->fop_fd); + return -1; + } + /* + * book us a LWS_CALLBACK_HTTP_WRITEABLE callback + */ + lws_callback_on_writable(wsi); + break; + } + + /* if not, send a file the easy way */ + if (!strncmp(in, "/cgit-data/", 11)) { + in = (char *)in + 11; + strcpy(buf, "/usr/share/cgit"); + } else + strcpy(buf, resource_path); + + if (strcmp(in, "/")) { + if (*((const char *)in) != '/') + strcat(buf, "/"); + strncat(buf, in, sizeof(buf) - strlen(buf) - 1); + } else /* default file to serve */ + strcat(buf, "/test.html"); + buf[sizeof(buf) - 1] = '\0'; + + /* refuse to serve files we don't understand */ + mimetype = get_mimetype(buf); + if (!mimetype) { + lwsl_err("Unknown mimetype for %s\n", buf); + lws_return_http_status(wsi, + HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, "Unknown Mimetype"); + return -1; + } + + /* demonstrates how to set a cookie on / */ + + other_headers = leaf_path; + p = (unsigned char *)leaf_path; + if (!strcmp((const char *)in, "/") && + !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) { + /* this isn't very unguessable but it'll do for us */ + gettimeofday(&tv, NULL); + n = sprintf(b64, "test=LWS_%u_%u_COOKIE;Max-Age=360000", + (unsigned int)tv.tv_sec, + (unsigned int)tv.tv_usec); + + if (lws_add_http_header_by_name(wsi, + (unsigned char *)"set-cookie:", + (unsigned char *)b64, n, &p, + (unsigned char *)leaf_path + sizeof(leaf_path))) + return 1; + } +#if !defined(LWS_WITH_HTTP2) + if (lws_is_ssl(wsi) && lws_add_http_header_by_name(wsi, + (unsigned char *) + "Strict-Transport-Security:", + (unsigned char *) + "max-age=15768000 ; " + "includeSubDomains", 36, &p, + (unsigned char *)leaf_path + + sizeof(leaf_path))) + return 1; +#endif + n = (char *)p - leaf_path; + + n = lws_serve_http_file(wsi, buf, mimetype, other_headers, n); + if (n < 0) + return -1; /* error*/ + + /* + * notice that the sending of the file completes asynchronously, + * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when + * it's done. That's the case even if we just completed the + * send, so wait for that. + */ + break; + + case LWS_CALLBACK_CLIENT_RECEIVE: + ((char *)in)[len] = '\0'; + lwsl_info("rx %d '%s'\n", (int)len, (char *)in); + break; + + case LWS_CALLBACK_HTTP_BODY: + /* create the POST argument parser if not already existing */ + if (!pss->spa) { + pss->spa = lws_spa_create(wsi, param_names, + ARRAY_SIZE(param_names), 1024, + file_upload_cb, pss); + if (!pss->spa) + return -1; + + pss->filename[0] = '\0'; + pss->file_length = 0; + } + + /* let it parse the POST data */ + if (lws_spa_process(pss->spa, in, len)) + return -1; + break; + + case LWS_CALLBACK_HTTP_BODY_COMPLETION: + lwsl_debug("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); + /* + * the whole of the sent body arrived, + * respond to the client with a redirect to show the + * results + */ + + /* call to inform no more payload data coming */ + lws_spa_finalize(pss->spa); + + p = (unsigned char *)pss->result + LWS_PRE; + end = p + sizeof(pss->result) - LWS_PRE - 1; + p += sprintf((char *)p, + "

Form results (after urldecoding)

" + ""); + + for (n = 0; n < ARRAY_SIZE(param_names); n++) + p += lws_snprintf((char *)p, end - p, + "", + param_names[n], + lws_spa_get_length(pss->spa, n), + lws_spa_get_string(pss->spa, n)); + + p += lws_snprintf((char *)p, end - p, "
NameLengthValue
%s%d%s

filename: %s, length %ld", + pss->filename, pss->file_length); + + p += lws_snprintf((char *)p, end - p, ""); + pss->result_len = p - (unsigned char *)(pss->result + LWS_PRE); + + p = buffer + LWS_PRE; + start = p; + end = p + sizeof(buffer) - LWS_PRE; + + if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &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; + if (lws_add_http_header_content_length(wsi, pss->result_len, &p, end)) + return 1; + if (lws_finalize_http_header(wsi, &p, end)) + return 1; + + n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS); + if (n < 0) + return 1; + + n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE, + pss->result_len, LWS_WRITE_HTTP); + if (n < 0) + return 1; + goto try_to_reuse; + case LWS_CALLBACK_HTTP_DROP_PROTOCOL: + lwsl_debug("LWS_CALLBACK_HTTP_DROP_PROTOCOL\n"); + + /* called when our wsi user_space is going to be destroyed */ + if (pss->spa) { + lws_spa_destroy(pss->spa); + pss->spa = NULL; + } + break; + case LWS_CALLBACK_HTTP_FILE_COMPLETION: + goto try_to_reuse; + + case LWS_CALLBACK_HTTP_WRITEABLE: + lwsl_info("LWS_CALLBACK_HTTP_WRITEABLE: %p\n", wsi); + + if (pss->client_finished) + return -1; + + if (!lws_get_child(wsi) && !pss->fop_fd) + goto try_to_reuse; + +#ifndef LWS_NO_CLIENT + if (pss->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) { + char *px = buf + LWS_PRE; + int lenx = sizeof(buf) - LWS_PRE; + /* + * our sink is writeable and our source has something + * to read. So read a lump of source material of + * suitable size to send or what's available, whichever + * is the smaller. + */ + + + pss->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY; + wsi1 = lws_get_child(wsi); + if (!wsi1) + break; + if (lws_http_client_read(wsi1, &px, &lenx) < 0) + return -1; + + if (pss->client_finished) + return -1; + + break; + } + + if (lws_get_child(wsi)) + break; + +#endif + /* + * we can send more of whatever it is we were sending + */ + sent = 0; + do { + /* we'd like the send this much */ + n = sizeof(buffer) - LWS_PRE; + + /* but if the peer told us he wants less, we can adapt */ + m = lws_get_peer_write_allowance(wsi); + + /* -1 means not using a protocol that has this info */ + if (m == 0) + /* right now, peer can't handle anything */ + goto later; + + if (m != -1 && m < n) + /* he couldn't handle that much */ + n = m; + + n = lws_vfs_file_read(pss->fop_fd, + &amount, buffer + LWS_PRE, n); + /* problem reading, close conn */ + if (n < 0) { + lwsl_err("problem reading file\n"); + goto bail; + } + n = (int)amount; + /* sent it all, close conn */ + if (n == 0) + goto penultimate; + /* + * To support HTTP2, must take care about preamble space + * + * identification of when we send the last payload frame + * is handled by the library itself if you sent a + * content-length header + */ + m = lws_write(wsi, buffer + LWS_PRE, n, LWS_WRITE_HTTP); + if (m < 0) { + lwsl_err("write failed\n"); + /* write failed, close conn */ + goto bail; + } + if (m) /* while still active, extend timeout */ + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 5); + sent += m; + + } while (!lws_send_pipe_choked(wsi) && (sent < 1024 * 1024)); +later: + lws_callback_on_writable(wsi); + break; +penultimate: + lws_vfs_file_close(&pss->fop_fd); + pss->fop_fd = NULL; + goto try_to_reuse; + +bail: + lws_vfs_file_close(&pss->fop_fd); + + return -1; + + /* + * callback for confirming to continue with client IP appear in + * protocol 0 callback since no websocket protocol has been agreed + * yet. You can just ignore this if you won't filter on client IP + * since the default unhandled callback return is 0 meaning let the + * connection continue. + */ + case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: + /* if we returned non-zero from here, we kill the connection */ + break; + +#ifndef LWS_NO_CLIENT + case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: { + char ctype[64], ctlen = 0; + lwsl_err("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP\n"); + p = buffer + LWS_PRE; + end = p + sizeof(buffer) - LWS_PRE; + if (lws_add_http_header_status(lws_get_parent(wsi), HTTP_STATUS_OK, &p, end)) + return 1; + if (lws_add_http_header_by_token(lws_get_parent(wsi), + WSI_TOKEN_HTTP_SERVER, + (unsigned char *)"libwebsockets", + 13, &p, end)) + return 1; + + ctlen = lws_hdr_copy(wsi, ctype, sizeof(ctype), WSI_TOKEN_HTTP_CONTENT_TYPE); + if (ctlen > 0) { + if (lws_add_http_header_by_token(lws_get_parent(wsi), + WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)ctype, ctlen, &p, end)) + return 1; + } +#if 0 + if (lws_add_http_header_content_length(lws_get_parent(wsi), + file_len, &p, end)) + return 1; +#endif + if (lws_finalize_http_header(lws_get_parent(wsi), &p, end)) + return 1; + + *p = '\0'; + lwsl_info("%s\n", buffer + LWS_PRE); + + n = lws_write(lws_get_parent(wsi), buffer + LWS_PRE, + p - (buffer + LWS_PRE), + LWS_WRITE_HTTP_HEADERS); + if (n < 0) + return -1; + + break; } + case LWS_CALLBACK_CLOSED_CLIENT_HTTP: + //lwsl_err("LWS_CALLBACK_CLOSED_CLIENT_HTTP\n"); + return -1; + break; + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: + //lwsl_err("LWS_CALLBACK_RECEIVE_CLIENT_HTTP: wsi %p\n", wsi); + assert(lws_get_parent(wsi)); + if (!lws_get_parent(wsi)) + break; + pss1 = lws_wsi_user(lws_get_parent(wsi)); + pss1->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY; + lws_callback_on_writable(lws_get_parent(wsi)); + break; + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: + //lwsl_err("LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ len %d\n", (int)len); + assert(lws_get_parent(wsi)); + m = lws_write(lws_get_parent(wsi), (unsigned char *)in, + len, LWS_WRITE_HTTP); + if (m < 0) + return -1; + break; + case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: + //lwsl_err("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); + assert(lws_get_parent(wsi)); + if (!lws_get_parent(wsi)) + break; + pss1 = lws_wsi_user(lws_get_parent(wsi)); + pss1->client_finished = 1; + break; +#endif + + /* + * callbacks for managing the external poll() array appear in + * protocol 0 callback + */ + + case LWS_CALLBACK_LOCK_POLL: + /* + * lock mutex to protect pollfd state + * called before any other POLL related callback + * if protecting wsi lifecycle change, len == 1 + */ + test_server_lock(len); + break; + + case LWS_CALLBACK_UNLOCK_POLL: + /* + * unlock mutex to protect pollfd state when + * called after any other POLL related callback + * if protecting wsi lifecycle change, len == 1 + */ + test_server_unlock(len); + break; + +#ifdef EXTERNAL_POLL + case LWS_CALLBACK_ADD_POLL_FD: + + if (count_pollfds >= max_poll_elements) { + lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n"); + return 1; + } + + fd_lookup[pa->fd] = count_pollfds; + pollfds[count_pollfds].fd = pa->fd; + pollfds[count_pollfds].events = pa->events; + pollfds[count_pollfds++].revents = 0; + break; + + case LWS_CALLBACK_DEL_POLL_FD: + if (!--count_pollfds) + break; + m = fd_lookup[pa->fd]; + /* have the last guy take up the vacant slot */ + pollfds[m] = pollfds[count_pollfds]; + fd_lookup[pollfds[count_pollfds].fd] = m; + break; + + case LWS_CALLBACK_CHANGE_MODE_POLL_FD: + pollfds[fd_lookup[pa->fd]].events = pa->events; + break; +#endif + + case LWS_CALLBACK_GET_THREAD_ID: + /* + * if you will call "lws_callback_on_writable" + * from a different thread, return the caller thread ID + * here so lws can use this information to work out if it + * should signal the poll() loop to exit and restart early + */ + + /* return pthread_getthreadid_np(); */ + + break; + +#if defined(LWS_OPENSSL_SUPPORT) + case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION: + /* Verify the client certificate */ + if (!len || (SSL_get_verify_result((SSL*)in) != X509_V_OK)) { + int err = X509_STORE_CTX_get_error((X509_STORE_CTX*)user); + int depth = X509_STORE_CTX_get_error_depth((X509_STORE_CTX*)user); + const char* msg = X509_verify_cert_error_string(err); + lwsl_err("LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION: SSL error: %s (%d), depth: %d\n", msg, err, depth); + return 1; + } + break; +#if defined(LWS_HAVE_SSL_CTX_set1_param) && !defined(LWS_WITH_MBEDTLS) + case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS: + if (crl_path[0]) { + /* Enable CRL checking */ + X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new(); + X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK); + SSL_CTX_set1_param((SSL_CTX*)user, param); + X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX*)user); + X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + n = X509_load_cert_crl_file(lookup, crl_path, X509_FILETYPE_PEM); + X509_VERIFY_PARAM_free(param); + if (n != 1) { + char errbuf[256]; + n = ERR_get_error(); + lwsl_err("LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS: SSL error: %s (%d)\n", ERR_error_string(n, errbuf), n); + return 1; + } + } + break; +#endif +#endif + + default: + break; + } + + return 0; + + /* if we're on HTTP1.1 or 2.0, will keep the idle connection alive */ +try_to_reuse: + if (lws_http_transaction_completed(wsi)) + return -1; + + return 0; +} diff -Nru libwebsockets-4.0.20/test-apps/test-server-libev.c libwebsockets-2.4.2/test-apps/test-server-libev.c --- libwebsockets-4.0.20/test-apps/test-server-libev.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-server-libev.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,347 @@ +/* + * libwebsockets-test-server - libwebsockets test implementation + * + * Copyright (C) 2011-2016 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * The person who associated a work with this deed has dedicated + * the work to the public domain by waiving all of his or her rights + * to the work worldwide under copyright law, including all related + * and neighboring rights, to the extent allowed by law. You can copy, + * modify, distribute and perform the work, even for commercial purposes, + * all without asking permission. + * + * The test apps are intended to be adapted for use in your code, which + * may be proprietary. So unlike the library itself, they are licensed + * Public Domain. + */ +#include "test-server.h" + +int close_testing; +int max_poll_elements; +int debug_level = 7; +volatile int force_exit = 0; +struct lws_context *context; +struct lws_plat_file_ops fops_plat; + +/* http server gets files from this path */ +#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server" +char *resource_path = LOCAL_RESOURCE_PATH; + +#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param) +char crl_path[1024] = ""; +#endif + +#define LWS_PLUGIN_STATIC +#include "../plugins/protocol_lws_mirror.c" +#include "../plugins/protocol_lws_status.c" + +/* singlethreaded version --> no locks */ + +void test_server_lock(int care) +{ +} +void test_server_unlock(int care) +{ +} + +/* + * This demo server shows how to use libwebsockets for one or more + * websocket protocols in the same server + * + * It defines the following websocket protocols: + * + * dumb-increment-protocol: once the socket is opened, an incrementing + * ascii string is sent down it every 50ms. + * If you send "reset\n" on the websocket, then + * the incrementing number is reset to 0. + * + * lws-mirror-protocol: copies any received packet to every connection also + * using this protocol, including the sender + */ + +enum demo_protocols { + /* always first */ + PROTOCOL_HTTP = 0, + + PROTOCOL_DUMB_INCREMENT, + PROTOCOL_LWS_MIRROR, + PROTOCOL_LWS_STATUS, + + /* always last */ + DEMO_PROTOCOL_COUNT +}; + +/* list of supported protocols and callbacks */ + +static struct lws_protocols protocols[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + callback_http, /* callback */ + sizeof (struct per_session_data__http), /* per_session_data_size */ + 0, /* max frame size / rx buffer */ + }, + { + "dumb-increment-protocol", + callback_dumb_increment, + sizeof(struct per_session_data__dumb_increment), + 10, /* rx buf size must be >= permessage-deflate rx size + * dumb-increment only sends very small packets, so we set + * this accordingly. If your protocol will send bigger + * things, adjust this to match */ + }, + LWS_PLUGIN_PROTOCOL_MIRROR, + LWS_PLUGIN_PROTOCOL_LWS_STATUS, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +static const struct lws_extension exts[] = { + { + "permessage-deflate", + lws_extension_callback_pm_deflate, + "permessage-deflate; client_no_context_takeover; client_max_window_bits" + }, + { + "deflate-frame", + lws_extension_callback_pm_deflate, + "deflate_frame" + }, + { NULL, NULL, NULL /* terminator */ } +}; + +/* this shows how to override the lws file operations. You don't need + * to do any of this unless you have a reason (eg, want to serve + * compressed files without decompressing the whole archive) + */ +static lws_fop_fd_t +test_server_fops_open(const struct lws_plat_file_ops *fops, + const char *vfs_path, const char *vpath, + lws_fop_flags_t *flags) +{ + lws_fop_fd_t n; + + /* call through to original platform implementation */ + n = fops_plat.open(fops, vfs_path, vpath, flags); + + lwsl_notice("%s: opening %s, ret %p\n", __func__, vfs_path, n); + + return n; +} + +void signal_cb(struct ev_loop *loop, struct ev_signal* watcher, int revents) +{ + lwsl_notice("Signal caught, exiting...\n"); + force_exit = 1; + switch (watcher->signum) { + case SIGTERM: + case SIGINT: + ev_break(loop, EVBREAK_ALL); + break; + default: + signal(SIGABRT, SIG_DFL); + abort(); + break; + } +} + +static void +ev_timeout_cb (EV_P_ ev_timer *w, int revents) +{ + lws_callback_on_writable_all_protocol(context, + &protocols[PROTOCOL_DUMB_INCREMENT]); +} + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "debug", required_argument, NULL, 'd' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, + { "allow-non-ssl", no_argument, NULL, 'a' }, + { "interface", required_argument, NULL, 'i' }, + { "closetest", no_argument, NULL, 'c' }, + { "libev", no_argument, NULL, 'e' }, +#ifndef LWS_NO_DAEMONIZE + { "daemonize", no_argument, NULL, 'D' }, +#endif + { "resource_path", required_argument, NULL, 'r' }, + { NULL, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + int sigs[] = { SIGINT, SIGKILL, SIGTERM, SIGSEGV, SIGFPE }; + struct ev_signal signals[ARRAY_SIZE(sigs)]; + struct ev_loop *loop = ev_default_loop(0); + struct lws_context_creation_info info; + char interface_name[128] = ""; + const char *iface = NULL; + ev_timer timeout_watcher; + char cert_path[1024]; + char key_path[1024]; + int use_ssl = 0; + int opts = 0; + int n = 0; +#ifndef _WIN32 + int syslog_options = LOG_PID | LOG_PERROR; +#endif +#ifndef LWS_NO_DAEMONIZE + int daemonize = 0; +#endif + + /* + * take care to zero down the info struct, he contains random garbaage + * from the stack otherwise + */ + memset(&info, 0, sizeof info); + info.port = 7681; + + while (n >= 0) { + n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL); + if (n < 0) + continue; + switch (n) { + case 'e': + opts |= LWS_SERVER_OPTION_LIBEV; + break; +#ifndef LWS_NO_DAEMONIZE + case 'D': + daemonize = 1; + #ifndef _WIN32 + syslog_options &= ~LOG_PERROR; + #endif + break; +#endif + case 'd': + debug_level = atoi(optarg); + break; + case 's': + use_ssl = 1; + break; + case 'a': + opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; + break; + case 'p': + info.port = atoi(optarg); + break; + case 'i': + strncpy(interface_name, optarg, sizeof interface_name); + interface_name[(sizeof interface_name) - 1] = '\0'; + iface = interface_name; + break; + case 'c': + close_testing = 1; + fprintf(stderr, " Close testing mode -- closes on " + "client after 50 dumb increments" + "and suppresses lws_mirror spam\n"); + break; + case 'r': + resource_path = optarg; + printf("Setting resource path to \"%s\"\n", resource_path); + break; + case 'h': + fprintf(stderr, "Usage: test-server " + "[--port=

] [--ssl] " + "[-d ] " + "[--resource_path ]\n"); + exit(1); + } + } + +#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32) + /* + * normally lock path would be /var/lock/lwsts or similar, to + * simplify getting started without having to take care about + * permissions or running as root, set to /tmp/.lwsts-lock + */ + if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) { + fprintf(stderr, "Failed to daemonize\n"); + return 1; + } +#endif + + for (n = 0; n < ARRAY_SIZE(sigs); n++) { + ev_init(&signals[n], signal_cb); + ev_signal_set(&signals[n], sigs[n]); + ev_signal_start(loop, &signals[n]); + } + +#ifndef _WIN32 + /* we will only try to log things according to our debug_level */ + setlogmask(LOG_UPTO (LOG_DEBUG)); + openlog("lwsts", syslog_options, LOG_DAEMON); +#endif + + /* tell the library what debug level to emit and to send it to syslog */ + lws_set_log_level(debug_level, lwsl_emit_syslog); + + lwsl_notice("libwebsockets test server libev - license LGPL2.1+SLE\n"); + lwsl_notice("(C) Copyright 2010-2016 Andy Green \n"); + + printf("Using resource path \"%s\"\n", resource_path); + + info.iface = iface; + info.protocols = protocols; + info.extensions = exts; + + info.ssl_cert_filepath = NULL; + info.ssl_private_key_filepath = NULL; + + if (use_ssl) { + if (strlen(resource_path) > sizeof(cert_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(cert_path, "%s/libwebsockets-test-server.pem", + resource_path); + if (strlen(resource_path) > sizeof(key_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(key_path, "%s/libwebsockets-test-server.key.pem", + resource_path); + + info.ssl_cert_filepath = cert_path; + info.ssl_private_key_filepath = key_path; + + opts |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + } + info.gid = -1; + info.uid = -1; + info.max_http_header_pool = 1; + info.options = opts | LWS_SERVER_OPTION_LIBEV; + + context = lws_create_context(&info); + if (context == NULL) { + lwsl_err("libwebsocket init failed\n"); + return -1; + } + + /* + * this shows how to override the lws file operations. You don't need + * to do any of this unless you have a reason (eg, want to serve + * compressed files without decompressing the whole archive) + */ + /* stash original platform fops */ + fops_plat = *(lws_get_fops(context)); + /* override the active fops */ + lws_get_fops(context)->open = test_server_fops_open; + + lws_ev_initloop(context, loop, 0); + + ev_timer_init(&timeout_watcher, ev_timeout_cb, 0.05, 0.05); + ev_timer_start(loop, &timeout_watcher); + ev_run(loop, 0); + + lws_context_destroy(context); + lwsl_notice("libwebsockets-test-server exited cleanly\n"); + +#ifndef _WIN32 + closelog(); +#endif + + return 0; +} diff -Nru libwebsockets-4.0.20/test-apps/test-server-libevent.c libwebsockets-2.4.2/test-apps/test-server-libevent.c --- libwebsockets-4.0.20/test-apps/test-server-libevent.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-server-libevent.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,345 @@ +/* + * libwebsockets-test-server - libwebsockets test implementation + * + * Copyright (C) 2011-2017 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * The person who associated a work with this deed has dedicated + * the work to the public domain by waiving all of his or her rights + * to the work worldwide under copyright law, including all related + * and neighboring rights, to the extent allowed by law. You can copy, + * modify, distribute and perform the work, even for commercial purposes, + * all without asking permission. + * + * The test apps are intended to be adapted for use in your code, which + * may be proprietary. So unlike the library itself, they are licensed + * Public Domain. + */ +#include "test-server.h" + +int close_testing; +int max_poll_elements; +int debug_level = 7; +volatile int force_exit = 0; +struct lws_context *context; +struct lws_plat_file_ops fops_plat; + +/* http server gets files from this path */ +#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server" +char *resource_path = LOCAL_RESOURCE_PATH; + +#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param) +char crl_path[1024] = ""; +#endif + +#define LWS_PLUGIN_STATIC +#include "../plugins/protocol_lws_mirror.c" +#include "../plugins/protocol_lws_status.c" +#include "../plugins/protocol_lws_meta.c" + +/* singlethreaded version --> no locks */ + +void test_server_lock(int care) +{ +} +void test_server_unlock(int care) +{ +} + +/* + * This demo server shows how to use libwebsockets for one or more + * websocket protocols in the same server + * + * It defines the following websocket protocols: + * + * dumb-increment-protocol: once the socket is opened, an incrementing + * ascii string is sent down it every 50ms. + * If you send "reset\n" on the websocket, then + * the incrementing number is reset to 0. + * + * lws-mirror-protocol: copies any received packet to every connection also + * using this protocol, including the sender + */ + +enum demo_protocols { + /* always first */ + PROTOCOL_HTTP = 0, + + PROTOCOL_DUMB_INCREMENT, + PROTOCOL_LWS_MIRROR, + PROTOCOL_LWS_META, + + /* always last */ + DEMO_PROTOCOL_COUNT +}; + +/* list of supported protocols and callbacks */ + +static struct lws_protocols protocols[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + callback_http, /* callback */ + sizeof (struct per_session_data__http), /* per_session_data_size */ + 0, /* max frame size / rx buffer */ + }, + { + "dumb-increment-protocol", + callback_dumb_increment, + sizeof(struct per_session_data__dumb_increment), + 10, + }, + + LWS_PLUGIN_PROTOCOL_MIRROR, + LWS_PLUGIN_PROTOCOL_LWS_STATUS, + LWS_PLUGIN_PROTOCOL_LWS_META, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +static const struct lws_extension exts[] = { + { + "permessage-deflate", + lws_extension_callback_pm_deflate, + "permessage-deflate; client_no_context_takeover; client_max_window_bits" + }, + { + "deflate-frame", + lws_extension_callback_pm_deflate, + "deflate_frame" + }, + { NULL, NULL, NULL /* terminator */ } +}; + +/* this shows how to override the lws file operations. You don't need + * to do any of this unless you have a reason (eg, want to serve + * compressed files without decompressing the whole archive) + */ +static lws_fop_fd_t +test_server_fops_open(const struct lws_plat_file_ops *fops, + const char *vfs_path, const char *vpath, + lws_fop_flags_t *flags) +{ + lws_fop_fd_t n; + + /* call through to original platform implementation */ + n = fops_plat.open(fops, vfs_path, vpath, flags); + + lwsl_notice("%s: opening %s, ret %p\n", __func__, vfs_path, n); + + return n; +} + +void signal_cb(evutil_socket_t sock_fd, short events, void *ctx) +{ + struct event_base *event_base_loop = ctx; + + lwsl_notice("Signal caught, exiting...\n"); + force_exit = 1; + if (events & EV_SIGNAL) + event_base_loopbreak(event_base_loop); +} + +static void +ev_timeout_cb (evutil_socket_t sock_fd, short events, void *ctx) +{ + lws_callback_on_writable_all_protocol(context, + &protocols[PROTOCOL_DUMB_INCREMENT]); +} + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "debug", required_argument, NULL, 'd' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, + { "allow-non-ssl", no_argument, NULL, 'a' }, + { "interface", required_argument, NULL, 'i' }, + { "closetest", no_argument, NULL, 'c' }, + { "libevent", no_argument, NULL, 'e' }, +#ifndef LWS_NO_DAEMONIZE + { "daemonize", no_argument, NULL, 'D' }, +#endif + { "resource_path", required_argument, NULL, 'r' }, + { NULL, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + int sigs[] = { SIGINT, SIGKILL, SIGTERM, SIGSEGV, SIGFPE }; + struct event *signals[ARRAY_SIZE(sigs)]; + struct event_base *event_base_loop = event_base_new(); + struct lws_context_creation_info info; + char interface_name[128] = ""; + const char *iface = NULL; + struct event *timeout_watcher; + char cert_path[1024]; + char key_path[1024]; + int use_ssl = 0; + int opts = 0; + int n = 0; +#ifndef _WIN32 + int syslog_options = LOG_PID | LOG_PERROR; +#endif +#ifndef LWS_NO_DAEMONIZE + int daemonize = 0; +#endif + + /* + * take care to zero down the info struct, he contains random garbaage + * from the stack otherwise + */ + memset(&info, 0, sizeof info); + info.port = 7681; + + while (n >= 0) { + n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL); + if (n < 0) + continue; + switch (n) { + case 'e': + opts |= LWS_SERVER_OPTION_LIBEVENT; + break; +#ifndef LWS_NO_DAEMONIZE + case 'D': + daemonize = 1; +#ifndef _WIN32 + syslog_options &= ~LOG_PERROR; +#endif + break; +#endif + case 'd': + debug_level = atoi(optarg); + break; + case 's': + use_ssl = 1; + break; + case 'a': + opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; + break; + case 'p': + info.port = atoi(optarg); + break; + case 'i': + strncpy(interface_name, optarg, sizeof interface_name); + interface_name[(sizeof interface_name) - 1] = '\0'; + iface = interface_name; + break; + case 'c': + close_testing = 1; + fprintf(stderr, " Close testing mode -- closes on " + "client after 50 dumb increments" + "and suppresses lws_mirror spam\n"); + break; + case 'r': + resource_path = optarg; + printf("Setting resource path to \"%s\"\n", resource_path); + break; + case 'h': + fprintf(stderr, "Usage: test-server " + "[--port=

] [--ssl] " + "[-d ] " + "[--resource_path ]\n"); + exit(1); + } + } + +#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32) + /* + * normally lock path would be /var/lock/lwsts or similar, to + * simplify getting started without having to take care about + * permissions or running as root, set to /tmp/.lwsts-lock + */ + if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) { + fprintf(stderr, "Failed to daemonize\n"); + return 1; + } +#endif + + for (n = 0; n < ARRAY_SIZE(sigs); n++) { + signals[n] = evsignal_new(event_base_loop, sigs[n], signal_cb, event_base_loop); + + evsignal_add(signals[n], NULL); + } + +#ifndef _WIN32 + /* we will only try to log things according to our debug_level */ + setlogmask(LOG_UPTO (LOG_DEBUG)); + openlog("lwsts", syslog_options, LOG_DAEMON); +#endif + + /* tell the library what debug level to emit and to send it to syslog */ + lws_set_log_level(debug_level, lwsl_emit_syslog); + + lwsl_notice("libwebsockets test server libevent - license LGPL2.1+SLE\n"); + lwsl_notice("(C) Copyright 2010-2016 Andy Green \n"); + + printf("Using resource path \"%s\"\n", resource_path); + + info.iface = iface; + info.protocols = protocols; + info.extensions = exts; + + info.ssl_cert_filepath = NULL; + info.ssl_private_key_filepath = NULL; + + if (use_ssl) { + if (strlen(resource_path) > sizeof(cert_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(cert_path, "%s/libwebsockets-test-server.pem", + resource_path); + if (strlen(resource_path) > sizeof(key_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(key_path, "%s/libwebsockets-test-server.key.pem", + resource_path); + + info.ssl_cert_filepath = cert_path; + info.ssl_private_key_filepath = key_path; + + opts |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + } + info.gid = -1; + info.uid = -1; + info.max_http_header_pool = 1; + info.options = opts | LWS_SERVER_OPTION_LIBEVENT; + + context = lws_create_context(&info); + if (context == NULL) { + lwsl_err("libwebsocket init failed\n"); + return -1; + } + + /* + * this shows how to override the lws file operations. You don't need + * to do any of this unless you have a reason (eg, want to serve + * compressed files without decompressing the whole archive) + */ + /* stash original platform fops */ + fops_plat = *(lws_get_fops(context)); + /* override the active fops */ + lws_get_fops(context)->open = test_server_fops_open; + + // Don't use the default Signal Event Watcher & Handler + lws_event_sigint_cfg(context, 0, NULL); + // Initialize the LWS with libevent loop + lws_event_initloop(context, event_base_loop, 0); + + timeout_watcher = event_new(event_base_loop, -1, EV_PERSIST, ev_timeout_cb, NULL); + struct timeval tv = {0, 50000}; + evtimer_add(timeout_watcher, &tv); + event_base_dispatch(event_base_loop); + + lws_context_destroy(context); + lwsl_notice("libwebsockets-test-server exited cleanly\n"); + +#ifndef _WIN32 + closelog(); +#endif + + return 0; +} diff -Nru libwebsockets-4.0.20/test-apps/test-server-libuv.c libwebsockets-2.4.2/test-apps/test-server-libuv.c --- libwebsockets-4.0.20/test-apps/test-server-libuv.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-server-libuv.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,480 @@ +/* + * libwebsockets-test-server for libev - libwebsockets test implementation + * + * Copyright (C) 2010-2015 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#define DI_HANDLED_BY_PLUGIN +#include "test-server.h" +#include + +int close_testing; +int max_poll_elements; +int debug_level = 7; +struct lws_context *context; +struct lws_plat_file_ops fops_plat; + +/* http server gets files from this path */ +#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server" +char *resource_path = LOCAL_RESOURCE_PATH; + +#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param) +char crl_path[1024] = ""; +#endif + +/* singlethreaded version --> no locks */ + +void test_server_lock(int care) +{ +} +void test_server_unlock(int care) +{ +} + +#define LWS_PLUGIN_STATIC +#include "../plugins/protocol_dumb_increment.c" +#include "../plugins/protocol_lws_mirror.c" +#include "../plugins/protocol_lws_status.c" +#include "../plugins/protocol_lws_meta.c" + +/* + * This demo server shows how to use libwebsockets for one or more + * websocket protocols in the same server + * + * It defines the following websocket protocols: + * + * dumb-increment-protocol: once the socket is opened, an incrementing + * ascii string is sent down it every 50ms. + * If you send "reset\n" on the websocket, then + * the incrementing number is reset to 0. + * + * lws-mirror-protocol: copies any received packet to every connection also + * using this protocol, including the sender + */ + +enum demo_protocols { + /* always first */ + PROTOCOL_HTTP = 0, + + PROTOCOL_DUMB_INCREMENT, + PROTOCOL_LWS_MIRROR, + PROTOCOL_LWS_STATUS, + PROTOCOL_LWS_META, + + /* always last */ + DEMO_PROTOCOL_COUNT +}; + +/* list of supported protocols and callbacks */ + +static struct lws_protocols protocols[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + callback_http, /* callback */ + sizeof (struct per_session_data__http), /* per_session_data_size */ + 0, /* max frame size / rx buffer */ + }, + LWS_PLUGIN_PROTOCOL_DUMB_INCREMENT, + LWS_PLUGIN_PROTOCOL_MIRROR, + LWS_PLUGIN_PROTOCOL_LWS_STATUS, + LWS_PLUGIN_PROTOCOL_LWS_META, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +static const struct lws_extension exts[] = { + { + "permessage-deflate", + lws_extension_callback_pm_deflate, + "permessage-deflate; client_no_context_takeover; client_max_window_bits" + }, + { + "deflate-frame", + lws_extension_callback_pm_deflate, + "deflate_frame" + }, + { NULL, NULL, NULL /* terminator */ } +}; + +void signal_cb(uv_signal_t *watcher, int signum) +{ + lwsl_err("Signal %d caught, exiting...\n", watcher->signum); + switch (watcher->signum) { + case SIGTERM: + case SIGINT: + break; + default: + signal(SIGABRT, SIG_DFL); + abort(); + break; + } + lws_libuv_stop(context); +} + + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "debug", required_argument, NULL, 'd' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, + { "allow-non-ssl", no_argument, NULL, 'a' }, + { "interface", required_argument, NULL, 'i' }, + { "closetest", no_argument, NULL, 'c' }, + { "libev", no_argument, NULL, 'e' }, + { "foreign", no_argument, NULL, 'f' }, +#ifndef LWS_NO_DAEMONIZE + { "daemonize", no_argument, NULL, 'D' }, +#endif + { "resource_path", required_argument, NULL, 'r' }, + { NULL, 0, 0, 0 } +}; + +#if UV_VERSION_MAJOR > 0 +/* ----- this code is only needed for foreign / external libuv tests -----*/ +struct counter +{ + int cur, lim; + int stop_loop; +}; + +static void timer_cb(uv_timer_t *t) +{ + struct counter *c = t->data; + + lwsl_notice(" timer %p cb, count %d, loop has %d handles\n", + t, c->cur, t->loop->active_handles); + + if (c->cur++ == c->lim) { + lwsl_debug("stop loop from timer\n"); + uv_timer_stop(t); + if (c->stop_loop) + uv_stop(t->loop); + } +} + +static void timer_close_cb(uv_handle_t *h) +{ + lwsl_notice("timer close cb %p, loop has %d handles\n", + h, h->loop->active_handles); +} + +void outer_signal_cb(uv_signal_t *s, int signum) +{ + lwsl_notice("Foreign loop got signal %d\n", signum); + uv_signal_stop(s); + uv_stop(s->loop); +} + +static void lws_uv_close_cb(uv_handle_t *handle) +{ + //lwsl_err("%s\n", __func__); +} + +static void lws_uv_walk_cb(uv_handle_t *handle, void *arg) +{ + uv_close(handle, lws_uv_close_cb); +} + +/* --- end of foreign test code ---- */ +#endif + +int main(int argc, char **argv) +{ + struct lws_context_creation_info info; + char interface_name[128] = ""; +#if UV_VERSION_MAJOR > 0 +/* --- only needed for foreign loop test ---> */ + uv_loop_t loop; + uv_signal_t signal_outer; + uv_timer_t timer_outer; + struct counter ctr; + int foreign_libuv_loop = 0; +/* <--- only needed for foreign loop test --- */ +#endif + const char *iface = NULL; + char cert_path[1024]; + char key_path[1024]; + int use_ssl = 0; + int opts = 0; + int n = 0; +#ifndef _WIN32 + int syslog_options = LOG_PID | LOG_PERROR; +#endif +#ifndef LWS_NO_DAEMONIZE + int daemonize = 0; +#endif + + /* + * take care to zero down the info struct, he contains random garbaage + * from the stack otherwise + */ + memset(&info, 0, sizeof info); + info.port = 7681; + + while (n >= 0) { + n = getopt_long(argc, argv, "feci:hsap:d:Dr:", options, NULL); + if (n < 0) + continue; + switch (n) { + case 'f': +#if UV_VERSION_MAJOR > 0 + foreign_libuv_loop = 1; +#endif + break; + case 'e': + opts |= LWS_SERVER_OPTION_LIBEV; + break; +#ifndef LWS_NO_DAEMONIZE + case 'D': + daemonize = 1; + #ifndef _WIN32 + syslog_options &= ~LOG_PERROR; + #endif + break; +#endif + case 'd': + debug_level = atoi(optarg); + break; + case 's': + use_ssl = 1; + break; + case 'a': + opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; + break; + case 'p': + info.port = atoi(optarg); + break; + case 'i': + strncpy(interface_name, optarg, sizeof interface_name); + interface_name[(sizeof interface_name) - 1] = '\0'; + iface = interface_name; + break; + case 'c': + close_testing = 1; + fprintf(stderr, " Close testing mode -- closes on " + "client after 50 dumb increments" + "and suppresses lws_mirror spam\n"); + break; + case 'r': + resource_path = optarg; + printf("Setting resource path to \"%s\"\n", resource_path); + break; + case 'h': + fprintf(stderr, "Usage: test-server " + "[--port=

] [--ssl] " + "[-d ] " + "[--resource_path ]\n"); + exit(1); + } + } + +#if !defined(WIN32) +#if !defined(LWS_NO_DAEMONIZE) + /* + * normally lock path would be /var/lock/lwsts or similar, to + * simplify getting started without having to take care about + * permissions or running as root, set to /tmp/.lwsts-lock + */ + if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) { + fprintf(stderr, "Failed to daemonize\n"); + return 1; + } +#endif + + /* we will only try to log things according to our debug_level */ + setlogmask(LOG_UPTO (LOG_DEBUG)); + openlog("lwsts", syslog_options, LOG_DAEMON); +#endif + + /* tell the library what debug level to emit and to send it to syslog */ + lws_set_log_level(debug_level, lwsl_emit_syslog); + + lwsl_notice("libwebsockets test server libuv - license LGPL2.1+SLE\n"); + lwsl_notice("(C) Copyright 2010-2016 Andy Green \n"); + + lwsl_info("Using resource path \"%s\"\n", resource_path); + + info.iface = iface; + info.protocols = protocols; + info.extensions = exts; + + info.ssl_cert_filepath = NULL; + info.ssl_private_key_filepath = NULL; + + if (use_ssl) { + if (strlen(resource_path) > sizeof(cert_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(cert_path, "%s/libwebsockets-test-server.pem", + resource_path); + if (strlen(resource_path) > sizeof(key_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(key_path, "%s/libwebsockets-test-server.key.pem", + resource_path); + + info.ssl_cert_filepath = cert_path; + info.ssl_private_key_filepath = key_path; + opts |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + } + info.gid = -1; + info.uid = -1; + info.max_http_header_pool = 1; + info.timeout_secs = 5; + info.options = opts | LWS_SERVER_OPTION_LIBUV; + +#if UV_VERSION_MAJOR > 0 + if (foreign_libuv_loop) { + /* create the foreign loop */ + uv_loop_init(&loop); + + /* run some timer on that loop just so loop is not 'clean' */ + + uv_signal_init(&loop, &signal_outer); + uv_signal_start(&signal_outer, outer_signal_cb, SIGINT); + + uv_timer_init(&loop, &timer_outer); + timer_outer.data = &ctr; + ctr.cur = 0; + ctr.lim = ctr.cur + 5; + ctr.stop_loop = 1; + uv_timer_start(&timer_outer, timer_cb, 0, 1000); + lwsl_notice("running loop without libwebsockets for %d s\n", ctr.lim); + + uv_run(&loop, UV_RUN_DEFAULT); + + /* timer will stop loop and we will get here */ + } +#endif + + context = lws_create_context(&info); + if (context == NULL) { + lwsl_err("libwebsocket init failed\n"); + return -1; + } + + lws_uv_sigint_cfg(context, 1, signal_cb); + +#if UV_VERSION_MAJOR > 0 + if (foreign_libuv_loop) + /* we have our own uv loop outside of lws */ + lws_uv_initloop(context, &loop, 0); + else +#endif + { + /* + * lws will create his own libuv loop in the context + */ + if (lws_uv_initloop(context, NULL, 0)) { + lwsl_err("lws_uv_initloop failed\n"); + + goto bail; + } + } + +#if UV_VERSION_MAJOR > 0 + if (foreign_libuv_loop) { + /* + * prepare inner timer on loop, to run along with lws. + * Will exit after 5s while lws keeps running + */ + struct counter ctr_inner = { 0, 3, 0 }; + int e; + uv_timer_t timer_inner; + uv_timer_init(&loop, &timer_inner); + timer_inner.data = &ctr_inner; + uv_timer_start(&timer_inner, timer_cb, 200, 1000); + + /* make this timer long-lived, should keep + * firing after lws exits */ + ctr.cur = 0; + ctr.lim = ctr.cur + 1000; + uv_timer_start(&timer_outer, timer_cb, 0, 1000); + + uv_run(&loop, UV_RUN_DEFAULT); + + /* we are here either because signal stopped us, + * or outer timer expired */ + + /* close short timer */ + uv_timer_stop(&timer_inner); + uv_close((uv_handle_t*)&timer_inner, timer_close_cb); + + + lwsl_notice("Destroying lws context\n"); + + /* detach lws */ + lws_context_destroy(context); + + lwsl_notice("Please wait while the outer libuv test continues for 10s\n"); + + ctr.lim = ctr.cur + 10; + + /* try and run outer timer for 10 more seconds, + * (or sigint outer handler) after lws has left the loop */ + uv_run(&loop, UV_RUN_DEFAULT); + + /* Clean up the foreign loop now */ + + /* PHASE 1: stop and close things we created + * outside of lws */ + + uv_timer_stop(&timer_outer); + uv_close((uv_handle_t*)&timer_outer, timer_close_cb); + uv_signal_stop(&signal_outer); + + e = 100; + while (e--) + uv_run(&loop, UV_RUN_NOWAIT); + + /* PHASE 2: close anything remaining */ + + uv_walk(&loop, lws_uv_walk_cb, NULL); + + e = 100; + while (e--) + uv_run(&loop, UV_RUN_NOWAIT); + + /* PHASE 3: close the UV loop itself */ + + e = uv_loop_close(&loop); + lwsl_notice("uv loop close rc %s\n", + e ? uv_strerror(e) : "ok"); + + /* PHASE 4: finalize context destruction */ + + lws_context_destroy2(context); + } else +#endif + { + lws_libuv_run(context, 0); + +bail: + lws_context_destroy(context); + lws_context_destroy2(context); + } + + lwsl_notice("libwebsockets-test-server exited cleanly\n"); + + context = NULL; + + return 0; +} diff -Nru libwebsockets-4.0.20/test-apps/test-server-pthreads.c libwebsockets-2.4.2/test-apps/test-server-pthreads.c --- libwebsockets-4.0.20/test-apps/test-server-pthreads.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-server-pthreads.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,393 @@ +/* + * libwebsockets-test-server - libwebsockets test implementation + * + * Copyright (C) 2010-2016 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * The person who associated a work with this deed has dedicated + * the work to the public domain by waiving all of his or her rights + * to the work worldwide under copyright law, including all related + * and neighboring rights, to the extent allowed by law. You can copy, + * modify, distribute and perform the work, even for commercial purposes, + * all without asking permission. + * + * The test apps are intended to be adapted for use in your code, which + * may be proprietary. So unlike the library itself, they are licensed + * Public Domain. + */ + +#include "test-server.h" +#include + +int close_testing; +int max_poll_elements; +int debug_level = 7; + +#ifdef EXTERNAL_POLL +struct lws_pollfd *pollfds; +int *fd_lookup; +int count_pollfds; +#endif +volatile int force_exit = 0; +struct lws_context *context; + +#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param) +char crl_path[1024] = ""; +#endif + +#define LWS_PLUGIN_STATIC +#include "../plugins/protocol_lws_mirror.c" +#include "../plugins/protocol_lws_status.c" + +/* + * This mutex lock protects code that changes or relies on wsi list outside of + * the service thread. The service thread will acquire it when changing the + * wsi list and other threads should acquire it while dereferencing wsis or + * calling apis like lws_callback_on_writable_all_protocol() which + * use the wsi list and wsis from a different thread context. + */ +pthread_mutex_t lock_established_conns; + +/* http server gets files from this path */ +#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server" +char *resource_path = LOCAL_RESOURCE_PATH; + +/* + * multithreaded version - protect wsi lifecycle changes in the library + * these are called from protocol 0 callbacks + */ + +void test_server_lock(int care) +{ + if (care) + pthread_mutex_lock(&lock_established_conns); +} +void test_server_unlock(int care) +{ + if (care) + pthread_mutex_unlock(&lock_established_conns); +} + +/* + * This demo server shows how to use libwebsockets for one or more + * websocket protocols in the same server + * + * It defines the following websocket protocols: + * + * dumb-increment-protocol: once the socket is opened, an incrementing + * ascii string is sent down it every 50ms. + * If you send "reset\n" on the websocket, then + * the incrementing number is reset to 0. + * + * lws-mirror-protocol: copies any received packet to every connection also + * using this protocol, including the sender + */ + +enum demo_protocols { + /* always first */ + PROTOCOL_HTTP = 0, + + PROTOCOL_DUMB_INCREMENT, + PROTOCOL_LWS_MIRROR, + PROTOCOL_LWS_STATUS, + + /* always last */ + DEMO_PROTOCOL_COUNT +}; + +/* list of supported protocols and callbacks */ + +static struct lws_protocols protocols[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + callback_http, /* callback */ + sizeof (struct per_session_data__http), /* per_session_data_size */ + 0, /* max frame size / rx buffer */ + }, + { + "dumb-increment-protocol", + callback_dumb_increment, + sizeof(struct per_session_data__dumb_increment), + 10, /* rx buf size must be >= permessage-deflate rx size + * dumb-increment only sends very small packets, so we set + * this accordingly. If your protocol will send bigger + * things, adjust this to match */ + }, + LWS_PLUGIN_PROTOCOL_MIRROR, + LWS_PLUGIN_PROTOCOL_LWS_STATUS, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +void *thread_dumb_increment(void *threadid) +{ + while (!force_exit) { + /* + * this lock means wsi in the active list cannot + * disappear underneath us, because the code to add and remove + * them is protected by the same lock + */ + pthread_mutex_lock(&lock_established_conns); + lws_callback_on_writable_all_protocol(context, + &protocols[PROTOCOL_DUMB_INCREMENT]); + pthread_mutex_unlock(&lock_established_conns); + usleep(100000); + } + + pthread_exit(NULL); +} + +void *thread_service(void *threadid) +{ + while (lws_service_tsi(context, 50, (int)(lws_intptr_t)threadid) >= 0 && !force_exit) + ; + + pthread_exit(NULL); +} + +void sighandler(int sig) +{ + force_exit = 1; + lws_cancel_service(context); +} + +static const struct lws_extension exts[] = { + { + "permessage-deflate", + lws_extension_callback_pm_deflate, + "permessage-deflate; client_no_context_takeover; client_max_window_bits" + }, + { + "deflate-frame", + lws_extension_callback_pm_deflate, + "deflate_frame" + }, + { NULL, NULL, NULL /* terminator */ } +}; + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "debug", required_argument, NULL, 'd' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, + { "allow-non-ssl", no_argument, NULL, 'a' }, + { "interface", required_argument, NULL, 'i' }, + { "closetest", no_argument, NULL, 'c' }, + { "libev", no_argument, NULL, 'e' }, + { "threads", required_argument, NULL, 'j' }, +#ifndef LWS_NO_DAEMONIZE + { "daemonize", no_argument, NULL, 'D' }, +#endif + { "resource_path", required_argument, NULL, 'r' }, + { NULL, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + struct lws_context_creation_info info; + char interface_name[128] = ""; + const char *iface = NULL; + pthread_t pthread_dumb, pthread_service[32]; + char cert_path[1024]; + char key_path[1024]; + int threads = 1; + int use_ssl = 0; + void *retval; + int opts = 0; + int n = 0; +#ifndef _WIN32 +/* LOG_PERROR is not POSIX standard, and may not be portable */ +#ifdef __sun + int syslog_options = LOG_PID; +#else + int syslog_options = LOG_PID | LOG_PERROR; +#endif +#endif +#ifndef LWS_NO_DAEMONIZE + int daemonize = 0; +#endif + + /* + * take care to zero down the info struct, he contains random garbaage + * from the stack otherwise + */ + memset(&info, 0, sizeof info); + info.port = 7681; + + pthread_mutex_init(&lock_established_conns, NULL); + + while (n >= 0) { + n = getopt_long(argc, argv, "eci:hsap:d:Dr:j:", options, NULL); + if (n < 0) + continue; + switch (n) { + case 'j': + threads = atoi(optarg); + if (threads > ARRAY_SIZE(pthread_service)) { + lwsl_err("Max threads %lu\n", + (unsigned long)ARRAY_SIZE(pthread_service)); + return 1; + } + break; + case 'e': + opts |= LWS_SERVER_OPTION_LIBEV; + break; +#ifndef LWS_NO_DAEMONIZE + case 'D': + daemonize = 1; + #if !defined(_WIN32) && !defined(__sun) + syslog_options &= ~LOG_PERROR; + #endif + break; +#endif + case 'd': + debug_level = atoi(optarg); + break; + case 's': + use_ssl = 1; + break; + case 'a': + opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; + break; + case 'p': + info.port = atoi(optarg); + break; + case 'i': + strncpy(interface_name, optarg, sizeof interface_name); + interface_name[(sizeof interface_name) - 1] = '\0'; + iface = interface_name; + break; + case 'c': + close_testing = 1; + fprintf(stderr, " Close testing mode -- closes on " + "client after 50 dumb increments" + "and suppresses lws_mirror spam\n"); + break; + case 'r': + resource_path = optarg; + printf("Setting resource path to \"%s\"\n", resource_path); + break; + case 'h': + fprintf(stderr, "Usage: test-server " + "[--port=

] [--ssl] " + "[-d ] " + "[--resource_path ]\n"); + exit(1); + } + } + +#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32) + /* + * normally lock path would be /var/lock/lwsts or similar, to + * simplify getting started without having to take care about + * permissions or running as root, set to /tmp/.lwsts-lock + */ + if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) { + fprintf(stderr, "Failed to daemonize\n"); + return 1; + } +#endif + + signal(SIGINT, sighandler); + +#ifndef _WIN32 + /* we will only try to log things according to our debug_level */ + setlogmask(LOG_UPTO (LOG_DEBUG)); + openlog("lwsts", syslog_options, LOG_DAEMON); +#endif + + /* tell the library what debug level to emit and to send it to syslog */ + lws_set_log_level(debug_level, lwsl_emit_syslog); + lwsl_notice("libwebsockets test server pthreads - license LGPL2.1+SLE\n"); + lwsl_notice("(C) Copyright 2010-2016 Andy Green \n"); + + printf("Using resource path \"%s\"\n", resource_path); +#ifdef EXTERNAL_POLL + max_poll_elements = getdtablesize(); + pollfds = malloc(max_poll_elements * sizeof (struct lws_pollfd)); + fd_lookup = malloc(max_poll_elements * sizeof (int)); + if (pollfds == NULL || fd_lookup == NULL) { + lwsl_err("Out of memory pollfds=%d\n", max_poll_elements); + return -1; + } +#endif + + info.iface = iface; + info.protocols = protocols; + info.extensions = exts; + + info.ssl_cert_filepath = NULL; + info.ssl_private_key_filepath = NULL; + + if (use_ssl) { + if (strlen(resource_path) > sizeof(cert_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(cert_path, "%s/libwebsockets-test-server.pem", + resource_path); + if (strlen(resource_path) > sizeof(key_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(key_path, "%s/libwebsockets-test-server.key.pem", + resource_path); + + info.ssl_cert_filepath = cert_path; + info.ssl_private_key_filepath = key_path; + } + info.gid = -1; + info.uid = -1; + info.options = opts; + info.count_threads = threads; + info.extensions = exts; + info.max_http_header_pool = 4; + + context = lws_create_context(&info); + if (context == NULL) { + lwsl_err("libwebsocket init failed\n"); + return -1; + } + + /* start the dumb increment thread */ + + n = pthread_create(&pthread_dumb, NULL, thread_dumb_increment, 0); + if (n) { + lwsl_err("Unable to create dumb thread\n"); + goto done; + } + + /* + * notice the actual number of threads may be capped by the library, + * so use lws_get_count_threads() to get the actual amount of threads + * initialized. + */ + + for (n = 0; n < lws_get_count_threads(context); n++) + if (pthread_create(&pthread_service[n], NULL, thread_service, + (void *)(lws_intptr_t)n)) + lwsl_err("Failed to start service thread\n"); + + /* wait for all the service threads to exit */ + + while ((--n) >= 0) + pthread_join(pthread_service[n], &retval); + + /* wait for pthread_dumb to exit */ + pthread_join(pthread_dumb, &retval); + +done: + lws_context_destroy(context); + pthread_mutex_destroy(&lock_established_conns); + + lwsl_notice("libwebsockets-test-server exited cleanly\n"); + +#ifndef _WIN32 + closelog(); +#endif + + return 0; +} diff -Nru libwebsockets-4.0.20/test-apps/test-server-v2.0.c libwebsockets-2.4.2/test-apps/test-server-v2.0.c --- libwebsockets-4.0.20/test-apps/test-server-v2.0.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-server-v2.0.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,571 @@ +/* + * libwebsockets-test-server-v2.0 - libwebsockets test implementation + * + * Copyright (C) 2010-2016 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * The person who associated a work with this deed has dedicated + * the work to the public domain by waiving all of his or her rights + * to the work worldwide under copyright law, including all related + * and neighboring rights, to the extent allowed by law. You can copy, + * modify, distribute and perform the work, even for commercial purposes, + * all without asking permission. + * + * The test apps are intended to be adapted for use in your code, which + * may be proprietary. So unlike the library itself, they are licensed + * Public Domain. + */ + +#include +#include +#include +#ifndef WIN32 +#include +#endif + +/* windows has no SIGUSR1 */ +#if !defined(WIN32) && !defined(_WIN32) +#define TEST_DYNAMIC_VHOST +#endif + +struct lws_context_creation_info info; +int debug_level = 7; +struct lws_context *context; + +#if defined(TEST_DYNAMIC_VHOST) +volatile int dynamic_vhost_enable = 0; +struct lws_vhost *dynamic_vhost; +uv_timer_t timeout_watcher; +#endif + +/* http server gets files from this path */ +#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server" +char *resource_path = LOCAL_RESOURCE_PATH; + +#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param) +char crl_path[1024] = ""; +#endif + +/* + * This test server is ONLY this .c file, it's radically simpler than the + * pre-v2.0 test servers. For example it has no user callback content or + * defines any protocols. + * + * To achieve that, it uses the LWS protocol plugins. Those in turn + * use libuv. So you must configure with LWS_WITH_PLUGINS (which implies + * libuv) to get this to build. + * + * You can find the individual protocol plugin sources in ../plugins + */ + +#if defined(TEST_DYNAMIC_VHOST) + +/* + * to test dynamic vhost creation, fire a SIGUSR1 at the test server. + * It will toggle the existence of a second identical vhost at port + 1 + * + * To synchronize with the event loop, it uses a libuv timer with 0 delay + * to get the business end called as the next event. + */ + +static void +uv_timeout_dynamic_vhost_toggle(uv_timer_t *w +#if UV_VERSION_MAJOR == 0 + , int status +#endif +) +{ + if (dynamic_vhost_enable && !dynamic_vhost) { + lwsl_notice("creating dynamic vhost...\n"); + dynamic_vhost = lws_create_vhost(context, &info); + } else + if (!dynamic_vhost_enable && dynamic_vhost) { + lwsl_notice("destroying dynamic vhost...\n"); + lws_vhost_destroy(dynamic_vhost); + dynamic_vhost = NULL; + } +} + +void sighandler_USR1(int sig) +{ + dynamic_vhost_enable ^= 1; + lwsl_notice("SIGUSR1: dynamic_vhost_enable: %d\n", + dynamic_vhost_enable); + uv_timer_start(&timeout_watcher, + uv_timeout_dynamic_vhost_toggle, 0, 0); +} +#endif + +void sighandler(int sig) +{ + lws_cancel_service(context); +} + +static const struct lws_extension exts[] = { + { + "permessage-deflate", + lws_extension_callback_pm_deflate, + "permessage-deflate" + }, + { + "deflate-frame", + lws_extension_callback_pm_deflate, + "deflate_frame" + }, + { NULL, NULL, NULL /* terminator */ } +}; + +/* + * mount handlers for sections of the URL space + */ + +static const struct lws_http_mount mount_ziptest = { + NULL, /* linked-list pointer to next*/ + "/ziptest", /* mountpoint in URL namespace on this vhost */ + LOCAL_RESOURCE_PATH"/candide.zip", /* handler */ + NULL, /* default filename if none given */ + NULL, + NULL, + NULL, + NULL, + 0, + 0, + 0, + 0, + 0, + 0, + LWSMPRO_FILE, /* origin points to a callback */ + 8, /* strlen("/ziptest"), ie length of the mountpoint */ + NULL, + + { NULL, NULL } // sentinel +}; + +static const struct lws_http_mount mount_post = { + (struct lws_http_mount *)&mount_ziptest, /* linked-list pointer to next*/ + "/formtest", /* mountpoint in URL namespace on this vhost */ + "protocol-post-demo", /* handler */ + NULL, /* default filename if none given */ + NULL, + NULL, + NULL, + NULL, + 0, + 0, + 0, + 0, + 0, + 0, + LWSMPRO_CALLBACK, /* origin points to a callback */ + 9, /* strlen("/formtest"), ie length of the mountpoint */ + NULL, + + { NULL, NULL } // sentinel +}; + +/* + * mount a filesystem directory into the URL space at / + * point it to our /usr/share directory with our assets in + * stuff from here is autoserved by the library + */ + +static const struct lws_http_mount mount = { + (struct lws_http_mount *)&mount_post, /* linked-list pointer to next*/ + "/", /* mountpoint in URL namespace on this vhost */ + LOCAL_RESOURCE_PATH, /* where to go on the filesystem for that */ + "test.html", /* default filename if none given */ + NULL, + NULL, + NULL, + NULL, + 0, + 0, + 0, + 0, + 0, + 0, + LWSMPRO_FILE, /* mount type is a directory in a filesystem */ + 1, /* strlen("/"), ie length of the mountpoint */ + NULL, + + { NULL, NULL } // sentinel +}; + +/* + * this sets a per-vhost, per-protocol option name:value pair + * the effect is to set this protocol to be the default one for the vhost, + * ie, selected if no Protocol: header is sent with the ws upgrade. + */ +#if 0 +static const struct lws_protocol_vhost_options pvo_opt = { + NULL, + NULL, + "default", + "1" +}; +#endif + +static const struct lws_protocol_vhost_options pvo_opt4a = { + NULL, + NULL, + "raw", /* indicate we are the protocol that gets raw connections */ + "1" +}; + +static const struct lws_protocol_vhost_options pvo_opt4 = { + &pvo_opt4a, + NULL, + "fifo-path", /* tell the raw test plugin to open a raw file here */ + "/tmp/lws-test-raw" +}; + +/* + * We must enable the plugin protocols we want into our vhost with a + * linked-list. We can also give the plugin per-vhost options here. + */ + +static const struct lws_protocol_vhost_options pvo_5 = { + NULL, + NULL, + "lws-meta", + "" /* ignored, just matches the protocol name above */ +}; + +static const struct lws_protocol_vhost_options pvo_4 = { + &pvo_5, + &pvo_opt4, /* set us as the protocol who gets raw connections */ + "protocol-lws-raw-test", + "" /* ignored, just matches the protocol name above */ +}; + +static const struct lws_protocol_vhost_options pvo_3 = { + &pvo_4, + NULL, + "protocol-post-demo", + "" /* ignored, just matches the protocol name above */ +}; + +static const struct lws_protocol_vhost_options pvo_2 = { + &pvo_3, + NULL, + "lws-status", + "" /* ignored, just matches the protocol name above */ +}; + +static const struct lws_protocol_vhost_options pvo_1 = { + &pvo_2, + NULL, + "lws-mirror-protocol", + "" +}; + +static const struct lws_protocol_vhost_options pvo = { + &pvo_1, + NULL, // &pvo_opt, + "dumb-increment-protocol", + "" +}; + +static void signal_cb(uv_signal_t *watcher, int signum) +{ + lwsl_err("Signal %d caught, exiting...\n", watcher->signum); + switch (watcher->signum) { + case SIGTERM: + case SIGINT: + break; + default: + signal(SIGABRT, SIG_DFL); + abort(); + break; + } + lws_libuv_stop(context); +} + +static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "debug", required_argument, NULL, 'd' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, + { "ssl-alerts", no_argument, NULL, 'S' }, + { "allow-non-ssl", no_argument, NULL, 'a' }, + { "interface", required_argument, NULL, 'i' }, + { "ssl-cert", required_argument, NULL, 'C' }, + { "ssl-key", required_argument, NULL, 'K' }, + { "ssl-ca", required_argument, NULL, 'A' }, +#if defined(LWS_OPENSSL_SUPPORT) + { "ssl-verify-client", no_argument, NULL, 'v' }, +#if defined(LWS_HAVE_SSL_CTX_set1_param) + { "ssl-crl", required_argument, NULL, 'R' }, +#endif +#endif +#ifndef LWS_NO_DAEMONIZE + { "daemonize", no_argument, NULL, 'D' }, +#endif + { "resource_path", required_argument, NULL, 'r' }, + { NULL, 0, 0, 0 } +}; + +static const char * const plugin_dirs[] = { + INSTALL_DATADIR"/libwebsockets-test-server/plugins/", + NULL +}; + +int main(int argc, char **argv) +{ + struct lws_vhost *vhost; + char interface_name[128] = ""; + const char *iface = NULL; + char cert_path[1024] = ""; + char key_path[1024] = ""; + char ca_path[1024] = ""; + int uid = -1, gid = -1; + int use_ssl = 0; + int opts = 0; + int n = 0; +#ifndef _WIN32 + int syslog_options = LOG_PID | LOG_PERROR; +#endif +#ifndef LWS_NO_DAEMONIZE + int daemonize = 0; +#endif + + /* + * take care to zero down the info struct, he contains random garbaage + * from the stack otherwise + */ + memset(&info, 0, sizeof info); + info.port = 7681; + + while (n >= 0) { + n = getopt_long(argc, argv, "i:hsap:d:Dr:C:K:A:R:vu:g:S", + (struct option *)options, NULL); + if (n < 0) + continue; + switch (n) { +#ifndef LWS_NO_DAEMONIZE + case 'D': + daemonize = 1; + #ifndef _WIN32 + syslog_options &= ~LOG_PERROR; + #endif + break; +#endif + case 'u': + uid = atoi(optarg); + break; + case 'g': + gid = atoi(optarg); + break; + case 'd': + debug_level = atoi(optarg); + break; + case 's': + use_ssl = 1; + break; + case 'S': +#if defined(LWS_OPENSSL_SUPPORT) + info.ssl_info_event_mask |= SSL_CB_ALERT; +#endif + break; + case 'a': + opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; + break; + case 'p': + info.port = atoi(optarg); + break; + case 'i': + strncpy(interface_name, optarg, sizeof interface_name); + interface_name[(sizeof interface_name) - 1] = '\0'; + iface = interface_name; + break; + case 'r': + resource_path = optarg; + printf("Setting resource path to \"%s\"\n", resource_path); + break; + case 'C': + strncpy(cert_path, optarg, sizeof(cert_path) - 1); + cert_path[sizeof(cert_path) - 1] = '\0'; + break; + case 'K': + strncpy(key_path, optarg, sizeof(key_path) - 1); + key_path[sizeof(key_path) - 1] = '\0'; + break; + case 'A': + strncpy(ca_path, optarg, sizeof(ca_path) - 1); + ca_path[sizeof(ca_path) - 1] = '\0'; + break; +#if defined(LWS_OPENSSL_SUPPORT) + case 'v': + use_ssl = 1; + opts |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT; + break; + +#if defined(LWS_HAVE_SSL_CTX_set1_param) + case 'R': + strncpy(crl_path, optarg, sizeof(crl_path) - 1); + crl_path[sizeof(crl_path) - 1] = '\0'; + break; +#endif +#endif + case 'h': + fprintf(stderr, "Usage: test-server " + "[--port=

] [--ssl] " + "[-d ] " + "[--resource_path ]\n"); + exit(1); + } + } + +#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32) + /* + * normally lock path would be /var/lock/lwsts or similar, to + * simplify getting started without having to take care about + * permissions or running as root, set to /tmp/.lwsts-lock + */ + if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) { + fprintf(stderr, "Failed to daemonize\n"); + return 10; + } +#endif + + signal(SIGINT, sighandler); +#if defined(TEST_DYNAMIC_VHOST) + signal(SIGUSR1, sighandler_USR1); +#endif + +#ifndef _WIN32 + /* we will only try to log things according to our debug_level */ + setlogmask(LOG_UPTO (LOG_DEBUG)); + openlog("lwsts", syslog_options, LOG_DAEMON); +#endif + + /* tell the library what debug level to emit and to send it to syslog */ + lws_set_log_level(debug_level, lwsl_emit_syslog); + + lwsl_notice("libwebsockets test server - license LGPL2.1+SLE\n"); + lwsl_notice("(C) Copyright 2010-2017 Andy Green \n"); + + lwsl_notice(" Using resource path \"%s\"\n", resource_path); + + info.iface = iface; + info.protocols = NULL; /* all protocols from lib / plugins */ + info.ssl_cert_filepath = NULL; + info.ssl_private_key_filepath = NULL; + info.gid = gid; + info.uid = uid; + info.max_http_header_pool = 16; + info.options = opts | LWS_SERVER_OPTION_EXPLICIT_VHOSTS | + LWS_SERVER_OPTION_FALLBACK_TO_RAW | + LWS_SERVER_OPTION_VALIDATE_UTF8 | + LWS_SERVER_OPTION_LIBUV; /* plugins require this */ + + if (use_ssl) { + if (strlen(resource_path) > sizeof(cert_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + if (!cert_path[0]) + sprintf(cert_path, "%s/libwebsockets-test-server.pem", + resource_path); + if (strlen(resource_path) > sizeof(key_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + if (!key_path[0]) + sprintf(key_path, "%s/libwebsockets-test-server.key.pem", + resource_path); + + info.ssl_cert_filepath = cert_path; + info.ssl_private_key_filepath = key_path; + if (ca_path[0]) + info.ssl_ca_filepath = ca_path; + + /* redirect guys coming on http */ + info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS; + } + + info.extensions = exts; + info.timeout_secs = 5; + info.ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-GCM-SHA384:" + "DHE-RSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-SHA384:" + "HIGH:!aNULL:!eNULL:!EXPORT:" + "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:" + "!SHA1:!DHE-RSA-AES128-GCM-SHA256:" + "!DHE-RSA-AES128-SHA256:" + "!AES128-GCM-SHA256:" + "!AES128-SHA256:" + "!DHE-RSA-AES256-SHA256:" + "!AES256-GCM-SHA384:" + "!AES256-SHA256"; + + /* tell lws to look for protocol plugins here */ + info.plugin_dirs = plugin_dirs; + + /* tell lws about our mount we want */ + info.mounts = &mount; + /* + * give it our linked-list of Per-Vhost Options, these control + * which protocols (from plugins) are allowed to be enabled on + * our vhost + */ + info.pvo = &pvo; + + /* + * Since we used LWS_SERVER_OPTION_EXPLICIT_VHOSTS, this only creates + * the context. We can modify info and create as many vhosts as we + * like subsequently. + */ + context = lws_create_context(&info); + if (context == NULL) { + lwsl_err("libwebsocket init failed\n"); + return -1; + } + + /* + * normally we would adapt at least info.name to reflect the + * external hostname for this server. + */ + vhost = lws_create_vhost(context, &info); + if (!vhost) { + lwsl_err("vhost creation failed\n"); + return -1; + } + +#if defined(TEST_DYNAMIC_VHOST) + /* our dynamic vhost is on port + 1 */ + info.port++; +#endif + + /* libuv event loop */ + lws_uv_sigint_cfg(context, 1, signal_cb); + if (lws_uv_initloop(context, NULL, 0)) { + lwsl_err("lws_uv_initloop failed\n"); + goto bail; + } + +#if defined(TEST_DYNAMIC_VHOST) + uv_timer_init(lws_uv_getloop(context, 0), &timeout_watcher); +#endif + lws_libuv_run(context, 0); + +#if defined(TEST_DYNAMIC_VHOST) + uv_timer_stop(&timeout_watcher); + uv_close((uv_handle_t *)&timeout_watcher, NULL); +#endif + +bail: + /* when we decided to exit the event loop */ + lws_context_destroy(context); + lws_context_destroy2(context); + lwsl_notice("libwebsockets-test-server exited cleanly\n"); + +#ifndef _WIN32 + closelog(); +#endif + + return 0; +} diff -Nru libwebsockets-4.0.20/test-apps/test-sshd.c libwebsockets-2.4.2/test-apps/test-sshd.c --- libwebsockets-4.0.20/test-apps/test-sshd.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/test-apps/test-sshd.c 2018-03-08 10:28:37.000000000 +0000 @@ -1,7 +1,7 @@ /* * Example embedded sshd server using libwebsockets sshd plugin * - * Written in 2010-2019 by Andy Green + * Copyright (C) 2017 Andy Green * * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. @@ -202,11 +202,10 @@ fd = lws_get_socket_fd(wsi_stdin); if (*buf != 0x0d) { - if (write(fd, buf, len) != (int)len) + if (write(fd, buf, len) != len) return -1; if (priv->pty_in_echo) { - if (!lws_ring_insert(priv->ring_stdout, buf, 1)) - lwsl_notice("dropping...\n"); + lws_ring_insert(priv->ring_stdout, buf, 1); lws_callback_on_writable(wsi); } } else { @@ -218,8 +217,7 @@ if (priv->pty_in_echo) { bbuf[0] = 0x0d; bbuf[1] = 0x0a; - if (!lws_ring_insert(priv->ring_stdout, bbuf, 2)) - lwsl_notice("dropping...\n"); + lws_ring_insert(priv->ring_stdout, bbuf, 2); lws_callback_on_writable(wsi); } } @@ -232,7 +230,7 @@ static size_t ssh_ops_get_server_key(struct lws *wsi, uint8_t *buf, size_t len) { - int fd = lws_open(TEST_SERVER_KEY_PATH, O_RDONLY), n; + int fd = open(TEST_SERVER_KEY_PATH, O_RDONLY), n; if (fd == -1) { lwsl_err("%s: unable to open %s for read: %s\n", __func__, @@ -255,7 +253,7 @@ static size_t ssh_ops_set_server_key(struct lws *wsi, uint8_t *buf, size_t len) { - int fd = lws_open(TEST_SERVER_KEY_PATH, O_CREAT | O_TRUNC | O_RDWR, 0600); + int fd = open(TEST_SERVER_KEY_PATH, O_CREAT | O_TRUNC | O_RDWR, 0600); int n; lwsl_notice("%s: %d\n", __func__, fd); @@ -283,7 +281,7 @@ ssh_ops_is_pubkey_authorized(const char *username, const char *type, const uint8_t *peer, int peer_len) { - char *aps, *p, *ps; + char *aps = NULL, *p, *ps; int n = strlen(type), alen = 2048, ret = 2, len; size_t s = 0; @@ -342,8 +340,8 @@ * EN that the peer sends us */ - if (lws_timingsafe_bcmp(peer, ps, peer_len)) { - lwsl_info("factors mismatch\n"); + if (memcmp(peer, ps, peer_len)) { + lwsl_notice("factors mismatch\n"); goto bail; } @@ -379,7 +377,7 @@ return 1; } - pvo->value = malloc(strlen(value) + 1); + pvo->value = malloc(strlen(name) + 1); if (!pvo->value) { free((char *)pvo->name); free(pvo); @@ -458,6 +456,7 @@ struct sshd_instance_priv *priv = _priv; struct lws_ring *r = priv->ring_stdout; void *rp; + uint8_t buf[256], *p, *d; size_t bytes; int n, m; @@ -480,13 +479,11 @@ break; } if (priv->pty_in_bloat_nl_to_crnl) { - uint8_t buf[256], *p, *d; - if (bytes != 1) n = bytes / 2; else n = 1; - if (n > (int)sizeof(buf)) + if (n > sizeof(buf)) n = sizeof(buf); if (!n) @@ -516,7 +513,7 @@ *p++ = *d++; } n = (void *)p - rp; - if (n < (int)bytes && priv->insert_lf) { + if (n < bytes && priv->insert_lf) { priv->insert_lf = 0; *p++ = 0x0d; n++; @@ -548,7 +545,7 @@ } static int -ssh_ops_exec(void *_priv, struct lws *wsi, const char *command, lws_ssh_finish_exec finish, void *finish_handle) +ssh_ops_exec(void *_priv, struct lws *wsi, const char *command) { lwsl_notice("%s: EXEC %s\n", __func__, command); @@ -557,7 +554,7 @@ } static int -ssh_ops_shell(void *_priv, struct lws *wsi, lws_ssh_finish_exec finish, void *finish_handle) +ssh_ops_shell(void *_priv, struct lws *wsi) { struct sshd_instance_priv *priv = _priv; const char *cmd[] = { @@ -581,12 +578,12 @@ static size_t ssh_ops_banner(char *buf, size_t max_len, char *lang, size_t max_lang_len) { - int n = lws_snprintf(buf, max_len, "\n" + int n = snprintf(buf, max_len, "\n" " |\\---/| lws-ssh Test Server\n" " | o_o | SSH Terminal Server\n" " \\_^_/ Copyright (C) 2017 Crash Barrier Ltd\n\n"); - lws_snprintf(lang, max_lang_len, "en/US"); + snprintf(lang, max_lang_len, "en/US"); return n; } @@ -617,7 +614,7 @@ .banner = ssh_ops_banner, .disconnect_reason = ssh_ops_disconnect_reason, .server_string = "SSH-2.0-Libwebsockets", - .api_version = 2, + .api_version = 1, }; /* @@ -690,7 +687,7 @@ n = 0; while (!n && !force_exit) - n = lws_service(context, 0); + n = lws_service(context, 500); ret = 0; Binary files /tmp/tmp16m1nG/8_F9BNqy2i/libwebsockets-4.0.20/test-apps/wss-over-h2.png and /tmp/tmp16m1nG/nMN6KYrts9/libwebsockets-2.4.2/test-apps/wss-over-h2.png differ diff -Nru libwebsockets-4.0.20/.travis.yml libwebsockets-2.4.2/.travis.yml --- libwebsockets-4.0.20/.travis.yml 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/.travis.yml 2018-03-08 10:28:37.000000000 +0000 @@ -4,29 +4,17 @@ global: - secure: "KhAdQ9ja+LBObWNQTYO7Df5J4DyOih6S+eerDMu8UPSO+CoWV2pWoQzbOfocjyOscGOwC+2PrrHDNZyGfqkCLDXg1BxynXPCFerHC1yc2IajvKpGXmAAygNIvp4KACDfGv/dkXrViqIzr/CdcNaU4vIMHSVb5xkeLi0W1dPnQOI=" matrix: - # 2019-09-30: travis build no longer has dbus - # LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2=/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_GENCRYPTO=1 -DLWS_WITH_JOSE=1 -DLWS_WITH_SYS_ASYNC_DNS=1" - # LWS_METHOD=lwsws2 CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2=/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_LWS_DSH=1" - - LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_WITH_GENCRYPTO=1 -DLWS_WITH_JOSE=1 -DLWS_WITH_SYS_ASYNC_DNS=1 -DLWS_WITH_SYS_NTPCLIENT=1" - - LWS_METHOD=lwsws2 CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_WITH_LWS_DSH=1" - - LWS_METHOD=default CMAKE_ARGS="-DLWS_WITH_MINIMAL_EXAMPLES=1" - - LWS_METHOD=mbedtls CMAKE_ARGS="-DLWS_WITH_MBEDTLS=1 -DLWS_WITH_HTTP2=1 -DLWS_WITH_LWSWS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_JOSE=1 -DCMAKE_BUILD_TYPE=DEBUG" - - LWS_METHOD=ss CMAKE_ARGS="-DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1" - - LWS_METHOD=ss+mbedtls CMAKE_ARGS="-DLWS_WITH_MBEDTLS=1 -DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1" - - LWS_METHOD=noserver CMAKE_ARGS="-DLWS_WITHOUT_SERVER=ON -DLWS_WITH_MINIMAL_EXAMPLES=1" - - LWS_METHOD=noclient CMAKE_ARGS="-DLWS_WITHOUT_CLIENT=ON -DLWS_WITH_MINIMAL_EXAMPLES=1" - - LWS_METHOD=noext CMAKE_ARGS="-DLWS_WITHOUT_EXTENSIONS=ON -DLWS_WITH_MINIMAL_EXAMPLES=1" - - LWS_METHOD=nonetwork CMAKE_ARGS="-DLWS_WITH_NETWORK=0" + - LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON" + - LWS_METHOD=default + - LWS_METHOD=noserver CMAKE_ARGS="-DLWS_WITHOUT_SERVER=ON" + - LWS_METHOD=noclient CMAKE_ARGS="-DLWS_WITHOUT_CLIENT=ON" + - LWS_METHOD=noext CMAKE_ARGS="-DLWS_WITHOUT_EXTENSIONS=ON" - LWS_METHOD=libev CMAKE_ARGS="-DLWS_WITH_LIBEV=ON" - - LWS_METHOD=ipv6 CMAKE_ARGS="-DLWS_IPV6=ON" + - LWS_METHOD=noipv6 CMAKE_ARGS="-DLWS_IPV6=OFF" - LWS_METHOD=nossl CMAKE_ARGS="-DLWS_WITH_SSL=OFF" - LWS_METHOD=nodaemon CMAKE_ARGS="-DLWS_WITHOUT_DAEMONIZE=ON" - LWS_METHOD=cgi CMAKE_ARGS="-DLWS_WITH_CGI=ON" - LWS_METHOD=nologs CMAKE_ARGS="-DLWS_WITH_NO_LOGS=ON" - - LWS_METHOD=smp CMAKE_ARGS="-DLWS_MAX_SMP=32 -DLWS_WITH_MINIMAL_EXAMPLES=1" - - LWS_METHOD=nows CMAKE_ARGS="-DLWS_ROLE_WS=0" - - LWS_METHOD=mqtt CMAKE_ARGS="-DLWS_ROLE_MQTT=1" - - LWS_METHOD=threadpool CMAKE_ARGS="-DLWS_WITH_THREADPOOL=1 -DLWS_WITH_MINIMAL_EXAMPLES=1" os: - linux @@ -34,13 +22,8 @@ language: generic install: - ./scripts/travis_install.sh -# - ./travis-tool.sh github_package jimhester/covr - -#after_success: -# - Rscript -e 'covr::coveralls()' - script: - - ./scripts/travis_control.sh + - if [ "$COVERITY_SCAN_BRANCH" != 1 -a "$TRAVIS_OS_NAME" = "osx" ]; then mkdir build && cd build && cmake -DOPENSSL_ROOT_DIR="/usr/local/opt/openssl" $CMAKE_ARGS .. && cmake --build .; else if [ "$COVERITY_SCAN_BRANCH" != 1 -a "$TRAVIS_OS_NAME" = "linux" ]; then mkdir build && cd build && cmake $CMAKE_ARGS .. && cmake --build .; fi ; fi sudo: required dist: trusty addons: diff -Nru libwebsockets-4.0.20/win32port/version.rc.in libwebsockets-2.4.2/win32port/version.rc.in --- libwebsockets-4.0.20/win32port/version.rc.in 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/win32port/version.rc.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -#include - -#define LWS_VERSION @LWS_LIBRARY_VERSION_MAJOR@,@LWS_LIBRARY_VERSION_MINOR@,@LWS_LIBRARY_VERSION_PATCH@,0 -#define LWS_VERSION_STR "@LWS_LIBRARY_VERSION_MAJOR@.@LWS_LIBRARY_VERSION_MINOR@.@LWS_LIBRARY_VERSION_PATCH@\0" -#define LWS_PACKAGE_NAME "@PACKAGE@\0" - -VS_VERSION_INFO VERSIONINFO - FILEVERSION LWS_VERSION - PRODUCTVERSION LWS_VERSION - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK - FILEFLAGS 0 - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "ProductName", LWS_PACKAGE_NAME - VALUE "ProductVersion", LWS_VERSION_STR - VALUE "FileVersion", LWS_VERSION_STR - VALUE "FileDescription", "Libwebsockets is a lightweight pure C library built to use minimal CPU and memory resources, and provide fast throughput in both directions as client or server.\0" - VALUE "InternalName", "websockets.dll\0" - VALUE "OriginalFilename", "websockets.dll\0" - VALUE "CompanyName", "Open Source Software community LGPL\0" - VALUE "LegalCopyright", "Copyright (C) Project contributors\0" - VALUE "Comments", "https://libwebsockets.org\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END diff -Nru libwebsockets-4.0.20/win32port/win32helpers/getopt_long.c libwebsockets-2.4.2/win32port/win32helpers/getopt_long.c --- libwebsockets-4.0.20/win32port/win32helpers/getopt_long.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/win32port/win32helpers/getopt_long.c 2018-03-08 10:28:37.000000000 +0000 @@ -38,9 +38,6 @@ #include #include "getopt.h" -#define lws_ptr_diff(head, tail) \ - ((int)((char *)(head) - (char *)(tail))) - extern int opterr; /* if error message should be printed */ extern int optind; /* index into parent argv vector */ extern int optopt; /* character checked for validity */ @@ -186,10 +183,10 @@ return(-1); } if ((has_equal = strchr(current_argv, '=')) != NULL) { - current_argv_len = lws_ptr_diff(has_equal, current_argv); + current_argv_len = has_equal - current_argv; has_equal++; } else - current_argv_len = (int)strlen(current_argv); + current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { if (strncmp(current_argv, long_options[i].name, current_argv_len)) diff -Nru libwebsockets-4.0.20/win32port/zlib/crc32.c libwebsockets-2.4.2/win32port/zlib/crc32.c --- libwebsockets-4.0.20/win32port/zlib/crc32.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/win32port/zlib/crc32.c 2018-03-08 10:28:37.000000000 +0000 @@ -295,7 +295,7 @@ } /* ========================================================================= */ -#define DOBIG4 c ^= *buf4++; \ +#define DOBIG4 c ^= *++buf4; \ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] #define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 @@ -317,6 +317,7 @@ } buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; while (len >= 32) { DOBIG32; len -= 32; @@ -325,6 +326,7 @@ DOBIG4; len -= 4; } + buf4++; buf = (const unsigned char FAR *)buf4; if (len) do { diff -Nru libwebsockets-4.0.20/win32port/zlib/deflate.c libwebsockets-2.4.2/win32port/zlib/deflate.c --- libwebsockets-4.0.20/win32port/zlib/deflate.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/win32port/zlib/deflate.c 2018-03-08 10:28:37.000000000 +0000 @@ -397,18 +397,6 @@ } /* ========================================================================= */ -int ZEXPORT deflatePending (strm, pending, bits) - unsigned *pending; - int *bits; - z_streamp strm; -{ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - *pending = strm->state->pending; - *bits = strm->state->bi_valid; - return Z_OK; -} - -/* ========================================================================= */ int ZEXPORT deflatePrime (strm, bits, value) z_streamp strm; int bits; diff -Nru libwebsockets-4.0.20/win32port/zlib/gzclose.c libwebsockets-2.4.2/win32port/zlib/gzclose.c --- libwebsockets-4.0.20/win32port/zlib/gzclose.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/win32port/zlib/gzclose.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,25 @@ +/* gzclose.c -- zlib gzclose() function + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* gzclose() is in a separate file so that it is linked in only if it is used. + That way the other gzclose functions can be used instead to avoid linking in + unneeded compression or decompression routines. */ +int ZEXPORT gzclose(file) + gzFile file; +{ +#ifndef NO_GZCOMPRESS + gz_statep state; + + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); +#else + return gzclose_r(file); +#endif +} diff -Nru libwebsockets-4.0.20/win32port/zlib/gzio.c libwebsockets-2.4.2/win32port/zlib/gzio.c --- libwebsockets-4.0.20/win32port/zlib/gzio.c 1970-01-01 00:00:00.000000000 +0000 +++ libwebsockets-2.4.2/win32port/zlib/gzio.c 2018-03-08 10:28:37.000000000 +0000 @@ -0,0 +1,1006 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* \param (#) $Id$ */ + +#include + +#include "zutil.h" +#include "gzguts.h" + +#ifdef NO_DEFLATE /* for compatiblity with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[20]; + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out, + s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + int err; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + err = do_flush (file, Z_FINISH); + if (err != Z_OK) return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +/* =========================================================================== + Returns the error message for the last error which occured on the + given compressed file. errnum is set to zlib error number. If an + error occured in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror() : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff -Nru libwebsockets-4.0.20/win32port/zlib/inflate.c libwebsockets-2.4.2/win32port/zlib/inflate.c --- libwebsockets-4.0.20/win32port/zlib/inflate.c 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/win32port/zlib/inflate.c 2018-03-08 10:28:37.000000000 +0000 @@ -1472,10 +1472,9 @@ { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) - return (long)(((unsigned long)0 - 1) << 16); - state = (struct inflate_state FAR *)strm->state; - return (long)(((unsigned long)((long)state->back)) << 16) + - (state->mode == COPY ? state->length : - (state->mode == MATCH ? state->was - state->length : 0)); + if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + state = (struct inflate_state FAR *)strm->state; + return ((long)(state->back) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); } diff -Nru libwebsockets-4.0.20/win32port/zlib/zconf.h libwebsockets-2.4.2/win32port/zlib/zconf.h --- libwebsockets-4.0.20/win32port/zlib/zconf.h 2020-07-09 13:11:46.000000000 +0000 +++ libwebsockets-2.4.2/win32port/zlib/zconf.h 2018-03-08 10:28:37.000000000 +0000 @@ -40,7 +40,6 @@ # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams -# define deflatePending z_deflatePending # define deflatePrime z_deflatePrime # define deflateReset z_deflateReset # define deflateSetDictionary z_deflateSetDictionary